clawvault 2.5.3 → 2.6.0
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 +159 -159
- package/bin/clawvault.js +111 -111
- package/bin/command-registration.test.js +166 -166
- package/bin/command-runtime.js +93 -93
- package/bin/command-runtime.test.js +154 -154
- package/bin/help-contract.test.js +39 -39
- package/bin/register-config-commands.js +153 -153
- package/bin/register-config-route-commands.test.js +121 -121
- package/bin/register-core-commands.js +237 -237
- package/bin/register-kanban-commands.js +56 -56
- package/bin/register-kanban-commands.test.js +83 -83
- package/bin/register-maintenance-commands.js +282 -282
- package/bin/register-project-commands.js +209 -209
- package/bin/register-project-commands.test.js +206 -206
- package/bin/register-query-commands.js +317 -317
- package/bin/register-query-commands.test.js +65 -65
- package/bin/register-resilience-commands.js +182 -182
- package/bin/register-resilience-commands.test.js +81 -81
- package/bin/register-route-commands.js +114 -114
- package/bin/register-session-lifecycle-commands.js +206 -206
- package/bin/register-tailscale-commands.js +106 -106
- package/bin/register-task-commands.js +348 -348
- package/bin/register-task-commands.test.js +69 -69
- package/bin/register-template-commands.js +75 -72
- package/bin/register-template-commands.test.js +87 -0
- package/bin/register-vault-operations-commands.js +300 -300
- package/bin/test-helpers/cli-command-fixtures.js +119 -119
- package/dashboard/lib/graph-diff.js +104 -104
- package/dashboard/lib/graph-diff.test.js +75 -75
- package/dashboard/lib/vault-parser.js +556 -556
- package/dashboard/lib/vault-parser.test.js +254 -254
- package/dashboard/public/app.js +796 -796
- package/dashboard/public/index.html +52 -52
- package/dashboard/public/styles.css +221 -221
- package/dashboard/server.js +374 -374
- package/dist/{chunk-J5EMBUPK.js → chunk-4OXMU5S2.js} +1 -1
- package/dist/{chunk-3FP5BJ42.js → chunk-4QYGFWRM.js} +1 -1
- package/dist/{chunk-4IV3R2F5.js → chunk-4TE4JMLA.js} +1 -1
- package/dist/{chunk-5GZFTAL7.js → chunk-AZYOKJYC.js} +128 -42
- package/dist/{chunk-FG6RJMCN.js → chunk-HA5M6KJB.js} +4 -4
- package/dist/{chunk-IZEY5S74.js → chunk-IEVLHNLU.js} +1 -1
- package/dist/{chunk-CLE2HHNT.js → chunk-IVRIKYFE.js} +18 -11
- package/dist/{chunk-AY4PGUVL.js → chunk-KL4NAOMO.js} +1 -1
- package/dist/{chunk-O7XHXF7F.js → chunk-MAKNAHAW.js} +4 -4
- package/dist/{chunk-OSMS7QIG.js → chunk-ME37YNW3.js} +2 -2
- package/dist/chunk-MFAWT5O5.js +301 -0
- package/dist/{chunk-TPDH3JPP.js → chunk-PBEE567J.js} +1 -1
- package/dist/{chunk-S2IG7VNM.js → chunk-Q2J5YTUF.js} +2 -2
- package/dist/{chunk-IOALNTAN.js → chunk-QWQ3TIKS.js} +103 -29
- package/dist/{chunk-YCVDVI5B.js → chunk-R2MIW5G7.js} +1 -1
- package/dist/{chunk-M25QVSJM.js → chunk-RVYA52PY.js} +1 -1
- package/dist/{chunk-NZ4ZZNSR.js → chunk-THRJVD4L.js} +1 -1
- package/dist/{chunk-4GBPTBFJ.js → chunk-TIGW564L.js} +1 -1
- package/dist/{chunk-LMEMZGUV.js → chunk-UEOUADMO.js} +3 -3
- package/dist/{chunk-GFJ3LIIB.js → chunk-XAVB4GB4.js} +1 -1
- package/dist/cli/index.js +15 -13
- package/dist/commands/backlog.js +3 -1
- package/dist/commands/blocked.js +3 -1
- package/dist/commands/canvas.js +3 -1
- package/dist/commands/context.js +3 -3
- package/dist/commands/doctor.js +9 -7
- package/dist/commands/embed.js +2 -2
- package/dist/commands/kanban.js +4 -2
- package/dist/commands/observe.js +7 -5
- package/dist/commands/project.js +5 -3
- package/dist/commands/rebuild.js +6 -4
- package/dist/commands/replay.js +6 -4
- package/dist/commands/setup.js +2 -2
- package/dist/commands/sleep.js +7 -5
- package/dist/commands/status.js +8 -6
- package/dist/commands/tailscale.js +3 -3
- package/dist/commands/task.js +4 -2
- package/dist/commands/template.d.ts +10 -1
- package/dist/commands/template.js +47 -55
- package/dist/commands/wake.js +2 -2
- package/dist/index.js +23 -22
- package/dist/lib/project-utils.js +4 -2
- package/dist/lib/tailscale.js +2 -2
- package/dist/lib/task-utils.d.ts +14 -13
- package/dist/lib/task-utils.js +3 -1
- package/dist/lib/template-engine.d.ts +1 -0
- package/dist/lib/webdav.js +1 -1
- package/hooks/clawvault/HOOK.md +83 -83
- package/hooks/clawvault/handler.js +816 -816
- package/hooks/clawvault/handler.test.js +263 -263
- package/package.json +94 -94
- package/templates/checkpoint.md +34 -19
- package/templates/daily-note.md +34 -19
- package/templates/daily.md +34 -19
- package/templates/decision.md +39 -17
- package/templates/handoff.md +34 -19
- package/templates/lesson.md +31 -16
- package/templates/person.md +37 -19
- package/templates/project.md +84 -23
- package/templates/task.md +81 -0
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route command registrations for custom observation entity routing.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
function printRoutesTable(routes) {
|
|
6
|
-
if (routes.length === 0) {
|
|
7
|
-
console.log('No custom routes configured.');
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const priorityWidth = Math.max(
|
|
12
|
-
'PRIORITY'.length,
|
|
13
|
-
...routes.map((route) => String(route.priority).length)
|
|
14
|
-
);
|
|
15
|
-
const patternWidth = Math.max(
|
|
16
|
-
'PATTERN'.length,
|
|
17
|
-
...routes.map((route) => route.pattern.length)
|
|
18
|
-
);
|
|
19
|
-
const targetWidth = Math.max(
|
|
20
|
-
'TARGET'.length,
|
|
21
|
-
...routes.map((route) => route.target.length)
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const header = `${'PRIORITY'.padEnd(priorityWidth)} ${'PATTERN'.padEnd(patternWidth)} ${'TARGET'.padEnd(targetWidth)}`;
|
|
25
|
-
const divider = `${'-'.repeat(priorityWidth)} ${'-'.repeat(patternWidth)} ${'-'.repeat(targetWidth)}`;
|
|
26
|
-
|
|
27
|
-
console.log(header);
|
|
28
|
-
console.log(divider);
|
|
29
|
-
for (const route of routes) {
|
|
30
|
-
console.log(
|
|
31
|
-
`${String(route.priority).padEnd(priorityWidth)} ${route.pattern.padEnd(patternWidth)} ${route.target}`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function registerRouteCommands(program, { chalk, resolveVaultPath }) {
|
|
37
|
-
const route = program
|
|
38
|
-
.command('route')
|
|
39
|
-
.description('Manage custom observation routing rules');
|
|
40
|
-
|
|
41
|
-
route
|
|
42
|
-
.command('list')
|
|
43
|
-
.description('List custom routing rules')
|
|
44
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
45
|
-
.action(async (options) => {
|
|
46
|
-
try {
|
|
47
|
-
const { listRouteRules } = await import('../dist/index.js');
|
|
48
|
-
const rules = listRouteRules(resolveVaultPath(options.vault));
|
|
49
|
-
printRoutesTable(rules);
|
|
50
|
-
} catch (err) {
|
|
51
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
route
|
|
57
|
-
.command('add <pattern> <target>')
|
|
58
|
-
.description('Add a custom routing rule')
|
|
59
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
60
|
-
.action(async (pattern, target, options) => {
|
|
61
|
-
try {
|
|
62
|
-
const { addRouteRule } = await import('../dist/index.js');
|
|
63
|
-
const rule = addRouteRule(resolveVaultPath(options.vault), pattern, target);
|
|
64
|
-
console.log(chalk.green('✓ Route added'));
|
|
65
|
-
console.log(chalk.dim(` Pattern: ${rule.pattern}`));
|
|
66
|
-
console.log(chalk.dim(` Target: ${rule.target}`));
|
|
67
|
-
console.log(chalk.dim(` Priority: ${rule.priority}`));
|
|
68
|
-
} catch (err) {
|
|
69
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
route
|
|
75
|
-
.command('remove <pattern>')
|
|
76
|
-
.description('Remove a custom routing rule by pattern')
|
|
77
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
78
|
-
.action(async (pattern, options) => {
|
|
79
|
-
try {
|
|
80
|
-
const { removeRouteRule } = await import('../dist/index.js');
|
|
81
|
-
const removed = removeRouteRule(resolveVaultPath(options.vault), pattern);
|
|
82
|
-
if (!removed) {
|
|
83
|
-
console.log(chalk.yellow(`No route found for pattern: ${pattern}`));
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
console.log(chalk.green(`✓ Removed route: ${pattern}`));
|
|
87
|
-
} catch (err) {
|
|
88
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
route
|
|
94
|
-
.command('test <text>')
|
|
95
|
-
.description('Test custom routes against text')
|
|
96
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
97
|
-
.action(async (text, options) => {
|
|
98
|
-
try {
|
|
99
|
-
const { testRouteRule } = await import('../dist/index.js');
|
|
100
|
-
const match = testRouteRule(resolveVaultPath(options.vault), text);
|
|
101
|
-
if (!match) {
|
|
102
|
-
console.log('No route matched.');
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
console.log(chalk.green('✓ Route matched'));
|
|
106
|
-
console.log(chalk.dim(` Pattern: ${match.pattern}`));
|
|
107
|
-
console.log(chalk.dim(` Target: ${match.target}`));
|
|
108
|
-
console.log(chalk.dim(` Priority: ${match.priority}`));
|
|
109
|
-
} catch (err) {
|
|
110
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Route command registrations for custom observation entity routing.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
function printRoutesTable(routes) {
|
|
6
|
+
if (routes.length === 0) {
|
|
7
|
+
console.log('No custom routes configured.');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const priorityWidth = Math.max(
|
|
12
|
+
'PRIORITY'.length,
|
|
13
|
+
...routes.map((route) => String(route.priority).length)
|
|
14
|
+
);
|
|
15
|
+
const patternWidth = Math.max(
|
|
16
|
+
'PATTERN'.length,
|
|
17
|
+
...routes.map((route) => route.pattern.length)
|
|
18
|
+
);
|
|
19
|
+
const targetWidth = Math.max(
|
|
20
|
+
'TARGET'.length,
|
|
21
|
+
...routes.map((route) => route.target.length)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const header = `${'PRIORITY'.padEnd(priorityWidth)} ${'PATTERN'.padEnd(patternWidth)} ${'TARGET'.padEnd(targetWidth)}`;
|
|
25
|
+
const divider = `${'-'.repeat(priorityWidth)} ${'-'.repeat(patternWidth)} ${'-'.repeat(targetWidth)}`;
|
|
26
|
+
|
|
27
|
+
console.log(header);
|
|
28
|
+
console.log(divider);
|
|
29
|
+
for (const route of routes) {
|
|
30
|
+
console.log(
|
|
31
|
+
`${String(route.priority).padEnd(priorityWidth)} ${route.pattern.padEnd(patternWidth)} ${route.target}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function registerRouteCommands(program, { chalk, resolveVaultPath }) {
|
|
37
|
+
const route = program
|
|
38
|
+
.command('route')
|
|
39
|
+
.description('Manage custom observation routing rules');
|
|
40
|
+
|
|
41
|
+
route
|
|
42
|
+
.command('list')
|
|
43
|
+
.description('List custom routing rules')
|
|
44
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
45
|
+
.action(async (options) => {
|
|
46
|
+
try {
|
|
47
|
+
const { listRouteRules } = await import('../dist/index.js');
|
|
48
|
+
const rules = listRouteRules(resolveVaultPath(options.vault));
|
|
49
|
+
printRoutesTable(rules);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
route
|
|
57
|
+
.command('add <pattern> <target>')
|
|
58
|
+
.description('Add a custom routing rule')
|
|
59
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
60
|
+
.action(async (pattern, target, options) => {
|
|
61
|
+
try {
|
|
62
|
+
const { addRouteRule } = await import('../dist/index.js');
|
|
63
|
+
const rule = addRouteRule(resolveVaultPath(options.vault), pattern, target);
|
|
64
|
+
console.log(chalk.green('✓ Route added'));
|
|
65
|
+
console.log(chalk.dim(` Pattern: ${rule.pattern}`));
|
|
66
|
+
console.log(chalk.dim(` Target: ${rule.target}`));
|
|
67
|
+
console.log(chalk.dim(` Priority: ${rule.priority}`));
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
route
|
|
75
|
+
.command('remove <pattern>')
|
|
76
|
+
.description('Remove a custom routing rule by pattern')
|
|
77
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
78
|
+
.action(async (pattern, options) => {
|
|
79
|
+
try {
|
|
80
|
+
const { removeRouteRule } = await import('../dist/index.js');
|
|
81
|
+
const removed = removeRouteRule(resolveVaultPath(options.vault), pattern);
|
|
82
|
+
if (!removed) {
|
|
83
|
+
console.log(chalk.yellow(`No route found for pattern: ${pattern}`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
console.log(chalk.green(`✓ Removed route: ${pattern}`));
|
|
87
|
+
} catch (err) {
|
|
88
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
route
|
|
94
|
+
.command('test <text>')
|
|
95
|
+
.description('Test custom routes against text')
|
|
96
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
97
|
+
.action(async (text, options) => {
|
|
98
|
+
try {
|
|
99
|
+
const { testRouteRule } = await import('../dist/index.js');
|
|
100
|
+
const match = testRouteRule(resolveVaultPath(options.vault), text);
|
|
101
|
+
if (!match) {
|
|
102
|
+
console.log('No route matched.');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log(chalk.green('✓ Route matched'));
|
|
106
|
+
console.log(chalk.dim(` Pattern: ${match.pattern}`));
|
|
107
|
+
console.log(chalk.dim(` Target: ${match.target}`));
|
|
108
|
+
console.log(chalk.dim(` Priority: ${match.priority}`));
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -1,206 +1,206 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session lifecycle command registrations (wake/sleep/handoff/recap).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export function registerSessionLifecycleCommands(
|
|
6
|
-
program,
|
|
7
|
-
{ chalk, resolveVaultPath, QmdUnavailableError, printQmdMissing, getVault, runQmd }
|
|
8
|
-
) {
|
|
9
|
-
// === WAKE (session start) ===
|
|
10
|
-
program
|
|
11
|
-
.command('wake')
|
|
12
|
-
.description('Start a session (recover + recap + summary)')
|
|
13
|
-
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include (default: 3)', '3')
|
|
14
|
-
.option('--full', 'Show full recap (default: brief)')
|
|
15
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
16
|
-
.action(async (options) => {
|
|
17
|
-
try {
|
|
18
|
-
const vaultPath = resolveVaultPath(options.vault);
|
|
19
|
-
const { wake } = await import('../dist/commands/wake.js');
|
|
20
|
-
const { formatRecoveryInfo } = await import('../dist/commands/recover.js');
|
|
21
|
-
const result = await wake({
|
|
22
|
-
vaultPath,
|
|
23
|
-
handoffLimit: parseInt(options.handoffLimit, 10),
|
|
24
|
-
brief: !options.full
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
console.log(chalk.cyan('\n🌅 ClawVault Wake\n'));
|
|
28
|
-
console.log(formatRecoveryInfo(result.recovery));
|
|
29
|
-
console.log();
|
|
30
|
-
console.log(chalk.cyan('Recap'));
|
|
31
|
-
console.log(result.recapMarkdown.trim());
|
|
32
|
-
console.log();
|
|
33
|
-
console.log(chalk.green(`You were working on: ${result.summary}`));
|
|
34
|
-
|
|
35
|
-
process.exitCode = result.recovery.died ? 1 : 0;
|
|
36
|
-
} catch (err) {
|
|
37
|
-
if (err instanceof QmdUnavailableError) {
|
|
38
|
-
printQmdMissing();
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// === SLEEP (session end) ===
|
|
47
|
-
program
|
|
48
|
-
.command('sleep <summary>')
|
|
49
|
-
.description('End a session with a handoff (and optional git commit)')
|
|
50
|
-
.option('-n, --next <items>', 'Next steps (comma-separated)')
|
|
51
|
-
.option('-b, --blocked <items>', 'Blocked items (comma-separated)')
|
|
52
|
-
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
53
|
-
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
54
|
-
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
55
|
-
.option('-s, --session <key>', 'Session key')
|
|
56
|
-
.option('--session-transcript <path>', 'Session transcript path for auto-observe')
|
|
57
|
-
.option('--reflect', 'Run weekly reflection pass after sleep handoff')
|
|
58
|
-
.option('--index', 'Update qmd index after handoff (default: disabled)')
|
|
59
|
-
.option('--no-git', 'Skip git commit prompt')
|
|
60
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
61
|
-
.action(async (summary, options) => {
|
|
62
|
-
try {
|
|
63
|
-
const vaultPath = resolveVaultPath(options.vault);
|
|
64
|
-
const { sleep } = await import('../dist/commands/sleep.js');
|
|
65
|
-
const result = await sleep({
|
|
66
|
-
workingOn: summary,
|
|
67
|
-
next: options.next,
|
|
68
|
-
blocked: options.blocked,
|
|
69
|
-
decisions: options.decisions,
|
|
70
|
-
questions: options.questions,
|
|
71
|
-
feeling: options.feeling,
|
|
72
|
-
sessionKey: options.session,
|
|
73
|
-
sessionTranscript: options.sessionTranscript,
|
|
74
|
-
reflect: options.reflect,
|
|
75
|
-
vaultPath,
|
|
76
|
-
index: options.index,
|
|
77
|
-
git: options.git
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
console.log(chalk.green(`✓ Handoff saved: ${result.document.id}`));
|
|
81
|
-
console.log(chalk.dim(` Path: ${result.document.path}`));
|
|
82
|
-
console.log(chalk.dim(` Working on: ${result.handoff.workingOn.join(', ')}`));
|
|
83
|
-
if (result.handoff.nextSteps.length > 0) {
|
|
84
|
-
console.log(chalk.dim(` Next: ${result.handoff.nextSteps.join(', ')}`));
|
|
85
|
-
} else {
|
|
86
|
-
console.log(chalk.dim(' Next: (none)'));
|
|
87
|
-
}
|
|
88
|
-
if (result.handoff.blocked.length > 0) {
|
|
89
|
-
console.log(chalk.dim(` Blocked: ${result.handoff.blocked.join(', ')}`));
|
|
90
|
-
} else {
|
|
91
|
-
console.log(chalk.dim(' Blocked: (none)'));
|
|
92
|
-
}
|
|
93
|
-
if (result.handoff.decisions?.length) {
|
|
94
|
-
console.log(chalk.dim(` Decisions: ${result.handoff.decisions.join(', ')}`));
|
|
95
|
-
}
|
|
96
|
-
if (result.handoff.openQuestions?.length) {
|
|
97
|
-
console.log(chalk.dim(` Questions: ${result.handoff.openQuestions.join(', ')}`));
|
|
98
|
-
}
|
|
99
|
-
if (result.handoff.feeling) {
|
|
100
|
-
console.log(chalk.dim(` Feeling: ${result.handoff.feeling}`));
|
|
101
|
-
}
|
|
102
|
-
if (options.index) {
|
|
103
|
-
console.log(chalk.dim(' qmd: index updated'));
|
|
104
|
-
}
|
|
105
|
-
if (result.git) {
|
|
106
|
-
if (result.git.committed) {
|
|
107
|
-
console.log(chalk.green(`✓ Git commit created${result.git.message ? `: ${result.git.message}` : ''}`));
|
|
108
|
-
} else if (result.git.skippedReason === 'clean') {
|
|
109
|
-
console.log(chalk.dim(' Git: clean'));
|
|
110
|
-
} else if (result.git.skippedReason === 'declined') {
|
|
111
|
-
console.log(chalk.dim(' Git: commit skipped'));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (result.observationRoutingSummary) {
|
|
115
|
-
console.log(chalk.dim(` Observe: ${result.observationRoutingSummary}`));
|
|
116
|
-
}
|
|
117
|
-
} catch (err) {
|
|
118
|
-
if (err instanceof QmdUnavailableError) {
|
|
119
|
-
printQmdMissing();
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// === HANDOFF (session bridge) ===
|
|
128
|
-
program
|
|
129
|
-
.command('handoff')
|
|
130
|
-
.description('Create a session handoff document')
|
|
131
|
-
.requiredOption('-w, --working-on <items>', 'What I was working on (comma-separated)')
|
|
132
|
-
.option('-b, --blocked <items>', 'What is blocked (comma-separated)')
|
|
133
|
-
.option('-n, --next <items>', 'What comes next (comma-separated)')
|
|
134
|
-
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
135
|
-
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
136
|
-
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
137
|
-
.option('-s, --session <key>', 'Session key')
|
|
138
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
139
|
-
.option('--no-index', 'Skip qmd index update (auto-updates by default)')
|
|
140
|
-
.option('--json', 'Output as JSON')
|
|
141
|
-
.action(async (options) => {
|
|
142
|
-
try {
|
|
143
|
-
const vault = await getVault(options.vault);
|
|
144
|
-
|
|
145
|
-
const handoff = {
|
|
146
|
-
workingOn: options.workingOn.split(',').map((item) => item.trim()),
|
|
147
|
-
blocked: options.blocked ? options.blocked.split(',').map((item) => item.trim()) : [],
|
|
148
|
-
nextSteps: options.next ? options.next.split(',').map((item) => item.trim()) : [],
|
|
149
|
-
decisions: options.decisions ? options.decisions.split(',').map((item) => item.trim()) : undefined,
|
|
150
|
-
openQuestions: options.questions ? options.questions.split(',').map((item) => item.trim()) : undefined,
|
|
151
|
-
feeling: options.feeling,
|
|
152
|
-
sessionKey: options.session
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const doc = await vault.createHandoff(handoff);
|
|
156
|
-
|
|
157
|
-
if (!options.json) {
|
|
158
|
-
console.log(chalk.green(`✓ Handoff created: ${doc.id}`));
|
|
159
|
-
console.log(chalk.dim(` Path: ${doc.path}`));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (options.index !== false) {
|
|
163
|
-
const collection = vault.getQmdCollection();
|
|
164
|
-
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (options.json) {
|
|
168
|
-
console.log(JSON.stringify({ id: doc.id, path: doc.path, handoff }, null, 2));
|
|
169
|
-
}
|
|
170
|
-
} catch (err) {
|
|
171
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
172
|
-
process.exit(1);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// === RECAP (session bootstrap) ===
|
|
177
|
-
program
|
|
178
|
-
.command('recap')
|
|
179
|
-
.description('Generate a session recap - who I was (bootstrap hook)')
|
|
180
|
-
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include (default: 3)', '3')
|
|
181
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
182
|
-
.option('--json', 'Output as JSON')
|
|
183
|
-
.option('--markdown', 'Output as markdown (default)')
|
|
184
|
-
.option('--brief', 'Minimal output for token savings')
|
|
185
|
-
.action(async (options) => {
|
|
186
|
-
try {
|
|
187
|
-
const vault = await getVault(options.vault);
|
|
188
|
-
|
|
189
|
-
const recap = await vault.generateRecap({
|
|
190
|
-
handoffLimit: parseInt(options.handoffLimit, 10),
|
|
191
|
-
brief: options.brief
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
if (options.json) {
|
|
195
|
-
console.log(JSON.stringify(recap, null, 2));
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const markdown = vault.formatRecap(recap, { brief: options.brief });
|
|
200
|
-
console.log(markdown);
|
|
201
|
-
} catch (err) {
|
|
202
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Session lifecycle command registrations (wake/sleep/handoff/recap).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function registerSessionLifecycleCommands(
|
|
6
|
+
program,
|
|
7
|
+
{ chalk, resolveVaultPath, QmdUnavailableError, printQmdMissing, getVault, runQmd }
|
|
8
|
+
) {
|
|
9
|
+
// === WAKE (session start) ===
|
|
10
|
+
program
|
|
11
|
+
.command('wake')
|
|
12
|
+
.description('Start a session (recover + recap + summary)')
|
|
13
|
+
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include (default: 3)', '3')
|
|
14
|
+
.option('--full', 'Show full recap (default: brief)')
|
|
15
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
16
|
+
.action(async (options) => {
|
|
17
|
+
try {
|
|
18
|
+
const vaultPath = resolveVaultPath(options.vault);
|
|
19
|
+
const { wake } = await import('../dist/commands/wake.js');
|
|
20
|
+
const { formatRecoveryInfo } = await import('../dist/commands/recover.js');
|
|
21
|
+
const result = await wake({
|
|
22
|
+
vaultPath,
|
|
23
|
+
handoffLimit: parseInt(options.handoffLimit, 10),
|
|
24
|
+
brief: !options.full
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log(chalk.cyan('\n🌅 ClawVault Wake\n'));
|
|
28
|
+
console.log(formatRecoveryInfo(result.recovery));
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(chalk.cyan('Recap'));
|
|
31
|
+
console.log(result.recapMarkdown.trim());
|
|
32
|
+
console.log();
|
|
33
|
+
console.log(chalk.green(`You were working on: ${result.summary}`));
|
|
34
|
+
|
|
35
|
+
process.exitCode = result.recovery.died ? 1 : 0;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err instanceof QmdUnavailableError) {
|
|
38
|
+
printQmdMissing();
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// === SLEEP (session end) ===
|
|
47
|
+
program
|
|
48
|
+
.command('sleep <summary>')
|
|
49
|
+
.description('End a session with a handoff (and optional git commit)')
|
|
50
|
+
.option('-n, --next <items>', 'Next steps (comma-separated)')
|
|
51
|
+
.option('-b, --blocked <items>', 'Blocked items (comma-separated)')
|
|
52
|
+
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
53
|
+
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
54
|
+
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
55
|
+
.option('-s, --session <key>', 'Session key')
|
|
56
|
+
.option('--session-transcript <path>', 'Session transcript path for auto-observe')
|
|
57
|
+
.option('--reflect', 'Run weekly reflection pass after sleep handoff')
|
|
58
|
+
.option('--index', 'Update qmd index after handoff (default: disabled)')
|
|
59
|
+
.option('--no-git', 'Skip git commit prompt')
|
|
60
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
61
|
+
.action(async (summary, options) => {
|
|
62
|
+
try {
|
|
63
|
+
const vaultPath = resolveVaultPath(options.vault);
|
|
64
|
+
const { sleep } = await import('../dist/commands/sleep.js');
|
|
65
|
+
const result = await sleep({
|
|
66
|
+
workingOn: summary,
|
|
67
|
+
next: options.next,
|
|
68
|
+
blocked: options.blocked,
|
|
69
|
+
decisions: options.decisions,
|
|
70
|
+
questions: options.questions,
|
|
71
|
+
feeling: options.feeling,
|
|
72
|
+
sessionKey: options.session,
|
|
73
|
+
sessionTranscript: options.sessionTranscript,
|
|
74
|
+
reflect: options.reflect,
|
|
75
|
+
vaultPath,
|
|
76
|
+
index: options.index,
|
|
77
|
+
git: options.git
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
console.log(chalk.green(`✓ Handoff saved: ${result.document.id}`));
|
|
81
|
+
console.log(chalk.dim(` Path: ${result.document.path}`));
|
|
82
|
+
console.log(chalk.dim(` Working on: ${result.handoff.workingOn.join(', ')}`));
|
|
83
|
+
if (result.handoff.nextSteps.length > 0) {
|
|
84
|
+
console.log(chalk.dim(` Next: ${result.handoff.nextSteps.join(', ')}`));
|
|
85
|
+
} else {
|
|
86
|
+
console.log(chalk.dim(' Next: (none)'));
|
|
87
|
+
}
|
|
88
|
+
if (result.handoff.blocked.length > 0) {
|
|
89
|
+
console.log(chalk.dim(` Blocked: ${result.handoff.blocked.join(', ')}`));
|
|
90
|
+
} else {
|
|
91
|
+
console.log(chalk.dim(' Blocked: (none)'));
|
|
92
|
+
}
|
|
93
|
+
if (result.handoff.decisions?.length) {
|
|
94
|
+
console.log(chalk.dim(` Decisions: ${result.handoff.decisions.join(', ')}`));
|
|
95
|
+
}
|
|
96
|
+
if (result.handoff.openQuestions?.length) {
|
|
97
|
+
console.log(chalk.dim(` Questions: ${result.handoff.openQuestions.join(', ')}`));
|
|
98
|
+
}
|
|
99
|
+
if (result.handoff.feeling) {
|
|
100
|
+
console.log(chalk.dim(` Feeling: ${result.handoff.feeling}`));
|
|
101
|
+
}
|
|
102
|
+
if (options.index) {
|
|
103
|
+
console.log(chalk.dim(' qmd: index updated'));
|
|
104
|
+
}
|
|
105
|
+
if (result.git) {
|
|
106
|
+
if (result.git.committed) {
|
|
107
|
+
console.log(chalk.green(`✓ Git commit created${result.git.message ? `: ${result.git.message}` : ''}`));
|
|
108
|
+
} else if (result.git.skippedReason === 'clean') {
|
|
109
|
+
console.log(chalk.dim(' Git: clean'));
|
|
110
|
+
} else if (result.git.skippedReason === 'declined') {
|
|
111
|
+
console.log(chalk.dim(' Git: commit skipped'));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (result.observationRoutingSummary) {
|
|
115
|
+
console.log(chalk.dim(` Observe: ${result.observationRoutingSummary}`));
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (err instanceof QmdUnavailableError) {
|
|
119
|
+
printQmdMissing();
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// === HANDOFF (session bridge) ===
|
|
128
|
+
program
|
|
129
|
+
.command('handoff')
|
|
130
|
+
.description('Create a session handoff document')
|
|
131
|
+
.requiredOption('-w, --working-on <items>', 'What I was working on (comma-separated)')
|
|
132
|
+
.option('-b, --blocked <items>', 'What is blocked (comma-separated)')
|
|
133
|
+
.option('-n, --next <items>', 'What comes next (comma-separated)')
|
|
134
|
+
.option('-d, --decisions <items>', 'Key decisions made (comma-separated)')
|
|
135
|
+
.option('-q, --questions <items>', 'Open questions (comma-separated)')
|
|
136
|
+
.option('-f, --feeling <state>', 'Emotional/energy state')
|
|
137
|
+
.option('-s, --session <key>', 'Session key')
|
|
138
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
139
|
+
.option('--no-index', 'Skip qmd index update (auto-updates by default)')
|
|
140
|
+
.option('--json', 'Output as JSON')
|
|
141
|
+
.action(async (options) => {
|
|
142
|
+
try {
|
|
143
|
+
const vault = await getVault(options.vault);
|
|
144
|
+
|
|
145
|
+
const handoff = {
|
|
146
|
+
workingOn: options.workingOn.split(',').map((item) => item.trim()),
|
|
147
|
+
blocked: options.blocked ? options.blocked.split(',').map((item) => item.trim()) : [],
|
|
148
|
+
nextSteps: options.next ? options.next.split(',').map((item) => item.trim()) : [],
|
|
149
|
+
decisions: options.decisions ? options.decisions.split(',').map((item) => item.trim()) : undefined,
|
|
150
|
+
openQuestions: options.questions ? options.questions.split(',').map((item) => item.trim()) : undefined,
|
|
151
|
+
feeling: options.feeling,
|
|
152
|
+
sessionKey: options.session
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const doc = await vault.createHandoff(handoff);
|
|
156
|
+
|
|
157
|
+
if (!options.json) {
|
|
158
|
+
console.log(chalk.green(`✓ Handoff created: ${doc.id}`));
|
|
159
|
+
console.log(chalk.dim(` Path: ${doc.path}`));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (options.index !== false) {
|
|
163
|
+
const collection = vault.getQmdCollection();
|
|
164
|
+
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (options.json) {
|
|
168
|
+
console.log(JSON.stringify({ id: doc.id, path: doc.path, handoff }, null, 2));
|
|
169
|
+
}
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// === RECAP (session bootstrap) ===
|
|
177
|
+
program
|
|
178
|
+
.command('recap')
|
|
179
|
+
.description('Generate a session recap - who I was (bootstrap hook)')
|
|
180
|
+
.option('-n, --handoff-limit <n>', 'Number of recent handoffs to include (default: 3)', '3')
|
|
181
|
+
.option('-v, --vault <path>', 'Vault path')
|
|
182
|
+
.option('--json', 'Output as JSON')
|
|
183
|
+
.option('--markdown', 'Output as markdown (default)')
|
|
184
|
+
.option('--brief', 'Minimal output for token savings')
|
|
185
|
+
.action(async (options) => {
|
|
186
|
+
try {
|
|
187
|
+
const vault = await getVault(options.vault);
|
|
188
|
+
|
|
189
|
+
const recap = await vault.generateRecap({
|
|
190
|
+
handoffLimit: parseInt(options.handoffLimit, 10),
|
|
191
|
+
brief: options.brief
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
if (options.json) {
|
|
195
|
+
console.log(JSON.stringify(recap, null, 2));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const markdown = vault.formatRecap(recap, { brief: options.brief });
|
|
200
|
+
console.log(markdown);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
console.error(chalk.red(`Error: ${err.message}`));
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|