mindforge-cc 1.0.5 → 2.0.0-alpha.6

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.
Files changed (119) hide show
  1. package/.agent/CLAUDE.md +83 -0
  2. package/.agent/mindforge/auto.md +22 -0
  3. package/.agent/mindforge/browse.md +26 -0
  4. package/.agent/mindforge/costs.md +11 -0
  5. package/.agent/mindforge/cross-review.md +17 -0
  6. package/.agent/mindforge/dashboard.md +98 -0
  7. package/.agent/mindforge/execute-phase.md +5 -3
  8. package/.agent/mindforge/init-project.md +12 -0
  9. package/.agent/mindforge/qa.md +16 -0
  10. package/.agent/mindforge/remember.md +14 -0
  11. package/.agent/mindforge/research.md +11 -0
  12. package/.agent/mindforge/steer.md +13 -0
  13. package/.agent/workflows/publish-release.md +36 -0
  14. package/.claude/CLAUDE.md +83 -0
  15. package/.claude/commands/mindforge/auto.md +22 -0
  16. package/.claude/commands/mindforge/browse.md +26 -0
  17. package/.claude/commands/mindforge/costs.md +11 -0
  18. package/.claude/commands/mindforge/cross-review.md +17 -0
  19. package/.claude/commands/mindforge/dashboard.md +98 -0
  20. package/.claude/commands/mindforge/execute-phase.md +5 -3
  21. package/.claude/commands/mindforge/qa.md +16 -0
  22. package/.claude/commands/mindforge/remember.md +14 -0
  23. package/.claude/commands/mindforge/research.md +11 -0
  24. package/.claude/commands/mindforge/steer.md +13 -0
  25. package/.mindforge/MINDFORGE-V2-SCHEMA.json +47 -0
  26. package/.mindforge/browser/daemon-protocol.md +24 -0
  27. package/.mindforge/browser/qa-engine.md +16 -0
  28. package/.mindforge/browser/session-manager.md +18 -0
  29. package/.mindforge/browser/visual-verify-spec.md +31 -0
  30. package/.mindforge/dashboard/api-reference.md +122 -0
  31. package/.mindforge/dashboard/dashboard-spec.md +96 -0
  32. package/.mindforge/engine/autonomous/auto-executor.md +266 -0
  33. package/.mindforge/engine/autonomous/headless-adapter.md +66 -0
  34. package/.mindforge/engine/autonomous/node-repair.md +190 -0
  35. package/.mindforge/engine/autonomous/progress-reporter.md +58 -0
  36. package/.mindforge/engine/autonomous/steering-manager.md +64 -0
  37. package/.mindforge/engine/autonomous/stuck-detector.md +89 -0
  38. package/.mindforge/memory/MEMORY-SCHEMA.md +155 -0
  39. package/.mindforge/memory/decision-library.jsonl +0 -0
  40. package/.mindforge/memory/engine/capture-protocol.md +36 -0
  41. package/.mindforge/memory/engine/global-sync-spec.md +42 -0
  42. package/.mindforge/memory/engine/retrieval-spec.md +44 -0
  43. package/.mindforge/memory/knowledge-base.jsonl +7 -0
  44. package/.mindforge/memory/pattern-library.jsonl +1 -0
  45. package/.mindforge/memory/team-preferences.jsonl +4 -0
  46. package/.mindforge/models/model-registry.md +48 -0
  47. package/.mindforge/models/model-router.md +30 -0
  48. package/.mindforge/personas/research-agent.md +24 -0
  49. package/.planning/approvals/v2-architecture-approval.json +15 -0
  50. package/.planning/browser-daemon.log +32 -0
  51. package/.planning/decisions/ADR-021-autonomy-boundary.md +17 -0
  52. package/.planning/decisions/ADR-022-node-repair-hierarchy.md +19 -0
  53. package/.planning/decisions/ADR-023-gate-3-timing.md +15 -0
  54. package/CHANGELOG.md +81 -0
  55. package/MINDFORGE.md +26 -3
  56. package/README.md +70 -18
  57. package/bin/autonomous/auto-runner.js +95 -0
  58. package/bin/autonomous/headless.js +36 -0
  59. package/bin/autonomous/progress-stream.js +49 -0
  60. package/bin/autonomous/repair-operator.js +213 -0
  61. package/bin/autonomous/steer.js +71 -0
  62. package/bin/autonomous/stuck-monitor.js +77 -0
  63. package/bin/browser/browser-daemon.js +139 -0
  64. package/bin/browser/daemon-manager.js +91 -0
  65. package/bin/browser/qa-engine.js +47 -0
  66. package/bin/browser/qa-report-writer.js +32 -0
  67. package/bin/browser/regression-writer.js +27 -0
  68. package/bin/browser/screenshot-store.js +49 -0
  69. package/bin/browser/session-manager.js +93 -0
  70. package/bin/browser/visual-verify-executor.js +89 -0
  71. package/bin/change-classifier.js +86 -0
  72. package/bin/dashboard/api-router.js +198 -0
  73. package/bin/dashboard/approval-handler.js +134 -0
  74. package/bin/dashboard/frontend/index.html +511 -0
  75. package/bin/dashboard/metrics-aggregator.js +296 -0
  76. package/bin/dashboard/server.js +135 -0
  77. package/bin/dashboard/sse-bridge.js +178 -0
  78. package/bin/dashboard/team-tracker.js +0 -0
  79. package/bin/governance/approve.js +60 -0
  80. package/bin/install.js +4 -4
  81. package/bin/installer-core.js +91 -35
  82. package/bin/memory/cli.js +99 -0
  83. package/bin/memory/global-sync.js +107 -0
  84. package/bin/memory/knowledge-capture.js +278 -0
  85. package/bin/memory/knowledge-indexer.js +172 -0
  86. package/bin/memory/knowledge-store.js +319 -0
  87. package/bin/memory/session-memory-loader.js +137 -0
  88. package/bin/migrations/0.1.0-to-0.5.0.js +2 -3
  89. package/bin/migrations/0.5.0-to-0.6.0.js +1 -1
  90. package/bin/migrations/0.6.0-to-1.0.0.js +3 -3
  91. package/bin/migrations/migrate.js +15 -11
  92. package/bin/mindforge-cli.js +87 -0
  93. package/bin/models/anthropic-provider.js +77 -0
  94. package/bin/models/cost-tracker.js +118 -0
  95. package/bin/models/gemini-provider.js +79 -0
  96. package/bin/models/model-client.js +98 -0
  97. package/bin/models/model-router.js +111 -0
  98. package/bin/models/openai-provider.js +78 -0
  99. package/bin/research/research-engine.js +115 -0
  100. package/bin/review/cross-review-engine.js +81 -0
  101. package/bin/review/finding-synthesizer.js +116 -0
  102. package/bin/review/review-report-writer.js +49 -0
  103. package/bin/updater/self-update.js +13 -13
  104. package/bin/wizard/setup-wizard.js +5 -1
  105. package/docs/adr/ADR-024-browser-localhost-only.md +17 -0
  106. package/docs/adr/ADR-025-visual-verify-failure-treatment.md +19 -0
  107. package/docs/adr/ADR-026-session-persistence-security.md +20 -0
  108. package/docs/architecture/README.md +6 -2
  109. package/docs/ci-cd.md +92 -0
  110. package/docs/commands-reference.md +1 -0
  111. package/docs/feature-dashboard.md +52 -0
  112. package/docs/publishing-guide.md +43 -0
  113. package/docs/reference/commands.md +17 -2
  114. package/docs/reference/sdk-api.md +6 -1
  115. package/docs/testing-current-version.md +130 -0
  116. package/docs/user-guide.md +115 -9
  117. package/docs/usp-features.md +70 -8
  118. package/docs/workflow-atlas.md +57 -0
  119. package/package.json +7 -3
