continuous-improvement 3.12.3 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHANGELOG.md +45 -1
  3. package/LICENSE +21 -21
  4. package/QUICKSTART.md +1 -1
  5. package/README.md +29 -8
  6. package/bin/analyze.sh +161 -161
  7. package/bin/backfill.mjs +19 -19
  8. package/bin/check-doc-runtime-claims.mjs +0 -0
  9. package/bin/check-docs-substrings.mjs +0 -0
  10. package/bin/check-everything-mirror.mjs +0 -0
  11. package/bin/check-routing-targets.mjs +0 -0
  12. package/bin/check-scripts-citation-drift.mjs +0 -0
  13. package/bin/check-skill-count-prose.mjs +168 -0
  14. package/bin/check-skill-count.mjs +32 -1
  15. package/bin/check-skill-law-tag.mjs +0 -0
  16. package/bin/check-skill-mirror.mjs +0 -0
  17. package/bin/check-skill-tiers.mjs +0 -0
  18. package/bin/check-test-imports-only.mjs +1 -1
  19. package/bin/check-third-party-shape.mjs +0 -0
  20. package/bin/check-tool-count.mjs +0 -0
  21. package/bin/companion-preference-status.mjs +0 -0
  22. package/bin/gateguard-clear.mjs +0 -0
  23. package/bin/generate-plugin-manifests.mjs +2 -1
  24. package/bin/harvest-friction.mjs +0 -0
  25. package/bin/hook-stats.mjs +21 -21
  26. package/bin/install.mjs +120 -2
  27. package/bin/lint-transcript.mjs +15 -3
  28. package/bin/mcp-server.mjs +66 -8
  29. package/bin/observe.mjs +0 -0
  30. package/bin/plan-pack.mjs +77 -0
  31. package/bin/pre-commit-block-strays.sh +0 -0
  32. package/bin/refresh-third-party.mjs +0 -0
  33. package/bin/unified-cli.mjs +55 -410
  34. package/commands/continuous-improvement.md +115 -115
  35. package/commands/dashboard.md +56 -56
  36. package/commands/model-forward.md +13 -0
  37. package/commands/production-readiness-review.md +53 -0
  38. package/commands/roast.md +34 -0
  39. package/commands/seven-laws.md +16 -16
  40. package/commands/ship.md +57 -0
  41. package/commands/workspace-surface-audit.md +77 -77
  42. package/hooks/companion-preference.mjs +31 -19
  43. package/hooks/gateguard.mjs +38 -16
  44. package/hooks/goal-drift-stop.mjs +0 -0
  45. package/hooks/hook-pack.mjs +110 -0
  46. package/hooks/observe.sh +0 -0
  47. package/hooks/recall-briefing.mjs +0 -0
  48. package/hooks/route-prompt.mjs +0 -0
  49. package/hooks/session.sh +106 -106
  50. package/hooks/three-section-close.mjs +0 -0
  51. package/hooks/workflow-distill.mjs +145 -0
  52. package/instinct-packs/go.json +58 -58
  53. package/instinct-packs/meta.json +16 -16
  54. package/instinct-packs/python.json +58 -58
  55. package/instinct-packs/react.json +58 -58
  56. package/lib/cli-anything.mjs +0 -0
  57. package/lib/gateguard-state.mjs +62 -13
  58. package/lib/goal-drift-gate.mjs +0 -0
  59. package/lib/goal-state.mjs +8 -4
  60. package/lib/hook-pack-gate.mjs +65 -0
  61. package/lib/install-targets.mjs +121 -0
  62. package/lib/observe-event.mjs +0 -0
  63. package/lib/plan-review-packet.mjs +96 -0
  64. package/lib/plugin-metadata.mjs +30 -4
  65. package/lib/pm-marketplace.mjs +0 -0
  66. package/lib/recall-briefing.mjs +0 -0
  67. package/lib/recall-index.mjs +0 -0
  68. package/lib/resolve-home-dir.mjs +0 -0
  69. package/lib/skill-distill.mjs +141 -0
  70. package/lib/skill-tiers.mjs +0 -0
  71. package/lib/version-check.mjs +115 -0
  72. package/llms.txt +2 -2
  73. package/package.json +6 -4
  74. package/plugins/beginner.json +2 -2
  75. package/plugins/continuous-improvement/.claude-plugin/marketplace.json +2 -2
  76. package/plugins/continuous-improvement/.claude-plugin/plugin.json +2 -2
  77. package/plugins/continuous-improvement/LICENSE +21 -21
  78. package/plugins/continuous-improvement/README.md +1 -1
  79. package/plugins/continuous-improvement/bin/backfill.mjs +19 -19
  80. package/plugins/continuous-improvement/bin/gateguard-clear.mjs +0 -0
  81. package/plugins/continuous-improvement/bin/mcp-server.mjs +66 -8
  82. package/plugins/continuous-improvement/bin/observe.mjs +0 -0
  83. package/plugins/continuous-improvement/commands/continuous-improvement.md +115 -115
  84. package/plugins/continuous-improvement/commands/dashboard.md +56 -56
  85. package/plugins/continuous-improvement/commands/model-forward.md +13 -0
  86. package/plugins/continuous-improvement/commands/production-readiness-review.md +53 -0
  87. package/plugins/continuous-improvement/commands/roast.md +34 -0
  88. package/plugins/continuous-improvement/commands/seven-laws.md +16 -16
  89. package/plugins/continuous-improvement/commands/ship.md +57 -0
  90. package/plugins/continuous-improvement/commands/workspace-surface-audit.md +77 -77
  91. package/plugins/continuous-improvement/hooks/companion-preference.mjs +31 -19
  92. package/plugins/continuous-improvement/hooks/gateguard.mjs +38 -16
  93. package/plugins/continuous-improvement/hooks/goal-drift-stop.mjs +0 -0
  94. package/plugins/continuous-improvement/hooks/hook-pack.mjs +110 -0
  95. package/plugins/continuous-improvement/hooks/hooks.json +16 -1
  96. package/plugins/continuous-improvement/hooks/observe.sh +0 -0
  97. package/plugins/continuous-improvement/hooks/recall-briefing.mjs +0 -0
  98. package/plugins/continuous-improvement/hooks/route-prompt.mjs +0 -0
  99. package/plugins/continuous-improvement/hooks/session.sh +106 -106
  100. package/plugins/continuous-improvement/hooks/three-section-close.mjs +0 -0
  101. package/plugins/continuous-improvement/hooks/workflow-distill.mjs +145 -0
  102. package/plugins/continuous-improvement/instinct-packs/go.json +58 -58
  103. package/plugins/continuous-improvement/instinct-packs/meta.json +16 -16
  104. package/plugins/continuous-improvement/instinct-packs/python.json +58 -58
  105. package/plugins/continuous-improvement/instinct-packs/react.json +58 -58
  106. package/plugins/continuous-improvement/lib/gateguard-state.mjs +62 -13
  107. package/plugins/continuous-improvement/lib/goal-drift-gate.mjs +0 -0
  108. package/plugins/continuous-improvement/lib/goal-state.mjs +8 -4
  109. package/plugins/continuous-improvement/lib/hook-pack-gate.mjs +65 -0
  110. package/plugins/continuous-improvement/lib/observe-event.mjs +0 -0
  111. package/plugins/continuous-improvement/lib/plugin-metadata.mjs +30 -4
  112. package/plugins/continuous-improvement/lib/recall-briefing.mjs +0 -0
  113. package/plugins/continuous-improvement/lib/recall-index.mjs +0 -0
  114. package/plugins/continuous-improvement/lib/resolve-home-dir.mjs +0 -0
  115. package/plugins/continuous-improvement/lib/skill-distill.mjs +141 -0
  116. package/plugins/continuous-improvement/skills/README.md +2 -1
  117. package/plugins/continuous-improvement/skills/gateguard/SKILL.md +4 -4
  118. package/plugins/continuous-improvement/skills/goal-monitor/SKILL.md +1 -1
  119. package/plugins/continuous-improvement/skills/handoff/SKILL.md +0 -1
  120. package/plugins/continuous-improvement/skills/model-forward/SKILL.md +44 -0
  121. package/plugins/continuous-improvement/skills/roast/SKILL.md +108 -0
  122. package/plugins/continuous-improvement/skills/strategic-compact/SKILL.md +12 -32
  123. package/plugins/continuous-improvement/skills/tdd-workflow/SKILL.md +411 -411
  124. package/plugins/expert.json +5 -1
  125. package/skills/README.md +5 -3
  126. package/skills/gateguard.md +4 -4
  127. package/skills/goal-monitor.md +1 -1
  128. package/skills/handoff.md +0 -1
  129. package/skills/model-forward.md +44 -0
  130. package/skills/roast.md +108 -0
  131. package/skills/strategic-compact.md +12 -32
  132. package/skills/tdd-workflow.md +411 -411
  133. package/lib/compound-engineering.mjs +0 -831
  134. package/lib/pm-skills.mjs +0 -1274
  135. package/lib/unified-plugin.mjs +0 -924
  136. package/plugins/continuous-improvement/skills/para-memory-files/SKILL.md +0 -108
  137. package/skills/para-memory-files.md +0 -108
