@grainulation/wheat 1.0.3 → 1.0.5

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 (43) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +32 -31
  3. package/bin/wheat.js +63 -40
  4. package/compiler/detect-sprints.js +108 -66
  5. package/compiler/generate-manifest.js +116 -69
  6. package/compiler/wheat-compiler.js +763 -471
  7. package/lib/compiler.js +11 -6
  8. package/lib/connect.js +273 -134
  9. package/lib/defaults.js +32 -0
  10. package/lib/disconnect.js +61 -40
  11. package/lib/guard.js +20 -17
  12. package/lib/index.js +8 -8
  13. package/lib/init.js +260 -142
  14. package/lib/install-prompt.js +26 -26
  15. package/lib/load-claims.js +88 -0
  16. package/lib/quickstart.js +225 -111
  17. package/lib/serve-mcp.js +495 -180
  18. package/lib/server.js +198 -111
  19. package/lib/stats.js +65 -39
  20. package/lib/status.js +65 -34
  21. package/lib/update.js +13 -11
  22. package/package.json +8 -4
  23. package/templates/claude.md +31 -17
  24. package/templates/commands/blind-spot.md +9 -2
  25. package/templates/commands/brief.md +11 -1
  26. package/templates/commands/calibrate.md +3 -1
  27. package/templates/commands/challenge.md +4 -1
  28. package/templates/commands/connect.md +12 -1
  29. package/templates/commands/evaluate.md +4 -0
  30. package/templates/commands/feedback.md +3 -1
  31. package/templates/commands/handoff.md +11 -7
  32. package/templates/commands/init.md +4 -1
  33. package/templates/commands/merge.md +4 -1
  34. package/templates/commands/next.md +1 -0
  35. package/templates/commands/present.md +3 -0
  36. package/templates/commands/prototype.md +2 -0
  37. package/templates/commands/pull.md +103 -0
  38. package/templates/commands/replay.md +8 -0
  39. package/templates/commands/research.md +1 -0
  40. package/templates/commands/resolve.md +4 -1
  41. package/templates/commands/status.md +4 -0
  42. package/templates/commands/sync.md +94 -0
  43. package/templates/commands/witness.md +6 -2
package/lib/stats.js CHANGED
@@ -9,42 +9,38 @@
9
9
  * Zero npm dependencies.
10
10
  */
11
11
 
12
- import fs from 'fs';
13
- import path from 'path';
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import { loadClaims } from "./load-claims.js";
14
15
 
15
16
  // ─── Helpers ──────────────────────────────────────────────────────────────────
16
17
 
17
- /** Safely parse JSON from a file path; returns null on failure. */
18
- function loadJSON(filePath) {
19
- try {
20
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
21
- } catch {
22
- return null;
23
- }
24
- }
25
-
26
18
  /** Find all claims.json files in repo (root + examples/). */
