continuous-improvement 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +202 -24
- package/action.yml +33 -0
- package/bin/install.mjs +77 -16
- package/bin/lint-transcript.mjs +267 -0
- package/bin/mcp-server.mjs +122 -2
- package/commands/dashboard.md +56 -0
- package/commands/discipline.md +37 -0
- package/hooks/session.sh +0 -0
- package/instinct-packs/go.json +58 -0
- package/instinct-packs/python.json +58 -0
- package/instinct-packs/react.json +58 -0
- package/llms.txt +43 -0
- package/package.json +21 -7
- package/plugins/beginner.json +1 -1
- package/plugins/expert.json +10 -2
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Transcript Linter
|
|
5
|
+
*
|
|
6
|
+
* Analyzes AI agent transcripts/observations for compliance with
|
|
7
|
+
* the 7 Laws of AI Agent Discipline.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node bin/lint-transcript.mjs <file.jsonl> # Lint a transcript file
|
|
11
|
+
* cat observations.jsonl | node bin/lint-transcript.mjs --stdin # Pipe input
|
|
12
|
+
* node bin/lint-transcript.mjs --help
|
|
13
|
+
*
|
|
14
|
+
* Exit codes:
|
|
15
|
+
* 0 — no violations (or --strict not set)
|
|
16
|
+
* 1 — violations found (with --strict)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import { createInterface } from "node:readline";
|
|
21
|
+
|
|
22
|
+
const LAWS = {
|
|
23
|
+
1: { name: "Research Before Executing", patterns: [] },
|
|
24
|
+
2: { name: "Plan Is Sacred", patterns: [] },
|
|
25
|
+
3: { name: "One Thing at a Time", patterns: [] },
|
|
26
|
+
4: { name: "Verify Before Reporting", patterns: [] },
|
|
27
|
+
5: { name: "Reflect After Sessions", patterns: [] },
|
|
28
|
+
6: { name: "Iterate One Change", patterns: [] },
|
|
29
|
+
7: { name: "Learn From Every Session", patterns: [] },
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function analyzeTranscript(events) {
|
|
33
|
+
const violations = [];
|
|
34
|
+
const stats = {
|
|
35
|
+
totalEvents: events.length,
|
|
36
|
+
toolCalls: 0,
|
|
37
|
+
researchTools: 0, // Grep, Glob, Read
|
|
38
|
+
writeTools: 0, // Write, Edit, Bash
|
|
39
|
+
verifyTools: 0, // Bash (test/build commands)
|
|
40
|
+
sessions: new Set(),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Classify events
|
|
44
|
+
for (const event of events) {
|
|
45
|
+
stats.toolCalls++;
|
|
46
|
+
if (event.session_id) stats.sessions.add(event.session_id);
|
|
47
|
+
|
|
48
|
+
const tool = event.tool || event.tool_name || "";
|
|
49
|
+
const input = event.tool_input || event.input || {};
|
|
50
|
+
const command = input.command || "";
|
|
51
|
+
|
|
52
|
+
if (["Grep", "Glob", "Read"].includes(tool)) {
|
|
53
|
+
stats.researchTools++;
|
|
54
|
+
}
|
|
55
|
+
if (["Write", "Edit"].includes(tool)) {
|
|
56
|
+
stats.writeTools++;
|
|
57
|
+
}
|
|
58
|
+
if (tool === "Bash" && /\b(test|build|npm run|jest|pytest|go test|cargo test)\b/.test(command)) {
|
|
59
|
+
stats.verifyTools++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Law 1: Research Before Executing
|
|
64
|
+
// Check if writes happen without prior reads/greps
|
|
65
|
+
let lastResearchIdx = -1;
|
|
66
|
+
for (let i = 0; i < events.length; i++) {
|
|
67
|
+
const tool = events[i].tool || events[i].tool_name || "";
|
|
68
|
+
if (["Grep", "Glob", "Read"].includes(tool)) {
|
|
69
|
+
lastResearchIdx = i;
|
|
70
|
+
}
|
|
71
|
+
if (["Write", "Edit"].includes(tool) && lastResearchIdx === -1) {
|
|
72
|
+
violations.push({
|
|
73
|
+
law: 1,
|
|
74
|
+
severity: "high",
|
|
75
|
+
message: `Write/Edit at event ${i} without any prior research (Grep/Glob/Read)`,
|
|
76
|
+
event: i,
|
|
77
|
+
});
|
|
78
|
+
break; // Only report first occurrence
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Law 1 ratio check
|
|
83
|
+
if (stats.writeTools > 0 && stats.researchTools === 0) {
|
|
84
|
+
violations.push({
|
|
85
|
+
law: 1,
|
|
86
|
+
severity: "high",
|
|
87
|
+
message: `${stats.writeTools} writes with 0 research operations. Agent likely skipped research.`,
|
|
88
|
+
});
|
|
89
|
+
} else if (stats.writeTools > stats.researchTools * 3) {
|
|
90
|
+
violations.push({
|
|
91
|
+
law: 1,
|
|
92
|
+
severity: "medium",
|
|
93
|
+
message: `Write-to-research ratio is ${stats.writeTools}:${stats.researchTools}. Consider more research.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Law 3: One Thing at a Time
|
|
98
|
+
// Detect rapid context switching (different files edited in quick succession without verification)
|
|
99
|
+
let consecutiveEdits = 0;
|
|
100
|
+
const editedFiles = new Set();
|
|
101
|
+
for (const event of events) {
|
|
102
|
+
const tool = event.tool || event.tool_name || "";
|
|
103
|
+
const input = event.tool_input || event.input || {};
|
|
104
|
+
|
|
105
|
+
if (["Write", "Edit"].includes(tool)) {
|
|
106
|
+
consecutiveEdits++;
|
|
107
|
+
if (input.file_path) editedFiles.add(input.file_path);
|
|
108
|
+
} else if (tool === "Bash") {
|
|
109
|
+
consecutiveEdits = 0; // Reset on verification
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (consecutiveEdits > 5) {
|
|
113
|
+
violations.push({
|
|
114
|
+
law: 3,
|
|
115
|
+
severity: "medium",
|
|
116
|
+
message: `${consecutiveEdits} consecutive edits without verification. Editing multiple files at once.`,
|
|
117
|
+
});
|
|
118
|
+
consecutiveEdits = 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Law 4: Verify Before Reporting
|
|
123
|
+
if (stats.writeTools > 0 && stats.verifyTools === 0) {
|
|
124
|
+
violations.push({
|
|
125
|
+
law: 4,
|
|
126
|
+
severity: "high",
|
|
127
|
+
message: `${stats.writeTools} code changes with 0 verification commands (test/build). Agent may have declared done without verifying.`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Law 6: Iterate One Change
|
|
132
|
+
if (editedFiles.size > 8) {
|
|
133
|
+
violations.push({
|
|
134
|
+
law: 6,
|
|
135
|
+
severity: "medium",
|
|
136
|
+
message: `${editedFiles.size} different files modified. Consider smaller, focused iterations.`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Calculate discipline score
|
|
141
|
+
const maxViolations = 7; // One per law
|
|
142
|
+
const violationWeight = violations.reduce((sum, v) => {
|
|
143
|
+
return sum + (v.severity === "high" ? 2 : 1);
|
|
144
|
+
}, 0);
|
|
145
|
+
const score = Math.max(0, Math.round(100 - (violationWeight / maxViolations) * 100));
|
|
146
|
+
|
|
147
|
+
return { violations, stats, score };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatReport(result) {
|
|
151
|
+
const { violations, stats, score } = result;
|
|
152
|
+
const lines = [];
|
|
153
|
+
|
|
154
|
+
lines.push("## Agent Discipline Report");
|
|
155
|
+
lines.push("");
|
|
156
|
+
lines.push(`**Score:** ${score}/100`);
|
|
157
|
+
lines.push(`**Events analyzed:** ${stats.totalEvents}`);
|
|
158
|
+
lines.push(`**Tool calls:** ${stats.toolCalls}`);
|
|
159
|
+
lines.push(`**Research ops:** ${stats.researchTools} | **Write ops:** ${stats.writeTools} | **Verify ops:** ${stats.verifyTools}`);
|
|
160
|
+
lines.push("");
|
|
161
|
+
|
|
162
|
+
if (violations.length === 0) {
|
|
163
|
+
lines.push("No law violations detected. Good discipline!");
|
|
164
|
+
} else {
|
|
165
|
+
lines.push(`### Violations (${violations.length})`);
|
|
166
|
+
lines.push("");
|
|
167
|
+
for (const v of violations) {
|
|
168
|
+
const severity = v.severity === "high" ? "HIGH" : "MEDIUM";
|
|
169
|
+
lines.push(`- **[${severity}] Law ${v.law}: ${LAWS[v.law].name}**`);
|
|
170
|
+
lines.push(` ${v.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
lines.push("");
|
|
175
|
+
lines.push("---");
|
|
176
|
+
lines.push("*Generated by [continuous-improvement](https://github.com/naimkatiman/continuous-improvement) transcript linter*");
|
|
177
|
+
|
|
178
|
+
return lines.join("\n");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- Main ---
|
|
182
|
+
|
|
183
|
+
const args = process.argv.slice(2);
|
|
184
|
+
|
|
185
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
186
|
+
console.log(`
|
|
187
|
+
Agent Transcript Linter — The 7 Laws of AI Agent Discipline
|
|
188
|
+
|
|
189
|
+
Usage:
|
|
190
|
+
node bin/lint-transcript.mjs <file.jsonl> Lint a transcript file
|
|
191
|
+
cat obs.jsonl | node bin/lint-transcript.mjs --stdin Pipe input
|
|
192
|
+
node bin/lint-transcript.mjs --help Show help
|
|
193
|
+
|
|
194
|
+
Options:
|
|
195
|
+
--stdin Read from stdin
|
|
196
|
+
--strict Exit 1 if violations found
|
|
197
|
+
--json Output as JSON instead of markdown
|
|
198
|
+
`);
|
|
199
|
+
process.exit(0);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const isStrict = args.includes("--strict");
|
|
203
|
+
const isJson = args.includes("--json");
|
|
204
|
+
const isStdin = args.includes("--stdin");
|
|
205
|
+
|
|
206
|
+
async function main() {
|
|
207
|
+
let lines;
|
|
208
|
+
|
|
209
|
+
if (isStdin) {
|
|
210
|
+
const rl = createInterface({ input: process.stdin, terminal: false });
|
|
211
|
+
lines = [];
|
|
212
|
+
for await (const line of rl) {
|
|
213
|
+
if (line.trim()) lines.push(line.trim());
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
const filePath = args.find((a) => !a.startsWith("--"));
|
|
217
|
+
if (!filePath) {
|
|
218
|
+
console.error("Error: provide a file path or use --stdin");
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
const content = readFileSync(filePath, "utf8");
|
|
222
|
+
lines = content.split("\n").filter((l) => l.trim());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const events = [];
|
|
226
|
+
for (const line of lines) {
|
|
227
|
+
try {
|
|
228
|
+
events.push(JSON.parse(line));
|
|
229
|
+
} catch {
|
|
230
|
+
// skip non-JSON lines
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (events.length === 0) {
|
|
235
|
+
console.log("No events found to analyze.");
|
|
236
|
+
process.exit(0);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const result = analyzeTranscript(events);
|
|
240
|
+
|
|
241
|
+
if (isJson) {
|
|
242
|
+
console.log(JSON.stringify(result, null, 2));
|
|
243
|
+
} else {
|
|
244
|
+
console.log(formatReport(result));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// GitHub Actions output
|
|
248
|
+
if (process.env.GITHUB_OUTPUT) {
|
|
249
|
+
const outputLines = [
|
|
250
|
+
`violations=${result.violations.length}`,
|
|
251
|
+
`score=${result.score}`,
|
|
252
|
+
];
|
|
253
|
+
const { appendFileSync } = await import("node:fs");
|
|
254
|
+
for (const line of outputLines) {
|
|
255
|
+
appendFileSync(process.env.GITHUB_OUTPUT, line + "\n");
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (isStrict && result.violations.length > 0) {
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
main().catch((err) => {
|
|
265
|
+
console.error(err.message);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
package/bin/mcp-server.mjs
CHANGED
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from "node:fs";
|
|
16
|
-
import { join, basename } from "node:path";
|
|
16
|
+
import { join, basename, dirname } from "node:path";
|
|
17
17
|
import { homedir } from "node:os";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
18
19
|
import { execSync } from "node:child_process";
|
|
19
20
|
import { createInterface } from "node:readline";
|
|
20
21
|
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
23
|
// Config
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
|
-
const VERSION = "3.
|
|
25
|
+
const VERSION = "3.1.0";
|
|
25
26
|
const INSTINCTS_DIR = join(homedir(), ".claude", "instincts");
|
|
26
27
|
const GLOBAL_DIR = join(INSTINCTS_DIR, "global");
|
|
27
28
|
|
|
@@ -200,6 +201,8 @@ const BEGINNER_TOOLS = [
|
|
|
200
201
|
},
|
|
201
202
|
];
|
|
202
203
|
|
|
204
|
+
const PACKS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "instinct-packs");
|
|
205
|
+
|
|
203
206
|
const EXPERT_TOOLS = [
|
|
204
207
|
{
|
|
205
208
|
name: "ci_reinforce",
|
|
@@ -263,6 +266,22 @@ const EXPERT_TOOLS = [
|
|
|
263
266
|
required: ["instincts_json"],
|
|
264
267
|
},
|
|
265
268
|
},
|
|
269
|
+
{
|
|
270
|
+
name: "ci_dashboard",
|
|
271
|
+
description: "Visual dashboard showing instinct health, observation stats, confidence distribution, and learning progress.",
|
|
272
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "ci_load_pack",
|
|
276
|
+
description: "Load a starter instinct pack (react, python, go) into the current project.",
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {
|
|
280
|
+
pack: { type: "string", description: "Pack name: react, python, or go" },
|
|
281
|
+
},
|
|
282
|
+
required: ["pack"],
|
|
283
|
+
},
|
|
284
|
+
},
|
|
266
285
|
];
|
|
267
286
|
|
|
268
287
|
function getAllTools() {
|
|
@@ -406,6 +425,107 @@ function handleTool(name, params) {
|
|
|
406
425
|
return text(`Imported ${imported} instincts (${toImport.length - imported} skipped as duplicates)`);
|
|
407
426
|
}
|
|
408
427
|
|
|
428
|
+
case "ci_dashboard": {
|
|
429
|
+
if (MODE !== "expert") return error("ci_dashboard requires expert mode");
|
|
430
|
+
const level = detectLevel(project.hash);
|
|
431
|
+
const obsCount = countObservations(project.hash);
|
|
432
|
+
const instincts = readInstincts(project.hash);
|
|
433
|
+
const byConf = {
|
|
434
|
+
auto: instincts.filter((i) => i.confidence >= 0.7),
|
|
435
|
+
suggest: instincts.filter((i) => i.confidence >= 0.5 && i.confidence < 0.7),
|
|
436
|
+
silent: instincts.filter((i) => i.confidence < 0.5),
|
|
437
|
+
};
|
|
438
|
+
const globalCount = instincts.filter((i) => i.scope === "global").length;
|
|
439
|
+
const projectCount = instincts.length - globalCount;
|
|
440
|
+
const today = new Date();
|
|
441
|
+
const stale = instincts.filter((i) => {
|
|
442
|
+
if (!i.last_seen) return false;
|
|
443
|
+
const diff = (today - new Date(i.last_seen)) / (1000 * 60 * 60 * 24);
|
|
444
|
+
return diff > 30;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const autoBar = "█".repeat(Math.min(10, byConf.auto.length)) + "░".repeat(Math.max(0, 10 - byConf.auto.length));
|
|
448
|
+
const sugBar = "█".repeat(Math.min(10, byConf.suggest.length)) + "░".repeat(Math.max(0, 10 - byConf.suggest.length));
|
|
449
|
+
const silBar = "█".repeat(Math.min(10, byConf.silent.length)) + "░".repeat(Math.max(0, 10 - byConf.silent.length));
|
|
450
|
+
|
|
451
|
+
const top5 = instincts
|
|
452
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
453
|
+
.slice(0, 5)
|
|
454
|
+
.map((i) => ` ${("█".repeat(Math.round(i.confidence * 10)) + "░".repeat(10 - Math.round(i.confidence * 10)))} ${i.confidence.toFixed(2)} ${i.id}`)
|
|
455
|
+
.join("\n");
|
|
456
|
+
|
|
457
|
+
// Check available packs
|
|
458
|
+
let packInfo = "";
|
|
459
|
+
if (existsSync(PACKS_DIR)) {
|
|
460
|
+
const packs = readdirSync(PACKS_DIR).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
461
|
+
packInfo = `\n Available packs: ${packs.join(", ")}`;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return text([
|
|
465
|
+
`╔══════════════════════════════════════════════════════════════╗`,
|
|
466
|
+
`║ continuous-improvement Dashboard ║`,
|
|
467
|
+
`╠══════════════════════════════════════════════════════════════╣`,
|
|
468
|
+
`║ ║`,
|
|
469
|
+
`║ Project: ${project.name.padEnd(20)} Level: ${level.padEnd(12)} ║`,
|
|
470
|
+
`║ Sessions: ~${String(Math.floor(obsCount / 10)).padEnd(17)} Mode: ${MODE.padEnd(13)} ║`,
|
|
471
|
+
`║ ║`,
|
|
472
|
+
`║ ┌─ Observations ────────────────────────────────────────┐ ║`,
|
|
473
|
+
`║ │ Total: ${String(obsCount).padEnd(48)} │ ║`,
|
|
474
|
+
`║ └───────────────────────────────────────────────────────┘ ║`,
|
|
475
|
+
`║ ║`,
|
|
476
|
+
`║ ┌─ Instincts ───────────────────────────────────────────┐ ║`,
|
|
477
|
+
`║ │ Total: ${String(instincts.length).padEnd(48)} │ ║`,
|
|
478
|
+
`║ │ ${autoBar} Auto-apply (0.7+): ${String(byConf.auto.length).padEnd(20)} │ ║`,
|
|
479
|
+
`║ │ ${sugBar} Suggest (0.5-0.69): ${String(byConf.suggest.length).padEnd(19)} │ ║`,
|
|
480
|
+
`║ │ ${silBar} Silent (< 0.5): ${String(byConf.silent.length).padEnd(23)} │ ║`,
|
|
481
|
+
`║ │ Global: ${String(globalCount).padEnd(10)} Project: ${String(projectCount).padEnd(28)} │ ║`,
|
|
482
|
+
`║ └───────────────────────────────────────────────────────┘ ║`,
|
|
483
|
+
`║ ║`,
|
|
484
|
+
instincts.length > 0 ? [
|
|
485
|
+
`║ ┌─ Top Instincts ───────────────────────────────────────┐ ║`,
|
|
486
|
+
...top5.split("\n").map((l) => `║ │${l.padEnd(56)}│ ║`),
|
|
487
|
+
`║ └───────────────────────────────────────────────────────┘ ║`,
|
|
488
|
+
].join("\n") : "",
|
|
489
|
+
`║ ║`,
|
|
490
|
+
`║ ┌─ Health ──────────────────────────────────────────────┐ ║`,
|
|
491
|
+
`║ │ Stale (30+ days): ${String(stale.length).padEnd(38)} │ ║`,
|
|
492
|
+
`║ └───────────────────────────────────────────────────────┘ ║`,
|
|
493
|
+
packInfo ? `║${packInfo.padEnd(63)}║` : "",
|
|
494
|
+
`║ ║`,
|
|
495
|
+
`╚══════════════════════════════════════════════════════════════╝`,
|
|
496
|
+
].filter(Boolean).join("\n"));
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
case "ci_load_pack": {
|
|
500
|
+
if (MODE !== "expert") return error("ci_load_pack requires expert mode");
|
|
501
|
+
const packName = params.pack;
|
|
502
|
+
const packPath = join(PACKS_DIR, `${packName}.json`);
|
|
503
|
+
if (!existsSync(packPath)) {
|
|
504
|
+
const available = existsSync(PACKS_DIR)
|
|
505
|
+
? readdirSync(PACKS_DIR).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""))
|
|
506
|
+
: [];
|
|
507
|
+
return error(`Unknown pack: ${packName}. Available: ${available.join(", ")}`);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const packInstincts = JSON.parse(readFileSync(packPath, "utf8"));
|
|
511
|
+
const existing = readInstincts(project.hash);
|
|
512
|
+
const existingIds = new Set(existing.map((i) => i.id));
|
|
513
|
+
let loaded = 0;
|
|
514
|
+
|
|
515
|
+
for (const inst of packInstincts) {
|
|
516
|
+
if (existingIds.has(inst.id)) continue;
|
|
517
|
+
writeInstinct(project.hash, {
|
|
518
|
+
...inst,
|
|
519
|
+
source: `pack-${packName}`,
|
|
520
|
+
scope: "project",
|
|
521
|
+
observation_count: 0,
|
|
522
|
+
});
|
|
523
|
+
loaded++;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return text(`Loaded ${loaded}/${packInstincts.length} instincts from **${packName}** pack (${packInstincts.length - loaded} already existed)`);
|
|
527
|
+
}
|
|
528
|
+
|
|
409
529
|
default:
|
|
410
530
|
return error(`Unknown tool: ${name}`);
|
|
411
531
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dashboard
|
|
3
|
+
description: Visual dashboard showing instinct health, observation stats, and learning progress
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Instinct Dashboard
|
|
7
|
+
|
|
8
|
+
Generate a visual dashboard for this project's continuous-improvement status.
|
|
9
|
+
|
|
10
|
+
## Instructions
|
|
11
|
+
|
|
12
|
+
1. **Find project hash:** Run `git rev-parse --show-toplevel 2>/dev/null`, then SHA-256 first 12 chars
|
|
13
|
+
2. **Read observations:** Count lines in `~/.claude/instincts/<hash>/observations.jsonl`
|
|
14
|
+
3. **Read instincts:** Load all `*.yaml` files from project dir + `global/`
|
|
15
|
+
4. **Read instinct packs:** Check if any packs from `instinct-packs/` have been loaded
|
|
16
|
+
|
|
17
|
+
## Display Format
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
21
|
+
║ continuous-improvement Dashboard ║
|
|
22
|
+
╠══════════════════════════════════════════════════════════════╣
|
|
23
|
+
║ ║
|
|
24
|
+
║ Project: <name> Level: <CAPTURE|ANALYZE|...> ║
|
|
25
|
+
║ Sessions: ~<obs/10> Mode: <beginner|expert> ║
|
|
26
|
+
║ ║
|
|
27
|
+
║ ┌─ Observations ────────────────────────────────────────┐ ║
|
|
28
|
+
║ │ Total: <n> Unprocessed: <n> Last: <date> │ ║
|
|
29
|
+
║ └───────────────────────────────────────────────────────┘ ║
|
|
30
|
+
║ ║
|
|
31
|
+
║ ┌─ Instincts ───────────────────────────────────────────┐ ║
|
|
32
|
+
║ │ Total: <n> │ ║
|
|
33
|
+
║ │ ████████░░ Auto-apply (0.7+): <n> │ ║
|
|
34
|
+
║ │ █████░░░░░ Suggest (0.5-0.69): <n> │ ║
|
|
35
|
+
║ │ ██░░░░░░░░ Silent (< 0.5): <n> │ ║
|
|
36
|
+
║ │ Global: <n> Project: <n> │ ║
|
|
37
|
+
║ └───────────────────────────────────────────────────────┘ ║
|
|
38
|
+
║ ║
|
|
39
|
+
║ ┌─ Top Instincts ───────────────────────────────────────┐ ║
|
|
40
|
+
║ │ <list top 5 instincts by confidence with bars> │ ║
|
|
41
|
+
║ └───────────────────────────────────────────────────────┘ ║
|
|
42
|
+
║ ║
|
|
43
|
+
║ ┌─ Health ──────────────────────────────────────────────┐ ║
|
|
44
|
+
║ │ Stale (30+ days): <n> Decaying: <n> │ ║
|
|
45
|
+
║ │ Recently reinforced: <n> │ ║
|
|
46
|
+
║ └───────────────────────────────────────────────────────┘ ║
|
|
47
|
+
║ ║
|
|
48
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## After Display
|
|
52
|
+
|
|
53
|
+
- If stale instincts > 0: suggest reviewing them
|
|
54
|
+
- If unprocessed observations > 20: suggest running analysis
|
|
55
|
+
- If no instincts exist: explain the auto-leveling timeline
|
|
56
|
+
- Show available instinct packs that haven't been loaded yet
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: discipline
|
|
3
|
+
description: Quick reference card for the 7 Laws of AI Agent Discipline
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# The 7 Laws — Quick Reference
|
|
7
|
+
|
|
8
|
+
Print this card and check yourself against each law.
|
|
9
|
+
|
|
10
|
+
## The Laws
|
|
11
|
+
|
|
12
|
+
| # | Law | Check | Red Flag |
|
|
13
|
+
|---|-----|-------|----------|
|
|
14
|
+
| 1 | **Research Before Executing** | Did I search for existing solutions? | "I'll just quickly..." |
|
|
15
|
+
| 2 | **Plan Is Sacred** | Did I state WILL / WILL NOT / VERIFY? | "Let me also add..." |
|
|
16
|
+
| 3 | **One Thing at a Time** | Am I finishing before starting? | "While I'm here..." |
|
|
17
|
+
| 4 | **Verify Before Reporting** | Did I check the ACTUAL output? | "This should work..." |
|
|
18
|
+
| 5 | **Reflect After Sessions** | Did I note what worked/failed? | "I'll remember..." |
|
|
19
|
+
| 6 | **Iterate One Change** | Am I changing one thing at a time? | "And also..." |
|
|
20
|
+
| 7 | **Learn From Every Session** | Did I capture this as an instinct? | "Next time I'll..." |
|
|
21
|
+
|
|
22
|
+
## The Loop
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Research → Plan → Execute (one thing) → Verify → Reflect → Learn → Iterate
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Self-Check
|
|
29
|
+
|
|
30
|
+
Before saying "Done", verify ALL:
|
|
31
|
+
- [ ] Code runs without errors
|
|
32
|
+
- [ ] Output matches expected result
|
|
33
|
+
- [ ] I checked the **actual** result (not assumed)
|
|
34
|
+
- [ ] Build passes
|
|
35
|
+
- [ ] I can explain the change in one sentence
|
|
36
|
+
|
|
37
|
+
If you're skipping a step, that's the step you need most.
|
package/hooks/session.sh
CHANGED
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "go-error-handling",
|
|
4
|
+
"trigger": "when calling functions that return errors",
|
|
5
|
+
"body": "Always check returned errors immediately. Never use _ to discard errors unless you've explicitly decided it's safe and documented why.",
|
|
6
|
+
"confidence": 0.7,
|
|
7
|
+
"domain": "patterns"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": "go-defer-cleanup",
|
|
11
|
+
"trigger": "when opening files, connections, or acquiring locks",
|
|
12
|
+
"body": "Use defer immediately after acquiring a resource for cleanup. Place defer right after the error check for the acquisition.",
|
|
13
|
+
"confidence": 0.7,
|
|
14
|
+
"domain": "patterns"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "go-interface-consumer",
|
|
18
|
+
"trigger": "when defining Go interfaces",
|
|
19
|
+
"body": "Define interfaces at the consumer side, not the producer side. Keep interfaces small (1-3 methods). Accept interfaces, return structs.",
|
|
20
|
+
"confidence": 0.65,
|
|
21
|
+
"domain": "patterns"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "go-table-driven-tests",
|
|
25
|
+
"trigger": "when writing Go tests",
|
|
26
|
+
"body": "Use table-driven tests with subtests (t.Run) for functions with multiple input/output cases. Name test cases descriptively.",
|
|
27
|
+
"confidence": 0.7,
|
|
28
|
+
"domain": "testing"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "go-context-propagation",
|
|
32
|
+
"trigger": "when writing HTTP handlers or long-running operations",
|
|
33
|
+
"body": "Accept context.Context as the first parameter. Propagate it to all downstream calls. Use it for cancellation and timeouts.",
|
|
34
|
+
"confidence": 0.65,
|
|
35
|
+
"domain": "patterns"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "go-goroutine-lifecycle",
|
|
39
|
+
"trigger": "when spawning goroutines",
|
|
40
|
+
"body": "Always ensure goroutines have a way to exit (context cancellation, done channel, or WaitGroup). Never fire-and-forget goroutines without cleanup.",
|
|
41
|
+
"confidence": 0.7,
|
|
42
|
+
"domain": "patterns"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "go-struct-zero-values",
|
|
46
|
+
"trigger": "when designing Go structs",
|
|
47
|
+
"body": "Design structs so their zero value is useful. Use pointer fields only when nil is a meaningful distinct state from zero value.",
|
|
48
|
+
"confidence": 0.6,
|
|
49
|
+
"domain": "patterns"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "go-mod-tidy",
|
|
53
|
+
"trigger": "when adding or removing dependencies",
|
|
54
|
+
"body": "Run 'go mod tidy' after adding or removing imports to keep go.mod and go.sum clean.",
|
|
55
|
+
"confidence": 0.65,
|
|
56
|
+
"domain": "workflow"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "python-virtual-env",
|
|
4
|
+
"trigger": "when starting work on a Python project",
|
|
5
|
+
"body": "Check for virtual environment (venv, .venv, conda) before installing packages. Never install to system Python.",
|
|
6
|
+
"confidence": 0.7,
|
|
7
|
+
"domain": "workflow"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"id": "python-type-hints",
|
|
11
|
+
"trigger": "when writing Python functions",
|
|
12
|
+
"body": "Add type hints to function parameters and return values. Use Optional[], list[], dict[] (Python 3.10+) or typing module for older versions.",
|
|
13
|
+
"confidence": 0.6,
|
|
14
|
+
"domain": "code-style"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "python-pathlib",
|
|
18
|
+
"trigger": "when working with file paths in Python",
|
|
19
|
+
"body": "Use pathlib.Path instead of os.path for file operations. It's more readable and cross-platform.",
|
|
20
|
+
"confidence": 0.65,
|
|
21
|
+
"domain": "patterns"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "python-context-managers",
|
|
25
|
+
"trigger": "when opening files or database connections",
|
|
26
|
+
"body": "Always use context managers (with statement) for files, database connections, and locks. Never rely on manual .close() calls.",
|
|
27
|
+
"confidence": 0.7,
|
|
28
|
+
"domain": "patterns"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "python-list-comprehension",
|
|
32
|
+
"trigger": "when writing simple for loops that build lists",
|
|
33
|
+
"body": "Prefer list comprehensions for simple transformations. Use regular for loops when the logic is complex or has side effects.",
|
|
34
|
+
"confidence": 0.6,
|
|
35
|
+
"domain": "code-style"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "python-requirements-check",
|
|
39
|
+
"trigger": "when adding a new import",
|
|
40
|
+
"body": "Check if the package is already in requirements.txt, pyproject.toml, or Pipfile before adding. Search for existing usage in the codebase.",
|
|
41
|
+
"confidence": 0.65,
|
|
42
|
+
"domain": "workflow"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "python-pytest-fixtures",
|
|
46
|
+
"trigger": "when writing Python tests",
|
|
47
|
+
"body": "Use pytest fixtures for test setup/teardown instead of unittest setUp/tearDown. Use conftest.py for shared fixtures.",
|
|
48
|
+
"confidence": 0.6,
|
|
49
|
+
"domain": "testing"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "python-dataclass",
|
|
53
|
+
"trigger": "when creating classes that primarily hold data",
|
|
54
|
+
"body": "Use @dataclass or Pydantic BaseModel instead of plain classes for data containers. They provide __init__, __repr__, and comparison for free.",
|
|
55
|
+
"confidence": 0.65,
|
|
56
|
+
"domain": "patterns"
|
|
57
|
+
}
|
|
58
|
+
]
|