package/hooks/session.sh CHANGED
@@ -1,106 +1,106 @@
1
- #!/usr/bin/env bash
2
- # session.sh — SessionStart/SessionEnd hook for continuous-improvement
3
- # SessionStart: loads instincts and prints status
4
- # SessionEnd: prompts reflection
5
- # Always exits 0 — never blocks the session
6
- trap 'exit 0' EXIT ERR INT TERM
7
-
8
- INSTINCTS_DIR="${HOME}/.claude/instincts"
9
-
10
- # Read stdin
11
- INPUT="$(cat)"
12
- [[ -z "$INPUT" ]] && exit 0
13
-
14
- # ---------------------------------------------------------------------------
15
- # Detect event type from hook context
16
- # ---------------------------------------------------------------------------
17
- # SessionStart hooks receive no tool_name, SessionEnd hooks may vary.
18
- # We detect based on the hook_type field or the calling context.
19
- EVENT_TYPE=""
20
- if command -v jq &>/dev/null; then
21
- EVENT_TYPE="$(printf '%s' "$INPUT" | jq -r '.hook_type // .event_type // "unknown"' 2>/dev/null)"
22
- else
23
- if printf '%s' "$INPUT" | grep -q '"SessionStart"'; then
24
- EVENT_TYPE="SessionStart"
25
- elif printf '%s' "$INPUT" | grep -q '"SessionEnd"'; then
26
- EVENT_TYPE="SessionEnd"
27
- fi
28
- fi
29
-
30
- # ---------------------------------------------------------------------------
31
- # Project detection (same as observe.sh)
32
- # ---------------------------------------------------------------------------
33
- PROJECT_ROOT=""
34
- if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
35
- PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
36
- fi
37
- if [[ -z "$PROJECT_ROOT" ]]; then
38
- PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
39
- fi
40
- if [[ -z "$PROJECT_ROOT" ]]; then
41
- PROJECT_ROOT="global"
42
- fi
43
-
44
- PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
45
- PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
46
-
47
- # ---------------------------------------------------------------------------
48
- # SessionStart: count observations and instincts, print brief status
49
- # ---------------------------------------------------------------------------
50
- if [[ "$EVENT_TYPE" == "SessionStart" || "$EVENT_TYPE" == "unknown" ]]; then
51
- OBS_COUNT=0
52
- INSTINCT_COUNT=0
53
-
54
- OBS_FILE="${PROJECT_DIR}/observations.jsonl"
55
- if [[ -f "$OBS_FILE" ]]; then
56
- OBS_COUNT="$(wc -l < "$OBS_FILE" 2>/dev/null || echo 0)"
57
- fi
58
-
59
- # Count yaml files in project + global
60
- for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
61
- if [[ -d "$dir" ]]; then
62
- count="$(find "$dir" -maxdepth 1 -name '*.yaml' 2>/dev/null | wc -l)"
63
- INSTINCT_COUNT=$((INSTINCT_COUNT + count))
64
- fi
65
- done
66
-
67
- # Determine level
68
- LEVEL="CAPTURE"
69
- if (( OBS_COUNT >= 20 )) || (( INSTINCT_COUNT > 0 )); then
70
- LEVEL="ANALYZE"
71
- fi
72
-
73
- # Check for high-confidence instincts
74
- if (( INSTINCT_COUNT > 0 )); then
75
- for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
76
- if [[ -d "$dir" ]]; then
77
- for f in "$dir"/*.yaml; do
78
- [[ -f "$f" ]] || continue
79
- conf="$(grep '^confidence:' "$f" 2>/dev/null | head -1 | sed 's/confidence: *//')"
80
- if [[ -n "$conf" ]]; then
81
- # Compare as integer (multiply by 100) using awk for portability
82
- int_conf="$(printf '%s' "$conf" | awk '{printf "%.0f", $1 * 100}')"
83
- if [[ -n "$int_conf" ]] && (( int_conf >= 70 )); then
84
- LEVEL="AUTO-APPLY"
85
- break 2
86
- elif (( int_conf >= 50 )); then
87
- LEVEL="SUGGEST"
88
- fi
89
- fi
90
- done
91
- fi
92
- done
93
- fi
94
-
95
- # Write status to stderr (visible in hook output, not blocking)
96
- echo "[continuous-improvement] Level: ${LEVEL} | Observations: ${OBS_COUNT} | Instincts: ${INSTINCT_COUNT}" >&2
97
- fi
98
-
99
- # ---------------------------------------------------------------------------
100
- # SessionEnd: remind to reflect
101
- # ---------------------------------------------------------------------------
102
- if [[ "$EVENT_TYPE" == "SessionEnd" ]]; then
103
- echo "[continuous-improvement] Session ending. Run /continuous-improvement to reflect and capture learnings." >&2
104
- fi
105
-
106
- exit 0
1
+ #!/usr/bin/env bash
2
+ # session.sh — SessionStart/SessionEnd hook for continuous-improvement
3
+ # SessionStart: loads instincts and prints status
4
+ # SessionEnd: prompts reflection
5
+ # Always exits 0 — never blocks the session
6
+ trap 'exit 0' EXIT ERR INT TERM
7
+
8
+ INSTINCTS_DIR="${HOME}/.claude/instincts"
9
+
10
+ # Read stdin
11
+ INPUT="$(cat)"
12
+ [[ -z "$INPUT" ]] && exit 0
13
+
14
+ # ---------------------------------------------------------------------------
15
+ # Detect event type from hook context
16
+ # ---------------------------------------------------------------------------
17
+ # SessionStart hooks receive no tool_name, SessionEnd hooks may vary.
18
+ # We detect based on the hook_type field or the calling context.
19
+ EVENT_TYPE=""
20
+ if command -v jq &>/dev/null; then
21
+ EVENT_TYPE="$(printf '%s' "$INPUT" | jq -r '.hook_type // .event_type // "unknown"' 2>/dev/null)"
22
+ else
23
+ if printf '%s' "$INPUT" | grep -q '"SessionStart"'; then
24
+ EVENT_TYPE="SessionStart"
25
+ elif printf '%s' "$INPUT" | grep -q '"SessionEnd"'; then
26
+ EVENT_TYPE="SessionEnd"
27
+ fi
28
+ fi
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Project detection (same as observe.sh)
32
+ # ---------------------------------------------------------------------------
33
+ PROJECT_ROOT=""
34
+ if [[ -n "${CLAUDE_PROJECT_DIR:-}" && -d "${CLAUDE_PROJECT_DIR}" ]]; then
35
+ PROJECT_ROOT="${CLAUDE_PROJECT_DIR}"
36
+ fi
37
+ if [[ -z "$PROJECT_ROOT" ]]; then
38
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
39
+ fi
40
+ if [[ -z "$PROJECT_ROOT" ]]; then
41
+ PROJECT_ROOT="global"
42
+ fi
43
+
44
+ PROJECT_HASH="$(printf '%s' "$PROJECT_ROOT" | sha256sum | cut -c1-12)"
45
+ PROJECT_DIR="${INSTINCTS_DIR}/${PROJECT_HASH}"
46
+
47
+ # ---------------------------------------------------------------------------
48
+ # SessionStart: count observations and instincts, print brief status
49
+ # ---------------------------------------------------------------------------
50
+ if [[ "$EVENT_TYPE" == "SessionStart" || "$EVENT_TYPE" == "unknown" ]]; then
51
+ OBS_COUNT=0
52
+ INSTINCT_COUNT=0
53
+
54
+ OBS_FILE="${PROJECT_DIR}/observations.jsonl"
55
+ if [[ -f "$OBS_FILE" ]]; then
56
+ OBS_COUNT="$(wc -l < "$OBS_FILE" 2>/dev/null || echo 0)"
57
+ fi
58
+
59
+ # Count yaml files in project + global
60
+ for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
61
+ if [[ -d "$dir" ]]; then
62
+ count="$(find "$dir" -maxdepth 1 -name '*.yaml' 2>/dev/null | wc -l)"
63
+ INSTINCT_COUNT=$((INSTINCT_COUNT + count))
64
+ fi
65
+ done
66
+
67
+ # Determine level
68
+ LEVEL="CAPTURE"
69
+ if (( OBS_COUNT >= 20 )) || (( INSTINCT_COUNT > 0 )); then
70
+ LEVEL="ANALYZE"
71
+ fi
72
+
73
+ # Check for high-confidence instincts
74
+ if (( INSTINCT_COUNT > 0 )); then
75
+ for dir in "$PROJECT_DIR" "${INSTINCTS_DIR}/global"; do
76
+ if [[ -d "$dir" ]]; then
77
+ for f in "$dir"/*.yaml; do
78
+ [[ -f "$f" ]] || continue
79
+ conf="$(grep '^confidence:' "$f" 2>/dev/null | head -1 | sed 's/confidence: *//')"
80
+ if [[ -n "$conf" ]]; then
81
+ # Compare as integer (multiply by 100) using awk for portability
82
+ int_conf="$(printf '%s' "$conf" | awk '{printf "%.0f", $1 * 100}')"
83
+ if [[ -n "$int_conf" ]] && (( int_conf >= 70 )); then
84
+ LEVEL="AUTO-APPLY"
85
+ break 2
86
+ elif (( int_conf >= 50 )); then
87
+ LEVEL="SUGGEST"
88
+ fi
89
+ fi
90
+ done
91
+ fi
92
+ done
93
+ fi
94
+
95
+ # Write status to stderr (visible in hook output, not blocking)
96
+ echo "[continuous-improvement] Level: ${LEVEL} | Observations: ${OBS_COUNT} | Instincts: ${INSTINCT_COUNT}" >&2
97
+ fi
98
+
99
+ # ---------------------------------------------------------------------------
100
+ # SessionEnd: remind to reflect
101
+ # ---------------------------------------------------------------------------
102
+ if [[ "$EVENT_TYPE" == "SessionEnd" ]]; then
103
+ echo "[continuous-improvement] Session ending. Run /continuous-improvement to reflect and capture learnings." >&2
104
+ fi
105
+
106
+ exit 0
File without changes
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ // workflow-distill.mts — Stop hook (opt-in, default off) that nudges the operator
3
+ // to persist a verified native Workflow run as a Mulahazah draft instinct via the
4
+ // ci_distill_from_workflow MCP tool. This is the one integration a per-turn
5
+ // orchestration primitive cannot do for itself: make the lesson of an expensive
6
+ // multi-agent run survive the run instead of evaporating when the turn ends — the
7
+ // fast-follow deferred in docs/plans/2026-06-08-workflow-instinct-bridge.md.
8
+ //
9
+ // Mode via CLAUDE_WORKFLOW_DISTILL_NUDGE: "on" | "off" (default).
10
+ // - off (default): no-op. Operators who do not use native Workflows are never
11
+ // nagged.
12
+ // - on: if the session's observation feed shows a verified Workflow run that
13
+ // has not already been nudged, print ONE stderr line suggesting
14
+ // ci_distill_from_workflow. It is an amplifier, never a gate — it cannot
15
+ // block the Stop and writes nothing to stdout.
16
+ //
17
+ // Project-hash resolution is byte-identical to bin/observe.mts and
18
+ // hooks/goal-drift-stop.mts so this hook reads the SAME observations.jsonl the
19
+ // observer writes. The "is this run verified" decision is the pure, already-tested
20
+ // workflowRunFromObservations (src/lib/skill-distill.mts), which fails closed:
21
+ // no Workflow row, unparseable meta, or no following verify-exit-0 => null => no
22
+ // nudge. Per-run dedup state lives in
23
+ // ~/.claude/instincts/<project-hash>/workflow-distill-state.json keyed by run
24
+ // name + verify command, so a run is nudged at most once. Fail-open by
25
+ // construction: any error exits 0 and never blocks. No network. 5s hook budget.
26
+ import { execFileSync } from "node:child_process";
27
+ import { createHash } from "node:crypto";
28
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
29
+ import { join } from "node:path";
30
+ import { resolveHomeDir } from "../lib/resolve-home-dir.mjs";
31
+ import { workflowRunFromObservations } from "../lib/skill-distill.mjs";
32
+ function safeJsonParse(text) {
33
+ try {
34
+ return JSON.parse(text);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ function resolveMode() {
41
+ return (process.env.CLAUDE_WORKFLOW_DISTILL_NUDGE ?? "off").trim().toLowerCase() === "on" ? "on" : "off";
42
+ }
43
+ // Mirrors bin/observe.mts:resolveProjectRoot — the same basis the observer uses
44
+ // to bucket observations.jsonl, so the hash here resolves to the same file.
45
+ function resolveProjectRoot() {
46
+ const fromEnv = process.env.CLAUDE_PROJECT_DIR;
47
+ if (fromEnv)
48
+ return fromEnv;
49
+ try {
50
+ const root = execFileSync("git", ["rev-parse", "--show-toplevel"], {
51
+ encoding: "utf8",
52
+ stdio: ["ignore", "pipe", "ignore"],
53
+ }).trim();
54
+ if (root)
55
+ return root;
56
+ }
57
+ catch {
58
+ // not a git repo
59
+ }
60
+ return "global";
61
+ }
62
+ function projectHash(root) {
63
+ return createHash("sha256").update(root).digest("hex").slice(0, 12);
64
+ }
65
+ function readObservations(instinctsProjectDir) {
66
+ const file = join(instinctsProjectDir, "observations.jsonl");
67
+ try {
68
+ if (!existsSync(file))
69
+ return [];
70
+ const lines = readFileSync(file, "utf8")
71
+ .split("\n")
72
+ .filter((line) => line.trim().length > 0);
73
+ const out = [];
74
+ // A workflow row plus its following verify both sit near the tail; a bounded
75
+ // window keeps the 5s budget safe on a long-lived feed.
76
+ for (const line of lines.slice(-5000)) {
77
+ const row = safeJsonParse(line);
78
+ if (!row)
79
+ continue;
80
+ out.push({
81
+ ts: typeof row.ts === "string" ? row.ts : "",
82
+ session: typeof row.session === "string" ? row.session : "",
83
+ session_id: typeof row.session_id === "string" ? row.session_id : "",
84
+ tool: typeof row.tool === "string" ? row.tool : "",
85
+ input_summary: typeof row.input_summary === "string" ? row.input_summary : "",
86
+ output_summary: typeof row.output_summary === "string" ? row.output_summary : "",
87
+ });
88
+ }
89
+ return out;
90
+ }
91
+ catch {
92
+ return [];
93
+ }
94
+ }
95
+ function readNudgedKeys(instinctsProjectDir) {
96
+ const file = join(instinctsProjectDir, "workflow-distill-state.json");
97
+ try {
98
+ if (!existsSync(file))
99
+ return new Set();
100
+ const parsed = safeJsonParse(readFileSync(file, "utf8"));
101
+ const arr = parsed && Array.isArray(parsed.nudged) ? parsed.nudged : [];
102
+ return new Set(arr.filter((x) => typeof x === "string"));
103
+ }
104
+ catch {
105
+ return new Set();
106
+ }
107
+ }
108
+ function persistNudged(instinctsProjectDir, keys) {
109
+ try {
110
+ mkdirSync(instinctsProjectDir, { recursive: true });
111
+ writeFileSync(join(instinctsProjectDir, "workflow-distill-state.json"), JSON.stringify({ ts: new Date().toISOString(), nudged: [...keys] }) + "\n", "utf8");
112
+ }
113
+ catch {
114
+ // dedup persistence is best-effort; never throw
115
+ }
116
+ }
117
+ function main() {
118
+ if (resolveMode() === "off")
119
+ return;
120
+ const home = resolveHomeDir();
121
+ if (!home)
122
+ return;
123
+ const projectRoot = resolveProjectRoot();
124
+ const instinctsProjectDir = join(home, ".claude", "instincts", projectHash(projectRoot));
125
+ const observations = readObservations(instinctsProjectDir);
126
+ if (observations.length === 0)
127
+ return;
128
+ const run = workflowRunFromObservations(observations);
129
+ if (!run)
130
+ return; // fail closed: no verified run, nothing to nudge
131
+ const runKey = `${run.name}::${run.verifyCommand}`;
132
+ const nudged = readNudgedKeys(instinctsProjectDir);
133
+ if (nudged.has(runKey))
134
+ return; // already nudged this run — stay quiet
135
+ process.stderr.write(`[continuous-improvement] workflow-distill: verified workflow run "${run.name}" detected — ` +
136
+ "run the ci_distill_from_workflow MCP tool to persist it as a Mulahazah draft instinct.\n");
137
+ nudged.add(runKey);
138
+ persistNudged(instinctsProjectDir, nudged);
139
+ }
140
+ try {
141
+ main();
142
+ }
143
+ catch {
144
+ // fail open — never block the session due to a hook bug
145
+ }
@@ -1,58 +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
- ]
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
+ ]
@@ -1,16 +1,16 @@
1
- [
2
- {
3
- "id": "skip-thin-observation-schema",
4
- "trigger": "when running /continuous-improvement and observations.jsonl entries contain only tool_start events with no inputs/outputs/errors",
5
- "body": "Skip instinct fabrication and report the schema gap instead. With only ts/event/session/tool/project_id/project_name fields, the stream cannot distinguish a `git status` Bash call from a `rm -rf` Bash call. Tool-name bigrams (Bash→Bash, Edit→Edit, Read→Edit) reflect parallel-call artifacts and harness-enforced read-before-edit, not learned preferences. Recommend enhancing PostToolUse/PreToolUse hooks to log tool inputs (Bash command head, Skill name) and tool_end with success flag before retrying analysis.",
6
- "confidence": 0.6,
7
- "domain": "meta"
8
- },
9
- {
10
- "id": "parallelize-independent-tool-calls",
11
- "trigger": "when issuing 2+ tool calls with no data dependency between them in the same turn",
12
- "body": "Batch independent tool calls into a single message instead of issuing them sequentially. Independent reads, independent greps, independent git inspections, independent file edits in unrelated files belong in one message with multiple tool blocks. Dependent calls (output of A determines input of B) stay sequential. Exception: when a single check would short-circuit the whole task (e.g., `git status` returning empty), one call first is fine. Sequential issuing of independent calls wastes wall-clock time and burns context per round-trip.",
13
- "confidence": 0.6,
14
- "domain": "workflow"
15
- }
16
- ]
1
+ [
2
+ {
3
+ "id": "skip-thin-observation-schema",
4
+ "trigger": "when running /continuous-improvement and observations.jsonl entries contain only tool_start events with no inputs/outputs/errors",
5
+ "body": "Skip instinct fabrication and report the schema gap instead. With only ts/event/session/tool/project_id/project_name fields, the stream cannot distinguish a `git status` Bash call from a `rm -rf` Bash call. Tool-name bigrams (Bash→Bash, Edit→Edit, Read→Edit) reflect parallel-call artifacts and harness-enforced read-before-edit, not learned preferences. Recommend enhancing PostToolUse/PreToolUse hooks to log tool inputs (Bash command head, Skill name) and tool_end with success flag before retrying analysis.",
6
+ "confidence": 0.6,
7
+ "domain": "meta"
8
+ },
9
+ {
10
+ "id": "parallelize-independent-tool-calls",
11
+ "trigger": "when issuing 2+ tool calls with no data dependency between them in the same turn",
12
+ "body": "Batch independent tool calls into a single message instead of issuing them sequentially. Independent reads, independent greps, independent git inspections, independent file edits in unrelated files belong in one message with multiple tool blocks. Dependent calls (output of A determines input of B) stay sequential. Exception: when a single check would short-circuit the whole task (e.g., `git status` returning empty), one call first is fine. Sequential issuing of independent calls wastes wall-clock time and burns context per round-trip.",
13
+ "confidence": 0.6,
14
+ "domain": "workflow"
15
+ }
16
+ ]
@@ -1,58 +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
- ]
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
+ ]