clawvault 2.4.5 → 2.4.7
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/bin/clawvault.js +10 -0
- package/bin/command-registration.test.js +1 -1
- package/bin/help-contract.test.js +1 -0
- package/bin/register-config-route-commands.test.js +8 -1
- package/bin/register-core-commands.js +3 -3
- package/bin/register-kanban-commands.js +56 -0
- package/bin/register-kanban-commands.test.js +83 -0
- package/bin/register-project-commands.js +209 -0
- package/bin/register-project-commands.test.js +201 -0
- package/bin/register-query-commands.js +40 -0
- package/bin/register-task-commands.js +60 -25
- package/bin/register-task-commands.test.js +49 -4
- package/bin/test-helpers/cli-command-fixtures.js +15 -0
- package/dist/{chunk-3PJIGGWV.js → chunk-2CDEETQN.js} +1 -0
- package/dist/{chunk-B3SMJZIZ.js → chunk-2RK2AG32.js} +5 -5
- package/dist/chunk-5GZFTAL7.js +340 -0
- package/dist/{chunk-YIRWDQKA.js → chunk-6RQPD7X6.js} +3 -4
- package/dist/{chunk-HNMFXFYP.js → chunk-7OHQFMJK.js} +2 -1
- package/dist/{chunk-4JJL47IJ.js → chunk-C3PF7WBA.js} +2 -2
- package/dist/{chunk-JXY6T5R7.js → chunk-FW465EEA.js} +1 -1
- package/dist/{chunk-BI6SGGZP.js → chunk-G3OQJ2NQ.js} +1 -1
- package/dist/chunk-GSD4ALSI.js +724 -0
- package/dist/chunk-IOALNTAN.js +757 -0
- package/dist/chunk-ITPEXLHA.js +528 -0
- package/dist/chunk-J5EMBUPK.js +399 -0
- package/dist/chunk-K3CDT7IH.js +122 -0
- package/dist/{chunk-AHGUJG76.js → chunk-KCCHROBR.js} +13 -69
- package/dist/{chunk-U2ONVV7N.js → chunk-LMCC5OC7.js} +2 -2
- package/dist/{chunk-QALB2V3E.js → chunk-MQUJNOHK.js} +1 -1
- package/dist/{chunk-RXEIQ3KQ.js → chunk-TMZMN7OS.js} +334 -457
- package/dist/{chunk-HVTTYDCJ.js → chunk-VR5NE7PZ.js} +1 -1
- package/dist/{chunk-22WE3J4F.js → chunk-WIICLBNF.js} +35 -4
- package/dist/chunk-YCVDVI5B.js +273 -0
- package/dist/{chunk-NAMFB7ZA.js → chunk-Z2XBWN7A.js} +0 -2
- package/dist/commands/archive.js +3 -3
- package/dist/commands/backlog.js +1 -1
- package/dist/commands/blocked.js +1 -1
- package/dist/commands/canvas.d.ts +1 -14
- package/dist/commands/canvas.js +123 -1543
- package/dist/commands/context.js +5 -6
- package/dist/commands/doctor.js +5 -5
- package/dist/commands/inject.d.ts +2 -0
- package/dist/commands/inject.js +14 -0
- package/dist/commands/kanban.d.ts +63 -0
- package/dist/commands/kanban.js +21 -0
- package/dist/commands/migrate-observations.js +2 -2
- package/dist/commands/observe.js +8 -6
- package/dist/commands/project.d.ts +85 -0
- package/dist/commands/project.js +411 -0
- package/dist/commands/rebuild.js +7 -5
- package/dist/commands/reflect.js +5 -4
- package/dist/commands/replay.js +10 -7
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +2 -2
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +11 -8
- package/dist/commands/status.js +5 -5
- package/dist/commands/task.d.ts +20 -8
- package/dist/commands/task.js +11 -244
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +4 -4
- package/dist/index.d.ts +76 -106
- package/dist/index.js +99 -34
- package/dist/inject-x65KXWPk.d.ts +137 -0
- package/dist/lib/project-utils.d.ts +97 -0
- package/dist/lib/project-utils.js +19 -0
- package/dist/lib/task-utils.d.ts +48 -12
- package/dist/lib/task-utils.js +5 -1
- package/dist/{types-DMU3SuAV.d.ts → types-jjuYN2Xn.d.ts} +1 -1
- package/package.json +2 -2
- package/dist/chunk-DEFBIVQ3.js +0 -373
- package/dist/chunk-L3DJ36BZ.js +0 -40
- package/dist/chunk-UMMCYTJV.js +0 -105
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
* Registers task, backlog, blocked, and canvas commands
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
function parseCsvList(value) {
|
|
7
|
+
if (!value) return undefined;
|
|
8
|
+
const items = String(value)
|
|
9
|
+
.split(',')
|
|
10
|
+
.map((item) => item.trim())
|
|
11
|
+
.filter(Boolean);
|
|
12
|
+
return items.length > 0 ? items : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function clearableValue(value, shouldClear) {
|
|
16
|
+
if (shouldClear) return null;
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
export function registerTaskCommands(
|
|
7
21
|
program,
|
|
8
22
|
{ chalk, resolveVaultPath }
|
|
@@ -21,6 +35,11 @@ export function registerTaskCommands(
|
|
|
21
35
|
.option('--project <project>', 'Project name')
|
|
22
36
|
.option('--priority <priority>', 'Priority (critical, high, medium, low)')
|
|
23
37
|
.option('--due <date>', 'Due date (YYYY-MM-DD)')
|
|
38
|
+
.option('--tags <tags>', 'Comma-separated tags')
|
|
39
|
+
.option('--description <description>', 'One-line task summary')
|
|
40
|
+
.option('--estimate <estimate>', 'Estimate (for example: 2h, 1d, 1w)')
|
|
41
|
+
.option('--parent <slug>', 'Parent task slug')
|
|
42
|
+
.option('--depends-on <slugs>', 'Comma-separated dependency slugs')
|
|
24
43
|
.action(async (title, options) => {
|
|
25
44
|
try {
|
|
26
45
|
const vaultPath = resolveVaultPath(options.vault);
|
|
@@ -31,7 +50,12 @@ export function registerTaskCommands(
|
|
|
31
50
|
owner: options.owner,
|
|
32
51
|
project: options.project,
|
|
33
52
|
priority: options.priority,
|
|
34
|
-
due: options.due
|
|
53
|
+
due: options.due,
|
|
54
|
+
tags: parseCsvList(options.tags),
|
|
55
|
+
description: options.description,
|
|
56
|
+
estimate: options.estimate,
|
|
57
|
+
parent: options.parent,
|
|
58
|
+
dependsOn: parseCsvList(options.dependsOn)
|
|
35
59
|
}
|
|
36
60
|
});
|
|
37
61
|
} catch (err) {
|
|
@@ -49,6 +73,9 @@ export function registerTaskCommands(
|
|
|
49
73
|
.option('--project <project>', 'Filter by project')
|
|
50
74
|
.option('--status <status>', 'Filter by status (open, in-progress, blocked, done)')
|
|
51
75
|
.option('--priority <priority>', 'Filter by priority')
|
|
76
|
+
.option('--due', 'Show only tasks with due dates (sorted by due date)')
|
|
77
|
+
.option('--tag <tag>', 'Filter by tag')
|
|
78
|
+
.option('--overdue', 'Show overdue tasks that are not done')
|
|
52
79
|
.option('--json', 'Output as JSON')
|
|
53
80
|
.action(async (options) => {
|
|
54
81
|
try {
|
|
@@ -60,6 +87,9 @@ export function registerTaskCommands(
|
|
|
60
87
|
project: options.project,
|
|
61
88
|
status: options.status,
|
|
62
89
|
priority: options.priority,
|
|
90
|
+
due: options.due,
|
|
91
|
+
tag: options.tag,
|
|
92
|
+
overdue: options.overdue,
|
|
63
93
|
json: options.json
|
|
64
94
|
}
|
|
65
95
|
});
|
|
@@ -76,12 +106,28 @@ export function registerTaskCommands(
|
|
|
76
106
|
.option('-v, --vault <path>', 'Vault path')
|
|
77
107
|
.option('--status <status>', 'New status')
|
|
78
108
|
.option('--owner <owner>', 'New owner')
|
|
109
|
+
.option('--clear-owner', 'Clear owner')
|
|
79
110
|
.option('--project <project>', 'New project')
|
|
111
|
+
.option('--clear-project', 'Clear project')
|
|
80
112
|
.option('--priority <priority>', 'New priority')
|
|
113
|
+
.option('--clear-priority', 'Clear priority')
|
|
81
114
|
.option('--blocked-by <blocker>', 'What is blocking this task')
|
|
115
|
+
.option('--clear-blocked-by', 'Clear blocked-by field')
|
|
82
116
|
.option('--due <date>', 'New due date')
|
|
117
|
+
.option('--clear-due', 'Clear due date')
|
|
118
|
+
.option('--tags <tags>', 'Comma-separated tags')
|
|
119
|
+
.option('--clear-tags', 'Clear tags')
|
|
120
|
+
.option('--description <description>', 'One-line task summary')
|
|
121
|
+
.option('--clear-description', 'Clear description')
|
|
122
|
+
.option('--estimate <estimate>', 'Estimate (for example: 2h, 1d, 1w)')
|
|
123
|
+
.option('--clear-estimate', 'Clear estimate')
|
|
124
|
+
.option('--parent <slug>', 'Parent task slug')
|
|
125
|
+
.option('--clear-parent', 'Clear parent task')
|
|
126
|
+
.option('--depends-on <slugs>', 'Comma-separated dependency slugs')
|
|
127
|
+
.option('--clear-depends-on', 'Clear dependencies')
|
|
83
128
|
.option('--confidence <value>', 'Transition confidence (0-1)', parseFloat)
|
|
84
129
|
.option('--reason <reason>', 'Reason for status change')
|
|
130
|
+
.option('--clear-reason', 'Clear reason')
|
|
85
131
|
.action(async (slug, options) => {
|
|
86
132
|
try {
|
|
87
133
|
const vaultPath = resolveVaultPath(options.vault);
|
|
@@ -90,13 +136,18 @@ export function registerTaskCommands(
|
|
|
90
136
|
slug,
|
|
91
137
|
options: {
|
|
92
138
|
status: options.status,
|
|
93
|
-
owner: options.owner,
|
|
94
|
-
project: options.project,
|
|
95
|
-
priority: options.priority,
|
|
96
|
-
blockedBy: options.blockedBy,
|
|
97
|
-
due: options.due,
|
|
139
|
+
owner: clearableValue(options.owner, options.clearOwner),
|
|
140
|
+
project: clearableValue(options.project, options.clearProject),
|
|
141
|
+
priority: clearableValue(options.priority, options.clearPriority),
|
|
142
|
+
blockedBy: clearableValue(options.blockedBy, options.clearBlockedBy),
|
|
143
|
+
due: clearableValue(options.due, options.clearDue),
|
|
144
|
+
tags: options.clearTags ? null : parseCsvList(options.tags),
|
|
145
|
+
description: clearableValue(options.description, options.clearDescription),
|
|
146
|
+
estimate: clearableValue(options.estimate, options.clearEstimate),
|
|
147
|
+
parent: clearableValue(options.parent, options.clearParent),
|
|
148
|
+
dependsOn: options.clearDependsOn ? null : parseCsvList(options.dependsOn),
|
|
98
149
|
confidence: options.confidence,
|
|
99
|
-
reason: options.reason
|
|
150
|
+
reason: clearableValue(options.reason, options.clearReason)
|
|
100
151
|
}
|
|
101
152
|
});
|
|
102
153
|
} catch (err) {
|
|
@@ -282,28 +333,12 @@ export function registerTaskCommands(
|
|
|
282
333
|
.description('Generate Obsidian canvas dashboard')
|
|
283
334
|
.option('-v, --vault <path>', 'Vault path')
|
|
284
335
|
.option('--output <path>', 'Output file path (default: dashboard.canvas)')
|
|
285
|
-
.option('--template <id>', 'Canvas template ID (default, project-board, brain, sprint)')
|
|
286
|
-
.option('--project <project>', 'Project filter for template-aware canvases')
|
|
287
|
-
.option('--owner <owner>', 'Filter tasks by owner (agent name or human)')
|
|
288
|
-
.option('--width <pixels>', 'Canvas width in pixels', parseInt)
|
|
289
|
-
.option('--height <pixels>', 'Canvas height in pixels', parseInt)
|
|
290
|
-
.option('--include-done', 'Include completed tasks (default: limited)')
|
|
291
|
-
.option('--list-templates', 'List available canvas templates and exit')
|
|
292
336
|
.action(async (options) => {
|
|
293
337
|
try {
|
|
294
|
-
const vaultPath = options.
|
|
295
|
-
? (options.vault || '.')
|
|
296
|
-
: resolveVaultPath(options.vault);
|
|
338
|
+
const vaultPath = resolveVaultPath(options.vault);
|
|
297
339
|
const { canvasCommand } = await import('../dist/commands/canvas.js');
|
|
298
340
|
await canvasCommand(vaultPath, {
|
|
299
|
-
output: options.output
|
|
300
|
-
template: options.template,
|
|
301
|
-
project: options.project,
|
|
302
|
-
owner: options.owner,
|
|
303
|
-
width: options.width,
|
|
304
|
-
height: options.height,
|
|
305
|
-
includeDone: options.includeDone,
|
|
306
|
-
listTemplates: options.listTemplates
|
|
341
|
+
output: options.output
|
|
307
342
|
});
|
|
308
343
|
} catch (err) {
|
|
309
344
|
console.error(chalk.red(`Error: ${err.message}`));
|
|
@@ -4,7 +4,53 @@ import { registerTaskCommands } from './register-task-commands.js';
|
|
|
4
4
|
import { chalkStub, stubResolveVaultPath } from './test-helpers/cli-command-fixtures.js';
|
|
5
5
|
|
|
6
6
|
describe('register-task-commands', () => {
|
|
7
|
-
it('adds
|
|
7
|
+
it('adds enriched task add/list/update options', () => {
|
|
8
|
+
const program = new Command();
|
|
9
|
+
registerTaskCommands(program, {
|
|
10
|
+
chalk: chalkStub,
|
|
11
|
+
resolveVaultPath: stubResolveVaultPath
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const taskCommand = program.commands.find((command) => command.name() === 'task');
|
|
15
|
+
expect(taskCommand).toBeDefined();
|
|
16
|
+
|
|
17
|
+
const addCommand = taskCommand?.commands.find((command) => command.name() === 'add');
|
|
18
|
+
const addFlags = addCommand?.options.map((option) => option.flags) ?? [];
|
|
19
|
+
expect(addFlags).toEqual(expect.arrayContaining([
|
|
20
|
+
'--due <date>',
|
|
21
|
+
'--tags <tags>',
|
|
22
|
+
'--description <description>',
|
|
23
|
+
'--estimate <estimate>',
|
|
24
|
+
'--parent <slug>',
|
|
25
|
+
'--depends-on <slugs>'
|
|
26
|
+
]));
|
|
27
|
+
|
|
28
|
+
const listCommand = taskCommand?.commands.find((command) => command.name() === 'list');
|
|
29
|
+
const listFlags = listCommand?.options.map((option) => option.flags) ?? [];
|
|
30
|
+
expect(listFlags).toEqual(expect.arrayContaining([
|
|
31
|
+
'--due',
|
|
32
|
+
'--tag <tag>',
|
|
33
|
+
'--overdue'
|
|
34
|
+
]));
|
|
35
|
+
|
|
36
|
+
const updateCommand = taskCommand?.commands.find((command) => command.name() === 'update');
|
|
37
|
+
const updateFlags = updateCommand?.options.map((option) => option.flags) ?? [];
|
|
38
|
+
expect(updateFlags).toEqual(expect.arrayContaining([
|
|
39
|
+
'--tags <tags>',
|
|
40
|
+
'--description <description>',
|
|
41
|
+
'--estimate <estimate>',
|
|
42
|
+
'--parent <slug>',
|
|
43
|
+
'--depends-on <slugs>',
|
|
44
|
+
'--clear-due',
|
|
45
|
+
'--clear-tags',
|
|
46
|
+
'--clear-description',
|
|
47
|
+
'--clear-estimate',
|
|
48
|
+
'--clear-parent',
|
|
49
|
+
'--clear-depends-on'
|
|
50
|
+
]));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('adds simplified canvas flags', () => {
|
|
8
54
|
const program = new Command();
|
|
9
55
|
registerTaskCommands(program, {
|
|
10
56
|
chalk: chalkStub,
|
|
@@ -16,9 +62,8 @@ describe('register-task-commands', () => {
|
|
|
16
62
|
|
|
17
63
|
const optionFlags = canvasCommand?.options.map((option) => option.flags) ?? [];
|
|
18
64
|
expect(optionFlags).toEqual(expect.arrayContaining([
|
|
19
|
-
'--
|
|
20
|
-
'--
|
|
21
|
-
'--project <project>'
|
|
65
|
+
'-v, --vault <path>',
|
|
66
|
+
'--output <path>'
|
|
22
67
|
]));
|
|
23
68
|
});
|
|
24
69
|
});
|
|
@@ -10,6 +10,9 @@ import { registerTemplateCommands } from '../register-template-commands.js';
|
|
|
10
10
|
import { registerVaultOperationsCommands } from '../register-vault-operations-commands.js';
|
|
11
11
|
import { registerConfigCommands } from '../register-config-commands.js';
|
|
12
12
|
import { registerRouteCommands } from '../register-route-commands.js';
|
|
13
|
+
import { registerTaskCommands } from '../register-task-commands.js';
|
|
14
|
+
import { registerKanbanCommands } from '../register-kanban-commands.js';
|
|
15
|
+
import { registerProjectCommands } from '../register-project-commands.js';
|
|
13
16
|
|
|
14
17
|
export const chalkStub = {
|
|
15
18
|
cyan: (value) => value,
|
|
@@ -99,6 +102,18 @@ export function registerAllCommandModules(program = new Command()) {
|
|
|
99
102
|
chalk: chalkStub,
|
|
100
103
|
resolveVaultPath: stubResolveVaultPath
|
|
101
104
|
});
|
|
105
|
+
registerTaskCommands(program, {
|
|
106
|
+
chalk: chalkStub,
|
|
107
|
+
resolveVaultPath: stubResolveVaultPath
|
|
108
|
+
});
|
|
109
|
+
registerKanbanCommands(program, {
|
|
110
|
+
chalk: chalkStub,
|
|
111
|
+
resolveVaultPath: stubResolveVaultPath
|
|
112
|
+
});
|
|
113
|
+
registerProjectCommands(program, {
|
|
114
|
+
chalk: chalkStub,
|
|
115
|
+
resolveVaultPath: stubResolveVaultPath
|
|
116
|
+
});
|
|
102
117
|
|
|
103
118
|
return program;
|
|
104
119
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getSessionsDir
|
|
3
|
-
} from "./chunk-HRLWZGMA.js";
|
|
4
1
|
import {
|
|
5
2
|
parseSessionFile
|
|
6
3
|
} from "./chunk-P5EPF6MB.js";
|
|
7
4
|
import {
|
|
8
5
|
Observer
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TMZMN7OS.js";
|
|
7
|
+
import {
|
|
8
|
+
getSessionsDir
|
|
9
|
+
} from "./chunk-HRLWZGMA.js";
|
|
10
10
|
import {
|
|
11
11
|
resolveVaultPath
|
|
12
12
|
} from "./chunk-MXSSG3QU.js";
|
|
13
13
|
import {
|
|
14
14
|
getObservationPath
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Z2XBWN7A.js";
|
|
16
16
|
|
|
17
17
|
// src/commands/observe.ts
|
|
18
18
|
import * as fs3 from "fs";
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listTasks,
|
|
3
|
+
slugify
|
|
4
|
+
} from "./chunk-IOALNTAN.js";
|
|
5
|
+
|
|
6
|
+
// src/lib/project-utils.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import matter from "gray-matter";
|
|
10
|
+
function extractTitle(content) {
|
|
11
|
+
const match = content.match(/^#\s+(.+)$/m);
|
|
12
|
+
return match ? match[1].trim() : "";
|
|
13
|
+
}
|
|
14
|
+
function isDateSlug(slug) {
|
|
15
|
+
return /^\d{4}-\d{2}-\d{2}$/.test(slug);
|
|
16
|
+
}
|
|
17
|
+
function normalizeStringArray(value) {
|
|
18
|
+
return value.map((item) => item.trim()).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
function getProjectsDir(vaultPath) {
|
|
21
|
+
return path.join(path.resolve(vaultPath), "projects");
|
|
22
|
+
}
|
|
23
|
+
function ensureProjectsDir(vaultPath) {
|
|
24
|
+
const projectsDir = getProjectsDir(vaultPath);
|
|
25
|
+
if (!fs.existsSync(projectsDir)) {
|
|
26
|
+
fs.mkdirSync(projectsDir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function getProjectPath(vaultPath, slug) {
|
|
30
|
+
return path.join(getProjectsDir(vaultPath), `${slug}.md`);
|
|
31
|
+
}
|
|
32
|
+
function parseProjectDateValue(filePath) {
|
|
33
|
+
const filename = path.basename(filePath, ".md");
|
|
34
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(filename)) {
|
|
35
|
+
const dateTs = Date.parse(`${filename}T00:00:00.000Z`);
|
|
36
|
+
if (!Number.isNaN(dateTs)) {
|
|
37
|
+
return dateTs;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return fs.statSync(filePath).mtime.getTime();
|
|
41
|
+
}
|
|
42
|
+
function parseSortableTimestamp(value) {
|
|
43
|
+
if (!value) return 0;
|
|
44
|
+
const timestamp = Date.parse(value);
|
|
45
|
+
return Number.isNaN(timestamp) ? 0 : timestamp;
|
|
46
|
+
}
|
|
47
|
+
function normalizeProjectStatus(value) {
|
|
48
|
+
if (value === "active" || value === "paused" || value === "completed" || value === "archived") {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
return "active";
|
|
52
|
+
}
|
|
53
|
+
function normalizeProjectFrontmatter(frontmatter) {
|
|
54
|
+
const normalizedCreated = typeof frontmatter.created === "string" && frontmatter.created ? frontmatter.created : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
55
|
+
const normalizedUpdated = typeof frontmatter.updated === "string" && frontmatter.updated ? frontmatter.updated : normalizedCreated;
|
|
56
|
+
const normalized = {
|
|
57
|
+
...frontmatter,
|
|
58
|
+
type: "project",
|
|
59
|
+
status: normalizeProjectStatus(frontmatter.status),
|
|
60
|
+
created: normalizedCreated,
|
|
61
|
+
updated: normalizedUpdated
|
|
62
|
+
};
|
|
63
|
+
if (normalized.team) {
|
|
64
|
+
const team = normalizeStringArray(normalized.team);
|
|
65
|
+
if (team.length === 0) {
|
|
66
|
+
delete normalized.team;
|
|
67
|
+
} else {
|
|
68
|
+
normalized.team = team;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (normalized.tags) {
|
|
72
|
+
const tags = normalizeStringArray(normalized.tags);
|
|
73
|
+
if (tags.length === 0) {
|
|
74
|
+
delete normalized.tags;
|
|
75
|
+
} else {
|
|
76
|
+
normalized.tags = tags;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return normalized;
|
|
80
|
+
}
|
|
81
|
+
function listProjects(vaultPath, filters) {
|
|
82
|
+
const projectsDir = getProjectsDir(vaultPath);
|
|
83
|
+
if (!fs.existsSync(projectsDir)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const projects = [];
|
|
87
|
+
const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const slug = entry.name.replace(/\.md$/, "");
|
|
93
|
+
if (isDateSlug(slug)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const project = readProject(vaultPath, slug);
|
|
97
|
+
if (!project) continue;
|
|
98
|
+
if (filters) {
|
|
99
|
+
if (filters.status && project.frontmatter.status !== filters.status) continue;
|
|
100
|
+
if (filters.owner && project.frontmatter.owner !== filters.owner) continue;
|
|
101
|
+
if (filters.client && project.frontmatter.client !== filters.client) continue;
|
|
102
|
+
if (filters.tag) {
|
|
103
|
+
const tags = project.frontmatter.tags || [];
|
|
104
|
+
const hasTag = tags.some((tag) => tag.toLowerCase() === filters.tag?.toLowerCase());
|
|
105
|
+
if (!hasTag) continue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
projects.push(project);
|
|
109
|
+
}
|
|
110
|
+
return projects.sort((left, right) => {
|
|
111
|
+
const rightTime = parseSortableTimestamp(right.frontmatter.updated || right.frontmatter.created);
|
|
112
|
+
const leftTime = parseSortableTimestamp(left.frontmatter.updated || left.frontmatter.created);
|
|
113
|
+
return rightTime - leftTime;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function readProject(vaultPath, slug) {
|
|
117
|
+
if (!slug || isDateSlug(slug) || slug.includes(path.sep)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const projectPath = getProjectPath(vaultPath, slug);
|
|
121
|
+
if (!fs.existsSync(projectPath)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const raw = fs.readFileSync(projectPath, "utf-8");
|
|
126
|
+
const { data, content } = matter(raw);
|
|
127
|
+
if (data.type !== "project") {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const frontmatter = normalizeProjectFrontmatter(data);
|
|
131
|
+
const title = extractTitle(content) || slug;
|
|
132
|
+
return {
|
|
133
|
+
slug,
|
|
134
|
+
title,
|
|
135
|
+
content,
|
|
136
|
+
frontmatter
|
|
137
|
+
};
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function createProject(vaultPath, title, options = {}) {
|
|
143
|
+
ensureProjectsDir(vaultPath);
|
|
144
|
+
const slug = slugify(title);
|
|
145
|
+
const projectPath = getProjectPath(vaultPath, slug);
|
|
146
|
+
if (fs.existsSync(projectPath)) {
|
|
147
|
+
throw new Error(`Project already exists: ${slug}`);
|
|
148
|
+
}
|
|
149
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
150
|
+
const frontmatter = {
|
|
151
|
+
type: "project",
|
|
152
|
+
status: options.status ?? "active",
|
|
153
|
+
created: now,
|
|
154
|
+
updated: now
|
|
155
|
+
};
|
|
156
|
+
if (options.owner) frontmatter.owner = options.owner;
|
|
157
|
+
if (options.team && options.team.length > 0) {
|
|
158
|
+
const team = normalizeStringArray(options.team);
|
|
159
|
+
if (team.length > 0) frontmatter.team = team;
|
|
160
|
+
}
|
|
161
|
+
if (options.client) frontmatter.client = options.client;
|
|
162
|
+
if (options.tags && options.tags.length > 0) {
|
|
163
|
+
const tags = normalizeStringArray(options.tags);
|
|
164
|
+
if (tags.length > 0) frontmatter.tags = tags;
|
|
165
|
+
}
|
|
166
|
+
if (options.description) frontmatter.description = options.description;
|
|
167
|
+
if (options.started) frontmatter.started = options.started;
|
|
168
|
+
if (options.deadline) frontmatter.deadline = options.deadline;
|
|
169
|
+
if (options.repo) frontmatter.repo = options.repo;
|
|
170
|
+
if (options.url) frontmatter.url = options.url;
|
|
171
|
+
if (options.completed) frontmatter.completed = options.completed;
|
|
172
|
+
if (options.reason) frontmatter.reason = options.reason;
|
|
173
|
+
let content = `# ${title}
|
|
174
|
+
`;
|
|
175
|
+
const wikiLinks = /* @__PURE__ */ new Set();
|
|
176
|
+
if (options.owner) wikiLinks.add(options.owner);
|
|
177
|
+
if (options.client) wikiLinks.add(options.client);
|
|
178
|
+
for (const member of options.team || []) {
|
|
179
|
+
const trimmed = member.trim();
|
|
180
|
+
if (trimmed) wikiLinks.add(trimmed);
|
|
181
|
+
}
|
|
182
|
+
if (wikiLinks.size > 0) {
|
|
183
|
+
content += `
|
|
184
|
+
${Array.from(wikiLinks).map((link) => `[[${link}]]`).join(" | ")}
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
if (options.content) {
|
|
188
|
+
content += `
|
|
189
|
+
${options.content}
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
const fileContent = matter.stringify(content, frontmatter);
|
|
193
|
+
fs.writeFileSync(projectPath, fileContent);
|
|
194
|
+
return {
|
|
195
|
+
slug,
|
|
196
|
+
title,
|
|
197
|
+
content,
|
|
198
|
+
frontmatter
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function updateProject(vaultPath, slug, updates) {
|
|
202
|
+
const project = readProject(vaultPath, slug);
|
|
203
|
+
if (!project) {
|
|
204
|
+
throw new Error(`Project not found: ${slug}`);
|
|
205
|
+
}
|
|
206
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
207
|
+
const nextFrontmatter = {
|
|
208
|
+
...project.frontmatter,
|
|
209
|
+
type: "project",
|
|
210
|
+
updated: now
|
|
211
|
+
};
|
|
212
|
+
if (updates.status !== void 0) {
|
|
213
|
+
nextFrontmatter.status = updates.status;
|
|
214
|
+
if (updates.status === "completed" && !updates.completed && !nextFrontmatter.completed) {
|
|
215
|
+
nextFrontmatter.completed = now;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (updates.owner !== void 0) {
|
|
219
|
+
if (updates.owner === null || updates.owner.trim() === "") {
|
|
220
|
+
delete nextFrontmatter.owner;
|
|
221
|
+
} else {
|
|
222
|
+
nextFrontmatter.owner = updates.owner;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (updates.team !== void 0) {
|
|
226
|
+
if (updates.team === null) {
|
|
227
|
+
delete nextFrontmatter.team;
|
|
228
|
+
} else {
|
|
229
|
+
const team = normalizeStringArray(updates.team);
|
|
230
|
+
if (team.length === 0) {
|
|
231
|
+
delete nextFrontmatter.team;
|
|
232
|
+
} else {
|
|
233
|
+
nextFrontmatter.team = team;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (updates.client !== void 0) {
|
|
238
|
+
if (updates.client === null || updates.client.trim() === "") {
|
|
239
|
+
delete nextFrontmatter.client;
|
|
240
|
+
} else {
|
|
241
|
+
nextFrontmatter.client = updates.client;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (updates.tags !== void 0) {
|
|
245
|
+
if (updates.tags === null) {
|
|
246
|
+
delete nextFrontmatter.tags;
|
|
247
|
+
} else {
|
|
248
|
+
const tags = normalizeStringArray(updates.tags);
|
|
249
|
+
if (tags.length === 0) {
|
|
250
|
+
delete nextFrontmatter.tags;
|
|
251
|
+
} else {
|
|
252
|
+
nextFrontmatter.tags = tags;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (updates.description !== void 0) {
|
|
257
|
+
if (updates.description === null || updates.description.trim() === "") {
|
|
258
|
+
delete nextFrontmatter.description;
|
|
259
|
+
} else {
|
|
260
|
+
nextFrontmatter.description = updates.description;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (updates.started !== void 0) {
|
|
264
|
+
if (updates.started === null || updates.started.trim() === "") {
|
|
265
|
+
delete nextFrontmatter.started;
|
|
266
|
+
} else {
|
|
267
|
+
nextFrontmatter.started = updates.started;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (updates.deadline !== void 0) {
|
|
271
|
+
if (updates.deadline === null || updates.deadline.trim() === "") {
|
|
272
|
+
delete nextFrontmatter.deadline;
|
|
273
|
+
} else {
|
|
274
|
+
nextFrontmatter.deadline = updates.deadline;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (updates.repo !== void 0) {
|
|
278
|
+
if (updates.repo === null || updates.repo.trim() === "") {
|
|
279
|
+
delete nextFrontmatter.repo;
|
|
280
|
+
} else {
|
|
281
|
+
nextFrontmatter.repo = updates.repo;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (updates.url !== void 0) {
|
|
285
|
+
if (updates.url === null || updates.url.trim() === "") {
|
|
286
|
+
delete nextFrontmatter.url;
|
|
287
|
+
} else {
|
|
288
|
+
nextFrontmatter.url = updates.url;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (updates.completed !== void 0) {
|
|
292
|
+
if (updates.completed === null || updates.completed.trim() === "") {
|
|
293
|
+
delete nextFrontmatter.completed;
|
|
294
|
+
} else {
|
|
295
|
+
nextFrontmatter.completed = updates.completed;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (updates.reason !== void 0) {
|
|
299
|
+
if (updates.reason === null || updates.reason.trim() === "") {
|
|
300
|
+
delete nextFrontmatter.reason;
|
|
301
|
+
} else {
|
|
302
|
+
nextFrontmatter.reason = updates.reason;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const projectPath = getProjectPath(vaultPath, slug);
|
|
306
|
+
fs.writeFileSync(projectPath, matter.stringify(project.content, nextFrontmatter));
|
|
307
|
+
return {
|
|
308
|
+
...project,
|
|
309
|
+
frontmatter: nextFrontmatter
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function archiveProject(vaultPath, slug, reason) {
|
|
313
|
+
return updateProject(vaultPath, slug, {
|
|
314
|
+
status: "archived",
|
|
315
|
+
reason: reason ?? null,
|
|
316
|
+
completed: (/* @__PURE__ */ new Date()).toISOString()
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function getProjectTasks(vaultPath, slug) {
|
|
320
|
+
return listTasks(vaultPath, { project: slug });
|
|
321
|
+
}
|
|
322
|
+
function getProjectActivity(vaultPath, slug) {
|
|
323
|
+
const projectActivityDir = path.join(getProjectsDir(vaultPath), slug);
|
|
324
|
+
if (!fs.existsSync(projectActivityDir) || !fs.statSync(projectActivityDir).isDirectory()) {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
const entries = fs.readdirSync(projectActivityDir, { withFileTypes: true });
|
|
328
|
+
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => path.join(projectActivityDir, entry.name));
|
|
329
|
+
return files.sort((left, right) => parseProjectDateValue(right) - parseProjectDateValue(left));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export {
|
|
333
|
+
listProjects,
|
|
334
|
+
readProject,
|
|
335
|
+
createProject,
|
|
336
|
+
updateProject,
|
|
337
|
+
archiveProject,
|
|
338
|
+
getProjectTasks,
|
|
339
|
+
getProjectActivity
|
|
340
|
+
};
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-6B3JWM7J.js";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_CATEGORIES
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2CDEETQN.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/setup.ts
|
|
9
9
|
import * as fs from "fs";
|
|
@@ -327,10 +327,9 @@ async function setupCommand(options = {}) {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
if (doCanvas) {
|
|
330
|
-
const templateId = typeof options.canvas === "string" ? options.canvas : "default";
|
|
331
330
|
try {
|
|
332
331
|
const { canvasCommand } = await import("./commands/canvas.js");
|
|
333
|
-
await canvasCommand(target.vaultPath
|
|
332
|
+
await canvasCommand(target.vaultPath);
|
|
334
333
|
} catch (err) {
|
|
335
334
|
console.log(`\u26A0 Canvas generation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
336
335
|
}
|
|
@@ -353,7 +352,7 @@ async function setupCommand(options = {}) {
|
|
|
353
352
|
console.log("\nCustomize further:");
|
|
354
353
|
console.log(" clawvault setup --theme neural # Neural neural graph colors");
|
|
355
354
|
console.log(" clawvault setup --theme minimal # Subtle category colors");
|
|
356
|
-
console.log(" clawvault setup --canvas
|
|
355
|
+
console.log(" clawvault setup --canvas # Generate vault status canvas");
|
|
357
356
|
console.log(" clawvault setup --no-bases --no-graph-colors # Structure only");
|
|
358
357
|
console.log(" clawvault setup --force # Overwrite existing configs");
|
|
359
358
|
}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_CATEGORIES,
|
|
12
12
|
TYPE_TO_CATEGORY
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2CDEETQN.js";
|
|
14
14
|
import {
|
|
15
15
|
buildOrUpdateMemoryGraphIndex
|
|
16
16
|
} from "./chunk-ZZA73MFY.js";
|
|
@@ -851,6 +851,7 @@ clawvault wake # restore context on startup
|
|
|
851
851
|
facts: "Raw information, data points, things that are true",
|
|
852
852
|
feelings: "Emotional states, reactions, energy levels",
|
|
853
853
|
decisions: "Choices made with context and reasoning",
|
|
854
|
+
rules: "Injectable operational constraints, guardrails, and runbooks",
|
|
854
855
|
lessons: "What I learned, insights, patterns observed",
|
|
855
856
|
commitments: "Promises, goals, obligations to fulfill",
|
|
856
857
|
preferences: "Likes, dislikes, how I want things",
|