27
19
  function findAllClaims(dir) {
28
20
  const results = [];
29
21
 
30
22
  // Root-level claims.json
31
- const rootClaims = path.join(dir, 'claims.json');
23
+ const rootClaims = path.join(dir, "claims.json");
32
24
  if (fs.existsSync(rootClaims)) {
33
- results.push({ path: rootClaims, label: '.' });
25
+ results.push({ path: rootClaims, label: "." });
34
26
  }
35
27
 
36
28
  // examples/<name>/claims.json
37
- const examplesDir = path.join(dir, 'examples');
29
+ const examplesDir = path.join(dir, "examples");
38
30
  if (fs.existsSync(examplesDir)) {
39
31
  try {
40
- for (const entry of fs.readdirSync(examplesDir, { withFileTypes: true })) {
32
+ for (const entry of fs.readdirSync(examplesDir, {
33
+ withFileTypes: true,
34
+ })) {
41
35
  if (!entry.isDirectory()) continue;
42
- const claimsPath = path.join(examplesDir, entry.name, 'claims.json');
36
+ const claimsPath = path.join(examplesDir, entry.name, "claims.json");
43
37
  if (fs.existsSync(claimsPath)) {
44
38
  results.push({ path: claimsPath, label: `examples/${entry.name}` });
45
39
  }
46
40
  }
47
- } catch { /* skip if unreadable */ }
41
+ } catch {
42
+ /* skip if unreadable */
43
+ }
48
44
  }
49
45
 
50
46
  return results;
@@ -57,7 +53,7 @@ export async function run(dir, _args) {
57
53
 
58
54
  if (sprintFiles.length === 0) {
59
55
  console.log();
60
- console.log(' No sprints found in this directory.');
56
+ console.log(" No sprints found in this directory.");
61
57
  console.log(' Run "wheat init" to start a research sprint.');
62
58
  console.log();
63
59
  process.exit(0);
@@ -72,12 +68,14 @@ export async function run(dir, _args) {
72
68
  const sprintSummaries = [];
73
69
 
74
70
  for (const sf of sprintFiles) {
75
- const data = loadJSON(sf.path);
71
+ const sfDir = path.dirname(sf.path);
72
+ const sfFilename = path.basename(sf.path);
73
+ const { data } = loadClaims(sfDir, { filename: sfFilename });
76
74
  if (!data) continue;
77
75
 
78
76
  const meta = data.meta || {};
79
77
  const claims = data.claims || [];
80
- const active = claims.filter(c => c.status === 'active');
78
+ const active = claims.filter((c) => c.status === "active");
81
79
 
82
80
  totalClaims += claims.length;
83
81
 
@@ -89,24 +87,25 @@ export async function run(dir, _args) {
89
87
 
90
88
  // Accumulate by phase_added
91
89
  for (const c of claims) {
92
- const phase = c.phase_added || 'unknown';
90
+ const phase = c.phase_added || "unknown";
93
91
  byPhase[phase] = (byPhase[phase] || 0) + 1;
94
92
  }
95
93
 
96
94
  // Accumulate by type
97
95
  for (const c of claims) {
98
- byType[c.type || 'unknown'] = (byType[c.type || 'unknown'] || 0) + 1;
96
+ byType[c.type || "unknown"] = (byType[c.type || "unknown"] || 0) + 1;
99
97
  }
100
98
 
101
99
  // Accumulate by evidence tier
102
100
  for (const c of claims) {
103
- byEvidence[c.evidence || 'unknown'] = (byEvidence[c.evidence || 'unknown'] || 0) + 1;
101
+ byEvidence[c.evidence || "unknown"] =
102
+ (byEvidence[c.evidence || "unknown"] || 0) + 1;
104
103
  }
105
104
 
106
105
  sprintSummaries.push({
107
106
  label: sf.label,
108
- question: (meta.question || '').slice(0, 60),
109
- phase: meta.phase || 'unknown',
107
+ question: (meta.question || "").slice(0, 60),
108
+ phase: meta.phase || "unknown",
110
109
  claims: claims.length,
111
110
  active: active.length,
112
111
  });
@@ -115,8 +114,8 @@ export async function run(dir, _args) {
115
114
  // ─── Print ────────────────────────────────────────────────────────────────
116
115
 
117
116
  console.log();
118
- console.log(' \x1b[1mwheat stats\x1b[0m — local sprint statistics');
119
- console.log(` ${''.repeat(50)}`);
117
+ console.log(" \x1b[1mwheat stats\x1b[0m — local sprint statistics");
118
+ console.log(` ${"".repeat(50)}`);
120
119
  console.log();
121
120
 
122
121
  // Sprint count
@@ -126,14 +125,24 @@ export async function run(dir, _args) {
126
125
  // Age
127
126
  if (earliestDate) {
128
127
  const days = Math.floor((Date.now() - earliestDate.getTime()) / 86400000);
129
- console.log(` Age: ${days} days since first sprint (${earliestDate.toISOString().slice(0, 10)})`);
128
+ console.log(
129
+ ` Age: ${days} days since first sprint (${earliestDate
130
+ .toISOString()
131
+ .slice(0, 10)})`
132
+ );
130
133
  }
131
134
 
132
135
  console.log();
133
136
 
134
137
  // By phase
135
- console.log(' \x1b[1mClaims by phase:\x1b[0m');
136
- const phaseOrder = ['define', 'research', 'prototype', 'evaluate', 'feedback'];
138
+ console.log(" \x1b[1mClaims by phase:\x1b[0m");
139
+ const phaseOrder = [
140
+ "define",
141
+ "research",
142
+ "prototype",
143
+ "evaluate",
144
+ "feedback",
145
+ ];
137
146
  const allPhases = [...new Set([...phaseOrder, ...Object.keys(byPhase)])];
138
147
  for (const p of allPhases) {
139
148
  if (byPhase[p]) {
@@ -144,8 +153,15 @@ export async function run(dir, _args) {
144
153
  console.log();
145
154
 
146
155
  // By type
147
- console.log(' \x1b[1mClaims by type:\x1b[0m');
148
- const typeOrder = ['constraint', 'factual', 'estimate', 'risk', 'recommendation', 'feedback'];
156
+ console.log(" \x1b[1mClaims by type:\x1b[0m");
157
+ const typeOrder = [
158
+ "constraint",
159
+ "factual",
160
+ "estimate",
161
+ "risk",
162
+ "recommendation",
163
+ "feedback",
164
+ ];
149
165
  const allTypes = [...new Set([...typeOrder, ...Object.keys(byType)])];
150
166
  for (const t of allTypes) {
151
167
  if (byType[t]) {
@@ -156,9 +172,11 @@ export async function run(dir, _args) {
156
172
  console.log();
157
173
 
158
174
  // By evidence tier
159
- console.log(' \x1b[1mClaims by evidence:\x1b[0m');
160
- const evidenceOrder = ['stated', 'web', 'documented', 'tested', 'production'];
161
- const allEvidence = [...new Set([...evidenceOrder, ...Object.keys(byEvidence)])];
175
+ console.log(" \x1b[1mClaims by evidence:\x1b[0m");
176
+ const evidenceOrder = ["stated", "web", "documented", "tested", "production"];
177
+ const allEvidence = [
178
+ ...new Set([...evidenceOrder, ...Object.keys(byEvidence)]),
179
+ ];
162
180
  for (const e of allEvidence) {
163
181
  if (byEvidence[e]) {
164
182
  console.log(` ${e.padEnd(14)} ${byEvidence[e]}`);
@@ -169,16 +187,24 @@ export async function run(dir, _args) {
169
187
 
170
188
  // Per-sprint table
171
189
  if (sprintFiles.length > 1) {
172
- console.log(' \x1b[1mPer-sprint breakdown:\x1b[0m');
190
+ console.log(" \x1b[1mPer-sprint breakdown:\x1b[0m");
173
191
  for (const s of sprintSummaries) {
174
- console.log(` ${s.label.padEnd(30)} ${String(s.claims).padStart(4)} claims (${s.active} active) [${s.phase}]`);
192
+ console.log(
193
+ ` ${s.label.padEnd(30)} ${String(s.claims).padStart(4)} claims (${
194
+ s.active
195
+ } active) [${s.phase}]`
196
+ );
175
197
  if (s.question) {
176
- console.log(` "${s.question}${s.question.length >= 60 ? '...' : ''}"`);
198
+ console.log(
199
+ ` "${s.question}${s.question.length >= 60 ? "..." : ""}"`
200
+ );
177
201
  }
178
202
  }
179
203
  console.log();
180
204
  }
181
205
 
182
- console.log(' No data leaves your machine. This is local self-inspection only.');
206
+ console.log(
207
+ " No data leaves your machine. This is local self-inspection only."
208
+ );
183
209
  console.log();
184
210
  }
package/lib/status.js CHANGED
@@ -7,34 +7,48 @@
7
7
  * Zero npm dependencies.
8
8
  */
9
9
 
10
- import fs from 'fs';
11
- import path from 'path';
12
- import { compile } from '../compiler/wheat-compiler.js';
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import { compile } from "../compiler/wheat-compiler.js";
13
+ import { loadClaims } from "./load-claims.js";
13
14
 
14
15
  export async function run(dir, args) {
15
- const jsonMode = args.includes('--json');
16
- const claimsPath = path.join(dir, 'claims.json');
16
+ const jsonMode = args.includes("--json");
17
+ const claimsPath = path.join(dir, "claims.json");
17
18
 
18
19
  if (!fs.existsSync(claimsPath)) {
19
20
  if (jsonMode) {
20
- console.log(JSON.stringify({ error: 'no_sprint', message: 'No sprint found in this directory.' }));
21
+ console.log(
22
+ JSON.stringify({
23
+ error: "no_sprint",
24
+ message: "No sprint found in this directory.",
25
+ })
26
+ );
21
27
  process.exit(0);
22
28
  }
23
29
  console.log();
24
- console.log(' No sprint found in this directory.');
30
+ console.log(" No sprint found in this directory.");
25
31
  console.log(' Run "wheat init" to start a research sprint.');
26
32
  console.log();
27
33
  process.exit(0);
28
34
  }
29
35
 
30
- const claimsData = JSON.parse(fs.readFileSync(claimsPath, 'utf8'));
31
- const compilationPath = path.join(dir, 'compilation.json');
36
+ const { data: claimsData, errors: loadErrors } = loadClaims(dir);
37
+ if (!claimsData) {
38
+ const msg =
39
+ loadErrors.length > 0
40
+ ? loadErrors[0].message
41
+ : "Failed to load claims.json";
42
+ console.error(` ${msg}`);
43
+ process.exit(1);
44
+ }
45
+ const compilationPath = path.join(dir, "compilation.json");
32
46
  const compilation = compile(claimsPath, compilationPath, dir);
33
47
 
34
48
  const meta = claimsData.meta || {};
35
49
  const claims = claimsData.claims || [];
36
- const active = claims.filter(c => c.status === 'active');
37
- const superseded = claims.filter(c => c.status === 'superseded');
50
+ const active = claims.filter((c) => c.status === "active");
51
+ const superseded = claims.filter((c) => c.status === "superseded");
38
52
 
39
53
  if (jsonMode) {
40
54
  const tiers = {};
@@ -43,9 +57,13 @@ export async function run(dir, args) {
43
57
  }
44
58
  const result = {
45
59
  question: meta.question || null,
46
- phase: meta.phase || 'unknown',
60
+ phase: meta.phase || "unknown",
47
61
  status: compilation.status,
48
- claims: { total: claims.length, active: active.length, superseded: superseded.length },
62
+ claims: {
63
+ total: claims.length,
64
+ active: active.length,
65
+ superseded: superseded.length,
66
+ },
49
67
  evidence: tiers,
50
68
  };
51
69
  if (compilation.conflicts) {
@@ -58,28 +76,35 @@ export async function run(dir, args) {
58
76
  result.topics = Object.keys(compilation.coverage);
59
77
  }
60
78
  if (meta.initiated) {
61
- const days = Math.floor((Date.now() - new Date(meta.initiated).getTime()) / 86400000);
79
+ const days = Math.floor(
80
+ (Date.now() - new Date(meta.initiated).getTime()) / 86400000
81
+ );
62
82
  result.age = { days, initiated: meta.initiated };
63
83
  }
64
84
  console.log(JSON.stringify(result, null, 2));
65
85
  process.exit(0);
66
86
  }
67
87
 
68
- const icon = compilation.status === 'ready' ? '\x1b[32m●\x1b[0m' : '\x1b[31m●\x1b[0m';
88
+ const icon =
89
+ compilation.status === "ready" ? "\x1b[32m●\x1b[0m" : "\x1b[31m●\x1b[0m";
69
90
 
70
91
  console.log();
71
- console.log(` ${icon} \x1b[1m${meta.question || 'No question set'}\x1b[0m`);
72
- console.log(` ${''.repeat(50)}`);
73
- console.log(` Phase: ${meta.phase || 'unknown'}`);
92
+ console.log(` ${icon} \x1b[1m${meta.question || "No question set"}\x1b[0m`);
93
+ console.log(` ${"".repeat(50)}`);
94
+ console.log(` Phase: ${meta.phase || "unknown"}`);
74
95
  console.log(` Status: ${compilation.status}`);
75
- console.log(` Claims: ${claims.length} total, ${active.length} active, ${superseded.length} superseded`);
96
+ console.log(
97
+ ` Claims: ${claims.length} total, ${active.length} active, ${superseded.length} superseded`
98
+ );
76
99
 
77
100
  // Evidence breakdown
78
101
  const tiers = {};
79
102
  for (const c of active) {
80
103
  tiers[c.evidence] = (tiers[c.evidence] || 0) + 1;
81
104
  }
82
- const tierStr = Object.entries(tiers).map(([k, v]) => `${k}:${v}`).join(' ');
105
+ const tierStr = Object.entries(tiers)
106
+ .map(([k, v]) => `${k}:${v}`)
107
+ .join(" ");
83
108
  if (tierStr) {
84
109
  console.log(` Evidence: ${tierStr}`);
85
110
  }
@@ -88,46 +113,52 @@ export async function run(dir, args) {
88
113
  if (compilation.conflicts) {
89
114
  const { resolved, unresolved } = compilation.conflicts;
90
115
  if (resolved.length > 0 || unresolved.length > 0) {
91
- console.log(` Conflicts: ${resolved.length} resolved, ${unresolved.length} unresolved`);
116
+ console.log(
117
+ ` Conflicts: ${resolved.length} resolved, ${unresolved.length} unresolved`
118
+ );
92
119
  }
93
120
  }
94
121
 
95
122
  // Topics
96
123
  if (compilation.coverage) {
97
124
  const topics = Object.keys(compilation.coverage);
98
- console.log(` Topics: ${topics.length} (${topics.join(', ')})`);
125
+ console.log(` Topics: ${topics.length} (${topics.join(", ")})`);
99
126
  }
100
127
 
101
128
  // Initiated
102
129
  if (meta.initiated) {
103
- const days = Math.floor((Date.now() - new Date(meta.initiated).getTime()) / 86400000);
130
+ const days = Math.floor(
131
+ (Date.now() - new Date(meta.initiated).getTime()) / 86400000
132
+ );
104
133
  console.log(` Age: ${days} days (since ${meta.initiated})`);
105
134
  }
106
135
 
107
136
  console.log();
108
137
 
109
138
  // Suggest next steps based on state
110
- console.log(' Next steps:');
139
+ console.log(" Next steps:");
111
140
 
112
141
  if (compilation.conflicts?.unresolved?.length > 0) {
113
- console.log(' /resolve — resolve unresolved conflicts');
142
+ console.log(" /resolve — resolve unresolved conflicts");
114
143
  }
115
144
 
116
145
  const weakTopics = Object.entries(compilation.coverage || {})
117
- .filter(([, d]) => d.max_evidence === 'stated' || d.max_evidence === 'web')
146
+ .filter(([, d]) => d.max_evidence === "stated" || d.max_evidence === "web")
118
147
  .map(([t]) => t);
119
148
 
120
149
  if (weakTopics.length > 0) {
121
- console.log(` /research — strengthen weak topics: ${weakTopics.join(', ')}`);
150
+ console.log(
151
+ ` /research — strengthen weak topics: ${weakTopics.join(", ")}`
152
+ );
122
153
  }
123
154
 
124
- if (meta.phase === 'define') {
125
- console.log(' /research <topic> — start investigating');
126
- } else if (meta.phase === 'research') {
127
- console.log(' /prototype — test your findings');
128
- } else if (meta.phase === 'prototype' || meta.phase === 'evaluate') {
129
- if (compilation.status === 'ready') {
130
- console.log(' /brief — compile the decision document');
155
+ if (meta.phase === "define") {
156
+ console.log(" /research <topic> — start investigating");
157
+ } else if (meta.phase === "research") {
158
+ console.log(" /prototype — test your findings");
159
+ } else if (meta.phase === "prototype" || meta.phase === "evaluate") {
160
+ if (compilation.status === "ready") {
161
+ console.log(" /brief — compile the decision document");
131
162
  }
132
163
  }
133
164
 
package/lib/update.js CHANGED
@@ -7,27 +7,27 @@
7
7
  * Zero npm dependencies.
8
8
  */
9
9
 
10
- import fs from 'fs';
11
- import path from 'path';
12
- import { fileURLToPath } from 'url';
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import { fileURLToPath } from "url";
13
13
 
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = path.dirname(__filename);
16
16
 
17
17
  function packageRoot() {
18
- return path.resolve(__dirname, '..');
18
+ return path.resolve(__dirname, "..");
19
19
  }
20
20
 
21
21
  export async function run(dir, args) {
22
- const force = args.includes('--force');
23
- const srcDir = path.join(packageRoot(), 'templates', 'commands');
24
- const destDir = path.join(dir, '.claude', 'commands');
22
+ const force = args.includes("--force");
23
+ const srcDir = path.join(packageRoot(), "templates", "commands");
24
+ const destDir = path.join(dir, ".claude", "commands");
25
25
 
26
26
  fs.mkdirSync(destDir, { recursive: true });
27
27
 
28
28
  let files;
29
29
  try {
30
- files = fs.readdirSync(srcDir).filter(f => f.endsWith('.md'));
30
+ files = fs.readdirSync(srcDir).filter((f) => f.endsWith(".md"));
31
31
  } catch (err) {
32
32
  console.error(`Cannot read command templates: ${err.message}`);
33
33
  process.exit(1);
@@ -46,10 +46,12 @@ export async function run(dir, args) {
46
46
 
47
47
  if (fs.existsSync(dest) && !force) {
48
48
  // Check if content differs
49
- const srcContent = fs.readFileSync(src, 'utf8');
50
- const destContent = fs.readFileSync(dest, 'utf8');
49
+ const srcContent = fs.readFileSync(src, "utf8");
50
+ const destContent = fs.readFileSync(dest, "utf8");
51
51
  if (srcContent !== destContent) {
52
- console.log(` \x1b[33m~\x1b[0m ${file} (differs, use --force to overwrite)`);
52
+ console.log(
53
+ ` \x1b[33m~\x1b[0m ${file} (differs, use --force to overwrite)`
54
+ );
53
55
  skipped++;
54
56
  } else {
55
57
  console.log(` \x1b[2m= ${file} (up to date)\x1b[0m`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grainulation/wheat",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Research-driven development framework — structured claims, compiled evidence, deterministic output",
5
5
  "license": "MIT",
6
6
  "author": "grainulation contributors",
@@ -32,7 +32,8 @@
32
32
  "./server": "./lib/server.js",
33
33
  "./guard": "./lib/guard.js",
34
34
  "./connect": "./lib/connect.js",
35
- "./mcp": "./lib/serve-mcp.js"
35
+ "./mcp": "./lib/serve-mcp.js",
36
+ "./load-claims": "./lib/load-claims.js"
36
37
  },
37
38
  "bin": {
38
39
  "wheat": "bin/wheat.js"
@@ -45,9 +46,12 @@
45
46
  "templates/"
46
47
  ],
47
48
  "scripts": {
48
- "test": "node --test test/cli.test.js test/compiler.test.js test/guard.test.js test/init.test.js"
49
+ "test": "node --test test/cli.test.js test/compiler.test.js test/guard.test.js test/init.test.js test/migration.test.js"
49
50
  },
50
51
  "engines": {
51
- "node": ">=18"
52
+ "node": ">=20"
53
+ },
54
+ "devDependencies": {
55
+ "@changesets/cli": "^2.30.0"
52
56
  }
53
57
  }
@@ -23,32 +23,36 @@ When the user sends a plain message (no slash command), assess whether a Wheat c
23
23
 
24
24
  **Route by intent:**
25
25
 
26
- | User says something like... | Route to | Why |
27
- |---|---|---|
28
- | "look into X", "what about X", "explore X", "how does X work" | `/research X` | Information gathering |
29
- | "build X", "try X", "make a quick X", "test whether X" | `/prototype` | Hands-on validation |
30
- | "is p001 really true?", "I doubt X", "what if X is wrong" | `/challenge <id>` | Adversarial testing |
31
- | "check this: <url>", "does <url> support X", "verify X against <url>" | `/witness <id> <url>` | External corroboration |
32
- | "what are we missing", "any gaps?", "what haven't we considered" | `/blind-spot` | Structural gap analysis |
33
- | "where are we", "what's the status", "show me the dashboard" | `/status` | Sprint snapshot |
34
- | "write it up", "give me the recommendation", "summarize for the team" | `/brief` | Decision document |
35
- | "make slides", "prepare for the meeting" | `/present` | Stakeholder presentation |
36
- | "someone else is taking over", "hand this off", "document for successor" | `/handoff` | Knowledge transfer |
37
- | "combine with the other sprint", "merge these" | `/merge <path>` | Cross-sprint merge |
38
- | "how did we get here", "show the history", "what changed over time" | `/replay` | Sprint archaeology |
39
- | "we shipped, here's what happened", "actual results were X" | `/calibrate --outcome "X"` | Prediction scoring |
40
- | "the stakeholder said X", "new constraint: X", "change of direction" | `/feedback` | Stakeholder input |
41
- | "resolve the conflict", "pick between X and Y" | `/resolve` | Conflict adjudication |
42
- | "connect to <repo/jira/docs>" | `/connect <type> <target>` | External source linking |
26
+ | User says something like... | Route to | Why |
27
+ | ------------------------------------------------------------------------ | -------------------------- | --------------------------- |
28
+ | "look into X", "what about X", "explore X", "how does X work" | `/research X` | Information gathering |
29
+ | "build X", "try X", "make a quick X", "test whether X" | `/prototype` | Hands-on validation |
30
+ | "is p001 really true?", "I doubt X", "what if X is wrong" | `/challenge <id>` | Adversarial testing |
31
+ | "check this: <url>", "does <url> support X", "verify X against <url>" | `/witness <id> <url>` | External corroboration |
32
+ | "what are we missing", "any gaps?", "what haven't we considered" | `/blind-spot` | Structural gap analysis |
33
+ | "where are we", "what's the status", "show me the dashboard" | `/status` | Sprint snapshot |
34
+ | "write it up", "give me the recommendation", "summarize for the team" | `/brief` | Decision document |
35
+ | "make slides", "prepare for the meeting" | `/present` | Stakeholder presentation |
36
+ | "someone else is taking over", "hand this off", "document for successor" | `/handoff` | Knowledge transfer |
37
+ | "combine with the other sprint", "merge these" | `/merge <path>` | Cross-sprint merge |
38
+ | "how did we get here", "show the history", "what changed over time" | `/replay` | Sprint archaeology |
39
+ | "we shipped, here's what happened", "actual results were X" | `/calibrate --outcome "X"` | Prediction scoring |
40
+ | "the stakeholder said X", "new constraint: X", "change of direction" | `/feedback` | Stakeholder input |
41
+ | "resolve the conflict", "pick between X and Y" | `/resolve` | Conflict adjudication |
42
+ | "connect to <repo/jira/docs>" | `/connect <type> <target>` | External source linking |
43
+ | "publish to confluence", "push to wiki", "sync to slack" | `/sync <target>` | Artifact publishing |
44
+ | "pull from deepwiki", "import from confluence", "backfill from repo" | `/pull <source>` | External knowledge backfill |
43
45
 
44
46
  **When NOT to route:** Questions about the framework itself ("how does the compiler work?"), code edits to wheat files, general conversation, ambiguous intent. When in doubt, ask: "That sounds like it could be a `/research` -- want me to run it as a full research pass, or just answer the question?"
45
47
 
46
48
  **Announce the routing:** Always tell the user what you're doing:
49
+
47
50
  > Running as `/research "SSE scalability"` -- this will create claims and compile. Say "just answer" if you wanted a quick response instead.
48
51
 
49
52
  This gives the user a chance to redirect before the full pipeline runs.
50
53
 
51
54
  ### Claims System (Bran IR)
55
+
52
56
  - All findings are tracked as typed claims in `claims.json`
53
57
  - Every slash command that produces findings MUST append claims
54
58
  - Every slash command that produces output artifacts MUST run `wheat compile` first
@@ -56,6 +60,7 @@ This gives the user a chance to redirect before the full pipeline runs.
56
60
  - The compiler is the enforcement layer -- if it says blocked, no artifact gets produced
57
61
 
58
62
  ### Claim Types
63
+
59
64
  - `constraint` -- hard requirements, non-negotiable boundaries
60
65
  - `factual` -- verifiable statements about the world
61
66
  - `estimate` -- projections, approximations, ranges
@@ -64,6 +69,7 @@ This gives the user a chance to redirect before the full pipeline runs.
64
69
  - `feedback` -- stakeholder input, opinions, direction changes
65
70
 
66
71
  ### Evidence Tiers (lowest to highest)
72
+
67
73
  1. `stated` -- stakeholder said it, no verification
68
74
  2. `web` -- found online, not independently verified
69
75
  3. `documented` -- in source code, official docs, or ADRs
@@ -71,6 +77,7 @@ This gives the user a chance to redirect before the full pipeline runs.
71
77
  5. `production` -- measured from live production systems
72
78
 
73
79
  ### Claim ID Prefixes
80
+
74
81
  - `d###` -- define phase (from /init)
75
82
  - `r###` -- research phase (from /research)
76
83
  - `p###` -- prototype phase (from /prototype)
@@ -83,6 +90,7 @@ This gives the user a chance to redirect before the full pipeline runs.
83
90
  - `<sprint-slug>-<prefix>###` -- merged claims keep original prefix with sprint slug (from /merge)
84
91
 
85
92
  ### Next Command Hints
93
+
86
94
  Every slash command MUST end its output with a "Next steps" section suggesting 2-4 concrete commands the user could run next, based on the current sprint state. Use this decision tree:
87
95
 
88
96
  - Unresolved conflicts exist -> suggest `/resolve`
@@ -92,10 +100,13 @@ Every slash command MUST end its output with a "Next steps" section suggesting 2
92
100
  - Sprint is late-phase with gaps -> suggest `/blind-spot`
93
101
  - Claims untested against reality -> suggest `/calibrate`
94
102
  - Sprint ready for output -> suggest `/brief`, `/present`, or `/handoff`
103
+ - Sprint ready and external wiki configured -> suggest `/sync confluence`
104
+ - Need external context -> suggest `/pull deepwiki <repo>` or `/pull confluence`
95
105
  - Multiple sprints exist -> suggest `/merge`
96
106
  - Want to understand history -> suggest `/replay`
97
107
 
98
108
  Format:
109
+
99
110
  ```
100
111
  Next steps:
101
112
  /challenge p001 -- stress-test the zero-deps claim
@@ -104,18 +115,21 @@ Next steps:
104
115
  ```
105
116
 
106
117
  ### Git Discipline
118
+
107
119
  - Every slash command that modifies claims.json auto-commits
108
120
  - Commit format: `wheat: /<command> <summary> -- added/updated <claim IDs>`
109
121
  - `git log --oneline claims.json` = the sprint event log
110
122
  - Compilation certificate references the claims hash for reproducibility
111
123
 
112
124
  ### Output Artifacts
125
+
113
126
  - HTML files are self-contained (inline CSS/JS, no external deps)
114
127
  - Use the dark scroll-snap template for explainers and presentations
115
128
  - Use the dashboard template for status and comparisons
116
129
  - PDFs generated via `node build-pdf.js <file.md>`
117
130
 
118
131
  ### Directory Structure
132
+
119
133
  - `research/` -- topic explainers (HTML + MD)
120
134
  - `prototypes/` -- working proof-of-concepts
121
135
  - `evidence/` -- evaluation results and comparison dashboards
@@ -1,10 +1,11 @@
1
1
  # /blind-spot — Analyze What's NOT Being Claimed
2
2
 
3
- You are scanning the claim set for structural gaps — not what's wrong, but what's *missing*. Read CLAUDE.md for sprint context, claims.json for existing claims, and compilation.json for coverage data.
3
+ You are scanning the claim set for structural gaps — not what's wrong, but what's _missing_. Read CLAUDE.md for sprint context, claims.json for existing claims, and compilation.json for coverage data.
4
4
 
5
5
  ## Process
6
6
 
7
7
  1. **Run the compiler** to get fresh data:
8
+
8
9
  ```bash
9
10
  npx @grainulation/wheat compile --summary
10
11
  ```
@@ -14,20 +15,26 @@ You are scanning the claim set for structural gaps — not what's wrong, but wha
14
15
  3. **Analyze four categories of blind spots**:
15
16
 
16
17
  ### (a) Dependency gaps
18
+
17
19
  Scan claim content for topic-like nouns that are NOT in the current topic set. If claims reference concepts like "latency," "compliance," "security," "cost," or "performance" but no topic covers those, they're implicit dependencies never addressed.
18
20
 
19
21
  ### (b) Type monoculture
22
+
20
23
  Check `type_diversity` in coverage for each topic. Flag topics with < 2 distinct claim types. A topic with 5 factual claims but no risks is suspicious — where's the downside analysis?
21
24
 
22
25
  ### (c) Echo chambers
26
+
23
27
  Check `source_origins` and `source_count` in coverage for each topic. Flag topics where:
28
+
24
29
  - All claims come from a single source origin (e.g., all "research" with no external feedback)
25
30
  - Claims >= 3 but source_count == 1
26
31
 
27
32
  ### (d) Evidence ceiling
33
+
28
34
  Check `max_evidence` relative to the current sprint phase. If the sprint phase is `prototype` but a key topic is still at `stated` or `web` tier, that's a gap.
29
35
 
30
36
  Phase expectations:
37
+
31
38
  - `define`: `stated` is fine everywhere
32
39
  - `research`: key topics should be at least `web`
33
40
  - `prototype`: key topics should be at least `tested`
@@ -44,4 +51,4 @@ Phase expectations:
44
51
  - Remind them they can dismiss false-positive blind spots by adding to `meta.dismissed_blind_spots`
45
52
  - If no blind spots found, say so — a clean bill of health is valuable information
46
53
 
47
- $ARGUMENTS
54
+ $ARGUMENTS