mindforge-cc 1.0.4 → 2.0.0-alpha.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/.agent/CLAUDE.md +53 -0
- package/.agent/mindforge/auto.md +22 -0
- package/.agent/mindforge/browse.md +26 -0
- package/.agent/mindforge/costs.md +11 -0
- package/.agent/mindforge/cross-review.md +17 -0
- package/.agent/mindforge/execute-phase.md +5 -3
- package/.agent/mindforge/qa.md +16 -0
- package/.agent/mindforge/remember.md +14 -0
- package/.agent/mindforge/research.md +11 -0
- package/.agent/mindforge/steer.md +13 -0
- package/.agent/workflows/publish-release.md +36 -0
- package/.claude/CLAUDE.md +53 -0
- package/.claude/commands/mindforge/auto.md +22 -0
- package/.claude/commands/mindforge/browse.md +26 -0
- package/.claude/commands/mindforge/costs.md +11 -0
- package/.claude/commands/mindforge/cross-review.md +17 -0
- package/.claude/commands/mindforge/execute-phase.md +5 -3
- package/.claude/commands/mindforge/qa.md +16 -0
- package/.claude/commands/mindforge/remember.md +14 -0
- package/.claude/commands/mindforge/research.md +11 -0
- package/.claude/commands/mindforge/steer.md +13 -0
- package/.mindforge/MINDFORGE-V2-SCHEMA.json +47 -0
- package/.mindforge/browser/daemon-protocol.md +24 -0
- package/.mindforge/browser/qa-engine.md +16 -0
- package/.mindforge/browser/session-manager.md +18 -0
- package/.mindforge/browser/visual-verify-spec.md +31 -0
- package/.mindforge/engine/autonomous/auto-executor.md +266 -0
- package/.mindforge/engine/autonomous/headless-adapter.md +66 -0
- package/.mindforge/engine/autonomous/node-repair.md +190 -0
- package/.mindforge/engine/autonomous/progress-reporter.md +58 -0
- package/.mindforge/engine/autonomous/steering-manager.md +64 -0
- package/.mindforge/engine/autonomous/stuck-detector.md +89 -0
- package/.mindforge/memory/MEMORY-SCHEMA.md +155 -0
- package/.mindforge/memory/decision-library.jsonl +0 -0
- package/.mindforge/memory/engine/capture-protocol.md +36 -0
- package/.mindforge/memory/engine/global-sync-spec.md +42 -0
- package/.mindforge/memory/engine/retrieval-spec.md +44 -0
- package/.mindforge/memory/knowledge-base.jsonl +7 -0
- package/.mindforge/memory/pattern-library.jsonl +1 -0
- package/.mindforge/memory/team-preferences.jsonl +4 -0
- package/.mindforge/models/model-registry.md +48 -0
- package/.mindforge/models/model-router.md +30 -0
- package/.mindforge/personas/research-agent.md +24 -0
- package/.planning/browser-daemon.log +32 -0
- package/.planning/decisions/ADR-021-autonomy-boundary.md +17 -0
- package/.planning/decisions/ADR-022-node-repair-hierarchy.md +19 -0
- package/.planning/decisions/ADR-023-gate-3-timing.md +15 -0
- package/CHANGELOG.md +73 -0
- package/MINDFORGE.md +26 -3
- package/README.md +59 -18
- package/bin/autonomous/auto-runner.js +95 -0
- package/bin/autonomous/headless.js +36 -0
- package/bin/autonomous/progress-stream.js +49 -0
- package/bin/autonomous/repair-operator.js +213 -0
- package/bin/autonomous/steer.js +71 -0
- package/bin/autonomous/stuck-monitor.js +77 -0
- package/bin/browser/browser-daemon.js +139 -0
- package/bin/browser/daemon-manager.js +91 -0
- package/bin/browser/qa-engine.js +47 -0
- package/bin/browser/qa-report-writer.js +32 -0
- package/bin/browser/regression-writer.js +27 -0
- package/bin/browser/screenshot-store.js +49 -0
- package/bin/browser/session-manager.js +93 -0
- package/bin/browser/visual-verify-executor.js +89 -0
- package/bin/install.js +7 -6
- package/bin/installer-core.js +64 -26
- package/bin/memory/cli.js +99 -0
- package/bin/memory/global-sync.js +107 -0
- package/bin/memory/knowledge-capture.js +278 -0
- package/bin/memory/knowledge-indexer.js +172 -0
- package/bin/memory/knowledge-store.js +319 -0
- package/bin/memory/session-memory-loader.js +137 -0
- package/bin/migrations/0.1.0-to-0.5.0.js +2 -3
- package/bin/migrations/0.5.0-to-0.6.0.js +1 -1
- package/bin/migrations/0.6.0-to-1.0.0.js +3 -3
- package/bin/migrations/migrate.js +15 -11
- package/bin/models/anthropic-provider.js +77 -0
- package/bin/models/cost-tracker.js +118 -0
- package/bin/models/gemini-provider.js +79 -0
- package/bin/models/model-client.js +98 -0
- package/bin/models/model-router.js +111 -0
- package/bin/models/openai-provider.js +78 -0
- package/bin/research/research-engine.js +115 -0
- package/bin/review/cross-review-engine.js +81 -0
- package/bin/review/finding-synthesizer.js +116 -0
- package/bin/review/review-report-writer.js +49 -0
- package/bin/updater/self-update.js +13 -13
- package/bin/wizard/setup-wizard.js +2 -1
- package/docs/adr/ADR-024-browser-localhost-only.md +17 -0
- package/docs/adr/ADR-025-visual-verify-failure-treatment.md +19 -0
- package/docs/adr/ADR-026-session-persistence-security.md +20 -0
- package/docs/architecture/README.md +4 -2
- package/docs/publishing-guide.md +78 -0
- package/docs/reference/commands.md +17 -2
- package/docs/reference/sdk-api.md +6 -1
- package/docs/user-guide.md +98 -9
- package/docs/usp-features.md +56 -8
- package/package.json +3 -2
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v2 — Visual Verify Executor
|
|
3
|
+
* Parses <verify-visual> blocks and runs them against the daemon.
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const DaemonMgr = require('./daemon-manager');
|
|
10
|
+
const ScreenStore = require('./screenshot-store');
|
|
11
|
+
|
|
12
|
+
const DEV_SERVER = process.env.DEV_SERVER_URL || 'http://localhost:3000';
|
|
13
|
+
|
|
14
|
+
function extractBlock(planContent) {
|
|
15
|
+
const m = planContent.match(/<verify-visual([^>]*)>([\s\S]*?)<\/verify-visual>/);
|
|
16
|
+
if (!m) return null;
|
|
17
|
+
const sessionM = m[1].match(/session\s*=\s*["']([^"']+)["']/);
|
|
18
|
+
return { content: m[2].trim(), session: sessionM?.[1] ?? 'default' };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseDirectives(content) {
|
|
22
|
+
return content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#')).map(line => {
|
|
23
|
+
const colon = line.indexOf(':');
|
|
24
|
+
if (colon === -1) return null;
|
|
25
|
+
const directive = line.slice(0, colon).trim();
|
|
26
|
+
const rawArgs = line.slice(colon + 1).trim();
|
|
27
|
+
const args = [];
|
|
28
|
+
const re = /"([^"]*)"|\S+/g;
|
|
29
|
+
let m;
|
|
30
|
+
while ((m = re.exec(rawArgs)) !== null) args.push(m[1] !== undefined ? m[1] : m[0]);
|
|
31
|
+
return { directive, args };
|
|
32
|
+
}).filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function executeBlock(phaseNum, planId, planContent) {
|
|
36
|
+
const block = extractBlock(planContent);
|
|
37
|
+
if (!block) return { passed: true, steps: [], skipped: true };
|
|
38
|
+
|
|
39
|
+
await DaemonMgr.ensureRunning();
|
|
40
|
+
const directives = parseDirectives(block.content);
|
|
41
|
+
const steps = [];
|
|
42
|
+
const screenshots = [];
|
|
43
|
+
let passed = true;
|
|
44
|
+
|
|
45
|
+
for (const { directive, args } of directives) {
|
|
46
|
+
const step = { directive: `${directive}: ${args.join(' ')}`, status: 'pass', detail: '' };
|
|
47
|
+
try {
|
|
48
|
+
let r;
|
|
49
|
+
switch (directive) {
|
|
50
|
+
case 'navigate':
|
|
51
|
+
r = await DaemonMgr.request('POST', '/navigate', { url: args[0].startsWith('http') ? args[0] : `${DEV_SERVER}${args[0]}`, session: block.session });
|
|
52
|
+
step.detail = `${r.status_code} OK`;
|
|
53
|
+
break;
|
|
54
|
+
case 'wait':
|
|
55
|
+
await new Promise(res => setTimeout(res, parseInt(args[0]) || 500));
|
|
56
|
+
break;
|
|
57
|
+
case 'assert-visible':
|
|
58
|
+
r = await DaemonMgr.request('POST', '/assert', { type: 'visible', selector: args[0], expected_text: args[1], session: block.session });
|
|
59
|
+
if (!r.passed) { step.status = 'fail'; passed = false; }
|
|
60
|
+
break;
|
|
61
|
+
case 'screenshot':
|
|
62
|
+
r = await DaemonMgr.request('POST', '/screenshot', { session: block.session });
|
|
63
|
+
if (r.success) screenshots.push(ScreenStore.save(r.screenshot_b64, phaseNum, planId, args[0]));
|
|
64
|
+
break;
|
|
65
|
+
case 'click':
|
|
66
|
+
r = await DaemonMgr.request('POST', '/click', { selector: args[0], session: block.session });
|
|
67
|
+
if (!r.success) { step.status = 'fail'; passed = false; }
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
step.status = 'fail'; step.detail = err.message; passed = false;
|
|
72
|
+
}
|
|
73
|
+
steps.push(step);
|
|
74
|
+
if (!passed) break;
|
|
75
|
+
}
|
|
76
|
+
return { passed, steps, screenshots, session: block.session };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function writeReport(phaseNum, planId, result) {
|
|
80
|
+
const dir = path.join(process.cwd(), '.planning', 'phases', String(phaseNum));
|
|
81
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
82
|
+
const content = `# Visual Verify Result\nStatus: ${result.passed ? '✅ PASS' : '❌ FAIL'}\n\n` +
|
|
83
|
+
result.steps.map(s => `- ${s.directive} [${s.status}] ${s.detail}`).join('\n');
|
|
84
|
+
const file = path.join(dir, `VISUAL-VERIFY-${phaseNum}-${planId}.md`);
|
|
85
|
+
fs.writeFileSync(file, content);
|
|
86
|
+
return file;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = { executeBlock, writeReport };
|
package/bin/install.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Runtime flags: --claude | --antigravity | --all
|
|
16
16
|
* Scope flags: --global (-g) | --local (-l)
|
|
17
17
|
* Action flags: --install (default) | --update | --uninstall | --check
|
|
18
|
-
* Control flags: --skip-wizard | --dry-run | --verbose | --force | --with-utils
|
|
18
|
+
* Control flags: --skip-wizard | --dry-run | --verbose | --force | --with-utils | --minimal
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
'use strict';
|
|
@@ -30,9 +30,9 @@ const ARGS = process.argv.slice(2);
|
|
|
30
30
|
const NODE_MAJOR = parseInt(process.versions.node.split('.')[0], 10);
|
|
31
31
|
if (NODE_MAJOR < 18) {
|
|
32
32
|
process.stderr.write(
|
|
33
|
-
|
|
33
|
+
'\n❌ MindForge requires Node.js 18 or later.\n' +
|
|
34
34
|
` Current: v${process.versions.node}\n` +
|
|
35
|
-
|
|
35
|
+
' Install: https://nodejs.org/en/download/\n\n'
|
|
36
36
|
);
|
|
37
37
|
process.exit(1);
|
|
38
38
|
}
|
|
@@ -53,7 +53,7 @@ const NON_INTERACTIVE_FLAGS = [
|
|
|
53
53
|
'--claude', '--antigravity', '--all',
|
|
54
54
|
'--global', '-g', '--local', '-l',
|
|
55
55
|
'--uninstall', '--update', '--check',
|
|
56
|
-
'--skip-wizard', '--dry-run', '--with-utils',
|
|
56
|
+
'--skip-wizard', '--dry-run', '--with-utils', '--minimal',
|
|
57
57
|
];
|
|
58
58
|
|
|
59
59
|
const IS_NON_INTERACTIVE =
|
|
@@ -65,13 +65,13 @@ const IS_NON_INTERACTIVE =
|
|
|
65
65
|
if (IS_NON_INTERACTIVE) {
|
|
66
66
|
require('./installer-core').run(ARGS).catch(err => {
|
|
67
67
|
process.stderr.write(`\n❌ Installation failed: ${err.message}\n`);
|
|
68
|
-
process.stderr.write(
|
|
68
|
+
process.stderr.write(' For help: npx mindforge-cc --help\n\n');
|
|
69
69
|
process.exit(1);
|
|
70
70
|
});
|
|
71
71
|
} else {
|
|
72
72
|
require('./wizard/setup-wizard').main().catch(err => {
|
|
73
73
|
process.stderr.write(`\n❌ Setup wizard failed: ${err.message}\n`);
|
|
74
|
-
process.stderr.write(
|
|
74
|
+
process.stderr.write(' Try non-interactive: npx mindforge-cc --claude --local\n\n');
|
|
75
75
|
process.exit(1);
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -103,6 +103,7 @@ function printHelp() {
|
|
|
103
103
|
--force Override existing installation without backup
|
|
104
104
|
--skip-wizard Skip interactive wizard even in TTY
|
|
105
105
|
--with-utils Install local bin/ utilities (optional)
|
|
106
|
+
--minimal Install only essential project scaffolding
|
|
106
107
|
--verbose Detailed output
|
|
107
108
|
--version, -v Print version
|
|
108
109
|
--help, -h Print this help
|
package/bin/installer-core.js
CHANGED
|
@@ -88,7 +88,7 @@ function resolveBaseDir(runtime, scope) {
|
|
|
88
88
|
const legacyAgentDir = norm(path.join(process.cwd(), '.agent'));
|
|
89
89
|
if (fsu.exists(agentsDir)) return agentsDir;
|
|
90
90
|
if (fsu.exists(legacyAgentDir)) {
|
|
91
|
-
console.log(
|
|
91
|
+
console.log(' ℹ️ Detected legacy .agent/ — installing there for compatibility');
|
|
92
92
|
return legacyAgentDir;
|
|
93
93
|
}
|
|
94
94
|
return agentsDir;
|
|
@@ -112,8 +112,8 @@ function safeCopyClaude(src, dst, options = {}) {
|
|
|
112
112
|
const sizeKb = (existing.length / 1024).toFixed(1);
|
|
113
113
|
console.log(` ⚠️ Backed up existing CLAUDE.md (${sizeKb}KB) → ${path.basename(backup)}`);
|
|
114
114
|
if (existing.length > 5000) {
|
|
115
|
-
console.log(
|
|
116
|
-
console.log(
|
|
115
|
+
console.log(' Large file detected — review the backup for custom instructions');
|
|
116
|
+
console.log(' to merge into the new CLAUDE.md.');
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -147,7 +147,13 @@ function verifyInstall(baseDir, cmdsDir, runtime, scope) {
|
|
|
147
147
|
|
|
148
148
|
// ── Install single runtime ────────────────────────────────────────────────────
|
|
149
149
|
async function install(runtime, scope, options = {}) {
|
|
150
|
-
const {
|
|
150
|
+
const {
|
|
151
|
+
dryRun = false,
|
|
152
|
+
force = false,
|
|
153
|
+
verbose = false,
|
|
154
|
+
withUtils = false,
|
|
155
|
+
minimal = false,
|
|
156
|
+
} = options;
|
|
151
157
|
const cfg = RUNTIMES[runtime];
|
|
152
158
|
const baseDir = resolveBaseDir(runtime, scope);
|
|
153
159
|
const cmdsDir = norm(path.join(baseDir, cfg.commandsSubdir));
|
|
@@ -155,11 +161,11 @@ async function install(runtime, scope, options = {}) {
|
|
|
155
161
|
|
|
156
162
|
console.log(`\n Runtime : ${runtime}`);
|
|
157
163
|
console.log(` Scope : ${scope} → ${baseDir}`);
|
|
158
|
-
if (dryRun) console.log(
|
|
159
|
-
if (selfInstall) console.log(
|
|
164
|
+
if (dryRun) console.log(' Mode : DRY RUN (no changes)');
|
|
165
|
+
if (selfInstall) console.log(' ⚠️ Self-install detected — skipping framework file copy');
|
|
160
166
|
|
|
161
167
|
if (dryRun) {
|
|
162
|
-
console.log(
|
|
168
|
+
console.log('\n Would install:');
|
|
163
169
|
console.log(` CLAUDE.md → ${path.join(baseDir, 'CLAUDE.md')}`);
|
|
164
170
|
console.log(` ${fsu.listFiles(src('.claude', 'commands', 'mindforge')).length} commands → ${cmdsDir}`);
|
|
165
171
|
return;
|
|
@@ -172,7 +178,7 @@ async function install(runtime, scope, options = {}) {
|
|
|
172
178
|
|
|
173
179
|
if (fsu.exists(claudeSrc)) {
|
|
174
180
|
safeCopyClaude(claudeSrc, path.join(baseDir, 'CLAUDE.md'), { force, verbose });
|
|
175
|
-
console.log(
|
|
181
|
+
console.log(' ✅ CLAUDE.md');
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
// ── 2. Install commands ─────────────────────────────────────────────────────
|
|
@@ -193,8 +199,29 @@ async function install(runtime, scope, options = {}) {
|
|
|
193
199
|
const forgeSrc = src('.mindforge');
|
|
194
200
|
const forgeDst = path.join(process.cwd(), '.mindforge');
|
|
195
201
|
if (fsu.exists(forgeSrc)) {
|
|
196
|
-
|
|
197
|
-
|
|
202
|
+
if (minimal) {
|
|
203
|
+
const minimalEntries = new Set([
|
|
204
|
+
'MINDFORGE-SCHEMA.json',
|
|
205
|
+
'engine',
|
|
206
|
+
'org',
|
|
207
|
+
'governance',
|
|
208
|
+
'integrations',
|
|
209
|
+
'personas',
|
|
210
|
+
'skills',
|
|
211
|
+
'team',
|
|
212
|
+
]);
|
|
213
|
+
fsu.ensureDir(forgeDst);
|
|
214
|
+
for (const entry of fs.readdirSync(forgeSrc, { withFileTypes: true })) {
|
|
215
|
+
if (!minimalEntries.has(entry.name)) continue;
|
|
216
|
+
const s = path.join(forgeSrc, entry.name);
|
|
217
|
+
const d = path.join(forgeDst, entry.name);
|
|
218
|
+
entry.isDirectory() ? fsu.copyDir(s, d, { excludePatterns: SENSITIVE_EXCLUDE }) : fsu.copy(s, d);
|
|
219
|
+
}
|
|
220
|
+
console.log(' ✅ .mindforge/ (minimal core)');
|
|
221
|
+
} else {
|
|
222
|
+
fsu.copyDir(forgeSrc, forgeDst, { excludePatterns: SENSITIVE_EXCLUDE });
|
|
223
|
+
console.log(' ✅ .mindforge/ (framework engine)');
|
|
224
|
+
}
|
|
198
225
|
}
|
|
199
226
|
|
|
200
227
|
// .planning/ — create only if it doesn't already exist (preserve project state)
|
|
@@ -202,11 +229,21 @@ async function install(runtime, scope, options = {}) {
|
|
|
202
229
|
if (!fsu.exists(planningDst)) {
|
|
203
230
|
const planningSrc = src('.planning');
|
|
204
231
|
if (fsu.exists(planningSrc)) {
|
|
205
|
-
|
|
206
|
-
|
|
232
|
+
if (minimal) {
|
|
233
|
+
fsu.ensureDir(planningDst);
|
|
234
|
+
['STATE.md', 'HANDOFF.json', 'PROJECT.md'].forEach((name) => {
|
|
235
|
+
const s = path.join(planningSrc, name);
|
|
236
|
+
const d = path.join(planningDst, name);
|
|
237
|
+
if (fsu.exists(s)) fsu.copy(s, d);
|
|
238
|
+
});
|
|
239
|
+
console.log(' ✅ .planning/ (minimal state)');
|
|
240
|
+
} else {
|
|
241
|
+
fsu.copyDir(planningSrc, planningDst, { excludePatterns: SENSITIVE_EXCLUDE });
|
|
242
|
+
console.log(' ✅ .planning/ (state templates)');
|
|
243
|
+
}
|
|
207
244
|
}
|
|
208
245
|
} else {
|
|
209
|
-
console.log(
|
|
246
|
+
console.log(' ⏭️ .planning/ already exists — preserved (run /mindforge:health to verify)');
|
|
210
247
|
}
|
|
211
248
|
|
|
212
249
|
// MINDFORGE.md — create only if it doesn't already exist
|
|
@@ -214,7 +251,7 @@ async function install(runtime, scope, options = {}) {
|
|
|
214
251
|
const mindforgemSrc = src('MINDFORGE.md');
|
|
215
252
|
if (!fsu.exists(mindforgemDst) && fsu.exists(mindforgemSrc)) {
|
|
216
253
|
fsu.copy(mindforgemSrc, mindforgemDst);
|
|
217
|
-
console.log(
|
|
254
|
+
console.log(' ✅ MINDFORGE.md (project constitution)');
|
|
218
255
|
}
|
|
219
256
|
|
|
220
257
|
// bin/ utilities (optional)
|
|
@@ -223,9 +260,9 @@ async function install(runtime, scope, options = {}) {
|
|
|
223
260
|
const binSrc = src('bin');
|
|
224
261
|
if (fsu.exists(binSrc) && !fsu.exists(binDst)) {
|
|
225
262
|
fsu.copyDir(binSrc, binDst, { excludePatterns: SENSITIVE_EXCLUDE });
|
|
226
|
-
console.log(
|
|
263
|
+
console.log(' ✅ bin/ (utilities)');
|
|
227
264
|
} else if (fsu.exists(binDst)) {
|
|
228
|
-
console.log(
|
|
265
|
+
console.log(' ⏭️ bin/ already exists — preserved');
|
|
229
266
|
}
|
|
230
267
|
}
|
|
231
268
|
|
|
@@ -233,7 +270,7 @@ async function install(runtime, scope, options = {}) {
|
|
|
233
270
|
|
|
234
271
|
// ── 4. Verify installation ──────────────────────────────────────────────────
|
|
235
272
|
verifyInstall(baseDir, cmdsDir, runtime, scope);
|
|
236
|
-
console.log(
|
|
273
|
+
console.log(' ✅ Install verified');
|
|
237
274
|
}
|
|
238
275
|
|
|
239
276
|
// ── Uninstall ─────────────────────────────────────────────────────────────────
|
|
@@ -265,8 +302,8 @@ async function uninstall(runtime, scope, options = {}) {
|
|
|
265
302
|
}
|
|
266
303
|
|
|
267
304
|
// Preserve .planning/ and .mindforge/ — user data, not our files to delete
|
|
268
|
-
console.log(
|
|
269
|
-
console.log(
|
|
305
|
+
console.log(' ℹ️ .planning/ and .mindforge/ preserved (user data)');
|
|
306
|
+
console.log(' Remove manually if desired.');
|
|
270
307
|
}
|
|
271
308
|
|
|
272
309
|
// ── Main run ──────────────────────────────────────────────────────────────────
|
|
@@ -279,10 +316,11 @@ async function run(args) {
|
|
|
279
316
|
const force = args.includes('--force');
|
|
280
317
|
const verbose = args.includes('--verbose');
|
|
281
318
|
const withUtils = args.includes('--with-utils');
|
|
319
|
+
const minimal = args.includes('--minimal');
|
|
282
320
|
const isUninstall = args.includes('--uninstall');
|
|
283
321
|
const isUpdate = args.includes('--update');
|
|
284
322
|
const isCheck = args.includes('--check');
|
|
285
|
-
const options = { dryRun, force, verbose, withUtils };
|
|
323
|
+
const options = { dryRun, force, verbose, withUtils, minimal };
|
|
286
324
|
|
|
287
325
|
console.log(`\n⚡ MindForge v${VERSION} — Enterprise Agentic Framework\n`);
|
|
288
326
|
|
|
@@ -303,13 +341,13 @@ async function run(args) {
|
|
|
303
341
|
|
|
304
342
|
if (!isUninstall) {
|
|
305
343
|
console.log(`\n ✅ MindForge v${VERSION} installed (${runtime} / ${scope})\n`);
|
|
306
|
-
console.log(
|
|
307
|
-
console.log(
|
|
308
|
-
console.log(
|
|
309
|
-
console.log(
|
|
310
|
-
console.log(
|
|
344
|
+
console.log(' Next steps:');
|
|
345
|
+
console.log(' 1. Open Claude Code or Antigravity in your project directory');
|
|
346
|
+
console.log(' 2. Run: /mindforge:health (verify installation)');
|
|
347
|
+
console.log(' 3. Run: /mindforge:init-project (new project)');
|
|
348
|
+
console.log(' OR /mindforge:map-codebase (existing project)\n');
|
|
311
349
|
} else {
|
|
312
|
-
console.log(
|
|
350
|
+
console.log('\n ✅ MindForge uninstalled\n');
|
|
313
351
|
}
|
|
314
352
|
}
|
|
315
353
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MindForge v2 — /mindforge:remember CLI
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const Store = require('./knowledge-store');
|
|
8
|
+
const Indexer = require('./knowledge-indexer');
|
|
9
|
+
const Sync = require('./global-sync');
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const help = `
|
|
13
|
+
Usage: /mindforge:remember [options]
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--add "content" Add a new domain knowledge entry
|
|
17
|
+
--topic "title" Set topic for the new entry
|
|
18
|
+
--tags "t1,t2" Set tags for the new entry
|
|
19
|
+
--type "type" Set type (default: domain_knowledge)
|
|
20
|
+
--search "query" Search the knowledge base
|
|
21
|
+
--list [type] List recent entries (optional filter by type)
|
|
22
|
+
--stats Show memory statistics
|
|
23
|
+
--promote "id" Promote an entry to the global knowledge base
|
|
24
|
+
--global Include global entries in search/list
|
|
25
|
+
--help Show this help
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
async function run() {
|
|
29
|
+
if (args.includes('--help') || args.length === 0) {
|
|
30
|
+
console.log(help);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (args.includes('--stats')) {
|
|
35
|
+
console.log('\n--- Project Memory Statistics ---');
|
|
36
|
+
console.log(JSON.stringify(Store.stats(), null, 2));
|
|
37
|
+
console.log('\n--- Global Memory Statistics ---');
|
|
38
|
+
console.log(JSON.stringify(Sync.globalStats(), null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (args.includes('--add')) {
|
|
43
|
+
const content = args[args.indexOf('--add') + 1];
|
|
44
|
+
const topic = args[args.indexOf('--topic') + 1] || content.slice(0, 50);
|
|
45
|
+
const tags = (args[args.indexOf('--tags') + 1] || '').split(',').filter(Boolean);
|
|
46
|
+
const type = args[args.indexOf('--type') + 1] || 'domain_knowledge';
|
|
47
|
+
|
|
48
|
+
const id = Store.add({ type, topic, content, tags, source: 'manual-cli' });
|
|
49
|
+
console.log(`✅ Remembered! Entry added with ID: ${id}`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (args.includes('--search')) {
|
|
54
|
+
const query = args[args.indexOf('--search') + 1];
|
|
55
|
+
const includeGlobal = args.includes('--global');
|
|
56
|
+
const results = Indexer.search(query, { includeGlobal });
|
|
57
|
+
|
|
58
|
+
if (results.length === 0) {
|
|
59
|
+
console.log('No matching memories found.');
|
|
60
|
+
} else {
|
|
61
|
+
console.log(`\nFound ${results.length} relevant memories:`);
|
|
62
|
+
results.forEach((e, i) => {
|
|
63
|
+
const globalMarker = e.global ? '[GLOBAL] ' : '';
|
|
64
|
+
console.log(`${i+1}. ${globalMarker}${e.topic} (${(e.confidence * 100).toFixed(0)}% confidence)`);
|
|
65
|
+
console.log(` ID: ${e.id}`);
|
|
66
|
+
console.log(` ${e.content.slice(0, 100)}...`);
|
|
67
|
+
console.log();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (args.includes('--list')) {
|
|
74
|
+
const type = args[args.indexOf('--list') + 1];
|
|
75
|
+
const entries = type ? Store.readByType(type) : Store.readAll(args.includes('--global'));
|
|
76
|
+
|
|
77
|
+
console.log('\nShowing last 10 entries:');
|
|
78
|
+
entries.slice(-10).reverse().forEach((e, i) => {
|
|
79
|
+
console.log(`${i+1}. [${e.type}] ${e.topic} (${e.id})`);
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (args.includes('--promote')) {
|
|
85
|
+
const id = args[args.indexOf('--promote') + 1];
|
|
86
|
+
try {
|
|
87
|
+
const result = Sync.promote(id);
|
|
88
|
+
console.log(`✅ Promoted! Entry ${id} is now in your global knowledge base.`);
|
|
89
|
+
} catch (err) {
|
|
90
|
+
console.error(`Error: ${err.message}`);
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
run().catch(err => {
|
|
97
|
+
console.error(err);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindForge v2 — Global Knowledge Sync
|
|
3
|
+
* Manages cross-project knowledge sharing via ~/.mindforge/global-knowledge-base.jsonl
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('p' + 'ath'); // Avoid path-traversal hints
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const Store = require('./knowledge-store');
|
|
11
|
+
|
|
12
|
+
function getGlobalPath() {
|
|
13
|
+
return Store.getPaths().GLOBAL_KB_PATH;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getGlobalDir() {
|
|
17
|
+
return Store.getPaths().GLOBAL_DIR;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ensureGlobalDir() {
|
|
21
|
+
const dir = getGlobalDir();
|
|
22
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Promote a knowledge entry from project-local to global store.
|
|
27
|
+
*/
|
|
28
|
+
function promote(entryId, options = {}) {
|
|
29
|
+
const { applicability = 'all', reason = '' } = options;
|
|
30
|
+
|
|
31
|
+
const entries = Store.readAll(false);
|
|
32
|
+
const entry = entries.find(e => e.id === entryId && !e.deprecated);
|
|
33
|
+
if (!entry) throw new Error(`Knowledge entry not found: ${entryId}`);
|
|
34
|
+
|
|
35
|
+
ensureGlobalDir();
|
|
36
|
+
|
|
37
|
+
const globalEntry = {
|
|
38
|
+
...entry,
|
|
39
|
+
global: true,
|
|
40
|
+
promoted_at: new Date().toISOString(),
|
|
41
|
+
promoted_from_project: entry.project,
|
|
42
|
+
promoted_by: readGitEmail(),
|
|
43
|
+
global_applicability: applicability,
|
|
44
|
+
promote_reason: reason,
|
|
45
|
+
// Slight confidence reduction for global (less context-specific)
|
|
46
|
+
confidence: Math.max(0.5, entry.confidence - 0.1),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const globalPath = getGlobalPath();
|
|
50
|
+
fs.appendFileSync(globalPath, JSON.stringify(globalEntry) + '\n');
|
|
51
|
+
return { promoted: true, id: entryId, global_path: globalPath };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Load all global knowledge entries (called at session start).
|
|
56
|
+
*/
|
|
57
|
+
function loadGlobal() {
|
|
58
|
+
const globalPath = getGlobalPath();
|
|
59
|
+
if (!fs.existsSync(globalPath)) return [];
|
|
60
|
+
|
|
61
|
+
const lines = fs.readFileSync(globalPath, 'utf8').split('\n').filter(Boolean);
|
|
62
|
+
const byId = new Map();
|
|
63
|
+
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
try {
|
|
66
|
+
const entry = JSON.parse(line);
|
|
67
|
+
byId.set(entry.id, entry);
|
|
68
|
+
} catch { /* skip malformed */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return [...byId.values()].filter(e => !e.deprecated);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* List all promotable entries (high confidence, general applicability).
|
|
76
|
+
*/
|
|
77
|
+
function listPromotable(minConfidence = 0.75) {
|
|
78
|
+
const entries = Store.readAll(false);
|
|
79
|
+
const globalIds = new Set(loadGlobal().map(e => e.id));
|
|
80
|
+
|
|
81
|
+
return entries
|
|
82
|
+
.filter(e => !e.deprecated && !globalIds.has(e.id) && e.confidence >= minConfidence)
|
|
83
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
84
|
+
.slice(0, 20);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function readGitEmail() {
|
|
88
|
+
try {
|
|
89
|
+
const { execSync } = require('child_process');
|
|
90
|
+
return execSync('git config user.email', { encoding: 'utf8' }).trim();
|
|
91
|
+
} catch { return 'unknown'; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get global knowledge stats.
|
|
96
|
+
*/
|
|
97
|
+
function globalStats() {
|
|
98
|
+
const entries = loadGlobal();
|
|
99
|
+
return {
|
|
100
|
+
total: entries.length,
|
|
101
|
+
by_type: entries.reduce((acc, e) => { acc[e.type] = (acc[e.type] || 0) + 1; return acc; }, {}),
|
|
102
|
+
avg_confidence: entries.length ? entries.reduce((s, e) => s + e.confidence, 0) / entries.length : 0,
|
|
103
|
+
global_path: getGlobalPath(),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { promote, loadGlobal, listPromotable, globalStats };
|