@grainulation/wheat 1.0.3 → 1.0.4
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/LICENSE +1 -1
- package/README.md +32 -31
- package/bin/wheat.js +47 -36
- package/compiler/detect-sprints.js +108 -66
- package/compiler/generate-manifest.js +116 -69
- package/compiler/wheat-compiler.js +763 -471
- package/lib/compiler.js +11 -6
- package/lib/connect.js +273 -134
- package/lib/disconnect.js +61 -40
- package/lib/guard.js +20 -17
- package/lib/index.js +8 -8
- package/lib/init.js +217 -142
- package/lib/install-prompt.js +26 -26
- package/lib/load-claims.js +88 -0
- package/lib/quickstart.js +225 -111
- package/lib/serve-mcp.js +495 -180
- package/lib/server.js +198 -111
- package/lib/stats.js +65 -39
- package/lib/status.js +65 -34
- package/lib/update.js +13 -11
- package/package.json +8 -4
- package/templates/claude.md +31 -17
- package/templates/commands/blind-spot.md +9 -2
- package/templates/commands/brief.md +11 -1
- package/templates/commands/calibrate.md +3 -1
- package/templates/commands/challenge.md +4 -1
- package/templates/commands/connect.md +12 -1
- package/templates/commands/evaluate.md +4 -0
- package/templates/commands/feedback.md +3 -1
- package/templates/commands/handoff.md +11 -7
- package/templates/commands/init.md +4 -1
- package/templates/commands/merge.md +4 -1
- package/templates/commands/next.md +1 -0
- package/templates/commands/present.md +3 -0
- package/templates/commands/prototype.md +2 -0
- package/templates/commands/pull.md +103 -0
- package/templates/commands/replay.md +8 -0
- package/templates/commands/research.md +1 -0
- package/templates/commands/resolve.md +4 -1
- package/templates/commands/status.md +4 -0
- package/templates/commands/sync.md +94 -0
- 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
|
|
13
|
-
import path from
|
|
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,
|
|
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,
|
|
29
|
+
const examplesDir = path.join(dir, "examples");
|
|
38
30
|
if (fs.existsSync(examplesDir)) {
|
|
39
31
|
try {
|
|
40
|
-
for (const entry of fs.readdirSync(examplesDir, {
|
|
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,
|
|
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 {
|
|
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(
|
|
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
|
|
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 ===
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
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 ||
|
|
109
|
-
phase: meta.phase ||
|
|
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(
|
|
119
|
-
console.log(` ${
|
|
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(
|
|
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(
|
|
136
|
-
const phaseOrder = [
|
|
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(
|
|
148
|
-
const typeOrder = [
|
|
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(
|
|
160
|
-
const evidenceOrder = [
|
|
161
|
-
const allEvidence = [
|
|
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(
|
|
190
|
+
console.log(" \x1b[1mPer-sprint breakdown:\x1b[0m");
|
|
173
191
|
for (const s of sprintSummaries) {
|
|
174
|
-
console.log(
|
|
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(
|
|
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(
|
|
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
|
|
11
|
-
import path from
|
|
12
|
-
import { compile } from
|
|
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(
|
|
16
|
-
const claimsPath = path.join(dir,
|
|
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(
|
|
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(
|
|
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 =
|
|
31
|
-
|
|
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 ===
|
|
37
|
-
const superseded = claims.filter(c => c.status ===
|
|
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 ||
|
|
60
|
+
phase: meta.phase || "unknown",
|
|
47
61
|
status: compilation.status,
|
|
48
|
-
claims: {
|
|
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(
|
|
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 =
|
|
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 ||
|
|
72
|
-
console.log(` ${
|
|
73
|
-
console.log(` Phase: ${meta.phase ||
|
|
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(
|
|
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)
|
|
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(
|
|
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(
|
|
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(
|
|
139
|
+
console.log(" Next steps:");
|
|
111
140
|
|
|
112
141
|
if (compilation.conflicts?.unresolved?.length > 0) {
|
|
113
|
-
console.log(
|
|
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 ===
|
|
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(
|
|
150
|
+
console.log(
|
|
151
|
+
` /research — strengthen weak topics: ${weakTopics.join(", ")}`
|
|
152
|
+
);
|
|
122
153
|
}
|
|
123
154
|
|
|
124
|
-
if (meta.phase ===
|
|
125
|
-
console.log(
|
|
126
|
-
} else if (meta.phase ===
|
|
127
|
-
console.log(
|
|
128
|
-
} else if (meta.phase ===
|
|
129
|
-
if (compilation.status ===
|
|
130
|
-
console.log(
|
|
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
|
|
11
|
-
import path from
|
|
12
|
-
import { fileURLToPath } from
|
|
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(
|
|
23
|
-
const srcDir = path.join(packageRoot(),
|
|
24
|
-
const destDir = path.join(dir,
|
|
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(
|
|
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,
|
|
50
|
-
const destContent = fs.readFileSync(dest,
|
|
49
|
+
const srcContent = fs.readFileSync(src, "utf8");
|
|
50
|
+
const destContent = fs.readFileSync(dest, "utf8");
|
|
51
51
|
if (srcContent !== destContent) {
|
|
52
|
-
console.log(
|
|
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
|
+
"version": "1.0.4",
|
|
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": ">=
|
|
52
|
+
"node": ">=20"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@changesets/cli": "^2.30.0"
|
|
52
56
|
}
|
|
53
57
|
}
|
package/templates/claude.md
CHANGED
|
@@ -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...
|
|
27
|
-
|
|
28
|
-
| "look into X", "what about X", "explore X", "how does X work"
|
|
29
|
-
| "build X", "try X", "make a quick X", "test whether X"
|
|
30
|
-
| "is p001 really true?", "I doubt X", "what if X is wrong"
|
|
31
|
-
| "check this: <url>", "does <url> support X", "verify X against <url>"
|
|
32
|
-
| "what are we missing", "any gaps?", "what haven't we considered"
|
|
33
|
-
| "where are we", "what's the status", "show me the dashboard"
|
|
34
|
-
| "write it up", "give me the recommendation", "summarize for the team"
|
|
35
|
-
| "make slides", "prepare for the meeting"
|
|
36
|
-
| "someone else is taking over", "hand this off", "document for successor" | `/handoff`
|
|
37
|
-
| "combine with the other sprint", "merge these"
|
|
38
|
-
| "how did we get here", "show the history", "what changed over time"
|
|
39
|
-
| "we shipped, here's what happened", "actual results were X"
|
|
40
|
-
| "the stakeholder said X", "new constraint: X", "change of direction"
|
|
41
|
-
| "resolve the conflict", "pick between X and Y"
|
|
42
|
-
| "connect to <repo/jira/docs>"
|
|
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
|
|
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
|