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.
- package/.claude-plugin/marketplace.json +78 -0
- package/CHANGELOG.md +210 -0
- package/LICENSE +21 -21
- package/QUICKSTART.md +101 -81
- package/README.md +224 -198
- package/SKILL.md +87 -9
- package/action.yml +33 -0
- package/bin/analyze.sh +161 -153
- package/bin/backfill.mjs +172 -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 +169 -0
- package/bin/harvest-friction.mjs +279 -0
- package/bin/hook-stats.mjs +258 -0
- package/bin/install.mjs +418 -456
- package/bin/lint-transcript.mjs +239 -0
- package/bin/mcp-server.mjs +842 -499
- 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 +115 -74
- package/commands/dashboard.md +56 -0
- package/commands/discipline.md +51 -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 +153 -0
- package/commands/swarm.md +101 -0
- package/commands/workspace-surface-audit.md +77 -0
- package/hooks/observe.sh +172 -134
- package/hooks/session.sh +106 -106
- package/hooks/three-section-close.mjs +181 -0
- package/instinct-packs/go.json +58 -0
- package/instinct-packs/meta.json +16 -0
- package/instinct-packs/python.json +58 -0
- package/instinct-packs/react.json +58 -0
- package/lib/cli-anything.mjs +401 -0
- package/lib/compound-engineering.mjs +831 -0
- package/lib/observe-event.mjs +128 -0
- package/lib/plugin-metadata.mjs +432 -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 +68 -0
- package/package.json +38 -15
- 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 +56 -0
- package/plugins/continuous-improvement/bin/backfill.mjs +172 -0
- package/plugins/continuous-improvement/bin/mcp-server.mjs +886 -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 +153 -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/hooks.json +60 -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 +432 -0
- package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +43 -0
- package/plugins/continuous-improvement/skills/README.md +34 -0
- package/plugins/continuous-improvement/skills/continuous-improvement/SKILL.md +249 -0
- package/plugins/continuous-improvement/skills/deploy-receipt/SKILL.md +131 -0
- package/plugins/continuous-improvement/skills/gateguard/SKILL.md +155 -0
- package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +108 -0
- package/plugins/continuous-improvement/skills/proceed-with-the-recommendation/SKILL.md +454 -0
- package/plugins/continuous-improvement/skills/ralph/SKILL.md +221 -0
- package/plugins/continuous-improvement/skills/safety-guard/SKILL.md +76 -0
- package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +104 -0
- package/plugins/continuous-improvement/skills/superpowers/SKILL.md +212 -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 +192 -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/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 +34 -5
- package/skills/README.md +79 -0
- package/skills/deploy-receipt.md +131 -0
- package/skills/gateguard.md +155 -0
- package/skills/para-memory-files.md +108 -0
- package/skills/proceed-with-the-recommendation.md +454 -0
- package/skills/ralph.md +221 -0
- package/skills/safety-guard.md +76 -0
- package/skills/strategic-compact.md +104 -0
- package/skills/superpowers.md +212 -0
- package/skills/tdd-workflow.md +411 -0
- package/skills/token-budget-advisor.md +136 -0
- package/skills/verification-loop.md +192 -0
- package/skills/wild-risa-balance.md +191 -0
- package/skills/workspace-surface-audit.md +147 -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 +47 -0
package/bin/mcp-server.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* continuous-improvement MCP Server
|
|
5
4
|
*
|
|
@@ -11,533 +10,877 @@
|
|
|
11
10
|
* node bin/mcp-server.mjs --mode expert # all tools
|
|
12
11
|
* node bin/mcp-server.mjs --mode beginner # explicit beginner
|
|
13
12
|
*/
|
|
14
|
-
|
|
15
|
-
import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from "node:fs";
|
|
16
|
-
import { join, basename } from "node:path";
|
|
17
|
-
import { homedir } from "node:os";
|
|
18
13
|
import { execSync } from "node:child_process";
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { basename, dirname, join } from "node:path";
|
|
19
17
|
import { createInterface } from "node:readline";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
import { PACKAGE_NAME, VERSION, getToolDefinitions, isPluginMode, } from "../lib/plugin-metadata.mjs";
|
|
21
|
+
function getHomeDir() {
|
|
22
|
+
return process.env.HOME || process.env.USERPROFILE || homedir();
|
|
23
|
+
}
|
|
24
|
+
function asRecord(value) {
|
|
25
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
26
|
+
? value
|
|
27
|
+
: null;
|
|
28
|
+
}
|
|
29
|
+
function getString(value, fallback = "") {
|
|
30
|
+
return typeof value === "string" ? value : fallback;
|
|
31
|
+
}
|
|
32
|
+
function getNumber(value, fallback = 0) {
|
|
33
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
34
|
+
}
|
|
35
|
+
function getBoolean(value, fallback = false) {
|
|
36
|
+
return typeof value === "boolean" ? value : fallback;
|
|
37
|
+
}
|
|
38
|
+
function toInstinct(value) {
|
|
39
|
+
const record = asRecord(value);
|
|
40
|
+
if (!record) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const id = getString(record.id);
|
|
44
|
+
const trigger = getString(record.trigger);
|
|
45
|
+
const body = getString(record.body);
|
|
46
|
+
if (!id || !trigger || !body) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
let confidence = getNumber(record.confidence, 0.6);
|
|
50
|
+
// Validate confidence bounds (0.0 - 0.9)
|
|
51
|
+
confidence = Math.max(0, Math.min(0.9, confidence));
|
|
52
|
+
const scopeValue = getString(record.scope);
|
|
53
|
+
const scope = scopeValue === "global" || scopeValue === "project"
|
|
54
|
+
? scopeValue
|
|
55
|
+
: undefined;
|
|
56
|
+
return {
|
|
57
|
+
id,
|
|
58
|
+
trigger,
|
|
59
|
+
body,
|
|
60
|
+
confidence,
|
|
61
|
+
domain: getString(record.domain) || undefined,
|
|
62
|
+
source: getString(record.source) || undefined,
|
|
63
|
+
scope,
|
|
64
|
+
project_id: getString(record.project_id) || undefined,
|
|
65
|
+
created: getString(record.created) || undefined,
|
|
66
|
+
last_seen: getString(record.last_seen) || undefined,
|
|
67
|
+
observation_count: typeof record.observation_count === "number" ? record.observation_count : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
71
|
+
const INSTINCTS_DIR = join(getHomeDir(), ".claude", "instincts");
|
|
26
72
|
const GLOBAL_DIR = join(INSTINCTS_DIR, "global");
|
|
27
|
-
|
|
73
|
+
const PACKS_DIR = join(REPO_ROOT, "instinct-packs");
|
|
74
|
+
const PLANNING_TEMPLATES_DIR = join(REPO_ROOT, "templates", "planning-with-files");
|
|
75
|
+
const DEFAULT_PLANNING_PHASES = ["Research", "Plan", "Execute", "Verify", "Reflect"];
|
|
76
|
+
const PLANNING_FILES = {
|
|
77
|
+
taskPlan: "task_plan.md",
|
|
78
|
+
findings: "findings.md",
|
|
79
|
+
progress: "progress.md",
|
|
80
|
+
};
|
|
28
81
|
const args = process.argv.slice(2);
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
// Helpers
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
|
|
82
|
+
const modeIndex = args.indexOf("--mode");
|
|
83
|
+
const requestedMode = args[modeIndex + 1];
|
|
84
|
+
const MODE = isPluginMode(requestedMode) ? requestedMode : "beginner";
|
|
36
85
|
function getProjectHash() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
86
|
+
try {
|
|
87
|
+
const root = execSync("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf8" }).trim();
|
|
88
|
+
const hash = createHash("sha256").update(root).digest("hex").slice(0, 12);
|
|
89
|
+
return { root, hash, name: basename(root) };
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return { root: "global", hash: "global", name: "global" };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
49
95
|
function readInstincts(projectHash) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
96
|
+
const instincts = [];
|
|
97
|
+
const dirs = [GLOBAL_DIR];
|
|
98
|
+
if (projectHash !== "global") {
|
|
99
|
+
dirs.push(join(INSTINCTS_DIR, projectHash));
|
|
100
|
+
}
|
|
101
|
+
for (const dir of dirs) {
|
|
102
|
+
if (!existsSync(dir)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
for (const file of readdirSync(dir)) {
|
|
106
|
+
if (!file.endsWith(".yaml")) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const parsed = parseYamlInstinct(readFileSync(join(dir, file), "utf8"));
|
|
111
|
+
if (parsed) {
|
|
112
|
+
instincts.push(parsed);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// skip malformed files
|
|
117
|
+
}
|
|
118
|
+
}
|
|
67
119
|
}
|
|
68
|
-
|
|
69
|
-
return instincts;
|
|
120
|
+
return instincts;
|
|
70
121
|
}
|
|
71
|
-
|
|
72
122
|
function parseYamlInstinct(content) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
const lines = content.split("\n");
|
|
124
|
+
const meta = {};
|
|
125
|
+
let body = "";
|
|
126
|
+
let inBody = false;
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
if (line.trim() === "---" && Object.keys(meta).length > 0) {
|
|
129
|
+
inBody = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (inBody) {
|
|
133
|
+
body += `${line}\n`;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const match = line.match(/^(\w[\w_-]*):\s*(.+)/);
|
|
137
|
+
if (!match) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const rawValue = match[2].trim().replace(/^["']|["']$/g, "");
|
|
141
|
+
const value = rawValue !== "" && !Number.isNaN(Number(rawValue))
|
|
142
|
+
? Number(rawValue)
|
|
143
|
+
: rawValue;
|
|
144
|
+
meta[match[1]] = value;
|
|
82
145
|
}
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
} else {
|
|
86
|
-
const match = line.match(/^(\w[\w_-]*):\s*(.+)/);
|
|
87
|
-
if (match) {
|
|
88
|
-
let val = match[2].trim().replace(/^["']|["']$/g, "");
|
|
89
|
-
if (!isNaN(val) && val !== "") val = parseFloat(val);
|
|
90
|
-
meta[match[1]] = val;
|
|
91
|
-
}
|
|
146
|
+
if (typeof meta.id !== "string" || typeof meta.trigger !== "string") {
|
|
147
|
+
return null;
|
|
92
148
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
149
|
+
return {
|
|
150
|
+
id: meta.id,
|
|
151
|
+
trigger: meta.trigger,
|
|
152
|
+
body: body.trim(),
|
|
153
|
+
confidence: typeof meta.confidence === "number" ? meta.confidence : 0.6,
|
|
154
|
+
domain: typeof meta.domain === "string" ? meta.domain : undefined,
|
|
155
|
+
source: typeof meta.source === "string" ? meta.source : undefined,
|
|
156
|
+
scope: meta.scope === "global" || meta.scope === "project" ? meta.scope : undefined,
|
|
157
|
+
project_id: typeof meta.project_id === "string" ? meta.project_id : undefined,
|
|
158
|
+
created: typeof meta.created === "string" ? meta.created : undefined,
|
|
159
|
+
last_seen: typeof meta.last_seen === "string" ? meta.last_seen : undefined,
|
|
160
|
+
observation_count: typeof meta.observation_count === "number" ? meta.observation_count : undefined,
|
|
161
|
+
};
|
|
97
162
|
}
|
|
98
|
-
|
|
99
163
|
function countObservations(projectHash) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
164
|
+
const observationsFile = join(INSTINCTS_DIR, projectHash, "observations.jsonl");
|
|
165
|
+
if (!existsSync(observationsFile)) {
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const content = readFileSync(observationsFile, "utf8");
|
|
170
|
+
return content.split("\n").filter((line) => line.trim().length > 0).length;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
110
176
|
function getRecentObservations(projectHash, limit = 50) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
177
|
+
const observationsFile = join(INSTINCTS_DIR, projectHash, "observations.jsonl");
|
|
178
|
+
if (!existsSync(observationsFile)) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
const lines = readFileSync(observationsFile, "utf8")
|
|
183
|
+
.split("\n")
|
|
184
|
+
.filter((line) => line.trim().length > 0);
|
|
185
|
+
return lines.slice(-limit).flatMap((line) => {
|
|
186
|
+
try {
|
|
187
|
+
return [JSON.parse(line)];
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return [];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
123
198
|
function detectLevel(projectHash) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
199
|
+
const observationCount = countObservations(projectHash);
|
|
200
|
+
const instincts = readInstincts(projectHash);
|
|
201
|
+
const hasHighConfidence = instincts.some((instinct) => instinct.confidence >= 0.7);
|
|
202
|
+
const hasMidConfidence = instincts.some((instinct) => instinct.confidence >= 0.5 && instinct.confidence < 0.7);
|
|
203
|
+
if (hasHighConfidence) {
|
|
204
|
+
return "AUTO-APPLY";
|
|
205
|
+
}
|
|
206
|
+
if (hasMidConfidence) {
|
|
207
|
+
return "SUGGEST";
|
|
208
|
+
}
|
|
209
|
+
if (observationCount >= 20 || instincts.length > 0) {
|
|
210
|
+
return "ANALYZE";
|
|
211
|
+
}
|
|
212
|
+
return "CAPTURE";
|
|
213
|
+
}
|
|
135
214
|
function writeInstinct(projectHash, instinct) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
215
|
+
const scope = instinct.scope === "global" ? "global" : "project";
|
|
216
|
+
const dir = scope === "global" ? GLOBAL_DIR : join(INSTINCTS_DIR, projectHash);
|
|
217
|
+
mkdirSync(dir, { recursive: true });
|
|
218
|
+
const today = new Date().toISOString().split("T")[0];
|
|
219
|
+
const yaml = [
|
|
220
|
+
`id: ${instinct.id}`,
|
|
221
|
+
`trigger: "${instinct.trigger}"`,
|
|
222
|
+
`confidence: ${instinct.confidence}`,
|
|
223
|
+
`domain: ${instinct.domain || "workflow"}`,
|
|
224
|
+
`source: ${instinct.source || "manual"}`,
|
|
225
|
+
`scope: ${scope}`,
|
|
226
|
+
`project_id: ${projectHash}`,
|
|
227
|
+
`created: "${instinct.created || today}"`,
|
|
228
|
+
`last_seen: "${instinct.last_seen || today}"`,
|
|
229
|
+
`observation_count: ${instinct.observation_count || 1}`,
|
|
230
|
+
"---",
|
|
231
|
+
instinct.body,
|
|
232
|
+
].join("\n");
|
|
233
|
+
writeFileSync(join(dir, `${instinct.id}.yaml`), `${yaml}\n`);
|
|
234
|
+
}
|
|
157
235
|
function updateInstinctConfidence(projectHash, instinctId, delta) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const newConf = Math.max(0, Math.min(0.9, (instinct.confidence || 0.5) + delta));
|
|
163
|
-
instinct.confidence = Math.round(newConf * 100) / 100;
|
|
164
|
-
instinct.last_seen = new Date().toISOString().split("T")[0];
|
|
165
|
-
writeInstinct(projectHash, instinct);
|
|
166
|
-
return instinct;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
// MCP Protocol (JSON-RPC over stdio, no SDK dependency)
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
|
|
173
|
-
const BEGINNER_TOOLS = [
|
|
174
|
-
{
|
|
175
|
-
name: "ci_status",
|
|
176
|
-
description: "Show current level, instinct count, and observation count for this project. Good starting point to see what the system has learned.",
|
|
177
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: "ci_instincts",
|
|
181
|
-
description: "List all learned instincts for this project with their confidence levels and behaviors.",
|
|
182
|
-
inputSchema: {
|
|
183
|
-
type: "object",
|
|
184
|
-
properties: {
|
|
185
|
-
min_confidence: { type: "number", description: "Minimum confidence to show (default: 0)", default: 0 },
|
|
186
|
-
},
|
|
187
|
-
required: [],
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
name: "ci_reflect",
|
|
192
|
-
description: "Generate a structured reflection for the current session. Provide a summary of what you worked on.",
|
|
193
|
-
inputSchema: {
|
|
194
|
-
type: "object",
|
|
195
|
-
properties: {
|
|
196
|
-
summary: { type: "string", description: "Brief summary of what was done this session" },
|
|
197
|
-
},
|
|
198
|
-
required: ["summary"],
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
];
|
|
202
|
-
|
|
203
|
-
const EXPERT_TOOLS = [
|
|
204
|
-
{
|
|
205
|
-
name: "ci_reinforce",
|
|
206
|
-
description: "Accept or reject an instinct suggestion. Adjusts confidence: +0.15 for accept, -0.1 for reject.",
|
|
207
|
-
inputSchema: {
|
|
208
|
-
type: "object",
|
|
209
|
-
properties: {
|
|
210
|
-
instinct_id: { type: "string", description: "The instinct ID to reinforce" },
|
|
211
|
-
accepted: { type: "boolean", description: "true = accept (+0.15), false = reject (-0.1)" },
|
|
212
|
-
},
|
|
213
|
-
required: ["instinct_id", "accepted"],
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: "ci_create_instinct",
|
|
218
|
-
description: "Manually create a new instinct with a trigger, body, and starting confidence.",
|
|
219
|
-
inputSchema: {
|
|
220
|
-
type: "object",
|
|
221
|
-
properties: {
|
|
222
|
-
id: { type: "string", description: "Unique instinct ID (kebab-case)" },
|
|
223
|
-
trigger: { type: "string", description: "When this instinct applies" },
|
|
224
|
-
body: { type: "string", description: "The behavior to follow" },
|
|
225
|
-
confidence: { type: "number", description: "Starting confidence 0.0-0.9 (default: 0.6)", default: 0.6 },
|
|
226
|
-
domain: { type: "string", description: "Domain: workflow|tooling|testing|patterns|code-style", default: "workflow" },
|
|
227
|
-
scope: { type: "string", description: "Scope: project|global", default: "project" },
|
|
228
|
-
},
|
|
229
|
-
required: ["id", "trigger", "body"],
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
name: "ci_observations",
|
|
234
|
-
description: "View recent tool call observations captured by hooks.",
|
|
235
|
-
inputSchema: {
|
|
236
|
-
type: "object",
|
|
237
|
-
properties: {
|
|
238
|
-
limit: { type: "number", description: "Number of recent observations to return (default: 20)", default: 20 },
|
|
239
|
-
},
|
|
240
|
-
required: [],
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
name: "ci_export",
|
|
245
|
-
description: "Export all instincts as a JSON array for sharing or backup.",
|
|
246
|
-
inputSchema: {
|
|
247
|
-
type: "object",
|
|
248
|
-
properties: {
|
|
249
|
-
scope: { type: "string", description: "Which instincts: project|global|all (default: all)", default: "all" },
|
|
250
|
-
},
|
|
251
|
-
required: [],
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
name: "ci_import",
|
|
256
|
-
description: "Import instincts from a JSON array. Skips duplicates by ID.",
|
|
257
|
-
inputSchema: {
|
|
258
|
-
type: "object",
|
|
259
|
-
properties: {
|
|
260
|
-
instincts_json: { type: "string", description: "JSON array of instinct objects to import" },
|
|
261
|
-
scope: { type: "string", description: "Import to: project|global (default: project)", default: "project" },
|
|
262
|
-
},
|
|
263
|
-
required: ["instincts_json"],
|
|
264
|
-
},
|
|
265
|
-
},
|
|
266
|
-
];
|
|
267
|
-
|
|
268
|
-
function getAllTools() {
|
|
269
|
-
if (MODE === "expert") return [...BEGINNER_TOOLS, ...EXPERT_TOOLS];
|
|
270
|
-
return BEGINNER_TOOLS;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ---------------------------------------------------------------------------
|
|
274
|
-
// Tool handlers
|
|
275
|
-
// ---------------------------------------------------------------------------
|
|
276
|
-
|
|
277
|
-
function handleTool(name, params) {
|
|
278
|
-
const project = getProjectHash();
|
|
279
|
-
|
|
280
|
-
switch (name) {
|
|
281
|
-
case "ci_status": {
|
|
282
|
-
const level = detectLevel(project.hash);
|
|
283
|
-
const obsCount = countObservations(project.hash);
|
|
284
|
-
const instincts = readInstincts(project.hash);
|
|
285
|
-
const byConfidence = {
|
|
286
|
-
silent: instincts.filter((i) => i.confidence < 0.5).length,
|
|
287
|
-
suggest: instincts.filter((i) => i.confidence >= 0.5 && i.confidence < 0.7).length,
|
|
288
|
-
autoApply: instincts.filter((i) => i.confidence >= 0.7).length,
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
return text([
|
|
292
|
-
`## continuous-improvement Status`,
|
|
293
|
-
``,
|
|
294
|
-
`**Project:** ${project.name}`,
|
|
295
|
-
`**Level:** ${level}`,
|
|
296
|
-
`**Observations:** ${obsCount}`,
|
|
297
|
-
`**Instincts:** ${instincts.length} total`,
|
|
298
|
-
` - Silent (< 0.5): ${byConfidence.silent}`,
|
|
299
|
-
` - Suggest (0.5-0.69): ${byConfidence.suggest}`,
|
|
300
|
-
` - Auto-apply (0.7+): ${byConfidence.autoApply}`,
|
|
301
|
-
``,
|
|
302
|
-
`**Mode:** ${MODE}`,
|
|
303
|
-
level === "CAPTURE" ? `\n_Keep working — hooks are capturing. Analysis begins at 20 observations._` : "",
|
|
304
|
-
].join("\n"));
|
|
236
|
+
const instincts = readInstincts(projectHash);
|
|
237
|
+
const instinct = instincts.find((candidate) => candidate.id === instinctId);
|
|
238
|
+
if (!instinct) {
|
|
239
|
+
return null;
|
|
305
240
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
const behavior = i.confidence >= 0.7 ? "AUTO-APPLY" : i.confidence >= 0.5 ? "SUGGEST" : "silent";
|
|
317
|
-
return `- **${i.id}** (${i.confidence}) [${behavior}]\n Trigger: ${i.trigger}\n ${i.body}`;
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
return text(`## Instincts for ${project.name}\n\n${lines.join("\n\n")}`);
|
|
241
|
+
const newConfidence = Math.max(0, Math.min(0.9, instinct.confidence + delta));
|
|
242
|
+
instinct.confidence = Math.round(newConfidence * 100) / 100;
|
|
243
|
+
instinct.last_seen = new Date().toISOString().split("T")[0];
|
|
244
|
+
writeInstinct(projectHash, instinct);
|
|
245
|
+
return instinct;
|
|
246
|
+
}
|
|
247
|
+
function getWorkspaceRoot() {
|
|
248
|
+
try {
|
|
249
|
+
const root = execSync("git rev-parse --show-toplevel 2>/dev/null", { encoding: "utf8" }).trim();
|
|
250
|
+
return root || process.cwd();
|
|
321
251
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const summary = params.summary || "No summary provided";
|
|
325
|
-
const reflection = [
|
|
326
|
-
`## Reflection — ${new Date().toISOString().split("T")[0]}`,
|
|
327
|
-
``,
|
|
328
|
-
`**Session summary:** ${summary}`,
|
|
329
|
-
``,
|
|
330
|
-
`Use this template to reflect:`,
|
|
331
|
-
`- **What worked:**`,
|
|
332
|
-
`- **What failed:**`,
|
|
333
|
-
`- **What I'd do differently:**`,
|
|
334
|
-
`- **Rule to add:** (becomes an instinct at 0.6 confidence)`,
|
|
335
|
-
].join("\n");
|
|
336
|
-
|
|
337
|
-
return text(reflection);
|
|
252
|
+
catch {
|
|
253
|
+
return process.cwd();
|
|
338
254
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (!updated) return error(`Instinct "${params.instinct_id}" not found`);
|
|
345
|
-
return text(`${params.accepted ? "Accepted" : "Rejected"} **${updated.id}** — confidence now ${updated.confidence}`);
|
|
255
|
+
}
|
|
256
|
+
function readPlanningTemplate(fileName, fallback) {
|
|
257
|
+
const templatePath = join(PLANNING_TEMPLATES_DIR, fileName);
|
|
258
|
+
if (!existsSync(templatePath)) {
|
|
259
|
+
return fallback;
|
|
346
260
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (MODE !== "expert") return error("ci_create_instinct requires expert mode");
|
|
350
|
-
writeInstinct(project.hash, {
|
|
351
|
-
id: params.id,
|
|
352
|
-
trigger: params.trigger,
|
|
353
|
-
body: params.body,
|
|
354
|
-
confidence: params.confidence || 0.6,
|
|
355
|
-
domain: params.domain || "workflow",
|
|
356
|
-
source: "manual",
|
|
357
|
-
scope: params.scope || "project",
|
|
358
|
-
observation_count: 1,
|
|
359
|
-
});
|
|
360
|
-
return text(`Created instinct **${params.id}** with confidence ${params.confidence || 0.6}`);
|
|
261
|
+
try {
|
|
262
|
+
return readFileSync(templatePath, "utf8");
|
|
361
263
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
if (MODE !== "expert") return error("ci_observations requires expert mode");
|
|
365
|
-
const limit = params.limit || 20;
|
|
366
|
-
const obs = getRecentObservations(project.hash, limit);
|
|
367
|
-
if (obs.length === 0) return text("No observations yet. Hooks capture tool calls automatically.");
|
|
368
|
-
const lines = obs.map((o) => `[${o.ts}] ${o.event} — ${o.tool}`);
|
|
369
|
-
return text(`## Recent Observations (${obs.length})\n\n${lines.join("\n")}`);
|
|
264
|
+
catch {
|
|
265
|
+
return fallback;
|
|
370
266
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
267
|
+
}
|
|
268
|
+
function replaceTemplateToken(template, token, value) {
|
|
269
|
+
return template.split(`{{${token}}}`).join(value);
|
|
270
|
+
}
|
|
271
|
+
function getToday() {
|
|
272
|
+
return new Date().toISOString().split("T")[0];
|
|
273
|
+
}
|
|
274
|
+
function normalizePhases(value) {
|
|
275
|
+
if (!Array.isArray(value)) {
|
|
276
|
+
return [...DEFAULT_PLANNING_PHASES];
|
|
379
277
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
278
|
+
const phases = value
|
|
279
|
+
.map((phase) => typeof phase === "string" ? phase.trim() : "")
|
|
280
|
+
.filter((phase) => phase.length > 0);
|
|
281
|
+
return phases.length > 0 ? phases : [...DEFAULT_PLANNING_PHASES];
|
|
282
|
+
}
|
|
283
|
+
function formatPhaseChecklist(phases) {
|
|
284
|
+
return phases.map((phase) => `- [ ] ${phase}`).join("\n");
|
|
285
|
+
}
|
|
286
|
+
function renderTaskPlan(goal, phases) {
|
|
287
|
+
const fallback = `# Task Plan
|
|
288
|
+
|
|
289
|
+
## Goal
|
|
290
|
+
{{goal}}
|
|
291
|
+
|
|
292
|
+
## Status
|
|
293
|
+
{{status}}
|
|
294
|
+
|
|
295
|
+
## Phases
|
|
296
|
+
{{phases}}
|
|
297
|
+
|
|
298
|
+
## Key Questions
|
|
299
|
+
- [ ] What existing implementation or context should be reviewed first?
|
|
300
|
+
- [ ] What constraints or risks need validation before execution?
|
|
301
|
+
- [ ] What verification proves the task is complete?
|
|
302
|
+
|
|
303
|
+
## Decisions Made
|
|
304
|
+
- None yet.
|
|
305
|
+
|
|
306
|
+
## Errors Encountered
|
|
307
|
+
| Error | Attempt | Resolution |
|
|
308
|
+
| --- | --- | --- |
|
|
309
|
+
| None yet | - | - |
|
|
310
|
+
`;
|
|
311
|
+
let template = readPlanningTemplate(PLANNING_FILES.taskPlan, fallback);
|
|
312
|
+
template = replaceTemplateToken(template, "goal", goal);
|
|
313
|
+
template = replaceTemplateToken(template, "status", "Not started");
|
|
314
|
+
template = replaceTemplateToken(template, "phases", formatPhaseChecklist(phases));
|
|
315
|
+
template = replaceTemplateToken(template, "date", getToday());
|
|
316
|
+
return template.endsWith("\n") ? template : `${template}\n`;
|
|
317
|
+
}
|
|
318
|
+
function renderFindings() {
|
|
319
|
+
const fallback = `# Findings
|
|
320
|
+
|
|
321
|
+
## Research Notes
|
|
322
|
+
- Created on {{date}}.
|
|
323
|
+
- Record source links, code references, and synthesized findings here.
|
|
324
|
+
|
|
325
|
+
## Sources
|
|
326
|
+
- Add links and short summaries here.
|
|
327
|
+
`;
|
|
328
|
+
let template = readPlanningTemplate(PLANNING_FILES.findings, fallback);
|
|
329
|
+
template = replaceTemplateToken(template, "date", getToday());
|
|
330
|
+
return template.endsWith("\n") ? template : `${template}\n`;
|
|
331
|
+
}
|
|
332
|
+
function renderProgress() {
|
|
333
|
+
const fallback = `# Progress
|
|
334
|
+
|
|
335
|
+
## Session Log
|
|
336
|
+
- {{date}}: Initialized planning files.
|
|
337
|
+
|
|
338
|
+
## Verification Notes
|
|
339
|
+
- Record commands, tests, outputs, and checkpoints here.
|
|
340
|
+
`;
|
|
341
|
+
let template = readPlanningTemplate(PLANNING_FILES.progress, fallback);
|
|
342
|
+
template = replaceTemplateToken(template, "date", getToday());
|
|
343
|
+
return template.endsWith("\n") ? template : `${template}\n`;
|
|
344
|
+
}
|
|
345
|
+
function writePlanningFile(filePath, content, force) {
|
|
346
|
+
const alreadyExists = existsSync(filePath);
|
|
347
|
+
if (alreadyExists && !force) {
|
|
348
|
+
return "preserved";
|
|
349
|
+
}
|
|
350
|
+
writeFileSync(filePath, content);
|
|
351
|
+
return alreadyExists ? "overwritten" : "created";
|
|
352
|
+
}
|
|
353
|
+
function getMarkdownSection(content, heading) {
|
|
354
|
+
const lines = content.split(/\r?\n/);
|
|
355
|
+
const sectionLines = [];
|
|
356
|
+
let inSection = false;
|
|
357
|
+
for (const line of lines) {
|
|
358
|
+
if (line.startsWith("## ")) {
|
|
359
|
+
if (inSection) {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
inSection = line.trim() === `## ${heading}`;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (inSection) {
|
|
366
|
+
sectionLines.push(line);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return sectionLines.join("\n").trim();
|
|
370
|
+
}
|
|
371
|
+
function parseTaskPlan(content) {
|
|
372
|
+
const checkedPhases = [];
|
|
373
|
+
const uncheckedPhases = [];
|
|
374
|
+
for (const line of content.split(/\r?\n/)) {
|
|
375
|
+
const match = line.match(/^- \[([ xX])\] (.+)$/);
|
|
376
|
+
if (!match) {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const phase = match[2].trim();
|
|
380
|
+
if (match[1].toLowerCase() === "x") {
|
|
381
|
+
checkedPhases.push(phase);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
uncheckedPhases.push(phase);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const inlineStatus = content.match(/^\*\*Status:\*\*\s*(.+)$/m) ?? content.match(/^Status:\s*(.+)$/m);
|
|
388
|
+
const statusFromSection = getMarkdownSection(content, "Status")
|
|
389
|
+
.split(/\r?\n/)
|
|
390
|
+
.map((line) => line.trim())
|
|
391
|
+
.find(Boolean);
|
|
392
|
+
return {
|
|
393
|
+
status: inlineStatus?.[1]?.trim() || statusFromSection || "Unknown",
|
|
394
|
+
checkedPhases,
|
|
395
|
+
uncheckedPhases,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function hasMeaningfulContent(content, placeholders) {
|
|
399
|
+
return content
|
|
400
|
+
.split(/\r?\n/)
|
|
401
|
+
.map((line) => line.trim())
|
|
402
|
+
.filter((line) => line.length > 0)
|
|
403
|
+
.filter((line) => !line.startsWith("#"))
|
|
404
|
+
.filter((line) => !/^[-|: ]+$/.test(line))
|
|
405
|
+
.some((line) => line !== "- None yet." &&
|
|
406
|
+
line !== "| None yet | - | - |" &&
|
|
407
|
+
!placeholders.some((placeholder) => line.includes(placeholder)));
|
|
408
|
+
}
|
|
409
|
+
function getAllTools() {
|
|
410
|
+
return getToolDefinitions(MODE);
|
|
411
|
+
}
|
|
412
|
+
function text(value) {
|
|
413
|
+
return { content: [{ type: "text", text: value }] };
|
|
414
|
+
}
|
|
415
|
+
function error(value) {
|
|
416
|
+
return { content: [{ type: "text", text: value }], isError: true };
|
|
417
|
+
}
|
|
418
|
+
function handleTool(name, params) {
|
|
419
|
+
const project = getProjectHash();
|
|
420
|
+
switch (name) {
|
|
421
|
+
case "ci_status": {
|
|
422
|
+
const level = detectLevel(project.hash);
|
|
423
|
+
const observationCount = countObservations(project.hash);
|
|
424
|
+
const instincts = readInstincts(project.hash);
|
|
425
|
+
const byConfidence = {
|
|
426
|
+
silent: instincts.filter((instinct) => instinct.confidence < 0.5).length,
|
|
427
|
+
suggest: instincts.filter((instinct) => instinct.confidence >= 0.5 && instinct.confidence < 0.7).length,
|
|
428
|
+
autoApply: instincts.filter((instinct) => instinct.confidence >= 0.7).length,
|
|
429
|
+
};
|
|
430
|
+
return text([
|
|
431
|
+
"## continuous-improvement Status",
|
|
432
|
+
"",
|
|
433
|
+
`**Project:** ${project.name}`,
|
|
434
|
+
`**Level:** ${level}`,
|
|
435
|
+
`**Observations:** ${observationCount}`,
|
|
436
|
+
`**Instincts:** ${instincts.length} total`,
|
|
437
|
+
` - Silent (< 0.5): ${byConfidence.silent}`,
|
|
438
|
+
` - Suggest (0.5-0.69): ${byConfidence.suggest}`,
|
|
439
|
+
` - Auto-apply (0.7+): ${byConfidence.autoApply}`,
|
|
440
|
+
"",
|
|
441
|
+
`**Mode:** ${MODE}`,
|
|
442
|
+
level === "CAPTURE" ? "\n_Keep working — hooks are capturing. Analysis begins at 20 observations._" : "",
|
|
443
|
+
].join("\n"));
|
|
444
|
+
}
|
|
445
|
+
case "ci_instincts": {
|
|
446
|
+
const minConfidence = getNumber(params.min_confidence, 0);
|
|
447
|
+
const instincts = readInstincts(project.hash).filter((instinct) => instinct.confidence >= minConfidence);
|
|
448
|
+
if (instincts.length === 0) {
|
|
449
|
+
return text("No instincts found. Keep working — the system learns from your sessions.");
|
|
450
|
+
}
|
|
451
|
+
const lines = instincts
|
|
452
|
+
.sort((left, right) => right.confidence - left.confidence)
|
|
453
|
+
.map((instinct) => {
|
|
454
|
+
const behavior = instinct.confidence >= 0.7
|
|
455
|
+
? "AUTO-APPLY"
|
|
456
|
+
: instinct.confidence >= 0.5
|
|
457
|
+
? "SUGGEST"
|
|
458
|
+
: "silent";
|
|
459
|
+
return `- **${instinct.id}** (${instinct.confidence}) [${behavior}]\n Trigger: ${instinct.trigger}\n ${instinct.body}`;
|
|
460
|
+
});
|
|
461
|
+
return text(`## Instincts for ${project.name}\n\n${lines.join("\n\n")}`);
|
|
462
|
+
}
|
|
463
|
+
case "ci_reflect": {
|
|
464
|
+
const summary = getString(params.summary, "No summary provided");
|
|
465
|
+
const reflection = [
|
|
466
|
+
`## Reflection — ${new Date().toISOString().split("T")[0]}`,
|
|
467
|
+
"",
|
|
468
|
+
`**Session summary:** ${summary}`,
|
|
469
|
+
"",
|
|
470
|
+
"Use this template to reflect:",
|
|
471
|
+
"- **What worked:**",
|
|
472
|
+
"- **What failed:**",
|
|
473
|
+
"- **What I'd do differently:**",
|
|
474
|
+
"- **Rule to add:** (becomes an instinct at 0.6 confidence)",
|
|
475
|
+
"- **Iteration — Next best recommendations (ranked, top 3):**",
|
|
476
|
+
" 1. <primary — strongest next core-development move>",
|
|
477
|
+
" 2. <alternative — different angle>",
|
|
478
|
+
" 3. <alternative — smaller or larger scope>",
|
|
479
|
+
" (or just `1. None — goal met, stop.` if the original goal is fully resolved)",
|
|
480
|
+
].join("\n");
|
|
481
|
+
return text(reflection);
|
|
482
|
+
}
|
|
483
|
+
case "ci_reinforce": {
|
|
484
|
+
if (MODE !== "expert") {
|
|
485
|
+
return error("ci_reinforce requires expert mode. Start server with --mode expert");
|
|
486
|
+
}
|
|
487
|
+
const instinctId = getString(params.instinct_id);
|
|
488
|
+
const delta = getBoolean(params.accepted) ? 0.15 : -0.1;
|
|
489
|
+
const updated = updateInstinctConfidence(project.hash, instinctId, delta);
|
|
490
|
+
if (!updated) {
|
|
491
|
+
return error(`Instinct "${instinctId}" not found`);
|
|
492
|
+
}
|
|
493
|
+
return text(`${getBoolean(params.accepted) ? "Accepted" : "Rejected"} **${updated.id}** — confidence now ${updated.confidence}`);
|
|
494
|
+
}
|
|
495
|
+
case "ci_create_instinct": {
|
|
496
|
+
if (MODE !== "expert") {
|
|
497
|
+
return error("ci_create_instinct requires expert mode");
|
|
498
|
+
}
|
|
499
|
+
const scope = getString(params.scope) === "global" ? "global" : "project";
|
|
500
|
+
const instinct = {
|
|
501
|
+
id: getString(params.id),
|
|
502
|
+
trigger: getString(params.trigger),
|
|
503
|
+
body: getString(params.body),
|
|
504
|
+
confidence: getNumber(params.confidence, 0.6),
|
|
505
|
+
domain: getString(params.domain, "workflow"),
|
|
506
|
+
source: "manual",
|
|
507
|
+
scope,
|
|
508
|
+
observation_count: 1,
|
|
509
|
+
};
|
|
510
|
+
writeInstinct(project.hash, instinct);
|
|
511
|
+
return text(`Created instinct **${instinct.id}** with confidence ${instinct.confidence}`);
|
|
512
|
+
}
|
|
513
|
+
case "ci_observations": {
|
|
514
|
+
if (MODE !== "expert") {
|
|
515
|
+
return error("ci_observations requires expert mode");
|
|
516
|
+
}
|
|
517
|
+
const limit = getNumber(params.limit, 20);
|
|
518
|
+
const observations = getRecentObservations(project.hash, limit);
|
|
519
|
+
if (observations.length === 0) {
|
|
520
|
+
return text("No observations yet. Hooks capture tool calls automatically.");
|
|
521
|
+
}
|
|
522
|
+
const lines = observations.map((observation) => `[${observation.ts}] ${observation.event} — ${observation.tool}`);
|
|
523
|
+
return text(`## Recent Observations (${observations.length})\n\n${lines.join("\n")}`);
|
|
524
|
+
}
|
|
525
|
+
case "ci_export": {
|
|
526
|
+
if (MODE !== "expert") {
|
|
527
|
+
return error("ci_export requires expert mode");
|
|
528
|
+
}
|
|
529
|
+
const scope = getString(params.scope, "all");
|
|
530
|
+
let instincts = readInstincts(project.hash);
|
|
531
|
+
if (scope === "project") {
|
|
532
|
+
instincts = instincts.filter((instinct) => instinct.scope === "project");
|
|
533
|
+
}
|
|
534
|
+
if (scope === "global") {
|
|
535
|
+
instincts = instincts.filter((instinct) => instinct.scope === "global");
|
|
536
|
+
}
|
|
537
|
+
return text(JSON.stringify(instincts, null, 2));
|
|
538
|
+
}
|
|
539
|
+
case "ci_import": {
|
|
540
|
+
if (MODE !== "expert") {
|
|
541
|
+
return error("ci_import requires expert mode");
|
|
542
|
+
}
|
|
543
|
+
const rawJson = getString(params.instincts_json);
|
|
544
|
+
let parsed;
|
|
545
|
+
try {
|
|
546
|
+
parsed = JSON.parse(rawJson);
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
return error("Invalid JSON. Provide a JSON array of instinct objects.");
|
|
550
|
+
}
|
|
551
|
+
if (!Array.isArray(parsed)) {
|
|
552
|
+
return error("Expected a JSON array");
|
|
553
|
+
}
|
|
554
|
+
const scope = getString(params.scope, "project") === "global" ? "global" : "project";
|
|
555
|
+
const existing = readInstincts(project.hash);
|
|
556
|
+
const existingIds = new Set(existing.map((instinct) => instinct.id));
|
|
557
|
+
let imported = 0;
|
|
558
|
+
for (const candidate of parsed) {
|
|
559
|
+
const instinct = toInstinct(candidate);
|
|
560
|
+
if (!instinct || existingIds.has(instinct.id)) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
instinct.scope = instinct.scope || scope;
|
|
564
|
+
instinct.source = "imported";
|
|
565
|
+
writeInstinct(project.hash, instinct);
|
|
566
|
+
imported++;
|
|
567
|
+
}
|
|
568
|
+
return text(`Imported ${imported} instincts (${parsed.length - imported} skipped as duplicates)`);
|
|
569
|
+
}
|
|
570
|
+
case "ci_plan_init": {
|
|
571
|
+
if (MODE !== "expert") {
|
|
572
|
+
return error("ci_plan_init requires expert mode");
|
|
573
|
+
}
|
|
574
|
+
const goal = getString(params.goal).trim();
|
|
575
|
+
if (!goal) {
|
|
576
|
+
return error("goal is required");
|
|
577
|
+
}
|
|
578
|
+
const phases = normalizePhases(params.phases);
|
|
579
|
+
const force = getBoolean(params.force, false);
|
|
580
|
+
const workspaceRoot = getWorkspaceRoot();
|
|
581
|
+
const taskPlanStatus = writePlanningFile(join(workspaceRoot, PLANNING_FILES.taskPlan), renderTaskPlan(goal, phases), force);
|
|
582
|
+
const findingsStatus = writePlanningFile(join(workspaceRoot, PLANNING_FILES.findings), renderFindings(), force);
|
|
583
|
+
const progressStatus = writePlanningFile(join(workspaceRoot, PLANNING_FILES.progress), renderProgress(), force);
|
|
584
|
+
return text([
|
|
585
|
+
"## Planning-With-Files Initialized",
|
|
586
|
+
"",
|
|
587
|
+
`**Project root:** ${workspaceRoot}`,
|
|
588
|
+
`**Goal:** ${goal}`,
|
|
589
|
+
`**Force overwrite:** ${force ? "yes" : "no"}`,
|
|
590
|
+
"",
|
|
591
|
+
"**Files:**",
|
|
592
|
+
`- ${PLANNING_FILES.taskPlan}: ${taskPlanStatus}`,
|
|
593
|
+
`- ${PLANNING_FILES.findings}: ${findingsStatus}`,
|
|
594
|
+
`- ${PLANNING_FILES.progress}: ${progressStatus}`,
|
|
595
|
+
"",
|
|
596
|
+
"**Phases:**",
|
|
597
|
+
...phases.map((phase) => `- [ ] ${phase}`),
|
|
598
|
+
].join("\n"));
|
|
599
|
+
}
|
|
600
|
+
case "ci_plan_status": {
|
|
601
|
+
if (MODE !== "expert") {
|
|
602
|
+
return error("ci_plan_status requires expert mode");
|
|
603
|
+
}
|
|
604
|
+
const workspaceRoot = getWorkspaceRoot();
|
|
605
|
+
const includeContents = getBoolean(params.include_contents, false);
|
|
606
|
+
const taskPlanPath = join(workspaceRoot, PLANNING_FILES.taskPlan);
|
|
607
|
+
const findingsPath = join(workspaceRoot, PLANNING_FILES.findings);
|
|
608
|
+
const progressPath = join(workspaceRoot, PLANNING_FILES.progress);
|
|
609
|
+
const taskPlanExists = existsSync(taskPlanPath);
|
|
610
|
+
const findingsExists = existsSync(findingsPath);
|
|
611
|
+
const progressExists = existsSync(progressPath);
|
|
612
|
+
if (!taskPlanExists && !findingsExists && !progressExists) {
|
|
613
|
+
return text(`No planning files found in ${workspaceRoot}. Run ci_plan_init or /planning-with-files to create ${PLANNING_FILES.taskPlan}, ${PLANNING_FILES.findings}, and ${PLANNING_FILES.progress}.`);
|
|
614
|
+
}
|
|
615
|
+
const taskPlanContent = taskPlanExists ? readFileSync(taskPlanPath, "utf8") : "";
|
|
616
|
+
const findingsContent = findingsExists ? readFileSync(findingsPath, "utf8") : "";
|
|
617
|
+
const progressContent = progressExists ? readFileSync(progressPath, "utf8") : "";
|
|
618
|
+
const summary = taskPlanExists
|
|
619
|
+
? parseTaskPlan(taskPlanContent)
|
|
620
|
+
: { status: "No task plan yet", checkedPhases: [], uncheckedPhases: [] };
|
|
621
|
+
const findingsState = findingsExists
|
|
622
|
+
? hasMeaningfulContent(findingsContent, [
|
|
623
|
+
"Created on",
|
|
624
|
+
"Record source links, code references, and synthesized findings here.",
|
|
625
|
+
"Add links and short summaries here.",
|
|
626
|
+
])
|
|
627
|
+
? "has notes"
|
|
628
|
+
: "template only"
|
|
629
|
+
: "missing";
|
|
630
|
+
const progressState = progressExists
|
|
631
|
+
? hasMeaningfulContent(progressContent, [
|
|
632
|
+
"Initialized planning files.",
|
|
633
|
+
"Record commands, tests, outputs, and checkpoints here.",
|
|
634
|
+
])
|
|
635
|
+
? "has entries"
|
|
636
|
+
: "template only"
|
|
637
|
+
: "missing";
|
|
638
|
+
const lines = [
|
|
639
|
+
"## Planning-With-Files Status",
|
|
640
|
+
"",
|
|
641
|
+
`**Project root:** ${workspaceRoot}`,
|
|
642
|
+
"",
|
|
643
|
+
"**Files:**",
|
|
644
|
+
`- ${PLANNING_FILES.taskPlan}: ${taskPlanExists ? "present" : "missing"}`,
|
|
645
|
+
`- ${PLANNING_FILES.findings}: ${findingsExists ? "present" : "missing"} (${findingsState})`,
|
|
646
|
+
`- ${PLANNING_FILES.progress}: ${progressExists ? "present" : "missing"} (${progressState})`,
|
|
647
|
+
"",
|
|
648
|
+
`**Status:** ${summary.status}`,
|
|
649
|
+
`**Completed phases:** ${summary.checkedPhases.length}/${summary.checkedPhases.length + summary.uncheckedPhases.length}`,
|
|
650
|
+
`**Checked phases:** ${summary.checkedPhases.length > 0 ? summary.checkedPhases.join(", ") : "(none)"}`,
|
|
651
|
+
`**Remaining phases:** ${summary.uncheckedPhases.length > 0 ? summary.uncheckedPhases.join(", ") : "(none)"}`,
|
|
652
|
+
];
|
|
653
|
+
if (includeContents) {
|
|
654
|
+
const filesToInclude = [
|
|
655
|
+
[PLANNING_FILES.taskPlan, taskPlanContent],
|
|
656
|
+
[PLANNING_FILES.findings, findingsContent],
|
|
657
|
+
[PLANNING_FILES.progress, progressContent],
|
|
658
|
+
];
|
|
659
|
+
for (const [fileName, content] of filesToInclude) {
|
|
660
|
+
if (!content) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
lines.push("", `## ${fileName}`, "```md", content.trimEnd(), "```");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return text(lines.join("\n"));
|
|
667
|
+
}
|
|
668
|
+
case "ci_dashboard": {
|
|
669
|
+
if (MODE !== "expert") {
|
|
670
|
+
return error("ci_dashboard requires expert mode");
|
|
671
|
+
}
|
|
672
|
+
const level = detectLevel(project.hash);
|
|
673
|
+
const observationCount = countObservations(project.hash);
|
|
674
|
+
const instincts = readInstincts(project.hash);
|
|
675
|
+
const byConfidence = {
|
|
676
|
+
auto: instincts.filter((instinct) => instinct.confidence >= 0.7),
|
|
677
|
+
suggest: instincts.filter((instinct) => instinct.confidence >= 0.5 && instinct.confidence < 0.7),
|
|
678
|
+
silent: instincts.filter((instinct) => instinct.confidence < 0.5),
|
|
679
|
+
};
|
|
680
|
+
const globalCount = instincts.filter((instinct) => instinct.scope === "global").length;
|
|
681
|
+
const projectCount = instincts.length - globalCount;
|
|
682
|
+
const today = Date.now();
|
|
683
|
+
const stale = instincts.filter((instinct) => {
|
|
684
|
+
if (!instinct.last_seen) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
const diff = (today - new Date(instinct.last_seen).getTime()) / (1000 * 60 * 60 * 24);
|
|
688
|
+
return diff > 30;
|
|
689
|
+
});
|
|
690
|
+
const autoBar = "█".repeat(Math.min(10, byConfidence.auto.length)) + "░".repeat(Math.max(0, 10 - byConfidence.auto.length));
|
|
691
|
+
const suggestBar = "█".repeat(Math.min(10, byConfidence.suggest.length)) + "░".repeat(Math.max(0, 10 - byConfidence.suggest.length));
|
|
692
|
+
const silentBar = "█".repeat(Math.min(10, byConfidence.silent.length)) + "░".repeat(Math.max(0, 10 - byConfidence.silent.length));
|
|
693
|
+
const top5 = instincts
|
|
694
|
+
.sort((left, right) => right.confidence - left.confidence)
|
|
695
|
+
.slice(0, 5)
|
|
696
|
+
.map((instinct) => ` ${"█".repeat(Math.round(instinct.confidence * 10)) + "░".repeat(10 - Math.round(instinct.confidence * 10))} ${instinct.confidence.toFixed(2)} ${instinct.id}`)
|
|
697
|
+
.join("\n");
|
|
698
|
+
let packInfo = "";
|
|
699
|
+
if (existsSync(PACKS_DIR)) {
|
|
700
|
+
const packs = readdirSync(PACKS_DIR)
|
|
701
|
+
.filter((file) => file.endsWith(".json"))
|
|
702
|
+
.map((file) => file.replace(".json", ""));
|
|
703
|
+
packInfo = `\n Available packs: ${packs.join(", ")}`;
|
|
704
|
+
}
|
|
705
|
+
return text([
|
|
706
|
+
"╔══════════════════════════════════════════════════════════════╗",
|
|
707
|
+
"║ continuous-improvement Dashboard ║",
|
|
708
|
+
"╠══════════════════════════════════════════════════════════════╣",
|
|
709
|
+
"║ ║",
|
|
710
|
+
`║ Project: ${project.name.padEnd(20)} Level: ${level.padEnd(12)} ║`,
|
|
711
|
+
`║ Sessions: ~${String(Math.floor(observationCount / 10)).padEnd(17)} Mode: ${MODE.padEnd(13)} ║`,
|
|
712
|
+
"║ ║",
|
|
713
|
+
"║ ┌─ Observations ────────────────────────────────────────┐ ║",
|
|
714
|
+
`║ │ Total: ${String(observationCount).padEnd(48)} │ ║`,
|
|
715
|
+
"║ └───────────────────────────────────────────────────────┘ ║",
|
|
716
|
+
"║ ║",
|
|
717
|
+
"║ ┌─ Instincts ───────────────────────────────────────────┐ ║",
|
|
718
|
+
`║ │ Total: ${String(instincts.length).padEnd(48)} │ ║`,
|
|
719
|
+
`║ │ ${autoBar} Auto-apply (0.7+): ${String(byConfidence.auto.length).padEnd(20)} │ ║`,
|
|
720
|
+
`║ │ ${suggestBar} Suggest (0.5-0.69): ${String(byConfidence.suggest.length).padEnd(19)} │ ║`,
|
|
721
|
+
`║ │ ${silentBar} Silent (< 0.5): ${String(byConfidence.silent.length).padEnd(23)} │ ║`,
|
|
722
|
+
`║ │ Global: ${String(globalCount).padEnd(10)} Project: ${String(projectCount).padEnd(28)} │ ║`,
|
|
723
|
+
"║ └───────────────────────────────────────────────────────┘ ║",
|
|
724
|
+
"║ ║",
|
|
725
|
+
instincts.length > 0
|
|
726
|
+
? [
|
|
727
|
+
"║ ┌─ Top Instincts ───────────────────────────────────────┐ ║",
|
|
728
|
+
...top5.split("\n").map((line) => `║ │${line.padEnd(56)}│ ║`),
|
|
729
|
+
"║ └───────────────────────────────────────────────────────┘ ║",
|
|
730
|
+
].join("\n")
|
|
731
|
+
: "",
|
|
732
|
+
"║ ║",
|
|
733
|
+
"║ ┌─ Health ──────────────────────────────────────────────┐ ║",
|
|
734
|
+
`║ │ Stale (30+ days): ${String(stale.length).padEnd(38)} │ ║`,
|
|
735
|
+
"║ └───────────────────────────────────────────────────────┘ ║",
|
|
736
|
+
packInfo ? `║${packInfo.padEnd(63)}║` : "",
|
|
737
|
+
"║ ║",
|
|
738
|
+
"╚══════════════════════════════════════════════════════════════╝",
|
|
739
|
+
].filter(Boolean).join("\n"));
|
|
740
|
+
}
|
|
741
|
+
case "ci_load_pack": {
|
|
742
|
+
if (MODE !== "expert") {
|
|
743
|
+
return error("ci_load_pack requires expert mode");
|
|
744
|
+
}
|
|
745
|
+
const packName = getString(params.pack);
|
|
746
|
+
const packPath = join(PACKS_DIR, `${packName}.json`);
|
|
747
|
+
if (!existsSync(packPath)) {
|
|
748
|
+
const available = existsSync(PACKS_DIR)
|
|
749
|
+
? readdirSync(PACKS_DIR)
|
|
750
|
+
.filter((file) => file.endsWith(".json"))
|
|
751
|
+
.map((file) => file.replace(".json", ""))
|
|
752
|
+
: [];
|
|
753
|
+
return error(`Unknown pack: ${packName}. Available: ${available.join(", ")}`);
|
|
754
|
+
}
|
|
755
|
+
const packInstincts = JSON.parse(readFileSync(packPath, "utf8"));
|
|
756
|
+
const existing = readInstincts(project.hash);
|
|
757
|
+
const existingIds = new Set(existing.map((instinct) => instinct.id));
|
|
758
|
+
let loaded = 0;
|
|
759
|
+
for (const candidate of packInstincts) {
|
|
760
|
+
const instinct = toInstinct(candidate);
|
|
761
|
+
if (!instinct || existingIds.has(instinct.id)) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
instinct.source = `pack-${packName}`;
|
|
765
|
+
instinct.scope = "project";
|
|
766
|
+
instinct.observation_count = 0;
|
|
767
|
+
writeInstinct(project.hash, instinct);
|
|
768
|
+
loaded++;
|
|
769
|
+
}
|
|
770
|
+
return text(`Loaded ${loaded}/${packInstincts.length} instincts from **${packName}** pack (${packInstincts.length - loaded} already existed)`);
|
|
771
|
+
}
|
|
772
|
+
default:
|
|
773
|
+
return error(`Unknown tool: ${name}`);
|
|
407
774
|
}
|
|
408
|
-
|
|
409
|
-
default:
|
|
410
|
-
return error(`Unknown tool: ${name}`);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function text(t) {
|
|
415
|
-
return { content: [{ type: "text", text: t }] };
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function error(t) {
|
|
419
|
-
return { content: [{ type: "text", text: t }], isError: true };
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// ---------------------------------------------------------------------------
|
|
423
|
-
// JSON-RPC stdio transport (zero dependencies)
|
|
424
|
-
// ---------------------------------------------------------------------------
|
|
425
|
-
|
|
775
|
+
}
|
|
426
776
|
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
427
777
|
let buffer = "";
|
|
428
|
-
|
|
429
778
|
rl.on("line", (line) => {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
} catch {
|
|
436
|
-
// incomplete JSON, keep buffering
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
function send(response) {
|
|
441
|
-
const json = JSON.stringify(response);
|
|
442
|
-
process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
function handleMessage(msg) {
|
|
446
|
-
const { id, method, params } = msg;
|
|
447
|
-
|
|
448
|
-
switch (method) {
|
|
449
|
-
case "initialize":
|
|
450
|
-
send({
|
|
451
|
-
jsonrpc: "2.0",
|
|
452
|
-
id,
|
|
453
|
-
result: {
|
|
454
|
-
protocolVersion: "2024-11-05",
|
|
455
|
-
capabilities: {
|
|
456
|
-
tools: { listChanged: false },
|
|
457
|
-
resources: { subscribe: false, listChanged: false },
|
|
458
|
-
},
|
|
459
|
-
serverInfo: {
|
|
460
|
-
name: "continuous-improvement",
|
|
461
|
-
version: VERSION,
|
|
462
|
-
},
|
|
463
|
-
},
|
|
464
|
-
});
|
|
465
|
-
break;
|
|
466
|
-
|
|
467
|
-
case "notifications/initialized":
|
|
468
|
-
// no response needed
|
|
469
|
-
break;
|
|
470
|
-
|
|
471
|
-
case "tools/list":
|
|
472
|
-
send({
|
|
473
|
-
jsonrpc: "2.0",
|
|
474
|
-
id,
|
|
475
|
-
result: { tools: getAllTools() },
|
|
476
|
-
});
|
|
477
|
-
break;
|
|
478
|
-
|
|
479
|
-
case "tools/call": {
|
|
480
|
-
const result = handleTool(params.name, params.arguments || {});
|
|
481
|
-
send({ jsonrpc: "2.0", id, result });
|
|
482
|
-
break;
|
|
779
|
+
buffer += line;
|
|
780
|
+
try {
|
|
781
|
+
const message = JSON.parse(buffer);
|
|
782
|
+
buffer = "";
|
|
783
|
+
handleMessage(message);
|
|
483
784
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const project = getProjectHash();
|
|
487
|
-
const resources = [
|
|
488
|
-
{
|
|
489
|
-
uri: `instincts://project/${project.hash}`,
|
|
490
|
-
name: `${project.name} instincts`,
|
|
491
|
-
description: `Learned instincts for ${project.name}`,
|
|
492
|
-
mimeType: "application/json",
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
uri: "instincts://global",
|
|
496
|
-
name: "Global instincts",
|
|
497
|
-
description: "Cross-project instincts",
|
|
498
|
-
mimeType: "application/json",
|
|
499
|
-
},
|
|
500
|
-
];
|
|
501
|
-
send({ jsonrpc: "2.0", id, result: { resources } });
|
|
502
|
-
break;
|
|
785
|
+
catch {
|
|
786
|
+
// incomplete JSON, keep buffering
|
|
503
787
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
788
|
+
});
|
|
789
|
+
function send(response) {
|
|
790
|
+
const json = JSON.stringify(response);
|
|
791
|
+
process.stdout.write(`Content-Length: ${Buffer.byteLength(json)}\r\n\r\n${json}`);
|
|
792
|
+
}
|
|
793
|
+
function handleMessage(message) {
|
|
794
|
+
const id = message.id;
|
|
795
|
+
const method = message.method;
|
|
796
|
+
const params = asRecord(message.params) ?? {};
|
|
797
|
+
switch (method) {
|
|
798
|
+
case "initialize":
|
|
799
|
+
send({
|
|
800
|
+
jsonrpc: "2.0",
|
|
801
|
+
id,
|
|
802
|
+
result: {
|
|
803
|
+
protocolVersion: "2024-11-05",
|
|
804
|
+
capabilities: {
|
|
805
|
+
tools: { listChanged: false },
|
|
806
|
+
resources: { subscribe: false, listChanged: false },
|
|
807
|
+
},
|
|
808
|
+
serverInfo: {
|
|
809
|
+
name: PACKAGE_NAME,
|
|
810
|
+
version: VERSION,
|
|
811
|
+
},
|
|
812
|
+
},
|
|
813
|
+
});
|
|
814
|
+
break;
|
|
815
|
+
case "notifications/initialized":
|
|
816
|
+
break;
|
|
817
|
+
case "tools/list":
|
|
818
|
+
send({
|
|
819
|
+
jsonrpc: "2.0",
|
|
820
|
+
id,
|
|
821
|
+
result: { tools: getAllTools() },
|
|
822
|
+
});
|
|
823
|
+
break;
|
|
824
|
+
case "tools/call": {
|
|
825
|
+
const toolName = getString(params.name);
|
|
826
|
+
const toolArgs = asRecord(params.arguments) ?? {};
|
|
827
|
+
const result = handleTool(toolName, toolArgs);
|
|
828
|
+
send({ jsonrpc: "2.0", id, result });
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
831
|
+
case "resources/list": {
|
|
832
|
+
const project = getProjectHash();
|
|
833
|
+
send({
|
|
834
|
+
jsonrpc: "2.0",
|
|
835
|
+
id,
|
|
836
|
+
result: {
|
|
837
|
+
resources: [
|
|
838
|
+
{
|
|
839
|
+
uri: `instincts://project/${project.hash}`,
|
|
840
|
+
name: `${project.name} instincts`,
|
|
841
|
+
description: `Learned instincts for ${project.name}`,
|
|
842
|
+
mimeType: "application/json",
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
uri: "instincts://global",
|
|
846
|
+
name: "Global instincts",
|
|
847
|
+
description: "Cross-project instincts",
|
|
848
|
+
mimeType: "application/json",
|
|
849
|
+
},
|
|
850
|
+
],
|
|
851
|
+
},
|
|
852
|
+
});
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
case "resources/read": {
|
|
856
|
+
const uri = getString(params.uri);
|
|
857
|
+
const project = getProjectHash();
|
|
858
|
+
const instincts = uri === "instincts://global"
|
|
859
|
+
? readInstincts("global").filter((instinct) => instinct.scope === "global")
|
|
860
|
+
: readInstincts(project.hash);
|
|
861
|
+
send({
|
|
862
|
+
jsonrpc: "2.0",
|
|
863
|
+
id,
|
|
864
|
+
result: {
|
|
865
|
+
contents: [
|
|
866
|
+
{
|
|
867
|
+
uri,
|
|
868
|
+
text: JSON.stringify(instincts, null, 2),
|
|
869
|
+
mimeType: "application/json",
|
|
870
|
+
},
|
|
871
|
+
],
|
|
872
|
+
},
|
|
873
|
+
});
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
default:
|
|
877
|
+
if (id !== undefined) {
|
|
878
|
+
send({
|
|
879
|
+
jsonrpc: "2.0",
|
|
880
|
+
id,
|
|
881
|
+
error: { code: -32601, message: `Method not found: ${method}` },
|
|
882
|
+
});
|
|
883
|
+
}
|
|
530
884
|
}
|
|
531
|
-
|
|
532
|
-
default:
|
|
533
|
-
if (id) {
|
|
534
|
-
send({
|
|
535
|
-
jsonrpc: "2.0",
|
|
536
|
-
id,
|
|
537
|
-
error: { code: -32601, message: `Method not found: ${method}` },
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
885
|
}
|
|
542
|
-
|
|
543
|
-
console.error(`continuous-improvement MCP server v${VERSION} started (mode: ${MODE})`);
|
|
886
|
+
console.error(`${PACKAGE_NAME} MCP server v${VERSION} started (mode: ${MODE})`);
|