continuous-improvement 3.1.0 → 3.9.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/.claude-plugin/marketplace.json +66 -0
- package/CHANGELOG.md +216 -0
- package/QUICKSTART.md +79 -12
- package/README.md +229 -349
- package/SKILL.md +87 -9
- package/action.yml +1 -1
- package/bin/analyze.sh +9 -1
- package/bin/backfill.mjs +172 -0
- package/bin/check-doc-runtime-claims.mjs +120 -0
- package/bin/check-docs-substrings.mjs +333 -0
- package/bin/check-everything-mirror.mjs +145 -0
- package/bin/check-routing-targets.mjs +151 -0
- package/bin/check-skill-law-tag.mjs +128 -0
- package/bin/check-skill-mirror.mjs +119 -0
- package/bin/check-skill-tiers.mjs +116 -0
- package/bin/check-third-party-shape.mjs +202 -0
- package/bin/generate-plugin-manifests.mjs +171 -0
- package/bin/harvest-friction.mjs +279 -0
- package/bin/hook-stats.mjs +258 -0
- package/bin/install.mjs +407 -499
- package/bin/lint-transcript.mjs +182 -210
- package/bin/mcp-server.mjs +834 -608
- package/bin/observe.mjs +148 -0
- package/bin/pre-commit-block-strays.sh +49 -0
- package/bin/refresh-third-party.mjs +416 -0
- package/bin/unified-cli.mjs +533 -0
- package/commands/continuous-improvement.md +43 -2
- package/commands/discipline.md +14 -0
- package/commands/harvest.md +76 -0
- package/commands/learn-eval.md +117 -0
- package/commands/planning-with-files.md +66 -0
- package/commands/proceed-with-the-recommendation.md +62 -0
- package/commands/ralph.md +103 -0
- package/commands/release-train.md +81 -0
- package/commands/seven-laws.md +16 -0
- package/commands/superpowers.md +180 -0
- package/commands/swarm.md +101 -0
- package/commands/workspace-surface-audit.md +77 -0
- package/hooks/gateguard.mjs +172 -0
- package/hooks/observe.sh +42 -4
- package/hooks/session.sh +3 -3
- package/hooks/three-section-close.mjs +181 -0
- package/instinct-packs/meta.json +16 -0
- package/lib/cli-anything.mjs +401 -0
- package/lib/compound-engineering.mjs +831 -0
- package/lib/gateguard-state.mjs +85 -0
- package/lib/observe-event.mjs +128 -0
- package/lib/plugin-metadata.mjs +435 -0
- package/lib/pm-marketplace.mjs +61 -0
- package/lib/pm-skills.mjs +1274 -0
- package/lib/resolve-home-dir.mjs +43 -0
- package/lib/skill-tiers.mjs +137 -0
- package/lib/unified-plugin.mjs +924 -0
- package/llms.txt +32 -7
- package/package.json +29 -19
- package/plugins/beginner.json +17 -6
- package/plugins/continuous-improvement/.claude-plugin/marketplace.json +20 -0
- package/plugins/continuous-improvement/.claude-plugin/plugin.json +26 -0
- package/plugins/continuous-improvement/LICENSE +21 -0
- package/plugins/continuous-improvement/README.md +57 -0
- package/plugins/continuous-improvement/agents/README.md +120 -0
- package/plugins/continuous-improvement/agents/code-reviewer.md +97 -0
- package/plugins/continuous-improvement/agents/security-auditor.md +101 -0
- package/plugins/continuous-improvement/agents/test-engineer.md +95 -0
- package/plugins/continuous-improvement/bin/backfill.mjs +172 -0
- package/plugins/continuous-improvement/bin/mcp-server.mjs +889 -0
- package/plugins/continuous-improvement/bin/observe.mjs +148 -0
- package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -0
- package/plugins/continuous-improvement/commands/dashboard.md +56 -0
- package/plugins/continuous-improvement/commands/discipline.md +51 -0
- package/plugins/continuous-improvement/commands/harvest.md +76 -0
- package/plugins/continuous-improvement/commands/learn-eval.md +117 -0
- package/plugins/continuous-improvement/commands/planning-with-files.md +66 -0
- package/plugins/continuous-improvement/commands/proceed-with-the-recommendation.md +62 -0
- package/plugins/continuous-improvement/commands/ralph.md +103 -0
- package/plugins/continuous-improvement/commands/release-train.md +81 -0
- package/plugins/continuous-improvement/commands/seven-laws.md +16 -0
- package/plugins/continuous-improvement/commands/superpowers.md +180 -0
- package/plugins/continuous-improvement/commands/swarm.md +101 -0
- package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -0
- package/plugins/continuous-improvement/hooks/gateguard.mjs +172 -0
- package/plugins/continuous-improvement/hooks/hooks.json +65 -0
- package/plugins/continuous-improvement/hooks/observe.sh +172 -0
- package/plugins/continuous-improvement/hooks/session.sh +106 -0
- package/plugins/continuous-improvement/hooks/three-section-close.mjs +181 -0
- package/plugins/continuous-improvement/instinct-packs/go.json +58 -0
- package/plugins/continuous-improvement/instinct-packs/meta.json +16 -0
- package/plugins/continuous-improvement/instinct-packs/python.json +58 -0
- package/plugins/continuous-improvement/instinct-packs/react.json +58 -0
- package/plugins/continuous-improvement/lib/observe-event.mjs +128 -0
- package/plugins/continuous-improvement/lib/plugin-metadata.mjs +435 -0
- package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +43 -0
- package/plugins/continuous-improvement/skills/README.md +37 -0
- package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +249 -0
- package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +178 -0
- package/plugins/continuous-improvement/skills/gateguard/SKILL.md +163 -0
- package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +108 -0
- package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +447 -0
- package/plugins/continuous-improvement/skills/ralph/SKILL.md +221 -0
- package/plugins/continuous-improvement/skills/recovery-classification/SKILL.md +73 -0
- package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +76 -0
- package/plugins/continuous-improvement/skills/state-reconciliation/SKILL.md +63 -0
- package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +104 -0
- package/plugins/continuous-improvement/skills/superpowers/SKILL.md +219 -0
- package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -0
- package/plugins/continuous-improvement/skills/token-budget-advisor/SKILL.md +136 -0
- package/plugins/continuous-improvement/skills/verification-loop/SKILL.md +224 -0
- package/plugins/continuous-improvement/skills/wild-risa-balance/SKILL.md +191 -0
- package/plugins/continuous-improvement/skills/workspace-surface-audit/SKILL.md +147 -0
- package/plugins/continuous-improvement/skills/worktree-safety/SKILL.md +66 -0
- package/plugins/continuous-improvement/templates/planning-with-files/findings.md +8 -0
- package/plugins/continuous-improvement/templates/planning-with-files/progress.md +7 -0
- package/plugins/continuous-improvement/templates/planning-with-files/task_plan.md +23 -0
- package/plugins/expert.json +26 -5
- package/skills/README.md +79 -0
- package/skills/deploy-receipt.md +178 -0
- package/skills/gateguard.md +163 -0
- package/skills/para-memory-files.md +108 -0
- package/skills/proceed-with-the-recommendation.md +447 -0
- package/skills/ralph.md +221 -0
- package/skills/recovery-classification.md +73 -0
- package/skills/safety-guard.md +76 -0
- package/skills/state-reconciliation.md +63 -0
- package/skills/strategic-compact.md +104 -0
- package/skills/superpowers.md +219 -0
- package/skills/tdd-workflow.md +411 -0
- package/skills/token-budget-advisor.md +136 -0
- package/skills/verification-loop.md +224 -0
- package/skills/wild-risa-balance.md +191 -0
- package/skills/workspace-surface-audit.md +147 -0
- package/skills/worktree-safety.md +66 -0
- package/templates/insights-claude-md.md +91 -0
- package/templates/planning-with-files/findings.md +8 -0
- package/templates/planning-with-files/progress.md +7 -0
- package/templates/planning-with-files/task_plan.md +23 -0
- package/templates/verify-ladder.example.json +60 -0
package/SKILL.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: continuous-improvement
|
|
3
|
-
|
|
3
|
+
tier: core
|
|
4
|
+
description: "Install structured self-improvement loops with instinct-based learning into Claude Code — research, plan, execute, verify, reflect, learn, iterate. On-demand or weekly analysis to save tokens. Supports multi-agent parallel analysis."
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# continuous-improvement
|
|
@@ -28,9 +29,9 @@ Before executing, state:
|
|
|
28
29
|
## Law 3: One Thing at a Time
|
|
29
30
|
|
|
30
31
|
- Complete and verify one task before starting the next
|
|
31
|
-
- Never spawn parallel work for tasks you can do directly
|
|
32
32
|
- Never report completion until you've checked actual output
|
|
33
33
|
- If you want to "also quickly add" something — stop. Finish first.
|
|
34
|
+
- **Multi-agent OK:** Delegate independent, parallelizable work to sub-agents (e.g., security review + code review + tests in parallel). Each agent follows the 7 Laws independently. Only parallelize when tasks have no shared state.
|
|
34
35
|
|
|
35
36
|
## Law 4: Verify Before Reporting
|
|
36
37
|
|
|
@@ -50,10 +51,35 @@ After non-trivial tasks:
|
|
|
50
51
|
- What failed:
|
|
51
52
|
- What I'd do differently:
|
|
52
53
|
- Rule to add:
|
|
54
|
+
- Iteration — Next best recommendations (ranked, top 3):
|
|
55
|
+
1. <primary — strongest next move>
|
|
56
|
+
2. <alternative — different angle, if user wants to pivot>
|
|
57
|
+
3. <alternative — smaller/larger scope, if user wants to adjust>
|
|
53
58
|
```
|
|
54
59
|
|
|
55
60
|
The "Rule to add" field feeds Law 7 — it becomes an instinct with 0.6 starting confidence.
|
|
56
61
|
|
|
62
|
+
The "Iteration — Next best recommendations" field feeds Law 6. List the **top 3 ranked** core-development moves based on the current code state — what to build, fix, refactor, or investigate next so the feature/system advances. Item #1 is the strongest recommendation; #2 and #3 are alternative directions the user can pick from. NOT git plumbing (commit, push, PR), NOT pure CI ceremony (run tests, type-check), NOT deploy steps. Those belong in the end-of-run summary, not here.
|
|
63
|
+
|
|
64
|
+
Format per item: `<verb> <object at path:line> (<why, one clause grounded in current context>)`.
|
|
65
|
+
|
|
66
|
+
Good examples (development progression):
|
|
67
|
+
- `Implement settleWeekAndPostPrizes writer for quiz source in src/scheduled.ts (real_contest writer exists; quiz path is recognized but inert)`
|
|
68
|
+
- `Refactor contestModeGuard at src/routes/trading-contest.ts to share the 4-mode switch with /admin/mode (logic duplicated, drift risk)`
|
|
69
|
+
- `Investigate why Saturday cron occasionally skips Week activation in src/scheduled.ts:625 (one missed run on 2026-04-19; root cause unknown)`
|
|
70
|
+
- `Add server-side enforcement for the $100 new-deposit rule in real-contest entry handler (currently advisory; admin reviews post-contest)`
|
|
71
|
+
|
|
72
|
+
Anti-examples (rejected — these are workflow, not development):
|
|
73
|
+
- `Commit changes` / `Open PR` / `Push to origin` → belongs in summary, not here
|
|
74
|
+
- `Run vitest` / `Run tsc` → that is verification (Law 4), already done before reporting
|
|
75
|
+
- `Deploy to prod` → operational, needs-approval, never an autonomous next step
|
|
76
|
+
|
|
77
|
+
Rules:
|
|
78
|
+
- Always exactly 3 items, ranked. Not 2, not 5.
|
|
79
|
+
- All 3 must be distinct directions — do not pad with rephrases of #1.
|
|
80
|
+
- If fewer than 3 real moves exist, fill remaining slots with `None — goal met from this angle.` rather than inventing busywork.
|
|
81
|
+
- If the goal is fully met across all angles, write `1. None — goal met, stop.` and omit #2 and #3.
|
|
82
|
+
|
|
57
83
|
## Law 6: Iterate Means One Thing
|
|
58
84
|
|
|
59
85
|
One change → verify → next change.
|
|
@@ -74,6 +100,19 @@ If the user corrects you, the instinct weakens. If they don't, it strengthens.
|
|
|
74
100
|
|
|
75
101
|
Nothing learned is permanent. Everything decays without reinforcement.
|
|
76
102
|
|
|
103
|
+
### Friction Harvest Pipeline (`/harvest`)
|
|
104
|
+
|
|
105
|
+
Beyond the reflection-driven path above, the **friction harvest classifier** turns observation logs (`~/.claude/instincts/<project-hash>/observations.jsonl`) into typed instincts automatically. Run via `/harvest` or `node bin/harvest-friction.mjs`. Four typed friction patterns with confidence scoring:
|
|
106
|
+
|
|
107
|
+
- **`env_issue`** — jq missing, command not found, not recognized as cmdlet
|
|
108
|
+
- **`permission_block`** — sandbox / harness blocked, Permission denied
|
|
109
|
+
- **`wrong_approach`** — file changed since last read (parallel-actor stale)
|
|
110
|
+
- **`buggy_code`** — file not read first, old_string ambiguous, file too large
|
|
111
|
+
|
|
112
|
+
Idempotent: each instinct's `dedup_key = sha1(type + tool + summary[:120])`; re-running on the same observations does not duplicate previously-written instincts. Confidence weights frequency × recency-decay so old failures fade and recurring ones strengthen, in line with the Law 7 contract above.
|
|
113
|
+
|
|
114
|
+
The harvest is opt-in: it runs only when explicitly invoked. Cron / hook triggers are deliberately not wired so the operator stays in control of when the classifier reads observation history.
|
|
115
|
+
|
|
77
116
|
## The Loop
|
|
78
117
|
|
|
79
118
|
```
|
|
@@ -86,11 +125,23 @@ If you're skipping a step, that's the step you need most.
|
|
|
86
125
|
|
|
87
126
|
## Instinct System (Mulahazah)
|
|
88
127
|
|
|
89
|
-
|
|
128
|
+
### Execution Mode: On-Demand (Default)
|
|
129
|
+
|
|
130
|
+
The instinct system does **NOT** run automatically at session start. This saves tokens.
|
|
131
|
+
|
|
132
|
+
| Mode | When it runs | Token cost |
|
|
133
|
+
|------|-------------|------------|
|
|
134
|
+
| **On-demand** (default) | Only when user runs `/continuous-improvement` or `/dashboard` | Zero overhead per session |
|
|
135
|
+
| **Weekly** | User schedules via `/loop 7d /continuous-improvement analyze` or cron | One analysis per week |
|
|
136
|
+
| **Always-on** (opt-in) | Set `always_on: true` in project instinct config | Runs at every session start |
|
|
137
|
+
|
|
138
|
+
**To enable always-on:** Create `~/.claude/instincts/<hash>/config.yaml` with `always_on: true`. Otherwise, instincts are only loaded when explicitly requested.
|
|
139
|
+
|
|
140
|
+
**Hooks still capture silently** — observations accumulate in `observations.jsonl` with near-zero cost. The expensive part (reading, analyzing, creating instincts) only happens when you ask for it.
|
|
90
141
|
|
|
91
142
|
### Auto-Level Detection
|
|
92
143
|
|
|
93
|
-
|
|
144
|
+
When analysis is triggered (on-demand, weekly, or always-on), determine level:
|
|
94
145
|
|
|
95
146
|
1. **Find project hash:** Run `git rev-parse --show-toplevel 2>/dev/null`, then SHA-256 first 12 chars of the path
|
|
96
147
|
2. **Check observations:** Count lines in `~/.claude/instincts/<hash>/observations.jsonl`
|
|
@@ -105,9 +156,9 @@ Determine current level automatically:
|
|
|
105
156
|
|
|
106
157
|
Multiple levels can be active simultaneously — you might auto-apply some instincts while suggesting others.
|
|
107
158
|
|
|
108
|
-
###
|
|
159
|
+
### Analysis (On-Demand)
|
|
109
160
|
|
|
110
|
-
When
|
|
161
|
+
When triggered by `/continuous-improvement analyze`, weekly schedule, or always-on mode:
|
|
111
162
|
|
|
112
163
|
1. Read `observations.jsonl` (last 500 lines)
|
|
113
164
|
2. Read existing instincts (project + global `*.yaml` files)
|
|
@@ -119,6 +170,16 @@ When 20+ unprocessed observations exist, analyze them as part of session startup
|
|
|
119
170
|
4. Create/update instinct YAML files in the project directory
|
|
120
171
|
5. Be conservative: only create instincts for 3+ observations of the same pattern
|
|
121
172
|
|
|
173
|
+
### Multi-Agent Analysis
|
|
174
|
+
|
|
175
|
+
For large observation backlogs (500+ lines), parallelize analysis across agents:
|
|
176
|
+
|
|
177
|
+
- **Agent 1:** Analyze user corrections and error→fix sequences
|
|
178
|
+
- **Agent 2:** Analyze repeated workflows and tool preferences
|
|
179
|
+
- **Agent 3:** Cross-reference with existing instincts for updates/promotions
|
|
180
|
+
|
|
181
|
+
Each agent writes to separate temp files; the orchestrator merges results and deduplicates.
|
|
182
|
+
|
|
122
183
|
### Instinct Format
|
|
123
184
|
|
|
124
185
|
Each instinct is a YAML file in `~/.claude/instincts/<hash>/` or `~/.claude/instincts/global/`:
|
|
@@ -160,12 +221,29 @@ Cap: 0.9 max. Scope: default to project; promote to global when seen in 2+ proje
|
|
|
160
221
|
|
|
161
222
|
## /continuous-improvement Command
|
|
162
223
|
|
|
163
|
-
Run `/continuous-improvement`
|
|
224
|
+
Run `/continuous-improvement` when you want to reflect and learn — not every session.
|
|
164
225
|
|
|
165
226
|
1. **Reflect** — Generate Law 5 reflection
|
|
166
227
|
2. **Analyze** — Process pending observations into instincts
|
|
167
228
|
3. **Status** — Show all instincts with confidence and current level
|
|
168
229
|
|
|
169
230
|
Subcommands:
|
|
170
|
-
- `/continuous-improvement status` — Instinct overview only
|
|
171
|
-
- `/continuous-improvement analyze` —
|
|
231
|
+
- `/continuous-improvement status` — Instinct overview only (lightweight, reads YAML only)
|
|
232
|
+
- `/continuous-improvement analyze` — Process pending observations into instincts
|
|
233
|
+
- `/continuous-improvement weekly` — Set up weekly analysis schedule
|
|
234
|
+
- `/continuous-improvement always-on` — Enable/disable always-on mode for this project
|
|
235
|
+
|
|
236
|
+
## Planning-With-Files (Opt-In)
|
|
237
|
+
|
|
238
|
+
Use this workflow only when the user explicitly asks for persistent, file-based planning or asks to use Planning-With-Files.
|
|
239
|
+
|
|
240
|
+
- Detect the project root with `git rev-parse --show-toplevel`; if that fails, use the current working directory.
|
|
241
|
+
- Create and maintain three project-root files:
|
|
242
|
+
- `task_plan.md` — phases, status, questions, decisions, errors
|
|
243
|
+
- `findings.md` — research notes, sources, synthesized discoveries
|
|
244
|
+
- `progress.md` — session log, verification notes, checkpoints
|
|
245
|
+
- Default phases in `task_plan.md`: `Research`, `Plan`, `Execute`, `Verify`, `Reflect`
|
|
246
|
+
- Never create these files automatically for normal work. This workflow is opt-in.
|
|
247
|
+
- Never overwrite existing planning files unless the user explicitly asks to reset or replace them.
|
|
248
|
+
|
|
249
|
+
When resuming work, read the three files before making major decisions so context survives long tasks and new sessions.
|
package/action.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: 'AI Agent Discipline Linter'
|
|
2
|
-
description: '
|
|
2
|
+
description: 'Catch agents that skipped research, planning, or verification — by linting their transcript in CI against the 7 Laws of AI Agent Discipline.'
|
|
3
3
|
author: 'naimkatiman'
|
|
4
4
|
|
|
5
5
|
branding:
|
package/bin/analyze.sh
CHANGED
|
@@ -20,7 +20,15 @@ if [[ -z "$PROJECT_ROOT" ]]; then
|
|
|
20
20
|
PROJECT_ROOT="global"
|
|
21
21
|
fi
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
# Use shasum on macOS, sha256sum on Linux
|
|
24
|
+
if command -v sha256sum &>/dev/null; then
|
|
25
|
+
PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
|
|
26
|
+
elif command -v shasum &>/dev/null; then
|
|
27
|
+
PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | shasum -a 256 | cut -c1-12)"
|
|
28
|
+
else
|
|
29
|
+
# Fallback: use project root basename if no hashing available
|
|
30
|
+
PROJECT_HASH="$(basename "$PROJECT_ROOT" | tr -cd 'a-zA-Z0-9' | cut -c1-12)"
|
|
31
|
+
fi
|
|
24
32
|
PROJECT_NAME="$(basename "${PROJECT_ROOT%.git}")"
|
|
25
33
|
PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
|
|
26
34
|
OBS_FILE="${PROJECT_DIR}/observations.jsonl"
|
package/bin/backfill.mjs
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// backfill.mts — Tag legacy thin-schema observation rows for skip-clean
|
|
3
|
+
// consumption by the analysis pass.
|
|
4
|
+
//
|
|
5
|
+
// Walks ~/.claude/instincts/<hash>/observations.jsonl files and writes a
|
|
6
|
+
// `schema: "thin" | "rich"` field on every row that doesn't already have one.
|
|
7
|
+
// Per the plan doc: this is a flag-pass, not a content-recovery — the original
|
|
8
|
+
// thin rows lack the input we'd need to fabricate richer fields. Tagging lets
|
|
9
|
+
// the analysis pass cleanly skip thin rows and surface a "X% of observations
|
|
10
|
+
// were thin" stat to operators so they understand why their auto-instinct
|
|
11
|
+
// pipeline historically produced nothing.
|
|
12
|
+
//
|
|
13
|
+
// Output:
|
|
14
|
+
// - <home>/.claude/instincts/<hash>/observations.jsonl (rewritten in place
|
|
15
|
+
// with schema field appended; original preserved as .bak)
|
|
16
|
+
// - <home>/.claude/instincts/backfill-summary.json (per-project digest)
|
|
17
|
+
//
|
|
18
|
+
// Flags:
|
|
19
|
+
// --dry-run Read everything, write nothing; print counts to stdout.
|
|
20
|
+
//
|
|
21
|
+
// Idempotent: re-running on already-tagged rows is a no-op.
|
|
22
|
+
// Exit code: always 0. Failures land on stderr only.
|
|
23
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { homedir } from "node:os";
|
|
25
|
+
import { basename, join } from "node:path";
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const DRY_RUN = args.includes("--dry-run");
|
|
28
|
+
const HELP = args.includes("--help") || args.includes("-h");
|
|
29
|
+
if (HELP) {
|
|
30
|
+
console.log(`
|
|
31
|
+
backfill — Tag legacy thin-schema observation rows.
|
|
32
|
+
|
|
33
|
+
Usage:
|
|
34
|
+
npx continuous-improvement backfill Tag rows in place
|
|
35
|
+
npx continuous-improvement backfill --dry-run Report counts, write nothing
|
|
36
|
+
|
|
37
|
+
What it does:
|
|
38
|
+
Walks ~/.claude/instincts/<hash>/observations.jsonl files and adds a
|
|
39
|
+
schema field ("thin" or "rich") to every row that lacks one. Thin rows
|
|
40
|
+
came from the legacy bash fallback (jq missing) and have no input_summary;
|
|
41
|
+
rich rows came from the Node observer and have full tool_input.command /
|
|
42
|
+
Edit.file_path / etc.
|
|
43
|
+
|
|
44
|
+
Output:
|
|
45
|
+
- observations.jsonl rewritten in place (original kept as .bak)
|
|
46
|
+
- backfill-summary.json with per-project counts
|
|
47
|
+
|
|
48
|
+
Idempotent. Always exits 0 — failures go to stderr only.
|
|
49
|
+
`);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
main();
|
|
53
|
+
function main() {
|
|
54
|
+
try {
|
|
55
|
+
runBackfill();
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
process.stderr.write(`[backfill] ${error instanceof Error ? error.message : String(error)}\n`);
|
|
59
|
+
}
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
function runBackfill() {
|
|
63
|
+
const home = process.env.HOME || process.env.USERPROFILE || homedir();
|
|
64
|
+
const instinctsDir = join(home, ".claude", "instincts");
|
|
65
|
+
if (!existsSync(instinctsDir)) {
|
|
66
|
+
if (!DRY_RUN)
|
|
67
|
+
mkdirSync(instinctsDir, { recursive: true });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const projectDirs = listProjectDirs(instinctsDir);
|
|
71
|
+
const summary = {
|
|
72
|
+
total: 0,
|
|
73
|
+
thin: 0,
|
|
74
|
+
rich: 0,
|
|
75
|
+
projects: {},
|
|
76
|
+
generated_at: new Date().toISOString(),
|
|
77
|
+
};
|
|
78
|
+
for (const dir of projectDirs) {
|
|
79
|
+
const obsFile = join(dir, "observations.jsonl");
|
|
80
|
+
if (!existsSync(obsFile))
|
|
81
|
+
continue;
|
|
82
|
+
const projectId = basename(dir);
|
|
83
|
+
const stat = { total: 0, thin: 0, rich: 0 };
|
|
84
|
+
const newLines = [];
|
|
85
|
+
const corruptLines = [];
|
|
86
|
+
let mutated = false;
|
|
87
|
+
const raw = readFileSync(obsFile, "utf8");
|
|
88
|
+
for (const line of raw.split("\n")) {
|
|
89
|
+
const trimmed = line.trim();
|
|
90
|
+
if (trimmed.length === 0)
|
|
91
|
+
continue;
|
|
92
|
+
let row;
|
|
93
|
+
try {
|
|
94
|
+
row = JSON.parse(trimmed);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
process.stderr.write(`[backfill] skipped corrupt row in ${projectId}: ${trimmed.slice(0, 80)}\n`);
|
|
98
|
+
// Drop from main file but preserve in observations.corrupt.jsonl
|
|
99
|
+
// so the operator never silently loses data.
|
|
100
|
+
corruptLines.push(trimmed);
|
|
101
|
+
mutated = true;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
stat.total++;
|
|
105
|
+
summary.total++;
|
|
106
|
+
const previousSchema = row.schema;
|
|
107
|
+
const isRich = typeof row.input_summary === "string" && row.input_summary.length > 0;
|
|
108
|
+
const newSchema = isRich ? "rich" : "thin";
|
|
109
|
+
if (previousSchema !== newSchema) {
|
|
110
|
+
row.schema = newSchema;
|
|
111
|
+
mutated = true;
|
|
112
|
+
}
|
|
113
|
+
if (newSchema === "thin") {
|
|
114
|
+
stat.thin++;
|
|
115
|
+
summary.thin++;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
stat.rich++;
|
|
119
|
+
summary.rich++;
|
|
120
|
+
}
|
|
121
|
+
newLines.push(JSON.stringify(row));
|
|
122
|
+
}
|
|
123
|
+
summary.projects[projectId] = stat;
|
|
124
|
+
if (mutated && !DRY_RUN) {
|
|
125
|
+
// Atomic-ish write: backup → write → keep .bak for one cycle.
|
|
126
|
+
const backup = obsFile + ".bak";
|
|
127
|
+
renameSync(obsFile, backup);
|
|
128
|
+
writeFileSync(obsFile, newLines.join("\n") + "\n", "utf8");
|
|
129
|
+
if (corruptLines.length > 0) {
|
|
130
|
+
// Quarantine corrupt rows so operator data is never silently lost.
|
|
131
|
+
const corruptFile = join(dir, "observations.corrupt.jsonl");
|
|
132
|
+
const existing = existsSync(corruptFile) ? readFileSync(corruptFile, "utf8") : "";
|
|
133
|
+
writeFileSync(corruptFile, existing + corruptLines.join("\n") + "\n", "utf8");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (DRY_RUN) {
|
|
138
|
+
process.stdout.write(formatReport(summary, true));
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const summaryPath = join(instinctsDir, "backfill-summary.json");
|
|
142
|
+
writeFileSync(summaryPath, JSON.stringify(summary, null, 2) + "\n", "utf8");
|
|
143
|
+
process.stdout.write(formatReport(summary, false));
|
|
144
|
+
}
|
|
145
|
+
function listProjectDirs(instinctsDir) {
|
|
146
|
+
// Real project-hash dirs are sha256[:12] (hex). Test fixtures use 12-char
|
|
147
|
+
// alpha labels (g/h/i are not hex but make tests readable). Both are
|
|
148
|
+
// matched here; the broader pattern is harmless because real instincts/
|
|
149
|
+
// sub-dirs only ever contain hex hashes plus 'global' / loose dotfiles.
|
|
150
|
+
return readdirSync(instinctsDir)
|
|
151
|
+
.filter((name) => /^[0-9a-z]{12}$/.test(name))
|
|
152
|
+
.map((name) => join(instinctsDir, name))
|
|
153
|
+
.filter((dir) => {
|
|
154
|
+
try {
|
|
155
|
+
return statSync(dir).isDirectory();
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function formatReport(summary, isDryRun) {
|
|
163
|
+
const projects = Object.keys(summary.projects);
|
|
164
|
+
const lines = [
|
|
165
|
+
isDryRun ? "[backfill --dry-run] would tag the following:" : "[backfill] tagged:",
|
|
166
|
+
` total rows: ${summary.total}`,
|
|
167
|
+
` thin rows: ${summary.thin}`,
|
|
168
|
+
` rich rows: ${summary.rich}`,
|
|
169
|
+
` projects: ${projects.length}`,
|
|
170
|
+
];
|
|
171
|
+
return lines.join("\n") + "\n";
|
|
172
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Doc Runtime-Claims Check
|
|
4
|
+
*
|
|
5
|
+
* Codifies the audit-twice rule (memory: feedback_grep_hook_before_claim).
|
|
6
|
+
*
|
|
7
|
+
* Any user-facing line that claims a runtime hook / PreToolUse gate /
|
|
8
|
+
* physical block must be accompanied by a `hooks/<file>.mjs` reference
|
|
9
|
+
* inside a ±5-line window. Without that anchor, the doc is making a
|
|
10
|
+
* claim a future reader cannot verify against the actual filesystem.
|
|
11
|
+
*
|
|
12
|
+
* Origin: PR #105 shipped a smoke test that promised gateguard would
|
|
13
|
+
* physically block, when the hook did not yet exist. PR #108 built the
|
|
14
|
+
* hook. PR #115 reverted the inverse drift after the hook shipped but
|
|
15
|
+
* docs still said "roadmap." This lint locks the audit-twice rule into
|
|
16
|
+
* automation so the next runtime claim cannot land without its anchor.
|
|
17
|
+
*
|
|
18
|
+
* Scope: QUICKSTART.md, README.md, skills/*.md.
|
|
19
|
+
* Out of scope: bundle mirrors under plugins/, docs/, third-party/.
|
|
20
|
+
*
|
|
21
|
+
* Trigger phrases (case-insensitive substring match on the line):
|
|
22
|
+
* - "PreToolUse hook"
|
|
23
|
+
* - "physically block" (catches "physically blocks", "physical block")
|
|
24
|
+
* - "runtime gate"
|
|
25
|
+
*
|
|
26
|
+
* Deliberately narrow per the spec that authorized this lint. "runtime
|
|
27
|
+
* hook" was considered and dropped: too prone to false positives on
|
|
28
|
+
* disclaimers ("not a runtime hook", "no runtime hook is bundled").
|
|
29
|
+
* Future trigger additions need an explicit owner decision; do not bolt
|
|
30
|
+
* on phrases just because they sound related.
|
|
31
|
+
*
|
|
32
|
+
* Anchor: any reference matching `hooks/<name>.mjs` (case-insensitive)
|
|
33
|
+
* inside the ±5-line window (the trigger line itself plus 5 lines above
|
|
34
|
+
* and 5 lines below).
|
|
35
|
+
*
|
|
36
|
+
* Usage:
|
|
37
|
+
* node bin/check-doc-runtime-claims.mjs # Check the current repo
|
|
38
|
+
* node bin/check-doc-runtime-claims.mjs <repo-root> # Check a specific repo root
|
|
39
|
+
*
|
|
40
|
+
* Exit codes:
|
|
41
|
+
* 0 — every runtime-claim line has an in-window hooks/ anchor
|
|
42
|
+
* 1 — at least one claim line is unanchored
|
|
43
|
+
*/
|
|
44
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
45
|
+
import { join } from "node:path";
|
|
46
|
+
import { argv, cwd, exit } from "node:process";
|
|
47
|
+
const SCAN_FILES = ["QUICKSTART.md", "README.md"];
|
|
48
|
+
const SCAN_SKILL_DIR = "skills";
|
|
49
|
+
const ANCHOR_WINDOW = 5;
|
|
50
|
+
const TRIGGER_PHRASES = [
|
|
51
|
+
"pretooluse hook",
|
|
52
|
+
"physically block",
|
|
53
|
+
"runtime gate",
|
|
54
|
+
];
|
|
55
|
+
const ANCHOR_PATTERN = /hooks\/[a-z0-9-]+\.mjs/i;
|
|
56
|
+
function listSkillFiles(repoRoot) {
|
|
57
|
+
const skillsDir = join(repoRoot, SCAN_SKILL_DIR);
|
|
58
|
+
let entries;
|
|
59
|
+
try {
|
|
60
|
+
entries = readdirSync(skillsDir);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
return entries
|
|
66
|
+
.filter((f) => /^[a-z][a-z0-9-]*\.md$/.test(f))
|
|
67
|
+
.map((f) => join(SCAN_SKILL_DIR, f))
|
|
68
|
+
.sort();
|
|
69
|
+
}
|
|
70
|
+
function scanFile(repoRoot, relPath) {
|
|
71
|
+
const fullPath = join(repoRoot, relPath);
|
|
72
|
+
let content;
|
|
73
|
+
try {
|
|
74
|
+
content = readFileSync(fullPath, "utf8");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
const lines = content.split(/\r?\n/);
|
|
80
|
+
const violations = [];
|
|
81
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
82
|
+
const lower = lines[i].toLowerCase();
|
|
83
|
+
const matchedPhrase = TRIGGER_PHRASES.find((p) => lower.includes(p));
|
|
84
|
+
if (!matchedPhrase)
|
|
85
|
+
continue;
|
|
86
|
+
const start = Math.max(0, i - ANCHOR_WINDOW);
|
|
87
|
+
const end = Math.min(lines.length - 1, i + ANCHOR_WINDOW);
|
|
88
|
+
const window = lines.slice(start, end + 1).join("\n");
|
|
89
|
+
if (ANCHOR_PATTERN.test(window))
|
|
90
|
+
continue;
|
|
91
|
+
violations.push({
|
|
92
|
+
file: relPath,
|
|
93
|
+
line: i + 1,
|
|
94
|
+
text: lines[i].trim(),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return violations;
|
|
98
|
+
}
|
|
99
|
+
function main() {
|
|
100
|
+
const repoRoot = argv[2] ?? cwd();
|
|
101
|
+
const files = [...SCAN_FILES, ...listSkillFiles(repoRoot)];
|
|
102
|
+
const allViolations = [];
|
|
103
|
+
for (const file of files) {
|
|
104
|
+
allViolations.push(...scanFile(repoRoot, file));
|
|
105
|
+
}
|
|
106
|
+
if (allViolations.length === 0) {
|
|
107
|
+
console.log(`OK doc-runtime-claims: every runtime-claim line in ${files.length} scanned file(s) has a hooks/<name>.mjs anchor within ±${ANCHOR_WINDOW} lines.`);
|
|
108
|
+
exit(0);
|
|
109
|
+
}
|
|
110
|
+
console.error(`FAIL doc-runtime-claims: ${allViolations.length} unanchored runtime-claim line(s) found.`);
|
|
111
|
+
console.error(`Each line below contains a runtime-claim phrase but has no hooks/<name>.mjs reference within ±${ANCHOR_WINDOW} lines.`);
|
|
112
|
+
console.error("Add the hook file path inline so a reader can verify the claim against the filesystem.");
|
|
113
|
+
console.error("");
|
|
114
|
+
for (const v of allViolations) {
|
|
115
|
+
console.error(` ${v.file}:${v.line}`);
|
|
116
|
+
console.error(` ${v.text}`);
|
|
117
|
+
}
|
|
118
|
+
exit(1);
|
|
119
|
+
}
|
|
120
|
+
main();
|