@grainulation/wheat 1.0.5 → 1.0.7
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/README.md +57 -76
- package/compiler/wheat-compiler.js +12 -0
- package/lib/hints.js +214 -0
- package/lib/init.js +105 -8
- package/lib/serve-mcp.js +0 -1
- package/lib/update.js +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://www.npmjs.com/package/@grainulation/wheat"><img src="https://img.shields.io/npm/v/@grainulation/wheat" alt="npm version"></a> <a href="https://www.npmjs.com/package/@grainulation/wheat"><img src="https://img.shields.io/npm/dm/@grainulation/wheat" alt="npm downloads"></a> <a href="https://github.com/grainulation/wheat/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license"></a> <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/@grainulation/wheat" alt="node"></a> <a href="https://github.com/grainulation/wheat/actions"><img src="https://github.com/grainulation/wheat/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@grainulation/wheat"><img src="https://img.shields.io/npm/v/@grainulation/wheat?label=%40grainulation%2Fwheat" alt="npm version"></a> <a href="https://www.npmjs.com/package/@grainulation/wheat"><img src="https://img.shields.io/npm/dm/@grainulation/wheat" alt="npm downloads"></a> <a href="https://github.com/grainulation/wheat/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license"></a> <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/@grainulation/wheat" alt="node"></a> <a href="https://github.com/grainulation/wheat/actions"><img src="https://github.com/grainulation/wheat/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
7
7
|
<a href="https://deepwiki.com/grainulation/wheat"><img src="https://deepwiki.com/badge.svg" alt="Explore on DeepWiki"></a>
|
|
8
8
|
</p>
|
|
9
9
|
|
|
@@ -15,118 +15,99 @@ The migration will take months. It will cost real money. And right now, the deci
|
|
|
15
15
|
|
|
16
16
|
You'd never ship code without tests. Why ship a decision without validated evidence?
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Install
|
|
18
|
+
## Quick start
|
|
21
19
|
|
|
22
20
|
```bash
|
|
23
|
-
npx @grainulation/wheat
|
|
21
|
+
npx @grainulation/wheat "Should we migrate to GraphQL?"
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
One command. Zero prompts. Sprint ready in under 3 seconds.
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
Then open your AI coding tool and start investigating:
|
|
29
27
|
|
|
30
|
-
```
|
|
31
|
-
|
|
28
|
+
```
|
|
29
|
+
/research "GraphQL performance vs REST"
|
|
30
|
+
/challenge r003
|
|
31
|
+
/blind-spot
|
|
32
|
+
/brief
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Works with [Claude Code](https://claude.com/claude-code), [Cursor](https://cursor.com), [GitHub Copilot](https://github.com/features/copilot), or standalone via CLI.
|
|
35
36
|
|
|
36
|
-
##
|
|
37
|
+
## Full MCP integration (optional)
|
|
38
|
+
|
|
39
|
+
For native tool access in Claude Code:
|
|
37
40
|
|
|
38
41
|
```bash
|
|
39
|
-
npx @grainulation/wheat
|
|
42
|
+
claude mcp add wheat -- npx -y @grainulation/wheat mcp
|
|
40
43
|
```
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
claims.json # Typed assertions (the test suite for your decision)
|
|
46
|
-
CLAUDE.md # AI assistant configuration
|
|
47
|
-
.claude/commands/ # 18 slash commands
|
|
48
|
-
output/ # Where compiled artifacts land
|
|
49
|
-
```
|
|
45
|
+
This gives Claude direct access to wheat's claims engine — add-claim, compile, search, status — without shelling out.
|
|
50
46
|
|
|
51
|
-
|
|
47
|
+
## See it in 30 seconds
|
|
52
48
|
|
|
53
|
-
```
|
|
54
|
-
/
|
|
55
|
-
/prototype # build something testable
|
|
56
|
-
/challenge r003 # stress-test a finding
|
|
57
|
-
/blind-spot # what are we missing?
|
|
58
|
-
/brief # compile the decision document
|
|
49
|
+
```bash
|
|
50
|
+
npx @grainulation/wheat quickstart
|
|
59
51
|
```
|
|
60
52
|
|
|
53
|
+
Creates a demo sprint with pre-seeded claims, an intentional conflict, compiles everything, and opens a dashboard. The compiler flags the conflict and blocks output until it's resolved.
|
|
54
|
+
|
|
61
55
|
## How it works
|
|
62
56
|
|
|
63
|
-
Wheat is a continuous planning pipeline. Findings are validated as they come in
|
|
57
|
+
Wheat is a continuous planning pipeline. Findings are validated as they come in:
|
|
64
58
|
|
|
65
59
|
```
|
|
66
|
-
You investigate
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
You investigate → Claims accumulate → Compiler validates → Brief compiles
|
|
61
|
+
/research typed, evidence-graded 7-pass pipeline backed by evidence
|
|
62
|
+
/prototype
|
|
63
|
+
/challenge
|
|
70
64
|
```
|
|
71
65
|
|
|
72
|
-
**
|
|
66
|
+
**Claim types:** constraint, factual, estimate, risk, recommendation, feedback
|
|
73
67
|
|
|
74
|
-
**Evidence tiers
|
|
68
|
+
**Evidence tiers:** stated → web → documented → tested → production
|
|
75
69
|
|
|
76
|
-
The compiler catches conflicts,
|
|
70
|
+
The compiler catches conflicts, flags weak evidence, and blocks the build when issues exist. You can't ship a brief built on contradictions — same as you can't merge with failing tests.
|
|
77
71
|
|
|
78
72
|
## Commands
|
|
79
73
|
|
|
80
|
-
| Command
|
|
81
|
-
|
|
82
|
-
| `/
|
|
83
|
-
| `/
|
|
84
|
-
| `/
|
|
85
|
-
| `/
|
|
86
|
-
| `/
|
|
87
|
-
| `/
|
|
88
|
-
| `/status`
|
|
89
|
-
| `/
|
|
90
|
-
| `/
|
|
91
|
-
| `/feedback` | Incorporate stakeholder input |
|
|
92
|
-
| `/resolve` | Adjudicate conflicts between claims |
|
|
93
|
-
| `/replay` | Time-travel through sprint history |
|
|
94
|
-
| `/calibrate` | Score predictions against actual outcomes |
|
|
95
|
-
| `/handoff` | Package sprint for knowledge transfer |
|
|
96
|
-
| `/merge <path>` | Combine findings across sprints |
|
|
97
|
-
| `/connect <type>` | Link external tools (Jira, docs, etc.) |
|
|
98
|
-
| `/evaluate` | Test claims against reality, resolve conflicts |
|
|
99
|
-
| `/next` | Route next steps through Farmer (mobile feedback) |
|
|
74
|
+
| Command | What it does |
|
|
75
|
+
|---------|-------------|
|
|
76
|
+
| `/research <topic>` | Deep dive on a topic, creates claims |
|
|
77
|
+
| `/prototype` | Build something testable |
|
|
78
|
+
| `/challenge <id>` | Adversarial stress-test of a claim |
|
|
79
|
+
| `/witness <id> <url>` | External corroboration |
|
|
80
|
+
| `/blind-spot` | Find gaps in your investigation |
|
|
81
|
+
| `/brief` | Compile the decision document |
|
|
82
|
+
| `/status` | Sprint dashboard |
|
|
83
|
+
| `/present` | Generate a stakeholder presentation |
|
|
84
|
+
| `/resolve` | Adjudicate conflicts between claims |
|
|
100
85
|
|
|
101
86
|
## Guard rails
|
|
102
87
|
|
|
103
|
-
Wheat installs two guard mechanisms:
|
|
104
|
-
|
|
105
|
-
1. **Git pre-commit hook** -- prevents committing broken `claims.json`
|
|
106
|
-
2. **Claude Code guard hook** -- prevents generating output artifacts from stale or blocked compilations
|
|
107
|
-
|
|
108
|
-
Both are optional and can be removed.
|
|
109
|
-
|
|
110
|
-
## Works in any repo
|
|
88
|
+
Wheat installs two optional guard mechanisms:
|
|
111
89
|
|
|
112
|
-
|
|
90
|
+
1. **Git pre-commit hook** — prevents committing broken claims
|
|
91
|
+
2. **Claude Code guard hook** — prevents generating output from stale compilations
|
|
113
92
|
|
|
114
|
-
##
|
|
93
|
+
## Works everywhere
|
|
115
94
|
|
|
116
|
-
|
|
95
|
+
Wheat doesn't care what language you use or what AI tool you run. Your Scala project, your Python monorepo, your Flutter app — wheat works the same everywhere. Node 20+ is the only requirement. Zero npm dependencies.
|
|
117
96
|
|
|
118
97
|
## Part of the grainulation ecosystem
|
|
119
98
|
|
|
120
|
-
| Tool
|
|
121
|
-
|
|
122
|
-
| **wheat**
|
|
123
|
-
| [farmer](https://github.com/grainulation/farmer)
|
|
124
|
-
| [barn](https://github.com/grainulation/barn)
|
|
125
|
-
| [mill](https://github.com/grainulation/mill)
|
|
126
|
-
| [silo](https://github.com/grainulation/silo)
|
|
127
|
-
| [harvest](https://github.com/grainulation/harvest)
|
|
128
|
-
| [orchard](https://github.com/grainulation/orchard)
|
|
129
|
-
| [grainulation](https://github.com/grainulation/grainulation) | Unified CLI
|
|
99
|
+
| Tool | Role |
|
|
100
|
+
|------|------|
|
|
101
|
+
| **wheat** | Research engine — grow structured evidence |
|
|
102
|
+
| [farmer](https://github.com/grainulation/farmer) | Permission dashboard — approve AI actions in real time |
|
|
103
|
+
| [barn](https://github.com/grainulation/barn) | Shared tools — templates, validators, sprint detection |
|
|
104
|
+
| [mill](https://github.com/grainulation/mill) | Format conversion — export to PDF, CSV, slides |
|
|
105
|
+
| [silo](https://github.com/grainulation/silo) | Knowledge storage — reusable claim libraries |
|
|
106
|
+
| [harvest](https://github.com/grainulation/harvest) | Analytics — cross-sprint patterns and prediction scoring |
|
|
107
|
+
| [orchard](https://github.com/grainulation/orchard) | Orchestration — multi-sprint coordination |
|
|
108
|
+
| [grainulation](https://github.com/grainulation/grainulation) | Unified CLI — single entry point to the ecosystem |
|
|
109
|
+
|
|
110
|
+
**You don't need all eight.** Start with wheat. That's it.
|
|
130
111
|
|
|
131
112
|
## License
|
|
132
113
|
|
|
@@ -19,6 +19,7 @@ import crypto from "crypto";
|
|
|
19
19
|
import path from "path";
|
|
20
20
|
|
|
21
21
|
import { fileURLToPath } from "url";
|
|
22
|
+
import { maybeHint } from "../lib/hints.js";
|
|
22
23
|
|
|
23
24
|
// Sprint detection — git-based, no config pointer needed (p013/f001)
|
|
24
25
|
import { detectSprints } from "./detect-sprints.js";
|
|
@@ -1195,6 +1196,17 @@ Options:
|
|
|
1195
1196
|
`Certificate: ${c.compilation_certificate.input_hash.slice(0, 20)}...`
|
|
1196
1197
|
);
|
|
1197
1198
|
|
|
1199
|
+
if (!jsonFlag) {
|
|
1200
|
+
try {
|
|
1201
|
+
const hint = maybeHint(compilation, { context: "compile" });
|
|
1202
|
+
if (hint) {
|
|
1203
|
+
process.stderr.write("\n" + hint + "\n");
|
|
1204
|
+
}
|
|
1205
|
+
} catch {
|
|
1206
|
+
// hints are non-critical — fail silently
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1198
1210
|
if (jsonFlag) {
|
|
1199
1211
|
console.log(JSON.stringify(c, null, 2));
|
|
1200
1212
|
}
|
package/lib/hints.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hints.js — Ecosystem cross-promotion hints
|
|
3
|
+
*
|
|
4
|
+
* Contextual, one-line, non-blocking hints that surface Grainulation
|
|
5
|
+
* ecosystem tools at the moment the user's workflow makes them relevant.
|
|
6
|
+
*
|
|
7
|
+
* Shows max 1 hint per invocation on stderr.
|
|
8
|
+
* Tracks shown hints in ~/.grainulation/hints.json
|
|
9
|
+
* Fails silently on any I/O error — never blocks the CLI.
|
|
10
|
+
*
|
|
11
|
+
* Zero dependencies.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
|
|
18
|
+
const HINTS_DIR = path.join(homedir(), ".grainulation");
|
|
19
|
+
const HINTS_FILE = path.join(HINTS_DIR, "hints.json");
|
|
20
|
+
const MAX_SHOWS = 3;
|
|
21
|
+
const COOLDOWN_MS = 24 * 60 * 60 * 1000; // 24h
|
|
22
|
+
|
|
23
|
+
// ─── State I/O (sync, fail-silent) ──────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
function readHints() {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(readFileSync(HINTS_FILE, "utf8"));
|
|
28
|
+
} catch {
|
|
29
|
+
return { shown: {}, dismissed: [], installed: [] };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function writeHints(data) {
|
|
34
|
+
try {
|
|
35
|
+
mkdirSync(HINTS_DIR, { recursive: true });
|
|
36
|
+
writeFileSync(HINTS_FILE, JSON.stringify(data, null, 2) + "\n");
|
|
37
|
+
} catch {
|
|
38
|
+
// fail silently
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function shouldSuppress(product, state) {
|
|
43
|
+
if ((state.dismissed || []).includes(product)) return true;
|
|
44
|
+
if ((state.installed || []).includes(product)) return true;
|
|
45
|
+
const record = (state.shown || {})[product];
|
|
46
|
+
if (!record) return false;
|
|
47
|
+
if (record.count >= MAX_SHOWS) return true;
|
|
48
|
+
if (record.last && Date.now() - new Date(record.last).getTime() < COOLDOWN_MS) return true;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ─── Triggers (ordered by priority, first match wins) ────────────────────────
|
|
53
|
+
|
|
54
|
+
const TRIGGERS = [
|
|
55
|
+
// 1. >20 claims → harvest
|
|
56
|
+
function detectHarvestFromClaims(compilation) {
|
|
57
|
+
const count = compilation?.claims?.length || 0;
|
|
58
|
+
if (count > 20) {
|
|
59
|
+
return {
|
|
60
|
+
product: "harvest",
|
|
61
|
+
message: "btw: harvest can visualize claim growth across sprints (npx @grainulation/harvest)",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// 2. >3 topics → orchard
|
|
68
|
+
function detectOrchardFromTopics(compilation) {
|
|
69
|
+
const claims = compilation?.claims || [];
|
|
70
|
+
const topics = new Set(claims.map((c) => c.topic).filter(Boolean));
|
|
71
|
+
if (topics.size > 3) {
|
|
72
|
+
return {
|
|
73
|
+
product: "orchard",
|
|
74
|
+
message: "btw: orchard can run multiple research sprints in parallel (npx @grainulation/orchard)",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// 3. brief/present context → mill
|
|
81
|
+
function detectMillFromBrief(compilation, context) {
|
|
82
|
+
if (context === "brief" || context === "present") {
|
|
83
|
+
return {
|
|
84
|
+
product: "mill",
|
|
85
|
+
message: "btw: mill can export this as PDF, slides, or Confluence (npx @grainulation/mill)",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// 4. first ever compile → farmer
|
|
92
|
+
function detectFarmerFirstCompile(compilation, context) {
|
|
93
|
+
if (context !== "compile") return null;
|
|
94
|
+
// Check if this looks like a first compile (no prior hint state)
|
|
95
|
+
try {
|
|
96
|
+
const state = readHints();
|
|
97
|
+
const totalShows = Object.values(state.shown || {}).reduce(
|
|
98
|
+
(sum, r) => sum + (r.count || 0),
|
|
99
|
+
0
|
|
100
|
+
);
|
|
101
|
+
if (totalShows === 0) {
|
|
102
|
+
return {
|
|
103
|
+
product: "farmer",
|
|
104
|
+
message: "btw: farmer gives you a mobile dashboard for AI permissions (npx @grainulation/farmer)",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
// fail silently
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// 5. >5 sprints → silo
|
|
114
|
+
function detectSiloFromSprints(compilation) {
|
|
115
|
+
const sprintCount = compilation?.sprints?.length || 0;
|
|
116
|
+
if (sprintCount > 5) {
|
|
117
|
+
return {
|
|
118
|
+
product: "silo",
|
|
119
|
+
message: "btw: silo stores reusable claim libraries across projects (npx @grainulation/silo)",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
// ─── Format ──────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
function formatHint(message) {
|
|
129
|
+
return ` \x1b[2m${message}\x1b[0m`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Evaluate trigger conditions and return a formatted hint string,
|
|
136
|
+
* or null if no hint should be shown.
|
|
137
|
+
*
|
|
138
|
+
* @param {object} compilation - The compilation.json data
|
|
139
|
+
* @param {object} opts
|
|
140
|
+
* @param {string} opts.context - "compile" | "init" | "brief" | "present"
|
|
141
|
+
* @returns {string|null} Formatted hint for stderr, or null
|
|
142
|
+
*/
|
|
143
|
+
export function maybeHint(compilation, opts = {}) {
|
|
144
|
+
try {
|
|
145
|
+
// Global suppression
|
|
146
|
+
if (process.env.WHEAT_BTW === "off") return null;
|
|
147
|
+
if (process.env.WHEAT_NO_HINTS === "1") return null;
|
|
148
|
+
if (process.env.CI) return null;
|
|
149
|
+
if (process.argv.includes("--quiet")) return null;
|
|
150
|
+
if (process.argv.includes("--json")) return null;
|
|
151
|
+
if (!process.stderr.isTTY) return null;
|
|
152
|
+
|
|
153
|
+
const state = readHints();
|
|
154
|
+
|
|
155
|
+
for (const trigger of TRIGGERS) {
|
|
156
|
+
const result = trigger(compilation, opts.context);
|
|
157
|
+
if (!result) continue;
|
|
158
|
+
if (shouldSuppress(result.product, state)) continue;
|
|
159
|
+
|
|
160
|
+
// Record the show
|
|
161
|
+
if (!state.shown) state.shown = {};
|
|
162
|
+
if (!state.shown[result.product]) state.shown[result.product] = { count: 0 };
|
|
163
|
+
state.shown[result.product].count++;
|
|
164
|
+
state.shown[result.product].last = new Date().toISOString();
|
|
165
|
+
writeHints(state);
|
|
166
|
+
|
|
167
|
+
return formatHint(result.message);
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
// fail silently — never block the main output
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Record that a product has been installed/connected.
|
|
177
|
+
* @param {string} product
|
|
178
|
+
*/
|
|
179
|
+
export function markInstalled(product) {
|
|
180
|
+
try {
|
|
181
|
+
const state = readHints();
|
|
182
|
+
if (!state.installed) state.installed = [];
|
|
183
|
+
if (!state.installed.includes(product)) {
|
|
184
|
+
state.installed.push(product);
|
|
185
|
+
writeHints(state);
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// fail silently
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Record that the user dismissed hints for a product.
|
|
194
|
+
* @param {string} product
|
|
195
|
+
*/
|
|
196
|
+
export function dismiss(product) {
|
|
197
|
+
try {
|
|
198
|
+
const state = readHints();
|
|
199
|
+
if (!state.dismissed) state.dismissed = [];
|
|
200
|
+
if (!state.dismissed.includes(product)) {
|
|
201
|
+
state.dismissed.push(product);
|
|
202
|
+
writeHints(state);
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
// fail silently
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Reset all hint state (for testing).
|
|
211
|
+
*/
|
|
212
|
+
export function reset() {
|
|
213
|
+
writeHints({ shown: {}, dismissed: [], installed: [] });
|
|
214
|
+
}
|
package/lib/init.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Creates in the TARGET repo:
|
|
10
10
|
* - claims.json (seeded with constraint claims)
|
|
11
11
|
* - CLAUDE.md (sprint configuration for Claude Code)
|
|
12
|
-
* - .claude/commands/*.md (slash commands)
|
|
12
|
+
* - .claude/commands/wheat/*.md (slash commands)
|
|
13
13
|
* - wheat.config.json (local config pointing back to package)
|
|
14
14
|
*
|
|
15
15
|
* Zero npm dependencies.
|
|
@@ -21,6 +21,7 @@ import readline from "readline";
|
|
|
21
21
|
import { execFileSync } from "child_process";
|
|
22
22
|
import { fileURLToPath } from "url";
|
|
23
23
|
import { DEFAULTS, outputMode } from "./defaults.js";
|
|
24
|
+
import { maybeHint } from "./hints.js";
|
|
24
25
|
|
|
25
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
26
27
|
const __dirname = path.dirname(__filename);
|
|
@@ -233,9 +234,9 @@ function buildClaudeMd(meta) {
|
|
|
233
234
|
|
|
234
235
|
function copyCommands(dir) {
|
|
235
236
|
const srcDir = path.join(packageRoot(), "templates", "commands");
|
|
236
|
-
const destDir = target(dir, ".claude", "commands");
|
|
237
|
+
const destDir = target(dir, ".claude", "commands", "wheat");
|
|
237
238
|
|
|
238
|
-
// Create .claude/commands/ if it doesn't exist
|
|
239
|
+
// Create .claude/commands/wheat/ if it doesn't exist
|
|
239
240
|
fs.mkdirSync(destDir, { recursive: true });
|
|
240
241
|
|
|
241
242
|
let copied = 0;
|
|
@@ -248,7 +249,7 @@ function copyCommands(dir) {
|
|
|
248
249
|
|
|
249
250
|
// Don't overwrite existing commands (user may have customized)
|
|
250
251
|
if (fs.existsSync(dest)) {
|
|
251
|
-
console.log(` Skipped .claude/commands/${file} (already exists)`);
|
|
252
|
+
console.log(` Skipped .claude/commands/wheat/${file} (already exists)`);
|
|
252
253
|
continue;
|
|
253
254
|
}
|
|
254
255
|
|
|
@@ -337,6 +338,86 @@ fi
|
|
|
337
338
|
}
|
|
338
339
|
}
|
|
339
340
|
|
|
341
|
+
// ─── .mcp.json & AGENTS.md ──────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
function writeMcpJson(dir) {
|
|
344
|
+
const mcpPath = target(dir, ".mcp.json");
|
|
345
|
+
const wheatEntry = {
|
|
346
|
+
command: "npx",
|
|
347
|
+
args: ["-y", "@grainulation/wheat", "mcp"],
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
let config = { mcpServers: {} };
|
|
351
|
+
if (fs.existsSync(mcpPath)) {
|
|
352
|
+
try {
|
|
353
|
+
config = JSON.parse(fs.readFileSync(mcpPath, "utf8"));
|
|
354
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
355
|
+
} catch {
|
|
356
|
+
// Corrupted file — overwrite with fresh config
|
|
357
|
+
config = { mcpServers: {} };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
config.mcpServers.wheat = wheatEntry;
|
|
362
|
+
fs.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n");
|
|
363
|
+
console.log(
|
|
364
|
+
" \x1b[32m+\x1b[0m .mcp.json (Claude Code MCP auto-discovery)"
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function writeAgentsMd(dir, meta) {
|
|
369
|
+
const agentsPath = target(dir, "AGENTS.md");
|
|
370
|
+
const constraintList =
|
|
371
|
+
meta.constraints.length > 0
|
|
372
|
+
? meta.constraints.map((c) => `- ${c}`).join("\n")
|
|
373
|
+
: "- (none specified)";
|
|
374
|
+
|
|
375
|
+
const section = `# Wheat Research Sprint
|
|
376
|
+
|
|
377
|
+
**Question:** ${meta.question}
|
|
378
|
+
|
|
379
|
+
**Audience:** ${meta.audience.join(", ")}
|
|
380
|
+
|
|
381
|
+
**Constraints:**
|
|
382
|
+
${constraintList}
|
|
383
|
+
|
|
384
|
+
**Done looks like:** ${meta.doneCriteria || "A recommendation with evidence"}
|
|
385
|
+
|
|
386
|
+
## Claims System
|
|
387
|
+
|
|
388
|
+
All findings are tracked as typed claims in \`claims.json\`. Claim types: constraint, factual, estimate, risk, recommendation, feedback. Evidence tiers (low to high): stated, web, documented, tested, production.
|
|
389
|
+
|
|
390
|
+
## Key Commands
|
|
391
|
+
|
|
392
|
+
- \`wheat init\` — bootstrap a research sprint
|
|
393
|
+
- \`wheat compile\` — validate and compile claims
|
|
394
|
+
- \`wheat status\` — sprint health dashboard
|
|
395
|
+
- \`wheat search <query>\` — search claims
|
|
396
|
+
- \`wheat add-claim\` — add a new claim
|
|
397
|
+
- \`wheat resolve <id>\` — resolve a conflicting claim
|
|
398
|
+
`;
|
|
399
|
+
|
|
400
|
+
if (fs.existsSync(agentsPath)) {
|
|
401
|
+
const existing = fs.readFileSync(agentsPath, "utf8");
|
|
402
|
+
if (existing.includes("# Wheat Research Sprint")) {
|
|
403
|
+
console.log(
|
|
404
|
+
" \x1b[34m-\x1b[0m AGENTS.md (wheat section already present)"
|
|
405
|
+
);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
// Append wheat section
|
|
409
|
+
fs.appendFileSync(agentsPath, "\n" + section);
|
|
410
|
+
console.log(
|
|
411
|
+
" \x1b[32m+\x1b[0m AGENTS.md (appended wheat section)"
|
|
412
|
+
);
|
|
413
|
+
} else {
|
|
414
|
+
fs.writeFileSync(agentsPath, section);
|
|
415
|
+
console.log(
|
|
416
|
+
" \x1b[32m+\x1b[0m AGENTS.md (universal AI instructions)"
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
340
421
|
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
341
422
|
|
|
342
423
|
export async function run(dir, args) {
|
|
@@ -501,12 +582,18 @@ export async function run(dir, args) {
|
|
|
501
582
|
fs.writeFileSync(claudePath, claudeMd);
|
|
502
583
|
console.log(" \x1b[32m+\x1b[0m CLAUDE.md");
|
|
503
584
|
|
|
504
|
-
// .claude/commands/
|
|
585
|
+
// .claude/commands/wheat/
|
|
505
586
|
const copied = copyCommands(dir);
|
|
506
587
|
console.log(
|
|
507
|
-
` \x1b[32m+\x1b[0m .claude/commands/ (${copied} commands installed)`
|
|
588
|
+
` \x1b[32m+\x1b[0m .claude/commands/wheat/ (${copied} commands installed)`
|
|
508
589
|
);
|
|
509
590
|
|
|
591
|
+
// .mcp.json (Claude Code MCP auto-discovery)
|
|
592
|
+
writeMcpJson(dir);
|
|
593
|
+
|
|
594
|
+
// AGENTS.md (universal AI instructions)
|
|
595
|
+
writeAgentsMd(dir, meta);
|
|
596
|
+
|
|
510
597
|
// Create output directories
|
|
511
598
|
const dirs = ["output", "research", "prototypes", "evidence"];
|
|
512
599
|
for (const d of dirs) {
|
|
@@ -530,7 +617,7 @@ export async function run(dir, args) {
|
|
|
530
617
|
constraints: meta.constraints.length,
|
|
531
618
|
done_criteria: meta.doneCriteria || null,
|
|
532
619
|
claims_seeded: claims.claims.length,
|
|
533
|
-
files_created: ["claims.json", "CLAUDE.md", ".claude/commands/"],
|
|
620
|
+
files_created: ["claims.json", "CLAUDE.md", ".claude/commands/wheat/", ".mcp.json", "AGENTS.md"],
|
|
534
621
|
dir,
|
|
535
622
|
})
|
|
536
623
|
);
|
|
@@ -556,6 +643,10 @@ export async function run(dir, args) {
|
|
|
556
643
|
console.log(` \x1b[1m\x1b[33mwheat\x1b[0m — sprint created`);
|
|
557
644
|
console.log(` ${meta.question}`);
|
|
558
645
|
console.log(` ${claims.claims.length} constraint(s) seeded`);
|
|
646
|
+
try {
|
|
647
|
+
const hint = maybeHint({ claims: claims.claims }, { context: "init" });
|
|
648
|
+
if (hint) process.stderr.write(hint + "\n");
|
|
649
|
+
} catch { /* non-critical */ }
|
|
559
650
|
console.log();
|
|
560
651
|
return;
|
|
561
652
|
}
|
|
@@ -571,7 +662,9 @@ export async function run(dir, args) {
|
|
|
571
662
|
console.log(" Created:");
|
|
572
663
|
console.log(" claims.json Your evidence database");
|
|
573
664
|
console.log(" CLAUDE.md AI assistant configuration");
|
|
574
|
-
console.log(" .claude/commands/
|
|
665
|
+
console.log(" .claude/commands/wheat/ slash commands for Claude Code");
|
|
666
|
+
console.log(" .mcp.json Claude Code MCP auto-discovery");
|
|
667
|
+
console.log(" AGENTS.md Universal AI instructions");
|
|
575
668
|
console.log(" output/ Where compiled artifacts land");
|
|
576
669
|
console.log();
|
|
577
670
|
console.log(" Next steps:");
|
|
@@ -582,5 +675,9 @@ export async function run(dir, args) {
|
|
|
582
675
|
);
|
|
583
676
|
console.log();
|
|
584
677
|
console.log(" Trust the process. The evidence will compound.");
|
|
678
|
+
try {
|
|
679
|
+
const hint = maybeHint({ claims: claims.claims }, { context: "init" });
|
|
680
|
+
if (hint) process.stderr.write(hint + "\n");
|
|
681
|
+
} catch { /* non-critical */ }
|
|
585
682
|
console.log();
|
|
586
683
|
}
|
package/lib/serve-mcp.js
CHANGED
package/lib/update.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* wheat update — Copy/update slash commands to .claude/commands/
|
|
2
|
+
* wheat update — Copy/update slash commands to .claude/commands/wheat/
|
|
3
3
|
*
|
|
4
4
|
* Copies command templates from the installed package into the user's
|
|
5
|
-
* .claude/commands/ directory. Existing files can be overwritten with --force.
|
|
5
|
+
* .claude/commands/wheat/ directory. Existing files can be overwritten with --force.
|
|
6
6
|
*
|
|
7
7
|
* Zero npm dependencies.
|
|
8
8
|
*/
|
|
@@ -21,7 +21,7 @@ function packageRoot() {
|
|
|
21
21
|
export async function run(dir, args) {
|
|
22
22
|
const force = args.includes("--force");
|
|
23
23
|
const srcDir = path.join(packageRoot(), "templates", "commands");
|
|
24
|
-
const destDir = path.join(dir, ".claude", "commands");
|
|
24
|
+
const destDir = path.join(dir, ".claude", "commands", "wheat");
|
|
25
25
|
|
|
26
26
|
fs.mkdirSync(destDir, { recursive: true });
|
|
27
27
|
|
|
@@ -34,7 +34,7 @@ export async function run(dir, args) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
console.log();
|
|
37
|
-
console.log(` Updating .claude/commands/ (${files.length} commands)`);
|
|
37
|
+
console.log(` Updating .claude/commands/wheat/ (${files.length} commands)`);
|
|
38
38
|
console.log();
|
|
39
39
|
|
|
40
40
|
let updated = 0;
|
package/package.json
CHANGED