continuous-improvement 3.23.0 → 3.25.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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +37 -0
- package/QUICKSTART.md +19 -20
- package/README.md +71 -18
- package/SKILL.md +4 -0
- package/bin/check-invariant-count.mjs +144 -0
- package/bin/check-routing-targets.mjs +74 -4
- package/bin/check-test-count.mjs +135 -0
- package/bin/companion-preference-status.mjs +2 -5
- package/bin/generate-plugin-manifests.mjs +21 -2
- package/bin/harvest-friction.mjs +10 -8
- package/bin/install.mjs +4 -14
- package/bin/mcp-server.mjs +4 -9
- package/bin/observe.mjs +3 -3
- package/bin/reconcile-instinct-hashes.mjs +226 -0
- package/bin/refresh-third-party.mjs +180 -10
- package/commands/discipline.md +5 -2
- package/commands/reconcile.md +1 -1
- package/commands/superpowers.md +2 -2
- package/commands/verify-install.md +8 -3
- package/hooks/companion-preference.mjs +3 -10
- package/hooks/config-guard.mjs +94 -0
- package/hooks/gateguard.mjs +39 -39
- package/hooks/goal-drift-stop.mjs +2 -2
- package/hooks/query-cost-nudge.mjs +2 -2
- package/hooks/recall-briefing.mjs +2 -2
- package/hooks/route-prompt.mjs +2 -5
- package/hooks/session.mjs +2 -2
- package/hooks/workflow-distill.mjs +2 -2
- package/lib/config-guard-gate.mjs +243 -0
- package/lib/destructive-bash.mjs +216 -0
- package/lib/gateguard-state.mjs +5 -1
- package/lib/plugin-metadata.mjs +12 -1
- package/lib/skill-catalog.mjs +169 -0
- package/llms.txt +12 -1
- package/package.json +5 -3
- package/plugins/beginner.json +1 -1
- package/plugins/continuous-improvement/.claude-plugin/marketplace.json +2 -2
- package/plugins/continuous-improvement/.claude-plugin/plugin.json +2 -2
- package/plugins/continuous-improvement/README.md +1 -2
- package/plugins/continuous-improvement/bin/mcp-server.mjs +4 -9
- package/plugins/continuous-improvement/bin/observe.mjs +3 -3
- package/plugins/continuous-improvement/commands/discipline.md +5 -2
- package/plugins/continuous-improvement/commands/reconcile.md +1 -1
- package/plugins/continuous-improvement/commands/superpowers.md +2 -2
- package/plugins/continuous-improvement/commands/verify-install.md +8 -3
- package/plugins/continuous-improvement/hooks/companion-preference.mjs +3 -10
- package/plugins/continuous-improvement/hooks/config-guard.mjs +94 -0
- package/plugins/continuous-improvement/hooks/gateguard.mjs +39 -39
- package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +2 -2
- package/plugins/continuous-improvement/hooks/hooks.json +10 -0
- package/plugins/continuous-improvement/hooks/query-cost-nudge.mjs +2 -2
- package/plugins/continuous-improvement/hooks/recall-briefing.mjs +2 -2
- package/plugins/continuous-improvement/hooks/route-prompt.mjs +2 -5
- package/plugins/continuous-improvement/hooks/session.mjs +2 -2
- package/plugins/continuous-improvement/hooks/workflow-distill.mjs +2 -2
- package/plugins/continuous-improvement/lib/config-guard-gate.mjs +243 -0
- package/plugins/continuous-improvement/lib/destructive-bash.mjs +216 -0
- package/plugins/continuous-improvement/lib/gateguard-state.mjs +5 -1
- package/plugins/continuous-improvement/lib/plugin-metadata.mjs +12 -1
- package/plugins/continuous-improvement/scripts/route-recommendation.routes.json +2 -2
- package/plugins/continuous-improvement/skills/README.md +0 -1
- package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +4 -0
- package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +1 -1
- package/plugins/continuous-improvement/skills/gateguard/SKILL.md +17 -2
- package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +2 -2
- package/plugins/continuous-improvement/skills/reconcile/SKILL.md +0 -1
- package/plugins/continuous-improvement/skills/superpowers/SKILL.md +5 -6
- package/plugins/expert.json +1 -1
- package/scripts/route-recommendation.routes.json +2 -2
- package/skills/README.md +1 -2
- package/skills/deploy-receipt.md +1 -1
- package/skills/gateguard.md +17 -2
- package/skills/proceed-with-the-recommendation.md +2 -2
- package/skills/reconcile.md +0 -1
- package/skills/superpowers.md +5 -6
- package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +0 -77
- package/skills/safety-guard.md +0 -77
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destructive-Bash classifier for hooks/gateguard.mjs.
|
|
3
|
+
*
|
|
4
|
+
* Two layers, pure and dependency-free so a table test and a future
|
|
5
|
+
* `gateguard-explain` CLI can drive it without spawning the hook:
|
|
6
|
+
*
|
|
7
|
+
* 1. Structured rules: the command is split at unquoted separators
|
|
8
|
+
* (`|`, `||`, `&&`, `;`, newline) and each simple command is tokenized,
|
|
9
|
+
* so flag order and spelling stop mattering. `rm -r -f`, `rm -Rf` and
|
|
10
|
+
* `rm --recursive --force` are the same command; `git clean -fdx` and
|
|
11
|
+
* `git clean --force -d` are the same command. Each rule has a stable id
|
|
12
|
+
* the deny reason prints, so a block is explainable.
|
|
13
|
+
* 2. The original substring list, kept verbatim as the fallback so nothing
|
|
14
|
+
* that was caught before stops being caught.
|
|
15
|
+
*
|
|
16
|
+
* What is deliberately NOT here: plain file writes through Bash (`cat > x`,
|
|
17
|
+
* `sed -i`). Gating those would break the operator workflow that uses Bash
|
|
18
|
+
* precisely to write files; that limit is disclosed on the site and README.
|
|
19
|
+
*
|
|
20
|
+
* Message-flag carve-out: the VALUE of a commit message, PR body, title, or
|
|
21
|
+
* note is prose, never a command. It is blanked before any rule runs, so
|
|
22
|
+
* `git commit -m "rm -rf the old helper"` stays allowed. `-c` is not a message
|
|
23
|
+
* flag: `bash -c "rm -rf /"` carries a real command and still gates.
|
|
24
|
+
*/
|
|
25
|
+
export const DESTRUCTIVE_PATTERNS = [
|
|
26
|
+
"rm -rf",
|
|
27
|
+
"rm -fr",
|
|
28
|
+
"git reset --hard",
|
|
29
|
+
"git push --force",
|
|
30
|
+
"git push -f",
|
|
31
|
+
"--force-with-lease",
|
|
32
|
+
"git branch -D",
|
|
33
|
+
"drop table",
|
|
34
|
+
"drop database",
|
|
35
|
+
"drop schema",
|
|
36
|
+
"truncate ",
|
|
37
|
+
"mkfs",
|
|
38
|
+
"dd if=",
|
|
39
|
+
"format ",
|
|
40
|
+
"rmdir /s",
|
|
41
|
+
"del /f /q",
|
|
42
|
+
"del /q /f",
|
|
43
|
+
"Remove-Item -Recurse",
|
|
44
|
+
"Remove-Item -Force",
|
|
45
|
+
];
|
|
46
|
+
// Flags whose VALUE is human prose (a commit message, a PR body) or a filename —
|
|
47
|
+
// never a command to execute. Their contents must not trip the destructive scan.
|
|
48
|
+
const MESSAGE_FLAG_RE = /(^|\s)(-m|--message|-F|--file|--body|--body-file|--title|--notes|-C|--reuse-message)(=|\s+)('[^']*'|"[^"]*"|\S+)/g;
|
|
49
|
+
/** Blank the value of every message/body flag; the flag itself is preserved. */
|
|
50
|
+
export function stripMessageArgs(command) {
|
|
51
|
+
return command.replace(MESSAGE_FLAG_RE, (_match, lead, flag) => `${lead}${flag} `);
|
|
52
|
+
}
|
|
53
|
+
// Split at shell separators that sit outside single or double quotes.
|
|
54
|
+
function splitSimpleCommands(command) {
|
|
55
|
+
const segments = [];
|
|
56
|
+
let current = "";
|
|
57
|
+
let quote = null;
|
|
58
|
+
for (let i = 0; i < command.length; i++) {
|
|
59
|
+
const ch = command[i];
|
|
60
|
+
if (quote) {
|
|
61
|
+
current += ch;
|
|
62
|
+
if (ch === quote)
|
|
63
|
+
quote = null;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (ch === '"' || ch === "'") {
|
|
67
|
+
quote = ch;
|
|
68
|
+
current += ch;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (ch === "\n" || ch === ";") {
|
|
72
|
+
segments.push(current);
|
|
73
|
+
current = "";
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (ch === "|" || ch === "&") {
|
|
77
|
+
// `||`, `&&`, `|`, `&` all end the simple command; collapse a doubled char.
|
|
78
|
+
if (command[i + 1] === ch)
|
|
79
|
+
i++;
|
|
80
|
+
segments.push(current);
|
|
81
|
+
current = "";
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
current += ch;
|
|
85
|
+
}
|
|
86
|
+
segments.push(current);
|
|
87
|
+
return segments.map((s) => s.trim()).filter((s) => s !== "");
|
|
88
|
+
}
|
|
89
|
+
function tokenize(segment) {
|
|
90
|
+
return segment.split(/\s+/).filter((t) => t !== "");
|
|
91
|
+
}
|
|
92
|
+
// Drop `sudo`, `env`, and leading VAR=value assignments so the command word is
|
|
93
|
+
// the first token the rules look at.
|
|
94
|
+
function commandTokens(segment) {
|
|
95
|
+
const tokens = tokenize(segment);
|
|
96
|
+
let i = 0;
|
|
97
|
+
while (i < tokens.length) {
|
|
98
|
+
const t = tokens[i];
|
|
99
|
+
if (t === "sudo" || t === "env" || t === "command" || /^[A-Za-z_][A-Za-z0-9_]*=/.test(t)) {
|
|
100
|
+
i++;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
return tokens.slice(i);
|
|
106
|
+
}
|
|
107
|
+
// For `git`, skip global options (`-C <dir>`, `-c k=v`, `--git-dir=…`) to find
|
|
108
|
+
// the subcommand and its own arguments.
|
|
109
|
+
function gitSubcommand(tokens) {
|
|
110
|
+
if (tokens[0] !== "git")
|
|
111
|
+
return null;
|
|
112
|
+
let i = 1;
|
|
113
|
+
while (i < tokens.length && tokens[i].startsWith("-")) {
|
|
114
|
+
const t = tokens[i];
|
|
115
|
+
i += t === "-C" || t === "-c" || t === "--git-dir" || t === "--work-tree" ? 2 : 1;
|
|
116
|
+
}
|
|
117
|
+
const sub = tokens[i];
|
|
118
|
+
if (!sub)
|
|
119
|
+
return null;
|
|
120
|
+
return { sub, args: tokens.slice(i + 1) };
|
|
121
|
+
}
|
|
122
|
+
const isShortFlag = (t) => /^-[A-Za-z]+$/.test(t);
|
|
123
|
+
const hasShortLetter = (t, letters) => isShortFlag(t) && letters.test(t.slice(1));
|
|
124
|
+
const STRUCTURED_RULES = [
|
|
125
|
+
{
|
|
126
|
+
id: "rm-recursive-force",
|
|
127
|
+
test: (tokens) => {
|
|
128
|
+
if (tokens[0] !== "rm")
|
|
129
|
+
return false;
|
|
130
|
+
const flags = tokens.slice(1).filter((t) => t.startsWith("-"));
|
|
131
|
+
const recursive = flags.some((t) => t === "--recursive" || hasShortLetter(t, /[rR]/));
|
|
132
|
+
const force = flags.some((t) => t === "--force" || hasShortLetter(t, /f/));
|
|
133
|
+
return recursive && force;
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "git-clean-force",
|
|
138
|
+
test: (tokens) => {
|
|
139
|
+
const git = gitSubcommand(tokens);
|
|
140
|
+
if (!git || git.sub !== "clean")
|
|
141
|
+
return false;
|
|
142
|
+
const dryRun = git.args.some((t) => t === "--dry-run" || hasShortLetter(t, /n/));
|
|
143
|
+
const force = git.args.some((t) => t === "--force" || hasShortLetter(t, /f/));
|
|
144
|
+
return force && !dryRun;
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "git-checkout-discard",
|
|
149
|
+
test: (tokens) => {
|
|
150
|
+
const git = gitSubcommand(tokens);
|
|
151
|
+
if (!git || git.sub !== "checkout")
|
|
152
|
+
return false;
|
|
153
|
+
const dash = git.args.indexOf("--");
|
|
154
|
+
if (dash !== -1)
|
|
155
|
+
return git.args.length > dash + 1; // `git checkout -- <pathspec>` discards
|
|
156
|
+
const first = git.args.find((t) => !t.startsWith("-"));
|
|
157
|
+
return first === "." || first === "./";
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "git-restore-discard",
|
|
162
|
+
test: (tokens) => {
|
|
163
|
+
const git = gitSubcommand(tokens);
|
|
164
|
+
if (!git || git.sub !== "restore")
|
|
165
|
+
return false;
|
|
166
|
+
const pathspec = git.args.some((t) => !t.startsWith("-"));
|
|
167
|
+
if (!pathspec)
|
|
168
|
+
return false;
|
|
169
|
+
const staged = git.args.some((t) => t === "--staged" || hasShortLetter(t, /S/));
|
|
170
|
+
const worktree = git.args.some((t) => t === "--worktree" || hasShortLetter(t, /W/));
|
|
171
|
+
return !staged || worktree; // `--staged` alone only unstages
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "find-delete",
|
|
176
|
+
test: (tokens) => tokens[0] === "find" && tokens.includes("-delete"),
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "git-push-plus-refspec",
|
|
180
|
+
test: (tokens) => {
|
|
181
|
+
const git = gitSubcommand(tokens);
|
|
182
|
+
if (!git || git.sub !== "push")
|
|
183
|
+
return false;
|
|
184
|
+
return git.args.some((t) => t.startsWith("+") && t.length > 1);
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "git-stash-drop",
|
|
189
|
+
test: (tokens) => {
|
|
190
|
+
const git = gitSubcommand(tokens);
|
|
191
|
+
if (!git || git.sub !== "stash")
|
|
192
|
+
return false;
|
|
193
|
+
return git.args[0] === "drop" || git.args[0] === "clear";
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
/** Classify one Bash command string. Pure; never throws on odd input. */
|
|
198
|
+
export function classifyDestructiveBash(command) {
|
|
199
|
+
if (typeof command !== "string" || command.trim() === "")
|
|
200
|
+
return { destructive: false, rule: null };
|
|
201
|
+
const stripped = stripMessageArgs(command);
|
|
202
|
+
for (const segment of splitSimpleCommands(stripped)) {
|
|
203
|
+
const tokens = commandTokens(segment);
|
|
204
|
+
if (tokens.length === 0)
|
|
205
|
+
continue;
|
|
206
|
+
for (const rule of STRUCTURED_RULES) {
|
|
207
|
+
if (rule.test(tokens))
|
|
208
|
+
return { destructive: true, rule: rule.id };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const lower = stripped.toLowerCase();
|
|
212
|
+
const hit = DESTRUCTIVE_PATTERNS.find((p) => lower.includes(p.toLowerCase()));
|
|
213
|
+
if (hit !== undefined)
|
|
214
|
+
return { destructive: true, rule: `substring:${hit}` };
|
|
215
|
+
return { destructive: false, rule: null };
|
|
216
|
+
}
|
package/lib/gateguard-state.mjs
CHANGED
|
@@ -45,7 +45,7 @@ export function resolveSessionDir(sessionId) {
|
|
|
45
45
|
if (fromEnv)
|
|
46
46
|
return fromEnv;
|
|
47
47
|
const projectRoot = resolveProjectRoot();
|
|
48
|
-
const projectHash =
|
|
48
|
+
const projectHash = hashProjectRoot(projectRoot);
|
|
49
49
|
const base = join(resolveInstinctsRoot(), projectHash);
|
|
50
50
|
const scoped = sanitizeSessionId(sessionId);
|
|
51
51
|
return scoped ? join(base, "sessions", scoped) : base;
|
|
@@ -160,6 +160,10 @@ export function canonicalizeProjectRoot(p) {
|
|
|
160
160
|
export function canonicalizeFileKey(p) {
|
|
161
161
|
return canonicalizePath(p);
|
|
162
162
|
}
|
|
163
|
+
/** SHA-256[:12] of the canonical project root. C:/x, c:/x, and C:\\x share a bucket. */
|
|
164
|
+
export function hashProjectRoot(projectRoot) {
|
|
165
|
+
return createHash("sha256").update(canonicalizeProjectRoot(projectRoot)).digest("hex").slice(0, 12);
|
|
166
|
+
}
|
|
163
167
|
export function isFileCleared(state, filePath) {
|
|
164
168
|
return canonicalizeFileKey(filePath) in state.cleared_files;
|
|
165
169
|
}
|
package/lib/plugin-metadata.mjs
CHANGED
|
@@ -26,7 +26,7 @@ const KEYWORDS = [
|
|
|
26
26
|
"transcript-linter",
|
|
27
27
|
];
|
|
28
28
|
const CLAUDE_PLUGIN_CATEGORY = "productivity";
|
|
29
|
-
const SHARED_PLUGIN_DESCRIPTION = "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as
|
|
29
|
+
const SHARED_PLUGIN_DESCRIPTION = "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as 28 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts.";
|
|
30
30
|
// Four vendored upstream companions registered alongside the CI plugin.
|
|
31
31
|
// Each entry points at a pinned-SHA snapshot under third-party/<name>/.
|
|
32
32
|
// See third-party/MANIFEST.md for refresh recipes and per-snapshot
|
|
@@ -474,6 +474,11 @@ export function getPluginHooksConfig() {
|
|
|
474
474
|
command: "node \"${CLAUDE_PLUGIN_ROOT}/hooks/hook-pack.mjs\"",
|
|
475
475
|
timeout: hookTimeoutSeconds,
|
|
476
476
|
};
|
|
477
|
+
const configGuardCommand = {
|
|
478
|
+
type: "command",
|
|
479
|
+
command: "node \"${CLAUDE_PLUGIN_ROOT}/hooks/config-guard.mjs\"",
|
|
480
|
+
timeout: hookTimeoutSeconds,
|
|
481
|
+
};
|
|
477
482
|
const observeCommand = {
|
|
478
483
|
type: "command",
|
|
479
484
|
command: "node \"${CLAUDE_PLUGIN_ROOT}/bin/observe.mjs\"",
|
|
@@ -538,6 +543,12 @@ export function getPluginHooksConfig() {
|
|
|
538
543
|
// Edit/Read/etc. Warn-default (CLAUDE_CI_HOOKPACK_GATE) — never blocks
|
|
539
544
|
// until the operator opts in.
|
|
540
545
|
{ matcher: "Bash", hooks: [hookPackCommand] },
|
|
546
|
+
// config-guard watches the files that wire the guardrails
|
|
547
|
+
// (.claude/settings*.json, .mcp.json, hooks.json, .claude/hooks/,
|
|
548
|
+
// .claude/plugins/, .claude-plugin/) and the `claude plugin|mcp|config`
|
|
549
|
+
// CLI forms. Matcher keeps read-only tools off the hot path.
|
|
550
|
+
// Warn-default (CI_CONFIG_GUARD) — denies only under block.
|
|
551
|
+
{ matcher: "Bash|Edit|MultiEdit|Write|NotebookEdit", hooks: [configGuardCommand] },
|
|
541
552
|
],
|
|
542
553
|
PostToolUse: [{ hooks: [observeCommand] }],
|
|
543
554
|
UserPromptSubmit: [{ hooks: [routePromptCommand, recallBriefingCommand] }],
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill -> enforcing hook filename. Explicit because hook filenames describe
|
|
3
|
+
* the event they fire on, not the skill they serve. Adding a hook without
|
|
4
|
+
* adding its row here makes the catalog under-claim, which is the safe
|
|
5
|
+
* direction.
|
|
6
|
+
*/
|
|
7
|
+
export const HOOK_BY_SKILL = {
|
|
8
|
+
gateguard: "gateguard.mjs",
|
|
9
|
+
"goal-monitor": "goal-drift-stop.mjs",
|
|
10
|
+
recall: "recall-briefing.mjs",
|
|
11
|
+
superpowers: "companion-preference.mjs",
|
|
12
|
+
"verification-loop": "typecheck-stop.mjs",
|
|
13
|
+
"skill-distillation": "workflow-distill.mjs",
|
|
14
|
+
};
|
|
15
|
+
const TIER_LABEL = {
|
|
16
|
+
core: "Core",
|
|
17
|
+
featured: "Featured",
|
|
18
|
+
"1": "Tier 1 — beginner",
|
|
19
|
+
"2": "Tier 2 — expert",
|
|
20
|
+
companion: "Always bundled",
|
|
21
|
+
unknown: "Unclassified",
|
|
22
|
+
};
|
|
23
|
+
const TIER_ORDER = ["core", "featured", "1", "2", "companion", "unknown"];
|
|
24
|
+
const ENFORCEMENT_TITLE = {
|
|
25
|
+
hook: "Enforced at the tool boundary by a hook — it fires whether or not the model cooperates.",
|
|
26
|
+
command: "Invoked by a slash command. Runs when you ask for it.",
|
|
27
|
+
prose: "Model-side discipline only. Nothing blocks and nothing fires automatically.",
|
|
28
|
+
};
|
|
29
|
+
/** Lift the Law tag out of a Law-tagged description. */
|
|
30
|
+
export function lawOf(description) {
|
|
31
|
+
if (!description)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (/\ball\s+7\s+Laws\b/i.test(description))
|
|
34
|
+
return "all 7";
|
|
35
|
+
if (/\bLaw\s+activator\b/i.test(description))
|
|
36
|
+
return "activator";
|
|
37
|
+
const multi = description.match(/\bLaw\s*([1-7])\s*\+\s*([1-7])\b/i);
|
|
38
|
+
if (multi)
|
|
39
|
+
return `${multi[1]} + ${multi[2]}`;
|
|
40
|
+
const single = description.match(/\bLaw\s*([1-7])\b/i);
|
|
41
|
+
return single ? single[1] : undefined;
|
|
42
|
+
}
|
|
43
|
+
/** Resolve the enforcement surface for one skill from the shipped file set. */
|
|
44
|
+
export function enforcementOf(name, commandNames) {
|
|
45
|
+
const hookFile = HOOK_BY_SKILL[name];
|
|
46
|
+
if (hookFile)
|
|
47
|
+
return { enforcement: "hook", hookFile };
|
|
48
|
+
if (commandNames.has(name))
|
|
49
|
+
return { enforcement: "command" };
|
|
50
|
+
return { enforcement: "prose" };
|
|
51
|
+
}
|
|
52
|
+
function escapeHtml(value) {
|
|
53
|
+
return value
|
|
54
|
+
.replace(/&/g, "&")
|
|
55
|
+
.replace(/</g, "<")
|
|
56
|
+
.replace(/>/g, ">")
|
|
57
|
+
.replace(/"/g, """);
|
|
58
|
+
}
|
|
59
|
+
/** Strip the "Enforces Law N (…) of the 7 Laws…" preamble; keep what it does. */
|
|
60
|
+
export function summarize(description) {
|
|
61
|
+
if (!description)
|
|
62
|
+
return "";
|
|
63
|
+
return description
|
|
64
|
+
.replace(/^Enforces\s+(all\s+7\s+Laws|Law\s*[1-7](\s*\+\s*[1-7])?)[^.]*\.\s*/i, "")
|
|
65
|
+
.replace(/^Use this skill to\s*/i, "")
|
|
66
|
+
.trim();
|
|
67
|
+
}
|
|
68
|
+
const STYLE = `
|
|
69
|
+
:root { color-scheme: light dark; --bg:#fbfbfa; --fg:#1a1a19; --muted:#6b6b66;
|
|
70
|
+
--line:#e3e3df; --card:#fff; --hook:#0f6f4b; --hookbg:#e4f3ec;
|
|
71
|
+
--cmd:#1b4f8a; --cmdbg:#e5eefa; --prose:#8a5a12; --prosebg:#faf0dc; }
|
|
72
|
+
@media (prefers-color-scheme: dark) { :root { --bg:#121211; --fg:#ececeb;
|
|
73
|
+
--muted:#9a9a94; --line:#2c2c29; --card:#1b1b1a; --hook:#6fd3a8; --hookbg:#12332a;
|
|
74
|
+
--cmd:#8fbdf0; --cmdbg:#132840; --prose:#e3b464; --prosebg:#3a2c12; } }
|
|
75
|
+
:root[data-theme="dark"] { --bg:#121211; --fg:#ececeb; --muted:#9a9a94; --line:#2c2c29;
|
|
76
|
+
--card:#1b1b1a; --hook:#6fd3a8; --hookbg:#12332a; --cmd:#8fbdf0; --cmdbg:#132840;
|
|
77
|
+
--prose:#e3b464; --prosebg:#3a2c12; }
|
|
78
|
+
:root[data-theme="light"] { --bg:#fbfbfa; --fg:#1a1a19; --muted:#6b6b66; --line:#e3e3df;
|
|
79
|
+
--card:#fff; --hook:#0f6f4b; --hookbg:#e4f3ec; --cmd:#1b4f8a; --cmdbg:#e5eefa;
|
|
80
|
+
--prose:#8a5a12; --prosebg:#faf0dc; }
|
|
81
|
+
body { margin:0; padding:2.5rem 1.25rem 4rem; background:var(--bg); color:var(--fg);
|
|
82
|
+
font:16px/1.6 ui-sans-serif,-apple-system,"Segoe UI",Roboto,sans-serif; }
|
|
83
|
+
.wrap { max-width:70rem; margin:0 auto; }
|
|
84
|
+
h1 { font-size:1.75rem; margin:0 0 .35rem; letter-spacing:-.015em; }
|
|
85
|
+
.sub { color:var(--muted); margin:0 0 1.25rem; max-width:52rem; }
|
|
86
|
+
h2 { font-size:1rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted);
|
|
87
|
+
margin:2.5rem 0 .75rem; font-weight:600; }
|
|
88
|
+
.legend { display:flex; flex-wrap:wrap; gap:1rem; margin:0 0 1rem; padding:0; list-style:none; }
|
|
89
|
+
.legend li { color:var(--muted); font-size:.85rem; }
|
|
90
|
+
.scroll { overflow-x:auto; border:1px solid var(--line); border-radius:.6rem; background:var(--card); }
|
|
91
|
+
table { border-collapse:collapse; width:100%; min-width:44rem; }
|
|
92
|
+
th, td { text-align:left; padding:.7rem .85rem; border-bottom:1px solid var(--line); vertical-align:top; }
|
|
93
|
+
th { font-size:.78rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); font-weight:600; }
|
|
94
|
+
tr:last-child td { border-bottom:0; }
|
|
95
|
+
td.name { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.88rem; white-space:nowrap; }
|
|
96
|
+
td.law { color:var(--muted); white-space:nowrap; font-variant-numeric:tabular-nums; }
|
|
97
|
+
td.what { min-width:22rem; }
|
|
98
|
+
.badge { display:inline-block; padding:.12rem .5rem; border-radius:1rem; font-size:.75rem;
|
|
99
|
+
font-weight:600; white-space:nowrap; }
|
|
100
|
+
.badge.hook { color:var(--hook); background:var(--hookbg); }
|
|
101
|
+
.badge.command { color:var(--cmd); background:var(--cmdbg); }
|
|
102
|
+
.badge.prose { color:var(--prose); background:var(--prosebg); }
|
|
103
|
+
.hookfile { display:block; margin-top:.25rem; font-family:ui-monospace,monospace;
|
|
104
|
+
font-size:.72rem; color:var(--muted); }
|
|
105
|
+
footer { margin-top:3rem; color:var(--muted); font-size:.85rem; max-width:52rem; }
|
|
106
|
+
code { font-family:ui-monospace,monospace; font-size:.9em; }
|
|
107
|
+
`;
|
|
108
|
+
/**
|
|
109
|
+
* Render the full catalog page. Pure — takes the resolved skill list and
|
|
110
|
+
* returns the complete HTML document.
|
|
111
|
+
*/
|
|
112
|
+
export function renderSkillCatalogHtml(skills) {
|
|
113
|
+
const counts = { hook: 0, command: 0, prose: 0 };
|
|
114
|
+
for (const s of skills)
|
|
115
|
+
counts[s.enforcement]++;
|
|
116
|
+
const sections = [];
|
|
117
|
+
for (const tier of TIER_ORDER) {
|
|
118
|
+
const rows = skills.filter((s) => s.tier === tier);
|
|
119
|
+
if (rows.length === 0)
|
|
120
|
+
continue;
|
|
121
|
+
const body = rows
|
|
122
|
+
.map((s) => {
|
|
123
|
+
const badge = `<span class="badge ${s.enforcement}" title="${escapeHtml(ENFORCEMENT_TITLE[s.enforcement])}">` +
|
|
124
|
+
`${s.enforcement}</span>` +
|
|
125
|
+
(s.hookFile ? `<span class="hookfile">hooks/${escapeHtml(s.hookFile)}</span>` : "");
|
|
126
|
+
return (" <tr>" +
|
|
127
|
+
`<td class="name">${escapeHtml(s.name)}</td>` +
|
|
128
|
+
`<td class="law">${escapeHtml(s.law ?? "—")}</td>` +
|
|
129
|
+
`<td>${badge}</td>` +
|
|
130
|
+
`<td class="what">${escapeHtml(summarize(s.description))}</td>` +
|
|
131
|
+
"</tr>");
|
|
132
|
+
})
|
|
133
|
+
.join("\n");
|
|
134
|
+
sections.push(` <h2>${escapeHtml(TIER_LABEL[tier])} (${rows.length})</h2>\n` +
|
|
135
|
+
' <div class="scroll">\n <table>\n' +
|
|
136
|
+
" <thead><tr><th>Skill</th><th>Law</th><th>Enforcement</th><th>What it does</th></tr></thead>\n" +
|
|
137
|
+
` <tbody>\n${body}\n </tbody>\n` +
|
|
138
|
+
" </table>\n </div>");
|
|
139
|
+
}
|
|
140
|
+
return [
|
|
141
|
+
"<!doctype html>",
|
|
142
|
+
'<html lang="en">',
|
|
143
|
+
"<head>",
|
|
144
|
+
' <meta charset="utf-8">',
|
|
145
|
+
' <meta name="viewport" content="width=device-width,initial-scale=1">',
|
|
146
|
+
` <title>Skill catalog — continuous-improvement (${skills.length} skills)</title>`,
|
|
147
|
+
" <!-- GENERATED by npm run build from skills/*.md frontmatter. Do not edit by hand. -->",
|
|
148
|
+
` <style>${STYLE} </style>`,
|
|
149
|
+
"</head>",
|
|
150
|
+
"<body>",
|
|
151
|
+
' <div class="wrap">',
|
|
152
|
+
" <h1>Skill catalog</h1>",
|
|
153
|
+
` <p class="sub">${skills.length} bundled skills, generated from <code>skills/*.md</code> frontmatter. ` +
|
|
154
|
+
"The <strong>Enforcement</strong> column is the point of this page: it states what actually backs each skill, " +
|
|
155
|
+
"derived from the shipped file set rather than from the skill's own prose. " +
|
|
156
|
+
`Today ${counts.hook} are enforced by a hook, ${counts.command} by a slash command, and ${counts.prose} by model-side discipline alone.</p>`,
|
|
157
|
+
' <ul class="legend">',
|
|
158
|
+
' <li><span class="badge hook">hook</span> fires at the tool boundary; model cooperation not required</li>',
|
|
159
|
+
' <li><span class="badge command">command</span> runs when you invoke it</li>',
|
|
160
|
+
' <li><span class="badge prose">prose</span> model-side discipline only — nothing blocks</li>',
|
|
161
|
+
" </ul>",
|
|
162
|
+
sections.join("\n"),
|
|
163
|
+
' <footer>A skill may only claim enforcement it ships. A <span class="badge prose">prose</span> skill that describes a runtime gate is a bug — that is what retired <code>safety-guard</code> on 2026-08-07. See <code>docs/plans/2026-08-07-six-rules-context-engineering.md</code>.</footer>',
|
|
164
|
+
" </div>",
|
|
165
|
+
"</body>",
|
|
166
|
+
"</html>",
|
|
167
|
+
"",
|
|
168
|
+
].join("\n");
|
|
169
|
+
}
|
package/llms.txt
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
# continuous-improvement
|
|
2
2
|
|
|
3
|
-
> The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as
|
|
3
|
+
> The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as 28 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts.
|
|
4
4
|
|
|
5
5
|
## What This Is
|
|
6
6
|
|
|
7
7
|
The persistent-memory and discipline layer for AI coding agents. It carries the corrections Claude has already received from one session into the next, grounds each edit in real facts before it lands, and learns from every session so its competence compounds over time — research, plan, execute one thing at a time, verify, reflect, iterate, learn — building behavioral instincts via the Mulahazah learning system, so the same correction never has to be given twice and each run starts smarter than the last. Orchestration tools run a task; this is the layer that makes the lessons survive the run.
|
|
8
8
|
|
|
9
|
+
Why these seven: every red flag an agent says ("this should work", "I'll remember", "next time I'll") is a wish standing in for a check. The Laws are one old sentence turned into checks an agent can run on itself: the wise one takes account of himself and works for what comes after; the weak one follows his impulse and merely wishes (Jami` at-Tirmidhi 2459). Before saying done: did I check, or did I hope? Sources and the mapping to each Law: docs/philosophy.md.
|
|
10
|
+
|
|
11
|
+
What the gate can and cannot do: hooks/gateguard.mjs denies the first Edit/Write/MultiEdit per file until a four-item fact list is presented and the printed clear command is run (honor system, 50 files per session); destructive Bash on a fixed blocklist is denied on every call with no clearance route; Bash file writes are not gated. Goal-drift warns by default (CLAUDE_GOAL_DRIFT_GATE=block to refuse), typecheck gate and recall briefing are opt-in.
|
|
12
|
+
|
|
13
|
+
Smarter models do not retire this product. Planning etiquette and "remember to verify" reminders merge into the model over time. The runtime gate, this-repo memory, and proof that a change worked do not. Keep goal-driven execution plus research / verify / learn guardrails; retire scaffolding when the native harness covers it (see skills/model-forward.md).
|
|
14
|
+
|
|
15
|
+
How you actually benefit:
|
|
16
|
+
1. Beginner install (or `npx continuous-improvement install`) — gateguard fires on Edit/Write; no prompt prefix required.
|
|
17
|
+
2. After a real session run `/seven-laws` — capture is silent; instincts form when you close the loop. `/recall` for "have I hit this before?". `/planning-with-files` writes `task_plan.md`.
|
|
18
|
+
3. Expert mode for MCP tools, `/harvest`, `/distill`, and optional `CLAUDE_RECALL_BRIEFING=1`. Empty harvest/distill on day 1 means missing observation history, not a broken command.
|
|
19
|
+
|
|
9
20
|
## Install
|
|
10
21
|
|
|
11
22
|
```bash
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "continuous-improvement",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Claude Code that gets sharper every session: the persistent-memory and runtime-discipline layer built on the 7 Laws of AI Agent Discipline. It grounds every edit in real facts before it lands and, through the Mulahazah engine, turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Shipped as
|
|
3
|
+
"version": "3.25.0",
|
|
4
|
+
"description": "Claude Code that gets sharper every session: the persistent-memory and runtime-discipline layer built on the 7 Laws of AI Agent Discipline. It grounds every edit in real facts before it lands and, through the Mulahazah engine, turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Shipped as 28 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts. Beginner: one /plugin install command. Expert: adds MCP tools and session hooks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
7
7
|
"claude-code-plugin",
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
"verify:skill-count": "node bin/check-skill-count.mjs",
|
|
53
53
|
"verify:skill-count-prose": "node bin/check-skill-count-prose.mjs",
|
|
54
54
|
"verify:command-count": "node bin/check-command-count.mjs",
|
|
55
|
+
"verify:test-count": "node bin/check-test-count.mjs",
|
|
56
|
+
"verify:invariant-count": "node bin/check-invariant-count.mjs",
|
|
55
57
|
"verify:docs-substrings": "node bin/check-docs-substrings.mjs",
|
|
56
58
|
"verify:everything-mirror": "node bin/check-everything-mirror.mjs",
|
|
57
59
|
"verify:routing-targets": "node bin/check-routing-targets.mjs",
|
|
@@ -62,7 +64,7 @@
|
|
|
62
64
|
"verify:third-party-shape": "node bin/check-third-party-shape.mjs",
|
|
63
65
|
"verify:tool-count": "node bin/check-tool-count.mjs",
|
|
64
66
|
"verify:reconcile-parity": "node bin/check-reconcile-parity.mjs",
|
|
65
|
-
"verify:all": "npm run verify:skill-mirror && npm run verify:skill-tiers && npm run verify:skill-law-tag && npm run verify:skill-count && npm run verify:skill-count-prose && npm run verify:command-count && npm run verify:docs-substrings && npm run verify:everything-mirror && npm run verify:routing-targets && npm run verify:doc-runtime-claims && npm run verify:test-imports-only && npm run verify:landing-version && npm run verify:scripts-citation-drift && npm run verify:third-party-shape && npm run verify:tool-count && npm run verify:reconcile-parity && npm run typecheck"
|
|
67
|
+
"verify:all": "npm run verify:skill-mirror && npm run verify:skill-tiers && npm run verify:skill-law-tag && npm run verify:skill-count && npm run verify:skill-count-prose && npm run verify:command-count && npm run verify:test-count && npm run verify:invariant-count && npm run verify:docs-substrings && npm run verify:everything-mirror && npm run verify:routing-targets && npm run verify:doc-runtime-claims && npm run verify:test-imports-only && npm run verify:landing-version && npm run verify:scripts-citation-drift && npm run verify:third-party-shape && npm run verify:tool-count && npm run verify:reconcile-parity && npm run typecheck"
|
|
66
68
|
},
|
|
67
69
|
"files": [
|
|
68
70
|
".claude-plugin/",
|
package/plugins/beginner.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "continuous-improvement",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.25.0",
|
|
4
4
|
"mode": "beginner",
|
|
5
5
|
"description": "Beginner mode: see what your agent learned, list its instincts, and request a session reflection. Bundles the ship fast path plus grounding skills (gateguard, tdd-workflow, verification-loop) so one-defect delivery, research, tests, and verification happen by default — every edit starts from facts, not guesses.",
|
|
6
6
|
"tools": [
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"plugins": [
|
|
8
8
|
{
|
|
9
9
|
"name": "continuous-improvement",
|
|
10
|
-
"description": "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as
|
|
11
|
-
"version": "3.
|
|
10
|
+
"description": "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as 28 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts.",
|
|
11
|
+
"version": "3.25.0",
|
|
12
12
|
"source": "./",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "naimkatiman"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "continuous-improvement",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as
|
|
3
|
+
"version": "3.25.0",
|
|
4
|
+
"description": "The persistent-memory and runtime-discipline layer for Claude Code. It remembers the corrections you already gave, grounds every edit in real facts before it lands, and — through the Mulahazah engine — turns each fix into a reusable instinct, so a lesson learned once is applied automatically next time with no re-teaching. Built on the 7 Laws of AI Agent Discipline (research, plan, verify, reflect, learn) and shipped as 28 bundled skills, instinct-aware hooks, an MCP toolset for recall and reflection, and a GitHub Action transcript linter that feeds real work history back into sharper instincts.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "naimkatiman",
|
|
7
7
|
"url": "https://github.com/naimkatiman"
|
|
@@ -36,8 +36,7 @@ the trade-off is fallback quality vs dedicated-skill quality.
|
|
|
36
36
|
- `tdd-workflow` — RED/GREEN/REFACTOR + 80% coverage gate
|
|
37
37
|
- `workspace-surface-audit` — environment + capability audit
|
|
38
38
|
- Tier-1/Tier-2 enforcement skills (`gateguard`, `verification-loop`,
|
|
39
|
-
`
|
|
40
|
-
`strategic-compact`, `wild-risa-balance`)
|
|
39
|
+
`token-budget-advisor`, `strategic-compact`, `wild-risa-balance`)
|
|
41
40
|
|
|
42
41
|
**Optional companions the orchestrator routes to (install separately if
|
|
43
42
|
you want the dedicated skill instead of the inline fallback):**
|
|
@@ -16,12 +16,11 @@ import { homedir } from "node:os";
|
|
|
16
16
|
import { basename, dirname, join, resolve } from "node:path";
|
|
17
17
|
import { createInterface } from "node:readline";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
|
-
import { createHash } from "node:crypto";
|
|
20
19
|
import { PACKAGE_NAME, VERSION, getToolDefinitions, isPluginMode, } from "../lib/plugin-metadata.mjs";
|
|
21
20
|
import { formatDriftReport, parseGoalFromPlan, scoreObservations, } from "../lib/goal-state.mjs";
|
|
22
21
|
import { buildIndex, formatRecallHits, parseSince, query as queryRecall, } from "../lib/recall-index.mjs";
|
|
23
22
|
import { draftFromCandidate, draftFromWorkflowRun, extractTrajectories, findCandidates, formatCandidates, serializeDraft, workflowRunFromObservations, } from "../lib/skill-distill.mjs";
|
|
24
|
-
import { MAX_CLEARED_FILES, canonicalizeFileKey, clearFiles, resolveInstinctsRoot, resolveSessionDir, } from "../lib/gateguard-state.mjs";
|
|
23
|
+
import { MAX_CLEARED_FILES, canonicalizeFileKey, clearFiles, hashProjectRoot, resolveInstinctsRoot, resolveProjectRoot, resolveSessionDir, } from "../lib/gateguard-state.mjs";
|
|
25
24
|
function getHomeDir() {
|
|
26
25
|
return process.env.HOME || process.env.USERPROFILE || homedir();
|
|
27
26
|
}
|
|
@@ -93,14 +92,10 @@ const modeIndex = args.indexOf("--mode");
|
|
|
93
92
|
const requestedMode = args[modeIndex + 1];
|
|
94
93
|
const MODE = isPluginMode(requestedMode) ? requestedMode : "beginner";
|
|
95
94
|
function getProjectHash() {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const hash = createHash("sha256").update(root).digest("hex").slice(0, 12);
|
|
99
|
-
return { root, hash, name: basename(root) };
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
95
|
+
const root = resolveProjectRoot();
|
|
96
|
+
if (root === "global")
|
|
102
97
|
return { root: "global", hash: "global", name: "global" };
|
|
103
|
-
}
|
|
98
|
+
return { root, hash: hashProjectRoot(root), name: basename(root) };
|
|
104
99
|
}
|
|
105
100
|
function readInstincts(projectHash) {
|
|
106
101
|
const instincts = [];
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
//
|
|
17
17
|
// See docs/plans/2026-05-05-node-observer-rich-schema.md for the full design.
|
|
18
18
|
import { execFileSync } from "node:child_process";
|
|
19
|
-
import { createHash } from "node:crypto";
|
|
20
19
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
21
20
|
import { homedir } from "node:os";
|
|
22
21
|
import { basename, join } from "node:path";
|
|
22
|
+
import { canonicalizeProjectRoot, hashProjectRoot } from "../lib/gateguard-state.mjs";
|
|
23
23
|
import { parseHookPayload, summariseInput, summariseOutput } from "../lib/observe-event.mjs";
|
|
24
24
|
const ROTATION_LINE_THRESHOLD = 10_000;
|
|
25
25
|
const ARCHIVE_RETENTION = 10;
|
|
@@ -40,8 +40,8 @@ function runObserver() {
|
|
|
40
40
|
const payload = parseHookPayload(raw);
|
|
41
41
|
if (!payload)
|
|
42
42
|
return;
|
|
43
|
-
const projectRoot = resolveProjectRoot();
|
|
44
|
-
const projectHash =
|
|
43
|
+
const projectRoot = canonicalizeProjectRoot(resolveProjectRoot());
|
|
44
|
+
const projectHash = hashProjectRoot(projectRoot);
|
|
45
45
|
const projectName = basename(projectRoot.replace(/\.git$/, ""));
|
|
46
46
|
const instinctsDir = join(getHomeDir(), ".claude", "instincts");
|
|
47
47
|
const projectDir = join(instinctsDir, projectHash);
|
|
@@ -19,6 +19,8 @@ Print this card and check yourself against each law.
|
|
|
19
19
|
| 6 | **Iterate One Change** | Am I changing one thing at a time? | "And also..." |
|
|
20
20
|
| 7 | **Learn From Every Session** | Did I capture this as an instinct? | "Next time I'll..." |
|
|
21
21
|
|
|
22
|
+
Read the seven as three moments around every act. **Before** (Laws 1, 2): set the terms. **During** (Laws 3, 6): watch yourself. **After** (Laws 4, 5, 7): settle the account and carry it forward. Every check is an audit you run on yourself; every red flag is you hoping instead. The sentence this comes from, and its sources: `docs/philosophy.md`.
|
|
23
|
+
|
|
22
24
|
## Operator Stakes
|
|
23
25
|
|
|
24
26
|
The Laws above are the *how*. These five principles are the *why*: code ships from your account, the incident lands on your pager, the bill hits your budget. Each one pairs with the Law that prevents it from going wrong.
|
|
@@ -31,7 +33,7 @@ The Laws above are the *how*. These five principles are the *why*: code ships fr
|
|
|
31
33
|
| 4 | **Problem framing** | Builds the websocket chat the ticket asked for | Finds out users wanted faster support replies, not chat | 1 |
|
|
32
34
|
| 5 | **Constraints management** | Calls the $0.02/image model on every upload | Does the math, adds client-side validation + caching + cheaper triage model | 2 |
|
|
33
35
|
|
|
34
|
-
Code is a liability, not an asset. Speed without these five turns into someone else's incident at 3am — except the someone is you.
|
|
36
|
+
Code is a liability, not an asset. Speed without these five turns into someone else's incident at 3am — except the someone is you. The other half of the why is not fear: the session ends and the context is gone, so the only work that survives is what you verified and wrote down for the one who comes after, whether that is tomorrow's session or the engineer who inherits the repo.
|
|
35
37
|
|
|
36
38
|
## Goal-Driven Execution maps onto the Laws
|
|
37
39
|
|
|
@@ -61,5 +63,6 @@ Before saying "Done", verify ALL:
|
|
|
61
63
|
- [ ] I checked the **actual** result (not assumed)
|
|
62
64
|
- [ ] Build passes
|
|
63
65
|
- [ ] I can explain the change in one sentence
|
|
66
|
+
- [ ] For each item above I checked, not hoped
|
|
64
67
|
|
|
65
|
-
If you're skipping a step, that's the step you need most.
|
|
68
|
+
If you're skipping a step, that's the step you need most. The step you skip is the one you are hoping through.
|
|
@@ -95,7 +95,7 @@ git branch -d <type>/<slug> # delete the merged feature branch (safe
|
|
|
95
95
|
## Pairs with
|
|
96
96
|
|
|
97
97
|
- **`reconcile`** skill — the discipline this command runs.
|
|
98
|
-
- **`gateguard`**
|
|
98
|
+
- **`gateguard`** — the runtime gate for mutating tool calls and destructive shell.
|
|
99
99
|
- **`recall`** — recall whether the same git op failed here before.
|
|
100
100
|
- **`audit`** — the loop that often produces the fix `/reconcile` then ships.
|
|
101
101
|
- **`/ship`** — the TDD-gated single-defect variant; `commit-commands:commit-push-pr` is the external-plugin equivalent of the commit → PR tail.
|