@softspark/ai-toolkit 2.3.1 → 2.4.1
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.
- package/CHANGELOG.md +33 -0
- package/README.md +8 -8
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +4 -4
- package/app/rules/common/coding-style.md +16 -2
- package/bin/ai-toolkit.js +44 -13
- package/kb/reference/architecture-overview.md +4 -4
- package/kb/reference/cli-reference.md +4 -4
- package/kb/reference/codex-cli-compatibility.md +4 -0
- package/kb/reference/extension-api.md +24 -13
- package/llms-full.txt +33 -18
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/config_resolver.py +23 -5
- package/scripts/doctor.py +73 -1
- package/scripts/hook_sources.py +109 -0
- package/scripts/inject_hook_cli.py +230 -25
- package/scripts/install.py +38 -4
- package/scripts/install_steps/ai_tools.py +78 -15
- package/scripts/install_steps/install_state.py +25 -0
- package/scripts/install_steps/markers.py +42 -0
- package/scripts/install_steps/project_registry.py +9 -0
- package/scripts/paths.py +1 -0
- package/scripts/propagate_global.py +92 -0
- package/scripts/rule_sources.py +5 -28
- package/scripts/update_projects.py +7 -1
- package/scripts/url_fetch.py +50 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v2.4.1 — Codex Global Install, Security Hardening & Editor Tracking (2026-04-15)
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Codex hook propagation** — `inject-hook` auto-propagates Codex-compatible events (`SessionStart`, `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`) to `~/.codex/hooks.json`. `remove-hook` cleans both targets.
|
|
14
|
+
- **Global editor tracking** — `ai-toolkit install --editors codex` installs editors globally (opt-in). Tracked in `state.json`, auto-refreshed on `update`. Default: Claude only.
|
|
15
|
+
- **Per-project editor tracking** — `install --local --editors` records editors in `projects.json`. `update` re-installs saved editors per project.
|
|
16
|
+
- **Auto-propagation** — `add-rule`, `remove-rule`, `mcp add` auto-propagate to globally installed editors via `propagate_global.py`.
|
|
17
|
+
- **Doctor Check 9** — URL hook sources health check with `--fix` re-fetch support.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- **Tarball path traversal** — `_extract_tarball` rejects symlinks, absolute paths, and paths escaping dest directory.
|
|
21
|
+
- **Git clone URL validation** — `_resolve_git` enforces HTTPS-only, rejects `file://`, `ssh://`.
|
|
22
|
+
- **`dr.error()` crash** — `DiagResult` has no `error()` method; fixed to `dr.fail()`.
|
|
23
|
+
- **`refresh_url_hooks` missing timestamp update** — now calls `register_url_source` after re-fetch.
|
|
24
|
+
- **Defense-in-depth name validation** — `register_url_source()` rejects path traversal chars in both `hook_sources` and `rule_sources`.
|
|
25
|
+
- **Unused `execSync` import** — removed from CLI entry point.
|
|
26
|
+
- **URL truncation detection** — `url_fetch.py` raises `ValueError` when response exceeds 10MB.
|
|
27
|
+
- **MCP templates path** — corrected `app/plugins/mcp-templates/` → `app/mcp-templates/` in ARCHITECTURE docs.
|
|
28
|
+
- **MCP templates header count** — corrected 25 → 26 in `app/ARCHITECTURE.md`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## v2.4.0 — URL Hook Injection & Karpathy Coding Rules (2026-04-15)
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- **URL hook injection** — `ai-toolkit inject-hook https://...` fetches, caches, and injects hooks from HTTPS URLs. Cached in `~/.softspark/ai-toolkit/hooks/external/`, auto-refreshed on every `update`. `remove-hook` also unregisters URL source and cleans cache.
|
|
36
|
+
- **Shared URL fetch module** — extracted `url_fetch.py` from `rule_sources.py` for reuse by both rule and hook URL sources.
|
|
37
|
+
- **Hook URL source registry** — `hook_sources.py` tracks URL-sourced hooks in `sources.json` (analogous to `rule_sources.py`).
|
|
38
|
+
- **Surgical Changes rule** — orphan cleanup protocol, match existing style, don't touch adjacent code (inspired by Karpathy's LLM coding guidelines).
|
|
39
|
+
- **Goal-Driven Execution rule** — `step → verify: check` pattern for multi-step tasks, strong success criteria before looping.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
10
43
|
## v2.3.1 — Release Quality Gate (2026-04-14)
|
|
11
44
|
|
|
12
45
|
### Fixed
|
package/README.md
CHANGED
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](app/skills/)
|
|
8
8
|
[](app/agents/)
|
|
9
|
-
[](tests/)
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
## What's New in v2.
|
|
13
|
+
## What's New in v2.4.1
|
|
14
14
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
15
|
+
- **Global editor tracking** — `ai-toolkit install --editors codex` installs Codex globally, tracked in `state.json`, auto-refreshed on `update`
|
|
16
|
+
- **Codex hook propagation** — `inject-hook` auto-propagates to `~/.codex/hooks.json` for Codex-compatible events
|
|
17
|
+
- **Auto-propagation** — `add-rule`, `remove-rule`, `mcp add` auto-propagate to all globally installed editors
|
|
18
|
+
- **Security hardening** — tarball path traversal fix, git URL scheme validation, defense-in-depth name validation
|
|
19
|
+
- **Doctor Check 9** — URL hook sources health check with `--fix` re-fetch support
|
|
20
20
|
|
|
21
21
|
See [CHANGELOG.md](CHANGELOG.md) for full history.
|
|
22
22
|
|
|
@@ -143,7 +143,7 @@ ai-toolkit/
|
|
|
143
143
|
│ └── ARCHITECTURE.md # Full system design
|
|
144
144
|
├── kb/ # Reference docs, procedures, plans
|
|
145
145
|
├── scripts/ # Validation, install, evaluation scripts
|
|
146
|
-
├── tests/ # Bats test suite (
|
|
146
|
+
├── tests/ # Bats test suite (618 tests)
|
|
147
147
|
└── CHANGELOG.md
|
|
148
148
|
```
|
|
149
149
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-toolkit",
|
|
3
3
|
"description": "Professional-grade Claude Code toolkit with persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.4.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SoftSpark",
|
|
7
7
|
"url": "https://github.com/softspark"
|
package/app/ARCHITECTURE.md
CHANGED
|
@@ -301,16 +301,16 @@ Lead Session (You)
|
|
|
301
301
|
|
|
302
302
|
## Extension Points
|
|
303
303
|
|
|
304
|
-
### MCP Templates (
|
|
305
|
-
`app/
|
|
304
|
+
### MCP Templates (26)
|
|
305
|
+
`app/mcp-templates/` ships 26 ready-to-use MCP server config templates (filesystems, databases, GitHub, Slack, etc.). Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
|
|
306
306
|
|
|
307
307
|
### Language Rules (68 files, 13 languages)
|
|
308
308
|
`app/rules/` contains per-language coding rules. Supported languages: TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, C++, Ruby, and common (shared). Auto-detected from project files via `--auto-detect` or selected with `--modules rules-<lang>`.
|
|
309
309
|
|
|
310
310
|
Language rules are propagated to **all configured editors** — not just Claude. `dir_rules_shared.build_language_rules()` reads `app/rules/<lang>/*.md`, strips frontmatter, and returns combined content per language. Each directory-based generator (Cursor, Windsurf, Cline, Roo, Augment, Antigravity, Codex) emits `ai-toolkit-lang-<lang>` files in its native format. Registered custom rules (`~/.softspark/ai-toolkit/rules/`) are similarly propagated as `ai-toolkit-custom-<name>` files via `build_registered_rules()`.
|
|
311
311
|
|
|
312
|
-
### Extension API (`inject-hook`)
|
|
313
|
-
The `inject_section_cli.py` script provides a stable marker-based injection API. Any tool can add sections to `CLAUDE.md`, `constitution.md`, or `ARCHITECTURE.md` without overwriting user content, using `<!-- TOOLKIT:START:<id> -->` / `<!-- TOOLKIT:END:<id> -->` markers.
|
|
312
|
+
### Extension API (`inject-hook`, `inject-rule`)
|
|
313
|
+
The `inject_section_cli.py` script provides a stable marker-based injection API. Any tool can add sections to `CLAUDE.md`, `constitution.md`, or `ARCHITECTURE.md` without overwriting user content, using `<!-- TOOLKIT:START:<id> -->` / `<!-- TOOLKIT:END:<id> -->` markers. `inject_hook_cli.py` injects hooks into `settings.json` with `_source` tags — supports both local files and HTTPS URLs (cached in `~/.softspark/ai-toolkit/hooks/external/`, auto-refreshed on `update`). Shared URL fetch logic lives in `url_fetch.py`.
|
|
314
314
|
|
|
315
315
|
### SLM Compilation (`compile-slm`)
|
|
316
316
|
`scripts/compile_slm.py` compiles the full toolkit (20K+ tokens) into a minimal system prompt for Small Language Models (2K-16K tokens). Pipeline: Parse → Score → Compress → Pack → Emit. Supports 4 compression levels (ultra-light, light, standard, extended), 4 output formats (raw, ollama, json-string, aider), persona-aware scoring, and language-aware rule filtering. Profile `offline-slm` in `manifest.json`. Constitution is always included (non-negotiable).
|
|
@@ -7,11 +7,12 @@ version: "1.0.0"
|
|
|
7
7
|
# Universal Coding Style
|
|
8
8
|
|
|
9
9
|
## Principles
|
|
10
|
-
- KISS: simplest solution that works. Clever code is a liability.
|
|
10
|
+
- KISS: simplest solution that works. Clever code is a liability. If 200 lines could be 50, rewrite.
|
|
11
11
|
- DRY: extract when you repeat 3+ times, not before.
|
|
12
|
-
- YAGNI: do not build features "just in case."
|
|
12
|
+
- YAGNI: do not build features "just in case." No abstractions for single-use code.
|
|
13
13
|
- Prefer immutability: use `const`, `final`, `val`, `let` by default.
|
|
14
14
|
- Fail fast: validate inputs at boundaries, return early on errors.
|
|
15
|
+
- State assumptions before coding. If uncertain or multiple interpretations exist, ask — don't pick silently.
|
|
15
16
|
|
|
16
17
|
## Naming
|
|
17
18
|
- Use descriptive names that reveal intent (`remainingRetries`, not `r`).
|
|
@@ -45,6 +46,19 @@ version: "1.0.0"
|
|
|
45
46
|
- Max line length: 80-120 characters depending on language convention.
|
|
46
47
|
- Trailing commas in multi-line structures (where language supports).
|
|
47
48
|
|
|
49
|
+
## Surgical Changes
|
|
50
|
+
- Touch only what the task requires. Every changed line should trace to the request.
|
|
51
|
+
- Match existing style, even if you would do it differently.
|
|
52
|
+
- Do not "improve" adjacent code, comments, or formatting unprompted.
|
|
53
|
+
- Orphan cleanup: remove imports/variables/functions that YOUR changes made unused.
|
|
54
|
+
- Do not remove pre-existing dead code unless explicitly asked.
|
|
55
|
+
|
|
56
|
+
## Goal-Driven Execution
|
|
57
|
+
- Transform vague tasks into verifiable goals before starting.
|
|
58
|
+
- For multi-step work, state a brief plan with verification per step:
|
|
59
|
+
`1. [Step] → verify: [check]`
|
|
60
|
+
- Strong success criteria enable independent looping. Weak criteria ("make it work") require clarification — ask first.
|
|
61
|
+
|
|
48
62
|
## Anti-Patterns to Avoid
|
|
49
63
|
- God classes/modules with 500+ lines and multiple responsibilities.
|
|
50
64
|
- Deep nesting (>3 levels): use early returns and extract functions.
|
package/bin/ai-toolkit.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const { execFileSync, spawnSync
|
|
4
|
+
const { execFileSync, spawnSync } = require('child_process');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
|
|
@@ -65,8 +65,8 @@ const COMMANDS = {
|
|
|
65
65
|
uninstall: 'Remove ai-toolkit from ~/.claude/',
|
|
66
66
|
'add-rule': 'Register a rule file or URL in ~/.softspark/ai-toolkit/rules/ (URL rules auto-refresh on update)',
|
|
67
67
|
'remove-rule': 'Unregister a rule from ~/.softspark/ai-toolkit/rules/ and remove its block from CLAUDE.md',
|
|
68
|
-
'inject-hook': 'Inject external hooks into ~/.claude/settings.json (
|
|
69
|
-
'remove-hook': 'Remove injected hooks by source name from ~/.claude/settings.json',
|
|
68
|
+
'inject-hook': 'Inject external hooks (file or URL) into ~/.claude/settings.json (URL hooks auto-refresh on update)',
|
|
69
|
+
'remove-hook': 'Remove injected hooks by source name from ~/.claude/settings.json (also unregisters URL source)',
|
|
70
70
|
validate: 'Verify toolkit integrity',
|
|
71
71
|
doctor: 'Check install health, hooks, and artifact drift',
|
|
72
72
|
eject: 'Export standalone config (no symlinks, no toolkit dependency)',
|
|
@@ -151,6 +151,23 @@ function run(script, args = [], opts = {}) {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Propagate changes to globally installed editors (from state.json).
|
|
156
|
+
* Silently skips if no global editors are configured.
|
|
157
|
+
* @param {...string} flags - Flags to pass: --rules, --hooks, --mcp
|
|
158
|
+
*/
|
|
159
|
+
function propagateGlobal(...flags) {
|
|
160
|
+
const result = spawnSync('python3', [scriptPath('propagate_global.py'), ...flags], {
|
|
161
|
+
stdio: 'inherit',
|
|
162
|
+
cwd: CWD,
|
|
163
|
+
env: { ...process.env },
|
|
164
|
+
});
|
|
165
|
+
// Non-fatal — propagation failure shouldn't block the primary operation
|
|
166
|
+
if (result.status !== 0) {
|
|
167
|
+
console.error('Warning: global editor propagation had issues (non-fatal)');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
154
171
|
/**
|
|
155
172
|
* Generic dispatcher for SCRIPT_COMMANDS entries.
|
|
156
173
|
* Resolves the script path and selects the correct cwd.
|
|
@@ -226,10 +243,11 @@ function showHelp() {
|
|
|
226
243
|
console.log(' <rule-name> Name of rule to unregister (filename without .md)');
|
|
227
244
|
console.log(' [target-dir] Target dir containing .claude/CLAUDE.md (default: $HOME)');
|
|
228
245
|
console.log('\nOptions for inject-hook:');
|
|
229
|
-
console.log(' <hooks-file>
|
|
230
|
-
console.log(' [
|
|
246
|
+
console.log(' <hooks-file-or-url> Path to JSON file or HTTPS URL with {"hooks": {"EventName": [...]}}');
|
|
247
|
+
console.log(' [hook-name] Override source name (default: filename/URL stem)');
|
|
248
|
+
console.log(' [target-dir] Target dir containing .claude/settings.json (default: $HOME)');
|
|
231
249
|
console.log('\nOptions for remove-hook:');
|
|
232
|
-
console.log(' <source-name> Source tag to remove (
|
|
250
|
+
console.log(' <source-name> Source tag to remove (also unregisters URL source if present)');
|
|
233
251
|
console.log(' [target-dir] Target dir containing .claude/settings.json (default: $HOME)');
|
|
234
252
|
console.log('\nOptions for add-rule:');
|
|
235
253
|
console.log(' <rule-file> Path to .md rule file or HTTPS URL to register globally');
|
|
@@ -330,6 +348,7 @@ function handleRemoveRule(args) {
|
|
|
330
348
|
}
|
|
331
349
|
const targetDir = args[1] || process.env.HOME;
|
|
332
350
|
run(scriptPath('remove_rule.py'), [ruleName, targetDir]);
|
|
351
|
+
propagateGlobal('--rules');
|
|
333
352
|
}
|
|
334
353
|
|
|
335
354
|
/**
|
|
@@ -344,24 +363,32 @@ function handleAddRule(args) {
|
|
|
344
363
|
}
|
|
345
364
|
// Pass URLs through directly (don't resolve as filesystem path)
|
|
346
365
|
const isUrl = ruleFile.startsWith('https://') || ruleFile.startsWith('http://');
|
|
366
|
+
if (ruleFile.startsWith('http://')) {
|
|
367
|
+
console.error('Error: only HTTPS URLs are supported. Use https:// for security.');
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
347
370
|
const absRuleFile = isUrl ? ruleFile : path.resolve(CWD, ruleFile);
|
|
348
371
|
const ruleName = args[1];
|
|
349
372
|
run(scriptPath('add_rule.py'), ruleName ? [absRuleFile, ruleName] : [absRuleFile]);
|
|
373
|
+
propagateGlobal('--rules');
|
|
350
374
|
}
|
|
351
375
|
|
|
352
376
|
/**
|
|
353
|
-
* Handle `ai-toolkit inject-hook` -- injects external hooks into settings.json.
|
|
377
|
+
* Handle `ai-toolkit inject-hook` -- injects external hooks (file or URL) into settings.json.
|
|
354
378
|
* @param {string[]} args
|
|
355
379
|
*/
|
|
356
380
|
function handleInjectHook(args) {
|
|
357
|
-
const
|
|
358
|
-
if (!
|
|
359
|
-
console.error('Usage: ai-toolkit inject-hook <hooks-file
|
|
381
|
+
const source = args[0];
|
|
382
|
+
if (!source) {
|
|
383
|
+
console.error('Usage: ai-toolkit inject-hook <hooks-file-or-url> [hook-name] [target-dir]');
|
|
360
384
|
process.exit(1);
|
|
361
385
|
}
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
386
|
+
const isUrl = source.startsWith('https://') || source.startsWith('http://');
|
|
387
|
+
// For URLs pass as-is; for files resolve to absolute path
|
|
388
|
+
const resolvedSource = isUrl ? source : path.resolve(CWD, source);
|
|
389
|
+
// Pass remaining args through — Python CLI handles positional parsing
|
|
390
|
+
const remaining = args.slice(1);
|
|
391
|
+
run(scriptPath('inject_hook_cli.py'), [resolvedSource, ...remaining]);
|
|
365
392
|
}
|
|
366
393
|
|
|
367
394
|
/**
|
|
@@ -388,6 +415,10 @@ function handleMcp(args) {
|
|
|
388
415
|
process.exit(1);
|
|
389
416
|
}
|
|
390
417
|
run(scriptPath('mcp_manager.py'), args);
|
|
418
|
+
// After `mcp add`, propagate to global editors
|
|
419
|
+
if (args[0] === 'add') {
|
|
420
|
+
propagateGlobal('--mcp');
|
|
421
|
+
}
|
|
391
422
|
}
|
|
392
423
|
|
|
393
424
|
/**
|
|
@@ -5,7 +5,7 @@ service: ai-toolkit
|
|
|
5
5
|
tags: [architecture, overview, design, structure]
|
|
6
6
|
version: "1.4.4"
|
|
7
7
|
created: "2026-03-23"
|
|
8
|
-
last_updated: "2026-04-
|
|
8
|
+
last_updated: "2026-04-15"
|
|
9
9
|
description: "Architecture of ai-toolkit: directory layout, global install model, editor-aware MCP install, Codex translation layer, skill tiers, and integration with projects."
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -335,13 +335,13 @@ Severity levels: HIGH (blocks deployment), WARN (should fix), INFO (best practic
|
|
|
335
335
|
## Extension Points
|
|
336
336
|
|
|
337
337
|
### MCP Templates
|
|
338
|
-
`app/
|
|
338
|
+
`app/mcp-templates/` contains 26 ready-to-use MCP server config templates. Opt-in via `ai-toolkit install --modules mcp-templates` or activated automatically with `--profile strict|full`.
|
|
339
339
|
|
|
340
340
|
### Language Rules
|
|
341
341
|
`app/rules/` provides language-specific rule files covering 13 languages (TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, C++, Ruby, common). Auto-detected from project files via `--auto-detect` or selectable with `--modules rules-<lang>`. See README.md for current count.
|
|
342
342
|
|
|
343
|
-
### Extension API (`inject-hook`)
|
|
344
|
-
`inject_section_cli.py` provides a stable marker-based API for injecting content into `CLAUDE.md`, `constitution.md`, or `ARCHITECTURE.md` without overwriting user content.
|
|
343
|
+
### Extension API (`inject-hook`, `inject-rule`)
|
|
344
|
+
`inject_section_cli.py` provides a stable marker-based API for injecting content into `CLAUDE.md`, `constitution.md`, or `ARCHITECTURE.md` without overwriting user content. `inject_hook_cli.py` injects hooks into `settings.json` with `_source` tags — supports both local files and HTTPS URLs (cached in `~/.softspark/ai-toolkit/hooks/external/`, auto-refreshed on `update`).
|
|
345
345
|
|
|
346
346
|
### Manifest Install (`--modules`, `--auto-detect`)
|
|
347
347
|
`manifest.json` defines all installable components as named modules. Install individual modules with `ai-toolkit install --modules <name>` or enable auto-detection to select language rules based on files found in the project.
|
|
@@ -4,7 +4,7 @@ category: reference
|
|
|
4
4
|
service: ai-toolkit
|
|
5
5
|
tags: [cli, commands, reference, install, update, plugin, mcp]
|
|
6
6
|
created: "2026-04-13"
|
|
7
|
-
last_updated: "2026-04-
|
|
7
|
+
last_updated: "2026-04-15"
|
|
8
8
|
description: "Complete CLI reference for all ai-toolkit commands, options, and flags."
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -34,10 +34,10 @@ Usage: ai-toolkit <command> [options]
|
|
|
34
34
|
|
|
35
35
|
| Command | Description |
|
|
36
36
|
|---------|-------------|
|
|
37
|
-
| `add-rule <rule.md> [name]` | Register rule in `~/.softspark/ai-toolkit/rules/` — auto-applied on every `update` |
|
|
37
|
+
| `add-rule <rule.md\|url> [name]` | Register rule in `~/.softspark/ai-toolkit/rules/` — auto-applied on every `update` |
|
|
38
38
|
| `remove-rule <name> [dir]` | Unregister rule and remove its block from `CLAUDE.md` |
|
|
39
|
-
| `inject-hook <file.json
|
|
40
|
-
| `remove-hook <name>` | Remove injected hooks by source name |
|
|
39
|
+
| `inject-hook <file.json\|url> [name]` | Inject external hooks (file or URL) into settings.json (idempotent, `_source` tagged, URL hooks auto-refresh on update) |
|
|
40
|
+
| `remove-hook <name>` | Remove injected hooks by source name (also unregisters URL source if present) |
|
|
41
41
|
|
|
42
42
|
## MCP Management
|
|
43
43
|
|
|
@@ -107,6 +107,10 @@ This means Claude-only events such as `TaskCompleted`, `TeammateIdle`,
|
|
|
107
107
|
`SubagentStart`, `SubagentStop`, `PreCompact`, `SessionEnd`, and
|
|
108
108
|
`Notification` are not available in `.codex/hooks.json`.
|
|
109
109
|
|
|
110
|
+
`inject-hook` automatically propagates Codex-compatible events to
|
|
111
|
+
`~/.codex/hooks.json` (global layer). Non-Codex events are silently skipped.
|
|
112
|
+
`remove-hook` cleans both Claude and Codex targets.
|
|
113
|
+
|
|
110
114
|
## Behavioral Limits
|
|
111
115
|
|
|
112
116
|
Codex wrappers preserve workflow intent, but not every Claude runtime behavior
|
|
@@ -3,9 +3,9 @@ title: "Extension API Reference"
|
|
|
3
3
|
category: reference
|
|
4
4
|
service: ai-toolkit
|
|
5
5
|
tags: [extension-api, inject-rule, inject-hook, mcp-templates, integration, editors]
|
|
6
|
-
version: "1.
|
|
6
|
+
version: "1.4.0"
|
|
7
7
|
created: "2026-04-07"
|
|
8
|
-
last_updated: "2026-04-
|
|
8
|
+
last_updated: "2026-04-15"
|
|
9
9
|
description: "Reference for ai-toolkit's extension API: inject-rule, inject-hook, remove-rule, remove-hook, and editor-aware MCP template management."
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -23,9 +23,9 @@ This design is intentional: ai-toolkit is a generic toolkit. Consumers (MCP serv
|
|
|
23
23
|
|---------|-------------|-----------|------------|
|
|
24
24
|
| `inject-rule <file.md>` | `~/.claude/CLAUDE.md` | HTML comment markers (`<!-- TOOLKIT:name -->`) | Yes |
|
|
25
25
|
| `remove-rule <name>` | `~/.claude/CLAUDE.md` | Strip markers by block name | Yes |
|
|
26
|
-
| `inject-hook <file.json
|
|
27
|
-
| `remove-hook <name>` | `~/.claude/settings.json` | Strip all entries with matching `_source
|
|
28
|
-
| `add-rule <file.md>` | `~/.softspark/ai-toolkit/rules/` | File copy + re-inject all rules on next `update` | Yes |
|
|
26
|
+
| `inject-hook <file.json\|url> [name]` | `~/.claude/settings.json` | JSON `_source` tag per entry, URL cached + registered | Yes |
|
|
27
|
+
| `remove-hook <name>` | `~/.claude/settings.json` | Strip all entries with matching `_source`, unregister URL source | Yes |
|
|
28
|
+
| `add-rule <file.md\|url>` | `~/.softspark/ai-toolkit/rules/` | File copy + re-inject all rules on next `update` | Yes |
|
|
29
29
|
| `mcp add <name...>` | `.mcp.json` | Merge `mcpServers` block from template | Yes |
|
|
30
30
|
| `mcp install --editor <name...>` | Native editor MCP config | Render canonical template into editor format | Yes |
|
|
31
31
|
|
|
@@ -60,13 +60,20 @@ The argument is the block name (file stem used during `inject-rule`). If the blo
|
|
|
60
60
|
|
|
61
61
|
## inject-hook
|
|
62
62
|
|
|
63
|
-
Injects hook entries from a JSON file into `~/.claude/settings.json`. Every injected entry is tagged with `"_source": "<source-name>"` where the source name is derived from the filename stem.
|
|
63
|
+
Injects hook entries from a JSON file or HTTPS URL into `~/.claude/settings.json`. Every injected entry is tagged with `"_source": "<source-name>"` where the source name is derived from the filename stem or URL last segment.
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
+
# From local file
|
|
66
67
|
npx @softspark/ai-toolkit inject-hook ./my-tool-hooks.json
|
|
68
|
+
|
|
69
|
+
# From URL (HTTPS only) — cached locally, auto-refreshed on update
|
|
70
|
+
npx @softspark/ai-toolkit inject-hook https://example.com/my-tool-hooks.json
|
|
71
|
+
|
|
72
|
+
# With explicit source name
|
|
73
|
+
npx @softspark/ai-toolkit inject-hook https://example.com/hooks.json my-tool-hooks
|
|
67
74
|
```
|
|
68
75
|
|
|
69
|
-
**Implementation:** `scripts/inject_hook_cli.py`.
|
|
76
|
+
**Implementation:** `scripts/inject_hook_cli.py`, `scripts/hook_sources.py`, `scripts/url_fetch.py`.
|
|
70
77
|
|
|
71
78
|
**Input format:**
|
|
72
79
|
```json
|
|
@@ -88,15 +95,19 @@ npx @softspark/ai-toolkit inject-hook ./my-tool-hooks.json
|
|
|
88
95
|
}
|
|
89
96
|
```
|
|
90
97
|
|
|
91
|
-
**Source name derivation:** `my-tool-hooks.json` → source name `"my-tool-hooks"`. All entries are tagged `"_source": "my-tool-hooks"` in settings.json.
|
|
98
|
+
**Source name derivation:** `my-tool-hooks.json` → source name `"my-tool-hooks"`. For URLs: `https://example.com/path/my-tool-hooks.json` → `"my-tool-hooks"`. All entries are tagged `"_source": "my-tool-hooks"` in settings.json.
|
|
99
|
+
|
|
100
|
+
**URL support:** When an HTTPS URL is provided, the JSON is fetched, validated, cached in `~/.softspark/ai-toolkit/hooks/external/<name>.json`, and registered in `sources.json`. On every `ai-toolkit update`, URL-sourced hooks are re-fetched and re-injected automatically. If the fetch fails during update, the cached version is used.
|
|
92
101
|
|
|
93
102
|
**Idempotency:** Re-running strips all existing entries with the same source name, then appends the new ones. No duplicates accumulate.
|
|
94
103
|
|
|
95
|
-
**Safety:** Entries tagged `"_source": "ai-toolkit"` are never modified or removed by this command. External tools cannot affect the toolkit's own hooks.
|
|
104
|
+
**Safety:** Entries tagged `"_source": "ai-toolkit"` are never modified or removed by this command. External tools cannot affect the toolkit's own hooks. Only HTTPS URLs are accepted.
|
|
105
|
+
|
|
106
|
+
**Codex propagation:** Codex-compatible events (`SessionStart`, `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`) are automatically propagated to `~/.codex/hooks.json`. Non-Codex events are silently skipped. No extra flags needed.
|
|
96
107
|
|
|
97
108
|
## remove-hook
|
|
98
109
|
|
|
99
|
-
Strips all hook entries from `~/.claude/settings.json` that carry a given `_source` tag.
|
|
110
|
+
Strips all hook entries from `~/.claude/settings.json` that carry a given `_source` tag. If the hook was URL-sourced, also unregisters the URL from `sources.json` and removes the cached file.
|
|
100
111
|
|
|
101
112
|
```bash
|
|
102
113
|
npx @softspark/ai-toolkit remove-hook my-tool-hooks
|
|
@@ -140,14 +151,14 @@ When `install` runs with `--scope project`, ai-toolkit also updates `.mcp.json`
|
|
|
140
151
|
│ Public Extension API: │
|
|
141
152
|
│ inject-rule <file.md> → CLAUDE.md │
|
|
142
153
|
│ remove-rule <name> → CLAUDE.md │
|
|
143
|
-
│ inject-hook <file
|
|
154
|
+
│ inject-hook <file|url> → settings.json │
|
|
144
155
|
│ remove-hook <name> → settings.json │
|
|
145
|
-
│ add-rule <file
|
|
156
|
+
│ add-rule <file|url> → rules/ registry │
|
|
146
157
|
│ mcp add <template> → .mcp.json │
|
|
147
158
|
│ mcp install <template> → editor-native MCP │
|
|
148
159
|
│ │
|
|
149
160
|
│ Idempotent: markers (rules) / _source tags (hooks) │
|
|
150
|
-
│
|
|
161
|
+
│ URL sources: cached + auto-refreshed on update │
|
|
151
162
|
└──────────────────────────────────────────────────────┘
|
|
152
163
|
▲
|
|
153
164
|
│ uses API
|
package/llms-full.txt
CHANGED
|
@@ -4791,8 +4791,8 @@ Severity levels: HIGH (blocks deployment), WARN (should fix), INFO (best practic
|
|
|
4791
4791
|
### Language Rules
|
|
4792
4792
|
`app/rules/` provides language-specific rule files covering 13 languages (TypeScript, Python, Go, Rust, Java, Kotlin, Swift, Dart, C#, PHP, C++, Ruby, common). Auto-detected from project files via `--auto-detect` or selectable with `--modules rules-<lang>`. See README.md for current count.
|
|
4793
4793
|
|
|
4794
|
-
### Extension API (`inject-hook`)
|
|
4795
|
-
`inject_section_cli.py` provides a stable marker-based API for injecting content into `CLAUDE.md`, `constitution.md`, or `ARCHITECTURE.md` without overwriting user content.
|
|
4794
|
+
### Extension API (`inject-hook`, `inject-rule`)
|
|
4795
|
+
`inject_section_cli.py` provides a stable marker-based API for injecting content into `CLAUDE.md`, `constitution.md`, or `ARCHITECTURE.md` without overwriting user content. `inject_hook_cli.py` injects hooks into `settings.json` with `_source` tags — supports both local files and HTTPS URLs (cached in `~/.softspark/ai-toolkit/hooks/external/`, auto-refreshed on `update`).
|
|
4796
4796
|
|
|
4797
4797
|
### Manifest Install (`--modules`, `--auto-detect`)
|
|
4798
4798
|
`manifest.json` defines all installable components as named modules. Install individual modules with `ai-toolkit install --modules <name>` or enable auto-detection to select language rules based on files found in the project.
|
|
@@ -5167,10 +5167,10 @@ Usage: ai-toolkit <command> [options]
|
|
|
5167
5167
|
|
|
5168
5168
|
| Command | Description |
|
|
5169
5169
|
|---------|-------------|
|
|
5170
|
-
| `add-rule <rule.md> [name]` | Register rule in `~/.softspark/ai-toolkit/rules/` — auto-applied on every `update` |
|
|
5170
|
+
| `add-rule <rule.md\|url> [name]` | Register rule in `~/.softspark/ai-toolkit/rules/` — auto-applied on every `update` |
|
|
5171
5171
|
| `remove-rule <name> [dir]` | Unregister rule and remove its block from `CLAUDE.md` |
|
|
5172
|
-
| `inject-hook <file.json
|
|
5173
|
-
| `remove-hook <name>` | Remove injected hooks by source name |
|
|
5172
|
+
| `inject-hook <file.json\|url> [name]` | Inject external hooks (file or URL) into settings.json (idempotent, `_source` tagged, URL hooks auto-refresh on update) |
|
|
5173
|
+
| `remove-hook <name>` | Remove injected hooks by source name (also unregisters URL source if present) |
|
|
5174
5174
|
|
|
5175
5175
|
## MCP Management
|
|
5176
5176
|
|
|
@@ -5380,6 +5380,10 @@ This means Claude-only events such as `TaskCompleted`, `TeammateIdle`,
|
|
|
5380
5380
|
`SubagentStart`, `SubagentStop`, `PreCompact`, `SessionEnd`, and
|
|
5381
5381
|
`Notification` are not available in `.codex/hooks.json`.
|
|
5382
5382
|
|
|
5383
|
+
`inject-hook` automatically propagates Codex-compatible events to
|
|
5384
|
+
`~/.codex/hooks.json` (global layer). Non-Codex events are silently skipped.
|
|
5385
|
+
`remove-hook` cleans both Claude and Codex targets.
|
|
5386
|
+
|
|
5383
5387
|
## Behavioral Limits
|
|
5384
5388
|
|
|
5385
5389
|
Codex wrappers preserve workflow intent, but not every Claude runtime behavior
|
|
@@ -6534,9 +6538,9 @@ title: "Extension API Reference"
|
|
|
6534
6538
|
category: reference
|
|
6535
6539
|
service: ai-toolkit
|
|
6536
6540
|
tags: [extension-api, inject-rule, inject-hook, mcp-templates, integration, editors]
|
|
6537
|
-
version: "1.
|
|
6541
|
+
version: "1.4.0"
|
|
6538
6542
|
created: "2026-04-07"
|
|
6539
|
-
last_updated: "2026-04-
|
|
6543
|
+
last_updated: "2026-04-15"
|
|
6540
6544
|
description: "Reference for ai-toolkit's extension API: inject-rule, inject-hook, remove-rule, remove-hook, and editor-aware MCP template management."
|
|
6541
6545
|
---
|
|
6542
6546
|
|
|
@@ -6554,9 +6558,9 @@ This design is intentional: ai-toolkit is a generic toolkit. Consumers (MCP serv
|
|
|
6554
6558
|
|---------|-------------|-----------|------------|
|
|
6555
6559
|
| `inject-rule <file.md>` | `~/.claude/CLAUDE.md` | HTML comment markers (`<!-- TOOLKIT:name -->`) | Yes |
|
|
6556
6560
|
| `remove-rule <name>` | `~/.claude/CLAUDE.md` | Strip markers by block name | Yes |
|
|
6557
|
-
| `inject-hook <file.json
|
|
6558
|
-
| `remove-hook <name>` | `~/.claude/settings.json` | Strip all entries with matching `_source
|
|
6559
|
-
| `add-rule <file.md>` | `~/.softspark/ai-toolkit/rules/` | File copy + re-inject all rules on next `update` | Yes |
|
|
6561
|
+
| `inject-hook <file.json\|url> [name]` | `~/.claude/settings.json` | JSON `_source` tag per entry, URL cached + registered | Yes |
|
|
6562
|
+
| `remove-hook <name>` | `~/.claude/settings.json` | Strip all entries with matching `_source`, unregister URL source | Yes |
|
|
6563
|
+
| `add-rule <file.md\|url>` | `~/.softspark/ai-toolkit/rules/` | File copy + re-inject all rules on next `update` | Yes |
|
|
6560
6564
|
| `mcp add <name...>` | `.mcp.json` | Merge `mcpServers` block from template | Yes |
|
|
6561
6565
|
| `mcp install --editor <name...>` | Native editor MCP config | Render canonical template into editor format | Yes |
|
|
6562
6566
|
|
|
@@ -6591,13 +6595,20 @@ The argument is the block name (file stem used during `inject-rule`). If the blo
|
|
|
6591
6595
|
|
|
6592
6596
|
## inject-hook
|
|
6593
6597
|
|
|
6594
|
-
Injects hook entries from a JSON file into `~/.claude/settings.json`. Every injected entry is tagged with `"_source": "<source-name>"` where the source name is derived from the filename stem.
|
|
6598
|
+
Injects hook entries from a JSON file or HTTPS URL into `~/.claude/settings.json`. Every injected entry is tagged with `"_source": "<source-name>"` where the source name is derived from the filename stem or URL last segment.
|
|
6595
6599
|
|
|
6596
6600
|
```bash
|
|
6601
|
+
# From local file
|
|
6597
6602
|
npx @softspark/ai-toolkit inject-hook ./my-tool-hooks.json
|
|
6603
|
+
|
|
6604
|
+
# From URL (HTTPS only) — cached locally, auto-refreshed on update
|
|
6605
|
+
npx @softspark/ai-toolkit inject-hook https://example.com/my-tool-hooks.json
|
|
6606
|
+
|
|
6607
|
+
# With explicit source name
|
|
6608
|
+
npx @softspark/ai-toolkit inject-hook https://example.com/hooks.json my-tool-hooks
|
|
6598
6609
|
```
|
|
6599
6610
|
|
|
6600
|
-
**Implementation:** `scripts/inject_hook_cli.py`.
|
|
6611
|
+
**Implementation:** `scripts/inject_hook_cli.py`, `scripts/hook_sources.py`, `scripts/url_fetch.py`.
|
|
6601
6612
|
|
|
6602
6613
|
**Input format:**
|
|
6603
6614
|
```json
|
|
@@ -6619,15 +6630,19 @@ npx @softspark/ai-toolkit inject-hook ./my-tool-hooks.json
|
|
|
6619
6630
|
}
|
|
6620
6631
|
```
|
|
6621
6632
|
|
|
6622
|
-
**Source name derivation:** `my-tool-hooks.json` → source name `"my-tool-hooks"`. All entries are tagged `"_source": "my-tool-hooks"` in settings.json.
|
|
6633
|
+
**Source name derivation:** `my-tool-hooks.json` → source name `"my-tool-hooks"`. For URLs: `https://example.com/path/my-tool-hooks.json` → `"my-tool-hooks"`. All entries are tagged `"_source": "my-tool-hooks"` in settings.json.
|
|
6634
|
+
|
|
6635
|
+
**URL support:** When an HTTPS URL is provided, the JSON is fetched, validated, cached in `~/.softspark/ai-toolkit/hooks/external/<name>.json`, and registered in `sources.json`. On every `ai-toolkit update`, URL-sourced hooks are re-fetched and re-injected automatically. If the fetch fails during update, the cached version is used.
|
|
6623
6636
|
|
|
6624
6637
|
**Idempotency:** Re-running strips all existing entries with the same source name, then appends the new ones. No duplicates accumulate.
|
|
6625
6638
|
|
|
6626
|
-
**Safety:** Entries tagged `"_source": "ai-toolkit"` are never modified or removed by this command. External tools cannot affect the toolkit's own hooks.
|
|
6639
|
+
**Safety:** Entries tagged `"_source": "ai-toolkit"` are never modified or removed by this command. External tools cannot affect the toolkit's own hooks. Only HTTPS URLs are accepted.
|
|
6640
|
+
|
|
6641
|
+
**Codex propagation:** Codex-compatible events (`SessionStart`, `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`) are automatically propagated to `~/.codex/hooks.json`. Non-Codex events are silently skipped. No extra flags needed.
|
|
6627
6642
|
|
|
6628
6643
|
## remove-hook
|
|
6629
6644
|
|
|
6630
|
-
Strips all hook entries from `~/.claude/settings.json` that carry a given `_source` tag.
|
|
6645
|
+
Strips all hook entries from `~/.claude/settings.json` that carry a given `_source` tag. If the hook was URL-sourced, also unregisters the URL from `sources.json` and removes the cached file.
|
|
6631
6646
|
|
|
6632
6647
|
```bash
|
|
6633
6648
|
npx @softspark/ai-toolkit remove-hook my-tool-hooks
|
|
@@ -6671,14 +6686,14 @@ When `install` runs with `--scope project`, ai-toolkit also updates `.mcp.json`
|
|
|
6671
6686
|
│ Public Extension API: │
|
|
6672
6687
|
│ inject-rule <file.md> → CLAUDE.md │
|
|
6673
6688
|
│ remove-rule <name> → CLAUDE.md │
|
|
6674
|
-
│ inject-hook <file
|
|
6689
|
+
│ inject-hook <file|url> → settings.json │
|
|
6675
6690
|
│ remove-hook <name> → settings.json │
|
|
6676
|
-
│ add-rule <file
|
|
6691
|
+
│ add-rule <file|url> → rules/ registry │
|
|
6677
6692
|
│ mcp add <template> → .mcp.json │
|
|
6678
6693
|
│ mcp install <template> → editor-native MCP │
|
|
6679
6694
|
│ │
|
|
6680
6695
|
│ Idempotent: markers (rules) / _source tags (hooks) │
|
|
6681
|
-
│
|
|
6696
|
+
│ URL sources: cached + auto-refreshed on update │
|
|
6682
6697
|
└──────────────────────────────────────────────────────┘
|
|
6683
6698
|
▲
|
|
6684
6699
|
│ uses API
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softspark/ai-toolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Professional-grade AI coding toolkit: 92 skills, 44 agents, multi-platform support (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo Code, Aider, Augment, Google Antigravity, Codex CLI), machine-enforced safety constitution, persona presets, skill security auditor, expanded lifecycle hooks, 11 plugin packs, and benchmark tooling.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -299,14 +299,28 @@ def _find_cached_npm(cache_dir: Path) -> Path | None:
|
|
|
299
299
|
|
|
300
300
|
|
|
301
301
|
def _extract_tarball(tarball: Path, dest: Path) -> None:
|
|
302
|
-
"""Extract npm tarball (which has a package/ prefix) to dest.
|
|
302
|
+
"""Extract npm tarball (which has a package/ prefix) to dest.
|
|
303
|
+
|
|
304
|
+
Validates that extracted paths stay within dest to prevent path traversal.
|
|
305
|
+
Rejects symlinks and absolute paths.
|
|
306
|
+
"""
|
|
307
|
+
dest_resolved = dest.resolve()
|
|
303
308
|
with tarfile.open(tarball, "r:gz") as tf:
|
|
304
309
|
for member in tf.getmembers():
|
|
305
310
|
# npm tarballs have a "package/" prefix
|
|
306
|
-
if member.name.startswith("package/"):
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
311
|
+
if not member.name.startswith("package/"):
|
|
312
|
+
continue
|
|
313
|
+
member.name = member.name[len("package/"):]
|
|
314
|
+
if not member.name: # skip empty (the "package/" dir itself)
|
|
315
|
+
continue
|
|
316
|
+
# Reject symlinks and absolute paths
|
|
317
|
+
if member.issym() or member.islnk() or member.name.startswith("/"):
|
|
318
|
+
continue
|
|
319
|
+
# Path traversal protection
|
|
320
|
+
target = (dest / member.name).resolve()
|
|
321
|
+
if not str(target).startswith(str(dest_resolved)):
|
|
322
|
+
continue
|
|
323
|
+
tf.extract(member, dest)
|
|
310
324
|
|
|
311
325
|
|
|
312
326
|
def _extract_version_from_tarball(filename: str, package_name: str) -> str:
|
|
@@ -332,6 +346,10 @@ def _resolve_git(
|
|
|
332
346
|
) -> BaseConfig:
|
|
333
347
|
"""Resolve from git URL (git+https://...)."""
|
|
334
348
|
url = source.removeprefix("git+")
|
|
349
|
+
if not url.startswith("https://"):
|
|
350
|
+
raise ConfigResolverError(
|
|
351
|
+
f"Only HTTPS git URLs are supported (got: {url.split('://')[0]}://)"
|
|
352
|
+
)
|
|
335
353
|
cache_key = hashlib.sha256(url.encode()).hexdigest()[:16]
|
|
336
354
|
cache_dir = _cache_root() / "git" / cache_key
|
|
337
355
|
|