continuous-improvement 3.0.0 → 3.8.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.
Files changed (126) hide show
  1. package/.claude-plugin/marketplace.json +78 -0
  2. package/CHANGELOG.md +210 -0
  3. package/LICENSE +21 -21
  4. package/QUICKSTART.md +101 -81
  5. package/README.md +224 -198
  6. package/SKILL.md +87 -9
  7. package/action.yml +33 -0
  8. package/bin/analyze.sh +161 -153
  9. package/bin/backfill.mjs +172 -0
  10. package/bin/check-docs-substrings.mjs +333 -0
  11. package/bin/check-everything-mirror.mjs +145 -0
  12. package/bin/check-routing-targets.mjs +151 -0
  13. package/bin/check-skill-law-tag.mjs +128 -0
  14. package/bin/check-skill-mirror.mjs +119 -0
  15. package/bin/check-skill-tiers.mjs +116 -0
  16. package/bin/check-third-party-shape.mjs +202 -0
  17. package/bin/generate-plugin-manifests.mjs +169 -0
  18. package/bin/harvest-friction.mjs +279 -0
  19. package/bin/hook-stats.mjs +258 -0
  20. package/bin/install.mjs +418 -456
  21. package/bin/lint-transcript.mjs +239 -0
  22. package/bin/mcp-server.mjs +842 -499
  23. package/bin/observe.mjs +148 -0
  24. package/bin/pre-commit-block-strays.sh +49 -0
  25. package/bin/refresh-third-party.mjs +416 -0
  26. package/bin/unified-cli.mjs +533 -0
  27. package/commands/continuous-improvement.md +115 -74
  28. package/commands/dashboard.md +56 -0
  29. package/commands/discipline.md +51 -0
  30. package/commands/harvest.md +76 -0
  31. package/commands/learn-eval.md +117 -0
  32. package/commands/planning-with-files.md +66 -0
  33. package/commands/proceed-with-the-recommendation.md +62 -0
  34. package/commands/ralph.md +103 -0
  35. package/commands/release-train.md +81 -0
  36. package/commands/seven-laws.md +16 -0
  37. package/commands/superpowers.md +153 -0
  38. package/commands/swarm.md +101 -0
  39. package/commands/workspace-surface-audit.md +77 -0
  40. package/hooks/observe.sh +172 -134
  41. package/hooks/session.sh +106 -106
  42. package/hooks/three-section-close.mjs +181 -0
  43. package/instinct-packs/go.json +58 -0
  44. package/instinct-packs/meta.json +16 -0
  45. package/instinct-packs/python.json +58 -0
  46. package/instinct-packs/react.json +58 -0
  47. package/lib/cli-anything.mjs +401 -0
  48. package/lib/compound-engineering.mjs +831 -0
  49. package/lib/observe-event.mjs +128 -0
  50. package/lib/plugin-metadata.mjs +432 -0
  51. package/lib/pm-marketplace.mjs +61 -0
  52. package/lib/pm-skills.mjs +1274 -0
  53. package/lib/resolve-home-dir.mjs +43 -0
  54. package/lib/skill-tiers.mjs +137 -0
  55. package/lib/unified-plugin.mjs +924 -0
  56. package/llms.txt +68 -0
  57. package/package.json +38 -15
  58. package/plugins/beginner.json +17 -6
  59. package/plugins/continuous-improvement/.claude-plugin/marketplace.json +20 -0
  60. package/plugins/continuous-improvement/.claude-plugin/plugin.json +26 -0
  61. package/plugins/continuous-improvement/LICENSE +21 -0
  62. package/plugins/continuous-improvement/README.md +56 -0
  63. package/plugins/continuous-improvement/bin/backfill.mjs +172 -0
  64. package/plugins/continuous-improvement/bin/mcp-server.mjs +886 -0
  65. package/plugins/continuous-improvement/bin/observe.mjs +148 -0
  66. package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -0
  67. package/plugins/continuous-improvement/commands/dashboard.md +56 -0
  68. package/plugins/continuous-improvement/commands/discipline.md +51 -0
  69. package/plugins/continuous-improvement/commands/harvest.md +76 -0
  70. package/plugins/continuous-improvement/commands/learn-eval.md +117 -0
  71. package/plugins/continuous-improvement/commands/planning-with-files.md +66 -0
  72. package/plugins/continuous-improvement/commands/proceed-with-the-recommendation.md +62 -0
  73. package/plugins/continuous-improvement/commands/ralph.md +103 -0
  74. package/plugins/continuous-improvement/commands/release-train.md +81 -0
  75. package/plugins/continuous-improvement/commands/seven-laws.md +16 -0
  76. package/plugins/continuous-improvement/commands/superpowers.md +153 -0
  77. package/plugins/continuous-improvement/commands/swarm.md +101 -0
  78. package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -0
  79. package/plugins/continuous-improvement/hooks/hooks.json +60 -0
  80. package/plugins/continuous-improvement/hooks/observe.sh +172 -0
  81. package/plugins/continuous-improvement/hooks/session.sh +106 -0
  82. package/plugins/continuous-improvement/hooks/three-section-close.mjs +181 -0
  83. package/plugins/continuous-improvement/instinct-packs/go.json +58 -0
  84. package/plugins/continuous-improvement/instinct-packs/meta.json +16 -0
  85. package/plugins/continuous-improvement/instinct-packs/python.json +58 -0
  86. package/plugins/continuous-improvement/instinct-packs/react.json +58 -0
  87. package/plugins/continuous-improvement/lib/observe-event.mjs +128 -0
  88. package/plugins/continuous-improvement/lib/plugin-metadata.mjs +432 -0
  89. package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +43 -0
  90. package/plugins/continuous-improvement/skills/README.md +34 -0
  91. package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +249 -0
  92. package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +131 -0
  93. package/plugins/continuous-improvement/skills/gateguard/SKILL.md +155 -0
  94. package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +108 -0
  95. package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +454 -0
  96. package/plugins/continuous-improvement/skills/ralph/SKILL.md +221 -0
  97. package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +76 -0
  98. package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +104 -0
  99. package/plugins/continuous-improvement/skills/superpowers/SKILL.md +212 -0
  100. package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -0
  101. package/plugins/continuous-improvement/skills/token-budget-advisor/SKILL.md +136 -0
  102. package/plugins/continuous-improvement/skills/verification-loop/SKILL.md +192 -0
  103. package/plugins/continuous-improvement/skills/wild-risa-balance/SKILL.md +191 -0
  104. package/plugins/continuous-improvement/skills/workspace-surface-audit/SKILL.md +147 -0
  105. package/plugins/continuous-improvement/templates/planning-with-files/findings.md +8 -0
  106. package/plugins/continuous-improvement/templates/planning-with-files/progress.md +7 -0
  107. package/plugins/continuous-improvement/templates/planning-with-files/task_plan.md +23 -0
  108. package/plugins/expert.json +34 -5
  109. package/skills/README.md +79 -0
  110. package/skills/deploy-receipt.md +131 -0
  111. package/skills/gateguard.md +155 -0
  112. package/skills/para-memory-files.md +108 -0
  113. package/skills/proceed-with-the-recommendation.md +454 -0
  114. package/skills/ralph.md +221 -0
  115. package/skills/safety-guard.md +76 -0
  116. package/skills/strategic-compact.md +104 -0
  117. package/skills/superpowers.md +212 -0
  118. package/skills/tdd-workflow.md +411 -0
  119. package/skills/token-budget-advisor.md +136 -0
  120. package/skills/verification-loop.md +192 -0
  121. package/skills/wild-risa-balance.md +191 -0
  122. package/skills/workspace-surface-audit.md +147 -0
  123. package/templates/planning-with-files/findings.md +8 -0
  124. package/templates/planning-with-files/progress.md +7 -0
  125. package/templates/planning-with-files/task_plan.md +23 -0
  126. package/templates/verify-ladder.example.json +47 -0