package/bin/install.js CHANGED
@@ -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
- `\n❌ MindForge requires Node.js 18 or later.\n` +
33
+ '\n❌ MindForge requires Node.js 18 or later.\n' +
34
34
  ` Current: v${process.versions.node}\n` +
35
- ` Install: https://nodejs.org/en/download/\n\n`
35
+ ' Install: https://nodejs.org/en/download/\n\n'
36
36
  );
37
37
  process.exit(1);
38
38
  }
@@ -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(` For help: npx mindforge-cc --help\n\n`);
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(` Try non-interactive: npx mindforge-cc --claude --local\n\n`);
74
+ process.stderr.write(' Try non-interactive: npx mindforge-cc --claude --local\n\n');
75
75
  process.exit(1);
76
76
  });
77
77
  }
@@ -21,7 +21,7 @@ const RUNTIMES = {
21
21
  antigravity: {
22
22
  globalDir: path.join(os.homedir(), '.gemini', 'antigravity'),
23
23
  localDir: 'agents',
24
- commandsSubdir: 'mindforge',
24
+ commandsSubdir: 'workflows',
25
25
  entryFile: 'CLAUDE.md',
26
26
  },
27
27
  };
@@ -51,6 +51,31 @@ const fsu = {
51
51
  },
52
52
  };
