@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/install-prompt.js
CHANGED
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
* nudging. This is an unclaimed UX innovation.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { readFileSync, writeFileSync, mkdirSync } from
|
|
17
|
-
import path from
|
|
18
|
-
import { homedir } from
|
|
16
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { homedir } from "os";
|
|
19
19
|
|
|
20
|
-
const USAGE_DIR = path.join(homedir(),
|
|
21
|
-
const USAGE_FILE = path.join(USAGE_DIR,
|
|
20
|
+
const USAGE_DIR = path.join(homedir(), ".grainulation");
|
|
21
|
+
const USAGE_FILE = path.join(USAGE_DIR, "usage.json");
|
|
22
22
|
|
|
23
23
|
const THRESHOLDS = {
|
|
24
24
|
suggest_global: 3,
|
|
@@ -30,7 +30,7 @@ const THRESHOLDS = {
|
|
|
30
30
|
|
|
31
31
|
function readUsage() {
|
|
32
32
|
try {
|
|
33
|
-
return JSON.parse(readFileSync(USAGE_FILE,
|
|
33
|
+
return JSON.parse(readFileSync(USAGE_FILE, "utf8"));
|
|
34
34
|
} catch {
|
|
35
35
|
return { commands: {}, prompted: false, installed: false };
|
|
36
36
|
}
|
|
@@ -39,7 +39,7 @@ function readUsage() {
|
|
|
39
39
|
function writeUsage(data) {
|
|
40
40
|
try {
|
|
41
41
|
mkdirSync(USAGE_DIR, { recursive: true });
|
|
42
|
-
writeFileSync(USAGE_FILE, JSON.stringify(data, null, 2) +
|
|
42
|
+
writeFileSync(USAGE_FILE, JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
43
43
|
} catch {
|
|
44
44
|
// fail silently
|
|
45
45
|
}
|
|
@@ -50,8 +50,8 @@ function writeUsage(data) {
|
|
|
50
50
|
* npx stores executables in a temporary _npx cache directory.
|
|
51
51
|
*/
|
|
52
52
|
function isNpxInvocation() {
|
|
53
|
-
const execPath = process.argv[1] ||
|
|
54
|
-
return execPath.includes(
|
|
53
|
+
const execPath = process.argv[1] || "";
|
|
54
|
+
return execPath.includes("_npx");
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -66,23 +66,23 @@ function isNpxInvocation() {
|
|
|
66
66
|
* (isNpxInvocation returns false, so prompts are already suppressed).
|
|
67
67
|
*/
|
|
68
68
|
function isInstalled() {
|
|
69
|
-
const execPath = process.argv[1] ||
|
|
69
|
+
const execPath = process.argv[1] || "";
|
|
70
70
|
|
|
71
71
|
// Local install: running from node_modules
|
|
72
|
-
if (execPath.includes(
|
|
72
|
+
if (execPath.includes("node_modules") && !execPath.includes("_npx")) {
|
|
73
73
|
return true;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Global install: binary is in a well-known global prefix
|
|
77
77
|
// (not npx, not node_modules — must be a permanent install)
|
|
78
|
-
if (!execPath.includes(
|
|
78
|
+
if (!execPath.includes("_npx") && !execPath.includes("node_modules")) {
|
|
79
79
|
const knownGlobalDirs = [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
path.join(homedir(),
|
|
83
|
-
path.join(homedir(),
|
|
84
|
-
path.join(homedir(),
|
|
85
|
-
path.join(homedir(),
|
|
80
|
+
"/usr/local/bin",
|
|
81
|
+
"/usr/bin",
|
|
82
|
+
path.join(homedir(), ".npm-global"),
|
|
83
|
+
path.join(homedir(), ".nvm"),
|
|
84
|
+
path.join(homedir(), ".volta"),
|
|
85
|
+
path.join(homedir(), ".local", "bin"),
|
|
86
86
|
];
|
|
87
87
|
for (const prefix of knownGlobalDirs) {
|
|
88
88
|
if (execPath.startsWith(prefix)) return true;
|
|
@@ -141,8 +141,8 @@ export function track(command) {
|
|
|
141
141
|
*/
|
|
142
142
|
export function maybePrompt(command) {
|
|
143
143
|
try {
|
|
144
|
-
if (process.env.WHEAT_NO_INSTALL_PROMPT ===
|
|
145
|
-
if (process.argv.includes(
|
|
144
|
+
if (process.env.WHEAT_NO_INSTALL_PROMPT === "1") return;
|
|
145
|
+
if (process.argv.includes("--quiet")) return;
|
|
146
146
|
if (isInstalled()) return;
|
|
147
147
|
if (!isNpxInvocation()) return;
|
|
148
148
|
if (!command) return;
|
|
@@ -162,22 +162,22 @@ export function maybePrompt(command) {
|
|
|
162
162
|
// 10+ invocations: more visible, but still non-blocking
|
|
163
163
|
process.stderr.write(
|
|
164
164
|
`\n wheat: You've used npx ${total} times (${command}: ${count}).` +
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
`\n Install for instant startup and offline use:` +
|
|
166
|
+
`\n npm i -g @grainulation/wheat (global)` +
|
|
167
|
+
`\n npm i -D @grainulation/wheat (project dev dep)\n\n`
|
|
168
168
|
);
|
|
169
169
|
} else if (effective >= THRESHOLDS.suggest_local) {
|
|
170
170
|
// 5-9 invocations: suggest both global and local
|
|
171
171
|
process.stderr.write(
|
|
172
172
|
` wheat: You've run wheat via npx ${total} times. ` +
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
`For instant startup: npm i -g @grainulation/wheat\n` +
|
|
174
|
+
` Or add to this project: npm i -D @grainulation/wheat\n`
|
|
175
175
|
);
|
|
176
176
|
} else if (effective >= THRESHOLDS.suggest_global) {
|
|
177
177
|
// 3-4 invocations: one-liner, suggest global only
|
|
178
178
|
process.stderr.write(
|
|
179
179
|
` wheat: You've run wheat via npx ${count} times. ` +
|
|
180
|
-
|
|
180
|
+
`For instant startup: npm i -g @grainulation/wheat\n`
|
|
181
181
|
);
|
|
182
182
|
}
|
|
183
183
|
} catch {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared claims.json loader with schema migration
|
|
3
|
+
*
|
|
4
|
+
* All consumers that read claims.json should use this helper to ensure
|
|
5
|
+
* the data is migrated to the current schema version before processing.
|
|
6
|
+
*
|
|
7
|
+
* Zero npm dependencies.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { checkAndMigrateSchema } from "../compiler/wheat-compiler.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Load and migrate claims.json from a directory.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} dir - Directory containing claims.json
|
|
18
|
+
* @param {object} [opts] - Options
|
|
19
|
+
* @param {string} [opts.filename='claims.json'] - Claims filename override
|
|
20
|
+
* @returns {{ data: object|null, errors: Array<{code: string, message: string}>, path: string }}
|
|
21
|
+
* - data: the full claims object (with schema_version, meta, claims) or null if file missing/invalid
|
|
22
|
+
* - errors: migration errors (empty array on success)
|
|
23
|
+
* - path: resolved file path
|
|
24
|
+
*/
|
|
25
|
+
export function loadClaims(dir, opts = {}) {
|
|
26
|
+
const filename = opts.filename || "claims.json";
|
|
27
|
+
const claimsPath = path.join(dir, filename);
|
|
28
|
+
|
|
29
|
+
if (!fs.existsSync(claimsPath)) {
|
|
30
|
+
return { data: null, errors: [], path: claimsPath };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let raw;
|
|
34
|
+
try {
|
|
35
|
+
raw = fs.readFileSync(claimsPath, "utf8");
|
|
36
|
+
} catch (err) {
|
|
37
|
+
return {
|
|
38
|
+
data: null,
|
|
39
|
+
errors: [
|
|
40
|
+
{
|
|
41
|
+
code: "E_READ",
|
|
42
|
+
message: `Failed to read ${filename}: ${err.message}`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
path: claimsPath,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = JSON.parse(raw);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return {
|
|
54
|
+
data: null,
|
|
55
|
+
errors: [
|
|
56
|
+
{
|
|
57
|
+
code: "E_PARSE",
|
|
58
|
+
message: `${filename} is not valid JSON: ${err.message}`,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
path: claimsPath,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Run schema migration
|
|
66
|
+
const result = checkAndMigrateSchema(parsed);
|
|
67
|
+
|
|
68
|
+
// Write migrated data back to disk so downstream tools that read the file
|
|
69
|
+
// directly (bypassing loadClaims) see the current schema version.
|
|
70
|
+
// Only write if migration actually changed something (avoid unnecessary I/O).
|
|
71
|
+
if (result.errors.length === 0 && result.data) {
|
|
72
|
+
const migrated = JSON.stringify(result.data, null, 2);
|
|
73
|
+
if (migrated !== raw) {
|
|
74
|
+
try {
|
|
75
|
+
fs.writeFileSync(claimsPath, migrated + "\n", "utf8");
|
|
76
|
+
} catch (err) {
|
|
77
|
+
// Non-fatal: migration write-back failure should not block reads.
|
|
78
|
+
// The in-memory data is still correct.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
data: result.errors.length > 0 ? null : result.data,
|
|
85
|
+
errors: result.errors,
|
|
86
|
+
path: claimsPath,
|
|
87
|
+
};
|
|
88
|
+
}
|