@mnemosyne_os/forge 1.2.3 → 1.2.5

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/cli.js CHANGED
@@ -1,141 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // MnemoForge CLI Entry Point
5
+ // Mnemosyne Neural OS · AI Inception Engine · XPACEGEMS LLC
6
+ // ─────────────────────────────────────────────────────────────────────────────
36
7
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
8
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
9
  };
39
10
  Object.defineProperty(exports, "__esModule", { value: true });
40
11
  const commander_1 = require("commander");
41
12
  const chalk_1 = __importDefault(require("chalk"));
42
- const inquirer_1 = __importDefault(require("inquirer"));
43
13
  const fs_1 = __importDefault(require("fs"));
44
14
  const path_1 = __importDefault(require("path"));
45
- const os_1 = __importDefault(require("os"));
46
15
  const vault_js_1 = require("./lib/vault.js");
47
- const chronicle_js_1 = require("./lib/chronicle.js");
48
16
  const init_flow_js_1 = require("./lib/init-flow.js");
49
- const index_js_1 = require("./lib/sources/index.js");
50
- // ─────────────────────────────────────────────
51
- // MnemoForge CLI — Mnemosyne Neural OS
52
- // AI Inception Engine · XPACEGEMS LLC
53
- // ─────────────────────────────────────────────
54
- // ─────────────────────────────────────────────
55
- // Welcome Dashboard — shown when no command given
56
- // ─────────────────────────────────────────────
57
- const V = chalk_1.default.hex('#8B5CF6');
58
- const V2 = chalk_1.default.hex('#A78BFA');
59
- const V3 = chalk_1.default.hex('#C4B5FD');
60
- const V4 = chalk_1.default.hex('#DDD6FE');
61
- const DIM = chalk_1.default.gray;
62
- const HL = chalk_1.default.hex('#A78BFA').bold;
63
- const ASCII = [
64
- V('███╗ ███╗███╗ ██╗███████╗███╗ ███╗ ██████╗ '),
65
- V('████╗ ████║████╗ ██║██╔════╝████╗ ████║██╔═══██╗'),
66
- V2('██╔████╔██║██╔██╗ ██║█████╗ ██╔████╔██║██║ ██║'),
67
- V3('██║╚██╔╝██║██║╚██╗██║██╔══╝ ██║╚██╔╝██║██║ ██║'),
68
- V4('██║ ╚═╝ ██║██║ ╚████║███████╗██║ ╚═╝ ██║╚██████╔╝'),
69
- chalk_1.default.hex('#EDE9FE')('╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═════╝ '),
70
- ].join('\n');
71
- function showWelcome() {
72
- const config = (0, vault_js_1.loadVaultConfig)();
73
- const w = '═'.repeat(52);
74
- const pad = (s, n) => s + ' '.repeat(Math.max(0, n - s.length));
75
- console.log();
76
- console.log(ASCII);
77
- console.log();
78
- console.log(V(' ╔' + w + '╗'));
79
- console.log(V(' ║') + chalk_1.default.hex('#F5F3FF').bold(' M N E M O S Y N E O S — N E U R A L O S ') + V(' ║'));
80
- console.log(V(' ║') + DIM(' MnemoForge CLI v1.2.0 · XPACEGEMS LLC ') + V(' ║'));
81
- console.log(V(' ╠' + w + '╣'));
82
- if (config) {
83
- const profiles = config.registeredModels ?? [];
84
- const activeChron = (0, vault_js_1.listChronicles)(config).length;
85
- const activeStyle = config.defaultChronicleStyle ?? 'session';
86
- const activeName = config.ide + ' / ' + config.provider;
87
- console.log(V(' ║') + DIM(' ⚡ RESONANCE PROFILES ') + V(' ║'));
88
- console.log(V(' ║') + ' ' + chalk_1.default.green('★') + ' ' + chalk_1.default.white(pad(activeName, 32)) + DIM(pad(activeStyle, 12)) + DIM(activeChron + ' chron.') + ' ' + chalk_1.default.green('[✓]') + ' ' + V(' ║'));
89
- if (profiles.length > 0) {
90
- console.log(V(' ║') + ' ' + DIM('─'.repeat(48)) + V(' ║'));
91
- profiles.forEach((p, i) => {
92
- const pName = p.ide + ' / ' + p.provider;
93
- const pStyle = p.defaultChronicleStyle ?? 'session';
94
- console.log(V(' ║') + ' ' + DIM('○') + ' ' + DIM(pad(pName, 32)) + DIM(pad(pStyle, 12)) + DIM('[' + (i + 1) + '] switch') + ' ' + V(' ║'));
95
- });
96
- }
97
- }
98
- else {
99
- console.log(V(' ║') + chalk_1.default.yellow(' ⚠ No vault configured.') + ' Run: ' + chalk_1.default.white('mnemoforge chronicle init') + ' ' + V(' ║'));
100
- }
101
- console.log(V(' ╠' + w + '╣'));
102
- console.log(V(' ║') + DIM(' COMMANDS ') + V(' ║'));
103
- console.log(V(' ║') + ' ' + V2('chronicle init ') + DIM('─ add / reconfigure a profile ') + V(' ║'));
104
- console.log(V(' ║') + ' ' + V2('chronicle switch ') + DIM('─ change active profile ') + V(' ║'));
105
- console.log(V(' ║') + ' ' + V2('chronicle config ') + DIM('─ edit a profile settings ') + V(' ║'));
106
- console.log(V(' ║') + ' ' + V2('chronicle commit ') + DIM('─ write a new chronicle ') + V(' ║'));
107
- console.log(V(' ║') + ' ' + V2('chronicle list ') + DIM('─ explore vault ') + V(' ║'));
108
- console.log(V(' ║') + ' ' + V2('models import ') + DIM('─ add from UniversalModelCard ') + V(' ║'));
109
- console.log(V(' ╠' + w + '╣'));
110
- console.log(V(' ║') + DIM(' "Memory is the architecture of intelligence." ') + V(' ║'));
111
- console.log(V(' ╚' + w + '╝'));
112
- console.log();
113
- }
114
- const BANNER = ASCII;
17
+ const ui_js_1 = require("./lib/ui.js");
18
+ const workspace_js_1 = require("./commands/workspace.js");
19
+ const index_js_1 = require("./commands/chronicle/index.js");
20
+ const canvas_js_1 = require("./commands/canvas.js");
21
+ // ── Bootstrap ─────────────────────────────────────────────────────────────
115
22
  const program = new commander_1.Command();