53
53
 
54
+ // ── Registry Management ────────────────────────────────────────────────────────
55
+ const RegistryManager = {
56
+ getRegistryPath: () => path.join(os.homedir(), '.mindforge', 'registry.json'),
57
+
58
+ registerProject(projectPath) {
59
+ const regPath = this.getRegistryPath();
60
+ fsu.ensureDir(path.dirname(regPath));
61
+
62
+ let registry = { projects: [] };
63
+ if (fsu.exists(regPath)) {
64
+ try {
65
+ registry = JSON.parse(fsu.read(regPath));
66
+ } catch (e) {
67
+ console.error(' ⚠️ Registry file corrupted, recreating...');
68
+ }
69
+ }
70
+
71
+ if (!registry.projects.includes(projectPath)) {
72
+ registry.projects.push(projectPath);
73
+ fsu.write(regPath, JSON.stringify(registry, null, 2));
74
+ console.log(` ✅ Registered project in ${regPath}`);
75
+ }
76
+ }
77
+ };
78
+
54
79
  // ── Self-install detection ────────────────────────────────────────────────────
55
80
  function isSelfInstall() {
56
81
  const pkgPath = path.join(process.cwd(), 'package.json');
@@ -88,7 +113,7 @@ function resolveBaseDir(runtime, scope) {
88
113
  const legacyAgentDir = norm(path.join(process.cwd(), '.agent'));
89
114
  if (fsu.exists(agentsDir)) return agentsDir;
90
115
  if (fsu.exists(legacyAgentDir)) {
91
- console.log(` ℹ️ Detected legacy .agent/ — installing there for compatibility`);
116
+ console.log(' ℹ️ Detected legacy .agent/ — installing there for compatibility');
92
117
  return legacyAgentDir;
93
118
  }
94
119
  return agentsDir;
@@ -112,8 +137,8 @@ function safeCopyClaude(src, dst, options = {}) {
112
137
  const sizeKb = (existing.length / 1024).toFixed(1);
113
138
  console.log(` ⚠️ Backed up existing CLAUDE.md (${sizeKb}KB) → ${path.basename(backup)}`);
114
139
  if (existing.length > 5000) {
115
- console.log(` Large file detected — review the backup for custom instructions`);
116
- console.log(` to merge into the new CLAUDE.md.`);
140
+ console.log(' Large file detected — review the backup for custom instructions');
141
+ console.log(' to merge into the new CLAUDE.md.');
117
142
  }
118
143
  }
119
144
  }
@@ -125,14 +150,14 @@ function safeCopyClaude(src, dst, options = {}) {
125
150
 
126
151
  // ── Install verification ──────────────────────────────────────────────────────
127
152
  function verifyInstall(baseDir, cmdsDir, runtime, scope) {
128
- // Minimum required files for a functional installation
153
+ const pfx = runtime === 'antigravity' ? 'mindforge:' : '';
129
154
  const required = [
130
- path.join(baseDir, 'CLAUDE.md'),
131
- path.join(cmdsDir, 'help.md'),
132
- path.join(cmdsDir, 'init-project.md'),
133
- path.join(cmdsDir, 'health.md'),
134
- path.join(cmdsDir, 'execute-phase.md'),
135
- path.join(cmdsDir, 'security-scan.md'),
155
+ scope === 'local' ? path.join(process.cwd(), 'CLAUDE.md') : path.join(baseDir, 'CLAUDE.md'),
156
+ path.join(cmdsDir, `${pfx}help.md`),
157
+ path.join(cmdsDir, `${pfx}init-project.md`),
158
+ path.join(cmdsDir, `${pfx}health.md`),
159
+ path.join(cmdsDir, `${pfx}execute-phase.md`),
160
+ path.join(cmdsDir, `${pfx}security-scan.md`),
136
161
  ];
137
162
 
138
163
  const missing = required.filter(f => !fsu.exists(f));
@@ -161,24 +186,38 @@ async function install(runtime, scope, options = {}) {
161
186
 
162
187
  console.log(`\n Runtime : ${runtime}`);
163
188
  console.log(` Scope : ${scope} → ${baseDir}`);
164
- if (dryRun) console.log(` Mode : DRY RUN (no changes)`);
165
- if (selfInstall) console.log(` ⚠️ Self-install detected — skipping framework file copy`);
189
+ if (dryRun) console.log(' Mode : DRY RUN (no changes)');
190
+ if (selfInstall) console.log(' ⚠️ Self-install detected — skipping framework file copy');
166
191
 
167
192
  if (dryRun) {
168
- console.log(`\n Would install:`);
193
+ console.log('\n Would install:');
169
194
  console.log(` CLAUDE.md → ${path.join(baseDir, 'CLAUDE.md')}`);
170
195
  console.log(` ${fsu.listFiles(src('.claude', 'commands', 'mindforge')).length} commands → ${cmdsDir}`);
171
196
  return;
172
197
  }
173
198
 
174
- // ── 1. Install CLAUDE.md ────────────────────────────────────────────────────
199
+ // ── 1. Install CLAUDE.md (Root standardization for IDEs) ────────────────────
175
200
  const claudeSrc = runtime === 'claude'
176
201
  ? src('.claude', 'CLAUDE.md')
177
202
  : src('.agent', 'CLAUDE.md');
178
203
 
179
204
  if (fsu.exists(claudeSrc)) {
205
+ // Keep legacy location based on runtime config
180
206
  safeCopyClaude(claudeSrc, path.join(baseDir, 'CLAUDE.md'), { force, verbose });
181
- console.log(` ✅ CLAUDE.md`);
207
+
208
+ // ✨ STANDARD: Inject into project root and IDE-specific rules files
209
+ if (scope === 'local' && !selfInstall) {
210
+ const rootClaude = path.join(process.cwd(), 'CLAUDE.md');
211
+ const rootCursor = path.join(process.cwd(), '.cursorrules');
212
+ const rootWindsurf = path.join(process.cwd(), '.windsurfrules');
213
+
214
+ safeCopyClaude(claudeSrc, rootClaude, { force, verbose });
215
+ safeCopyClaude(claudeSrc, rootCursor, { force, verbose });
216
+ safeCopyClaude(claudeSrc, rootWindsurf, { force, verbose });
217
+ console.log(' ✅ CLAUDE.md (Mirrored to project root & .cursorrules)');
218
+ } else {
219
+ console.log(' ✅ CLAUDE.md');
220
+ }
182
221
  }
183
222
 
184
223
  // ── 2. Install commands ─────────────────────────────────────────────────────
@@ -189,8 +228,24 @@ async function install(runtime, scope, options = {}) {
189
228
  if (fsu.exists(cmdSrc)) {
190
229
  fsu.ensureDir(cmdsDir);
191
230
  const files = fsu.listFiles(cmdSrc).filter(f => f.endsWith('.md'));
192
- files.forEach(f => fsu.copy(path.join(cmdSrc, f), path.join(cmdsDir, f)));
193
- console.log(` ✅ ${files.length} commands`);
231
+
232
+ // Install for specific runtime
233
+ files.forEach(f => {
234
+ const targetName = runtime === 'antigravity' ? `mindforge:${f}` : f;
235
+ fsu.copy(path.join(cmdSrc, f), path.join(cmdsDir, targetName));
236
+ });
237
+
238
+ // ✨ STANDARD: Mirror to .claude/commands for cross-IDE compatibility (Cursor/Windsurf/Claude Code)
239
+ if (scope === 'local' && runtime !== 'claude' && !selfInstall) {
240
+ const standardCmdDir = path.join(process.cwd(), '.claude', 'commands', 'mindforge');
241
+ fsu.ensureDir(standardCmdDir);
242
+ files.forEach(f => {
243
+ fsu.copy(path.join(cmdSrc, f), path.join(standardCmdDir, f));
244
+ });
245
+ console.log(` ✅ ${files.length} commands (Mirrored to .claude/commands/mindforge/)`);
246
+ } else {
247
+ console.log(` ✅ ${files.length} commands`);
248
+ }
194
249
  }
195
250
 
196
251
  // ── 3. Framework files (local scope only, non-self-install) ─────────────────
@@ -217,10 +272,10 @@ async function install(runtime, scope, options = {}) {
217
272
  const d = path.join(forgeDst, entry.name);
218
273
  entry.isDirectory() ? fsu.copyDir(s, d, { excludePatterns: SENSITIVE_EXCLUDE }) : fsu.copy(s, d);
219
274
  }
220
- console.log(` ✅ .mindforge/ (minimal core)`);
275
+ console.log(' ✅ .mindforge/ (minimal core)');
221
276
  } else {
222
277
  fsu.copyDir(forgeSrc, forgeDst, { excludePatterns: SENSITIVE_EXCLUDE });
223
- console.log(` ✅ .mindforge/ (framework engine)`);
278
+ console.log(' ✅ .mindforge/ (framework engine)');
224
279
  }
225
280
  }
226
281
 
@@ -236,14 +291,14 @@ async function install(runtime, scope, options = {}) {
236
291
  const d = path.join(planningDst, name);
237
292
  if (fsu.exists(s)) fsu.copy(s, d);
238
293
  });
239
- console.log(` ✅ .planning/ (minimal state)`);
294
+ console.log(' ✅ .planning/ (minimal state)');
240
295
  } else {
241
296
  fsu.copyDir(planningSrc, planningDst, { excludePatterns: SENSITIVE_EXCLUDE });
242
- console.log(` ✅ .planning/ (state templates)`);
297
+ console.log(' ✅ .planning/ (state templates)');
243
298
  }
244
299
  }
