@softspark/ai-toolkit 4.4.1 → 4.5.0
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 +44 -0
- package/README.md +28 -28
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +1 -1
- package/app/hooks/_profile-check.sh +17 -0
- package/app/hooks/guard-destructive.sh +19 -1
- package/app/hooks/loop-guard.sh +59 -0
- package/app/hooks/session-start.sh +4 -2
- package/app/hooks.json +10 -0
- package/app/plugins/memory-pack/README.md +2 -2
- package/app/plugins/memory-pack/scripts/strip_private.py +31 -4
- package/app/skills/hook-creator/SKILL.md +1 -0
- package/app/skills/instinct-review/SKILL.md +10 -10
- package/app/skills/skill-audit/SKILL.md +10 -0
- package/app/skills/verification-before-completion/SKILL.md +19 -0
- package/benchmarks/ecosystem-doctor-snapshot.json +27 -15
- package/kb/reference/hooks-catalog.md +22 -3
- package/kb/reference/mcp-editor-compatibility.md +1 -1
- package/kb/reference/supported-tools-registry.md +3 -3
- package/llms-full.txt +68 -17
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/audit_skills.py +82 -2
- package/scripts/doctor.py +51 -0
- package/scripts/ecosystem_tools.json +9 -1
- package/scripts/generate_codex_hooks.py +5 -4
- package/scripts/validate.py +84 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,50 @@ Versioning follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## v4.5.0 - Unicode-safety scanner, loop guard, honest instincts (2026-06-02)
|
|
11
|
+
|
|
12
|
+
Minor release. Hardens the security audit against invisible prompt injection, adds a repeated-action loop guard, fixes destructive-guard false positives, and turns the instinct system from a dormant promise into a working manual feature.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **Unicode-safety scanner** — `scripts/audit_skills.py` now flags invisible/smuggled Unicode across shipped prompt text (skills, agents, rules, personas, mcp-templates): tag-block ASCII smuggling (`U+E0000–E007F`) and Trojan Source bidi controls as HIGH, zero-width/invisible format chars as WARN. Emoji ZWJ (`U+200D`) is allowlisted. Runs inside the existing `--ci` gate.
|
|
16
|
+
- **`loop-guard.sh`** — advisory `PostToolUse` hook (`Bash|Edit|MultiEdit|Write`) that warns when the same action repeats within a short window, catching successful-but-identical loops the `/repeat` circuit breaker (failures-only) misses. Edits track content, so iterative editing of one file does not trip it. Tunable via `AI_TOOLKIT_LOOP_WINDOW`/`AI_TOOLKIT_LOOP_THRESHOLD`. Hook entries: 28 → 29.
|
|
17
|
+
- **Per-hook opt-out** — `AI_TOOLKIT_DISABLED_HOOKS` (comma list) disables named profile-gated hooks via `_profile-check.sh`. Safety guards are intentionally excluded — remove them with `ai-toolkit remove-hook`.
|
|
18
|
+
- **Editor hooks honesty** — the README platform matrix gained a **Hooks** column marking which editors get lifecycle hook enforcement; `validate.py` cross-checks the claim against the actual `generate_*_hooks.py` generators and fails on drift.
|
|
19
|
+
- **`doctor` language-rules drift check** — warns when a local-install project gained a language (e.g. a new `Cargo.toml`) whose `<lang>-rules` were never injected.
|
|
20
|
+
- **Live-app rubric verification** — `verification-before-completion` documents an optional weighted-rubric scoring pass for behavioral/visual work.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- **Instincts load by default** — `session-start.sh` loads `.claude/instincts/*.md` whenever present (previously gated behind `AI_TOOLKIT_HOOK_VERBOSE=1`, so they never surfaced by default). `AI_TOOLKIT_HOOK_QUIET=1` suppresses; no instincts on disk means no output.
|
|
24
|
+
- **memory-pack `strip_private.py`** — now redacts high-confidence secrets (API keys, tokens, private keys) to `[REDACTED:<label>]` in addition to stripping `<private>` blocks before SQLite storage.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
- **`guard-destructive.sh` false positives** — `git push --force-with-lease`/`--force-if-includes` are no longer blocked, and a single non-chained `echo`/`printf`/`git commit`/`git tag` carrying a destructive token as data (e.g. a commit message mentioning `DROP TABLE`) passes. Chained commands (`&&`, `||`, `;`, `|`) and real executions (e.g. `psql -c "DROP TABLE"`) are still inspected and blocked.
|
|
28
|
+
- **`instinct-review` documentation** — removed the false claim of an automatic session-end instinct extractor that never existed; the skill now documents the real manual authoring + curation flow.
|
|
29
|
+
|
|
30
|
+
Test count: 1151 → 1179. No skill/agent count change.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## v4.4.2 - Ecosystem sync: Claude Code 2.1.158 + Codex 0.134 hook events (2026-05-30)
|
|
35
|
+
|
|
36
|
+
Patch release. Syncs the tool registry and `hook-creator` skill with newly detected upstream hook events and refreshes the ecosystem drift snapshot. No generated output or runtime behavior changes.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **Claude Code hook events** - Recorded `MessageDisplay` (new in Claude Code 2.1.152) in `scripts/ecosystem_tools.json` and the `hook-creator` skill's Supported Hook Events table. Also added three real-but-untracked events to the registry: `PostToolUseFailure`, `PostToolBatch`, `UserPromptExpansion`.
|
|
41
|
+
- **Codex hook events** - Recorded the four Codex events we did not track (`PreCompact`, `PostCompact`, `SubagentStart`, `SubagentStop`); Codex exposes 10 lifecycle events. Updated the `scripts/generate_codex_hooks.py` docstring (6 to 10) and `kb/reference/supported-tools-registry.md`.
|
|
42
|
+
- **Ecosystem snapshot** - Refreshed `benchmarks/ecosystem-doctor-snapshot.json` to baseline Claude Code 2.1.158 and Codex 0.134, and bumped the registry `last_updated`.
|
|
43
|
+
|
|
44
|
+
### Ecosystem
|
|
45
|
+
|
|
46
|
+
- Class-B drift (new Claude Code and Codex hook events) tracked in the registry and skill docs; wiring the Codex subagent/compaction events into generated `.codex/hooks.json` is deferred to a future release. GitHub Copilot docs reorg classified class C (not adopted). Seven editors showed cosmetic-only doc drift (class A).
|
|
47
|
+
|
|
48
|
+
### Verification
|
|
49
|
+
|
|
50
|
+
- `python3 scripts/validate.py --strict`
|
|
51
|
+
- `python3 scripts/ecosystem_doctor.py --offline --check`
|
|
52
|
+
- `npm test` (1151 passing)
|
|
53
|
+
|
|
10
54
|
## v4.4.1 - Codex hook output compatibility fix (2026-05-25)
|
|
11
55
|
|
|
12
56
|
Patch release. Fixes Codex `UserPromptSubmit` hook JSON validation failures and visible hook-context noise while preserving hook side effects.
|
package/README.md
CHANGED
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](app/skills/)
|
|
8
8
|
[](app/agents/)
|
|
9
|
-
[](tests/)
|
|
10
10
|
|
|
11
|
-
## What's New in v4.
|
|
11
|
+
## What's New in v4.5.0
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Minor release. Hardens the security audit against invisible prompt injection, adds a repeated-action loop guard, fixes destructive-guard false positives, and makes the instinct system actually work.
|
|
14
14
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
-
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
15
|
+
- **Unicode-safety scanner**: the skill/agent audit now flags invisible and smuggled Unicode (tag-block ASCII smuggling, Trojan Source bidi controls) across all shipped prompt text — a prompt-injection vector regex checks never caught. Runs in the `--ci` gate.
|
|
16
|
+
- **Loop guard**: a new advisory `PostToolUse` hook warns when the same action repeats, catching stuck loops that the `/repeat` failure-counter circuit breaker misses.
|
|
17
|
+
- **`git push --force-with-lease` unblocked**: the destructive-command guard no longer false-positives on safe force-pushes or on commit messages that merely mention `DROP TABLE`/`rm -rf`.
|
|
18
|
+
- **Instincts load by default**: hand-authored `.claude/instincts/*.md` now load at session start (previously dormant behind a verbose flag). The skill docs were corrected to drop a never-implemented auto-extractor claim.
|
|
19
|
+
- **Editor honesty + secret scrubbing**: the platform matrix marks which editors get hook enforcement, and memory-pack now redacts secrets before storing session observations.
|
|
20
20
|
|
|
21
21
|
See [CHANGELOG.md](CHANGELOG.md) for full history.
|
|
22
22
|
|
|
@@ -99,22 +99,22 @@ See [CLI Reference](kb/reference/cli-reference.md) for all commands and options.
|
|
|
99
99
|
|
|
100
100
|
## Platform Support
|
|
101
101
|
|
|
102
|
-
| Platform | Config Files | Scope |
|
|
103
|
-
|
|
104
|
-
| Claude Code | `~/.claude/` | global |
|
|
105
|
-
| Cursor | `.cursor/rules/*.mdc` + `.cursor/mcp.json` + `.cursor/skills/*` | project (`~/.cursor/mcp.json` for MCP only) |
|
|
106
|
-
| Windsurf | `~/.codeium/.../global_rules.md` + `~/.codeium/windsurf/skills/*` + `.windsurf/rules/*.md` | global + project |
|
|
107
|
-
| Gemini CLI | `~/.gemini/GEMINI.md` | global |
|
|
108
|
-
| GitHub Copilot | `.github/copilot-instructions.md` | project |
|
|
109
|
-
| Cline | `~/.cline/rules/*.md` + `~/.cline/skills/*` + `.clinerules/*.md` | global + project |
|
|
110
|
-
| Roo Code | `~/.roo/rules/*.md` + `.roomodes` + `.roo/rules/*.md` | global rules + project |
|
|
111
|
-
| Aider | `~/.aider.conf.yml` + `.aider.conf.yml` + `CONVENTIONS.md` | global + project |
|
|
112
|
-
| Augment | `~/.augment/rules/*.md` + `.augment/rules/ai-toolkit-*.md` | global + project |
|
|
113
|
-
| Google Antigravity | `.agent/rules/*.md` + `.agent/workflows/*.md` | project |
|
|
114
|
-
| Codex CLI | `AGENTS.md` + `.agents/rules/*.md` + `.agents/skills/*` + `.codex/hooks.json` | project + global plugin |
|
|
115
|
-
| opencode | `AGENTS.md` + `.opencode/{agents,commands,plugins}/*` + `opencode.json` | project + global (`~/.config/opencode/`) |
|
|
116
|
-
|
|
117
|
-
> Claude Code is always installed (primary platform). Other editors on demand with `--editors`.
|
|
102
|
+
| Platform | Config Files | Hooks | Scope |
|
|
103
|
+
|----------|-------------|:-----:|-------|
|
|
104
|
+
| Claude Code | `~/.claude/` | ✅ | global |
|
|
105
|
+
| Cursor | `.cursor/rules/*.mdc` + `.cursor/mcp.json` + `.cursor/skills/*` | ✅ | project (`~/.cursor/mcp.json` for MCP only) |
|
|
106
|
+
| Windsurf | `~/.codeium/.../global_rules.md` + `~/.codeium/windsurf/skills/*` + `.windsurf/rules/*.md` | ✅ | global + project |
|
|
107
|
+
| Gemini CLI | `~/.gemini/GEMINI.md` | ✅ | global |
|
|
108
|
+
| GitHub Copilot | `.github/copilot-instructions.md` | — | project |
|
|
109
|
+
| Cline | `~/.cline/rules/*.md` + `~/.cline/skills/*` + `.clinerules/*.md` | — | global + project |
|
|
110
|
+
| Roo Code | `~/.roo/rules/*.md` + `.roomodes` + `.roo/rules/*.md` | — | global rules + project |
|
|
111
|
+
| Aider | `~/.aider.conf.yml` + `.aider.conf.yml` + `CONVENTIONS.md` | — | global + project |
|
|
112
|
+
| Augment | `~/.augment/rules/*.md` + `.augment/rules/ai-toolkit-*.md` | ✅ | global + project |
|
|
113
|
+
| Google Antigravity | `.agent/rules/*.md` + `.agent/workflows/*.md` | — | project |
|
|
114
|
+
| Codex CLI | `AGENTS.md` + `.agents/rules/*.md` + `.agents/skills/*` + `.codex/hooks.json` | ✅ | project + global plugin |
|
|
115
|
+
| opencode | `AGENTS.md` + `.opencode/{agents,commands,plugins}/*` + `opencode.json` | ✅ | project + global (`~/.config/opencode/`) |
|
|
116
|
+
|
|
117
|
+
> Claude Code is always installed (primary platform). Other editors on demand with `--editors`. Every platform receives the agent/skill catalog, guidelines, and registered custom rules as text. The **Hooks** column marks platforms that also get lifecycle hook enforcement — the machine-enforced constitution (guard-destructive, quality gates, search-first discipline). Platforms marked — receive those rules as guidance only, without blocking hooks.
|
|
118
118
|
|
|
119
119
|
---
|
|
120
120
|
|
|
@@ -126,7 +126,7 @@ See [CLI Reference](kb/reference/cli-reference.md) for all commands and options.
|
|
|
126
126
|
| `skills/` (hybrid) | 30 | Slash commands with agent knowledge base |
|
|
127
127
|
| `skills/` (knowledge) | 45 | Domain knowledge auto-loaded by agents (includes 13 `<lang>-rules` skills) |
|
|
128
128
|
| `agents/` | 44 | Specialized agents across 10 categories |
|
|
129
|
-
| `hooks/` |
|
|
129
|
+
| `hooks/` | 29 entries / 14 events | Quality gates, path safety, prompt governance, loop guard, session lifecycle |
|
|
130
130
|
| `plugins/` | 11 packs | Opt-in domain bundles (security, research, frontend, enterprise, 6 language packs) |
|
|
131
131
|
| `constitution.md` | 6 articles | Machine-enforced safety rules |
|
|
132
132
|
| `rules/` | auto-injected | Language-specific and custom rules injected into your configs |
|
|
@@ -142,14 +142,14 @@ ai-toolkit/
|
|
|
142
142
|
│ ├── agents/ # 44 agent definitions
|
|
143
143
|
│ ├── skills/ # 107 skills (task / hybrid / knowledge)
|
|
144
144
|
│ ├── rules/ # Auto-injected into your CLAUDE.md
|
|
145
|
-
│ ├── hooks/ # Hook scripts (
|
|
145
|
+
│ ├── hooks/ # Hook scripts (29 entries, 14 lifecycle events)
|
|
146
146
|
│ ├── plugins/ # 11 experimental plugin packs (opt-in)
|
|
147
147
|
│ ├── output-styles/ # System prompt output style overrides
|
|
148
148
|
│ ├── constitution.md # 6 immutable safety articles
|
|
149
149
|
│ └── ARCHITECTURE.md # Full system design
|
|
150
150
|
├── kb/ # Reference docs, procedures, plans
|
|
151
151
|
├── scripts/ # Validation, install, evaluation scripts
|
|
152
|
-
├── tests/ # Bats test suite (
|
|
152
|
+
├── tests/ # Bats test suite (1179 tests)
|
|
153
153
|
└── CHANGELOG.md
|
|
154
154
|
```
|
|
155
155
|
|
|
@@ -161,7 +161,7 @@ ai-toolkit/
|
|
|
161
161
|
|
|
162
162
|
**Machine-enforced constitution** — 6-article safety constitution enforced via `PreToolUse` hooks that actually block `rm -rf`, `DROP TABLE`, and irreversible operations. Not just documentation.
|
|
163
163
|
|
|
164
|
-
**
|
|
164
|
+
**29 lifecycle hooks** — Executable scripts across 14 events (SessionStart → SessionEnd, plus InstructionsLoaded + ConfigChange). Guards, governance, quality gates, session persistence, MCP health checks, revert protection, test-cohesion enforcement, loop guard, search-first discipline. See [Hooks Catalog](kb/reference/hooks-catalog.md).
|
|
165
165
|
|
|
166
166
|
**Security scanning** — `/skill-audit` for code-level risks, `/cve-scan` for dependency CVEs. Both CI-ready with exit codes.
|
|
167
167
|
|
|
@@ -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": "4.
|
|
4
|
+
"version": "4.5.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "SoftSpark",
|
|
7
7
|
"url": "https://github.com/softspark"
|
package/app/ARCHITECTURE.md
CHANGED
|
@@ -8,7 +8,7 @@ Universal multi-agent system for software development. Works across all reposito
|
|
|
8
8
|
|-----------|-------|
|
|
9
9
|
| Agents | See agents catalog |
|
|
10
10
|
| Skills | See skills catalog |
|
|
11
|
-
| Hooks | 14 events /
|
|
11
|
+
| Hooks | 14 events / 29 entries (SessionStart ×3, Notification ×1, PreToolUse ×5, UserPromptSubmit ×2, PostToolUse ×5, Stop ×4, TaskCompleted ×1, TeammateIdle ×1, SubagentStart ×1, SubagentStop ×1, PreCompact ×2, SessionEnd ×1, InstructionsLoaded ×1, ConfigChange ×1) |
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -9,3 +9,20 @@
|
|
|
9
9
|
|
|
10
10
|
PROFILE="${TOOLKIT_HOOK_PROFILE:-standard}"
|
|
11
11
|
[ "$PROFILE" = "minimal" ] && exit 0
|
|
12
|
+
|
|
13
|
+
# Per-hook soft opt-out: AI_TOOLKIT_DISABLED_HOOKS is a comma-separated list of
|
|
14
|
+
# hook names (with or without the .sh suffix). A listed hook exits 0 (no-op).
|
|
15
|
+
# Only hooks that source this file are covered — the safety guards
|
|
16
|
+
# (guard-destructive/path/config) intentionally do not, and must be removed
|
|
17
|
+
# deliberately with `ai-toolkit remove-hook` rather than silently env-disabled.
|
|
18
|
+
if [ -n "${AI_TOOLKIT_DISABLED_HOOKS:-}" ]; then
|
|
19
|
+
_hook_name="$(basename "${BASH_SOURCE[1]:-$0}" .sh)"
|
|
20
|
+
IFS=',' read -ra _disabled_hooks <<< "$AI_TOOLKIT_DISABLED_HOOKS"
|
|
21
|
+
for _d in "${_disabled_hooks[@]}"; do
|
|
22
|
+
_d="${_d// /}"
|
|
23
|
+
_d="${_d%.sh}"
|
|
24
|
+
if [ -n "$_d" ] && [ "$_d" = "$_hook_name" ]; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
done
|
|
28
|
+
fi
|
|
@@ -18,6 +18,24 @@ fi
|
|
|
18
18
|
# Normalize command for matching: collapse whitespace, strip backslash escapes
|
|
19
19
|
NORMALIZED=$(printf '%s' "$COMMAND" | tr -s '[:space:]' ' ' | sed 's/\\//g')
|
|
20
20
|
|
|
21
|
+
# Benign data contexts: a single, non-chained command whose program only PRINTS
|
|
22
|
+
# or RECORDS text (echo/printf) or writes a commit/tag message (git commit/tag)
|
|
23
|
+
# cannot itself execute a destructive op — the dangerous tokens are data, not
|
|
24
|
+
# actions. Skip matching so "git commit -m 'fix DROP TABLE race'" is not blocked.
|
|
25
|
+
# Bail out of the allowlist when a command separator is present (&&, ||, ;, |) so
|
|
26
|
+
# chained commands like `git commit -m x && rm -rf /tmp` are still inspected.
|
|
27
|
+
if ! printf '%s' "$NORMALIZED" | grep -qE '(&&|\|\||;|\|)'; then
|
|
28
|
+
if printf '%s' "$NORMALIZED" | grep -qE '^[[:space:]]*(echo|printf|git[[:space:]]+(commit|tag))([[:space:]]|$)'; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Safe git force variants (--force-with-lease / --force-if-includes) are the
|
|
34
|
+
# recommended way to force-push without clobbering others' work. Strip them
|
|
35
|
+
# before matching so they do not trip the broad --force patterns; a bare
|
|
36
|
+
# --force or -f left behind still blocks.
|
|
37
|
+
SAFE_STRIPPED=$(printf '%s' "$NORMALIZED" | sed -E 's/--force-with-lease(=[^ ]*)?//g; s/--force-if-includes//g')
|
|
38
|
+
|
|
21
39
|
# Destructive patterns — word-boundary aware where possible
|
|
22
40
|
DESTRUCTIVE_PATTERNS=(
|
|
23
41
|
# rm variants (short flags, long flags, separated flags, sudo, xargs/find piped)
|
|
@@ -69,7 +87,7 @@ DESTRUCTIVE_PATTERNS=(
|
|
|
69
87
|
# Build combined regex
|
|
70
88
|
REGEX=$(IFS='|'; echo "${DESTRUCTIVE_PATTERNS[*]}")
|
|
71
89
|
|
|
72
|
-
if echo "$
|
|
90
|
+
if echo "$SAFE_STRIPPED" | grep -qEi "($REGEX)"; then
|
|
73
91
|
echo "WARNING: Potentially destructive command detected. Please verify." >&2
|
|
74
92
|
exit 2
|
|
75
93
|
fi
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# loop-guard.sh — Advisory detection of repeated identical actions (stuck loops).
|
|
3
|
+
#
|
|
4
|
+
# Fires on: PostToolUse (Bash|Edit|MultiEdit|Write)
|
|
5
|
+
# Never blocks. Emits an advisory when the SAME tool action repeats at least
|
|
6
|
+
# THRESHOLD times within a short window — catching successful-but-identical
|
|
7
|
+
# loops that the /repeat circuit breaker (which only counts FAILURES) misses.
|
|
8
|
+
# Skipped in minimal profile; honours AI_TOOLKIT_DISABLED_HOOKS.
|
|
9
|
+
#
|
|
10
|
+
# Tunables: AI_TOOLKIT_LOOP_WINDOW (default 6), AI_TOOLKIT_LOOP_THRESHOLD (3).
|
|
11
|
+
|
|
12
|
+
# shellcheck source=_profile-check.sh
|
|
13
|
+
source "$(dirname "$0")/_profile-check.sh"
|
|
14
|
+
# shellcheck source=_hook-io.sh
|
|
15
|
+
source "$(dirname "$0")/_hook-io.sh"
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
TOOL_NAME=$(hook_tool_name)
|
|
19
|
+
[ -z "$TOOL_NAME" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# Build an identity for the action. For Bash it is the command; for edits it is
|
|
22
|
+
# file + new content, so normal iterative editing of one file does NOT trip the
|
|
23
|
+
# guard — only re-applying an IDENTICAL change does.
|
|
24
|
+
CMD=$(hook_command)
|
|
25
|
+
if [ -n "$CMD" ]; then
|
|
26
|
+
IDENT="$CMD"
|
|
27
|
+
else
|
|
28
|
+
FILE=$(hook_file_path)
|
|
29
|
+
[ -z "$FILE" ] && exit 0
|
|
30
|
+
IDENT="${FILE}::$(hook_new_content)"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
SID=$(hook_session_id)
|
|
34
|
+
STATE_DIR="${HOME}/.softspark/ai-toolkit/sessions"
|
|
35
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
|
|
36
|
+
LOG="${STATE_DIR}/${SID}-actions.log"
|
|
37
|
+
|
|
38
|
+
WINDOW="${AI_TOOLKIT_LOOP_WINDOW:-6}"
|
|
39
|
+
THRESHOLD="${AI_TOOLKIT_LOOP_THRESHOLD:-3}"
|
|
40
|
+
|
|
41
|
+
# Store only a short hash of "tool|identity" — never the raw payload.
|
|
42
|
+
HASH=$(printf '%s|%s' "$TOOL_NAME" "$IDENT" | { shasum -a 256 2>/dev/null || sha256sum 2>/dev/null; } | cut -c1-16)
|
|
43
|
+
[ -z "$HASH" ] && exit 0
|
|
44
|
+
printf '%s\n' "$HASH" >> "$LOG" 2>/dev/null || exit 0
|
|
45
|
+
|
|
46
|
+
# Keep only the last WINDOW entries so the file stays tiny.
|
|
47
|
+
lines=$(wc -l < "$LOG" 2>/dev/null | tr -d ' ')
|
|
48
|
+
if [ -n "$lines" ] && [ "$lines" -gt "$WINDOW" ]; then
|
|
49
|
+
tail -n "$WINDOW" "$LOG" > "${LOG}.tmp" 2>/dev/null && mv "${LOG}.tmp" "$LOG" 2>/dev/null
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
COUNT=$(grep -cxF "$HASH" "$LOG" 2>/dev/null | tr -d ' ')
|
|
53
|
+
[ -z "$COUNT" ] && COUNT=0
|
|
54
|
+
if [ "$COUNT" -ge "$THRESHOLD" ]; then
|
|
55
|
+
AI_TOOLKIT_HOOK_FORMAT=json
|
|
56
|
+
hook_emit_context "PostToolUse" \
|
|
57
|
+
"Loop guard: the same ${TOOL_NAME} action has repeated ${COUNT}x within the last ${WINDOW} steps. If you are not making progress, stop and reassess — try a different approach or ask the user — instead of retrying the identical action."
|
|
58
|
+
fi
|
|
59
|
+
exit 0
|
|
@@ -58,9 +58,11 @@ if [ -f "$SESSION_FILE" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ] && [ "${AI
|
|
|
58
58
|
printf '%s\n' "====================="
|
|
59
59
|
fi
|
|
60
60
|
|
|
61
|
-
# 3. Load active instincts (if any)
|
|
61
|
+
# 3. Load active instincts (if any). These are explicit, hand-authored files, so
|
|
62
|
+
# they load by default whenever present (unlike auto-saved session context above,
|
|
63
|
+
# which stays verbose-gated). No instincts on disk => no output, no token cost.
|
|
62
64
|
INSTINCTS_DIR=".claude/instincts"
|
|
63
|
-
if [ -d "$INSTINCTS_DIR" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ] &&
|
|
65
|
+
if [ -d "$INSTINCTS_DIR" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ] && ls "$INSTINCTS_DIR"/*.md >/dev/null 2>&1; then
|
|
64
66
|
printf '%s\n' "=== Active Instincts ==="
|
|
65
67
|
for f in "$INSTINCTS_DIR"/*.md; do
|
|
66
68
|
printf '%s\n' "- $(head -1 "$f")"
|
package/app/hooks.json
CHANGED
|
@@ -139,6 +139,16 @@
|
|
|
139
139
|
}
|
|
140
140
|
]
|
|
141
141
|
},
|
|
142
|
+
{
|
|
143
|
+
"_source": "ai-toolkit",
|
|
144
|
+
"matcher": "Bash|Edit|MultiEdit|Write",
|
|
145
|
+
"hooks": [
|
|
146
|
+
{
|
|
147
|
+
"type": "command",
|
|
148
|
+
"command": "\"$HOME/.softspark/ai-toolkit/hooks/loop-guard.sh\""
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
},
|
|
142
152
|
{
|
|
143
153
|
"_source": "ai-toolkit",
|
|
144
154
|
"matcher": "Edit|MultiEdit|Write",
|
|
@@ -5,13 +5,13 @@ Persistent session memory with SQLite storage and full-text search.
|
|
|
5
5
|
## Includes
|
|
6
6
|
- Skills: `/mem-search` (FTS5 search across past sessions)
|
|
7
7
|
- Hooks: `observation-capture.sh` (PostToolUse), `session-summary.sh` (Stop)
|
|
8
|
-
- Scripts: `init_db.py` (database setup), `strip_private.py` (privacy filter)
|
|
8
|
+
- Scripts: `init_db.py` (database setup), `strip_private.py` (privacy filter + secret redaction)
|
|
9
9
|
|
|
10
10
|
## How It Works
|
|
11
11
|
1. **Capture**: Every tool use is recorded to `~/.softspark/ai-toolkit/memory.db` (PostToolUse hook)
|
|
12
12
|
2. **Summarize**: Session summary generated on each Stop hook (tools used, observation count, time range)
|
|
13
13
|
3. **Search**: `/mem-search <query>` runs FTS5 full-text search with progressive disclosure
|
|
14
|
-
4. **Privacy**:
|
|
14
|
+
4. **Privacy**: `strip_private.py` runs before storage — it removes `<private>...</private>` blocks and redacts high-confidence secrets (API keys, tokens, private keys) to `[REDACTED:<label>]`
|
|
15
15
|
|
|
16
16
|
## Database Schema
|
|
17
17
|
- `sessions` — one row per Claude Code session (id, project_dir, started_at, ended_at, summary)
|
|
@@ -1,20 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Sanitize stdin before it is stored in the session memory database.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Two passes:
|
|
5
|
+
1. Remove ``<private>...</private>`` blocks (explicit user opt-out).
|
|
6
|
+
2. Redact high-confidence secrets (API keys, tokens, private keys) so a token
|
|
7
|
+
that scrolls through a tool output never lands in the SQLite memory store.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Used by observation-capture.sh and session-summary.sh.
|
|
10
|
+
|
|
11
|
+
Usage: echo "text <private>secret</private> sk-..." | python3 strip_private.py
|
|
8
12
|
"""
|
|
9
13
|
from __future__ import annotations
|
|
10
14
|
|
|
11
15
|
import re
|
|
12
16
|
import sys
|
|
13
17
|
|
|
18
|
+
# High-confidence, structural secret patterns only. These match a specific
|
|
19
|
+
# token shape (not "password = ..." prose) so false positives stay near zero —
|
|
20
|
+
# important because over-redaction silently corrupts stored observations.
|
|
21
|
+
_SECRET_PATTERNS: list[tuple[re.Pattern[str], str]] = [
|
|
22
|
+
(re.compile(r"AKIA[0-9A-Z]{16}"), "aws-key"),
|
|
23
|
+
(re.compile(r"\bsk-[A-Za-z0-9]{20,}"), "api-key"),
|
|
24
|
+
(re.compile(r"\bgh[posru]_[A-Za-z0-9]{36,}"), "github-token"),
|
|
25
|
+
(re.compile(r"\bxox[baprs]-[A-Za-z0-9-]{10,}"), "slack-token"),
|
|
26
|
+
(re.compile(r"\bAIza[0-9A-Za-z_\-]{35}"), "google-key"),
|
|
27
|
+
(re.compile(
|
|
28
|
+
r"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"
|
|
29
|
+
r"[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----"
|
|
30
|
+
), "private-key"),
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def scrub_secrets(text: str) -> str:
|
|
35
|
+
"""Replace recognised secret tokens with a labelled redaction marker."""
|
|
36
|
+
for pattern, label in _SECRET_PATTERNS:
|
|
37
|
+
text = pattern.sub(f"[REDACTED:{label}]", text)
|
|
38
|
+
return text
|
|
39
|
+
|
|
14
40
|
|
|
15
41
|
def main() -> None:
|
|
16
42
|
content = sys.stdin.read()
|
|
17
43
|
cleaned = re.sub(r"<private>.*?</private>", "", content, flags=re.DOTALL)
|
|
44
|
+
cleaned = scrub_secrets(cleaned)
|
|
18
45
|
sys.stdout.write(cleaned)
|
|
19
46
|
|
|
20
47
|
|
|
@@ -23,6 +23,7 @@ Create a new Claude Code hook following ai-toolkit conventions.
|
|
|
23
23
|
| `SessionEnd` | Session is closing | any | Flush logs, save transcripts |
|
|
24
24
|
| `UserPromptSubmit` | User submits a prompt | any | Prompt governance, usage tracking |
|
|
25
25
|
| `Notification` | Claude sends a notification | any | OS alerts, Slack pings |
|
|
26
|
+
| `MessageDisplay` | Assistant message is about to be shown to the user | any | Transform or hide assistant message text before display |
|
|
26
27
|
|
|
27
28
|
### Tool lifecycle
|
|
28
29
|
|
|
@@ -13,9 +13,9 @@ $ARGUMENTS
|
|
|
13
13
|
|
|
14
14
|
## What This Does
|
|
15
15
|
|
|
16
|
-
Manages the instinct system that
|
|
16
|
+
Manages the instinct system: project-local behavioral notes that Claude loads at the start of a session.
|
|
17
17
|
|
|
18
|
-
Instincts are
|
|
18
|
+
Instincts are markdown files in `.claude/instincts/`, loaded at session start by `session-start.sh` whenever any exist (set `AI_TOOLKIT_HOOK_QUIET=1` to suppress).
|
|
19
19
|
|
|
20
20
|
## Commands
|
|
21
21
|
|
|
@@ -48,10 +48,10 @@ Interactive review: shows each instinct and asks promote/remove/keep.
|
|
|
48
48
|
|
|
49
49
|
## How Instincts Work
|
|
50
50
|
|
|
51
|
-
1. **
|
|
52
|
-
2. **Storage**:
|
|
53
|
-
3. **Loading**: At session start, instincts
|
|
54
|
-
4. **Curation**: Use `/instinct-review` to promote
|
|
51
|
+
1. **Authoring**: Instinct files are written by hand — one markdown file per pattern in `.claude/instincts/`. There is no automatic session-end extractor today (see [When NOT to Use](#when-not-to-use)).
|
|
52
|
+
2. **Storage**: `.claude/instincts/<pattern-name>.md`, each carrying a confidence score (see format below).
|
|
53
|
+
3. **Loading**: At session start, `session-start.sh` loads every instinct file into context whenever any exist. No instincts on disk means nothing is loaded; set `AI_TOOLKIT_HOOK_QUIET=1` to suppress.
|
|
54
|
+
4. **Curation**: Use `/instinct-review` to list, promote, remove, or clear them.
|
|
55
55
|
|
|
56
56
|
## Instinct Format
|
|
57
57
|
|
|
@@ -88,14 +88,14 @@ Last seen: 2026-03-25
|
|
|
88
88
|
## Gotchas
|
|
89
89
|
|
|
90
90
|
- Instincts live in **project-local** `.claude/instincts/`, not in `~/.softspark/ai-toolkit/`. Running this skill in a different project sees a different set — do not treat the list as global state.
|
|
91
|
-
-
|
|
91
|
+
- Every instinct file loads into session context by default (whenever the directory is non-empty), so each one costs startup tokens. This is why curation matters — prune aggressively and keep the set small.
|
|
92
92
|
- Promoted instincts (confidence = 1.0) load into every session's context, costing tokens. Too many pinned instincts bloat startup. Keep ≤10 pinned.
|
|
93
|
-
- Instinct files are plain markdown
|
|
93
|
+
- Instinct files are plain markdown following the format above. If you accumulate many, a `--clear` and re-author is often cleaner than migrating stale notes after a major toolkit bump.
|
|
94
94
|
- The filename (`<pattern-name>.md`) is the identity used by `--promote` and `--remove`. Renaming files manually breaks those flags until the user reopens the review UI.
|
|
95
95
|
|
|
96
96
|
## When NOT to Use
|
|
97
97
|
|
|
98
|
-
- To **extract**
|
|
98
|
+
- To **auto-extract** instincts from a session — not implemented. No Stop hook writes instinct files; author them by hand (or via a future extraction tool). This skill curates existing files, it does not generate them.
|
|
99
99
|
- To search past session memory — use `/mem-search`
|
|
100
100
|
- To edit global toolkit memory files — those live in `~/.claude/projects/*/memory/` and are managed by the auto-memory system, not this skill
|
|
101
|
-
- To
|
|
101
|
+
- To stop instincts from loading — delete the files with `--clear`, or set `AI_TOOLKIT_HOOK_QUIET=1` to suppress all session-start output. There is no extraction process to disable.
|
|
@@ -73,6 +73,16 @@ Scan skill and agent definitions for security risks before installation or after
|
|
|
73
73
|
| `-----BEGIN.*PRIVATE KEY` | HIGH | Private key |
|
|
74
74
|
| `ghp_[a-zA-Z0-9]{36}` | HIGH | GitHub PAT |
|
|
75
75
|
|
|
76
|
+
### Unicode Safety
|
|
77
|
+
|
|
78
|
+
Shipped prompt text (skills, agents, rules, personas, mcp-templates) is scanned for invisible/smuggled characters that a human reviewer cannot see. `U+200D` (ZWJ) is allowlisted so legitimate emoji sequences do not flag.
|
|
79
|
+
|
|
80
|
+
| Pattern | Severity | Description |
|
|
81
|
+
|---------|----------|-------------|
|
|
82
|
+
| Tag block `U+E0000–U+E007F` | HIGH | ASCII smuggling / invisible prompt injection |
|
|
83
|
+
| Bidi controls (LRE/RLE/PDF/LRO/RLO, LRI/RLI/FSI/PDI) | HIGH | Trojan Source text reordering |
|
|
84
|
+
| Zero-width / invisible format chars (ZWSP, ZWNJ, WJ, BOM, soft hyphen, …) | WARN | Verify it is intentional |
|
|
85
|
+
|
|
76
86
|
## Output Format
|
|
77
87
|
|
|
78
88
|
```markdown
|
|
@@ -105,6 +105,25 @@ CORRECT: Agent reports success → Check VCS diff → Verify changes → Report
|
|
|
105
105
|
WRONG: Trust agent report at face value
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
## Live-App Rubric Verification (optional)
|
|
109
|
+
|
|
110
|
+
When the success criterion is **behavioral or visual** (a UI flow, a generated app, a multi-step interaction), a pass/fail command is not enough — the proof is the running app, observed. Use a weighted rubric instead of a single assertion:
|
|
111
|
+
|
|
112
|
+
1. **Define the rubric BEFORE building** — 3–6 criteria, each with a weight and an explicit pass bar. Example:
|
|
113
|
+
|
|
114
|
+
| Criterion | Weight | Pass bar |
|
|
115
|
+
|-----------|--------|----------|
|
|
116
|
+
| Core flow completes end-to-end | 0.40 | No error, reaches success state |
|
|
117
|
+
| Empty / loading / error states render | 0.25 | All three visible |
|
|
118
|
+
| Matches the requested layout | 0.20 | No major deviation |
|
|
119
|
+
| No console errors | 0.15 | Console clean |
|
|
120
|
+
|
|
121
|
+
2. **Launch the app and observe** — actually run it and capture the behavior (screenshot, console, network). Do not infer from the source.
|
|
122
|
+
3. **Score with a fresh evaluator** — have an independent agent grade the observed behavior against the rubric, not the implementer who wrote it (self-grading anchors high). Compute the weighted score.
|
|
123
|
+
4. **Gate on the threshold** — below the bar (e.g. < 0.8) the claim is NOT verified: list the failing criteria as concrete defects and iterate. At or above the bar, state the score WITH the captured evidence.
|
|
124
|
+
|
|
125
|
+
The rubric is the verification command for work that has no green/red exit code. The same Iron Law applies: observed evidence before the claim, every time.
|
|
126
|
+
|
|
108
127
|
## Constitutional Anchors
|
|
109
128
|
|
|
110
129
|
This skill enforces **Constitution Art. VI.4 (Verify Before Claiming Done)**. The diff re-read is not optional: before any completion claim, confirm no orphaned references, no missing test coverage for changed paths, no stale docs. A task is not done while any of those exist.
|