116
23
  program
117
24
  .name('mnemoforge')
118
25
  .description('MnemoForge — AI Inception Engine for the Mnemosyne Neural OS ecosystem')
119
- .version('1.2.0', '-v, --version', 'Display current version')
120
- .addHelpText('beforeAll', BANNER + '\n')
121
- .action(() => { showWelcome(); });
26
+ .version('1.2.5', '-v, --version', 'Display current version')
27
+ .addHelpText('beforeAll', ui_js_1.ASCII + '\n')
28
+ .action(() => { (0, ui_js_1.showWelcome)(); });
29
+ // ── init (top-level — vault setup shortcut) ───────────────────────────────
122
30
  program
123
31
  .command('init')
124
32
  .description('Scaffold a new Mnemosyne-grade module with AI governance DNA')
125
33
  .argument('[module-name]', 'Name of the module to create (PascalCase recommended)')
126
- .option('--no-git', 'Skip git initialization')
127
34
  .action(async () => {
128
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n\u29e1 MnemoChronicle \u2014 Vault Init\n'));
35
+ console.log(chalk_1.default.hex('#8B5CF6').bold('\n MnemoChronicle Vault Init\n'));
129
36
  const existing = (0, vault_js_1.loadVaultConfig)();
130
37
  if (existing) {
131
- console.log(chalk_1.default.yellow(' \u26a0 Already configured:'));
38
+ console.log(chalk_1.default.yellow(' Already configured:'));
132
39
  console.log(chalk_1.default.gray(' IDE: ' + existing.ide));
133
40
  console.log(chalk_1.default.gray(' Provider: ' + existing.provider + '\n'));
134
41
  }
135
42
  const profile = await (0, init_flow_js_1.askPrimaryProfile)(existing);
136
- // \u2500\u2500 Extra profiles (loop)
137
43
  const registeredModels = existing?.registeredModels ?? [];
138
- // Auto-demote old primary when switching
139
44
  if (existing && (existing.ide !== profile.ide || existing.provider !== profile.provider)) {
140
45
  const old = { ide: existing.ide, provider: existing.provider, defaultChronicleStyle: existing.defaultChronicleStyle };
141
46
  if (!registeredModels.some(m => m.ide === old.ide && m.provider === old.provider))
@@ -143,650 +48,23 @@ program
143
48
  }
144
49
  let extra = await (0, init_flow_js_1.askExtraProfile)(registeredModels.length);
145
50
  while (extra) {
146
- console.log(chalk_1.default.green(' \u2714 Added: ' + extra.provider + ' (' + extra.ide + ') \u00b7 style: ' + extra.defaultChronicleStyle));
51
+ console.log(chalk_1.default.green(' Added: ' + extra.provider + ' (' + extra.ide + ') · style: ' + extra.defaultChronicleStyle));
147
52
  registeredModels.push(extra);
148
53
  extra = await (0, init_flow_js_1.askExtraProfile)(registeredModels.length);
149
54
  }
150
55
  const config = { ...profile, registeredModels };
151
56
  (0, vault_js_1.saveVaultConfig)(config);
152
57
  const res = config.vaultPath + '/.cli_resonance/' + config.ide + '/' + config.provider + '/';
153
- console.log(chalk_1.default.green('\n \u2714 Vault configured!'));
58
+ console.log(chalk_1.default.green('\n Vault configured!'));
154
59
  console.log(chalk_1.default.gray(' IDE: ') + chalk_1.default.white(config.ide));
155
60
  console.log(chalk_1.default.gray(' Provider: ') + chalk_1.default.white(config.provider));
156
61
  console.log(chalk_1.default.gray(' Style: ') + chalk_1.default.hex('#A78BFA')(config.defaultChronicleStyle ?? 'session'));
157
62
  if (registeredModels.length > 0)
158
63
  console.log(chalk_1.default.gray(' + ' + registeredModels.length + ' extra profile(s)'));
159
- console.log(chalk_1.default.gray('\n Chronicles \u2192 ') + chalk_1.default.hex('#A78BFA')(res) + '\n');
160
- console.log(chalk_1.default.gray(' Run ') + chalk_1.default.white('mnemoforge') + chalk_1.default.gray(' to see your dashboard.\n'));
161
- });
162
- // ─────────────────────────────────────────────
163
- // ─────────────────────────────────────────────
164
- // CATALOG — IDE / Provider / Model
165
- // ─────────────────────────────────────────────
166
- // Model names are free text — stored in chronicle frontmatter, not in folder structure
167
- // ─────────────────────────────────────────────
168
- // WORKSPACE COMMANDS
169
- // mnemoforge workspace init | show | add-rule
170
- // mnemoforge project init
171
- // External safety memory — readable by any agent at session start
172
- // ─────────────────────────────────────────────
173
- const workspace = new commander_1.Command('workspace')
174
- .description('Workspace & Resonance Project memory — rules that survive IDE sessions');
175
- workspace
176
- .command('init')
177
- .description('Initialize a Workspace (ecosystem / org container) in the current directory')
178
- .option('--name <name>', 'Workspace name (e.g. Mnemosyne-OS)')
179
- .action(async (opts) => {
180
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoWorkspace — Init\n'));
181
- const answers = await inquirer_1.default.prompt([
182
- {
183
- type: 'input', name: 'name', message: 'Workspace name (e.g. Mnemosyne-OS):',
184
- default: opts.name ?? path_1.default.basename(process.cwd()),
185
- when: !opts.name,
186
- },
187
- ]);
188
- const name = opts.name ?? answers.name;
189
- const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
190
- const existing = fs_1.default.existsSync(wsPath) ? JSON.parse(fs_1.default.readFileSync(wsPath, 'utf8')) : {};
191
- const ws = {
192
- ...existing,
193
- workspace: name,
194
- workspace_version: '0.1',
195
- last_updated: new Date().toISOString().slice(0, 10),
196
- updated_by: 'mnemoforge workspace init',
197
- };
198
- fs_1.default.mkdirSync(path_1.default.join(process.cwd(), '.cli_resonance'), { recursive: true });
199
- fs_1.default.writeFileSync(wsPath, JSON.stringify(ws, null, 2), 'utf8');
200
- console.log(chalk_1.default.green(` ✔ Workspace "${name}" initialized\n`));
201
- console.log(chalk_1.default.gray(` → .cli_resonance/WORKSPACE.json\n`));
202
- console.log(chalk_1.default.cyan(' Next: ') + chalk_1.default.white('mnemoforge project init') + chalk_1.default.gray(' ← create a Resonance Project\n'));
203
- });
204
- // ─────────────────────────────────────────────
205
- // PROJECT COMMANDS
206
- // mnemoforge project init
207
- // ─────────────────────────────────────────────
208
- const project = new commander_1.Command('project')
209
- .description('Resonance Project — a component or feature within a Workspace');
210
- project
211
- .command('init')
212
- .description('Initialize a Resonance Project — links a workspace + project to the vault')
213
- .action(async () => {
214
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ Resonance Project — Init\n'));
215
- // Load existing workspace name from WORKSPACE.json if present
216
- const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
217
- const wsData = fs_1.default.existsSync(wsPath) ? JSON.parse(fs_1.default.readFileSync(wsPath, 'utf8')) : {};
218
- const existingConfig = (0, vault_js_1.loadVaultConfig)();
219
- const answers = await inquirer_1.default.prompt([
220
- {
221
- type: 'input', name: 'workspace',
222
- message: 'Workspace name (ecosystem / org):',
223
- default: wsData.workspace ?? 'Mnemosyne-OS',
224
- },
225
- {
226
- type: 'input', name: 'resonanceProject',
227
- message: 'Resonance Project name (feature / component):',
228
- default: path_1.default.basename(process.cwd()),
229
- },
230
- ]);
231
- // Merge into vault config
232
- const config = {
233
- ...(existingConfig ?? {
234
- vaultPath: vault_js_1.DEFAULT_VAULT,
235
- ide: 'Antigravity',
236
- provider: 'Anthropic',
237
- }),
238
- workspace: answers.workspace,
239
- resonanceProject: answers.resonanceProject,
240
- };
241
- (0, vault_js_1.saveVaultConfig)(config);
242
- // Update WORKSPACE.json too
243
- const ws = { ...wsData, workspace: answers.workspace, resonanceProject: answers.resonanceProject, last_updated: new Date().toISOString().slice(0, 10) };
244
- fs_1.default.mkdirSync(path_1.default.join(process.cwd(), '.cli_resonance'), { recursive: true });
245
- fs_1.default.writeFileSync(wsPath, JSON.stringify(ws, null, 2), 'utf8');
246
- // Create handbook/chronicles/ scaffold
247
- const chroniclesDir = path_1.default.join(process.cwd(), 'handbook', 'chronicles');
248
- fs_1.default.mkdirSync(chroniclesDir, { recursive: true });
249
- console.log(chalk_1.default.green(`\n ✔ Resonance Project "${answers.resonanceProject}" initialized\n`));
250
- console.log(chalk_1.default.cyan(' Workspace : ') + chalk_1.default.white(answers.workspace));
251
- console.log(chalk_1.default.cyan(' Project : ') + chalk_1.default.white(answers.resonanceProject));
252
- console.log(chalk_1.default.cyan(' Vault path : ') + chalk_1.default.gray(`${config.vaultPath}/${answers.workspace}/${answers.resonanceProject}/`));
253
- console.log(chalk_1.default.cyan(' Chronicles : ') + chalk_1.default.gray('./handbook/chronicles/\n'));
254
- console.log(chalk_1.default.hex('#8B5CF6')(' Chronicles go in vault:'));
255
- console.log(chalk_1.default.gray(` ${config.vaultPath}/${answers.workspace}/${answers.resonanceProject}/${config.ide}/${config.provider}/\n`));
256
- });
257
- program.addCommand(project);
258
- workspace
259
- .command('show')
260
- .description('Show workspace rules (agent briefing before starting work)')
261
- .action(async () => {
262
- const config = (0, vault_js_1.loadVaultConfig)();
263
- const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
264
- const globalWsPath = path_1.default.join(config?.vaultPath ?? path_1.default.join(os_1.default.homedir(), 'Documents', 'MnemoVault'), '.cli_resonance', 'WORKSPACE.json');
265
- const target = fs_1.default.existsSync(wsPath) ? wsPath : fs_1.default.existsSync(globalWsPath) ? globalWsPath : null;
266
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoWorkspace — Agent Briefing\n'));
267
- if (!target) {
268
- console.log(chalk_1.default.yellow(' ⚠ No WORKSPACE.json found.'));
269
- console.log(chalk_1.default.gray(' Run from a project root or use: mnemoforge workspace init\n'));
270
- return;
271
- }
272
- const ws = JSON.parse(fs_1.default.readFileSync(target, 'utf8'));
273
- console.log(chalk_1.default.cyan(` Project : `) + chalk_1.default.white(ws.project ?? '—'));
274
- console.log(chalk_1.default.cyan(` Version : `) + chalk_1.default.gray(ws.version ?? '—'));
275
- console.log(chalk_1.default.cyan(` Source : `) + chalk_1.default.gray(target));
276
- if (config?.workspace)
277
- console.log(chalk_1.default.cyan(` Vault ws : `) + chalk_1.default.gray(config.workspace));
278
- // Print all rule arrays found in the workspace
279
- const printRules = (label, rules) => {
280
- if (!rules?.length)
281
- return;
282
- console.log(chalk_1.default.hex('#8B5CF6')(`\n ▸ ${label}`));
283
- rules.forEach(r => console.log(chalk_1.default.gray(' • ') + r));
284
- };
285
- Object.entries(ws).forEach(([key, val]) => {
286
- if (val?.rules?.length)
287
- printRules(key, val.rules);
288
- });
289
- if (ws.neural_coding?.principles?.length) {
290
- printRules('neural_coding.principles', ws.neural_coding.principles);
291
- }
292
- console.log(chalk_1.default.gray(`\n Last updated: ${ws.last_updated ?? '—'} by ${ws.updated_by ?? '—'}\n`));
293
- });
294
- workspace
295
- .command('add-rule')
296
- .description('Append a rule to the workspace safety memory')
297
- .argument('<rule>', 'The rule text to add')
298
- .option('--section <section>', 'Section to add to (npm | architecture | dev)', 'dev')
299
- .action(async (rule, opts) => {
300
- const wsPath = path_1.default.join(process.cwd(), '.cli_resonance', 'WORKSPACE.json');
301
- if (!fs_1.default.existsSync(wsPath)) {
302
- console.log(chalk_1.default.red('\n ✖ No WORKSPACE.json in current directory.\n'));
303
- process.exit(1);
304
- }
305
- const ws = JSON.parse(fs_1.default.readFileSync(wsPath, 'utf8'));
306
- const section = opts.section ?? 'dev';
307
- if (!ws[section])
308
- ws[section] = {};
309
- if (!ws[section].rules)
310
- ws[section].rules = [];
311
- ws[section].rules.push(rule);
312
- ws.last_updated = new Date().toISOString().slice(0, 10);
313
- ws.updated_by = 'agent (auto)';
314
- fs_1.default.writeFileSync(wsPath, JSON.stringify(ws, null, 2), 'utf8');
315
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoWorkspace — Rule Added\n'));
316
- console.log(chalk_1.default.green(` ✔ [${section}] ${rule}\n`));
317
- });
318
- program.addCommand(workspace);
319
- // ─────────────────────────────────────────────
320
- // CHRONICLE COMMANDS
321
- // mnemoforge chronicle init | commit | sweep | list
322
- // ─────────────────────────────────────────────
323
- const chronicle = new commander_1.Command('chronicle')
324
- .description('MnemoChronicle — multi-agent memory archiving system');
325
- chronicle
326
- .command('init')
327
- .description('Initialize .cli_resonance vault — choose IDE / Provider / Model')
328
- .action(async () => {
329
- const existing = (0, vault_js_1.loadVaultConfig)();
330
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n\u29e1 MnemoChronicle \u2014 Vault Init\n'));
331
- if (existing) {
332
- console.log(chalk_1.default.yellow(' \u26a0 Re-configuring vault:'));
333
- console.log(chalk_1.default.gray(' IDE: ' + existing.ide));
334
- console.log(chalk_1.default.gray(' Provider: ' + existing.provider + '\n'));
335
- }
336
- const profile = await (0, init_flow_js_1.askPrimaryProfile)(existing);
337
- // \u2500\u2500 Extra profiles (loop)
338
- const reg = existing?.registeredModels ?? [];
339
- if (existing && (existing.ide !== profile.ide || existing.provider !== profile.provider)) {
340
- const old = { ide: existing.ide, provider: existing.provider, defaultChronicleStyle: existing.defaultChronicleStyle };
341
- if (!reg.some(m => m.ide === old.ide && m.provider === old.provider))
342
- reg.unshift(old);
343
- }
344
- let extra = await (0, init_flow_js_1.askExtraProfile)(reg.length);
345
- while (extra) {
346
- console.log(chalk_1.default.green(' \u2714 Added: ' + extra.ide + ' / ' + extra.provider + ' \u00b7 ' + extra.defaultChronicleStyle));
347
- reg.push(extra);
348
- extra = await (0, init_flow_js_1.askExtraProfile)(reg.length);
349
- }
350
- const config = { ...profile, registeredModels: reg };
351
- (0, vault_js_1.saveVaultConfig)(config);
352
- const dir = config.vaultPath + '/.cli_resonance/' + config.ide + '/' + config.provider + '/';
353
- console.log(chalk_1.default.green('\n \u2714 Vault configured!'));
354
- console.log(chalk_1.default.gray(' IDE: ') + chalk_1.default.white(config.ide));
355
- console.log(chalk_1.default.gray(' Provider: ') + chalk_1.default.white(config.provider));
356
- console.log(chalk_1.default.gray(' Style: ') + chalk_1.default.hex('#A78BFA')(config.defaultChronicleStyle ?? 'session'));
357
- if (reg.length > 0)
358
- console.log(chalk_1.default.gray(' + ' + reg.length + ' extra profile(s)'));
359
- console.log(chalk_1.default.gray('\n Chronicles \u2192 ') + chalk_1.default.hex('#A78BFA')(dir) + '\n');
64
+ console.log(chalk_1.default.gray('\n Chronicles ') + chalk_1.default.hex('#A78BFA')(res) + '\n');
360
65
  console.log(chalk_1.default.gray(' Run ') + chalk_1.default.white('mnemoforge') + chalk_1.default.gray(' to see your dashboard.\n'));
361
66
  });
