greprag 5.38.0 → 5.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/discover.d.ts +1 -1
- package/dist/commands/discover.js +6 -5
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/fix.d.ts +28 -0
- package/dist/commands/fix.js +455 -37
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/init.js +18 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mechanic.d.ts +64 -0
- package/dist/commands/mechanic.js +374 -0
- package/dist/commands/mechanic.js.map +1 -0
- package/dist/fix-trigger.d.ts +43 -0
- package/dist/fix-trigger.js +270 -0
- package/dist/fix-trigger.js.map +1 -0
- package/dist/guard.d.ts +92 -0
- package/dist/guard.js +404 -0
- package/dist/guard.js.map +1 -0
- package/dist/hook.js +117 -70
- package/dist/hook.js.map +1 -1
- package/dist/index.js +66 -46
- package/dist/index.js.map +1 -1
- package/dist/secret-scrubber.d.ts +48 -0
- package/dist/secret-scrubber.js +368 -0
- package/dist/secret-scrubber.js.map +1 -0
- package/package.json +1 -1
- package/skill/greprag/SKILL.md +6 -3
- package/skill/greprag/docs/corpus.md +15 -0
- package/skill/greprag/docs/discover.md +1 -1
- package/skill/greprag/docs/email.md +46 -0
- package/skill/greprag/docs/fix.md +86 -0
- package/skill/greprag/docs/inbox-watch.md +5 -4
- package/skill/greprag/docs/inbox.md +10 -8
- package/skill/greprag/docs/setup.md +1 -1
- package/skill/mechanic/SKILL.md +22 -2
- package/skill/templates/chip-spawn.md +30 -8
- package/skill/templates/workshop-chip.md +43 -0
- package/skill/greprag/docs/lore.md +0 -70
|
@@ -107,16 +107,17 @@ function renderHuman(r) {
|
|
|
107
107
|
const nameW = Math.max(4, ...r.projects.map(p => (p.name || '<unnamed>').length));
|
|
108
108
|
// Each shape column is wide enough for its label or a 5-digit count.
|
|
109
109
|
const colWs = types.map(t => Math.max(t.length, 5));
|
|
110
|
-
//
|
|
111
|
-
// kind=
|
|
112
|
-
|
|
110
|
+
// Repaired + smell columns ride after the memory shapes — they live in their
|
|
111
|
+
// own kind=fix store, not the memory pyramid, so they're separate from
|
|
112
|
+
// row_counts.
|
|
113
|
+
const repairedW = Math.max('repaired'.length, 5);
|
|
113
114
|
const smellW = Math.max('smell'.length, 5);
|
|
114
115
|
const dateW = 10;
|
|
115
116
|
const header = [
|
|
116
117
|
'name'.padEnd(nameW),
|
|
117
118
|
'anchor'.padEnd(10),
|
|
118
119
|
...types.map((t, i) => t.padStart(colWs[i])),
|
|
119
|
-
'
|
|
120
|
+
'repaired'.padStart(repairedW),
|
|
120
121
|
'smell'.padStart(smellW),
|
|
121
122
|
'first'.padEnd(dateW),
|
|
122
123
|
'last'.padEnd(dateW),
|
|
@@ -129,7 +130,7 @@ function renderHuman(r) {
|
|
|
129
130
|
(p.name || '<unnamed>').padEnd(nameW),
|
|
130
131
|
p.anchor.padEnd(10),
|
|
131
132
|
...types.map((t, i) => (p.row_counts[t] || 0).toString().padStart(colWs[i])),
|
|
132
|
-
(p.
|
|
133
|
+
(p.repaired || 0).toString().padStart(repairedW),
|
|
133
134
|
(p.smell || 0).toString().padStart(smellW),
|
|
134
135
|
fmtDate(p.first_activity).padEnd(dateW),
|
|
135
136
|
fmtDate(p.last_activity).padEnd(dateW),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/commands/discover.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/commands/discover.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/fix.d.ts
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
|
+
/** greprag fix — the Mechanic's per-project fix queue (friction → fix → repair).
|
|
2
|
+
*
|
|
3
|
+
* A "fix" is a rough spot worth repairing — docs/mechanic.md. Entries are
|
|
4
|
+
* project-specific emergent knowledge: discovered constraints, gotchas,
|
|
5
|
+
* tribal knowledge that drifts as the codebase evolves, plus raw smells
|
|
6
|
+
* (scope='smell') awaiting digestion. Distinct from static project knowledge
|
|
7
|
+
* (CLAUDE.md / docs / code structure). Reviewed via /mechanic.
|
|
8
|
+
*
|
|
9
|
+
* Pull-based: agents seed fixes a future spawned chip would have wanted;
|
|
10
|
+
* future chips query the relevant scope at spawn time and skip the discovery
|
|
11
|
+
* they would otherwise repeat. All server work delegates to FixService in
|
|
12
|
+
* @greprag/core; the CLI is a thin HTTP printer + project resolver.
|
|
13
|
+
*
|
|
14
|
+
* Subcommand surface — runFix dispatches:
|
|
15
|
+
* log "<text>" [--scope <s>] [--repaired] open a fix (raw friction);
|
|
16
|
+
* --repaired records a known rule directly
|
|
17
|
+
* list [--all] [--repaired] [--format json] open queue by default;
|
|
18
|
+
* --repaired = resolved rules;
|
|
19
|
+
* --all = tenant-wide census
|
|
20
|
+
* repair <id> "<rule>" [--scope <s>] resolve a fix: record the durable rule + close
|
|
21
|
+
* delete <id> hard-remove a fix (noise / cleanup)
|
|
22
|
+
* search "<query>" [--scope <s>] [--limit N] lexical search across fixes
|
|
23
|
+
* scopes distinct scope labels in use
|
|
24
|
+
*
|
|
25
|
+
* All verbs accept [--project <name>] to target another project's queue.
|
|
26
|
+
* Project resolution: without --project, uses the current cwd's anchor.
|
|
27
|
+
* With --project <name>, hits /v1/inbox/projects to resolve name → projectId
|
|
28
|
+
* (case-insensitive). */
|
|
1
29
|
export declare function runFix(args: string[]): Promise<void>;
|
package/dist/commands/fix.js
CHANGED
|
@@ -1,22 +1,439 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// `greprag fix` — the Mechanic's fix queue (friction → fix → repair).
|
|
3
|
-
//
|
|
4
|
-
// A thin verb layer over `greprag lore` (still the live backend) during the
|
|
5
|
-
// mechanic migration. Maps the canon verbs onto the existing store:
|
|
6
|
-
// log → open a fix (lore smell; --repaired → lore add, a known rule)
|
|
7
|
-
// list → the queue (lore smells; --repaired → lore list, resolved)
|
|
8
|
-
// repair → resolve + keep (lore add <rule>, then lore delete <id>)
|
|
9
|
-
// delete → hard remove (lore delete)
|
|
10
|
-
// search → lore query
|
|
11
|
-
// scopes → lore scopes
|
|
12
|
-
//
|
|
13
|
-
// `greprag lore` stays as the back-compat alias. The kind=lore→kind=fix DB
|
|
14
|
-
// migration + the FixService core rename landed in migration 056.
|
|
15
2
|
// adr: adr/lore-rename.md
|
|
3
|
+
/** greprag fix — the Mechanic's per-project fix queue (friction → fix → repair).
|
|
4
|
+
*
|
|
5
|
+
* A "fix" is a rough spot worth repairing — docs/mechanic.md. Entries are
|
|
6
|
+
* project-specific emergent knowledge: discovered constraints, gotchas,
|
|
7
|
+
* tribal knowledge that drifts as the codebase evolves, plus raw smells
|
|
8
|
+
* (scope='smell') awaiting digestion. Distinct from static project knowledge
|
|
9
|
+
* (CLAUDE.md / docs / code structure). Reviewed via /mechanic.
|
|
10
|
+
*
|
|
11
|
+
* Pull-based: agents seed fixes a future spawned chip would have wanted;
|
|
12
|
+
* future chips query the relevant scope at spawn time and skip the discovery
|
|
13
|
+
* they would otherwise repeat. All server work delegates to FixService in
|
|
14
|
+
* @greprag/core; the CLI is a thin HTTP printer + project resolver.
|
|
15
|
+
*
|
|
16
|
+
* Subcommand surface — runFix dispatches:
|
|
17
|
+
* log "<text>" [--scope <s>] [--repaired] open a fix (raw friction);
|
|
18
|
+
* --repaired records a known rule directly
|
|
19
|
+
* list [--all] [--repaired] [--format json] open queue by default;
|
|
20
|
+
* --repaired = resolved rules;
|
|
21
|
+
* --all = tenant-wide census
|
|
22
|
+
* repair <id> "<rule>" [--scope <s>] resolve a fix: record the durable rule + close
|
|
23
|
+
* delete <id> hard-remove a fix (noise / cleanup)
|
|
24
|
+
* search "<query>" [--scope <s>] [--limit N] lexical search across fixes
|
|
25
|
+
* scopes distinct scope labels in use
|
|
26
|
+
*
|
|
27
|
+
* All verbs accept [--project <name>] to target another project's queue.
|
|
28
|
+
* Project resolution: without --project, uses the current cwd's anchor.
|
|
29
|
+
* With --project <name>, hits /v1/inbox/projects to resolve name → projectId
|
|
30
|
+
* (case-insensitive). */
|
|
31
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
34
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
35
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
36
|
+
}
|
|
37
|
+
Object.defineProperty(o, k2, desc);
|
|
38
|
+
}) : (function(o, m, k, k2) {
|
|
39
|
+
if (k2 === undefined) k2 = k;
|
|
40
|
+
o[k2] = m[k];
|
|
41
|
+
}));
|
|
42
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
43
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
44
|
+
}) : function(o, v) {
|
|
45
|
+
o["default"] = v;
|
|
46
|
+
});
|
|
47
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
48
|
+
var ownKeys = function(o) {
|
|
49
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
50
|
+
var ar = [];
|
|
51
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
52
|
+
return ar;
|
|
53
|
+
};
|
|
54
|
+
return ownKeys(o);
|
|
55
|
+
};
|
|
56
|
+
return function (mod) {
|
|
57
|
+
if (mod && mod.__esModule) return mod;
|
|
58
|
+
var result = {};
|
|
59
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
60
|
+
__setModuleDefault(result, mod);
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
})();
|
|
16
64
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
65
|
exports.runFix = runFix;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
66
|
+
const fs = __importStar(require("fs"));
|
|
67
|
+
const path = __importStar(require("path"));
|
|
68
|
+
const project_anchor_1 = require("../project-anchor");
|
|
69
|
+
const API_URL_DEFAULT = 'https://api.greprag.com';
|
|
70
|
+
// Reserved scope for raw, undigested SMELLS — see docs/lore-smell.md. The CLI's
|
|
71
|
+
// `fix log` (no --repaired) writes here; `fix list` (no --repaired) reads here.
|
|
72
|
+
// Durable readers (`fix list --repaired` / search / scopes) exclude it. The
|
|
73
|
+
// stored value stays 'smell' — the fix CLI maps smell ↔ open-fix at the verb
|
|
74
|
+
// layer, the server is the authority for cross-project reads.
|
|
75
|
+
const SMELL_SCOPE = 'smell';
|
|
76
|
+
// ---------- Config / env --------------------------------------------------
|
|
77
|
+
function loadEnvFile(filePath) {
|
|
78
|
+
try {
|
|
79
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
80
|
+
for (const line of content.split('\n')) {
|
|
81
|
+
const trimmed = line.trim();
|
|
82
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
83
|
+
continue;
|
|
84
|
+
const eqIdx = trimmed.indexOf('=');
|
|
85
|
+
if (eqIdx < 1)
|
|
86
|
+
continue;
|
|
87
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
88
|
+
let value = trimmed.slice(eqIdx + 1).trim();
|
|
89
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
90
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
91
|
+
value = value.slice(1, -1);
|
|
92
|
+
}
|
|
93
|
+
if (!process.env[key])
|
|
94
|
+
process.env[key] = value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch { /* file missing — fine */ }
|
|
98
|
+
}
|
|
99
|
+
function getConfig() {
|
|
100
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
101
|
+
if (homeDir)
|
|
102
|
+
loadEnvFile(path.join(homeDir, '.greprag', '.env'));
|
|
103
|
+
if (!process.env.GREPRAG_API_KEY)
|
|
104
|
+
loadEnvFile(path.join(process.cwd(), '.env'));
|
|
105
|
+
return {
|
|
106
|
+
apiUrl: process.env.GREPRAG_API_URL || API_URL_DEFAULT,
|
|
107
|
+
apiKey: process.env.GREPRAG_API_KEY || '',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
async function apiCall(url, apiKey, body) {
|
|
111
|
+
const res = await fetch(url, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
114
|
+
body: JSON.stringify(body),
|
|
115
|
+
});
|
|
116
|
+
if (!res.ok)
|
|
117
|
+
throw new Error(`API ${res.status}: ${await res.text()}`);
|
|
118
|
+
return res.json();
|
|
119
|
+
}
|
|
120
|
+
async function apiGet(url, apiKey) {
|
|
121
|
+
const res = await fetch(url, { headers: { 'Authorization': `Bearer ${apiKey}` } });
|
|
122
|
+
if (!res.ok)
|
|
123
|
+
throw new Error(`API ${res.status}: ${await res.text()}`);
|
|
124
|
+
return res.json();
|
|
125
|
+
}
|
|
126
|
+
async function apiDelete(url, apiKey) {
|
|
127
|
+
const res = await fetch(url, {
|
|
128
|
+
method: 'DELETE',
|
|
129
|
+
headers: { 'Authorization': `Bearer ${apiKey}` },
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok)
|
|
132
|
+
throw new Error(`API ${res.status}: ${await res.text()}`);
|
|
133
|
+
return res.json();
|
|
134
|
+
}
|
|
135
|
+
// ---------- Helpers --------------------------------------------------------
|
|
136
|
+
function getFlag(args, flag) {
|
|
137
|
+
const idx = args.indexOf(flag);
|
|
138
|
+
if (idx === -1 || idx + 1 >= args.length)
|
|
139
|
+
return undefined;
|
|
140
|
+
return args[idx + 1];
|
|
141
|
+
}
|
|
142
|
+
function abortNotConfigured() {
|
|
143
|
+
console.error('Not configured. Run: greprag init');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
/** Resolve which project a fix command operates on. --project <name> wins via
|
|
147
|
+
* the tenant's projects registry; otherwise the cwd's anchor decides. */
|
|
148
|
+
async function resolveProject(args, apiUrl, apiKey) {
|
|
149
|
+
const projectArg = getFlag(args, '--project');
|
|
150
|
+
if (!projectArg) {
|
|
151
|
+
const anchor = (0, project_anchor_1.readAnchor)(process.cwd());
|
|
152
|
+
return { projectId: anchor.projectId, projectName: anchor.projectName };
|
|
153
|
+
}
|
|
154
|
+
const r = await apiGet(`${apiUrl}/v1/inbox/projects`, apiKey);
|
|
155
|
+
const needle = projectArg.trim().toLowerCase();
|
|
156
|
+
const match = (r.projects || []).find(p => p.project_name.toLowerCase() === needle);
|
|
157
|
+
if (!match) {
|
|
158
|
+
throw new Error(`project "${projectArg}" not found under this tenant. ` +
|
|
159
|
+
`Register it via 'greprag init' in that directory, then retry.`);
|
|
160
|
+
}
|
|
161
|
+
return { projectId: match.project_id, projectName: match.project_name };
|
|
162
|
+
}
|
|
163
|
+
/** Best-effort session uuid from the harness env (Claude Code exposes
|
|
164
|
+
* CLAUDE_SESSION_ID on hook invocations). Callers can override via
|
|
165
|
+
* --source-session. */
|
|
166
|
+
function readSessionEnv() {
|
|
167
|
+
return process.env.CLAUDE_SESSION_ID || process.env.GREPRAG_SESSION_ID || null;
|
|
168
|
+
}
|
|
169
|
+
// ---------- add (the --repaired path of `fix log`) ------------------------
|
|
170
|
+
async function addCmd(args) {
|
|
171
|
+
const cfg = getConfig();
|
|
172
|
+
if (!cfg.apiKey)
|
|
173
|
+
abortNotConfigured();
|
|
174
|
+
const text = args[0];
|
|
175
|
+
if (!text || text.startsWith('--')) {
|
|
176
|
+
console.error('Usage: greprag fix log "<text>" --repaired --scope <scope> [--project <name>] [--source-session <id>]');
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
const scope = getFlag(args, '--scope');
|
|
180
|
+
if (!scope) {
|
|
181
|
+
console.error('Usage: greprag fix log "<text>" --repaired --scope <scope> [--project <name>]');
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
const sourceSession = getFlag(args, '--source-session') || readSessionEnv();
|
|
185
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
186
|
+
const result = await apiCall(`${cfg.apiUrl}/v1/fix/${projectId}`, cfg.apiKey, {
|
|
187
|
+
text, scope,
|
|
188
|
+
sourceSessionId: sourceSession,
|
|
189
|
+
projectName,
|
|
190
|
+
});
|
|
191
|
+
if (!result.ok || !result.nodeId)
|
|
192
|
+
throw new Error(result.error || 'add failed');
|
|
193
|
+
console.log(`Added fix [${result.nodeId}] scope=${scope} project=${projectName}`);
|
|
194
|
+
}
|
|
195
|
+
// ---------- smell (the default path of `fix log`) -------------------------
|
|
196
|
+
/** `fix log "<text>"` (no --repaired) → leave a RAW signal for /mechanic to
|
|
197
|
+
* digest. Zero structure by design: no --scope, no trigger. A smell is NOT a
|
|
198
|
+
* durable rule — it's an item awaiting digestion (the advisor decides
|
|
199
|
+
* fix-vs-instantiate, assigns scope + injection trigger). Frictionless capture
|
|
200
|
+
* is the whole point; defer all judgment to digestion. docs/lore-smell.md. */
|
|
201
|
+
async function smellCmd(args) {
|
|
202
|
+
const cfg = getConfig();
|
|
203
|
+
if (!cfg.apiKey)
|
|
204
|
+
abortNotConfigured();
|
|
205
|
+
const text = args[0];
|
|
206
|
+
if (!text || text.startsWith('--')) {
|
|
207
|
+
console.error('Usage: greprag fix log "<text>" [--project <name>]');
|
|
208
|
+
console.error('Leave a raw signal for the Mechanic to digest — a broken mechanism,');
|
|
209
|
+
console.error('a missing guardrail, or a durable truth not yet captured.');
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const sourceSession = getFlag(args, '--source-session') || readSessionEnv();
|
|
213
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
214
|
+
const result = await apiCall(`${cfg.apiUrl}/v1/fix/${projectId}`, cfg.apiKey, { text, scope: SMELL_SCOPE, sourceSessionId: sourceSession, projectName });
|
|
215
|
+
if (!result.ok || !result.nodeId)
|
|
216
|
+
throw new Error(result.error || 'smell failed');
|
|
217
|
+
console.log(`Fix logged [${result.nodeId}] in ${projectName}. Digest via /mechanic.`);
|
|
218
|
+
}
|
|
219
|
+
/** `fix list` (no --repaired) → the raw smell queue awaiting digestion. The
|
|
220
|
+
* only reader that surfaces SMELL_SCOPE entries; durable list/search/scopes
|
|
221
|
+
* hide them. Without --all: the current project only. With --all: every
|
|
222
|
+
* project in the tenant, each row project-annotated (the Mechanic's
|
|
223
|
+
* Phase 0 census — so a backlog in another venture root can't hide). */
|
|
224
|
+
async function smellsCmd(args) {
|
|
225
|
+
const cfg = getConfig();
|
|
226
|
+
if (!cfg.apiKey)
|
|
227
|
+
abortNotConfigured();
|
|
228
|
+
const format = (getFlag(args, '--format') || 'text').toLowerCase();
|
|
229
|
+
if (args.includes('--all')) {
|
|
230
|
+
return smellsAllCmd(cfg, format);
|
|
231
|
+
}
|
|
232
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
233
|
+
const result = await apiGet(`${cfg.apiUrl}/v1/fix/${projectId}/list`, cfg.apiKey);
|
|
234
|
+
if (!result.ok)
|
|
235
|
+
throw new Error(result.error || 'smells failed');
|
|
236
|
+
const rows = (result.fix ?? []).filter(r => r.scope === SMELL_SCOPE);
|
|
237
|
+
if (format === 'json') {
|
|
238
|
+
console.log(JSON.stringify({ project: projectName, smells: rows }, null, 2));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (rows.length === 0) {
|
|
242
|
+
console.log(`No fixes awaiting digestion in ${projectName}.`);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
console.log(`\n${rows.length} fix${rows.length === 1 ? '' : 'es'} awaiting digestion in ${projectName}:\n`);
|
|
246
|
+
for (const r of rows) {
|
|
247
|
+
const date = r.createdAt ? r.createdAt.slice(0, 10) : '';
|
|
248
|
+
console.log(` [${r.nodeId}] ${date} ${r.text}`);
|
|
249
|
+
}
|
|
250
|
+
console.log(`\nDigest: /mechanic · retire one: greprag fix delete <nodeId>`);
|
|
251
|
+
}
|
|
252
|
+
/** `fix list --all` → tenant-wide smell census. One server call returns every
|
|
253
|
+
* smell across every project; the CLI groups them and stamps the top-level
|
|
254
|
+
* total + byProject summary the Mechanic skill consumes. JSON shape is a
|
|
255
|
+
* stable contract — do not reshape casually. */
|
|
256
|
+
async function smellsAllCmd(cfg, format) {
|
|
257
|
+
const result = await apiGet(`${cfg.apiUrl}/v1/fix/census/smells`, cfg.apiKey);
|
|
258
|
+
if (!result.ok || !result.smells)
|
|
259
|
+
throw new Error(result.error || 'smells census failed');
|
|
260
|
+
// Project server rows onto the published smell shape (drop projectId + any
|
|
261
|
+
// server-internal fields so the JSON contract stays exactly:
|
|
262
|
+
// id, nodeId, text, scope, sourceSessionId, createdAt, project).
|
|
263
|
+
const smells = result.smells.map(s => ({
|
|
264
|
+
id: s.id,
|
|
265
|
+
nodeId: s.nodeId,
|
|
266
|
+
text: s.text,
|
|
267
|
+
scope: s.scope,
|
|
268
|
+
sourceSessionId: s.sourceSessionId,
|
|
269
|
+
createdAt: s.createdAt,
|
|
270
|
+
project: s.project,
|
|
271
|
+
}));
|
|
272
|
+
const total = smells.length;
|
|
273
|
+
const counts = new Map();
|
|
274
|
+
for (const s of smells)
|
|
275
|
+
counts.set(s.project, (counts.get(s.project) ?? 0) + 1);
|
|
276
|
+
const byProject = [...counts.entries()]
|
|
277
|
+
.map(([project, count]) => ({ project, count }))
|
|
278
|
+
.sort((a, b) => b.count - a.count || a.project.localeCompare(b.project));
|
|
279
|
+
if (format === 'json') {
|
|
280
|
+
console.log(JSON.stringify({ total, byProject, smells }, null, 2));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (total === 0) {
|
|
284
|
+
console.log('No fixes awaiting digestion across any project.');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
console.log(`\n${total} fix${total === 1 ? '' : 'es'} awaiting digestion across ${byProject.length} project${byProject.length === 1 ? '' : 's'}:\n`);
|
|
288
|
+
const grouped = new Map();
|
|
289
|
+
for (const s of smells) {
|
|
290
|
+
if (!grouped.has(s.project))
|
|
291
|
+
grouped.set(s.project, []);
|
|
292
|
+
grouped.get(s.project).push(s);
|
|
293
|
+
}
|
|
294
|
+
for (const { project, count } of byProject) {
|
|
295
|
+
console.log(`${project} (${count}):`);
|
|
296
|
+
for (const r of grouped.get(project) ?? []) {
|
|
297
|
+
const date = r.createdAt ? r.createdAt.slice(0, 10) : '';
|
|
298
|
+
console.log(` [${r.nodeId}] ${date} ${r.text}`);
|
|
299
|
+
}
|
|
300
|
+
console.log('');
|
|
301
|
+
}
|
|
302
|
+
console.log(`Digest: /mechanic · retire one: greprag fix delete <nodeId> --project <name>`);
|
|
303
|
+
}
|
|
304
|
+
// ---------- query (`fix search`) ------------------------------------------
|
|
305
|
+
async function queryCmd(args) {
|
|
306
|
+
const cfg = getConfig();
|
|
307
|
+
if (!cfg.apiKey)
|
|
308
|
+
abortNotConfigured();
|
|
309
|
+
const scope = getFlag(args, '--scope');
|
|
310
|
+
const queryStr = getFlag(args, '--query');
|
|
311
|
+
const limit = parseInt(getFlag(args, '--limit') || '10', 10);
|
|
312
|
+
const format = (getFlag(args, '--format') || 'markdown').toLowerCase();
|
|
313
|
+
if (format !== 'markdown' && format !== 'json') {
|
|
314
|
+
console.error('--format must be markdown or json');
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
318
|
+
const params = new URLSearchParams();
|
|
319
|
+
if (scope)
|
|
320
|
+
params.set('scope', scope);
|
|
321
|
+
if (queryStr)
|
|
322
|
+
params.set('query', queryStr);
|
|
323
|
+
params.set('limit', String(limit));
|
|
324
|
+
const result = await apiGet(`${cfg.apiUrl}/v1/fix/${projectId}?${params.toString()}`, cfg.apiKey);
|
|
325
|
+
const allRows = result.fix;
|
|
326
|
+
if (!result.ok || !allRows)
|
|
327
|
+
throw new Error(result.error || 'query failed');
|
|
328
|
+
// Hide raw smells from durable reads unless the caller explicitly asked for
|
|
329
|
+
// that scope (`--scope smell`), which is a deliberate queue peek.
|
|
330
|
+
const rows = scope ? allRows : allRows.filter(r => r.scope !== SMELL_SCOPE);
|
|
331
|
+
if (format === 'json') {
|
|
332
|
+
console.log(JSON.stringify({
|
|
333
|
+
project: projectName,
|
|
334
|
+
scope: scope ?? null,
|
|
335
|
+
query: queryStr ?? null,
|
|
336
|
+
fix: rows,
|
|
337
|
+
}, null, 2));
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (rows.length === 0) {
|
|
341
|
+
const scopeNote = scope ? ` (scope=${scope})` : '';
|
|
342
|
+
const queryNote = queryStr ? ` matching "${queryStr}"` : '';
|
|
343
|
+
console.log(`No fixes in ${projectName}${scopeNote}${queryNote}.`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
for (let i = 0; i < rows.length; i++) {
|
|
347
|
+
console.log(`${i + 1}. ${rows[i].text}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// ---------- list (`fix list --repaired`) ----------------------------------
|
|
351
|
+
async function listCmd(args) {
|
|
352
|
+
const cfg = getConfig();
|
|
353
|
+
if (!cfg.apiKey)
|
|
354
|
+
abortNotConfigured();
|
|
355
|
+
const format = (getFlag(args, '--format') || 'text').toLowerCase();
|
|
356
|
+
if (format !== 'text' && format !== 'json' && format !== 'markdown') {
|
|
357
|
+
console.error('--format must be text, markdown, or json');
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
361
|
+
const result = await apiGet(`${cfg.apiUrl}/v1/fix/${projectId}/list`, cfg.apiKey);
|
|
362
|
+
const allRows = result.fix;
|
|
363
|
+
if (!result.ok || !allRows)
|
|
364
|
+
throw new Error(result.error || 'list failed');
|
|
365
|
+
// The durable-review surface — never show raw smells (see smellsCmd).
|
|
366
|
+
const rows = allRows.filter(r => r.scope !== SMELL_SCOPE);
|
|
367
|
+
if (format === 'json') {
|
|
368
|
+
console.log(JSON.stringify({
|
|
369
|
+
project: projectName,
|
|
370
|
+
fix: rows,
|
|
371
|
+
}, null, 2));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (rows.length === 0) {
|
|
375
|
+
console.log(`No fixes recorded in ${projectName} yet.`);
|
|
376
|
+
console.log('Add one: greprag fix log "<text>"');
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
// Group by scope (server already orders scope ASC, createdAt DESC).
|
|
380
|
+
const grouped = new Map();
|
|
381
|
+
for (const f of rows) {
|
|
382
|
+
const scope = f.scope || '(no scope)';
|
|
383
|
+
if (!grouped.has(scope))
|
|
384
|
+
grouped.set(scope, []);
|
|
385
|
+
grouped.get(scope).push(f);
|
|
386
|
+
}
|
|
387
|
+
console.log(`\n${rows.length} fix entr${rows.length === 1 ? 'y' : 'ies'} in ${projectName}:\n`);
|
|
388
|
+
for (const [scope, rows] of grouped) {
|
|
389
|
+
console.log(`## ${scope} (${rows.length})`);
|
|
390
|
+
for (const f of rows) {
|
|
391
|
+
const date = f.createdAt ? f.createdAt.slice(0, 10) : '';
|
|
392
|
+
console.log(` [${f.nodeId}] ${date} ${f.text}`);
|
|
393
|
+
}
|
|
394
|
+
console.log('');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// ---------- delete --------------------------------------------------------
|
|
398
|
+
async function deleteCmd(args) {
|
|
399
|
+
const cfg = getConfig();
|
|
400
|
+
if (!cfg.apiKey)
|
|
401
|
+
abortNotConfigured();
|
|
402
|
+
const nodeId = args[0];
|
|
403
|
+
if (!nodeId || nodeId.startsWith('--')) {
|
|
404
|
+
console.error('Usage: greprag fix delete <nodeId> [--project <name>]');
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
if (!/^[0-9a-f]{8}$/i.test(nodeId)) {
|
|
408
|
+
console.error('nodeId must be 8 hex chars (printed in `greprag fix list`).');
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
412
|
+
const result = await apiDelete(`${cfg.apiUrl}/v1/fix/${projectId}/${nodeId}`, cfg.apiKey);
|
|
413
|
+
if (!result.ok)
|
|
414
|
+
throw new Error(result.error || 'delete failed');
|
|
415
|
+
console.log(`Deleted fix [${nodeId}] from ${projectName}`);
|
|
416
|
+
}
|
|
417
|
+
// ---------- scopes --------------------------------------------------------
|
|
418
|
+
async function scopesCmd(args) {
|
|
419
|
+
const cfg = getConfig();
|
|
420
|
+
if (!cfg.apiKey)
|
|
421
|
+
abortNotConfigured();
|
|
422
|
+
const { projectId, projectName } = await resolveProject(args, cfg.apiUrl, cfg.apiKey);
|
|
423
|
+
const result = await apiGet(`${cfg.apiUrl}/v1/fix/${projectId}/scopes`, cfg.apiKey);
|
|
424
|
+
if (!result.ok || !result.scopes)
|
|
425
|
+
throw new Error(result.error || 'scopes failed');
|
|
426
|
+
// The reserved smell scope is an internal queue, not a durable scope label.
|
|
427
|
+
const scopes = result.scopes.filter(s => s !== SMELL_SCOPE);
|
|
428
|
+
if (scopes.length === 0) {
|
|
429
|
+
console.log(`No scopes in ${projectName} yet.`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
for (const s of scopes)
|
|
433
|
+
console.log(s);
|
|
434
|
+
}
|
|
435
|
+
// ---------- Dispatch ------------------------------------------------------
|
|
436
|
+
const FIX_HELP = `greprag fix — the Mechanic's per-project fix queue (friction → fix → repair)
|
|
20
437
|
|
|
21
438
|
USAGE
|
|
22
439
|
greprag fix log "<text>" [--scope <s>] [--repaired] Open a fix (raw friction).
|
|
@@ -33,13 +450,13 @@ USAGE
|
|
|
33
450
|
NOTES
|
|
34
451
|
• A 'repair' RECORDS the durable rule (kept, browsable via 'fix list --repaired')
|
|
35
452
|
and closes the open fix. To remove a fix without keeping a rule, use 'delete'.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
453
|
+
|
|
454
|
+
Examples:
|
|
455
|
+
greprag fix log "Migrations live at repo root, not packages/api/migrations." --repaired --scope chip-startup
|
|
456
|
+
greprag fix search "where do migrations live" --limit 5
|
|
457
|
+
greprag fix list --repaired
|
|
458
|
+
greprag fix delete a3f2c1d4
|
|
459
|
+
greprag fix scopes`;
|
|
43
460
|
async function runFix(args) {
|
|
44
461
|
const sub = args[0];
|
|
45
462
|
const rest = args.slice(1);
|
|
@@ -49,16 +466,18 @@ async function runFix(args) {
|
|
|
49
466
|
}
|
|
50
467
|
switch (sub) {
|
|
51
468
|
case 'log': {
|
|
52
|
-
// Raw friction → open fix (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
469
|
+
// Raw friction → open fix (smell). --repaired → a known durable rule.
|
|
470
|
+
if (rest.includes('--repaired')) {
|
|
471
|
+
return addCmd(rest.filter(a => a !== '--repaired'));
|
|
472
|
+
}
|
|
473
|
+
return smellCmd(rest);
|
|
56
474
|
}
|
|
57
475
|
case 'list': {
|
|
58
|
-
// Open queue
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
476
|
+
// Open queue by default; --repaired → resolved/durable rules.
|
|
477
|
+
if (rest.includes('--repaired')) {
|
|
478
|
+
return listCmd(rest.filter(a => a !== '--repaired'));
|
|
479
|
+
}
|
|
480
|
+
return smellsCmd(rest);
|
|
62
481
|
}
|
|
63
482
|
case 'repair': {
|
|
64
483
|
// Resolve: record the durable rule (kept) + close the open fix.
|
|
@@ -73,13 +492,12 @@ async function runFix(args) {
|
|
|
73
492
|
let flags = rest.slice(2);
|
|
74
493
|
if (!flags.includes('--scope'))
|
|
75
494
|
flags = [...flags, '--scope', 'general'];
|
|
76
|
-
await (
|
|
77
|
-
const project =
|
|
78
|
-
return (
|
|
495
|
+
await addCmd([rule, ...flags]);
|
|
496
|
+
const project = getFlag(flags, '--project');
|
|
497
|
+
return deleteCmd([id, ...(project ? ['--project', project] : [])]);
|
|
79
498
|
}
|
|
80
499
|
case 'delete': {
|
|
81
|
-
|
|
82
|
-
return (0, lore_1.runLore)(['delete', ...rest]);
|
|
500
|
+
return deleteCmd(rest);
|
|
83
501
|
}
|
|
84
502
|
case 'search': {
|
|
85
503
|
const q = rest[0];
|
|
@@ -87,10 +505,10 @@ async function runFix(args) {
|
|
|
87
505
|
console.error('Usage: greprag fix search "<query>" [--scope <s>]');
|
|
88
506
|
process.exit(1);
|
|
89
507
|
}
|
|
90
|
-
return (
|
|
508
|
+
return queryCmd(['--query', q, ...rest.slice(1)]);
|
|
91
509
|
}
|
|
92
510
|
case 'scopes': {
|
|
93
|
-
return (
|
|
511
|
+
return scopesCmd(rest);
|
|
94
512
|
}
|
|
95
513
|
default:
|
|
96
514
|
console.error(`Unknown fix subcommand: ${sub}\n`);
|