Please use the full script name in "allowed-tools" like below:
allowed-tools: Bash(bun run scripts/compress.ts:*)
Save the :* syntax for "arguments following a tool call", never treat it like a glob pattern.
Permissions are notoriously spotty and I've been having inconsistent behavior with the configuration in this lesson. We'll introduce stronger control in a "hooks" section in upcoming lessons.
When building Claude Code skills, you may encounter limitations with the allowed-tools feature, particularly when a CLI tool's syntax includes special characters like shell redirects (>). This can cause parsing issues and prevent your agent from getting the necessary permissions to run the command.
This lesson demonstrates a powerful workaround: abstracting complex or problematic CLI commands into dedicated scripts. By doing this, you can create more robust, maintainable, and even cross-platform skills.
allowed-tools and Shell RedirectsDirectly allowing a tool like tar or gzip that requires output redirection can fail because the parser for allowed-tools may not correctly handle the special characters. This leads to the agent getting stuck and unable to proceed.
Instead of fighting with the syntax, we can move the complex logic into a script (e.g., a TypeScript script run with Bun). This approach offers several advantages:
bun run scripts/compress.ts), which is a much simpler command for allowed-tools to handle.tar-fs), you can replace system-specific commands (like tar) with JavaScript implementations that work consistently across Windows, macOS, and Linux.tar command with a redirect fails the allowed-tools permission step in our compress skill.SKILL.md file to remove the direct tar command and replace it with a call to our new script, updating the allowed-tools accordingly (e.g., Bash(bun run scripts:*)).tar-fs) instead of relying on the system's tar command, making the skill universally usable.This lesson provides a practical pattern for building sophisticated and reliable Claude Code skills, moving beyond simple CLI commands to create powerful, script-driven automations that work seamlessly with the skill stacking patterns learned in previous lessons.
Please create a summary of @index.ts and then compress the summary.
tar -czvf <project-root>/compressions/<timestamp>-<file-name-based-on-the-user-request>.tar.gz <files-from-user-request>
---
Please convert this into a script in my scripts folder that uses bun and TypeScript that accepts arguments and avoids redirects
We don't need this script to be executable, we will be running it with bun.
In my @.claude/skills/compress/SKILL.md Please replace any references to tar with our script.
Please remove the timestamp logic from our script. We are still going to rely on the timestamp skill to format the name of the output compressed file
Please update our skill to represent that we're still using the timestamp skill and that our output name will have a timestamp in it just like we had before.
@compress.ts Without changing any of the logic of the script, please install the necessary packages to make this cross-platform so that anyone on Windows, Linux, or Mac can all run this same script.
node
zsh
chmod +x /Users/johnlindquist/dev/claude-lessons/scripts/compress.ts
bun run scripts/compress.ts 2025-10-23-10-13-37-index-summary.tar.gz index-summary.md
bun init
bun add tar-fs
bun run scripts/compress.ts test-archive.tar.gz compress.ts
The initial compress skill's SKILL.md file uses a tar command that causes issues with allowed-tools:
---
name: compress
description: Compress the resulting files of a user's request
allowed-tools: [Write, Bash(tar:*)]
---
## Requirements
You must use the timestamp skill to create the timestamp.
## Usage
```bash
tar -czvf <project-root>/compressions/<timestamp>-<file-name-based-on-the-user-request>.tar.gz <files-from-user-request>
```
After creating the script, we update the allowed-tools to reference our script instead:
# allowed-tools syntax for running scripts
allowed-tools: Write, Bash(bun run scripts/compress.ts:*)
// Initial system-dependent tar command in script
await $`tar -czf ${outputPath} ${filesToCompress}`;
[00:00] For our compress skill, if we attempt to use gzip, where the syntax requires a character like a redirect character, and then we try to run our same prompt that we've run in the past, it'll create the summary as usual, we'll allow this, it'll grab the compress skill, then probably grab the timestamp skill. There it is. Now even though we've allowed the gzip tool, characters like the redirect character will interfere with how allowed tools parses these commands. And unfortunately there is no workaround to allow any tools that require redirects. So your agent will always hang on permissions for gzip unless you turn on dangerously skip permissions.
[00:41] So what I recommend is we'll exit out of this and I'll start a new session. I'm going to grab this command, paste it in here, and then I always insert a markdown line break whenever I'm separating commands and sections. That's just a personal preference. But I'm going to tell it to please convert this into a script in my scripts folder that uses bun and typescript that accepts arguments and avoids redirects. So we'll let it generate this script for us and I'm going to allow it to make this script.
[01:11] We actually don't need this to be executable, we're going to run it with bun. So I'll hit escape. We don't need this script to be executable. We will be running it with bun. Then let it continue and then I'll tell it in my skill for compress please replace any references to gzip with our script and then I'll allow these changes and let me check on the work so far.
[01:32] Let's check on compress. I should have checked before but this is creating a timestamp which we don't want and it is using tar which I'm fine with. The goal here is to show extracting tools into scripts and tar is using gzip compression anyway, but I will tell it to please remove the timestamp logic from our script. We are still going to rely on the timestamp skill to format the name of the output compressed file. And then just to get rid of the red squiggly we could tell Clod to type check our script and fix any errors or since cursor is already showing us the error we can go to view problem and either copy the problem from here or what I like to do is open the problems panel and then if you select this with command A and command C you can essentially copy and paste the content of the problem with the file path and all the information which really helps Claude zero in on the issues.
[02:34] So this is just one of those tricks I use if a file has multiple problems or warnings that have come up during a refactor. Since tools like Cursor and VS Code have these in line you can jump to the Problems tab, select them all, copy and paste and drop them in, then just hit enter and it'll take care of all those problems for you. So let's close the problems. So now it has a type guard and the red squiggly is gone. So with all of that taken care of our skill, we'll now use bun run scripts with our compress And we still need to restore some of the skill verbiage around timestamps.
[03:06] So I'll say please update our skill to represent that we're still using the timestamp skill and that our output name will have a timestamp in it just like we had before. This could have all been avoided with a better prompt up front and this change looks good, but instead of spending too much time planning or thinking of the perfect prompt up front, my approach is usually to give it a try, see what happens, and if it gets out of control, I'll start over with a better prompt up front. All of the issues that came along in this refactor were all things that I felt in control of. So now this looks much better. It even added an example section which I'm fine with.
[03:41] It didn't quite understand the allowed tool syntax. I'm going to change this to bun run scripts or bun run scripts colon asterisk. And this means that this tool is allowed to run any scripts from our scripts directory. So now if we exit out, and I want to quickly discard the previous summary just so we don't worry about that being there during the summarization step. And I'll start up Clot again and this time once I run our previous prompt it'll grab the compress skill, the timestamp skill, create the date, it'll run our script, and compress the file as we want it.
[04:21] Where the main takeaway here is as you start dealing with more advanced tool usage, whether it's how complex arguments get to the tools themselves or issues with permissions in allowed tools, it becomes much easier just to create a scripts directory and choose your preferred runtime for running scripts and then just build those out from there. What this also allows us is if I go to our compress script again you can see that this is running tar from the system because this syntax is expecting tar to exist. And we can create scripts where if I go into my scripts directory, I bun and knit, create a blank project, then fire up Cloud. We'll make sure compress is referenced, and I'll say without changing any of the logic of the script please install the necessary packages to make this cross-platform so that anyone on Windows, Linux, or Mac can all run the same script. And then when you're building out these tools inside of these skills, if you have people working across different machines, you'll have much better luck supporting them rather than hard coding in tools that are specific to a system.
[05:26] So let bun add this package, and we'll allow all writes. I'll just shift tab on that. And looks like it just wants to test it. We'll allow it to test it, that's fine. All right so this is all tested and verified and if we look at the script you'll see now it's not calling tar anymore.
[05:43] It's actually using a tar package. And one of the beauties of this is we'll have much better control over logging and error handling for when these skills and tools inevitably fail based on input or based on agents misunderstanding the tools, which essentially gives us a fourth layer beyond the skill where this section would load the body, which then could load references to other files, which then run scripts, which then can push in more context from logs or errors or anything we need to handle to make sure that our skills run successfully.