@mnemosyne_os/forge 1.2.4 → 1.2.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.
@@ -0,0 +1,514 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.chronicleCommand = void 0;
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+ // MnemoForge — chronicle commands
42
+ // ─────────────────────────────────────────────────────────────────────────────
43
+ const commander_1 = require("commander");
44
+ const chalk_1 = __importDefault(require("chalk"));
45
+ const inquirer_1 = __importDefault(require("inquirer"));
46
+ const fs_1 = __importDefault(require("fs"));
47
+ const path_1 = __importDefault(require("path"));
48
+ const vault_js_1 = require("../lib/vault.js");
49
+ const chronicle_js_1 = require("../lib/chronicle.js");
50
+ const init_flow_js_1 = require("../lib/init-flow.js");
51
+ const index_js_1 = require("../lib/sources/index.js");
52
+ const ui_js_1 = require("../lib/ui.js");
53
+ exports.chronicleCommand = new commander_1.Command('chronicle')
54
+ .description('MnemoChronicle — multi-agent memory archiving system');
55
+ // ── init ──────────────────────────────────────────────────────────────────
56
+ exports.chronicleCommand
57
+ .command('init')
58
+ .description('Initialize .cli_resonance vault — choose IDE / Provider / Model')
59
+ .action(async () => {
60
+ const existing = (0, vault_js_1.loadVaultConfig)();
61
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — Vault Init\n'));
62
+ if (existing) {
63
+ console.log(chalk_1.default.yellow(' ⚠ Re-configuring vault:'));
64
+ console.log(chalk_1.default.gray(' IDE: ' + existing.ide));
65
+ console.log(chalk_1.default.gray(' Provider: ' + existing.provider + '\n'));
66
+ }
67
+ const profile = await (0, init_flow_js_1.askPrimaryProfile)(existing);
68
+ const reg = existing?.registeredModels ?? [];
69
+ if (existing && (existing.ide !== profile.ide || existing.provider !== profile.provider)) {
70
+ const old = { ide: existing.ide, provider: existing.provider, defaultChronicleStyle: existing.defaultChronicleStyle };
71
+ if (!reg.some(m => m.ide === old.ide && m.provider === old.provider))
72
+ reg.unshift(old);
73
+ }
74
+ let extra = await (0, init_flow_js_1.askExtraProfile)(reg.length);
75
+ while (extra) {
76
+ console.log(chalk_1.default.green(' ✔ Added: ' + extra.ide + ' / ' + extra.provider + ' · ' + extra.defaultChronicleStyle));
77
+ reg.push(extra);
78
+ extra = await (0, init_flow_js_1.askExtraProfile)(reg.length);
79
+ }
80
+ const config = { ...profile, registeredModels: reg };
81
+ (0, vault_js_1.saveVaultConfig)(config);
82
+ const dir = config.vaultPath + '/.cli_resonance/' + config.ide + '/' + config.provider + '/';
83
+ console.log(chalk_1.default.green('\n ✔ Vault configured!'));
84
+ console.log(chalk_1.default.gray(' IDE: ') + chalk_1.default.white(config.ide));
85
+ console.log(chalk_1.default.gray(' Provider: ') + chalk_1.default.white(config.provider));
86
+ console.log(chalk_1.default.gray(' Style: ') + chalk_1.default.hex('#A78BFA')(config.defaultChronicleStyle ?? 'session'));
87
+ if (reg.length > 0)
88
+ console.log(chalk_1.default.gray(' + ' + reg.length + ' extra profile(s)'));
89
+ console.log(chalk_1.default.gray('\n Chronicles → ') + chalk_1.default.hex('#A78BFA')(dir) + '\n');
90
+ console.log(chalk_1.default.gray(' Run ') + chalk_1.default.white('mnemoforge') + chalk_1.default.gray(' to see your dashboard.\n'));
91
+ });
92
+ // ── commit ────────────────────────────────────────────────────────────────
93
+ exports.chronicleCommand
94
+ .command('commit')
95
+ .description('Write a new Chronicle to the vault')
96
+ .option('-t, --title <title>', 'Chronicle title')
97
+ .option('--type <type>', 'Chronicle style: session | reflection | decision | sweep | narcissus')
98
+ .option('--tags <tags>', 'Comma-separated tags')
99
+ .option('--auto', 'Auto-generate draft and open in VS Code')
100
+ .action(async (opts) => {
101
+ const config = (0, vault_js_1.loadVaultConfig)();
102
+ if (!config) {
103
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
104
+ process.exit(1);
105
+ }
106
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — New Chronicle\n'));
107
+ if (opts.auto) {
108
+ console.log(chalk_1.default.cyan(' ⟳ Reading conversation source…'));
109
+ const ctx = (0, index_js_1.readSourceForConfig)(config);
110
+ let draftContent = '';
111
+ let draftTitle = opts.title ?? 'Session chronicle';
112
+ if (ctx) {
113
+ draftContent = (0, index_js_1.generateChronicleDraft)(ctx, config);
114
+ draftTitle = opts.title || ctx.sessionTitle.slice(0, 60) || draftTitle;
115
+ console.log(chalk_1.default.green(` ✔ Draft from: ${ctx.conversationId.slice(0, 8)}…`));
116
+ console.log(chalk_1.default.gray(` Files: ${ctx.filesTouched.length} · Decisions: ${ctx.keyDecisions.length}`));
117
+ }
118
+ else {
119
+ console.log(chalk_1.default.yellow(' ⚠ No source — creating blank template.\n'));
120
+ draftContent = (0, index_js_1.generateChronicleDraft)({ conversationId: 'manual', startedAt: null, sessionTitle: draftTitle,
121
+ filesTouched: [], commandsRun: [], keyDecisions: [], rawTurns: [], sourcePath: '' }, config);
122
+ }
123
+ const tags = opts.tags ? opts.tags.split(',').map((t) => t.trim()) : [];
124
+ const { filePath, filename } = (0, chronicle_js_1.writeChronicle)({
125
+ title: draftTitle, type: (opts.type ?? config.defaultChronicleStyle ?? 'session'),
126
+ content: draftContent, tags, config,
127
+ });
128
+ console.log(chalk_1.default.green(`\n ✔ Chronicle created: ${filename}`));
129
+ console.log(chalk_1.default.gray(` ${filePath}`));
130
+ console.log(chalk_1.default.cyan('\n Opening in VS Code…\n'));
131
+ try {
132
+ const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
133
+ spawn('code', [filePath], { detached: true, stdio: 'ignore', shell: true }).unref();
134
+ }
135
+ catch {
136
+ console.log(chalk_1.default.gray(` (Could not open VS Code automatically)\n`));
137
+ }
138
+ return;
139
+ }
140
+ const answers = await inquirer_1.default.prompt([
141
+ { type: 'input', name: 'title', message: chalk_1.default.cyan('Chronicle title?'), default: opts.title, when: !opts.title, validate: (v) => v.trim() !== '' || 'Required' },
142
+ { type: 'input', name: 'content', message: chalk_1.default.cyan('Short summary') + chalk_1.default.gray(' (you can edit the file after)'), default: '' },
143
+ ]);
144
+ const title = opts.title || answers.title;
145
+ const tags = opts.tags ? opts.tags.split(',').map((t) => t.trim()) : [];
146
+ const { filePath, filename } = (0, chronicle_js_1.writeChronicle)({
147
+ title, type: (opts.type ?? config.defaultChronicleStyle ?? 'session'),
148
+ content: answers.content, tags, config,
149
+ });
150
+ console.log(chalk_1.default.green(`\n ✔ Chronicle written: ${filename}`));
151
+ console.log(chalk_1.default.gray(` ${filePath}\n`));
152
+ console.log(chalk_1.default.gray(` Tip: run with `) + chalk_1.default.white('--auto') + chalk_1.default.gray(' to generate a full draft automatically.\n'));
153
+ });
154
+ // ── archive ───────────────────────────────────────────────────────────────
155
+ exports.chronicleCommand
156
+ .command('archive')
157
+ .description('Archive an agent-written chronicle into the vault (primary workflow)')
158
+ .option('-f, --file <path>', 'Path to the .md chronicle file to archive')
159
+ .action(async (opts) => {
160
+ const config = (0, vault_js_1.loadVaultConfig)();
161
+ if (!config) {
162
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
163
+ process.exit(1);
164
+ }
165
+ if (!opts.file) {
166
+ console.log(chalk_1.default.red('\n ✖ Specify a file: mnemoforge chronicle archive --file <path>\n'));
167
+ process.exit(1);
168
+ }
169
+ const srcPath = path_1.default.resolve(opts.file);
170
+ if (!fs_1.default.existsSync(srcPath)) {
171
+ console.log(chalk_1.default.red(`\n ✖ File not found: ${srcPath}\n`));
172
+ process.exit(1);
173
+ }
174
+ const destDir = (0, vault_js_1.resolveChronicleDir)(config);
175
+ fs_1.default.mkdirSync(destDir, { recursive: true });
176
+ const filename = path_1.default.basename(srcPath);
177
+ const destPath = path_1.default.join(destDir, filename);
178
+ fs_1.default.copyFileSync(srcPath, destPath);
179
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — Archived\n'));
180
+ console.log(chalk_1.default.green(` ✔ ${filename}`));
181
+ console.log(chalk_1.default.gray(` → ${destPath}\n`));
182
+ });
183
+ // ── sweep ─────────────────────────────────────────────────────────────────
184
+ exports.chronicleCommand
185
+ .command('sweep')
186
+ .description('Create a daily sweep Chronicle from recent entries')
187
+ .action(async () => {
188
+ const config = (0, vault_js_1.loadVaultConfig)();
189
+ if (!config) {
190
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
191
+ process.exit(1);
192
+ }
193
+ const today = new Date().toISOString().slice(0, 10);
194
+ const recent = (0, vault_js_1.listChronicles)(config).filter(f => f.includes(today));
195
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — Daily Sweep\n'));
196
+ console.log(chalk_1.default.gray(` Found ${recent.length} chronicle(s) from today (${today})\n`));
197
+ if (recent.length === 0) {
198
+ console.log(chalk_1.default.yellow(' No chronicles today to sweep.\n'));
199
+ return;
200
+ }
201
+ const { doSweep } = await inquirer_1.default.prompt([{
202
+ type: 'confirm', name: 'doSweep', message: `Create sweep from ${recent.length} chronicle(s)?`, default: true,
203
+ }]);
204
+ if (!doSweep)
205
+ return;
206
+ const sweepContent = (0, chronicle_js_1.buildSweepContent)(recent, config);
207
+ const { filePath, filename } = (0, chronicle_js_1.writeChronicle)({
208
+ title: `Daily Sweep — ${today}`, type: 'sweep',
209
+ content: sweepContent, tags: ['sweep', today], config,
210
+ });
211
+ console.log(chalk_1.default.green(`\n ✔ Sweep written: ${filename}`));
212
+ console.log(chalk_1.default.gray(` ${filePath}\n`));
213
+ });
214
+ // ── list ──────────────────────────────────────────────────────────────────
215
+ exports.chronicleCommand
216
+ .command('list')
217
+ .description('List recent Chronicles in the vault')
218
+ .option('-n, --count <n>', 'Number of chronicles to show', '10')
219
+ .action((opts) => {
220
+ const config = (0, vault_js_1.loadVaultConfig)();
221
+ if (!config) {
222
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
223
+ process.exit(1);
224
+ }
225
+ const dir = (0, vault_js_1.resolveChronicleDir)(config);
226
+ const all = (0, vault_js_1.listChronicles)(config);
227
+ const chronicles = all.slice(0, parseInt(opts.count || '10'));
228
+ const TYPE_COLORS = {
229
+ session: chalk_1.default.hex('#60A5FA'), decision: chalk_1.default.hex('#FB923C'),
230
+ reflection: chalk_1.default.hex('#C084FC'), sweep: chalk_1.default.hex('#94A3B8'), narcissus: chalk_1.default.hex('#FBBF24'),
231
+ };
232
+ const TYPE_ICONS = {
233
+ session: '◈', decision: '◆', reflection: '◇', sweep: '↻', narcissus: '✦',
234
+ };
235
+ const parseChronicle = (filename) => {
236
+ const filePath = path_1.default.join(dir, filename);
237
+ const rawSlug = filename.replace(/^CHRONICLE-\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, '').replace(/-/g, ' ');
238
+ let title = rawSlug.charAt(0).toUpperCase() + rawSlug.slice(1);
239
+ let type = 'session';
240
+ const tags = [];
241
+ let excerpt = '';
242
+ const dateMatch = filename.match(/CHRONICLE-(\d{4}-\d{2}-\d{2})/);
243
+ let date = dateMatch ? dateMatch[1] : '';
244
+ try {
245
+ const raw = fs_1.default.readFileSync(filePath, 'utf8');
246
+ const resonanceIdx = raw.indexOf('<!--resonance');
247
+ const content = resonanceIdx !== -1 ? raw.slice(0, resonanceIdx) : raw;
248
+ const lines = content.split('\n');
249
+ let inFrontmatter = false, frontmatterDone = false, dividerCount = 0;
250
+ const bodyLines = [];
251
+ for (const line of lines.slice(0, 40)) {
252
+ const l = line.trim();
253
+ if (l === '---' && !frontmatterDone) {
254
+ dividerCount++;
255
+ if (dividerCount === 1) {
256
+ inFrontmatter = true;
257
+ continue;
258
+ }
259
+ if (dividerCount === 2) {
260
+ inFrontmatter = false;
261
+ frontmatterDone = true;
262
+ continue;
263
+ }
264
+ }
265
+ if (inFrontmatter) {
266
+ if (l.startsWith('title:'))
267
+ title = l.replace('title:', '').trim().replace(/^['"]|['"]$/g, '');
268
+ if (l.startsWith('type:'))
269
+ type = l.replace('type:', '').trim();
270
+ if (l.startsWith('date:') && l.match(/\d{4}-\d{2}-\d{2}/))
271
+ date = l.match(/\d{4}-\d{2}-\d{2}/)[0];
272
+ continue;
273
+ }
274
+ if (l.startsWith('# ')) {
275
+ title = l.slice(2).trim();
276
+ continue;
277
+ }
278
+ if (l.startsWith('**Type**:')) {
279
+ type = l.replace('**Type**:', '').trim().toLowerCase();
280
+ continue;
281
+ }
282
+ if (l.startsWith('**Date**:')) {
283
+ const d = l.replace('**Date**:', '').trim();
284
+ if (d.match(/\d{4}-\d{2}-\d{2}/))
285
+ date = d.match(/\d{4}-\d{2}-\d{2}/)[0];
286
+ continue;
287
+ }
288
+ if (l.match(/^\*\*\w/))
289
+ continue;
290
+ if (l === '---')
291
+ continue;
292
+ if (l.length > 8 && frontmatterDone && !l.startsWith('#') && !l.match(/^[\w_]+:\s*/))
293
+ bodyLines.push(l);
294
+ }
295
+ const firstMeaningful = bodyLines.find(l => l.length > 10);
296
+ if (firstMeaningful)
297
+ excerpt = firstMeaningful.slice(0, 92) + (firstMeaningful.length > 92 ? '…' : '');
298
+ }
299
+ catch { /* use defaults */ }
300
+ return { title, type, tags, excerpt, date };
301
+ };
302
+ const SEP = chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72));
303
+ console.log(chalk_1.default.hex('#8B5CF6').bold(`\n ⬡ MnemoChronicle — Vault\n`));
304
+ if (config.workspace && config.resonanceProject) {
305
+ console.log(chalk_1.default.gray(' Workspace : ') + chalk_1.default.hex('#A78BFA')(config.workspace) + chalk_1.default.gray(' · Project : ') + chalk_1.default.hex('#A78BFA')(config.resonanceProject));
306
+ }
307
+ console.log(chalk_1.default.gray(' Agent : ') + chalk_1.default.white(config.ide) + chalk_1.default.gray(' / ') + chalk_1.default.white(config.provider));
308
+ console.log(chalk_1.default.gray(` Vault : `) + chalk_1.default.hex('#64748B')(dir));
309
+ console.log(chalk_1.default.gray(` Total : `) + chalk_1.default.white(String(all.length)) + chalk_1.default.gray(` chronicles · showing ${chronicles.length}\n`));
310
+ console.log(SEP);
311
+ chronicles.forEach((filename, i) => {
312
+ const { title, type, tags, excerpt, date } = parseChronicle(filename);
313
+ const typeColor = TYPE_COLORS[type] ?? chalk_1.default.white;
314
+ const icon = TYPE_ICONS[type] ?? '○';
315
+ const badge = typeColor(`[${type}]`);
316
+ const num = chalk_1.default.hex('#6B7280')(String(i + 1).padStart(2, ' '));
317
+ console.log(`\n ${num} ${badge} ` + chalk_1.default.hex('#94A3B8')('📅 ') + chalk_1.default.hex('#CBD5E1')(date) + chalk_1.default.hex('#475569')(' ') + typeColor(icon));
318
+ console.log(chalk_1.default.white(` ${title}`));
319
+ if (excerpt)
320
+ console.log(chalk_1.default.hex('#64748B')(` "${excerpt}"`));
321
+ if (tags.length > 0)
322
+ console.log(' ' + tags.map(t => chalk_1.default.hex('#7C3AED')(t)).join(' '));
323
+ });
324
+ console.log('\n' + SEP + '\n');
325
+ console.log(chalk_1.default.gray(' Tip: ') + chalk_1.default.white('mnemoforge chronicle list -n 20') + chalk_1.default.gray(' · ') + chalk_1.default.white('mnemoforge chronicle open') + chalk_1.default.gray(' to browse interactively\n'));
326
+ });
327
+ // ── open ──────────────────────────────────────────────────────────────────
328
+ exports.chronicleCommand
329
+ .command('open')
330
+ .description('Browse and read chronicles interactively (arrow keys + Enter)')
331
+ .option('-n, --count <n>', 'Max chronicles to show in picker', '20')
332
+ .action(async (opts) => {
333
+ const config = (0, vault_js_1.loadVaultConfig)();
334
+ if (!config) {
335
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
336
+ process.exit(1);
337
+ }
338
+ const dir = (0, vault_js_1.resolveChronicleDir)(config);
339
+ const all = (0, vault_js_1.listChronicles)(config);
340
+ if (all.length === 0) {
341
+ console.log(chalk_1.default.yellow('\n No chronicles in vault yet.\n'));
342
+ return;
343
+ }
344
+ const TYPE_ICONS = { session: '◈', decision: '◆', reflection: '◇', sweep: '↻', narcissus: '✦' };
345
+ const getType = (filename) => {
346
+ try {
347
+ const raw = fs_1.default.readFileSync(path_1.default.join(dir, filename), 'utf8').slice(0, 800);
348
+ const m = raw.match(/\*\*Type\*\*:\s*(\w+)/i) || raw.match(/^type:\s*(\w+)/im);
349
+ return m ? m[1].toLowerCase() : 'session';
350
+ }
351
+ catch {
352
+ return 'session';
353
+ }
354
+ };
355
+ const renderChronicle = (filename) => {
356
+ const filePath = path_1.default.join(dir, filename);
357
+ const raw = fs_1.default.readFileSync(filePath, 'utf8');
358
+ const resonanceIdx = raw.indexOf('<!--resonance');
359
+ const content = resonanceIdx !== -1 ? raw.slice(0, resonanceIdx) : raw;
360
+ const lines = content.split('\n');
361
+ console.log('\n' + chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72)));
362
+ console.log(chalk_1.default.gray(` ${filePath}`));
363
+ console.log(chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72)) + '\n');
364
+ let dividerCount = 0;
365
+ for (const line of lines) {
366
+ const l = line.trim();
367
+ if (l.startsWith('# ')) {
368
+ console.log('\n ' + chalk_1.default.hex('#8B5CF6').bold(l.slice(2)));
369
+ continue;
370
+ }
371
+ if (l.startsWith('## ')) {
372
+ console.log('\n ' + chalk_1.default.hex('#A78BFA').bold(' ' + l.slice(3)));
373
+ continue;
374
+ }
375
+ if (l.startsWith('### ')) {
376
+ console.log(' ' + chalk_1.default.hex('#C084FC')(' ' + l.slice(4)));
377
+ continue;
378
+ }
379
+ if (l === '---') {
380
+ dividerCount++;
381
+ if (dividerCount <= 2)
382
+ console.log(chalk_1.default.hex('#312E81')(' ' + '─'.repeat(60)));
383
+ continue;
384
+ }
385
+ if (l.match(/^\*\*[\w\s]+\*\*:/)) {
386
+ const parts = l.match(/^\*\*([\w\s]+)\*\*:\s*(.*)/) ?? [];
387
+ if (parts.length >= 3)
388
+ console.log(' ' + chalk_1.default.hex('#64748B')(` ${parts[1].padEnd(14)} `) + chalk_1.default.hex('#94A3B8')(parts[2]));
389
+ continue;
390
+ }
391
+ if (l.startsWith('> ')) {
392
+ console.log(' ' + chalk_1.default.hex('#7C3AED')(' │ ') + chalk_1.default.hex('#DDD6FE').italic(l.slice(2)));
393
+ continue;
394
+ }
395
+ if (l.startsWith('- ') || l.startsWith('* ')) {
396
+ console.log(' ' + chalk_1.default.hex('#6B7280')(' • ') + chalk_1.default.white(l.slice(2)));
397
+ continue;
398
+ }
399
+ if (l.match(/^\d+\. /)) {
400
+ console.log(' ' + chalk_1.default.hex('#6B7280')(' ') + chalk_1.default.white(l));
401
+ continue;
402
+ }
403
+ if (l.startsWith('```')) {
404
+ console.log(' ' + chalk_1.default.hex('#312E81')(' ' + '·'.repeat(50)));
405
+ continue;
406
+ }
407
+ if (l.match(/^#\w/) && !l.startsWith('# ')) {
408
+ console.log('\n ' + (l.match(/#\w+/g) ?? []).map((t) => chalk_1.default.hex('#7C3AED')(t)).join(' '));
409
+ continue;
410
+ }
411
+ if (l.length > 0) {
412
+ console.log(' ' + chalk_1.default.hex('#E2E8F0')(' ' + l));
413
+ }
414
+ else {
415
+ console.log();
416
+ }
417
+ }
418
+ console.log('\n' + chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72)));
419
+ console.log(chalk_1.default.gray(`\n Open in editor → `) + chalk_1.default.white(`code "${filePath}"`) + '\n');
420
+ };
421
+ const picks = all.slice(0, parseInt(opts.count || '20'));
422
+ while (true) {
423
+ const choices = picks.map((filename) => {
424
+ const dateMatch = filename.match(/CHRONICLE-(\d{4}-\d{2}-\d{2})/);
425
+ const date = dateMatch ? dateMatch[1] : '';
426
+ const slug = filename.replace(/^CHRONICLE-\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, '').replace(/-/g, ' ');
427
+ const type = getType(filename);
428
+ const icon = TYPE_ICONS[type] ?? '○';
429
+ return { name: ` ${icon} ${chalk_1.default.hex('#CBD5E1')(date)} ${chalk_1.default.white(slug)}`, value: filename, short: slug };
430
+ });
431
+ choices.push({ name: chalk_1.default.hex('#475569')('\n ✖ Quitter'), value: '__exit__', short: 'exit' });
432
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n ⬡ MnemoChronicle — Vault\n'));
433
+ const { selected } = await inquirer_1.default.prompt([{
434
+ type: 'list', name: 'selected',
435
+ message: chalk_1.default.hex('#A78BFA')('Sélectionner une chronicle ') + chalk_1.default.gray('(↑↓ + Enter)'),
436
+ choices, pageSize: 15,
437
+ }]);
438
+ if (selected === '__exit__') {
439
+ console.log(chalk_1.default.gray('\n Vault fermé.\n'));
440
+ break;
441
+ }
442
+ renderChronicle(selected);
443
+ }
444
+ });
445
+ // ── switch ────────────────────────────────────────────────────────────────
446
+ exports.chronicleCommand
447
+ .command('switch')
448
+ .description('Switch the active profile to another registered one')
449
+ .action(async () => {
450
+ const config = (0, vault_js_1.loadVaultConfig)();
451
+ if (!config) {
452
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
453
+ process.exit(1);
454
+ }
455
+ const profiles = config.registeredModels ?? [];
456
+ if (profiles.length === 0) {
457
+ console.log(chalk_1.default.yellow('\n ⚠ No other profiles registered.'));
458
+ console.log(chalk_1.default.gray(' Use: mnemoforge chronicle init → "Add a new profile"\n'));
459
+ return;
460
+ }
461
+ const choices = profiles.map((p, i) => ({ name: p.ide + ' / ' + p.provider + ' · ' + p.displayName, value: i }));
462
+ const { idx } = await inquirer_1.default.prompt([{ type: 'list', name: 'idx', message: chalk_1.default.cyan('Which profile to make active?'), choices }]);
463
+ const selected = profiles[idx];
464
+ const displaced = { ide: config.ide, provider: config.provider, modelId: config.modelId, displayName: config.displayName, defaultChronicleStyle: config.defaultChronicleStyle };
465
+ const newRegistered = profiles.filter((_p, i) => i !== idx);
466
+ newRegistered.push(displaced);
467
+ (0, vault_js_1.saveVaultConfig)({ ...config, ide: selected.ide, provider: selected.provider, modelId: selected.modelId, displayName: selected.displayName, defaultChronicleStyle: selected.defaultChronicleStyle ?? 'session', registeredModels: newRegistered });
468
+ console.log(chalk_1.default.green('\n ✔ Switched to: ' + selected.displayName + ' (' + selected.provider + ')\n'));
469
+ (0, ui_js_1.showWelcome)();
470
+ });
471
+ // ── config ────────────────────────────────────────────────────────────────
472
+ exports.chronicleCommand
473
+ .command('config')
474
+ .description('Edit settings (style, display name) of any registered profile')
475
+ .action(async () => {
476
+ const config = (0, vault_js_1.loadVaultConfig)();
477
+ if (!config) {
478
+ console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
479
+ process.exit(1);
480
+ }
481
+ const profiles = config.registeredModels ?? [];
482
+ let targetIdx = -1;
483
+ if (profiles.length > 0) {
484
+ const allChoices = [
485
+ { name: '★ ' + config.ide + ' / ' + config.provider + ' · ' + config.displayName + ' [ACTIVE]', value: -1 },
486
+ ...profiles.map((p, i) => ({ name: '○ ' + p.ide + ' / ' + p.provider + ' · ' + p.displayName, value: i })),
487
+ ];
488
+ const { which } = await inquirer_1.default.prompt([{ type: 'list', name: 'which', message: chalk_1.default.cyan('Which profile to edit?'), choices: allChoices }]);
489
+ targetIdx = which;
490
+ }
491
+ const isActive = targetIdx === -1;
492
+ const curStyle = isActive ? (config.defaultChronicleStyle ?? 'session') : (profiles[targetIdx].defaultChronicleStyle ?? 'session');
493
+ const curName = isActive ? config.displayName : profiles[targetIdx].displayName;
494
+ const edit = await inquirer_1.default.prompt([
495
+ { type: 'list', name: 'style', message: chalk_1.default.cyan('Chronicle style?'), default: curStyle, choices: [
496
+ { name: 'Session — coding/work session', value: 'session' },
497
+ { name: 'Reflection — deep thoughts', value: 'reflection' },
498
+ { name: 'Decision — ADR-style record', value: 'decision' },
499
+ { name: 'Sweep — daily digest', value: 'sweep' },
500
+ { name: 'Narcissus — soul narrative', value: 'narcissus' },
501
+ ] },
502
+ { type: 'input', name: 'displayName', message: chalk_1.default.cyan('Display name?'), default: curName },
503
+ ]);
504
+ if (isActive) {
505
+ (0, vault_js_1.saveVaultConfig)({ ...config, defaultChronicleStyle: edit.style, displayName: edit.displayName });
506
+ }
507
+ else {
508
+ const updated = [...profiles];
509
+ updated[targetIdx] = { ...updated[targetIdx], defaultChronicleStyle: edit.style, displayName: edit.displayName };
510
+ (0, vault_js_1.saveVaultConfig)({ ...config, registeredModels: updated });
511
+ }
512
+ console.log(chalk_1.default.green('\n ✔ Profile updated!\n'));
513
+ (0, ui_js_1.showWelcome)();
514
+ });
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.projectCommand = exports.workspaceCommand = void 0;
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ // MnemoForge — workspace + project commands
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ const commander_1 = require("commander");
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const inquirer_1 = __importDefault(require("inquirer"));
13
+ const fs_1 = __importDefault(require("fs"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const os_1 = __importDefault(require("os"));
16
+ const vault_js_1 = require("../lib/vault.js");
17
+ // ── workspace ─────────────────────────────────────────────────────────────
18
+ exports.workspaceCommand = new commander_1.Command('workspace')
19
+ .description('Workspace & Resonance Project memory — rules that survive IDE sessions');
20
+ exports.workspaceCommand
21
+ .command('init')
22
+ .description('Initialize a Workspace (ecosystem / org container) in the current directory')
23
+ .option('--name <name>', 'Workspace name (e.g. Mnemosyne-OS)')
24
+ .action(async (opts) => {
25
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoWorkspace — Init\n'));
26
+ const answers = await inquirer_1.default.prompt([
27
+ { type: 'input', name: 'name', message: 'Workspace name (e.g. Mnemosyne-OS):',
28
+ default: opts.name ?? path_1.default.basename(process.cwd()), when: !opts.name },
29
+ ]);
30
+ const name = opts.name ?? answers.name;
31
+ const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
32
+ const existing = fs_1.default.existsSync(wsPath) ? JSON.parse(fs_1.default.readFileSync(wsPath, 'utf8')) : {};
33
+ const ws = { ...existing, workspace: name, workspace_version: '0.1',
34
+ last_updated: new Date().toISOString().slice(0, 10), updated_by: 'mnemoforge workspace init' };
35
+ fs_1.default.mkdirSync(path_1.default.join(process.cwd(), '.cli_resonance'), { recursive: true });
36
+ fs_1.default.writeFileSync(wsPath, JSON.stringify(ws, null, 2), 'utf8');
37
+ console.log(chalk_1.default.green(` ✔ Workspace "${name}" initialized\n`));
38
+ console.log(chalk_1.default.gray(` → .cli_resonance/WORKSPACE.json\n`));
39
+ console.log(chalk_1.default.cyan(' Next: ') + chalk_1.default.white('mnemoforge project init') + chalk_1.default.gray(' ← create a Resonance Project\n'));
40
+ });
41
+ exports.workspaceCommand
42
+ .command('show')
43
+ .description('Show workspace rules (agent briefing before starting work)')
44
+ .action(async () => {
45
+ const config = (0, vault_js_1.loadVaultConfig)();
46
+ const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
47
+ const globalWsPath = path_1.default.join(config?.vaultPath ?? path_1.default.join(os_1.default.homedir(), 'Documents', 'MnemoVault'), '.cli_resonance', 'WORKSPACE.json');
48
+ const target = fs_1.default.existsSync(wsPath) ? wsPath : fs_1.default.existsSync(globalWsPath) ? globalWsPath : null;
49
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoWorkspace — Agent Briefing\n'));
50
+ if (!target) {
51
+ console.log(chalk_1.default.yellow(' ⚠ No WORKSPACE.json found.'));
52
+ console.log(chalk_1.default.gray(' Run from a project root or use: mnemoforge workspace init\n'));
53
+ return;
54
+ }
55
+ const ws = JSON.parse(fs_1.default.readFileSync(target, 'utf8'));
56
+ console.log(chalk_1.default.cyan(` Project : `) + chalk_1.default.white(ws.project ?? '—'));
57
+ console.log(chalk_1.default.cyan(` Version : `) + chalk_1.default.gray(ws.version ?? '—'));
58
+ console.log(chalk_1.default.cyan(` Source : `) + chalk_1.default.gray(target));
59
+ if (config?.workspace)
60
+ console.log(chalk_1.default.cyan(` Vault ws : `) + chalk_1.default.gray(config.workspace));
61
+ const printRules = (label, rules) => {
62
+ if (!rules?.length)
63
+ return;
64
+ console.log(chalk_1.default.hex('#8B5CF6')(`\n ▸ ${label}`));
65
+ rules.forEach(r => console.log(chalk_1.default.gray(' • ') + r));
66
+ };
67
+ Object.entries(ws).forEach(([key, val]) => {
68
+ if (val?.rules?.length)
69
+ printRules(key, val.rules);
70
+ });
71
+ if (ws.neural_coding?.principles?.length)
72
+ printRules('neural_coding.principles', ws.neural_coding.principles);
73
+ console.log(chalk_1.default.gray(`\n Last updated: ${ws.last_updated ?? '—'} by ${ws.updated_by ?? '—'}\n`));
74
+ });
75
+ exports.workspaceCommand
76
+ .command('add-rule')
77
+ .description('Append a rule to the workspace safety memory')
78
+ .argument('<rule>', 'The rule text to add')
79
+ .option('--section <section>', 'Section to add to (npm | architecture | dev)', 'dev')
80
+ .action(async (rule, opts) => {
81
+ const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
82
+ if (!fs_1.default.existsSync(wsPath)) {
83
+ console.log(chalk_1.default.red('\n ✖ No WORKSPACE.json in current directory.\n'));
84
+ process.exit(1);
85
+ }
86
+ const ws = JSON.parse(fs_1.default.readFileSync(wsPath, 'utf8'));
87
+ const section = opts.section ?? 'dev';
88
+ if (!ws[section])
89
+ ws[section] = {};
90
+ if (!ws[section].rules)
91
+ ws[section].rules = [];
92
+ ws[section].rules.push(rule);
93
+ ws.last_updated = new Date().toISOString().slice(0, 10);
94
+ ws.updated_by = 'agent (auto)';
95
+ fs_1.default.writeFileSync(wsPath, JSON.stringify(ws, null, 2), 'utf8');
96
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoWorkspace — Rule Added\n'));
97
+ console.log(chalk_1.default.green(` ✔ [${section}] ${rule}\n`));
98
+ });
99
+ // ── project ───────────────────────────────────────────────────────────────
100
+ exports.projectCommand = new commander_1.Command('project')
101
+ .description('Resonance Project — a component or feature within a Workspace');
102
+ exports.projectCommand
103
+ .command('init')
104
+ .description('Initialize a Resonance Project — links a workspace + project to the vault')
105
+ .action(async () => {
106
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ Resonance Project — Init\n'));
107
+ const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
108
+ const wsData = fs_1.default.existsSync(wsPath) ? JSON.parse(fs_1.default.readFileSync(wsPath, 'utf8')) : {};
109
+ const existingConfig = (0, vault_js_1.loadVaultConfig)();
110
+ const answers = await inquirer_1.default.prompt([
111
+ { type: 'input', name: 'workspace', message: 'Workspace name (ecosystem / org):', default: wsData.workspace ?? 'Mnemosyne-OS' },
112
+ { type: 'input', name: 'resonanceProject', message: 'Resonance Project name (feature / component):', default: path_1.default.basename(process.cwd()) },
113
+ ]);
114
+ const config = {
115
+ ...(existingConfig ?? { vaultPath: vault_js_1.DEFAULT_VAULT, ide: 'Antigravity', provider: 'Anthropic' }),
116
+ workspace: answers.workspace, resonanceProject: answers.resonanceProject,
117
+ };
118
+ (0, vault_js_1.saveVaultConfig)(config);
119
+ const ws = { ...wsData, workspace: answers.workspace, resonanceProject: answers.resonanceProject,
120
+ last_updated: new Date().toISOString().slice(0, 10) };
121
+ fs_1.default.mkdirSync(path_1.default.join(process.cwd(), '.cli_resonance'), { recursive: true });
122
+ fs_1.default.writeFileSync(wsPath, JSON.stringify(ws, null, 2), 'utf8');
123
+ fs_1.default.mkdirSync(path_1.default.join(process.cwd(), 'handbook', 'chronicles'), { recursive: true });
124
+ console.log(chalk_1.default.green(`\n ✔ Resonance Project "${answers.resonanceProject}" initialized\n`));
125
+ console.log(chalk_1.default.cyan(' Workspace : ') + chalk_1.default.white(answers.workspace));
126
+ console.log(chalk_1.default.cyan(' Project : ') + chalk_1.default.white(answers.resonanceProject));
127
+ console.log(chalk_1.default.cyan(' Vault path : ') + chalk_1.default.gray(`${config.vaultPath}/${answers.workspace}/${answers.resonanceProject}/`));
128
+ console.log(chalk_1.default.cyan(' Chronicles : ') + chalk_1.default.gray('./handbook/chronicles/\n'));
129
+ });