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.
@@ -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
+ });
@@ -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.0.0";
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
+ ]