@mnemosyne_os/forge 1.2.4 → 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 +32 -890
- package/dist/commands/canvas.js +153 -0
- package/dist/commands/chronicle/index.js +17 -0
- package/dist/commands/chronicle/reader.js +99 -0
- package/dist/commands/chronicle/vault.js +120 -0
- package/dist/commands/chronicle/writer.js +147 -0
- package/dist/commands/chronicle.js +514 -0
- package/dist/commands/workspace.js +129 -0
- package/dist/lib/canvas/canvas.js +92 -0
- package/dist/lib/canvas/renderer.js +40 -0
- package/dist/lib/canvas/templates/cli/files.js +288 -0
- package/dist/lib/chronicle-parser.js +90 -0
- package/dist/lib/chronicle-render.js +76 -0
- package/dist/lib/ui.js +67 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,141 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
120
|
-
.addHelpText('beforeAll',
|
|
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
|
|
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('
|
|
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,785 +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('
|
|
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
|
|
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
|
|
64
|
+
console.log(chalk_1.default.gray('\n Chronicles → ') + chalk_1.default.hex('#A78BFA')(res) + '\n');
|
|
160
65
|
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.white('mnemoforge') + chalk_1.default.gray(' to see your dashboard.\n'));
|
|
161
66
|
});
|
|
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');
|
|
360
|
-
console.log(chalk_1.default.gray(' Run ') + chalk_1.default.white('mnemoforge') + chalk_1.default.gray(' to see your dashboard.\n'));
|
|
361
|
-
});
|
|
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(' · ') + chalk_1.default.white('mnemoforge chronicle open') + chalk_1.default.gray(' to browse interactively\n'));
|
|
671
|
-
});
|
|
672
|
-
// ── chronicle open — interactive picker + inline reader ───────────────────
|
|
673
|
-
chronicle
|
|
674
|
-
.command('open')
|
|
675
|
-
.description('Browse and read chronicles interactively (arrow keys + Enter)')
|
|
676
|
-
.option('-n, --count <n>', 'Max chronicles to show in picker', '20')
|
|
677
|
-
.action(async (opts) => {
|
|
678
|
-
const config = (0, vault_js_1.loadVaultConfig)();
|
|
679
|
-
if (!config) {
|
|
680
|
-
console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
|
|
681
|
-
process.exit(1);
|
|
682
|
-
}
|
|
683
|
-
const { resolveChronicleDir } = require('./lib/vault.js');
|
|
684
|
-
const dir = resolveChronicleDir(config);
|
|
685
|
-
const all = (0, vault_js_1.listChronicles)(config);
|
|
686
|
-
if (all.length === 0) {
|
|
687
|
-
console.log(chalk_1.default.yellow('\n No chronicles in vault yet.\n'));
|
|
688
|
-
return;
|
|
689
|
-
}
|
|
690
|
-
// ── Build picker choices ──────────────────────────────
|
|
691
|
-
const TYPE_ICONS = {
|
|
692
|
-
session: '◈', decision: '◆', reflection: '◇', sweep: '↻', narcissus: '✦',
|
|
693
|
-
};
|
|
694
|
-
const getType = (filename) => {
|
|
695
|
-
try {
|
|
696
|
-
const raw = fs_1.default.readFileSync(path_1.default.join(dir, filename), 'utf8').slice(0, 800);
|
|
697
|
-
const m = raw.match(/\*\*Type\*\*:\s*(\w+)/i) || raw.match(/^type:\s*(\w+)/im);
|
|
698
|
-
return m ? m[1].toLowerCase() : 'session';
|
|
699
|
-
}
|
|
700
|
-
catch {
|
|
701
|
-
return 'session';
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
const picks = all.slice(0, parseInt(opts.count || '20'));
|
|
705
|
-
const choices = picks.map((filename) => {
|
|
706
|
-
const dateMatch = filename.match(/CHRONICLE-(\d{4}-\d{2}-\d{2})/);
|
|
707
|
-
const date = dateMatch ? dateMatch[1] : '';
|
|
708
|
-
const slug = filename.replace(/^CHRONICLE-\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, '').replace(/-/g, ' ');
|
|
709
|
-
const type = getType(filename);
|
|
710
|
-
const icon = TYPE_ICONS[type] ?? '○';
|
|
711
|
-
return {
|
|
712
|
-
name: ` ${icon} ${chalk_1.default.hex('#CBD5E1')(date)} ${chalk_1.default.white(slug)}`,
|
|
713
|
-
value: filename,
|
|
714
|
-
short: slug,
|
|
715
|
-
};
|
|
716
|
-
});
|
|
717
|
-
choices.push({ name: chalk_1.default.gray('\n ✖ Cancel'), value: '__cancel__', short: 'cancel' });
|
|
718
|
-
console.log(chalk_1.default.hex('#8B5CF6').bold('\n ⬡ MnemoChronicle — Open\n'));
|
|
719
|
-
const { selected } = await inquirer_1.default.prompt([{
|
|
720
|
-
type: 'list',
|
|
721
|
-
name: 'selected',
|
|
722
|
-
message: chalk_1.default.hex('#A78BFA')('Select a chronicle to read:'),
|
|
723
|
-
choices,
|
|
724
|
-
pageSize: 15,
|
|
725
|
-
}]);
|
|
726
|
-
if (selected === '__cancel__')
|
|
727
|
-
return;
|
|
728
|
-
// ── Render chronicle ─────────────────────────────────
|
|
729
|
-
const filePath = path_1.default.join(dir, selected);
|
|
730
|
-
const raw = fs_1.default.readFileSync(filePath, 'utf8');
|
|
731
|
-
const resonanceIdx = raw.indexOf('<!--resonance');
|
|
732
|
-
const content = resonanceIdx !== -1 ? raw.slice(0, resonanceIdx) : raw;
|
|
733
|
-
const lines = content.split('\n');
|
|
734
|
-
console.log('\n' + chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72)));
|
|
735
|
-
console.log(chalk_1.default.gray(` ${filePath}`));
|
|
736
|
-
console.log(chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72)) + '\n');
|
|
737
|
-
let inMeta = false;
|
|
738
|
-
let metaDone = false;
|
|
739
|
-
let dividerCount = 0;
|
|
740
|
-
for (const line of lines) {
|
|
741
|
-
const l = line.trim();
|
|
742
|
-
// Title
|
|
743
|
-
if (l.startsWith('# ')) {
|
|
744
|
-
console.log('\n ' + chalk_1.default.hex('#8B5CF6').bold(l.slice(2)));
|
|
745
|
-
continue;
|
|
746
|
-
}
|
|
747
|
-
// Section headers
|
|
748
|
-
if (l.startsWith('## ')) {
|
|
749
|
-
console.log('\n ' + chalk_1.default.hex('#A78BFA').bold(' ' + l.slice(3)));
|
|
750
|
-
continue;
|
|
751
|
-
}
|
|
752
|
-
if (l.startsWith('### ')) {
|
|
753
|
-
console.log(' ' + chalk_1.default.hex('#C084FC')(' ' + l.slice(4)));
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
// Dividers — dim, show once as meta separator
|
|
757
|
-
if (l === '---') {
|
|
758
|
-
dividerCount++;
|
|
759
|
-
if (dividerCount <= 2)
|
|
760
|
-
console.log(chalk_1.default.hex('#312E81')(' ' + '─'.repeat(60)));
|
|
761
|
-
continue;
|
|
762
|
-
}
|
|
763
|
-
// Bold metadata fields **Key**: value
|
|
764
|
-
if (l.match(/^\*\*[\w\s]+\*\*:/)) {
|
|
765
|
-
const parts = l.match(/^\*\*([\w\s]+)\*\*:\s*(.*)/) ?? [];
|
|
766
|
-
if (parts.length >= 3) {
|
|
767
|
-
console.log(' ' + chalk_1.default.hex('#64748B')(` ${parts[1].padEnd(14)} `) + chalk_1.default.hex('#94A3B8')(parts[2]));
|
|
768
|
-
}
|
|
769
|
-
continue;
|
|
770
|
-
}
|
|
771
|
-
// Blockquotes
|
|
772
|
-
if (l.startsWith('> ')) {
|
|
773
|
-
console.log(' ' + chalk_1.default.hex('#7C3AED')(' │ ') + chalk_1.default.hex('#DDD6FE').italic(l.slice(2)));
|
|
774
|
-
continue;
|
|
775
|
-
}
|
|
776
|
-
// List items
|
|
777
|
-
if (l.startsWith('- ') || l.startsWith('* ')) {
|
|
778
|
-
console.log(' ' + chalk_1.default.hex('#6B7280')(' • ') + chalk_1.default.white(l.slice(2)));
|
|
779
|
-
continue;
|
|
780
|
-
}
|
|
781
|
-
// Numbered list
|
|
782
|
-
if (l.match(/^\d+\. /)) {
|
|
783
|
-
console.log(' ' + chalk_1.default.hex('#6B7280')(' ') + chalk_1.default.white(l));
|
|
784
|
-
continue;
|
|
785
|
-
}
|
|
786
|
-
// Code blocks (single line inline or fence)
|
|
787
|
-
if (l.startsWith('```')) {
|
|
788
|
-
console.log(' ' + chalk_1.default.hex('#312E81')(' ' + '·'.repeat(50)));
|
|
789
|
-
continue;
|
|
790
|
-
}
|
|
791
|
-
// Tags line
|
|
792
|
-
if (l.match(/^#\w/) && !l.startsWith('# ')) {
|
|
793
|
-
const tags = l.match(/#\w+/g) ?? [];
|
|
794
|
-
console.log('\n ' + tags.map(t => chalk_1.default.hex('#7C3AED')(t)).join(' '));
|
|
795
|
-
continue;
|
|
796
|
-
}
|
|
797
|
-
// Regular text
|
|
798
|
-
if (l.length > 0) {
|
|
799
|
-
console.log(' ' + chalk_1.default.hex('#E2E8F0')(' ' + l));
|
|
800
|
-
}
|
|
801
|
-
else {
|
|
802
|
-
console.log();
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
console.log('\n' + chalk_1.default.hex('#312E81')(' ' + '─'.repeat(72)));
|
|
806
|
-
console.log(chalk_1.default.gray(`\n Tip: open in editor → `) + chalk_1.default.white(`code "${filePath}"`) + '\n');
|
|
807
|
-
});
|
|
808
|
-
chronicle
|
|
809
|
-
.command('switch')
|
|
810
|
-
.description('Switch the active profile to another registered one')
|
|
811
|
-
.action(async () => {
|
|
812
|
-
const config = (0, vault_js_1.loadVaultConfig)();
|
|
813
|
-
if (!config) {
|
|
814
|
-
console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
|
|
815
|
-
process.exit(1);
|
|
816
|
-
}
|
|
817
|
-
const profiles = config.registeredModels ?? [];
|
|
818
|
-
if (profiles.length === 0) {
|
|
819
|
-
console.log(chalk_1.default.yellow('\n ⚠ No other profiles registered.'));
|
|
820
|
-
console.log(chalk_1.default.gray(' Use: mnemoforge chronicle init → "Add a new profile"\n'));
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
const choices = profiles.map((p, i) => ({
|
|
824
|
-
name: p.ide + ' / ' + p.provider + ' · ' + p.displayName,
|
|
825
|
-
value: i,
|
|
826
|
-
}));
|
|
827
|
-
const { idx } = await inquirer_1.default.prompt([{
|
|
828
|
-
type: 'list',
|
|
829
|
-
name: 'idx',
|
|
830
|
-
message: chalk_1.default.cyan('Which profile to make active?'),
|
|
831
|
-
choices,
|
|
832
|
-
}]);
|
|
833
|
-
const selected = profiles[idx];
|
|
834
|
-
const displaced = {
|
|
835
|
-
ide: config.ide,
|
|
836
|
-
provider: config.provider,
|
|
837
|
-
modelId: config.modelId,
|
|
838
|
-
displayName: config.displayName,
|
|
839
|
-
defaultChronicleStyle: config.defaultChronicleStyle,
|
|
840
|
-
};
|
|
841
|
-
const newRegistered = profiles.filter((_p, i) => i !== idx);
|
|
842
|
-
newRegistered.push(displaced);
|
|
843
|
-
(0, vault_js_1.saveVaultConfig)({
|
|
844
|
-
...config,
|
|
845
|
-
ide: selected.ide,
|
|
846
|
-
provider: selected.provider,
|
|
847
|
-
modelId: selected.modelId,
|
|
848
|
-
displayName: selected.displayName,
|
|
849
|
-
defaultChronicleStyle: selected.defaultChronicleStyle ?? 'session',
|
|
850
|
-
registeredModels: newRegistered,
|
|
851
|
-
});
|
|
852
|
-
console.log(chalk_1.default.green('\n ✔ Switched to: ' + selected.displayName + ' (' + selected.provider + ')\n'));
|
|
853
|
-
showWelcome();
|
|
854
|
-
});
|
|
855
|
-
// ── chronicle config ───────────────────────────────────────────
|
|
856
|
-
chronicle
|
|
857
|
-
.command('config')
|
|
858
|
-
.description('Edit settings (style, display name) of any registered profile')
|
|
859
|
-
.action(async () => {
|
|
860
|
-
const config = (0, vault_js_1.loadVaultConfig)();
|
|
861
|
-
if (!config) {
|
|
862
|
-
console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
|
|
863
|
-
process.exit(1);
|
|
864
|
-
}
|
|
865
|
-
const profiles = config.registeredModels ?? [];
|
|
866
|
-
// Choose profile to edit
|
|
867
|
-
let targetIdx = -1;
|
|
868
|
-
if (profiles.length > 0) {
|
|
869
|
-
const allChoices = [
|
|
870
|
-
{ name: '★ ' + config.ide + ' / ' + config.provider + ' · ' + config.displayName + ' [ACTIVE]', value: -1 },
|
|
871
|
-
...profiles.map((p, i) => ({
|
|
872
|
-
name: '○ ' + p.ide + ' / ' + p.provider + ' · ' + p.displayName,
|
|
873
|
-
value: i,
|
|
874
|
-
})),
|
|
875
|
-
];
|
|
876
|
-
const { which } = await inquirer_1.default.prompt([{
|
|
877
|
-
type: 'list',
|
|
878
|
-
name: 'which',
|
|
879
|
-
message: chalk_1.default.cyan('Which profile to edit?'),
|
|
880
|
-
choices: allChoices,
|
|
881
|
-
}]);
|
|
882
|
-
targetIdx = which;
|
|
883
|
-
}
|
|
884
|
-
const isActive = targetIdx === -1;
|
|
885
|
-
const curStyle = isActive ? (config.defaultChronicleStyle ?? 'session') : (profiles[targetIdx].defaultChronicleStyle ?? 'session');
|
|
886
|
-
const curName = isActive ? config.displayName : profiles[targetIdx].displayName;
|
|
887
|
-
const edit = await inquirer_1.default.prompt([
|
|
888
|
-
{
|
|
889
|
-
type: 'list',
|
|
890
|
-
name: 'style',
|
|
891
|
-
message: chalk_1.default.cyan('Chronicle style?'),
|
|
892
|
-
choices: [
|
|
893
|
-
{ name: 'Session — coding/work session', value: 'session' },
|
|
894
|
-
{ name: 'Reflection — deep thoughts', value: 'reflection' },
|
|
895
|
-
{ name: 'Decision — ADR-style record', value: 'decision' },
|
|
896
|
-
{ name: 'Sweep — daily digest', value: 'sweep' },
|
|
897
|
-
{ name: 'Narcissus — soul narrative', value: 'narcissus' },
|
|
898
|
-
],
|
|
899
|
-
default: curStyle,
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
type: 'input',
|
|
903
|
-
name: 'displayName',
|
|
904
|
-
message: chalk_1.default.cyan('Display name?'),
|
|
905
|
-
default: curName,
|
|
906
|
-
},
|
|
907
|
-
]);
|
|
908
|
-
if (isActive) {
|
|
909
|
-
(0, vault_js_1.saveVaultConfig)({ ...config, defaultChronicleStyle: edit.style, displayName: edit.displayName });
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
const updated = [...profiles];
|
|
913
|
-
updated[targetIdx] = { ...updated[targetIdx], defaultChronicleStyle: edit.style, displayName: edit.displayName };
|
|
914
|
-
(0, vault_js_1.saveVaultConfig)({ ...config, registeredModels: updated });
|
|
915
|
-
}
|
|
916
|
-
console.log(chalk_1.default.green('\n ✔ Profile updated!\n'));
|
|
917
|
-
showWelcome();
|
|
918
|
-
});
|
|
919
|
-
program.addCommand(chronicle);
|
|
920
|
-
// ─────────────────────────────────────────────
|
|
921
|
-
// MODELS COMMAND
|
|
922
|
-
// mnemoforge models import | list
|
|
923
|
-
// ─────────────────────────────────────────────
|
|
924
|
-
const vault_js_2 = require("./lib/vault.js");
|
|
67
|
+
// ── models ────────────────────────────────────────────────────────────────
|
|
925
68
|
const models = new commander_1.Command('models')
|
|
926
69
|
.description('Manage the local model registry (UniversalModelCard compatible)');
|
|
927
70
|
models
|
|
@@ -933,39 +76,38 @@ models
|
|
|
933
76
|
console.log(chalk_1.default.red(`\n ✖ File not found: ${resolved}\n`));
|
|
934
77
|
process.exit(1);
|
|
935
78
|
}
|
|
936
|
-
const entry = (0,
|
|
79
|
+
const entry = (0, vault_js_1.importFromModelCard)(resolved);
|
|
937
80
|
if (!entry) {
|
|
938
|
-
console.log(chalk_1.default.red('\n ✖ Could not extract model info
|
|
81
|
+
console.log(chalk_1.default.red('\n ✖ Could not extract model info.\n'));
|
|
939
82
|
process.exit(1);
|
|
940
83
|
}
|
|
941
|
-
const existing = (0,
|
|
942
|
-
|
|
943
|
-
if (alreadyExists) {
|
|
84
|
+
const existing = (0, vault_js_1.loadCustomModels)();
|
|
85
|
+
if (existing.some(m => m.modelId === entry.modelId)) {
|
|
944
86
|
console.log(chalk_1.default.yellow(`\n ⚠ Model already registered: ${entry.modelId}\n`));
|
|
945
87
|
return;
|
|
946
88
|
}
|
|
947
|
-
(0,
|
|
948
|
-
console.log(chalk_1.default.green(`\n ✔ Model imported
|
|
949
|
-
console.log(chalk_1.default.gray(` ID: ${entry.modelId}`));
|
|
950
|
-
console.log(chalk_1.default.gray(` Provider: ${entry.provider}`));
|
|
951
|
-
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`));
|
|
952
91
|
});
|
|
953
92
|
models
|
|
954
93
|
.command('list')
|
|
955
94
|
.description('List all registered custom models')
|
|
956
95
|
.action(() => {
|
|
957
|
-
const custom = (0,
|
|
96
|
+
const custom = (0, vault_js_1.loadCustomModels)();
|
|
958
97
|
console.log(chalk_1.default.hex('#8B5CF6').bold('\n⬡ Custom Models Registry\n'));
|
|
959
98
|
if (custom.length === 0) {
|
|
960
99
|
console.log(chalk_1.default.gray(' No custom models. Use: mnemoforge models import <file.json>\n'));
|
|
961
100
|
return;
|
|
962
101
|
}
|
|
963
102
|
custom.forEach((m, i) => {
|
|
964
|
-
console.log(chalk_1.default.gray(` ${String(i + 1).padStart(2, ' ')}. `) +
|
|
965
|
-
chalk_1.default.white(`${m.displayName}`) +
|
|
966
|
-
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}`));
|
|
967
104
|
});
|
|
968
105
|
console.log();
|
|
969
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);
|
|
970
111
|
program.addCommand(models);
|
|
112
|
+
program.addCommand(canvas_js_1.canvasCommand);
|
|
971
113
|
program.parse(process.argv);
|