@mnemosyne_os/forge 1.2.2 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +233 -230
- package/dist/cli.js +280 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,230 +1,233 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
```
|
|
4
|
-
███╗ ███╗███╗ ██╗███████╗███╗ ███╗ ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗
|
|
5
|
-
████╗ ████║████╗ ██║██╔════╝████╗ ████║██╔═══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
|
|
6
|
-
██╔████╔██║██╔██╗ ██║█████╗ ██╔████╔██║██║ ██║█████╗ ██║ ██║██████╔╝██║ ███╗█████╗
|
|
7
|
-
██║╚██╔╝██║██║╚██╗██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
|
|
8
|
-
██║ ╚═╝ ██║██║ ╚████║███████╗██║ ╚═╝ ██║╚██████╔╝██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
|
|
9
|
-
╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
**MnemoForge CLI** — The Inception Engine of the Mnemosyne Neural OS
|
|
13
|
-
|
|
14
|
-
[](https://www.npmjs.com/package/@mnemosyne_os/forge)
|
|
15
|
+
[](#)
|
|
16
|
+
[](./LICENSE)
|
|
17
|
+
[](https://www.typescriptlang.org/)
|
|
18
|
+
[](https://nodejs.org/)
|
|
19
|
+
[](https://github.com/yaka0007/Mnemosyne-Neural-OS)
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
> **⚠️ This package is in active beta development.** APIs and commands may change between minor versions. We are using it in production on Mnemosyne OS itself — feedback and issues welcome.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## What is MnemoForge?
|
|
28
|
+
|
|
29
|
+
**MnemoForge** is the official CLI for the [Mnemosyne Neural OS](https://github.com/yaka0007/Mnemosyne-Neural-OS) ecosystem.
|
|
30
|
+
|
|
31
|
+
It gives AI agents something they fundamentally lack: **persistent, versionable, IDE-agnostic memory**.
|
|
32
|
+
|
|
33
|
+
Every time an agent starts a new session, it starts from zero. MnemoForge fixes this with two systems:
|
|
34
|
+
|
|
35
|
+
| System | Purpose |
|
|
36
|
+
|---|---|
|
|
37
|
+
| **Chronicles** | Structured memory files capturing key decisions and sessions |
|
|
38
|
+
| **Workspace / Resonance Project** | Project-level rules that any agent reads at session start |
|
|
39
|
+
|
|
40
|
+
> **"Don't just scaffold code. Scaffold intelligence."**
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g @mnemosyne_os/forge
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Verify:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
mnemoforge --version
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 1. Initialize the vault (once per machine)
|
|
62
|
+
mnemoforge chronicle init
|
|
63
|
+
|
|
64
|
+
# 2. Set up a Workspace (ecosystem / org)
|
|
65
|
+
mnemoforge workspace init
|
|
66
|
+
# → Workspace name: Mnemosyne-OS
|
|
67
|
+
|
|
68
|
+
# 3. Create a Resonance Project (feature / component)
|
|
69
|
+
mnemoforge project init
|
|
70
|
+
# → Project: CLI
|
|
71
|
+
# → Chronicles vault: ~/MnemoVault/Mnemosyne-OS/CLI/Antigravity/Anthropic/
|
|
72
|
+
|
|
73
|
+
# 4. Check workspace rules before starting work (agent briefing)
|
|
74
|
+
mnemoforge workspace show
|
|
75
|
+
|
|
76
|
+
# 5. Archive a chronicle written by your agent
|
|
77
|
+
mnemoforge chronicle archive --file "handbook/chronicles/CHRONICLE-2026-04-05-my-decision.md"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Core Concepts
|
|
83
|
+
|
|
84
|
+
### Chronicles
|
|
85
|
+
|
|
86
|
+
Chronicles are structured Markdown files that capture key decisions, sessions, and architectural moments.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
CHRONICLE-YYYY-MM-DD-short-slug.md
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
They are written by the AI agent at the moment a decision is made — not after. They are archived into a structured vault organized by Workspace → Project → IDE → Provider.
|
|
93
|
+
|
|
94
|
+
**Chronicle styles:** `session` · `decision` · `reflection` · `sweep` · `narcissus`
|
|
95
|
+
|
|
96
|
+
### Workspace & Resonance Project
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
MnemoVault/
|
|
100
|
+
Mnemosyne-OS/ ← Workspace (ecosystem)
|
|
101
|
+
CLI/ ← Resonance Project (feature)
|
|
102
|
+
Antigravity/ ← IDE
|
|
103
|
+
Anthropic/ ← Provider
|
|
104
|
+
CHRONICLE-...md
|
|
105
|
+
Dashboard/
|
|
106
|
+
...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The `WORKSPACE.json` file stores project-level rules readable by any agent at session start — independent of any IDE, cloud, or session state.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Commands
|
|
114
|
+
|
|
115
|
+
### Scaffold
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
mnemoforge init [module-name] # scaffold a new Mnemosyne module
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Chronicle (Memory)
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
mnemoforge chronicle init # configure vault (IDE, provider, path)
|
|
125
|
+
mnemoforge chronicle archive --file <path> # archive an agent-written chronicle
|
|
126
|
+
mnemoforge chronicle commit # create a chronicle interactively or --auto
|
|
127
|
+
mnemoforge chronicle list # list chronicles in the vault
|
|
128
|
+
mnemoforge chronicle sweep # generate a daily consolidation chronicle
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Workspace
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
mnemoforge workspace init # create WORKSPACE.json in the current project
|
|
135
|
+
mnemoforge workspace show # display rules as an agent briefing
|
|
136
|
+
mnemoforge workspace add-rule "<rule>" [--section <section>]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Resonance Project
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
mnemoforge project init # link workspace + project + scaffold handbook/chronicles/
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Neural Coding
|
|
148
|
+
|
|
149
|
+
MnemoForge implements **Neural Coding** — a development methodology where:
|
|
150
|
+
|
|
151
|
+
- The **human** holds the intention and understands the system
|
|
152
|
+
- The **agent** understands the context and executes without mechanical re-explanations
|
|
153
|
+
- The **memory** persists outside of any IDE, session, or conversation
|
|
154
|
+
|
|
155
|
+
The code follows the thought. Not the other way around.
|
|
156
|
+
|
|
157
|
+
→ [Read the Neural Coding definition](https://github.com/yaka0007/Mnemosyne-Neural-OS/tree/main/cli/docs/04-neural-coding.md)
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Documentation
|
|
162
|
+
|
|
163
|
+
Full documentation available in the [cli/docs](https://github.com/yaka0007/Mnemosyne-Neural-OS/tree/main/cli/docs) folder:
|
|
164
|
+
|
|
165
|
+
- [Getting Started](https://github.com/yaka0007/Mnemosyne-Neural-OS/blob/main/cli/docs/01-getting-started.md)
|
|
166
|
+
- [Chronicle System](https://github.com/yaka0007/Mnemosyne-Neural-OS/blob/main/cli/docs/02-chronicle.md)
|
|
167
|
+
- [Workspace Memory](https://github.com/yaka0007/Mnemosyne-Neural-OS/blob/main/cli/docs/03-workspace.md)
|
|
168
|
+
- [Neural Coding Principles](https://github.com/yaka0007/Mnemosyne-Neural-OS/blob/main/cli/docs/04-neural-coding.md)
|
|
169
|
+
- [Command Reference](https://github.com/yaka0007/Mnemosyne-Neural-OS/blob/main/cli/docs/05-command-reference.md)
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Roadmap
|
|
174
|
+
|
|
175
|
+
- [x] `mnemoforge init` — module scaffolding with AI governance injection
|
|
176
|
+
- [x] `mnemoforge chronicle init/archive/commit/list/sweep` — agent memory vault
|
|
177
|
+
- [x] `mnemoforge workspace init/show/add-rule` — project safety memory
|
|
178
|
+
- [x] `mnemoforge project init` — Resonance Project hierarchy
|
|
179
|
+
- [ ] `mnemoforge workspace watcher` — auto-archive chronicles on file creation
|
|
180
|
+
- [ ] `mnemoforge canvas` — deploy pre-configured project templates
|
|
181
|
+
- [ ] Chronicle certification — cryptographic signature (Neural Coding P5)
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Development
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
git clone https://github.com/yaka0007/Mnemosyne-Neural-OS.git
|
|
189
|
+
cd Mnemosyne-Neural-OS/cli
|
|
190
|
+
npm install
|
|
191
|
+
npm run build
|
|
192
|
+
npm link
|
|
193
|
+
mnemoforge --version
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Tech Stack
|
|
199
|
+
|
|
200
|
+
| Layer | Technology |
|
|
201
|
+
|---|---|
|
|
202
|
+
| Runtime | Node.js 18+ |
|
|
203
|
+
| Language | TypeScript 5 (strict) |
|
|
204
|
+
| CLI Framework | Commander.js v11 |
|
|
205
|
+
| Prompts | Inquirer.js v8 |
|
|
206
|
+
| Output Styling | Chalk v4 |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT © 2026 [XPACEGEMS LLC](https://xpacegems.com) — Tony Trochet
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## About
|
|
217
|
+
|
|
218
|
+
**XPACEGEMS LLC** — Independent AI Software Lab
|
|
219
|
+
Miami, FL 33122, USA
|
|
220
|
+
Founder & Lead Architect: [Tony Trochet](https://www.linkedin.com/in/tony-t-19544650/)
|
|
221
|
+
|
|
222
|
+
Built as part of **Mnemosyne Neural OS** — a sovereign AI operating system.
|
|
223
|
+
Powered by **Antigravity (Google DeepMind)** · **Claude (Anthropic)** · **Cursor**
|
|
224
|
+
|
|
225
|
+
> *"The model may not know who it is. The soul does."*
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
<div align="center">
|
|
230
|
+
|
|
231
|
+
**[⭐ Star on GitHub](https://github.com/yaka0007/Mnemosyne-Neural-OS)** · **[📖 Documentation](https://github.com/yaka0007/Mnemosyne-Neural-OS/tree/main/cli/docs)** · **[🐛 Report Issues](https://github.com/yaka0007/Mnemosyne-Neural-OS/issues)**
|
|
232
|
+
|
|
233
|
+
</div>
|
package/dist/cli.js
CHANGED
|
@@ -521,18 +521,290 @@ chronicle
|
|
|
521
521
|
console.log(chalk_1.default.red('\n ✖ Not initialized. Run: mnemoforge chronicle init\n'));
|
|
522
522
|
process.exit(1);
|
|
523
523
|
}
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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'));
|
|
528
688
|
return;
|
|
529
689
|
}
|
|
530
|
-
|
|
531
|
-
|
|
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
|
+
};
|
|
532
716
|
});
|
|
533
|
-
|
|
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');
|
|
534
807
|
});
|
|
535
|
-
// ── chronicle switch ───────────────────────────────────────────
|
|
536
808
|
chronicle
|
|
537
809
|
.command('switch')
|
|
538
810
|
.description('Switch the active profile to another registered one')
|
package/package.json
CHANGED