clawvault 1.4.0 → 1.4.2
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 +23 -15
- package/bin/clawvault.js +175 -49
- package/dist/chunk-MILVYUPK.js +146 -0
- package/dist/commands/doctor.d.ts +16 -0
- package/dist/commands/doctor.js +256 -0
- package/dist/commands/link.js +3 -3
- package/dist/commands/recover.js +5 -140
- package/dist/commands/shell-init.d.ts +7 -0
- package/dist/commands/shell-init.js +40 -0
- package/dist/commands/sleep.d.ts +32 -0
- package/dist/commands/sleep.js +157 -0
- package/dist/commands/status.js +11 -27
- package/dist/commands/wake.d.ts +19 -0
- package/dist/commands/wake.js +57 -0
- package/dist/index.d.ts +3 -158
- package/dist/index.js +6 -6
- package/dist/types-DO8rJ490.d.ts +158 -0
- package/hooks/clawvault/HOOK.md +51 -0
- package/hooks/clawvault/handler.js +147 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -55,13 +55,16 @@ clawvault search "decision" # BM25 keyword search
|
|
|
55
55
|
clawvault vsearch "what did I decide" # Semantic search
|
|
56
56
|
|
|
57
57
|
# Session management
|
|
58
|
-
clawvault
|
|
59
|
-
clawvault
|
|
58
|
+
clawvault wake
|
|
59
|
+
clawvault sleep "build wake/sleep commands" --next "run doctor"
|
|
60
|
+
clawvault handoff --working-on "task1" --next "task2" # Manual handoff (advanced)
|
|
61
|
+
clawvault recap # Manual recap (advanced)
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
**Tip:** Set `CLAWVAULT_PATH`
|
|
64
|
+
**Tip:** Set `CLAWVAULT_PATH` to skip directory walk (or use `shell-init`):
|
|
63
65
|
```bash
|
|
64
66
|
echo 'export CLAWVAULT_PATH="$HOME/memory"' >> ~/.bashrc
|
|
67
|
+
eval "$(clawvault shell-init)"
|
|
65
68
|
```
|
|
66
69
|
|
|
67
70
|
## Search: qmd vs memory_search
|
|
@@ -135,18 +138,19 @@ clawvault stats # Vault overview
|
|
|
135
138
|
### Session Continuity
|
|
136
139
|
|
|
137
140
|
```bash
|
|
138
|
-
#
|
|
139
|
-
clawvault
|
|
140
|
-
|
|
141
|
+
# Start a session (recover + recap + summary)
|
|
142
|
+
clawvault wake
|
|
143
|
+
|
|
144
|
+
# End a session with a handoff
|
|
145
|
+
clawvault sleep "building CRM, fixing webhook" \
|
|
141
146
|
--blocked "waiting for API key" \
|
|
142
147
|
--next "deploy to production" \
|
|
143
148
|
--decisions "chose Supabase over Firebase" \
|
|
144
149
|
--feeling "focused"
|
|
145
150
|
|
|
146
|
-
#
|
|
147
|
-
clawvault
|
|
148
|
-
clawvault recap --brief # Token-efficient
|
|
149
|
-
clawvault recap --json # For programmatic use
|
|
151
|
+
# Manual tools (advanced)
|
|
152
|
+
clawvault handoff --working-on "task1" --next "task2"
|
|
153
|
+
clawvault recap --brief # Token-efficient recap
|
|
150
154
|
|
|
151
155
|
# Health check
|
|
152
156
|
clawvault doctor
|
|
@@ -173,15 +177,19 @@ clawvault remember decision "Title" --content "..."
|
|
|
173
177
|
clawvault remember lesson "Title" --content "..."
|
|
174
178
|
\`\`\`
|
|
175
179
|
|
|
176
|
-
### Session
|
|
177
|
-
|
|
180
|
+
### Session Start
|
|
181
|
+
\`\`\`bash
|
|
182
|
+
clawvault wake
|
|
183
|
+
\`\`\`
|
|
184
|
+
|
|
185
|
+
### Session End
|
|
178
186
|
\`\`\`bash
|
|
179
|
-
clawvault
|
|
187
|
+
clawvault sleep "..." --next "..."
|
|
180
188
|
\`\`\`
|
|
181
189
|
|
|
182
|
-
|
|
190
|
+
### Checkpoint (during heavy work)
|
|
183
191
|
\`\`\`bash
|
|
184
|
-
clawvault
|
|
192
|
+
clawvault checkpoint --working-on "..." --focus "..." --blocked "..."
|
|
185
193
|
\`\`\`
|
|
186
194
|
|
|
187
195
|
### Why qmd over memory_search?
|
package/bin/clawvault.js
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
ClawVault,
|
|
15
15
|
createVault,
|
|
16
16
|
findVault,
|
|
17
|
-
hasQmd,
|
|
18
17
|
QmdUnavailableError,
|
|
19
18
|
QMD_INSTALL_COMMAND
|
|
20
19
|
} from '../dist/index.js';
|
|
@@ -59,6 +58,28 @@ async function getVault(vaultPath) {
|
|
|
59
58
|
return vault;
|
|
60
59
|
}
|
|
61
60
|
|
|
61
|
+
function resolveVaultPath(vaultPath) {
|
|
62
|
+
if (vaultPath) {
|
|
63
|
+
return path.resolve(vaultPath);
|
|
64
|
+
}
|
|
65
|
+
if (process.env.CLAWVAULT_PATH) {
|
|
66
|
+
return path.resolve(process.env.CLAWVAULT_PATH);
|
|
67
|
+
}
|
|
68
|
+
let current = process.cwd();
|
|
69
|
+
while (true) {
|
|
70
|
+
if (fs.existsSync(path.join(current, '.clawvault.json'))) {
|
|
71
|
+
return current;
|
|
72
|
+
}
|
|
73
|
+
const parent = path.dirname(current);
|
|
74
|
+
if (parent === current) {
|
|
75
|
+
console.error(chalk.red('Error: No ClawVault found. Run `clawvault init` first.'));
|
|
76
|
+
console.log(chalk.dim('Tip: Set CLAWVAULT_PATH environment variable to your vault path'));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
current = parent;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
62
83
|
async function runQmd(args) {
|
|
63
84
|
return new Promise((resolve, reject) => {
|
|
64
85
|
const proc = spawn('qmd', args, { stdio: 'inherit' });
|
|
@@ -571,6 +592,117 @@ program
|
|
|
571
592
|
}
|
|
572
593
|
});
|
|
573
594
|
|
|
595
|
+
// === WAKE (session start) ===
|
|
596
|
+
program
|
|
597
|
+
.command('wake')
|
|
598
|
+
.description('Start a session (recover + recap + summary)')
|
|
599
|
+
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include', '3')
|
|
600
|
+
.option('--full', 'Show full recap (default: brief)')
|
|
601
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
602
|
+
.action(async (options) => {
|
|
603
|
+
try {
|
|
604
|
+
const vaultPath = resolveVaultPath(options.vault);
|
|
605
|
+
const { wake } = await import('../dist/commands/wake.js');
|
|
606
|
+
const { formatRecoveryInfo } = await import('../dist/commands/recover.js');
|
|
607
|
+
const result = await wake({
|
|
608
|
+
vaultPath,
|
|
609
|
+
handoffLimit: parseInt(options.handoffLimit),
|
|
610
|
+
brief: !options.full
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
console.log(chalk.cyan('\n🌅 ClawVault Wake\n'));
|
|
614
|
+
console.log(formatRecoveryInfo(result.recovery));
|
|
615
|
+
console.log();
|
|
616
|
+
console.log(chalk.cyan('Recap'));
|
|
617
|
+
console.log(result.recapMarkdown.trim());
|
|
618
|
+
console.log();
|
|
619
|
+
console.log(chalk.green(`You were working on: ${result.summary}`));
|
|
620
|
+
|
|
621
|
+
process.exitCode = result.recovery.died ? 1 : 0;
|
|
622
|
+
} catch (err) {
|
|
623
|
+
if (err instanceof QmdUnavailableError) {
|
|
624
|
+
printQmdMissing();
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
628
|
+
process.exit(1);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
// === SLEEP (session end) ===
|
|
633
|
+
program
|
|
634
|
+
.command('sleep <summary>')
|
|
635
|
+
.description('End a session with a handoff (and optional git commit)')
|
|
636
|
+
.option('-n, --next <items>', 'Next steps (comma-separated)')
|
|
637
|
+
.option('-b, --blocked <items>', 'Blocked items (comma-separated)')
|
|
638
|
+
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
639
|
+
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
640
|
+
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
641
|
+
.option('-s, --session <key>', 'Session key')
|
|
642
|
+
.option('--index', 'Update qmd index after handoff')
|
|
643
|
+
.option('--no-git', 'Skip git commit prompt')
|
|
644
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
645
|
+
.action(async (summary, options) => {
|
|
646
|
+
try {
|
|
647
|
+
const vaultPath = resolveVaultPath(options.vault);
|
|
648
|
+
const { sleep } = await import('../dist/commands/sleep.js');
|
|
649
|
+
const result = await sleep({
|
|
650
|
+
workingOn: summary,
|
|
651
|
+
next: options.next,
|
|
652
|
+
blocked: options.blocked,
|
|
653
|
+
decisions: options.decisions,
|
|
654
|
+
questions: options.questions,
|
|
655
|
+
feeling: options.feeling,
|
|
656
|
+
sessionKey: options.session,
|
|
657
|
+
vaultPath,
|
|
658
|
+
index: options.index,
|
|
659
|
+
git: options.git
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
console.log(chalk.green(`✓ Handoff saved: ${result.document.id}`));
|
|
663
|
+
console.log(chalk.dim(` Path: ${result.document.path}`));
|
|
664
|
+
console.log(chalk.dim(` Working on: ${result.handoff.workingOn.join(', ')}`));
|
|
665
|
+
if (result.handoff.nextSteps.length > 0) {
|
|
666
|
+
console.log(chalk.dim(` Next: ${result.handoff.nextSteps.join(', ')}`));
|
|
667
|
+
} else {
|
|
668
|
+
console.log(chalk.dim(' Next: (none)'));
|
|
669
|
+
}
|
|
670
|
+
if (result.handoff.blocked.length > 0) {
|
|
671
|
+
console.log(chalk.dim(` Blocked: ${result.handoff.blocked.join(', ')}`));
|
|
672
|
+
} else {
|
|
673
|
+
console.log(chalk.dim(' Blocked: (none)'));
|
|
674
|
+
}
|
|
675
|
+
if (result.handoff.decisions?.length) {
|
|
676
|
+
console.log(chalk.dim(` Decisions: ${result.handoff.decisions.join(', ')}`));
|
|
677
|
+
}
|
|
678
|
+
if (result.handoff.openQuestions?.length) {
|
|
679
|
+
console.log(chalk.dim(` Questions: ${result.handoff.openQuestions.join(', ')}`));
|
|
680
|
+
}
|
|
681
|
+
if (result.handoff.feeling) {
|
|
682
|
+
console.log(chalk.dim(` Feeling: ${result.handoff.feeling}`));
|
|
683
|
+
}
|
|
684
|
+
if (options.index) {
|
|
685
|
+
console.log(chalk.dim(' qmd: index updated'));
|
|
686
|
+
}
|
|
687
|
+
if (result.git) {
|
|
688
|
+
if (result.git.committed) {
|
|
689
|
+
console.log(chalk.green(`✓ Git commit created${result.git.message ? `: ${result.git.message}` : ''}`));
|
|
690
|
+
} else if (result.git.skippedReason === 'clean') {
|
|
691
|
+
console.log(chalk.dim(' Git: clean'));
|
|
692
|
+
} else if (result.git.skippedReason === 'declined') {
|
|
693
|
+
console.log(chalk.dim(' Git: commit skipped'));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
} catch (err) {
|
|
697
|
+
if (err instanceof QmdUnavailableError) {
|
|
698
|
+
printQmdMissing();
|
|
699
|
+
process.exit(1);
|
|
700
|
+
}
|
|
701
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
702
|
+
process.exit(1);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
|
|
574
706
|
// === HANDOFF (session bridge) ===
|
|
575
707
|
program
|
|
576
708
|
.command('handoff')
|
|
@@ -651,6 +783,20 @@ program
|
|
|
651
783
|
}
|
|
652
784
|
});
|
|
653
785
|
|
|
786
|
+
// === SHELL INIT ===
|
|
787
|
+
program
|
|
788
|
+
.command('shell-init')
|
|
789
|
+
.description('Output shell integration for ClawVault')
|
|
790
|
+
.action(async () => {
|
|
791
|
+
try {
|
|
792
|
+
const { shellInit } = await import('../dist/commands/shell-init.js');
|
|
793
|
+
console.log(shellInit());
|
|
794
|
+
} catch (err) {
|
|
795
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
796
|
+
process.exit(1);
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
|
|
654
800
|
// === TEMPLATE ===
|
|
655
801
|
const template = program
|
|
656
802
|
.command('template')
|
|
@@ -725,59 +871,39 @@ program
|
|
|
725
871
|
.description('Check ClawVault setup health')
|
|
726
872
|
.option('-v, --vault <path>', 'Vault path')
|
|
727
873
|
.action(async (options) => {
|
|
728
|
-
console.log(chalk.cyan('\n🩺 ClawVault Health Check\n'));
|
|
729
|
-
|
|
730
|
-
let issues = 0;
|
|
731
|
-
|
|
732
|
-
// Check qmd
|
|
733
|
-
if (hasQmd()) {
|
|
734
|
-
console.log(chalk.green('✓ qmd installed'));
|
|
735
|
-
} else {
|
|
736
|
-
console.log(chalk.red('✗ qmd not installed'));
|
|
737
|
-
console.log(chalk.dim(` Install: ${QMD_INSTALL_COMMAND}`));
|
|
738
|
-
issues++;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
// Check vault
|
|
742
874
|
try {
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const collection = vault.getQmdCollection();
|
|
751
|
-
if (collection) {
|
|
752
|
-
console.log(chalk.green(`✓ qmd collection: ${collection}`));
|
|
753
|
-
} else {
|
|
754
|
-
console.log(chalk.yellow('⚠ No qmd collection configured'));
|
|
755
|
-
issues++;
|
|
875
|
+
const { doctor } = await import('../dist/commands/doctor.js');
|
|
876
|
+
const report = await doctor(options.vault);
|
|
877
|
+
|
|
878
|
+
console.log(chalk.cyan('\n🩺 ClawVault Health Check\n'));
|
|
879
|
+
if (report.vaultPath) {
|
|
880
|
+
console.log(chalk.dim(`Vault: ${report.vaultPath}`));
|
|
881
|
+
console.log();
|
|
756
882
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
883
|
+
|
|
884
|
+
for (const check of report.checks) {
|
|
885
|
+
const prefix = check.status === 'ok'
|
|
886
|
+
? chalk.green('✓')
|
|
887
|
+
: check.status === 'warn'
|
|
888
|
+
? chalk.yellow('⚠')
|
|
889
|
+
: chalk.red('✗');
|
|
890
|
+
const detail = check.detail ? ` — ${check.detail}` : '';
|
|
891
|
+
console.log(`${prefix} ${check.label}${detail}`);
|
|
892
|
+
if (check.hint) {
|
|
893
|
+
console.log(chalk.dim(` ${check.hint}`));
|
|
894
|
+
}
|
|
764
895
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
896
|
+
|
|
897
|
+
const issues = report.warnings + report.errors;
|
|
898
|
+
console.log();
|
|
899
|
+
if (issues === 0) {
|
|
900
|
+
console.log(chalk.green('✅ ClawVault is healthy!\n'));
|
|
901
|
+
} else {
|
|
902
|
+
console.log(chalk.yellow(`⚠ ${issues} issue(s) found\n`));
|
|
769
903
|
}
|
|
770
|
-
|
|
771
904
|
} catch (err) {
|
|
772
|
-
console.
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
console.log();
|
|
777
|
-
if (issues === 0) {
|
|
778
|
-
console.log(chalk.green('✅ ClawVault is healthy!\n'));
|
|
779
|
-
} else {
|
|
780
|
-
console.log(chalk.yellow(`⚠ ${issues} issue(s) found\n`));
|
|
905
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
906
|
+
process.exit(1);
|
|
781
907
|
}
|
|
782
908
|
});
|
|
783
909
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkDirtyDeath,
|
|
3
|
+
clearDirtyFlag
|
|
4
|
+
} from "./chunk-MZZJLQNQ.js";
|
|
5
|
+
import {
|
|
6
|
+
formatAge
|
|
7
|
+
} from "./chunk-7ZRP733D.js";
|
|
8
|
+
|
|
9
|
+
// src/commands/recover.ts
|
|
10
|
+
import * as fs from "fs";
|
|
11
|
+
import * as path from "path";
|
|
12
|
+
async function recover(vaultPath, options = {}) {
|
|
13
|
+
const { clearFlag = false } = options;
|
|
14
|
+
const { died, checkpoint, deathTime } = await checkDirtyDeath(vaultPath);
|
|
15
|
+
if (!died) {
|
|
16
|
+
return {
|
|
17
|
+
died: false,
|
|
18
|
+
deathTime: null,
|
|
19
|
+
checkpoint: null,
|
|
20
|
+
handoffPath: null,
|
|
21
|
+
handoffContent: null,
|
|
22
|
+
recoveryMessage: "No context death detected. Clean startup."
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const handoffsDir = path.join(vaultPath, "handoffs");
|
|
26
|
+
let handoffPath = null;
|
|
27
|
+
let handoffContent = null;
|
|
28
|
+
if (fs.existsSync(handoffsDir)) {
|
|
29
|
+
const files = fs.readdirSync(handoffsDir).filter((f) => f.startsWith("handoff-") && f.endsWith(".md")).sort().reverse();
|
|
30
|
+
if (files.length > 0) {
|
|
31
|
+
handoffPath = path.join(handoffsDir, files[0]);
|
|
32
|
+
handoffContent = fs.readFileSync(handoffPath, "utf-8");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
let message = "\u26A0\uFE0F **CONTEXT DEATH DETECTED**\n\n";
|
|
36
|
+
message += `Your previous session died at ${deathTime}.
|
|
37
|
+
|
|
38
|
+
`;
|
|
39
|
+
if (checkpoint) {
|
|
40
|
+
message += "**Last known state:**\n";
|
|
41
|
+
if (checkpoint.workingOn) {
|
|
42
|
+
message += `- Working on: ${checkpoint.workingOn}
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
if (checkpoint.focus) {
|
|
46
|
+
message += `- Focus: ${checkpoint.focus}
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
if (checkpoint.blocked) {
|
|
50
|
+
message += `- Blocked: ${checkpoint.blocked}
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
message += "\n";
|
|
54
|
+
}
|
|
55
|
+
if (handoffPath) {
|
|
56
|
+
message += `**Last handoff:** ${path.basename(handoffPath)}
|
|
57
|
+
`;
|
|
58
|
+
message += "Review and resume from where you left off.\n";
|
|
59
|
+
} else {
|
|
60
|
+
message += "**No handoff found.** You may have lost context.\n";
|
|
61
|
+
}
|
|
62
|
+
if (clearFlag) {
|
|
63
|
+
await clearDirtyFlag(vaultPath);
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
died: true,
|
|
67
|
+
deathTime,
|
|
68
|
+
checkpoint,
|
|
69
|
+
handoffPath,
|
|
70
|
+
handoffContent,
|
|
71
|
+
recoveryMessage: message
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function formatRecoveryInfo(info, options = {}) {
|
|
75
|
+
const { verbose = false } = options;
|
|
76
|
+
if (!info.died) {
|
|
77
|
+
return "\u2713 Clean startup - no context death detected.";
|
|
78
|
+
}
|
|
79
|
+
let output = "\n\u26A0\uFE0F CONTEXT DEATH DETECTED\n";
|
|
80
|
+
output += "\u2550".repeat(40) + "\n\n";
|
|
81
|
+
output += `Death time: ${info.deathTime}
|
|
82
|
+
`;
|
|
83
|
+
if (info.checkpoint?.timestamp) {
|
|
84
|
+
const age = formatAge(Date.now() - new Date(info.checkpoint.timestamp).getTime());
|
|
85
|
+
output += `Checkpoint: ${info.checkpoint.timestamp} (${age} ago)
|
|
86
|
+
`;
|
|
87
|
+
}
|
|
88
|
+
output += "\n";
|
|
89
|
+
if (info.checkpoint) {
|
|
90
|
+
output += "Last checkpoint:\n";
|
|
91
|
+
if (info.checkpoint.workingOn) {
|
|
92
|
+
output += ` \u2022 Working on: ${info.checkpoint.workingOn}
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
if (info.checkpoint.focus) {
|
|
96
|
+
output += ` \u2022 Focus: ${info.checkpoint.focus}
|
|
97
|
+
`;
|
|
98
|
+
}
|
|
99
|
+
if (info.checkpoint.blocked) {
|
|
100
|
+
output += ` \u2022 Blocked: ${info.checkpoint.blocked}
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
if (info.checkpoint.sessionKey || info.checkpoint.model || info.checkpoint.tokenEstimate !== void 0) {
|
|
104
|
+
output += " \u2022 Session:\n";
|
|
105
|
+
if (info.checkpoint.sessionKey) {
|
|
106
|
+
output += ` - Key: ${info.checkpoint.sessionKey}
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
if (info.checkpoint.model) {
|
|
110
|
+
output += ` - Model: ${info.checkpoint.model}
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
if (info.checkpoint.tokenEstimate !== void 0) {
|
|
114
|
+
output += ` - Token estimate: ${info.checkpoint.tokenEstimate}
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
output += "\n";
|
|
119
|
+
} else {
|
|
120
|
+
output += "No checkpoint data found.\n\n";
|
|
121
|
+
}
|
|
122
|
+
if (info.handoffPath) {
|
|
123
|
+
output += `Last handoff: ${path.basename(info.handoffPath)}
|
|
124
|
+
`;
|
|
125
|
+
} else {
|
|
126
|
+
output += "No handoff found - context may be lost.\n";
|
|
127
|
+
}
|
|
128
|
+
if (verbose) {
|
|
129
|
+
if (info.checkpoint) {
|
|
130
|
+
output += "\nCheckpoint JSON:\n";
|
|
131
|
+
output += JSON.stringify(info.checkpoint, null, 2) + "\n";
|
|
132
|
+
}
|
|
133
|
+
if (info.handoffContent) {
|
|
134
|
+
output += "\nHandoff content:\n";
|
|
135
|
+
output += info.handoffContent.trim() + "\n";
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
output += "\n" + "\u2550".repeat(40) + "\n";
|
|
139
|
+
output += "Run `clawvault recap` to see full context.\n";
|
|
140
|
+
return output;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export {
|
|
144
|
+
recover,
|
|
145
|
+
formatRecoveryInfo
|
|
146
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type DoctorStatus = 'ok' | 'warn' | 'error';
|
|
2
|
+
interface DoctorCheck {
|
|
3
|
+
label: string;
|
|
4
|
+
status: DoctorStatus;
|
|
5
|
+
detail?: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
}
|
|
8
|
+
interface DoctorReport {
|
|
9
|
+
vaultPath?: string;
|
|
10
|
+
checks: DoctorCheck[];
|
|
11
|
+
warnings: number;
|
|
12
|
+
errors: number;
|
|
13
|
+
}
|
|
14
|
+
declare function doctor(vaultPath?: string): Promise<DoctorReport>;
|
|
15
|
+
|
|
16
|
+
export { type DoctorCheck, type DoctorReport, type DoctorStatus, doctor };
|