package/bin/install.mjs CHANGED
@@ -1,497 +1,459 @@
1
1
  #!/usr/bin/env node
2
-
3
2
  /**
4
- * continuous-improvement installer
3
+ * continuous-improvement installer (Claude Code)
5
4
  *
6
5
  * Usage:
7
- * npx continuous-improvement install # auto-detect & install (beginner)
8
- * npx continuous-improvement install --target claude # install to ~/.claude/skills/ + Mulahazah
9
- * npx continuous-improvement install --target openclaw # install to ~/.openclaw/skills/
10
- * npx continuous-improvement install --target cursor # install to ~/.cursor/skills/
11
- * npx continuous-improvement install --target all # install to all detected targets
12
- * npx continuous-improvement install --mode beginner # hooks only (default)
13
- * npx continuous-improvement install --mode expert # hooks + MCP server + session hooks
14
- * npx continuous-improvement install --mode mcp # MCP server only (any editor)
15
- * npx continuous-improvement install --uninstall # remove from all targets
6
+ * npx continuous-improvement install # beginner mode (default)
7
+ * npx continuous-improvement install --mode expert # + MCP server + session hooks
8
+ * npx continuous-improvement install --pack react # load starter instinct pack
9
+ * npx continuous-improvement install --uninstall # remove everything
16
10
  */
17
-
18
- import {
19
- existsSync,
20
- mkdirSync,
21
- copyFileSync,
22
- readFileSync,
23
- writeFileSync,
24
- rmSync,
25
- chmodSync,
26
- } from "node:fs";
27
- import { join, dirname } from "node:path";
11
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, } from "node:fs";
12
+ import { execSync } from "node:child_process";
28
13
  import { homedir } from "node:os";
14
+ import { dirname, join } from "node:path";
29
15
  import { fileURLToPath } from "node:url";
30
-
16
+ import { createHash } from "node:crypto";
31
17
  const __filename = fileURLToPath(import.meta.url);
32
18
  const __dirname = dirname(__filename);
33
19
  const SKILL_SOURCE = join(__dirname, "..", "SKILL.md");
34
20
  const SKILL_NAME = "continuous-improvement";
35
21
  const REPO_ROOT = join(__dirname, "..");