362
- chronicle
363
- .command('commit')
364
- .description('Write a new Chronicle to the vault')
365
- .option('-t, --title <title>', 'Chronicle title')
366
- .option('--type <type>', 'Chronicle style: session | reflection | decision | sweep | narcissus')
367
- .option('--tags <tags>', 'Comma-separated tags')
368
- .option('--auto', 'Auto-generate draft and open in VS Code (no editor prompt)')
369
- .action(async (opts) => {
370
- const config = (0, vault_js_1.loadVaultConfig)();
371
- if (!config) {
372
- console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
373
- process.exit(1);
374
- }
375
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — New Chronicle\n'));
376
- // ── AUTO mode: generate draft → write directly → open in editor ──────────
377
- if (opts.auto) {
378
- console.log(chalk_1.default.cyan(' ⟳ Reading conversation source…'));
379
- const ctx = (0, index_js_1.readSourceForConfig)(config);
380
- let draftContent = '';
381
- let draftTitle = opts.title ?? 'Session chronicle';
382
- if (ctx) {
383
- draftContent = (0, index_js_1.generateChronicleDraft)(ctx, config);
384
- draftTitle = opts.title || ctx.sessionTitle.slice(0, 60) || draftTitle;
385
- console.log(chalk_1.default.green(` ✔ Draft from: ${ctx.conversationId.slice(0, 8)}…`));
386
- console.log(chalk_1.default.gray(` Files: ${ctx.filesTouched.length} · Decisions: ${ctx.keyDecisions.length}`));
387
- }
388
- else {
389
- // No source — create a blank template from the style
390
- console.log(chalk_1.default.yellow(' ⚠ No source — creating blank template.\n'));
391
- draftContent = (0, index_js_1.generateChronicleDraft)({ conversationId: 'manual', startedAt: null, sessionTitle: draftTitle,
392
- filesTouched: [], commandsRun: [], keyDecisions: [], rawTurns: [], sourcePath: '' }, config);
393
- }
394
- const tags = opts.tags ? opts.tags.split(',').map((t) => t.trim()) : [];
395
- const { filePath, filename } = (0, chronicle_js_1.writeChronicle)({
396
- title: draftTitle,
397
- type: (opts.type ?? config.defaultChronicleStyle ?? 'session'),
398
- content: draftContent,
399
- tags,
400
- config,
401
- });
402
- console.log(chalk_1.default.green(`\n ✔ Chronicle created: ${filename}`));
403
- console.log(chalk_1.default.gray(` ${filePath}`));
404
- console.log(chalk_1.default.cyan('\n Opening in VS Code…\n'));
405
- // Open in VS Code — shell:true required on Windows for PATH resolution
406
- try {
407
- const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
408
- spawn('code', [filePath], { detached: true, stdio: 'ignore', shell: true }).unref();
409
- }
410
- catch {
411
- console.log(chalk_1.default.gray(` (Could not open VS Code automatically — open the file manually)\n`));
412
- }
413
- return;
414
- }
415
- // ── MANUAL mode: ask title + content via simple prompts ─────────────────
416
- const answers = await inquirer_1.default.prompt([
417
- {
418
- type: 'input',
419
- name: 'title',
420
- message: chalk_1.default.cyan('Chronicle title?'),
421
- default: opts.title,
422
- when: !opts.title,
423
- validate: (v) => v.trim() !== '' || 'Required',
424
- },
425
- {
426
- type: 'input',
427
- name: 'content',
428
- message: chalk_1.default.cyan('Short summary') + chalk_1.default.gray(' (you can edit the file after)'),
429
- default: '',
430
- },
431
- ]);
432
- const title = opts.title || answers.title;
433
- const tags = opts.tags ? opts.tags.split(',').map((t) => t.trim()) : [];
434
- const { filePath, filename } = (0, chronicle_js_1.writeChronicle)({
435
- title,
436
- type: (opts.type ?? config.defaultChronicleStyle ?? 'session'),
437
- content: answers.content,
438
- tags,
439
- config,
440
- });
441
- console.log(chalk_1.default.green(`\n ✔ Chronicle written: ${filename}`));
442
- console.log(chalk_1.default.gray(` ${filePath}\n`));
443
- 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'));
444
- });
445
- // ── chronicle archive ──────────────────────────────────────────────────────
446
- // Primary workflow: agent writes a .md file → CLI archives it to the vault.
447
- // The human never writes chronicles. The agent does.
448
- chronicle
449
- .command('archive')
450
- .description('Archive an agent-written chronicle into the vault (primary workflow)')
451
- .option('-f, --file <path>', 'Path to the .md chronicle file to archive')
452
- .action(async (opts) => {
453
- const config = (0, vault_js_1.loadVaultConfig)();
454
- if (!config) {
455
- console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
456
- process.exit(1);
457
- }
458
- if (!opts.file) {
459
- console.log(chalk_1.default.red('\n ✖ Specify a file: mnemoforge chronicle archive --file <path>\n'));
460
- process.exit(1);
461
- }
462
- const srcPath = path_1.default.resolve(opts.file);
463
- if (!fs_1.default.existsSync(srcPath)) {
464
- console.log(chalk_1.default.red(`\n ✖ File not found: ${srcPath}\n`));
465
- process.exit(1);
466
- }
467
- // Resolve destination dir from config
468
- const { resolveChronicleDir } = await Promise.resolve().then(() => __importStar(require('./lib/vault.js')));
469
- const destDir = resolveChronicleDir(config);
470
- fs_1.default.mkdirSync(destDir, { recursive: true });
471
- const filename = path_1.default.basename(srcPath);
472
- const destPath = path_1.default.join(destDir, filename);
473
- fs_1.default.copyFileSync(srcPath, destPath);
474
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — Archived\n'));
475
- console.log(chalk_1.default.green(` ✔ ${filename}`));
476
- console.log(chalk_1.default.gray(` → ${destPath}\n`));
477
- });
478
- chronicle
479
- .command('sweep')
480
- .description('Create a daily sweep Chronicle from recent entries')
481
- .action(async () => {
482
- const config = (0, vault_js_1.loadVaultConfig)();
483
- if (!config) {
484
- console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
485
- process.exit(1);
486
- }
487
- const today = new Date().toISOString().slice(0, 10);
488
- const recent = (0, vault_js_1.listChronicles)(config).filter(f => f.includes(today));
489
- console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ MnemoChronicle — Daily Sweep\n'));
490
- console.log(chalk_1.default.gray(` Found ${recent.length} chronicle(s) from today (${today})\n`));
491
- if (recent.length === 0) {
492
- console.log(chalk_1.default.yellow(' No chronicles today to sweep.\n'));
493
- return;
494
- }
495
- const { doSweep } = await inquirer_1.default.prompt([{
496
- type: 'confirm',
497
- name: 'doSweep',
498
- message: `Create sweep from ${recent.length} chronicle(s)?`,
499
- default: true,
500
- }]);
501
- if (!doSweep)
502
- return;
503
- const sweepContent = (0, chronicle_js_1.buildSweepContent)(recent, config);
504
- const { filePath, filename } = (0, chronicle_js_1.writeChronicle)({
505
- title: `Daily Sweep — ${today}`,
506
- type: 'sweep',
507
- content: sweepContent,
508
- tags: ['sweep', today],
509
- config,
510
- });
511
- console.log(chalk_1.default.green(`\n ✔ Sweep written: ${filename}`));
512
- console.log(chalk_1.default.gray(` ${filePath}\n`));
513
- });
514
- chronicle
515
- .command('list')
516
- .description('List recent Chronicles in the vault')
517
- .option('-n, --count <n>', 'Number of chronicles to show', '10')
518
- .action((opts) => {
519
- const config = (0, vault_js_1.loadVaultConfig)();
520
- if (!config) {
521
- console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
522
- process.exit(1);
523
- }
524
- const { resolveChronicleDir } = require('./lib/vault.js');
525
- const dir = resolveChronicleDir(config);
526
- const all = (0, vault_js_1.listChronicles)(config);
527
- const chronicles = all.slice(0, parseInt(opts.count || '10'));
528
- // ── Color palette ──────────────────────────────────
529
- const TYPE_COLORS = {
530
- session: chalk_1.default.hex('#60A5FA'), // blue
531
- decision: chalk_1.default.hex('#FB923C'), // orange
532
- reflection: chalk_1.default.hex('#C084FC'), // purple
533
- sweep: chalk_1.default.hex('#94A3B8'), // slate
534
- narcissus: chalk_1.default.hex('#FBBF24'), // gold
535
- };
536
- const TYPE_ICONS = {
537
- session: '◈',
538
- decision: '◆',
539
- reflection: '◇',
540
- sweep: '↻',
541
- narcissus: '✦',
542
- };
543
- // ── Helper: parse chronicle metadata from first lines ──
544
- const parseChronicle = (filename) => {
545
- const filePath = path_1.default.join(dir, filename);
546
- const rawSlug = filename.replace(/^CHRONICLE-\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, '').replace(/-/g, ' ');
547
- let title = rawSlug.charAt(0).toUpperCase() + rawSlug.slice(1);
548
- let type = 'session';
549
- let tags = [];
550
- let excerpt = '';
551
- const dateMatch = filename.match(/CHRONICLE-(\d{4}-\d{2}-\d{2})/);
552
- let date = dateMatch ? dateMatch[1] : '';
553
- try {
554
- const raw = fs_1.default.readFileSync(filePath, 'utf8');
555
- // Stop before <!--resonance block
556
- const resonanceIdx = raw.indexOf('<!--resonance');
557
- const content = resonanceIdx !== -1 ? raw.slice(0, resonanceIdx) : raw;
558
- const lines = content.split('\n');
559
- let inFrontmatter = false;
560
- let frontmatterDone = false;
561
- let dividerCount = 0;
562
- const bodyLines = [];
563
- for (const line of lines.slice(0, 40)) {
564
- const l = line.trim();
565
- // YAML frontmatter block (file starts with ---)
566
- if (l === '---' && !frontmatterDone) {
567
- dividerCount++;
568
- if (dividerCount === 1) {
569
- inFrontmatter = true;
570
- continue;
571
- }
572
- if (dividerCount === 2) {
573
- inFrontmatter = false;
574
- frontmatterDone = true;
575
- continue;
576
- }
577
- }
578
- if (inFrontmatter) {
579
- // Extract from YAML frontmatter
580
- if (l.startsWith('title:'))
581
- title = l.replace('title:', '').trim().replace(/^['"]|['"]$/g, '');
582
- if (l.startsWith('type:'))
583
- type = l.replace('type:', '').trim();
584
- if (l.startsWith('date:') && l.match(/\d{4}-\d{2}-\d{2}/))
585
- date = l.match(/\d{4}-\d{2}-\d{2}/)[0];
586
- continue;
587
- }
588
- // Structured chronicle format (** bold fields **)
589
- if (l.startsWith('# ')) {
590
- title = l.slice(2).trim();
591
- continue;
592
- }
593
- if (l.startsWith('**Type**:')) {
594
- type = l.replace('**Type**:', '').trim().toLowerCase();
595
- continue;
596
- }
597
- if (l.startsWith('**Date**:')) {
598
- const d = l.replace('**Date**:', '').trim();
599
- if (d.match(/\d{4}-\d{2}-\d{2}/))
600
- date = d.match(/\d{4}-\d{2}-\d{2}/)[0];
601
- continue;
602
- }
603
- if (l.match(/^\*\*\w/))
604
- continue; // skip any other **Field**: lines
605
- if (l.match(/^#[a-z]/i) && !l.startsWith('# ')) {
606
- tags = l.match(/#\w+/g) ?? [];
607
- continue;
608
- }
609
- // Body content — skip dividers, metadata-looking lines, empty
610
- if (l === '---')
611
- continue;
612
- if (l.match(/^\w[\w_\s]+:\s+\S/))
613
- continue; // skip key: value lines
614
- // Only push real body lines — skip headings, YAML-like keys
615
- if (l.length > 8 && frontmatterDone && !l.startsWith('#') && !l.match(/^[\w_]+:\s*/))
616
- bodyLines.push(l);
617
- }
618
- // For structured chronicles (no YAML), body is after the header divider
619
- if (!frontmatterDone) {
620
- // Find content between the two horizontal rules
621
- const dividers = lines.reduce((acc, l, i) => l.trim() === '---' ? [...acc, i] : acc, []);
622
- if (dividers.length >= 2) {
623
- const between = lines.slice(dividers[0] + 1, dividers[1]);
624
- for (const l of between) {
625
- const lt = l.trim();
626
- if (lt.length > 8 && !lt.startsWith('**') && !lt.startsWith('#') && !lt.match(/^[\w_]+:\s*/))
627
- bodyLines.push(lt);
628
- }
629
- }
630
- }
631
- const firstMeaningful = bodyLines.find(l => l.length > 10);
632
- if (firstMeaningful)
633
- excerpt = firstMeaningful.slice(0, 92) + (firstMeaningful.length > 92 ? '…' : '');
634
- }
635
- catch { /* file unreadable — use defaults */ }
636
- return { title, type, tags, excerpt, date };
637
- };
638
- // ── Separator ──────────────────────────────────────
639
- const SEP = chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72));
640
- // ── Header ────────────────────────────────────────
641
- console.log(chalk_1.default.hex('#8B5CF6').bold(`\n ⬡ MnemoChronicle — Vault\n`));
642
- if (config.workspace && config.resonanceProject) {
643
- console.log(chalk_1.default.gray(' Workspace : ') + chalk_1.default.hex('#A78BFA')(config.workspace) +
644
- chalk_1.default.gray(' · Project : ') + chalk_1.default.hex('#A78BFA')(config.resonanceProject));
645
- }
646
- console.log(chalk_1.default.gray(' Agent : ') + chalk_1.default.white(config.ide) + chalk_1.default.gray(' / ') + chalk_1.default.white(config.provider));
647
- console.log(chalk_1.default.gray(` Vault : `) + chalk_1.default.hex('#64748B')(dir));
648
- console.log(chalk_1.default.gray(` Total : `) + chalk_1.default.white(String(all.length)) + chalk_1.default.gray(` chronicles · showing ${chronicles.length}\n`));
649
- console.log(SEP);
650
- // ── Chronicle cards ───────────────────────────────
651
- chronicles.forEach((filename, i) => {
652
- const { title, type, tags, excerpt, date } = parseChronicle(filename);
653
- const typeColor = TYPE_COLORS[type] ?? chalk_1.default.white;
654
- const icon = TYPE_ICONS[type] ?? '○';
655
- const badge = typeColor(`[${type}]`);
656
- const num = chalk_1.default.hex('#6B7280')(String(i + 1).padStart(2, ' '));
657
- console.log(`\n ${num} ${badge} ` +
658
- chalk_1.default.hex('#94A3B8')('📅 ') + chalk_1.default.hex('#CBD5E1')(date) +
659
- chalk_1.default.hex('#475569')(' ') + typeColor(icon));
660
- console.log(chalk_1.default.white(` ${title}`));
661
- if (excerpt) {
662
- console.log(chalk_1.default.hex('#64748B')(` "${excerpt}"`));
663
- }
664
- if (tags.length > 0) {
665
- const tagStr = tags.map(t => chalk_1.default.hex('#7C3AED')(t)).join(' ');
666
- console.log(' ' + tagStr);
667
- }
668
- });
669
- console.log('\n' + SEP + '\n');
670
- console.log(chalk_1.default.gray(' Tip: ') + chalk_1.default.white('mnemoforge chronicle list -n 20') + chalk_1.default.gray(' to show more\n'));
671
- });
672
- // ── chronicle switch ───────────────────────────────────────────
673
- chronicle
674
- .command('switch')
675
- .description('Switch the active profile to another registered one')
676
- .action(async () => {
677
- const config = (0, vault_js_1.loadVaultConfig)();
678
- if (!config) {
679
- console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
680
- process.exit(1);
681
- }
682
- const profiles = config.registeredModels ?? [];
683
- if (profiles.length === 0) {
684
- console.log(chalk_1.default.yellow('\n ⚠ No other profiles registered.'));
685
- console.log(chalk_1.default.gray(' Use: mnemoforge chronicle init → "Add a new profile"\n'));
686
- return;
687
- }
688
- const choices = profiles.map((p, i) => ({
689
- name: p.ide + ' / ' + p.provider + ' · ' + p.displayName,
690
- value: i,
691
- }));
692
- const { idx } = await inquirer_1.default.prompt([{
693
- type: 'list',
694
- name: 'idx',
695
- message: chalk_1.default.cyan('Which profile to make active?'),
696
- choices,
697
- }]);
698
- const selected = profiles[idx];
699
- const displaced = {
700
- ide: config.ide,
701
- provider: config.provider,
702
- modelId: config.modelId,
703
- displayName: config.displayName,
704
- defaultChronicleStyle: config.defaultChronicleStyle,
705
- };
706
- const newRegistered = profiles.filter((_p, i) => i !== idx);
707
- newRegistered.push(displaced);
708
- (0, vault_js_1.saveVaultConfig)({
709
- ...config,
710
- ide: selected.ide,
711
- provider: selected.provider,
712
- modelId: selected.modelId,
713
- displayName: selected.displayName,
714
- defaultChronicleStyle: selected.defaultChronicleStyle ?? 'session',
715
- registeredModels: newRegistered,
716
- });
717
- console.log(chalk_1.default.green('\n ✔ Switched to: ' + selected.displayName + ' (' + selected.provider + ')\n'));
718
- showWelcome();
719
- });
720
- // ── chronicle config ───────────────────────────────────────────
721
- chronicle
722
- .command('config')
723
- .description('Edit settings (style, display name) of any registered profile')
724
- .action(async () => {
725
- const config = (0, vault_js_1.loadVaultConfig)();
726
- if (!config) {
727
- console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
728
- process.exit(1);
729
- }
730
- const profiles = config.registeredModels ?? [];
731
- // Choose profile to edit
732
- let targetIdx = -1;
733
- if (profiles.length > 0) {
734
- const allChoices = [
735
- { name: '★ ' + config.ide + ' / ' + config.provider + ' · ' + config.displayName + ' [ACTIVE]', value: -1 },
736
- ...profiles.map((p, i) => ({
737
- name: '○ ' + p.ide + ' / ' + p.provider + ' · ' + p.displayName,
738
- value: i,
739
- })),
740
- ];
741
- const { which } = await inquirer_1.default.prompt([{
742
- type: 'list',
743
- name: 'which',
744
- message: chalk_1.default.cyan('Which profile to edit?'),
745
- choices: allChoices,
746
- }]);
747
- targetIdx = which;
748
- }
749
- const isActive = targetIdx === -1;
750
- const curStyle = isActive ? (config.defaultChronicleStyle ?? 'session') : (profiles[targetIdx].defaultChronicleStyle ?? 'session');
751
- const curName = isActive ? config.displayName : profiles[targetIdx].displayName;
752
- const edit = await inquirer_1.default.prompt([
753
- {
754
- type: 'list',
755
- name: 'style',
756
- message: chalk_1.default.cyan('Chronicle style?'),
757
- choices: [
758
- { name: 'Session — coding/work session', value: 'session' },
759
- { name: 'Reflection — deep thoughts', value: 'reflection' },
760
- { name: 'Decision — ADR-style record', value: 'decision' },
761
- { name: 'Sweep — daily digest', value: 'sweep' },
762
- { name: 'Narcissus — soul narrative', value: 'narcissus' },
763
- ],
764
- default: curStyle,
765
- },
766
- {
767
- type: 'input',
768
- name: 'displayName',
769
- message: chalk_1.default.cyan('Display name?'),
770
- default: curName,
771
- },
772
- ]);
773
- if (isActive) {
774
- (0, vault_js_1.saveVaultConfig)({ ...config, defaultChronicleStyle: edit.style, displayName: edit.displayName });
775
- }
776
- else {
777
- const updated = [...profiles];
778
- updated[targetIdx] = { ...updated[targetIdx], defaultChronicleStyle: edit.style, displayName: edit.displayName };
779
- (0, vault_js_1.saveVaultConfig)({ ...config, registeredModels: updated });
780
- }
781
- console.log(chalk_1.default.green('\n ✔ Profile updated!\n'));
782
- showWelcome();
783
- });
784
- program.addCommand(chronicle);
785
- // ─────────────────────────────────────────────
786
- // MODELS COMMAND
787
- // mnemoforge models import | list
788
- // ─────────────────────────────────────────────
789
- const vault_js_2 = require("./lib/vault.js");
67
+ // ── models ────────────────────────────────────────────────────────────────
790
68
  const models = new commander_1.Command('models')
791
69
  .description('Manage the local model registry (UniversalModelCard compatible)');
792
70
  models
@@ -798,39 +76,38 @@ models
798
76
  console.log(chalk_1.default.red(`\n ✖ File not found: ${resolved}\n`));
799
77
  process.exit(1);
800
78
  }
801
- const entry = (0, vault_js_2.importFromModelCard)(resolved);
79
+ const entry = (0, vault_js_1.importFromModelCard)(resolved);
802
80
  if (!entry) {
803
- console.log(chalk_1.default.red('\n ✖ Could not extract model info. Expected fields: model_id, provider\n'));
81
+ console.log(chalk_1.default.red('\n ✖ Could not extract model info.\n'));
804
82
  process.exit(1);
805
83
  }
806
- const existing = (0, vault_js_2.loadCustomModels)();
807
- const alreadyExists = existing.some(m => m.modelId === entry.modelId);
808
- if (alreadyExists) {
84
+ const existing = (0, vault_js_1.loadCustomModels)();
85
+ if (existing.some(m => m.modelId === entry.modelId)) {
809
86
  console.log(chalk_1.default.yellow(`\n ⚠ Model already registered: ${entry.modelId}\n`));
810
87
  return;
811
88
  }
812
- (0, vault_js_2.saveCustomModels)([...existing, entry]);
813
- console.log(chalk_1.default.green(`\n ✔ Model imported!`));
814
- console.log(chalk_1.default.gray(` ID: ${entry.modelId}`));
815
- console.log(chalk_1.default.gray(` Provider: ${entry.provider}`));
816
- console.log(chalk_1.default.gray(` Name: ${entry.displayName}\n`));
89
+ (0, vault_js_1.saveCustomModels)([...existing, entry]);
90
+ console.log(chalk_1.default.green(`\n ✔ Model imported! ${entry.displayName} (${entry.provider})\n`));
817
91
  });