245
300
  } else {
246
- console.log(` ⏭️ .planning/ already exists — preserved (run /mindforge:health to verify)`);
301
+ console.log(' ⏭️ .planning/ already exists — preserved (run /mindforge:health to verify)');
247
302
  }
248
303
 
249
304
  // MINDFORGE.md — create only if it doesn't already exist
@@ -251,7 +306,7 @@ async function install(runtime, scope, options = {}) {
251
306
  const mindforgemSrc = src('MINDFORGE.md');
252
307
  if (!fsu.exists(mindforgemDst) && fsu.exists(mindforgemSrc)) {
253
308
  fsu.copy(mindforgemSrc, mindforgemDst);
254
- console.log(` ✅ MINDFORGE.md (project constitution)`);
309
+ console.log(' ✅ MINDFORGE.md (project constitution)');
255
310
  }
256
311
 
257
312
  // bin/ utilities (optional)
@@ -260,17 +315,18 @@ async function install(runtime, scope, options = {}) {
260
315
  const binSrc = src('bin');
261
316
  if (fsu.exists(binSrc) && !fsu.exists(binDst)) {
262
317
  fsu.copyDir(binSrc, binDst, { excludePatterns: SENSITIVE_EXCLUDE });
263
- console.log(` ✅ bin/ (utilities)`);
318
+ console.log(' ✅ bin/ (utilities)');
264
319
  } else if (fsu.exists(binDst)) {
265
- console.log(` ⏭️ bin/ already exists — preserved`);
320
+ console.log(' ⏭️ bin/ already exists — preserved');
266
321
  }
267
322
  }
268
323
 
324
+ RegistryManager.registerProject(process.cwd());
269
325
  }
