clawvault 3.2.0 → 3.3.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 +54 -14
- package/bin/clawvault.js +0 -2
- package/bin/command-registration.test.js +13 -1
- package/bin/help-contract.test.js +14 -0
- package/bin/register-core-commands.js +88 -0
- package/bin/register-core-commands.test.js +80 -0
- package/bin/register-maintenance-commands.js +57 -6
- package/bin/register-query-commands.js +10 -28
- package/bin/test-helpers/cli-command-fixtures.js +1 -0
- package/dist/chunk-2PKBIKDH.js +130 -0
- package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
- package/dist/{chunk-77Q5CSPJ.js → chunk-7SWP5FKU.js} +33 -701
- package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
- package/dist/{chunk-23YDQ3QU.js → chunk-BLQXXX7Q.js} +6 -6
- package/dist/chunk-CSHO3PJB.js +684 -0
- package/dist/{chunk-SLXOR3CC.js → chunk-DOIUYIXV.js} +2 -2
- package/dist/{chunk-NCKFNBHJ.js → chunk-DVOUSOR3.js} +79 -5
- package/dist/{chunk-CLJTREDS.js → chunk-ECGJYWNA.js} +193 -41
- package/dist/{chunk-BUEW6IIK.js → chunk-EL6UBSX5.js} +5 -5
- package/dist/{chunk-6FH3IULF.js → chunk-FZ5I2NF7.js} +1 -1
- package/dist/{chunk-ZN54U2OZ.js → chunk-GFCHWMGD.js} +3 -3
- package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
- package/dist/chunk-H3JZIB5O.js +322 -0
- package/dist/chunk-HEHO7SMV.js +51 -0
- package/dist/{chunk-STCQGCEQ.js → chunk-HGDDW24U.js} +3 -3
- package/dist/chunk-J3YUXVID.js +907 -0
- package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
- package/dist/{chunk-W4SPAEE7.js → chunk-OFOCU2V4.js} +5 -4
- package/dist/chunk-PTWPPVC7.js +972 -0
- package/dist/{chunk-QSHD36LH.js → chunk-QFWERBDP.js} +2 -2
- package/dist/{chunk-QSRRMEYM.js → chunk-S7N7HI5E.js} +1 -1
- package/dist/{chunk-PBACDKKP.js → chunk-T7E764W3.js} +3 -3
- package/dist/chunk-TDWFBDAQ.js +1016 -0
- package/dist/{chunk-ESVS6K2B.js → chunk-TWMI3SNN.js} +6 -5
- package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
- package/dist/{chunk-ESFLMDRB.js → chunk-VXAGOLDP.js} +3 -3
- package/dist/chunk-YCUVAOFC.js +158 -0
- package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
- package/dist/chunk-ZKWPCBYT.js +600 -0
- package/dist/cli/index.js +24 -24
- package/dist/commands/archive.js +2 -2
- package/dist/commands/benchmark.d.ts +12 -0
- package/dist/commands/benchmark.js +12 -0
- package/dist/commands/context.js +6 -5
- package/dist/commands/doctor.d.ts +8 -3
- package/dist/commands/doctor.js +6 -20
- package/dist/commands/embed.js +5 -4
- package/dist/commands/entities.js +1 -1
- package/dist/commands/graph.js +2 -2
- package/dist/commands/inbox.d.ts +23 -0
- package/dist/commands/inbox.js +11 -0
- package/dist/commands/inject.d.ts +1 -1
- package/dist/commands/inject.js +3 -3
- package/dist/commands/link.js +6 -6
- package/dist/commands/maintain.d.ts +32 -0
- package/dist/commands/maintain.js +12 -0
- package/dist/commands/migrate-observations.js +2 -2
- package/dist/commands/observe.js +9 -8
- package/dist/commands/rebuild-embeddings.js +47 -16
- package/dist/commands/rebuild.js +7 -6
- package/dist/commands/reflect.js +5 -5
- package/dist/commands/replay.js +8 -7
- package/dist/commands/setup.js +3 -2
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +17 -15
- package/dist/commands/status.js +26 -24
- package/dist/commands/sync-bd.js +2 -2
- package/dist/commands/tailscale.js +2 -2
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +8 -7
- package/dist/index.d.ts +168 -16
- package/dist/index.js +271 -108
- package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
- package/dist/lib/config.js +1 -1
- package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
- package/hooks/clawvault/HOOK.md +22 -5
- package/hooks/clawvault/handler.js +213 -78
- package/hooks/clawvault/handler.test.js +109 -43
- package/hooks/clawvault/integrity.js +112 -0
- package/hooks/clawvault/integrity.test.js +32 -0
- package/hooks/clawvault/openclaw.plugin.json +133 -15
- package/openclaw.plugin.json +126 -20
- package/package.json +2 -2
- package/bin/register-workgraph-commands.js +0 -1368
- package/dist/chunk-33VSQP4J.js +0 -37
- package/dist/chunk-4BQTQMJP.js +0 -93
- package/dist/chunk-EK6S23ZB.js +0 -469
- package/dist/chunk-GAOWA7GR.js +0 -501
- package/dist/chunk-GGA32J2R.js +0 -784
- package/dist/chunk-MM6QGW3P.js +0 -207
- package/dist/chunk-QVEERJSP.js +0 -152
- package/dist/chunk-U4O6C46S.js +0 -154
- package/dist/chunk-VSL7KY3M.js +0 -189
- package/dist/chunk-WMGIIABP.js +0 -15
- package/dist/commands/workgraph.d.ts +0 -124
- package/dist/commands/workgraph.js +0 -38
- package/dist/ledger-B7g7jhqG.d.ts +0 -44
- package/dist/registry-BR4326o0.d.ts +0 -30
- package/dist/store-CA-6sKCJ.d.ts +0 -34
- package/dist/thread-B9LhXNU0.d.ts +0 -41
- package/dist/workgraph/index.d.ts +0 -5
- package/dist/workgraph/index.js +0 -23
- package/dist/workgraph/ledger.d.ts +0 -2
- package/dist/workgraph/ledger.js +0 -25
- package/dist/workgraph/registry.d.ts +0 -2
- package/dist/workgraph/registry.js +0 -19
- package/dist/workgraph/store.d.ts +0 -2
- package/dist/workgraph/store.js +0 -25
- package/dist/workgraph/thread.d.ts +0 -2
- package/dist/workgraph/thread.js +0 -25
- package/dist/workgraph/types.d.ts +0 -54
- package/dist/workgraph/types.js +0 -7
|
@@ -1,1368 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workgraph CLI commands — multi-agent coordination primitives.
|
|
3
|
-
*
|
|
4
|
-
* Commands under 'wg' namespace (beautiful, agent-native):
|
|
5
|
-
* wg status Agent morning briefing
|
|
6
|
-
* wg thread create Create a new thread
|
|
7
|
-
* wg thread list List threads with filters
|
|
8
|
-
* wg thread claim Claim a thread
|
|
9
|
-
* wg thread done Mark thread complete
|
|
10
|
-
* wg thread block Block thread on dependency
|
|
11
|
-
* wg thread release Release thread back to pool
|
|
12
|
-
* wg thread decompose Break thread into sub-threads
|
|
13
|
-
* wg ledger View coordination history
|
|
14
|
-
* wg define Define new primitive type
|
|
15
|
-
* wg types List all primitive types
|
|
16
|
-
* wg create Create any primitive
|
|
17
|
-
* wg board Terminal kanban board
|
|
18
|
-
*
|
|
19
|
-
* Legacy commands (still available):
|
|
20
|
-
* thread create <title> Create a new thread
|
|
21
|
-
* thread list List threads (filterable by status)
|
|
22
|
-
* thread show <path> Show thread details + ledger history
|
|
23
|
-
* thread claim <path> Claim a thread for this agent
|
|
24
|
-
* thread release <path> Release a claimed thread
|
|
25
|
-
* thread done <path> Mark thread complete
|
|
26
|
-
* thread block <path> Mark thread blocked
|
|
27
|
-
* thread unblock <path> Unblock a thread
|
|
28
|
-
* thread decompose <path> Break into sub-threads
|
|
29
|
-
* primitive define <name> Define a new primitive type
|
|
30
|
-
* primitive list List all primitive types
|
|
31
|
-
* primitive create <type> Create an instance of any type
|
|
32
|
-
* ledger show Show recent ledger entries
|
|
33
|
-
* ledger history <path> Show history of a specific file
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
import * as os from 'os';
|
|
37
|
-
import * as path from 'path';
|
|
38
|
-
|
|
39
|
-
export function registerWorkgraphCommands(program, { chalk, resolveVaultPath }) {
|
|
40
|
-
// Register the new 'wg' namespace with beautiful, agent-native commands
|
|
41
|
-
registerWgCommands(program, { chalk, resolveVaultPath });
|
|
42
|
-
|
|
43
|
-
// Keep legacy commands for backward compatibility
|
|
44
|
-
const agentName = process.env.CLAWVAULT_AGENT || process.env.USER || 'anonymous';
|
|
45
|
-
|
|
46
|
-
// =========================================================================
|
|
47
|
-
// thread
|
|
48
|
-
// =========================================================================
|
|
49
|
-
const threadCmd = program
|
|
50
|
-
.command('thread')
|
|
51
|
-
.description('Coordinate work through threads (workgraph core)');
|
|
52
|
-
|
|
53
|
-
threadCmd
|
|
54
|
-
.command('create <title>')
|
|
55
|
-
.description('Create a new thread')
|
|
56
|
-
.requiredOption('-g, --goal <goal>', 'What success looks like')
|
|
57
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
58
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
59
|
-
.option('-p, --priority <level>', 'urgent | high | medium | low', 'medium')
|
|
60
|
-
.option('--deps <paths>', 'Comma-separated dependency thread paths')
|
|
61
|
-
.option('--parent <path>', 'Parent thread path')
|
|
62
|
-
.option('--context <refs>', 'Comma-separated vault doc refs for context')
|
|
63
|
-
.option('--tags <tags>', 'Comma-separated tags')
|
|
64
|
-
.action(async (title, opts) => {
|
|
65
|
-
try {
|
|
66
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
67
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
68
|
-
const t = thread.createThread(vaultPath, title, opts.goal, opts.actor, {
|
|
69
|
-
priority: opts.priority,
|
|
70
|
-
deps: csv(opts.deps),
|
|
71
|
-
parent: opts.parent,
|
|
72
|
-
context_refs: csv(opts.context),
|
|
73
|
-
tags: csv(opts.tags),
|
|
74
|
-
});
|
|
75
|
-
console.log(chalk.green(`✓ Thread created: ${t.path}`));
|
|
76
|
-
console.log(` Title: ${t.fields.title}`);
|
|
77
|
-
console.log(` Status: ${t.fields.status}`);
|
|
78
|
-
console.log(` Priority: ${t.fields.priority}`);
|
|
79
|
-
} catch (err) {
|
|
80
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
threadCmd
|
|
86
|
-
.command('list')
|
|
87
|
-
.description('List threads')
|
|
88
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
89
|
-
.option('-s, --status <status>', 'Filter: open | active | blocked | done | cancelled')
|
|
90
|
-
.option('--json', 'Output as JSON')
|
|
91
|
-
.action(async (opts) => {
|
|
92
|
-
try {
|
|
93
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
94
|
-
const { store } = await import('../dist/workgraph/index.js');
|
|
95
|
-
let threads = store.list(vaultPath, 'thread');
|
|
96
|
-
if (opts.status) threads = threads.filter(t => t.fields.status === opts.status);
|
|
97
|
-
|
|
98
|
-
if (opts.json) {
|
|
99
|
-
console.log(JSON.stringify(threads.map(t => ({ path: t.path, ...t.fields })), null, 2));
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (threads.length === 0) {
|
|
104
|
-
console.log(chalk.dim('No threads found.'));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const statusColor = { open: 'blue', active: 'yellow', blocked: 'red', done: 'green', cancelled: 'dim' };
|
|
109
|
-
for (const t of threads) {
|
|
110
|
-
const s = String(t.fields.status);
|
|
111
|
-
const colorFn = chalk[statusColor[s]] || chalk.white;
|
|
112
|
-
const owner = t.fields.owner ? chalk.dim(` (${t.fields.owner})`) : '';
|
|
113
|
-
console.log(` ${colorFn(`[${s}]`)} ${t.fields.title}${owner}`);
|
|
114
|
-
console.log(chalk.dim(` ${t.path}`));
|
|
115
|
-
}
|
|
116
|
-
console.log(chalk.dim(`\n${threads.length} thread(s)`));
|
|
117
|
-
} catch (err) {
|
|
118
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
threadCmd
|
|
124
|
-
.command('show <path>')
|
|
125
|
-
.description('Show thread details and ledger history')
|
|
126
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
127
|
-
.action(async (threadPath, opts) => {
|
|
128
|
-
try {
|
|
129
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
130
|
-
const { store, ledger } = await import('../dist/workgraph/index.js');
|
|
131
|
-
const t = store.read(vaultPath, threadPath);
|
|
132
|
-
if (!t) { console.error(chalk.red(`Not found: ${threadPath}`)); process.exit(1); }
|
|
133
|
-
|
|
134
|
-
console.log(chalk.bold(String(t.fields.title)));
|
|
135
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
136
|
-
console.log(`Status: ${t.fields.status}`);
|
|
137
|
-
console.log(`Owner: ${t.fields.owner || chalk.dim('unclaimed')}`);
|
|
138
|
-
console.log(`Priority: ${t.fields.priority}`);
|
|
139
|
-
if (t.fields.deps?.length) console.log(`Deps: ${(t.fields.deps).join(', ')}`);
|
|
140
|
-
if (t.fields.parent) console.log(`Parent: ${t.fields.parent}`);
|
|
141
|
-
if (t.fields.tags?.length) console.log(`Tags: ${(t.fields.tags).join(', ')}`);
|
|
142
|
-
console.log(`Path: ${t.path}`);
|
|
143
|
-
console.log();
|
|
144
|
-
if (t.body) console.log(t.body);
|
|
145
|
-
|
|
146
|
-
const history = ledger.historyOf(vaultPath, threadPath);
|
|
147
|
-
if (history.length > 0) {
|
|
148
|
-
console.log(chalk.dim('\n─── Ledger History ───'));
|
|
149
|
-
for (const e of history) {
|
|
150
|
-
const time = new Date(e.ts).toLocaleTimeString();
|
|
151
|
-
const data = e.data ? chalk.dim(` ${JSON.stringify(e.data)}`) : '';
|
|
152
|
-
console.log(` ${chalk.dim(time)} ${chalk.cyan(e.op)} by ${e.actor}${data}`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
} catch (err) {
|
|
156
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
threadCmd
|
|
162
|
-
.command('claim <path>')
|
|
163
|
-
.description('Claim a thread — only you can work on it')
|
|
164
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
165
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
166
|
-
.action(async (threadPath, opts) => {
|
|
167
|
-
try {
|
|
168
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
169
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
170
|
-
const t = thread.claim(vaultPath, threadPath, opts.actor);
|
|
171
|
-
console.log(chalk.green(`✓ Claimed: ${threadPath}`));
|
|
172
|
-
console.log(` Owner: ${opts.actor}`);
|
|
173
|
-
} catch (err) {
|
|
174
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
threadCmd
|
|
180
|
-
.command('release <path>')
|
|
181
|
-
.description('Release a claimed thread back to open')
|
|
182
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
183
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
184
|
-
.option('--reason <reason>', 'Why you are releasing')
|
|
185
|
-
.action(async (threadPath, opts) => {
|
|
186
|
-
try {
|
|
187
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
188
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
189
|
-
thread.release(vaultPath, threadPath, opts.actor, opts.reason);
|
|
190
|
-
console.log(chalk.green(`✓ Released: ${threadPath}`));
|
|
191
|
-
} catch (err) {
|
|
192
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
193
|
-
process.exit(1);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
threadCmd
|
|
198
|
-
.command('done <path>')
|
|
199
|
-
.description('Mark thread complete')
|
|
200
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
201
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
202
|
-
.option('-o, --output <text>', 'Output/result summary')
|
|
203
|
-
.action(async (threadPath, opts) => {
|
|
204
|
-
try {
|
|
205
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
206
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
207
|
-
thread.done(vaultPath, threadPath, opts.actor, opts.output);
|
|
208
|
-
console.log(chalk.green(`✓ Done: ${threadPath}`));
|
|
209
|
-
} catch (err) {
|
|
210
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
threadCmd
|
|
216
|
-
.command('block <path>')
|
|
217
|
-
.description('Mark thread blocked on a dependency')
|
|
218
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
219
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
220
|
-
.requiredOption('-b, --blocked-by <dep>', 'What is blocking this thread')
|
|
221
|
-
.option('--reason <reason>', 'Why it is blocked')
|
|
222
|
-
.action(async (threadPath, opts) => {
|
|
223
|
-
try {
|
|
224
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
225
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
226
|
-
thread.block(vaultPath, threadPath, opts.actor, opts.blockedBy, opts.reason);
|
|
227
|
-
console.log(chalk.red(`⊘ Blocked: ${threadPath}`));
|
|
228
|
-
console.log(` Blocked by: ${opts.blockedBy}`);
|
|
229
|
-
} catch (err) {
|
|
230
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
231
|
-
process.exit(1);
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
threadCmd
|
|
236
|
-
.command('unblock <path>')
|
|
237
|
-
.description('Unblock a thread')
|
|
238
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
239
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
240
|
-
.action(async (threadPath, opts) => {
|
|
241
|
-
try {
|
|
242
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
243
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
244
|
-
thread.unblock(vaultPath, threadPath, opts.actor);
|
|
245
|
-
console.log(chalk.green(`✓ Unblocked: ${threadPath}`));
|
|
246
|
-
} catch (err) {
|
|
247
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
threadCmd
|
|
253
|
-
.command('decompose <path>')
|
|
254
|
-
.description('Break a thread into sub-threads')
|
|
255
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
256
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
257
|
-
.requiredOption('--sub <specs...>', 'Sub-threads as "title|goal" pairs')
|
|
258
|
-
.action(async (threadPath, opts) => {
|
|
259
|
-
try {
|
|
260
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
261
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
262
|
-
const subs = opts.sub.map(spec => {
|
|
263
|
-
const [title, ...goalParts] = spec.split('|');
|
|
264
|
-
return { title: title.trim(), goal: goalParts.join('|').trim() || title.trim() };
|
|
265
|
-
});
|
|
266
|
-
const children = thread.decompose(vaultPath, threadPath, subs, opts.actor);
|
|
267
|
-
console.log(chalk.green(`✓ Decomposed ${threadPath} into ${children.length} sub-threads:`));
|
|
268
|
-
for (const c of children) {
|
|
269
|
-
console.log(` → ${c.path}`);
|
|
270
|
-
}
|
|
271
|
-
} catch (err) {
|
|
272
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
273
|
-
process.exit(1);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// =========================================================================
|
|
278
|
-
// primitive
|
|
279
|
-
// =========================================================================
|
|
280
|
-
const primitiveCmd = program
|
|
281
|
-
.command('primitive')
|
|
282
|
-
.description('Manage workgraph primitive types (define new types, list, create)');
|
|
283
|
-
|
|
284
|
-
primitiveCmd
|
|
285
|
-
.command('define <name>')
|
|
286
|
-
.description('Define a new primitive type that agents can instantiate')
|
|
287
|
-
.requiredOption('-d, --description <desc>', 'What this type represents')
|
|
288
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
289
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
290
|
-
.option('--fields <specs...>', 'Field definitions as "name:type" (types: string, number, boolean, list, date, ref)')
|
|
291
|
-
.option('--dir <directory>', 'Storage directory name')
|
|
292
|
-
.action(async (name, opts) => {
|
|
293
|
-
try {
|
|
294
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
295
|
-
const { registry } = await import('../dist/workgraph/index.js');
|
|
296
|
-
const fields = {};
|
|
297
|
-
if (opts.fields) {
|
|
298
|
-
for (const spec of opts.fields) {
|
|
299
|
-
const [fieldName, fieldType = 'string'] = spec.split(':');
|
|
300
|
-
fields[fieldName.trim()] = { type: fieldType.trim() };
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
const typeDef = registry.defineType(vaultPath, name, opts.description, fields, opts.actor, opts.dir);
|
|
304
|
-
console.log(chalk.green(`✓ Defined type: ${typeDef.name}`));
|
|
305
|
-
console.log(` Directory: ${typeDef.directory}/`);
|
|
306
|
-
console.log(` Fields: ${Object.keys(typeDef.fields).join(', ')}`);
|
|
307
|
-
} catch (err) {
|
|
308
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
309
|
-
process.exit(1);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
primitiveCmd
|
|
314
|
-
.command('list')
|
|
315
|
-
.description('List all registered primitive types')
|
|
316
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
317
|
-
.option('--json', 'Output as JSON')
|
|
318
|
-
.action(async (opts) => {
|
|
319
|
-
try {
|
|
320
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
321
|
-
const { registry } = await import('../dist/workgraph/index.js');
|
|
322
|
-
const types = registry.listTypes(vaultPath);
|
|
323
|
-
|
|
324
|
-
if (opts.json) {
|
|
325
|
-
console.log(JSON.stringify(types, null, 2));
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
for (const t of types) {
|
|
330
|
-
const badge = t.builtIn ? chalk.dim('[built-in]') : chalk.cyan(`[${t.createdBy}]`);
|
|
331
|
-
console.log(` ${chalk.bold(t.name)} ${badge}`);
|
|
332
|
-
console.log(chalk.dim(` ${t.description}`));
|
|
333
|
-
console.log(chalk.dim(` dir: ${t.directory}/ fields: ${Object.keys(t.fields).join(', ')}`));
|
|
334
|
-
}
|
|
335
|
-
console.log(chalk.dim(`\n${types.length} type(s) — ${types.filter(t => !t.builtIn).length} agent-defined`));
|
|
336
|
-
} catch (err) {
|
|
337
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
338
|
-
process.exit(1);
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
primitiveCmd
|
|
343
|
-
.command('create <type> <title>')
|
|
344
|
-
.description('Create an instance of any primitive type')
|
|
345
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
346
|
-
.option('-a, --actor <name>', 'Agent name', agentName)
|
|
347
|
-
.option('--set <fields...>', 'Set fields as "key=value" pairs')
|
|
348
|
-
.option('--body <text>', 'Markdown body content', '')
|
|
349
|
-
.action(async (type, title, opts) => {
|
|
350
|
-
try {
|
|
351
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
352
|
-
const { store } = await import('../dist/workgraph/index.js');
|
|
353
|
-
const fields = { title };
|
|
354
|
-
if (opts.set) {
|
|
355
|
-
for (const pair of opts.set) {
|
|
356
|
-
const eqIdx = pair.indexOf('=');
|
|
357
|
-
if (eqIdx === -1) continue;
|
|
358
|
-
const key = pair.slice(0, eqIdx).trim();
|
|
359
|
-
let val = pair.slice(eqIdx + 1).trim();
|
|
360
|
-
if (val.includes(',')) val = val.split(',').map(s => s.trim());
|
|
361
|
-
fields[key] = val;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
const inst = store.create(vaultPath, type, fields, opts.body, opts.actor);
|
|
365
|
-
console.log(chalk.green(`✓ Created ${type}: ${inst.path}`));
|
|
366
|
-
} catch (err) {
|
|
367
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
368
|
-
process.exit(1);
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
// =========================================================================
|
|
373
|
-
// ledger
|
|
374
|
-
// =========================================================================
|
|
375
|
-
const ledgerCmd = program
|
|
376
|
-
.command('ledger')
|
|
377
|
-
.description('View the workgraph audit trail');
|
|
378
|
-
|
|
379
|
-
ledgerCmd
|
|
380
|
-
.command('show')
|
|
381
|
-
.description('Show recent ledger entries')
|
|
382
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
383
|
-
.option('-n, --count <n>', 'Number of entries', '20')
|
|
384
|
-
.option('--actor <name>', 'Filter by actor')
|
|
385
|
-
.option('--json', 'Output as JSON')
|
|
386
|
-
.action(async (opts) => {
|
|
387
|
-
try {
|
|
388
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
389
|
-
const { ledger } = await import('../dist/workgraph/index.js');
|
|
390
|
-
let entries = ledger.recent(vaultPath, parseInt(opts.count));
|
|
391
|
-
if (opts.actor) entries = entries.filter(e => e.actor === opts.actor);
|
|
392
|
-
|
|
393
|
-
if (opts.json) {
|
|
394
|
-
console.log(JSON.stringify(entries, null, 2));
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (entries.length === 0) {
|
|
399
|
-
console.log(chalk.dim('No ledger entries.'));
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
const opColor = { create: 'green', claim: 'yellow', release: 'blue', done: 'green', block: 'red', unblock: 'cyan', cancel: 'dim', update: 'white', delete: 'red', define: 'magenta', decompose: 'cyan' };
|
|
404
|
-
for (const e of entries) {
|
|
405
|
-
const time = new Date(e.ts).toLocaleString();
|
|
406
|
-
const colorFn = chalk[opColor[e.op]] || chalk.white;
|
|
407
|
-
const data = e.data ? chalk.dim(` ${JSON.stringify(e.data)}`) : '';
|
|
408
|
-
console.log(` ${chalk.dim(time)} ${colorFn(e.op.padEnd(10))} ${e.actor.padEnd(15)} ${e.target}${data}`);
|
|
409
|
-
}
|
|
410
|
-
} catch (err) {
|
|
411
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
412
|
-
process.exit(1);
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
ledgerCmd
|
|
417
|
-
.command('history <path>')
|
|
418
|
-
.description('Show full history of a specific file')
|
|
419
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
420
|
-
.action(async (targetPath, opts) => {
|
|
421
|
-
try {
|
|
422
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
423
|
-
const { ledger } = await import('../dist/workgraph/index.js');
|
|
424
|
-
const history = ledger.historyOf(vaultPath, targetPath);
|
|
425
|
-
|
|
426
|
-
if (history.length === 0) {
|
|
427
|
-
console.log(chalk.dim(`No history for ${targetPath}`));
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
console.log(chalk.bold(`History: ${targetPath}`));
|
|
432
|
-
console.log(chalk.dim('─'.repeat(50)));
|
|
433
|
-
for (const e of history) {
|
|
434
|
-
const time = new Date(e.ts).toLocaleString();
|
|
435
|
-
const data = e.data ? chalk.dim(` ${JSON.stringify(e.data)}`) : '';
|
|
436
|
-
console.log(` ${chalk.dim(time)} ${chalk.cyan(e.op)} by ${e.actor}${data}`);
|
|
437
|
-
}
|
|
438
|
-
} catch (err) {
|
|
439
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
440
|
-
process.exit(1);
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
ledgerCmd
|
|
445
|
-
.command('claims')
|
|
446
|
-
.description('Show all active claims')
|
|
447
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
448
|
-
.action(async (opts) => {
|
|
449
|
-
try {
|
|
450
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
451
|
-
const { ledger } = await import('../dist/workgraph/index.js');
|
|
452
|
-
const claims = ledger.allClaims(vaultPath);
|
|
453
|
-
|
|
454
|
-
if (claims.size === 0) {
|
|
455
|
-
console.log(chalk.dim('No active claims.'));
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
for (const [target, owner] of claims) {
|
|
460
|
-
console.log(` ${chalk.yellow(owner.padEnd(20))} → ${target}`);
|
|
461
|
-
}
|
|
462
|
-
console.log(chalk.dim(`\n${claims.size} active claim(s)`));
|
|
463
|
-
} catch (err) {
|
|
464
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
465
|
-
process.exit(1);
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function csv(value) {
|
|
471
|
-
if (!value) return undefined;
|
|
472
|
-
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
476
|
-
// Beautiful, agent-native 'wg' namespace
|
|
477
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
478
|
-
|
|
479
|
-
const BOX = {
|
|
480
|
-
topLeft: '╭',
|
|
481
|
-
topRight: '╮',
|
|
482
|
-
bottomLeft: '╰',
|
|
483
|
-
bottomRight: '╯',
|
|
484
|
-
horizontal: '─',
|
|
485
|
-
vertical: '│',
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
const PRIORITY_CONFIG = {
|
|
489
|
-
urgent: { symbol: '🔴', label: 'URGENT' },
|
|
490
|
-
high: { symbol: '🟠', label: 'HIGH' },
|
|
491
|
-
medium: { symbol: '🔵', label: 'MEDIUM' },
|
|
492
|
-
low: { symbol: '⚪', label: 'LOW' },
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
const STATUS_CONFIG = {
|
|
496
|
-
open: { symbol: '○', label: 'Open' },
|
|
497
|
-
active: { symbol: '●', label: 'Active' },
|
|
498
|
-
blocked: { symbol: '⊘', label: 'Blocked' },
|
|
499
|
-
done: { symbol: '✓', label: 'Done' },
|
|
500
|
-
cancelled: { symbol: '✗', label: 'Cancelled' },
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
const OP_COLORS = {
|
|
504
|
-
create: 'green',
|
|
505
|
-
update: 'blue',
|
|
506
|
-
delete: 'red',
|
|
507
|
-
claim: 'yellow',
|
|
508
|
-
release: 'cyan',
|
|
509
|
-
block: 'red',
|
|
510
|
-
unblock: 'green',
|
|
511
|
-
done: 'greenBright',
|
|
512
|
-
cancel: 'gray',
|
|
513
|
-
define: 'magenta',
|
|
514
|
-
decompose: 'cyan',
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
function getAgentName() {
|
|
518
|
-
return process.env.CLAWVAULT_AGENT || os.hostname();
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function formatRelativeTime(isoTimestamp) {
|
|
522
|
-
const now = Date.now();
|
|
523
|
-
const then = new Date(isoTimestamp).getTime();
|
|
524
|
-
const diffMs = now - then;
|
|
525
|
-
if (diffMs < 0) return 'just now';
|
|
526
|
-
const seconds = Math.floor(diffMs / 1000);
|
|
527
|
-
const minutes = Math.floor(seconds / 60);
|
|
528
|
-
const hours = Math.floor(minutes / 60);
|
|
529
|
-
const days = Math.floor(hours / 24);
|
|
530
|
-
const weeks = Math.floor(days / 7);
|
|
531
|
-
if (weeks > 0) return `${weeks}w ago`;
|
|
532
|
-
if (days > 0) return `${days}d ago`;
|
|
533
|
-
if (hours > 0) return `${hours}h ago`;
|
|
534
|
-
if (minutes > 0) return `${minutes}m ago`;
|
|
535
|
-
if (seconds > 0) return `${seconds}s ago`;
|
|
536
|
-
return 'just now';
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function truncate(str, maxLen) {
|
|
540
|
-
if (str.length <= maxLen) return str;
|
|
541
|
-
return str.slice(0, maxLen - 1) + '…';
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function stripAnsi(str) {
|
|
545
|
-
return str.replace(/\x1B\[[0-9;]*m/g, '');
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
function drawLine(width, title) {
|
|
549
|
-
if (!title) return BOX.horizontal.repeat(width);
|
|
550
|
-
const titlePadded = ` ${title} `;
|
|
551
|
-
const remaining = width - titlePadded.length - 2;
|
|
552
|
-
const left = Math.floor(remaining / 2);
|
|
553
|
-
const right = remaining - left;
|
|
554
|
-
return BOX.horizontal.repeat(left) + titlePadded + BOX.horizontal.repeat(right);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function drawBox(chalk, title, lines, width = 60) {
|
|
558
|
-
const innerWidth = width - 2;
|
|
559
|
-
const output = [];
|
|
560
|
-
output.push(BOX.topLeft + drawLine(innerWidth, title) + BOX.topRight);
|
|
561
|
-
for (const line of lines) {
|
|
562
|
-
const stripped = stripAnsi(line);
|
|
563
|
-
const padding = innerWidth - stripped.length;
|
|
564
|
-
output.push(BOX.vertical + line + ' '.repeat(Math.max(0, padding)) + BOX.vertical);
|
|
565
|
-
}
|
|
566
|
-
output.push(BOX.bottomLeft + BOX.horizontal.repeat(innerWidth) + BOX.bottomRight);
|
|
567
|
-
return output.join('\n');
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
function getGreeting(hour) {
|
|
571
|
-
if (hour < 12) return '☀️ Good morning';
|
|
572
|
-
if (hour < 17) return '🌤️ Good afternoon';
|
|
573
|
-
return '🌙 Good evening';
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
function sortByPriority(threads) {
|
|
577
|
-
const priorityOrder = { urgent: 0, high: 1, medium: 2, low: 3 };
|
|
578
|
-
return [...threads].sort((a, b) => {
|
|
579
|
-
const pa = priorityOrder[String(a.fields.priority || 'medium')] ?? 2;
|
|
580
|
-
const pb = priorityOrder[String(b.fields.priority || 'medium')] ?? 2;
|
|
581
|
-
return pa - pb;
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
function formatThreadLine(chalk, inst, showOwner = true) {
|
|
586
|
-
const status = inst.fields.status;
|
|
587
|
-
const priority = inst.fields.priority || 'medium';
|
|
588
|
-
const title = truncate(String(inst.fields.title || inst.path), 40);
|
|
589
|
-
const owner = inst.fields.owner;
|
|
590
|
-
const statusCfg = STATUS_CONFIG[status] || STATUS_CONFIG.open;
|
|
591
|
-
const priorityCfg = PRIORITY_CONFIG[priority] || PRIORITY_CONFIG.medium;
|
|
592
|
-
const statusColors = { open: 'cyan', active: 'green', blocked: 'red', done: 'gray', cancelled: 'dim' };
|
|
593
|
-
const priorityColors = { urgent: 'red', high: 'yellow', medium: 'blue', low: 'gray' };
|
|
594
|
-
const statusColor = chalk[statusColors[status]] || chalk.white;
|
|
595
|
-
let line = `${statusColor(statusCfg.symbol)} ${priorityCfg.symbol} ${chalk.white(title)}`;
|
|
596
|
-
if (showOwner && owner) {
|
|
597
|
-
line += chalk.dim(` @${owner}`);
|
|
598
|
-
}
|
|
599
|
-
return line;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
function formatPriority(chalk, priority) {
|
|
603
|
-
const cfg = PRIORITY_CONFIG[priority] || PRIORITY_CONFIG.medium;
|
|
604
|
-
const colors = { urgent: 'red', high: 'yellow', medium: 'blue', low: 'gray' };
|
|
605
|
-
const colorFn = chalk[colors[priority]] || chalk.blue;
|
|
606
|
-
return colorFn(`${cfg.symbol} ${cfg.label}`);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
function normalizeThreadPath(input) {
|
|
610
|
-
if (input.startsWith('threads/')) return input;
|
|
611
|
-
if (input.endsWith('.md')) return `threads/${input}`;
|
|
612
|
-
return `threads/${input}.md`;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
function formatError(chalk, what, why, fix) {
|
|
616
|
-
return [
|
|
617
|
-
'',
|
|
618
|
-
chalk.red.bold('✗ Error: ') + chalk.red(what),
|
|
619
|
-
'',
|
|
620
|
-
chalk.dim('Why: ') + why,
|
|
621
|
-
chalk.dim('Fix: ') + chalk.cyan(fix),
|
|
622
|
-
'',
|
|
623
|
-
].join('\n');
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
function registerWgCommands(program, { chalk, resolveVaultPath }) {
|
|
627
|
-
const wg = program
|
|
628
|
-
.command('wg')
|
|
629
|
-
.description('Workgraph — beautiful, agent-native multi-agent coordination');
|
|
630
|
-
|
|
631
|
-
// wg status
|
|
632
|
-
wg.command('status')
|
|
633
|
-
.description('Agent morning briefing with active work, available tasks, and team status')
|
|
634
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
635
|
-
.action(async (opts) => {
|
|
636
|
-
try {
|
|
637
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
638
|
-
const { store, ledger } = await import('../dist/workgraph/index.js');
|
|
639
|
-
const agentName = getAgentName();
|
|
640
|
-
const now = new Date();
|
|
641
|
-
const greeting = getGreeting(now.getHours());
|
|
642
|
-
|
|
643
|
-
console.log('');
|
|
644
|
-
console.log(chalk.bold.cyan(`${greeting}, ${agentName}!`));
|
|
645
|
-
console.log(chalk.dim(`${now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}`));
|
|
646
|
-
console.log('');
|
|
647
|
-
|
|
648
|
-
const allThreads = store.list(vaultPath, 'thread');
|
|
649
|
-
const activeThreads = allThreads.filter(t => t.fields.status === 'active');
|
|
650
|
-
const openThreads = allThreads.filter(t => t.fields.status === 'open');
|
|
651
|
-
const blockedThreads = allThreads.filter(t => t.fields.status === 'blocked');
|
|
652
|
-
const myActiveThreads = activeThreads.filter(t => t.fields.owner === agentName);
|
|
653
|
-
|
|
654
|
-
if (myActiveThreads.length > 0) {
|
|
655
|
-
const activeLines = myActiveThreads.map(t => formatThreadLine(chalk, t, false));
|
|
656
|
-
console.log(drawBox(chalk, '🔥 Your Active Work', activeLines, 65));
|
|
657
|
-
console.log('');
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
if (openThreads.length > 0) {
|
|
661
|
-
const sorted = sortByPriority(openThreads);
|
|
662
|
-
const availableLines = sorted.slice(0, 5).map(t => formatThreadLine(chalk, t, false));
|
|
663
|
-
if (sorted.length > 5) {
|
|
664
|
-
availableLines.push(chalk.dim(` ... and ${sorted.length - 5} more`));
|
|
665
|
-
}
|
|
666
|
-
console.log(drawBox(chalk, '📋 Available Work', availableLines, 65));
|
|
667
|
-
console.log('');
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
if (blockedThreads.length > 0) {
|
|
671
|
-
const blockedLines = blockedThreads.slice(0, 3).map(t => {
|
|
672
|
-
const title = truncate(String(t.fields.title || t.path), 35);
|
|
673
|
-
const deps = t.fields.deps || [];
|
|
674
|
-
const depStr = deps.length > 0 ? chalk.dim(` → ${deps[0]}`) : '';
|
|
675
|
-
const statusColor = chalk.red;
|
|
676
|
-
return `${statusColor(STATUS_CONFIG.blocked.symbol)} ${title}${depStr}`;
|
|
677
|
-
});
|
|
678
|
-
console.log(drawBox(chalk, '⛔ Blocked', blockedLines, 65));
|
|
679
|
-
console.log('');
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
const recentEntries = ledger.recent(vaultPath, 8);
|
|
683
|
-
if (recentEntries.length > 0) {
|
|
684
|
-
const activityLines = recentEntries.reverse().map(e => {
|
|
685
|
-
const opColorName = OP_COLORS[e.op] || 'white';
|
|
686
|
-
const opColor = chalk[opColorName] || chalk.white;
|
|
687
|
-
const target = truncate(path.basename(e.target, '.md'), 25);
|
|
688
|
-
const time = formatRelativeTime(e.ts);
|
|
689
|
-
return `${opColor(e.op.padEnd(8))} ${chalk.white(target)} ${chalk.dim(time)}`;
|
|
690
|
-
});
|
|
691
|
-
console.log(drawBox(chalk, '📜 Recent Activity', activityLines, 65));
|
|
692
|
-
console.log('');
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const claims = ledger.allClaims(vaultPath);
|
|
696
|
-
const teamMembers = new Map();
|
|
697
|
-
for (const [target, owner] of claims) {
|
|
698
|
-
const current = teamMembers.get(owner) || [];
|
|
699
|
-
current.push(target);
|
|
700
|
-
teamMembers.set(owner, current);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
if (teamMembers.size > 0) {
|
|
704
|
-
const teamLines = [];
|
|
705
|
-
for (const [member, threads] of teamMembers) {
|
|
706
|
-
const isYou = member === agentName;
|
|
707
|
-
const name = isYou ? chalk.green(`${member} (you)`) : chalk.white(member);
|
|
708
|
-
teamLines.push(`${chalk.cyan('●')} ${name}: ${chalk.dim(`${threads.length} active`)}`);
|
|
709
|
-
}
|
|
710
|
-
console.log(drawBox(chalk, '👥 Team Status', teamLines, 65));
|
|
711
|
-
console.log('');
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
const summaryParts = [
|
|
715
|
-
chalk.green(`${activeThreads.length} active`),
|
|
716
|
-
chalk.cyan(`${openThreads.length} open`),
|
|
717
|
-
chalk.red(`${blockedThreads.length} blocked`),
|
|
718
|
-
];
|
|
719
|
-
console.log(chalk.dim('Summary: ') + summaryParts.join(chalk.dim(' · ')));
|
|
720
|
-
console.log('');
|
|
721
|
-
} catch (err) {
|
|
722
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
723
|
-
process.exit(1);
|
|
724
|
-
}
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
// wg thread subcommands
|
|
728
|
-
const threadCmd = wg
|
|
729
|
-
.command('thread')
|
|
730
|
-
.description('Thread lifecycle operations');
|
|
731
|
-
|
|
732
|
-
threadCmd
|
|
733
|
-
.command('create <title>')
|
|
734
|
-
.description('Create a new thread')
|
|
735
|
-
.option('--goal <goal>', 'What success looks like')
|
|
736
|
-
.option('--priority <priority>', 'urgent | high | medium | low', 'medium')
|
|
737
|
-
.option('--deps <deps>', 'Comma-separated dependency paths')
|
|
738
|
-
.option('--tags <tags>', 'Comma-separated tags')
|
|
739
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
740
|
-
.action(async (title, opts) => {
|
|
741
|
-
try {
|
|
742
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
743
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
744
|
-
const agentName = getAgentName();
|
|
745
|
-
const goal = opts.goal || `Complete: ${title}`;
|
|
746
|
-
const priority = opts.priority || 'medium';
|
|
747
|
-
const deps = csv(opts.deps) || [];
|
|
748
|
-
const tags = csv(opts.tags) || [];
|
|
749
|
-
|
|
750
|
-
const inst = thread.createThread(vaultPath, title, goal, agentName, {
|
|
751
|
-
priority,
|
|
752
|
-
deps,
|
|
753
|
-
tags,
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
console.log('');
|
|
757
|
-
console.log(chalk.green.bold('✓ Thread created'));
|
|
758
|
-
console.log('');
|
|
759
|
-
console.log(chalk.dim(' Path: ') + chalk.white(inst.path));
|
|
760
|
-
console.log(chalk.dim(' Title: ') + chalk.white(inst.fields.title));
|
|
761
|
-
console.log(chalk.dim(' Goal: ') + chalk.white(inst.fields.goal));
|
|
762
|
-
console.log(chalk.dim(' Priority: ') + formatPriority(chalk, priority));
|
|
763
|
-
if (deps.length > 0) {
|
|
764
|
-
console.log(chalk.dim(' Deps: ') + chalk.cyan(deps.join(', ')));
|
|
765
|
-
}
|
|
766
|
-
if (tags.length > 0) {
|
|
767
|
-
console.log(chalk.dim(' Tags: ') + chalk.magenta(tags.join(', ')));
|
|
768
|
-
}
|
|
769
|
-
console.log('');
|
|
770
|
-
console.log(chalk.dim(`Claim it: ${chalk.cyan(`clawvault wg thread claim ${inst.path}`)}`));
|
|
771
|
-
console.log('');
|
|
772
|
-
} catch (err) {
|
|
773
|
-
console.error(formatError(chalk,
|
|
774
|
-
'Failed to create thread',
|
|
775
|
-
err.message,
|
|
776
|
-
'Check the title is unique and vault path is correct'
|
|
777
|
-
));
|
|
778
|
-
process.exit(1);
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
threadCmd
|
|
783
|
-
.command('list')
|
|
784
|
-
.description('List threads with optional filters')
|
|
785
|
-
.option('--status <status>', 'Filter by status: open | active | blocked | done | cancelled')
|
|
786
|
-
.option('--owner <owner>', 'Filter by owner (use "me" for current agent)')
|
|
787
|
-
.option('--json', 'Output as JSON')
|
|
788
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
789
|
-
.action(async (opts) => {
|
|
790
|
-
try {
|
|
791
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
792
|
-
const { store } = await import('../dist/workgraph/index.js');
|
|
793
|
-
let threads = store.list(vaultPath, 'thread');
|
|
794
|
-
|
|
795
|
-
if (opts.status) {
|
|
796
|
-
threads = threads.filter(t => t.fields.status === opts.status);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
if (opts.owner) {
|
|
800
|
-
const ownerFilter = opts.owner === 'me' ? getAgentName() : opts.owner;
|
|
801
|
-
threads = threads.filter(t => t.fields.owner === ownerFilter);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
if (opts.json) {
|
|
805
|
-
console.log(JSON.stringify(threads, null, 2));
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (threads.length === 0) {
|
|
810
|
-
console.log('');
|
|
811
|
-
console.log(chalk.dim('No threads found matching filters.'));
|
|
812
|
-
console.log('');
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
console.log('');
|
|
817
|
-
console.log(chalk.bold(`Threads (${threads.length})`));
|
|
818
|
-
console.log(chalk.dim('─'.repeat(70)));
|
|
819
|
-
|
|
820
|
-
const sorted = sortByPriority(threads);
|
|
821
|
-
for (const t of sorted) {
|
|
822
|
-
const status = t.fields.status;
|
|
823
|
-
const priority = t.fields.priority || 'medium';
|
|
824
|
-
const title = truncate(String(t.fields.title || t.path), 35);
|
|
825
|
-
const owner = t.fields.owner;
|
|
826
|
-
const updated = formatRelativeTime(String(t.fields.updated));
|
|
827
|
-
|
|
828
|
-
const statusCfg = STATUS_CONFIG[status] || STATUS_CONFIG.open;
|
|
829
|
-
const priorityCfg = PRIORITY_CONFIG[priority] || PRIORITY_CONFIG.medium;
|
|
830
|
-
const statusColors = { open: 'cyan', active: 'green', blocked: 'red', done: 'gray', cancelled: 'dim' };
|
|
831
|
-
const statusColor = chalk[statusColors[status]] || chalk.white;
|
|
832
|
-
|
|
833
|
-
let line = `${statusColor(statusCfg.symbol.padEnd(2))}`;
|
|
834
|
-
line += `${priorityCfg.symbol} `;
|
|
835
|
-
line += chalk.white(title.padEnd(37));
|
|
836
|
-
line += owner ? chalk.cyan(`@${owner}`.padEnd(15)) : ' '.repeat(15);
|
|
837
|
-
line += chalk.dim(updated);
|
|
838
|
-
|
|
839
|
-
console.log(line);
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
console.log(chalk.dim('─'.repeat(70)));
|
|
843
|
-
console.log('');
|
|
844
|
-
} catch (err) {
|
|
845
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
846
|
-
process.exit(1);
|
|
847
|
-
}
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
threadCmd
|
|
851
|
-
.command('claim <path>')
|
|
852
|
-
.description('Claim a thread and show work brief')
|
|
853
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
854
|
-
.action(async (threadPath, opts) => {
|
|
855
|
-
try {
|
|
856
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
857
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
858
|
-
const agentName = getAgentName();
|
|
859
|
-
const normalizedPath = normalizeThreadPath(threadPath);
|
|
860
|
-
|
|
861
|
-
const inst = thread.claim(vaultPath, normalizedPath, agentName);
|
|
862
|
-
|
|
863
|
-
console.log('');
|
|
864
|
-
console.log(chalk.green.bold('✓ Thread claimed'));
|
|
865
|
-
console.log('');
|
|
866
|
-
|
|
867
|
-
const briefLines = [
|
|
868
|
-
chalk.dim('Title: ') + chalk.white.bold(inst.fields.title),
|
|
869
|
-
chalk.dim('Goal: ') + chalk.white(inst.fields.goal),
|
|
870
|
-
chalk.dim('Priority: ') + formatPriority(chalk, String(inst.fields.priority || 'medium')),
|
|
871
|
-
];
|
|
872
|
-
|
|
873
|
-
const deps = inst.fields.deps || [];
|
|
874
|
-
if (deps.length > 0) {
|
|
875
|
-
briefLines.push(chalk.dim('Deps: ') + chalk.cyan(deps.join(', ')));
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
const contextRefs = inst.fields.context_refs || [];
|
|
879
|
-
if (contextRefs.length > 0) {
|
|
880
|
-
briefLines.push(chalk.dim('Context: ') + chalk.magenta(contextRefs.join(', ')));
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
console.log(drawBox(chalk, '📋 Work Brief', briefLines, 65));
|
|
884
|
-
console.log('');
|
|
885
|
-
|
|
886
|
-
if (inst.body && inst.body.trim()) {
|
|
887
|
-
console.log(chalk.dim('─'.repeat(65)));
|
|
888
|
-
console.log(chalk.dim('Notes:'));
|
|
889
|
-
console.log(inst.body.trim().split('\n').slice(0, 10).join('\n'));
|
|
890
|
-
console.log(chalk.dim('─'.repeat(65)));
|
|
891
|
-
console.log('');
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
console.log(chalk.dim(`When done: ${chalk.cyan(`clawvault wg thread done ${normalizedPath}`)}`));
|
|
895
|
-
console.log('');
|
|
896
|
-
} catch (err) {
|
|
897
|
-
console.error(formatError(chalk,
|
|
898
|
-
'Failed to claim thread',
|
|
899
|
-
err.message,
|
|
900
|
-
'Ensure the thread exists and is in "open" status'
|
|
901
|
-
));
|
|
902
|
-
process.exit(1);
|
|
903
|
-
}
|
|
904
|
-
});
|
|
905
|
-
|
|
906
|
-
threadCmd
|
|
907
|
-
.command('done <path>')
|
|
908
|
-
.description('Mark thread as complete')
|
|
909
|
-
.option('--output <output>', 'Completion summary or output')
|
|
910
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
911
|
-
.action(async (threadPath, opts) => {
|
|
912
|
-
try {
|
|
913
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
914
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
915
|
-
const agentName = getAgentName();
|
|
916
|
-
const normalizedPath = normalizeThreadPath(threadPath);
|
|
917
|
-
|
|
918
|
-
const inst = thread.done(vaultPath, normalizedPath, agentName, opts.output);
|
|
919
|
-
|
|
920
|
-
console.log('');
|
|
921
|
-
console.log(chalk.green.bold('✓ Thread completed!'));
|
|
922
|
-
console.log('');
|
|
923
|
-
console.log(chalk.dim(' Title: ') + chalk.white(inst.fields.title));
|
|
924
|
-
console.log(chalk.dim(' Status: ') + chalk.green('done'));
|
|
925
|
-
if (opts.output) {
|
|
926
|
-
console.log(chalk.dim(' Output: ') + chalk.white(truncate(opts.output, 50)));
|
|
927
|
-
}
|
|
928
|
-
console.log('');
|
|
929
|
-
console.log(chalk.dim('Great work! 🎉'));
|
|
930
|
-
console.log('');
|
|
931
|
-
} catch (err) {
|
|
932
|
-
console.error(formatError(chalk,
|
|
933
|
-
'Failed to complete thread',
|
|
934
|
-
err.message,
|
|
935
|
-
'Ensure you own the thread and it is in "active" status'
|
|
936
|
-
));
|
|
937
|
-
process.exit(1);
|
|
938
|
-
}
|
|
939
|
-
});
|
|
940
|
-
|
|
941
|
-
threadCmd
|
|
942
|
-
.command('block <path>')
|
|
943
|
-
.description('Block thread on a dependency')
|
|
944
|
-
.requiredOption('--by <blocker>', 'What is blocking this thread')
|
|
945
|
-
.option('--reason <reason>', 'Additional context')
|
|
946
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
947
|
-
.action(async (threadPath, opts) => {
|
|
948
|
-
try {
|
|
949
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
950
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
951
|
-
const agentName = getAgentName();
|
|
952
|
-
const normalizedPath = normalizeThreadPath(threadPath);
|
|
953
|
-
|
|
954
|
-
const inst = thread.block(vaultPath, normalizedPath, agentName, opts.by, opts.reason);
|
|
955
|
-
|
|
956
|
-
console.log('');
|
|
957
|
-
console.log(chalk.yellow.bold('⊘ Thread blocked'));
|
|
958
|
-
console.log('');
|
|
959
|
-
console.log(chalk.dim(' Title: ') + chalk.white(inst.fields.title));
|
|
960
|
-
console.log(chalk.dim(' Blocked by: ') + chalk.red(opts.by));
|
|
961
|
-
if (opts.reason) {
|
|
962
|
-
console.log(chalk.dim(' Reason: ') + chalk.white(opts.reason));
|
|
963
|
-
}
|
|
964
|
-
console.log('');
|
|
965
|
-
} catch (err) {
|
|
966
|
-
console.error(formatError(chalk,
|
|
967
|
-
'Failed to block thread',
|
|
968
|
-
err.message,
|
|
969
|
-
'Ensure the thread exists and is in "active" status'
|
|
970
|
-
));
|
|
971
|
-
process.exit(1);
|
|
972
|
-
}
|
|
973
|
-
});
|
|
974
|
-
|
|
975
|
-
threadCmd
|
|
976
|
-
.command('release <path>')
|
|
977
|
-
.description('Release thread back to the pool')
|
|
978
|
-
.option('--reason <reason>', 'Why releasing')
|
|
979
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
980
|
-
.action(async (threadPath, opts) => {
|
|
981
|
-
try {
|
|
982
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
983
|
-
const { thread } = await import('../dist/workgraph/index.js');
|
|
984
|
-
const agentName = getAgentName();
|
|
985
|
-
const normalizedPath = normalizeThreadPath(threadPath);
|
|
986
|
-
|
|
987
|
-
const inst = thread.release(vaultPath, normalizedPath, agentName, opts.reason);
|
|
988
|
-
|
|
989
|
-
console.log('');
|
|
990
|
-
console.log(chalk.cyan.bold('↩ Thread released'));
|
|
991
|
-
console.log('');
|
|
992
|
-
console.log(chalk.dim(' Title: ') + chalk.white(inst.fields.title));
|
|
993
|
-
console.log(chalk.dim(' Status: ') + chalk.cyan('open'));
|
|
994
|
-
if (opts.reason) {
|
|
995
|
-
console.log(chalk.dim(' Reason: ') + chalk.white(opts.reason));
|
|
996
|
-
}
|
|
997
|
-
console.log('');
|
|
998
|
-
console.log(chalk.dim('Thread is now available for others to claim.'));
|
|
999
|
-
console.log('');
|
|
1000
|
-
} catch (err) {
|
|
1001
|
-
console.error(formatError(chalk,
|
|
1002
|
-
'Failed to release thread',
|
|
1003
|
-
err.message,
|
|
1004
|
-
'Ensure you own the thread'
|
|
1005
|
-
));
|
|
1006
|
-
process.exit(1);
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
threadCmd
|
|
1011
|
-
.command('decompose <path>')
|
|
1012
|
-
.description('Break thread into sub-threads')
|
|
1013
|
-
.option('--into <titles...>', 'Sub-thread titles')
|
|
1014
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1015
|
-
.action(async (threadPath, opts) => {
|
|
1016
|
-
try {
|
|
1017
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
1018
|
-
const { thread, store } = await import('../dist/workgraph/index.js');
|
|
1019
|
-
const agentName = getAgentName();
|
|
1020
|
-
const normalizedPath = normalizeThreadPath(threadPath);
|
|
1021
|
-
|
|
1022
|
-
if (!opts.into || opts.into.length === 0) {
|
|
1023
|
-
console.error(formatError(chalk,
|
|
1024
|
-
'Missing --into option',
|
|
1025
|
-
'You must specify sub-thread titles',
|
|
1026
|
-
'clawvault wg thread decompose <path> --into "sub1" --into "sub2"'
|
|
1027
|
-
));
|
|
1028
|
-
process.exit(1);
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
const parent = store.read(vaultPath, normalizedPath);
|
|
1032
|
-
if (!parent) {
|
|
1033
|
-
throw new Error(`Thread not found: ${normalizedPath}`);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
const subthreads = opts.into.map(title => ({
|
|
1037
|
-
title,
|
|
1038
|
-
goal: `Sub-task of: ${parent.fields.title}`,
|
|
1039
|
-
}));
|
|
1040
|
-
|
|
1041
|
-
const created = thread.decompose(vaultPath, normalizedPath, subthreads, agentName);
|
|
1042
|
-
|
|
1043
|
-
console.log('');
|
|
1044
|
-
console.log(chalk.green.bold('✓ Thread decomposed'));
|
|
1045
|
-
console.log('');
|
|
1046
|
-
console.log(chalk.dim(' Parent: ') + chalk.white(parent.fields.title));
|
|
1047
|
-
console.log(chalk.dim(' Created sub-threads:'));
|
|
1048
|
-
for (const sub of created) {
|
|
1049
|
-
console.log(chalk.cyan(` → ${sub.fields.title}`));
|
|
1050
|
-
}
|
|
1051
|
-
console.log('');
|
|
1052
|
-
} catch (err) {
|
|
1053
|
-
console.error(formatError(chalk,
|
|
1054
|
-
'Failed to decompose thread',
|
|
1055
|
-
err.message,
|
|
1056
|
-
'Ensure the thread exists'
|
|
1057
|
-
));
|
|
1058
|
-
process.exit(1);
|
|
1059
|
-
}
|
|
1060
|
-
});
|
|
1061
|
-
|
|
1062
|
-
// wg ledger
|
|
1063
|
-
wg.command('ledger')
|
|
1064
|
-
.description('View coordination history')
|
|
1065
|
-
.option('--last <n>', 'Number of entries to show', '20')
|
|
1066
|
-
.option('--actor <actor>', 'Filter by actor (use "me" for current agent)')
|
|
1067
|
-
.option('--target <target>', 'Filter by target path substring')
|
|
1068
|
-
.option('--json', 'Output as JSON')
|
|
1069
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1070
|
-
.action(async (opts) => {
|
|
1071
|
-
try {
|
|
1072
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
1073
|
-
const { ledger } = await import('../dist/workgraph/index.js');
|
|
1074
|
-
let entries = ledger.readAll(vaultPath);
|
|
1075
|
-
|
|
1076
|
-
if (opts.actor) {
|
|
1077
|
-
const actorFilter = opts.actor === 'me' ? getAgentName() : opts.actor;
|
|
1078
|
-
entries = entries.filter(e => e.actor === actorFilter);
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
if (opts.target) {
|
|
1082
|
-
entries = entries.filter(e => e.target.includes(opts.target));
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
const limit = parseInt(opts.last) || 20;
|
|
1086
|
-
entries = entries.slice(-limit);
|
|
1087
|
-
|
|
1088
|
-
if (opts.json) {
|
|
1089
|
-
console.log(JSON.stringify(entries, null, 2));
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
if (entries.length === 0) {
|
|
1094
|
-
console.log('');
|
|
1095
|
-
console.log(chalk.dim('No ledger entries found.'));
|
|
1096
|
-
console.log('');
|
|
1097
|
-
return;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
console.log('');
|
|
1101
|
-
console.log(chalk.bold(`Ledger (last ${entries.length} entries)`));
|
|
1102
|
-
console.log(chalk.dim('─'.repeat(80)));
|
|
1103
|
-
|
|
1104
|
-
for (const entry of entries.reverse()) {
|
|
1105
|
-
const opColorName = OP_COLORS[entry.op] || 'white';
|
|
1106
|
-
const opColor = chalk[opColorName] || chalk.white;
|
|
1107
|
-
const time = formatRelativeTime(entry.ts);
|
|
1108
|
-
const target = truncate(entry.target, 30);
|
|
1109
|
-
const actor = entry.actor;
|
|
1110
|
-
|
|
1111
|
-
let line = chalk.dim(time.padEnd(10));
|
|
1112
|
-
line += opColor(entry.op.toUpperCase().padEnd(10));
|
|
1113
|
-
line += chalk.white(target.padEnd(32));
|
|
1114
|
-
line += chalk.cyan(`@${actor}`);
|
|
1115
|
-
|
|
1116
|
-
console.log(line);
|
|
1117
|
-
|
|
1118
|
-
if (entry.data && Object.keys(entry.data).length > 0) {
|
|
1119
|
-
const dataStr = JSON.stringify(entry.data);
|
|
1120
|
-
console.log(chalk.dim(` ${truncate(dataStr, 68)}`));
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
console.log(chalk.dim('─'.repeat(80)));
|
|
1125
|
-
console.log('');
|
|
1126
|
-
} catch (err) {
|
|
1127
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1128
|
-
process.exit(1);
|
|
1129
|
-
}
|
|
1130
|
-
});
|
|
1131
|
-
|
|
1132
|
-
// wg define
|
|
1133
|
-
wg.command('define <type>')
|
|
1134
|
-
.description('Define a new primitive type')
|
|
1135
|
-
.option('--fields <fields>', 'Comma-separated field definitions (name:type)')
|
|
1136
|
-
.option('--dir <directory>', 'Custom directory for instances')
|
|
1137
|
-
.option('--description <desc>', 'Type description')
|
|
1138
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1139
|
-
.action(async (typeName, opts) => {
|
|
1140
|
-
try {
|
|
1141
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
1142
|
-
const { registry } = await import('../dist/workgraph/index.js');
|
|
1143
|
-
const agentName = getAgentName();
|
|
1144
|
-
const description = opts.description || `Custom type: ${typeName}`;
|
|
1145
|
-
|
|
1146
|
-
const fields = {};
|
|
1147
|
-
if (opts.fields) {
|
|
1148
|
-
const fieldPairs = opts.fields.split(',');
|
|
1149
|
-
for (const pair of fieldPairs) {
|
|
1150
|
-
const [name, type] = pair.split(':').map(s => s.trim());
|
|
1151
|
-
if (name && type) {
|
|
1152
|
-
fields[name] = { type };
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
const typeDef = registry.defineType(
|
|
1158
|
-
vaultPath,
|
|
1159
|
-
typeName,
|
|
1160
|
-
description,
|
|
1161
|
-
fields,
|
|
1162
|
-
agentName,
|
|
1163
|
-
opts.dir
|
|
1164
|
-
);
|
|
1165
|
-
|
|
1166
|
-
console.log('');
|
|
1167
|
-
console.log(chalk.green.bold('✓ Type defined'));
|
|
1168
|
-
console.log('');
|
|
1169
|
-
console.log(chalk.dim(' Name: ') + chalk.magenta(typeDef.name));
|
|
1170
|
-
console.log(chalk.dim(' Directory: ') + chalk.white(typeDef.directory));
|
|
1171
|
-
console.log(chalk.dim(' Fields:'));
|
|
1172
|
-
for (const [fieldName, fieldDef] of Object.entries(typeDef.fields)) {
|
|
1173
|
-
const required = fieldDef.required ? chalk.red('*') : ' ';
|
|
1174
|
-
console.log(chalk.dim(` ${required} ${fieldName}: ${fieldDef.type}`));
|
|
1175
|
-
}
|
|
1176
|
-
console.log('');
|
|
1177
|
-
console.log(chalk.dim(`Create instances: ${chalk.cyan(`clawvault wg create ${typeDef.name} "title"`)}`));
|
|
1178
|
-
console.log('');
|
|
1179
|
-
} catch (err) {
|
|
1180
|
-
console.error(formatError(chalk,
|
|
1181
|
-
'Failed to define type',
|
|
1182
|
-
err.message,
|
|
1183
|
-
'Ensure the type name is unique and not a built-in type'
|
|
1184
|
-
));
|
|
1185
|
-
process.exit(1);
|
|
1186
|
-
}
|
|
1187
|
-
});
|
|
1188
|
-
|
|
1189
|
-
// wg types
|
|
1190
|
-
wg.command('types')
|
|
1191
|
-
.description('List all primitive types with their fields')
|
|
1192
|
-
.option('--json', 'Output as JSON')
|
|
1193
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1194
|
-
.action(async (opts) => {
|
|
1195
|
-
try {
|
|
1196
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
1197
|
-
const { registry } = await import('../dist/workgraph/index.js');
|
|
1198
|
-
const types = registry.listTypes(vaultPath);
|
|
1199
|
-
|
|
1200
|
-
if (opts.json) {
|
|
1201
|
-
console.log(JSON.stringify(types, null, 2));
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
console.log('');
|
|
1206
|
-
console.log(chalk.bold(`Primitive Types (${types.length})`));
|
|
1207
|
-
console.log(chalk.dim('─'.repeat(70)));
|
|
1208
|
-
|
|
1209
|
-
for (const typeDef of types) {
|
|
1210
|
-
const builtInBadge = typeDef.builtIn ? chalk.cyan(' [built-in]') : chalk.magenta(' [custom]');
|
|
1211
|
-
console.log('');
|
|
1212
|
-
console.log(chalk.white.bold(typeDef.name) + builtInBadge);
|
|
1213
|
-
console.log(chalk.dim(` ${typeDef.description}`));
|
|
1214
|
-
console.log(chalk.dim(` Directory: ${typeDef.directory}/`));
|
|
1215
|
-
console.log(chalk.dim(' Fields:'));
|
|
1216
|
-
|
|
1217
|
-
const fieldEntries = Object.entries(typeDef.fields);
|
|
1218
|
-
for (const [fieldName, fieldDef] of fieldEntries) {
|
|
1219
|
-
const required = fieldDef.required ? chalk.red('*') : ' ';
|
|
1220
|
-
const defaultVal = fieldDef.default !== undefined ? chalk.dim(` = ${JSON.stringify(fieldDef.default)}`) : '';
|
|
1221
|
-
const desc = fieldDef.description ? chalk.dim(` — ${fieldDef.description}`) : '';
|
|
1222
|
-
console.log(` ${required} ${chalk.cyan(fieldName)}: ${fieldDef.type}${defaultVal}${desc}`);
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
console.log('');
|
|
1227
|
-
console.log(chalk.dim('─'.repeat(70)));
|
|
1228
|
-
console.log('');
|
|
1229
|
-
} catch (err) {
|
|
1230
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1231
|
-
process.exit(1);
|
|
1232
|
-
}
|
|
1233
|
-
});
|
|
1234
|
-
|
|
1235
|
-
// wg create
|
|
1236
|
-
wg.command('create <type> <title>')
|
|
1237
|
-
.description('Create any primitive instance')
|
|
1238
|
-
.option('--body <body>', 'Markdown body content')
|
|
1239
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1240
|
-
.allowUnknownOption(true)
|
|
1241
|
-
.action(async (typeName, title, opts) => {
|
|
1242
|
-
try {
|
|
1243
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
1244
|
-
const { store } = await import('../dist/workgraph/index.js');
|
|
1245
|
-
const agentName = getAgentName();
|
|
1246
|
-
const body = opts.body || '';
|
|
1247
|
-
|
|
1248
|
-
const fields = { title };
|
|
1249
|
-
const knownOptions = new Set(['body', 'vault']);
|
|
1250
|
-
for (const [key, value] of Object.entries(opts)) {
|
|
1251
|
-
if (!knownOptions.has(key) && value !== undefined) {
|
|
1252
|
-
fields[key] = value;
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
const inst = store.create(vaultPath, typeName, fields, body, agentName);
|
|
1257
|
-
|
|
1258
|
-
console.log('');
|
|
1259
|
-
console.log(chalk.green.bold(`✓ ${typeName} created`));
|
|
1260
|
-
console.log('');
|
|
1261
|
-
console.log(chalk.dim(' Path: ') + chalk.white(inst.path));
|
|
1262
|
-
console.log(chalk.dim(' Title: ') + chalk.white(inst.fields.title));
|
|
1263
|
-
console.log('');
|
|
1264
|
-
} catch (err) {
|
|
1265
|
-
console.error(formatError(chalk,
|
|
1266
|
-
`Failed to create ${typeName}`,
|
|
1267
|
-
err.message,
|
|
1268
|
-
`Ensure the type "${typeName}" exists. Run: clawvault wg types`
|
|
1269
|
-
));
|
|
1270
|
-
process.exit(1);
|
|
1271
|
-
}
|
|
1272
|
-
});
|
|
1273
|
-
|
|
1274
|
-
// wg board
|
|
1275
|
-
wg.command('board')
|
|
1276
|
-
.description('Terminal kanban board view')
|
|
1277
|
-
.option('--width <width>', 'Terminal width override')
|
|
1278
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
1279
|
-
.action(async (opts) => {
|
|
1280
|
-
try {
|
|
1281
|
-
const vaultPath = resolveVaultPath(opts.vault);
|
|
1282
|
-
const { store } = await import('../dist/workgraph/index.js');
|
|
1283
|
-
const threads = store.list(vaultPath, 'thread');
|
|
1284
|
-
const termWidth = opts.width ? parseInt(opts.width) : (process.stdout.columns || 120);
|
|
1285
|
-
|
|
1286
|
-
const columns = {
|
|
1287
|
-
open: [],
|
|
1288
|
-
active: [],
|
|
1289
|
-
blocked: [],
|
|
1290
|
-
done: [],
|
|
1291
|
-
cancelled: [],
|
|
1292
|
-
};
|
|
1293
|
-
|
|
1294
|
-
for (const t of threads) {
|
|
1295
|
-
const status = t.fields.status;
|
|
1296
|
-
if (columns[status]) {
|
|
1297
|
-
columns[status].push(t);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
for (const status of Object.keys(columns)) {
|
|
1302
|
-
columns[status] = sortByPriority(columns[status]);
|
|
1303
|
-
}
|
|
1304
|
-
|
|
1305
|
-
const visibleStatuses = ['open', 'active', 'blocked', 'done'];
|
|
1306
|
-
const colWidth = Math.floor((termWidth - visibleStatuses.length - 1) / visibleStatuses.length);
|
|
1307
|
-
const cardWidth = colWidth - 4;
|
|
1308
|
-
|
|
1309
|
-
console.log('');
|
|
1310
|
-
console.log(chalk.bold.cyan('╔' + '═'.repeat(termWidth - 2) + '╗'));
|
|
1311
|
-
console.log(chalk.bold.cyan('║') + chalk.bold(' WORKGRAPH BOARD').padEnd(termWidth - 2) + chalk.bold.cyan('║'));
|
|
1312
|
-
console.log(chalk.bold.cyan('╚' + '═'.repeat(termWidth - 2) + '╝'));
|
|
1313
|
-
console.log('');
|
|
1314
|
-
|
|
1315
|
-
let headerLine = '';
|
|
1316
|
-
const statusColors = { open: 'cyan', active: 'green', blocked: 'red', done: 'gray', cancelled: 'dim' };
|
|
1317
|
-
for (const status of visibleStatuses) {
|
|
1318
|
-
const cfg = STATUS_CONFIG[status];
|
|
1319
|
-
const header = `${cfg.symbol} ${cfg.label} (${columns[status].length})`;
|
|
1320
|
-
const padded = header.padEnd(colWidth);
|
|
1321
|
-
const colorFn = chalk[statusColors[status]] || chalk.white;
|
|
1322
|
-
headerLine += colorFn(padded);
|
|
1323
|
-
}
|
|
1324
|
-
console.log(headerLine);
|
|
1325
|
-
console.log(chalk.dim('─'.repeat(termWidth)));
|
|
1326
|
-
|
|
1327
|
-
const maxRows = Math.max(...visibleStatuses.map(s => columns[s].length), 1);
|
|
1328
|
-
|
|
1329
|
-
for (let row = 0; row < Math.min(maxRows, 15); row++) {
|
|
1330
|
-
let line = '';
|
|
1331
|
-
for (const status of visibleStatuses) {
|
|
1332
|
-
const t = columns[status][row];
|
|
1333
|
-
if (t) {
|
|
1334
|
-
const priority = t.fields.priority || 'medium';
|
|
1335
|
-
const title = truncate(String(t.fields.title || t.path), cardWidth - 4);
|
|
1336
|
-
const owner = t.fields.owner;
|
|
1337
|
-
const priorityCfg = PRIORITY_CONFIG[priority] || PRIORITY_CONFIG.medium;
|
|
1338
|
-
|
|
1339
|
-
let card = `${priorityCfg.symbol} ${title}`;
|
|
1340
|
-
if (owner) {
|
|
1341
|
-
card += chalk.dim(` @${truncate(owner, 8)}`);
|
|
1342
|
-
}
|
|
1343
|
-
line += card.padEnd(colWidth);
|
|
1344
|
-
} else {
|
|
1345
|
-
line += ' '.repeat(colWidth);
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
console.log(line);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
if (maxRows > 15) {
|
|
1352
|
-
console.log(chalk.dim(`... and ${maxRows - 15} more rows`));
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
console.log('');
|
|
1356
|
-
console.log(chalk.dim('─'.repeat(termWidth)));
|
|
1357
|
-
|
|
1358
|
-
const legendParts = Object.entries(PRIORITY_CONFIG).map(([key, cfg]) =>
|
|
1359
|
-
`${cfg.symbol} ${cfg.label}`
|
|
1360
|
-
);
|
|
1361
|
-
console.log(chalk.dim('Priority: ') + legendParts.join(chalk.dim(' · ')));
|
|
1362
|
-
console.log('');
|
|
1363
|
-
} catch (err) {
|
|
1364
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
1365
|
-
process.exit(1);
|
|
1366
|
-
}
|
|
1367
|
-
});
|
|
1368
|
-
}
|