818
92
  models
819
93
  .command('list')
820
94
  .description('List all registered custom models')
821
95
  .action(() => {
822
- const custom = (0, vault_js_2.loadCustomModels)();
96
+ const custom = (0, vault_js_1.loadCustomModels)();
823
97
  console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ Custom Models Registry\n'));
824
98
  if (custom.length === 0) {
825
99
  console.log(chalk_1.default.gray(' No custom models. Use: mnemoforge models import <file.json>\n'));
826
100
  return;
827
101
  }
828
102
  custom.forEach((m, i) => {
829
- console.log(chalk_1.default.gray(` ${String(i + 1).padStart(2, ' ')}. `) +
830
- chalk_1.default.white(`${m.displayName}`) +
831
- chalk_1.default.gray(` · ${m.provider} · ${m.modelId}`));
103
+ console.log(chalk_1.default.gray(` ${String(i + 1).padStart(2, ' ')}. `) + chalk_1.default.white(`${m.displayName}`) + chalk_1.default.gray(` · ${m.provider} · ${m.modelId}`));
832
104
  });
833
105
  console.log();
834
106
  });
107
+ // ── Register all commands ─────────────────────────────────────────────────
108
+ program.addCommand(workspace_js_1.workspaceCommand);
109
+ program.addCommand(workspace_js_1.projectCommand);
110
+ program.addCommand(index_js_1.chronicleCommand);
835
111
  program.addCommand(models);
112
+ program.addCommand(canvas_js_1.canvasCommand);
836
113
  program.parse(process.argv);