270
326
 
271
327
  // ── 4. Verify installation ──────────────────────────────────────────────────
272
328
  verifyInstall(baseDir, cmdsDir, runtime, scope);
273
- console.log(` ✅ Install verified`);
329
+ console.log(' ✅ Install verified');
274
330
  }
275
331
 
276
332
  // ── Uninstall ─────────────────────────────────────────────────────────────────
@@ -302,8 +358,8 @@ async function uninstall(runtime, scope, options = {}) {
302
358
  }
303
359
 
304
360
  // Preserve .planning/ and .mindforge/ — user data, not our files to delete
305
- console.log(` ℹ️ .planning/ and .mindforge/ preserved (user data)`);
306
- console.log(` Remove manually if desired.`);
361
+ console.log(' ℹ️ .planning/ and .mindforge/ preserved (user data)');
362
+ console.log(' Remove manually if desired.');
307
363
  }
308
364
 
309
365
  // ── Main run ──────────────────────────────────────────────────────────────────
@@ -341,13 +397,13 @@ async function run(args) {
341
397
 
342
398
  if (!isUninstall) {
343
399
  console.log(`\n ✅ MindForge v${VERSION} installed (${runtime} / ${scope})\n`);
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`);
400
+ console.log(' Next steps:');
401
+ console.log(' 1. Open Claude Code or Antigravity in your project directory');
402
+ console.log(' 2. Run: /mindforge:health (verify installation)');
403
+ console.log(' 3. Run: /mindforge:init-project (new project)');
404
+ console.log(' OR /mindforge:map-codebase (existing project)\n');
349
405
  } else {
350
- console.log(`\n ✅ MindForge uninstalled\n`);
406
+ console.log('\n ✅ MindForge uninstalled\n');
351
407
  }
352
408
  }
353
409
 
@@ -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 };