36
-
37
- // Parse --mode flag
38
- const _args = process.argv.slice(2);
39
- const _modeIdx = _args.indexOf("--mode");
40
- const INSTALL_MODE = _modeIdx !== -1 && _args[_modeIdx + 1] ? _args[_modeIdx + 1] : "beginner";
41
-
42
- const TARGETS = {
43
- claude: {
44
- label: "Claude Code",
45
- dir: join(homedir(), ".claude", "skills", SKILL_NAME),
46
- },
47
- openclaw: {
48
- label: "OpenClaw",
49
- dir: join(homedir(), ".openclaw", "skills", SKILL_NAME),
50
- },
51
- cursor: {
52
- label: "Cursor",
53
- dir: join(homedir(), ".cursor", "skills", SKILL_NAME),
54
- },
55
- codex: {
56
- label: "Codex",
57
- dir: join(homedir(), ".codex", "skills", SKILL_NAME),
58
- },
59
- };
60
-
61
- function detectTargets() {
62
- const detected = [];
63
- for (const [key, target] of Object.entries(TARGETS)) {
64
- const parentDir = dirname(target.dir);
65
- const configDir = dirname(parentDir);
66
- if (existsSync(configDir)) {
67
- detected.push(key);
68
- }
69
- }
70
- return detected;
22
+ const COMMAND_FILES = [
23
+ "continuous-improvement.md",
24
+ "planning-with-files.md",
25
+ "proceed-with-the-recommendation.md",
26
+ "discipline.md",
27
+ "dashboard.md",
28
+ "learn-eval.md",
29
+ ];
30
+ const HOOK_TYPES = ["PreToolUse", "PostToolUse"];
31
+ const SESSION_HOOK_TYPES = ["SessionStart", "SessionEnd"];
32
+ function getHomeDir() {
33
+ return process.env.HOME || process.env.USERPROFILE || homedir();
34
+ }
35
+ function isInstallMode(value) {
36
+ return value === "beginner" || value === "expert";
37
+ }
38
+ function getErrorMessage(error) {
39
+ return error instanceof Error ? error.message : String(error);
40
+ }
41
+ function readJsonFile(filePath) {
42
+ try {
43
+ return JSON.parse(readFileSync(filePath, "utf8"));
44
+ }
45
+ catch {
46
+ return null;
47
+ }
71
48
  }
72
-
73
- function installTo(key) {
74
- const target = TARGETS[key];
75
- if (!target) {
76
- console.error(` Unknown target: ${key}`);
77
- return false;
78
- }
79
-
80
- try {
81
- // MCP-only mode: skip skill file copy, just register MCP server
82
- if (INSTALL_MODE === "mcp") {
83
- if (key === "claude") {
49
+ const rawArgs = process.argv.slice(2);
50
+ const modeIndex = rawArgs.indexOf("--mode");
51
+ const requestedMode = modeIndex !== -1 ? rawArgs[modeIndex + 1] : undefined;
52
+ const INSTALL_MODE = isInstallMode(requestedMode) ? requestedMode : "beginner";
53
+ const SKILL_DIR = join(getHomeDir(), ".claude", "skills", SKILL_NAME);
54
+ function installSkill() {
55
+ try {
56
+ mkdirSync(SKILL_DIR, { recursive: true });
57
+ copyFileSync(SKILL_SOURCE, join(SKILL_DIR, "SKILL.md"));
58
+ console.log(` ✓ Claude Code skill ${SKILL_DIR}/SKILL.md`);
84
59
  setupMulahazah();
85
- console.log(` ✓ ${target.label} → MCP server only`);
86
- } else {
87
- console.log(` ⊘ ${target.label} — MCP mode only applies to Claude Code`);
88
- }
89
- return true;
90
- }
91
-
92
- mkdirSync(target.dir, { recursive: true });
93
- copyFileSync(SKILL_SOURCE, join(target.dir, "SKILL.md"));
94
- console.log(` ✓ ${target.label} → ${target.dir}/SKILL.md`);
95
-
96
- if (key === "claude") {
97
- setupMulahazah();
98
- }
99
-
100
- return true;
101
- } catch (err) {
102
- console.error(` ✗ ${target.label}: ${err.message}`);
103
- return false;
104
- }
60
+ return true;
61
+ }
62
+ catch (error) {
63
+ console.error(` ✗ Install failed: ${getErrorMessage(error)}`);
64
+ return false;
65
+ }
105
66
  }
106
-
107
67
  function setupMulahazah() {
108
- const home = homedir();
109
- const instinctsDir = join(home, ".claude", "instincts");
110
- const globalDir = join(instinctsDir, "global");
111
-
112
- // 1. Create directory structure
113
- mkdirSync(globalDir, { recursive: true });
114
- console.log(` ✓ Instincts dir ${instinctsDir}/`);
115
-
116
- // 2. Copy observe.sh and make executable
117
- const observeSrc = join(REPO_ROOT, "hooks", "observe.sh");
118
- const observeDest = join(instinctsDir, "observe.sh");
119
- if (existsSync(observeSrc)) {
120
- copyFileSync(observeSrc, observeDest);
121
- chmodSync(observeDest, 0o755);
122
- console.log(` ✓ observe.sh ${observeDest}`);
123
- }
124
-
125
- // 3. Copy session.sh for expert mode
126
- if (INSTALL_MODE === "expert") {
127
- const sessionSrc = join(REPO_ROOT, "hooks", "session.sh");
128
- const sessionDest = join(instinctsDir, "session.sh");
129
- if (existsSync(sessionSrc)) {
130
- copyFileSync(sessionSrc, sessionDest);
131
- chmodSync(sessionDest, 0o755);
132
- console.log(` ✓ session.sh → ${sessionDest}`);
133
- }
134
- }
135
-
136
- // 4. Copy /continuous-improvement command
137
- const commandsDir = join(home, ".claude", "commands");
138
- mkdirSync(commandsDir, { recursive: true });
139
- const cmdSrc = join(REPO_ROOT, "commands", "continuous-improvement.md");
140
- const cmdDest = join(commandsDir, "continuous-improvement.md");
141
- if (existsSync(cmdSrc)) {
142
- copyFileSync(cmdSrc, cmdDest);
143
- console.log(` ✓ /continuous-improvement command → ${cmdDest}`);
144
- }
145
-
146
- // 5. Patch ~/.claude/settings.json with hooks
147
- patchClaudeSettings(observeDest);
148
-
149
- // 6. Setup MCP server for expert or mcp mode
150
- if (INSTALL_MODE === "expert" || INSTALL_MODE === "mcp") {
151
- setupMcpServer();
152
- }
68
+ const home = getHomeDir();
69
+ const instinctsDir = join(home, ".claude", "instincts");
70
+ const globalDir = join(instinctsDir, "global");
71
+ mkdirSync(globalDir, { recursive: true });
72
+ console.log(` Instincts dir ${instinctsDir}/`);
73
+ const observeSrc = join(REPO_ROOT, "hooks", "observe.sh");
74
+ const observeDest = join(instinctsDir, "observe.sh");
75
+ if (existsSync(observeSrc)) {
76
+ copyFileSync(observeSrc, observeDest);
77
+ chmodSync(observeDest, 0o755);
78
+ console.log(` observe.sh → ${observeDest}`);
79
+ }
80
+ // Node observer (Phase 1 of the two-phase hook). The bash shim above
81
+ // exec's this when `node` and the file are both present; otherwise it
82
+ // falls back to the in-bash thin-schema path. Layout under instinctsDir
83
+ // mirrors the repo's bin/ + lib/ structure so the relative import in
84
+ // observe.mjs (`../lib/observe-event.mjs`) resolves correctly.
85
+ const observerJsSrc = join(REPO_ROOT, "bin", "observe.mjs");
86
+ const observeEventSrc = join(REPO_ROOT, "lib", "observe-event.mjs");
87
+ if (existsSync(observerJsSrc) && existsSync(observeEventSrc)) {
88
+ const binDir = join(instinctsDir, "bin");
89
+ const libDir = join(instinctsDir, "lib");
90
+ mkdirSync(binDir, { recursive: true });
91
+ mkdirSync(libDir, { recursive: true });
92
+ const observerJsDest = join(binDir, "observe.mjs");
93
+ const observeEventDest = join(libDir, "observe-event.mjs");
94
+ copyFileSync(observerJsSrc, observerJsDest);
95
+ copyFileSync(observeEventSrc, observeEventDest);
96
+ console.log(` Node observer ${observerJsDest}`);
97
+ }
98
+ if (INSTALL_MODE === "expert") {
99
+ const sessionSrc = join(REPO_ROOT, "hooks", "session.sh");
100
+ const sessionDest = join(instinctsDir, "session.sh");
101
+ if (existsSync(sessionSrc)) {
102
+ copyFileSync(sessionSrc, sessionDest);
103
+ chmodSync(sessionDest, 0o755);
104
+ console.log(` ✓ session.sh → ${sessionDest}`);
105
+ }
106
+ }
107
+ const commandsDir = join(home, ".claude", "commands");
108
+ mkdirSync(commandsDir, { recursive: true });
109
+ for (const commandFile of COMMAND_FILES) {
110
+ const commandSource = join(REPO_ROOT, "commands", commandFile);
111
+ const commandDest = join(commandsDir, commandFile);
112
+ if (existsSync(commandSource)) {
113
+ copyFileSync(commandSource, commandDest);
114
+ console.log(` ✓ /${commandFile.replace(".md", "")} command → ${commandDest}`);
115
+ }
116
+ }
117
+ patchClaudeSettings(observeDest);
118
+ if (INSTALL_MODE === "expert") {
119
+ setupMcpServer();
120
+ }
153
121
  }
154
-
155
122
  function setupMcpServer() {
156
- const home = homedir();
157
- const mcpServerPath = join(REPO_ROOT, "bin", "mcp-server.mjs");
158
- const mcpMode = INSTALL_MODE === "mcp" ? "beginner" : "expert";
159
-
160
- // Patch Claude Code settings.json with MCP server config
161
- const settingsPath = join(home, ".claude", "settings.json");
162
- let settings = {};
163
- if (existsSync(settingsPath)) {
164
- try {
165
- settings = JSON.parse(readFileSync(settingsPath, "utf8"));
166
- } catch {
167
- console.warn(` ! Could not parse settings.json — skipping MCP setup`);
168
- return;
169
- }
170
- }
171
-
172
- if (!settings.mcpServers) settings.mcpServers = {};
173
-
174
- const alreadySetup = settings.mcpServers["continuous-improvement"];
175
- if (!alreadySetup) {
176
- settings.mcpServers["continuous-improvement"] = {
177
- command: "node",
178
- args: [mcpServerPath, "--mode", mcpMode],
179
- };
180
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
181
- console.log(` ✓ MCP server registered (mode: ${mcpMode})`);
182
- } else {
183
- console.log(` ✓ MCP server already registered — no change`);
184
- }
185
-
186
- // Also write claude_desktop_config.json if it exists
187
- const desktopConfig = join(home, ".claude", "claude_desktop_config.json");
188
- if (existsSync(desktopConfig)) {
189
- try {
190
- const config = JSON.parse(readFileSync(desktopConfig, "utf8"));
191
- if (!config.mcpServers) config.mcpServers = {};
192
- if (!config.mcpServers["continuous-improvement"]) {
193
- config.mcpServers["continuous-improvement"] = {
194
- command: "node",
195
- args: [mcpServerPath, "--mode", mcpMode],
123
+ const home = getHomeDir();
124
+ const mcpServerPath = join(REPO_ROOT, "bin", "mcp-server.mjs");
125
+ const settingsPath = join(home, ".claude", "settings.json");
126
+ const settings = existsSync(settingsPath) ? readJsonFile(settingsPath) : {};
127
+ if (settings === null) {
128
+ console.warn(" ! Could not parse settings.json — skipping MCP setup");
129
+ return;
130
+ }
131
+ if (!settings.mcpServers) {
132
+ settings.mcpServers = {};
133
+ }
134
+ if (!settings.mcpServers["continuous-improvement"]) {
135
+ settings.mcpServers["continuous-improvement"] = {
136
+ command: "node",
137
+ args: [mcpServerPath, "--mode", "expert"],
138
+ };
139
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
140
+ console.log(" ✓ MCP server registered (mode: expert)");
141
+ }
142
+ else {
143
+ console.log(" MCP server already registered — no change");
144
+ }
145
+ const desktopConfigPath = join(home, ".claude", "claude_desktop_config.json");
146
+ if (!existsSync(desktopConfigPath)) {
147
+ return;
148
+ }
149
+ const desktopConfig = readJsonFile(desktopConfigPath);
150
+ if (!desktopConfig) {
151
+ return;
152
+ }
153
+ if (!desktopConfig.mcpServers) {
154
+ desktopConfig.mcpServers = {};
155
+ }
156
+ if (!desktopConfig.mcpServers["continuous-improvement"]) {
157
+ desktopConfig.mcpServers["continuous-improvement"] = {
158
+ command: "node",
159
+ args: [mcpServerPath, "--mode", "expert"],
196
160
  };
197
- writeFileSync(desktopConfig, JSON.stringify(config, null, 2) + "\n");
198
- console.log(` ✓ Claude Desktop MCP config updated`);
199
- }
200
- } catch {
201
- // skip
161
+ writeFileSync(desktopConfigPath, JSON.stringify(desktopConfig, null, 2) + "\n");
162
+ console.log(" ✓ Claude Desktop MCP config updated");
202
163
  }
203
- }
204
164
  }
205
-
165
+ function hookAlreadyPatched(entries, scriptName) {
166
+ return entries.some((entry) => Array.isArray(entry.hooks) &&
167
+ entry.hooks.some((hook) => hook.command.includes(scriptName)));
168
+ }
206
169
  function patchClaudeSettings(observePath) {
207
- const settingsPath = join(homedir(), ".claude", "settings.json");
208
-
209
- let settings = {};
210
- if (existsSync(settingsPath)) {
211
- try {
212
- settings = JSON.parse(readFileSync(settingsPath, "utf8"));
213
- } catch {
214
- console.warn(` ! Could not parse ${settingsPath} — skipping hook patch`);
215
- return;
216
- }
217
- }
218
-
219
- if (!settings.hooks) settings.hooks = {};
220
-
221
- const hookEntry = {
222
- matcher: "",
223
- hooks: [
224
- {
225
- type: "command",
226
- command: `bash "${observePath}"`,
227
- },
228
- ],
229
- };
230
-
231
- let changed = false;
232
-
233
- for (const hookType of ["PreToolUse", "PostToolUse"]) {
234
- if (!Array.isArray(settings.hooks[hookType])) {
235
- settings.hooks[hookType] = [];
236
- }
237
- const alreadyPatched = settings.hooks[hookType].some(
238
- (h) =>
239
- Array.isArray(h.hooks) &&
240
- h.hooks.some((hh) => hh.command && hh.command.includes("observe.sh"))
241
- );
242
- if (!alreadyPatched) {
243
- settings.hooks[hookType].push(hookEntry);
244
- changed = true;
245
- }
246
- }
247
-
248
- // Expert mode: add session hooks
249
- if (INSTALL_MODE === "expert") {
250
- const sessionPath = join(homedir(), ".claude", "instincts", "session.sh");
251
- const sessionHook = {
252
- matcher: "",
253
- hooks: [{ type: "command", command: `bash "${sessionPath}"` }],
170
+ const settingsPath = join(getHomeDir(), ".claude", "settings.json");
171
+ const settings = existsSync(settingsPath) ? readJsonFile(settingsPath) : {};
172
+ if (settings === null) {
173
+ console.warn(` ! Could not parse ${settingsPath} — skipping hook patch`);
174
+ return;
175
+ }
176
+ if (!settings.hooks) {
177
+ settings.hooks = {};
178
+ }
179
+ const hookEntry = {
180
+ matcher: "",
181
+ hooks: [
182
+ {
183
+ type: "command",
184
+ command: `bash "${observePath}"`,
185
+ },
186
+ ],
254
187
  };
255
-
256
- // Note: SessionStart/SessionEnd hooks may not be supported in all versions.
257
- // We register them — they'll be silently ignored if unsupported.
258
- for (const hookType of ["SessionStart", "SessionEnd"]) {
259
- if (!Array.isArray(settings.hooks[hookType])) {
260
- settings.hooks[hookType] = [];
261
- }
262
- const alreadyPatched = settings.hooks[hookType].some(
263
- (h) =>
264
- Array.isArray(h.hooks) &&
265
- h.hooks.some((hh) => hh.command && hh.command.includes("session.sh"))
266
- );
267
- if (!alreadyPatched) {
268
- settings.hooks[hookType].push(sessionHook);
269
- changed = true;
270
- }
271
- }
272
- }
273
-
274
- if (changed) {
275
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
276
- const hookTypes = INSTALL_MODE === "expert"
277
- ? "PreToolUse/PostToolUse/SessionStart/SessionEnd"
278
- : "PreToolUse/PostToolUse";
279
- console.log(` ✓ Patched ~/.claude/settings.json with ${hookTypes} hooks`);
280
- } else {
281
- console.log(` ✓ settings.json already has hooks — no change needed`);
282
- }
188
+ let changed = false;
189
+ for (const hookType of HOOK_TYPES) {
190
+ if (!Array.isArray(settings.hooks[hookType])) {
191
+ settings.hooks[hookType] = [];
192
+ }
193
+ const hookEntries = settings.hooks[hookType];
194
+ if (!hookAlreadyPatched(hookEntries, "observe.sh")) {
195
+ hookEntries.push(hookEntry);
196
+ changed = true;
197
+ }
198
+ }
199
+ if (INSTALL_MODE === "expert") {
200
+ const sessionPath = join(getHomeDir(), ".claude", "instincts", "session.sh");
201
+ const sessionHook = {
202
+ matcher: "",
203
+ hooks: [{ type: "command", command: `bash "${sessionPath}"` }],
204
+ };
205
+ for (const hookType of SESSION_HOOK_TYPES) {
206
+ if (!Array.isArray(settings.hooks[hookType])) {
207
+ settings.hooks[hookType] = [];
208
+ }
209
+ const hookEntries = settings.hooks[hookType];
210
+ if (!hookAlreadyPatched(hookEntries, "session.sh")) {
211
+ hookEntries.push(sessionHook);
212
+ changed = true;
213
+ }
214
+ }
215
+ }
216
+ if (changed) {
217
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
218
+ const hookTypes = INSTALL_MODE === "expert"
219
+ ? "PreToolUse/PostToolUse/SessionStart/SessionEnd"
220
+ : "PreToolUse/PostToolUse";
221
+ console.log(` ✓ Patched ~/.claude/settings.json with ${hookTypes} hooks`);
222
+ }
223
+ else {
224
+ console.log(" ✓ settings.json already has hooks — no change needed");
225
+ }
283
226
  }
284
-
285
227
  function uninstallAll() {
286
- console.log("\nUninstalling continuous-improvement skill...\n");
287
- const home = homedir();
288
- let removed = 0;
289
-
290
- // 1. Remove skill files from all targets
291
- for (const [key, target] of Object.entries(TARGETS)) {
292
- const skillFile = join(target.dir, "SKILL.md");
293
- if (existsSync(skillFile)) {
294
- try {
295
- rmSync(target.dir, { recursive: true });
296
- console.log(` ✓ Removed skill from ${target.label}`);
297
- removed++;
298
- } catch (err) {
299
- console.error(` ✗ ${target.label}: ${err.message}`);
300
- }
301
- }
302
- }
303
-
304
- // 2. Remove /continuous-improvement command
305
- const cmdFile = join(home, ".claude", "commands", "continuous-improvement.md");
306
- if (existsSync(cmdFile)) {
307
- try {
308
- rmSync(cmdFile);
309
- console.log(` ✓ Removed /continuous-improvement command`);
310
- } catch (err) {
311
- console.error(` ✗ Command file: ${err.message}`);
312
- }
313
- }
314
-
315
- // 3. Remove observe.sh and session.sh from instincts dir
316
- for (const hookFile of ["observe.sh", "session.sh"]) {
317
- const filePath = join(home, ".claude", "instincts", hookFile);
318
- if (existsSync(filePath)) {
319
- try {
320
- rmSync(filePath);
321
- console.log(` ✓ Removed ${hookFile}`);
322
- } catch (err) {
323
- console.error(` ✗ ${hookFile}: ${err.message}`);
324
- }
325
- }
326
- }
327
-
328
- // 4. Remove hooks and MCP server from settings.json
329
- const settingsPath = join(home, ".claude", "settings.json");
330
- if (existsSync(settingsPath)) {
331
- try {
332
- const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
333
- let changed = false;
334
-
335
- // Remove all hook types
336
- for (const hookType of ["PreToolUse", "PostToolUse", "SessionStart", "SessionEnd"]) {
337
- if (Array.isArray(settings.hooks?.[hookType])) {
338
- const before = settings.hooks[hookType].length;
339
- settings.hooks[hookType] = settings.hooks[hookType].filter(
340
- (h) =>
341
- !(
342
- Array.isArray(h.hooks) &&
343
- h.hooks.some(
344
- (hh) => hh.command && (hh.command.includes("observe.sh") || hh.command.includes("session.sh"))
345
- )
346
- )
347
- );
348
- if (settings.hooks[hookType].length < before) changed = true;
228
+ console.log("\nUninstalling continuous-improvement skill...\n");
229
+ const home = getHomeDir();
230
+ let removed = 0;
231
+ if (existsSync(SKILL_DIR)) {
232
+ try {
233
+ rmSync(SKILL_DIR, { recursive: true });
234
+ console.log(" ✓ Removed Claude Code skill");
235
+ removed++;
349
236
  }
350
- }
351
-
352
- // Remove MCP server
353
- if (settings.mcpServers?.["continuous-improvement"]) {
354
- delete settings.mcpServers["continuous-improvement"];
355
- changed = true;
356
- }
357
-
358
- if (changed) {
359
- writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
360
- console.log(` ✓ Removed hooks and MCP server from settings.json`);
361
- }
362
- } catch {
363
- console.warn(` ! Could not clean settings.json — remove hooks manually`);
364
- }
365
- }
366
-
367
- // 5. Clean claude_desktop_config.json MCP entry
368
- const desktopConfig = join(home, ".claude", "claude_desktop_config.json");
369
- if (existsSync(desktopConfig)) {
370
- try {
371
- const config = JSON.parse(readFileSync(desktopConfig, "utf8"));
372
- if (config.mcpServers?.["continuous-improvement"]) {
373
- delete config.mcpServers["continuous-improvement"];
374
- writeFileSync(desktopConfig, JSON.stringify(config, null, 2) + "\n");
375
- console.log(` ✓ Removed MCP server from Claude Desktop config`);
376
- }
377
- } catch {
378
- // skip
379
- }
380
- }
381
-
382
- if (removed === 0) {
383
- console.log(" No skill installations found.");
384
- }
385
- console.log(
386
- "\n Note: Instinct data in ~/.claude/instincts/ was preserved.\n" +
387
- " To remove learned data too: rm -rf ~/.claude/instincts/\n"
388
- );
237
+ catch (error) {
238
+ console.error(` ✗ Skill removal failed: ${getErrorMessage(error)}`);
239
+ }
240
+ }
241
+ for (const commandName of COMMAND_FILES) {
242
+ const commandFile = join(home, ".claude", "commands", commandName);
243
+ if (!existsSync(commandFile)) {
244
+ continue;
245
+ }
246
+ try {
247
+ rmSync(commandFile);
248
+ console.log(` ✓ Removed /${commandName.replace(".md", "")} command`);
249
+ }
250
+ catch (error) {
251
+ console.error(` ✗ ${commandName}: ${getErrorMessage(error)}`);
252
+ }
253
+ }
254
+ for (const hookFile of ["observe.sh", "session.sh"]) {
255
+ const filePath = join(home, ".claude", "instincts", hookFile);
256
+ if (!existsSync(filePath)) {
257
+ continue;
258
+ }
259
+ try {
260
+ rmSync(filePath);
261
+ console.log(` ✓ Removed ${hookFile}`);
262
+ }
263
+ catch (error) {
264
+ console.error(` ✗ ${hookFile}: ${getErrorMessage(error)}`);
265
+ }
266
+ }
267
+ // Remove the Node observer artifacts deployed alongside observe.sh.
268
+ for (const observerFile of [
269
+ join("bin", "observe.mjs"),
270
+ join("lib", "observe-event.mjs"),
271
+ ]) {
272
+ const filePath = join(home, ".claude", "instincts", observerFile);
273
+ if (!existsSync(filePath))
274
+ continue;
275
+ try {
276
+ rmSync(filePath);
277
+ console.log(` ✓ Removed ${observerFile}`);
278
+ }
279
+ catch (error) {
280
+ console.error(` ✗ ${observerFile}: ${getErrorMessage(error)}`);
281
+ }
282
+ }
283
+ const settingsPath = join(home, ".claude", "settings.json");
284
+ if (existsSync(settingsPath)) {
285
+ const settings = readJsonFile(settingsPath);
286
+ if (settings) {
287
+ let changed = false;
288
+ for (const hookType of [...HOOK_TYPES, ...SESSION_HOOK_TYPES]) {
289
+ const hookEntries = settings.hooks?.[hookType];
290
+ if (!Array.isArray(hookEntries)) {
291
+ continue;
292
+ }
293
+ const nextEntries = hookEntries.filter((entry) => !entry.hooks.some((hook) => hook.command.includes("observe.sh") || hook.command.includes("session.sh")));
294
+ if (nextEntries.length !== hookEntries.length) {
295
+ changed = true;
296
+ }
297
+ settings.hooks[hookType] = nextEntries;
298
+ }
299
+ if (settings.mcpServers?.["continuous-improvement"]) {
300
+ delete settings.mcpServers["continuous-improvement"];
301
+ changed = true;
302
+ }
303
+ if (changed) {
304
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
305
+ console.log(" ✓ Removed hooks and MCP server from settings.json");
306
+ }
307
+ }
308
+ else {
309
+ console.warn(" ! Could not clean settings.json — remove hooks manually");
310
+ }
311
+ }
312
+ const desktopConfigPath = join(home, ".claude", "claude_desktop_config.json");
313
+ if (existsSync(desktopConfigPath)) {
314
+ const desktopConfig = readJsonFile(desktopConfigPath);
315
+ if (desktopConfig?.mcpServers?.["continuous-improvement"]) {
316
+ delete desktopConfig.mcpServers["continuous-improvement"];
317
+ writeFileSync(desktopConfigPath, JSON.stringify(desktopConfig, null, 2) + "\n");
318
+ console.log(" ✓ Removed MCP server from Claude Desktop config");
319
+ }
320
+ }
321
+ if (removed === 0) {
322
+ console.log(" No skill installations found.");
323
+ }
324
+ console.log("\n Note: Instinct data in ~/.claude/instincts/ was preserved.\n" +
325
+ " To remove learned data too: rm -rf ~/.claude/instincts/\n");
389
326
  }
390
-
391
327
  function printUsage() {
392
- console.log(`
393
- Usage: npx continuous-improvement install [options]
394
-
395
- Options:
396
- --target <name> Install to specific target (claude, openclaw, cursor, codex, all)
397
- --mode <mode> Installation mode:
398
- beginner hooks only, no MCP server (default)
399
- expert — hooks + MCP server + session hooks + all tools
400
- mcp — MCP server only (works with any MCP client)
401
- --uninstall Remove from all targets
402
- --help Show this help
403
-
404
- Modes explained:
405
- BEGINNER (default) Just works. Hooks capture silently, instincts grow over time.
406
- 3 tools via /continuous-improvement command.
407
-
408
- EXPERT Everything in beginner + MCP server with 8 tools:
409
- import/export, manual instinct creation, observation viewer,
410
- confidence tuning. Plus session start/end hooks.
411
-
412
- MCP MCP server only for editors that support MCP but not
413
- Claude Code hooks (Cursor, Zed, Windsurf, VS Code).
414
-
415
- Examples:
416
- npx continuous-improvement install # beginner (default)
417
- npx continuous-improvement install --mode expert # full power
418
- npx continuous-improvement install --mode mcp # MCP server only
419
- npx continuous-improvement install --target all # install everywhere
420
- npx continuous-improvement install --uninstall # remove all
328
+ console.log(`
329
+ Usage: npx continuous-improvement <subcommand> [options]
330
+
331
+ Subcommands:
332
+ install Install the plugin (default subcommand)
333
+ backfill Tag legacy thin-schema rows in observations.jsonl files
334
+ --uninstall Remove all installed files
335
+
336
+ Options for 'install':
337
+ --mode <mode> Installation mode:
338
+ beginner hooks + skill + commands (default)
339
+ expert — beginner + MCP server + session hooks
340
+ --pack <name> Load a starter instinct pack (react, python, go, meta)
341
+ --help Show this help
342
+
343
+ Options for 'backfill':
344
+ --dry-run Report counts only, no file writes
345
+ --help Show backfill-specific help
346
+
347
+ Examples:
348
+ npx continuous-improvement install # beginner (default)
349
+ npx continuous-improvement install --mode expert # full power
350
+ npx continuous-improvement install --pack react # load React instincts
351
+ npx continuous-improvement backfill --dry-run # report thin/rich row counts
352
+ npx continuous-improvement backfill # tag rows in place
353
+ npx continuous-improvement install --uninstall # remove everything
421
354
  `);
422
355
  }
423
-
424
- // --- Main ---
425
-
356
+ function getProjectHashSync() {
357
+ try {
358
+ const root = execSync("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf8" }).trim();
359
+ const hash = createHash("sha256").update(root).digest("hex").slice(0, 12);
360
+ return { root, hash };
361
+ }
362
+ catch {
363
+ return { root: "global", hash: "global" };
364
+ }
365
+ }
426
366
  const args = process.argv.slice(2);
427
367
  const command = args[0];
428
-
368
+ const validCommands = new Set(["install", "backfill", "--help", "-h", "--uninstall"]);
429
369
  if (args.includes("--help") || args.includes("-h")) {
430
- printUsage();
431
- process.exit(0);
370
+ printUsage();
371
+ process.exit(0);
432
372
  }
433
-
434
- if (!command || !["install", "--help", "-h", "--uninstall"].includes(command)) {
435
- printUsage();
436
- process.exit(command ? 1 : 0);
373
+ if (!command || !validCommands.has(command)) {
374
+ printUsage();
375
+ process.exit(command ? 1 : 0);
376
+ }
377
+ if (command === "backfill") {
378
+ // Delegate to bin/backfill.mjs without spawning a separate process so the
379
+ // user gets one binary surface. Forward the remaining args (e.g. --dry-run).
380
+ const { execFileSync } = await import("node:child_process");
381
+ const backfillBin = join(REPO_ROOT, "bin", "backfill.mjs");
382
+ try {
383
+ execFileSync("node", [backfillBin, ...args.slice(1)], { stdio: "inherit" });
384
+ }
385
+ catch {
386
+ // backfill.mjs always exits 0; if execFileSync threw, propagate the exit
387
+ // status without a stack trace so operator output stays clean.
388
+ }
389
+ process.exit(0);
437
390
  }
438
-
439
391
  if (args.includes("--uninstall")) {
440
- uninstallAll();
441
- process.exit(0);
392
+ uninstallAll();
393
+ process.exit(0);
442
394
  }
443
-
444
- console.log(`
445
- continuous-improvement v3.0 (mode: ${INSTALL_MODE})
446
- Research → Plan → Execute → Verify → Reflect → Learn → Iterate
395
+ console.log(`
396
+ continuous-improvement (mode: ${INSTALL_MODE})
397
+ Research Plan → Execute → Verify → Reflect → Learn → Iterate
447
398
  `);
448
-
449
- const targetIdx = args.indexOf("--target");
450
- let targets;
451
-
452
- if (targetIdx !== -1 && args[targetIdx + 1]) {
453
- const requested = args[targetIdx + 1].toLowerCase();
454
- if (requested === "all") {
455
- targets = Object.keys(TARGETS);
456
- } else if (TARGETS[requested]) {
457
- targets = [requested];
458
- } else {
459
- console.error(`Unknown target: ${requested}`);
460
- console.error(`Available: ${Object.keys(TARGETS).join(", ")}, all`);
461
- process.exit(1);
462
- }
463
- } else {
464
- targets = detectTargets();
465
- if (targets.length === 0) {
466
- console.log("No supported agent configs detected. Installing to Claude Code by default.\n");
467
- targets = ["claude"];
468
- } else {
469
- console.log(`Detected: ${targets.map((t) => TARGETS[t].label).join(", ")}\n`);
470
- }
471
- }
472
-
473
- console.log("Installing...\n");
474
-
475
- let installed = 0;
476
- for (const t of targets) {
477
- if (installTo(t)) installed++;
478
- }
479
-
480
- const hasClaude = targets.includes("claude");
481
-
399
+ console.log("Installing to Claude Code...\n");
400
+ const installed = installSkill() ? 1 : 0;
482
401
  const modeInfo = {
483
- beginner: "Hooks are capturing silently. System auto-levels as you use it.",
484
- expert: "Full plugin active: hooks + MCP server + session hooks. 8 tools available.",
485
- mcp: "MCP server registered. Connect from any MCP-compatible editor.",
402
+ beginner: "Hooks are capturing silently. System auto-levels as you use it.",
403
+ expert: "Full plugin active: hooks + MCP server + session hooks. 12 tools available.",
486
404
  };
487
-
488
- console.log(`
489
- ${installed > 0 ? "Done." : "Failed."} Installed to ${installed}/${targets.length} target(s).
490
- ${hasClaude ? `\n${modeInfo[INSTALL_MODE] || modeInfo.beginner}` : ""}
491
- Next steps:
492
- 1. Start a new Claude Code session
493
- 2. Say: "Use the continuous-improvement framework to [your task]"
494
- 3. After your first task, run: /continuous-improvement
495
- ${INSTALL_MODE === "expert" ? "\nMCP tools available: ci_status, ci_instincts, ci_reflect, ci_reinforce,\n ci_create_instinct, ci_observations, ci_export, ci_import" : ""}
496
- ${INSTALL_MODE === "mcp" ? "\nMCP tools available: ci_status, ci_instincts, ci_reflect" : ""}
405
+ const packIndex = rawArgs.indexOf("--pack");
406
+ if (packIndex !== -1 && rawArgs[packIndex + 1]) {
407
+ const packName = rawArgs[packIndex + 1];
408
+ const packPath = join(REPO_ROOT, "instinct-packs", `${packName}.json`);
409
+ if (existsSync(packPath)) {
410
+ const project = getProjectHashSync();
411
+ const targetDir = join(getHomeDir(), ".claude", "instincts", project.hash);
412
+ mkdirSync(targetDir, { recursive: true });
413
+ const instincts = JSON.parse(readFileSync(packPath, "utf8"));
414
+ let loaded = 0;
415
+ for (const instinct of instincts) {
416
+ const instinctPath = join(targetDir, `${instinct.id}.yaml`);
417
+ if (existsSync(instinctPath)) {
418
+ continue;
419
+ }
420
+ const today = new Date().toISOString().split("T")[0];
421
+ const yaml = [
422
+ `id: ${instinct.id}`,
423
+ `trigger: "${instinct.trigger}"`,
424
+ `confidence: ${instinct.confidence}`,
425
+ `domain: ${instinct.domain || "workflow"}`,
426
+ `source: pack-${packName}`,
427
+ "scope: project",
428
+ `project_id: ${project.hash}`,
429
+ `created: "${today}"`,
430
+ `last_seen: "${today}"`,
431
+ "observation_count: 0",
432
+ "---",
433
+ instinct.body,
434
+ ].join("\n");
435
+ writeFileSync(instinctPath, yaml + "\n");
436
+ loaded++;
437
+ }
438
+ console.log(` ✓ Loaded ${loaded}/${instincts.length} instincts from ${packName} pack`);
439
+ }
440
+ else {
441
+ const available = readdirSync(join(REPO_ROOT, "instinct-packs"))
442
+ .filter((file) => file.endsWith(".json"))
443
+ .map((file) => file.replace(".json", ""));
444
+ console.error(` ✗ Unknown pack: ${packName}. Available: ${available.join(", ")}`);
445
+ }
446
+ }
447
+ console.log(`
448
+ ${installed === 1 ? "Done." : "Failed."}
449
+ ${modeInfo[INSTALL_MODE]}
450
+
451
+ Next steps:
452
+ 1. Start a new Claude Code session
453
+ 2. Say: "Use the continuous-improvement framework to [your task]"
454
+ 3. If a task needs persistent planning, run: /planning-with-files
455
+ 4. After your first task, run: /continuous-improvement
456
+ 5. Try: /discipline for quick reference, /dashboard for instinct health
457
+ ${INSTALL_MODE === "expert" ? "\nMCP tools available: ci_status, ci_instincts, ci_reflect, ci_reinforce,\n ci_create_instinct, ci_observations, ci_export, ci_import, ci_plan_init,\n ci_plan_status, ci_dashboard, ci_load_pack" : ""}
458
+ Available instinct packs: npx continuous-improvement install --pack react|python|go|meta
497
459
  `);