aiblueprint-cli 1.4.60 → 1.4.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,15 @@
5
5
  approval_policy = "never"
6
6
  sandbox_mode = "danger-full-access"
7
7
 
8
+ [[hooks.PreToolUse]]
9
+ matcher = "^Bash$"
10
+
11
+ [[hooks.PreToolUse.hooks]]
12
+ type = "command"
13
+ command = "/usr/bin/env node \"$HOME/.codex/hooks/command-deny-list.ts\""
14
+ timeout = 5
15
+ statusMessage = "Checking command safety"
16
+
8
17
  [tui]
9
18
  status_line = [
10
19
  "model-with-reasoning",
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+
7
+ type HookPayload = {
8
+ tool_input?: {
9
+ command?: unknown;
10
+ };
11
+ };
12
+
13
+ type ClaudeSettings = {
14
+ permissions?: {
15
+ deny?: unknown;
16
+ };
17
+ };
18
+
19
+ const CLAUDE_SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
20
+ const FALLBACK_DENY_PATTERNS = [
21
+ "Bash(rm -rf *)",
22
+ "Bash(sudo *)",
23
+ "Bash(mkfs *)",
24
+ "Bash(dd *)",
25
+ "Bash(git push --force *)",
26
+ "Bash(git reset --hard *)",
27
+ "Bash(*prisma reset*)",
28
+ "Bash(curl * | bash)",
29
+ "Bash(wget * | bash)",
30
+ ];
31
+
32
+ const BLOCK_REASON =
33
+ "Blocked by Codex command safety rules. Use a safer alternative or ask the user.";
34
+ const TOKEN_BOUNDARY = String.raw`(?:^|[\s"'([{<])`;
35
+ const GH_COMMAND_PREFIX = String.raw`(?:(?:command|builtin|env)\s+(?:[A-Za-z_][A-Za-z0-9_]*=(?:\S+)\s+)*)?`;
36
+ const EXECUTABLE_PATH_PREFIX = String.raw`(?:\S*/)?`;
37
+
38
+ function getCommand(payload: HookPayload): string {
39
+ const command = payload.tool_input?.command;
40
+ return typeof command === "string" ? command : "";
41
+ }
42
+
43
+ function readClaudeDenyPatterns(): string[] {
44
+ try {
45
+ const settings = JSON.parse(
46
+ readFileSync(CLAUDE_SETTINGS_PATH, "utf8"),
47
+ ) as ClaudeSettings;
48
+ const deny = settings.permissions?.deny;
49
+
50
+ if (Array.isArray(deny)) {
51
+ return deny.filter((pattern): pattern is string => typeof pattern === "string");
52
+ }
53
+ } catch {
54
+ return FALLBACK_DENY_PATTERNS;
55
+ }
56
+
57
+ return FALLBACK_DENY_PATTERNS;
58
+ }
59
+
60
+ function extractBashPattern(pattern: string): string | null {
61
+ const match = pattern.match(/^Bash\((.*)\)$/);
62
+ return match?.[1] ?? null;
63
+ }
64
+
65
+ function escapeRegExp(value: string): string {
66
+ return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
67
+ }
68
+
69
+ function globPatternToRegExp(pattern: string): RegExp {
70
+ const normalized = normalizeWhitespace(pattern);
71
+ const regex = normalized
72
+ .split("*")
73
+ .map(escapeRegExp)
74
+ .join(".*")
75
+ .replaceAll(" ", "\\s+");
76
+
77
+ return new RegExp(`^${regex}$`, "i");
78
+ }
79
+
80
+ function normalizeWhitespace(value: string): string {
81
+ return value.replace(/\\\n/g, " ").replace(/\s+/g, " ").trim();
82
+ }
83
+
84
+ function commandCandidates(command: string): string[] {
85
+ const normalized = normalizeWhitespace(command);
86
+ const segments = normalized
87
+ .split(/\s*(?:&&|\|\||;)\s*/g)
88
+ .map((segment) => segment.trim())
89
+ .filter(Boolean);
90
+
91
+ return [...new Set([normalized, ...segments])];
92
+ }
93
+
94
+ function containsForcedRecursiveRm(command: string): boolean {
95
+ const rmInvocations = normalizeWhitespace(command).matchAll(
96
+ /\brm\b(?<args>[^\n;&|()]*)/gi,
97
+ );
98
+
99
+ for (const match of rmInvocations) {
100
+ const args = match.groups?.args ?? "";
101
+ const shortOptions = [...args.matchAll(/(?<!\S)-([A-Za-z]+)\b/g)].map(
102
+ (optionMatch) => optionMatch[1] ?? "",
103
+ );
104
+
105
+ let hasRecursive = shortOptions.some((option) =>
106
+ option.toLowerCase().includes("r"),
107
+ );
108
+ let hasForce = shortOptions.some((option) =>
109
+ option.toLowerCase().includes("f"),
110
+ );
111
+
112
+ const longOptions = new Set(
113
+ [...args.matchAll(/--([A-Za-z-]+)\b/g)].map(
114
+ (optionMatch) => optionMatch[1] ?? "",
115
+ ),
116
+ );
117
+
118
+ hasRecursive ||= longOptions.has("recursive") || longOptions.has("dir");
119
+ hasForce ||= longOptions.has("force");
120
+
121
+ if (hasRecursive && hasForce) {
122
+ return true;
123
+ }
124
+ }
125
+
126
+ return false;
127
+ }
128
+
129
+ function containsPullRequestMergeCommand(command: string): boolean {
130
+ const matcher = new RegExp(
131
+ `${TOKEN_BOUNDARY}${GH_COMMAND_PREFIX}${EXECUTABLE_PATH_PREFIX}gh\\s+pr\\s+merge(?:\\s|$)`,
132
+ "i",
133
+ );
134
+
135
+ return commandCandidates(command).some((candidate) => matcher.test(candidate));
136
+ }
137
+
138
+ function matchesImportedDenyPattern(command: string, patterns: string[]): boolean {
139
+ const candidates = commandCandidates(command);
140
+
141
+ for (const pattern of patterns) {
142
+ const bashPattern = extractBashPattern(pattern);
143
+ if (!bashPattern) {
144
+ continue;
145
+ }
146
+
147
+ const matcher = globPatternToRegExp(bashPattern);
148
+ if (candidates.some((candidate) => matcher.test(candidate))) {
149
+ return true;
150
+ }
151
+ }
152
+
153
+ return false;
154
+ }
155
+
156
+ function deny(): void {
157
+ process.stdout.write(
158
+ JSON.stringify({
159
+ hookSpecificOutput: {
160
+ hookEventName: "PreToolUse",
161
+ permissionDecision: "deny",
162
+ permissionDecisionReason: BLOCK_REASON,
163
+ },
164
+ decision: "block",
165
+ reason: BLOCK_REASON,
166
+ }),
167
+ );
168
+ }
169
+
170
+ async function readStdin(): Promise<string> {
171
+ const chunks: Buffer[] = [];
172
+
173
+ for await (const chunk of process.stdin) {
174
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
175
+ }
176
+
177
+ return Buffer.concat(chunks).toString("utf8");
178
+ }
179
+
180
+ async function main(): Promise<void> {
181
+ const input = await readStdin();
182
+ if (!input.trim()) {
183
+ return;
184
+ }
185
+
186
+ let payload: HookPayload;
187
+ try {
188
+ payload = JSON.parse(input) as HookPayload;
189
+ } catch {
190
+ return;
191
+ }
192
+
193
+ const command = getCommand(payload);
194
+ if (
195
+ containsForcedRecursiveRm(command) ||
196
+ containsPullRequestMergeCommand(command) ||
197
+ matchesImportedDenyPattern(command, readClaudeDenyPatterns())
198
+ ) {
199
+ deny();
200
+ }
201
+ }
202
+
203
+ await main();
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: environments-manager
3
- description: Set up a per-worktree environment for one or more IDEs (Claude Code, Cursor, Codex) in a single pass. Generates shared scripts/worktree-up.sh, scripts/worktree-down.sh, and scripts/dev.sh, then wires them into each selected IDE's config. Use whenever the user asks to "set up a worktree environment", "configure worktrees", "make this repo worktree-ready", or mentions any of .codex/environments/environment.toml, .cursor/worktrees.json, .claude/settings.json SessionStart, $CODEX_WORKTREE_PATH, $ROOT_WORKTREE_PATH, or $CLAUDE_PROJECT_DIR.
3
+ description: Set up per-worktree environments for Claude Code, Cursor, or Codex. Use for worktree-ready repos, IDE environment config, worktree-up/down scripts, or dev.sh wiring.
4
4
  disable-model-invocation: false
5
5
  allow_implicit_invocation: true
6
6
  ---
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: rules-manager
3
- description: Create and maintain agent rules in AGENTS.md and .agents/rules/. Use when adding project rules, conventions, or constraints for AI agents. AGENTS.md acts as the index - it must reference every rule file living in .agents/rules/. Triggers - "create a rule", "add a rule", "agents rule", "AGENTS.md", ".agents/rules", "new rule for", "rules-manager".
4
- argument-hint: [init | add <rule-name> | optimize | task description]
3
+ description: Create and maintain agent rules in AGENTS.md and .agents/rules/. Use for project rules, conventions, constraints, rule indexes, or requests to add or optimize agent rules.
4
+ argument-hint: "[init | add <rule-name> | optimize | task description]"
5
5
  ---
6
6
 
7
7
  <core_principle>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: skill-manager
3
- description: Create and edit skills/rules across Claude Code, OpenAI Codex, and Cursor. Use when the user asks to "create a skill", "build a skill", "write a skill", "make a cursor rule", "add a codex skill", "skill manager", or mentions SKILL.md, .cursor/rules, agents/openai.yaml, or multi-agent rule files. Handles frontmatter, file layout, and discovery rules for each platform.
3
+ description: Create or edit Claude, Codex, and Cursor skills/rules. Use for SKILL.md, .cursor/rules, AGENTS.md, skill prompts, frontmatter, references, scripts, and discovery rules.
4
4
  ---
5
5
 
6
6
  # Skill Manager
@@ -18,6 +18,7 @@ Pick the platform first, then read the matching reference file:
18
18
  - Claude Code → see [references/claude-code.md](references/claude-code.md)
19
19
  - Codex → see [references/codex.md](references/codex.md)
20
20
  - Cursor → see [references/cursor.md](references/cursor.md)
21
+ - Description quality → see [references/description-recommandation.md](references/description-recommandation.md)
21
22
 
22
23
  If unsure which the user wants, ask. Default to Claude Code when working under `~/.claude/` or `.claude/`, Codex when under `~/.agents/` or `.agents/`, Cursor when under `.cursor/`.
23
24
 
@@ -65,15 +66,32 @@ Do NOT add README, CHANGELOG, INSTALLATION_GUIDE, etc. Only files the agent need
65
66
 
66
67
  ## Writing the description
67
68
 
68
- The `description` field is the single most important line in the file - it's what the model uses to decide to load the skill. Write it as:
69
+ The `description` field is the single most important line in the file - it's what the model uses to decide to load the skill. This is a hard rule: descriptions must be between 50 and 300 characters.
70
+
71
+ Write it as:
69
72
 
70
73
  - Front-load trigger words ("Use when...", "create a skill", "review a PR").
71
74
  - State scope and boundaries explicitly.
72
75
  - Mention concrete file names or commands the user might type.
76
+ - Keep it short enough to stay under 300 characters, while still specific enough to trigger reliably.
73
77
 
74
78
  Bad: `"Helper for skills."`
75
79
  Good: `"Create and edit skills/rules across Claude Code, OpenAI Codex, and Cursor. Use when the user asks to 'create a skill'..."`
76
80
 
81
+ To validate skill descriptions, frontmatter fields, `agents/openai.yaml`, and skill directory shape, run:
82
+
83
+ ```bash
84
+ bun scripts/inspect-description.ts
85
+ ```
86
+
87
+ By default, this checks `~/.agents/skills` only and follows symlinked skill directories inside that root. Pass one or more roots to inspect a different set. Passing a `.cursor/rules` directory validates Cursor rule frontmatter too:
88
+
89
+ ```bash
90
+ bun scripts/inspect-description.ts .agents/skills
91
+ ```
92
+
93
+ For researched guidance and examples, see [references/description-recommandation.md](references/description-recommandation.md).
94
+
77
95
  ## Where each platform stores skills
78
96
 
79
97
  See the per-platform reference files. Quick reminders:
@@ -0,0 +1,97 @@
1
+ # Description Recommendations
2
+
3
+ Use this when writing or refining the `description` field for Claude Code skills, OpenAI Codex skills, or Cursor intelligent rules.
4
+
5
+ ## Sources
6
+
7
+ - OpenAI Codex Agent Skills: https://developers.openai.com/codex/skills
8
+ - Anthropic Agent Skills best practices: https://console.anthropic.com/docs/en/agents-and-tools/agent-skills/best-practices
9
+ - Anthropic Agent Skills overview: https://console.anthropic.com/docs/en/agents-and-tools/agent-skills/overview
10
+ - Claude Code skills docs: https://docs.anthropic.com/en/docs/claude-code/skills
11
+ - Cursor rules docs: https://cursor.com/help/customization/rules
12
+
13
+ ## Hard Rules
14
+
15
+ - Keep every description between 50 and 300 characters.
16
+ - Make the first clause useful on its own; skill lists may shorten descriptions.
17
+ - Include both what the skill does and when it should be used.
18
+ - Use third person. Do not write "I can..." or "You can...".
19
+ - Do not include XML tags, markdown tables, long examples, or implementation steps.
20
+ - Do not duplicate the full SKILL.md body. The description is only discovery metadata.
21
+
22
+ ## What Good Descriptions Do
23
+
24
+ A good description helps the model decide whether to load the skill before it has read the skill body. It should answer three questions:
25
+
26
+ 1. What capability does this skill provide?
27
+ 2. Which user requests, file names, commands, or domain terms should trigger it?
28
+ 3. What boundary keeps it from triggering on adjacent but wrong tasks?
29
+
30
+ Prefer concrete nouns, verbs, and file names over generic labels. Use terms a user would actually type: `SKILL.md`, `.cursor/rules`, `commit message`, `invoice`, `wrangler`, `Netlify deploy`, `DOCX`, `.xlsx`.
31
+
32
+ ## Recommended Shape
33
+
34
+ Use one or two compact sentences:
35
+
36
+ ```yaml
37
+ description: <Primary capability>. Use when <trigger phrases, file types, commands, or task context>.
38
+ ```
39
+
40
+ For reference-only skills:
41
+
42
+ ```yaml
43
+ description: <Domain conventions or reference material>. Use when working on <specific files, modules, APIs, or workflows>.
44
+ ```
45
+
46
+ For side-effecting workflows, be narrower:
47
+
48
+ ```yaml
49
+ description: Prepare and verify production deploys. Use only when the user explicitly asks to deploy, release, publish, or check deployment status.
50
+ ```
51
+
52
+ ## Examples
53
+
54
+ Good:
55
+
56
+ ```yaml
57
+ description: Create or edit Claude, Codex, and Cursor skills/rules. Use for SKILL.md, .cursor/rules, AGENTS.md, frontmatter, references, scripts, and discovery rules.
58
+ ```
59
+
60
+ Good:
61
+
62
+ ```yaml
63
+ description: Review GitHub pull request feedback and implement requested changes. Use when the user asks to address PR comments, review threads, or requested changes.
64
+ ```
65
+
66
+ Too vague:
67
+
68
+ ```yaml
69
+ description: Helps with skills.
70
+ ```
71
+
72
+ Too broad:
73
+
74
+ ```yaml
75
+ description: Manage all developer workflow tasks, docs, rules, automation, repository work, deployment, testing, and issue handling.
76
+ ```
77
+
78
+ Wrong point of view:
79
+
80
+ ```yaml
81
+ description: I can help you write better skill descriptions.
82
+ ```
83
+
84
+ ## Checklist
85
+
86
+ - [ ] 50-300 characters.
87
+ - [ ] First words contain the primary trigger.
88
+ - [ ] Mentions the key files, commands, domains, or user phrases.
89
+ - [ ] Says when to use it, not only what it is.
90
+ - [ ] Specific enough to avoid accidental invocation.
91
+ - [ ] No body-level instructions, setup steps, or long examples.
92
+
93
+ Run the local inspector after editing:
94
+
95
+ ```bash
96
+ bun scripts/inspect-description.ts
97
+ ```