clawvault 2.4.6 → 2.5.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.
Files changed (69) hide show
  1. package/bin/clawvault.js +5 -0
  2. package/bin/command-registration.test.js +1 -1
  3. package/bin/help-contract.test.js +1 -0
  4. package/bin/register-config-route-commands.test.js +8 -1
  5. package/bin/register-core-commands.js +3 -3
  6. package/bin/register-project-commands.js +209 -0
  7. package/bin/register-project-commands.test.js +201 -0
  8. package/bin/register-query-commands.js +40 -0
  9. package/bin/register-task-commands.js +2 -18
  10. package/bin/register-task-commands.test.js +3 -4
  11. package/bin/test-helpers/cli-command-fixtures.js +5 -0
  12. package/dist/{chunk-3PJIGGWV.js → chunk-2CDEETQN.js} +1 -0
  13. package/dist/{chunk-FD2ZA65C.js → chunk-2RK2AG32.js} +5 -5
  14. package/dist/chunk-5GZFTAL7.js +340 -0
  15. package/dist/{chunk-P2ZH6AN5.js → chunk-6RQPD7X6.js} +3 -4
  16. package/dist/{chunk-HNMFXFYP.js → chunk-7OHQFMJK.js} +2 -1
  17. package/dist/{chunk-FKQJB6XC.js → chunk-C3PF7WBA.js} +2 -2
  18. package/dist/{chunk-JXY6T5R7.js → chunk-FW465EEA.js} +1 -1
  19. package/dist/{chunk-BI6SGGZP.js → chunk-G3OQJ2NQ.js} +1 -1
  20. package/dist/chunk-GSD4ALSI.js +724 -0
  21. package/dist/{chunk-6QLRSPLZ.js → chunk-IOALNTAN.js} +268 -47
  22. package/dist/chunk-ITPEXLHA.js +528 -0
  23. package/dist/{chunk-LLN5SPGL.js → chunk-J5EMBUPK.js} +1 -1
  24. package/dist/chunk-K3CDT7IH.js +122 -0
  25. package/dist/{chunk-AHGUJG76.js → chunk-KCCHROBR.js} +13 -69
  26. package/dist/{chunk-JTO7NZLS.js → chunk-LMCC5OC7.js} +2 -2
  27. package/dist/{chunk-QALB2V3E.js → chunk-MQUJNOHK.js} +1 -1
  28. package/dist/{chunk-H6WQUUNK.js → chunk-TMZMN7OS.js} +334 -457
  29. package/dist/{chunk-HVTTYDCJ.js → chunk-VR5NE7PZ.js} +1 -1
  30. package/dist/{chunk-22WE3J4F.js → chunk-WIICLBNF.js} +35 -4
  31. package/dist/chunk-YCVDVI5B.js +273 -0
  32. package/dist/{chunk-NAMFB7ZA.js → chunk-Z2XBWN7A.js} +0 -2
  33. package/dist/commands/archive.js +3 -3
  34. package/dist/commands/backlog.js +1 -1
  35. package/dist/commands/blocked.js +1 -1
  36. package/dist/commands/canvas.d.ts +1 -14
  37. package/dist/commands/canvas.js +123 -1543
  38. package/dist/commands/context.js +5 -6
  39. package/dist/commands/doctor.js +2 -2
  40. package/dist/commands/inject.d.ts +2 -0
  41. package/dist/commands/inject.js +14 -0
  42. package/dist/commands/kanban.js +2 -2
  43. package/dist/commands/migrate-observations.js +2 -2
  44. package/dist/commands/observe.js +8 -6
  45. package/dist/commands/project.d.ts +85 -0
  46. package/dist/commands/project.js +411 -0
  47. package/dist/commands/rebuild.js +7 -5
  48. package/dist/commands/reflect.js +5 -4
  49. package/dist/commands/replay.js +10 -7
  50. package/dist/commands/setup.d.ts +1 -1
  51. package/dist/commands/setup.js +2 -2
  52. package/dist/commands/sleep.d.ts +1 -1
  53. package/dist/commands/sleep.js +11 -8
  54. package/dist/commands/status.js +2 -2
  55. package/dist/commands/task.d.ts +2 -2
  56. package/dist/commands/task.js +11 -301
  57. package/dist/commands/wake.d.ts +1 -1
  58. package/dist/commands/wake.js +4 -4
  59. package/dist/index.d.ts +75 -107
  60. package/dist/index.js +78 -36
  61. package/dist/inject-x65KXWPk.d.ts +137 -0
  62. package/dist/lib/project-utils.d.ts +97 -0
  63. package/dist/lib/project-utils.js +19 -0
  64. package/dist/lib/task-utils.d.ts +8 -3
  65. package/dist/lib/task-utils.js +1 -1
  66. package/dist/{types-DMU3SuAV.d.ts → types-jjuYN2Xn.d.ts} +1 -1
  67. package/package.json +2 -2
  68. package/dist/chunk-L3DJ36BZ.js +0 -40
  69. package/dist/chunk-UMMCYTJV.js +0 -105
package/bin/clawvault.js CHANGED
@@ -19,6 +19,7 @@ import { registerVaultOperationsCommands } from './register-vault-operations-com
19
19
  import { registerConfigCommands } from './register-config-commands.js';
20
20
  import { registerRouteCommands } from './register-route-commands.js';
21
21
  import { registerKanbanCommands } from './register-kanban-commands.js';
22
+ import { registerProjectCommands } from './register-project-commands.js';
22
23
 
23
24
  import { registerTaskCommands } from './register-task-commands.js';
24
25
 
@@ -98,6 +99,10 @@ registerKanbanCommands(program, {
98
99
  chalk,
99
100
  resolveVaultPath
100
101
  });
102
+ registerProjectCommands(program, {
103
+ chalk,
104
+ resolveVaultPath
105
+ });
101
106
 
102
107
  registerTailscaleCommands(program, { chalk });
103
108
  registerConfigCommands(program, { chalk, resolveVaultPath });
@@ -49,7 +49,7 @@ describe('CLI command registration modules', () => {
49
49
  });
50
50
 
51
51
  const names = listCommandNames(program);
52
- expect(names).toEqual(expect.arrayContaining(['search', 'vsearch', 'context', 'observe', 'reflect', 'session-recap']));
52
+ expect(names).toEqual(expect.arrayContaining(['search', 'vsearch', 'context', 'inject', 'observe', 'reflect', 'session-recap']));
53
53
 
54
54
  const contextCommand = program.commands.find((command) => command.name() === 'context');
55
55
  const profileOption = contextCommand?.options.find((option) => option.flags.includes('--profile <profile>'));
@@ -6,6 +6,7 @@ describe('CLI help contract', () => {
6
6
  const help = registerAllCommandModules().helpInformation();
7
7
  expect(help).toContain('init');
8
8
  expect(help).toContain('context');
9
+ expect(help).toContain('inject');
9
10
  expect(help).toContain('compat');
10
11
  expect(help).toContain('graph');
11
12
  expect(help).toContain('reflect');
@@ -31,9 +31,16 @@ vi.mock('../dist/index.js', () => ({
31
31
  'theme',
32
32
  'observe.model',
33
33
  'observe.provider',
34
+ 'observer.compression.provider',
35
+ 'observer.compression.model',
36
+ 'observer.compression.baseUrl',
37
+ 'observer.compression.apiKey',
34
38
  'context.maxResults',
35
39
  'context.defaultProfile',
36
- 'graph.maxHops'
40
+ 'graph.maxHops',
41
+ 'inject.maxResults',
42
+ 'inject.useLlm',
43
+ 'inject.scope'
37
44
  ],
38
45
  getConfigValue: getConfigValueMock,
39
46
  setConfigValue: setConfigValueMock,
@@ -17,7 +17,7 @@ export function registerCoreCommands(
17
17
  .option('--no-tasks', 'Skip tasks/ and backlog/ directories')
18
18
  .option('--no-graph', 'Skip initial graph build')
19
19
  .option('--categories <list>', 'Comma-separated list of custom categories to create')
20
- .option('--canvas <template>', 'Generate a canvas dashboard on init (default, brain, project-board, sprint)')
20
+ .option('--canvas', 'Generate a vault status canvas dashboard on init')
21
21
  .option('--theme <style>', 'Graph color theme to apply (neural, minimal, none)', 'none')
22
22
  .option('--minimal', 'Create minimal vault (memory categories only, no tasks/bases/graph)')
23
23
  .action(async (vaultPath, options) => {
@@ -106,7 +106,7 @@ export function registerCoreCommands(
106
106
  await setupCommand({
107
107
  graphColors: false,
108
108
  bases: false,
109
- canvas: options.canvas === true ? 'default' : options.canvas,
109
+ canvas: true,
110
110
  theme: 'none',
111
111
  vault: resolvedPath
112
112
  });
@@ -138,7 +138,7 @@ export function registerCoreCommands(
138
138
  .option('--no-graph-colors', 'Skip graph color configuration')
139
139
  .option('--bases', 'Generate Obsidian Bases views for task management')
140
140
  .option('--no-bases', 'Skip Bases file generation')
141
- .option('--canvas [template]', 'Generate canvas dashboard (default, brain, project-board, sprint)')
141
+ .option('--canvas', 'Generate vault status canvas dashboard')
142
142
  .option('--no-canvas', 'Skip canvas generation')
143
143
  .option('--theme <style>', 'Graph color theme (neural, minimal, none)', 'neural')
144
144
  .option('--force', 'Overwrite existing configuration files')
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Project command registrations for ClawVault
3
+ * Registers project add/update/archive/list/show/tasks/board commands
4
+ */
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
+ export function registerProjectCommands(
16
+ program,
17
+ { chalk, resolveVaultPath }
18
+ ) {
19
+ const projectCmd = program
20
+ .command('project')
21
+ .description('Project management');
22
+
23
+ projectCmd
24
+ .command('add <title>')
25
+ .description('Add a new project')
26
+ .option('-v, --vault <path>', 'Vault path')
27
+ .option('--owner <owner>', 'Project owner')
28
+ .option('--status <status>', 'Project status (active, paused, completed, archived)')
29
+ .option('--team <team>', 'Comma-separated team members')
30
+ .option('--client <client>', 'Client name')
31
+ .option('--tags <tags>', 'Comma-separated tags')
32
+ .option('--description <description>', 'One-line project summary')
33
+ .option('--deadline <date>', 'Deadline (YYYY-MM-DD)')
34
+ .option('--repo <url>', 'Repository URL')
35
+ .option('--url <url>', 'Production URL')
36
+ .action(async (title, options) => {
37
+ try {
38
+ const vaultPath = resolveVaultPath(options.vault);
39
+ const { projectCommand } = await import('../dist/commands/project.js');
40
+ await projectCommand(vaultPath, 'add', {
41
+ title,
42
+ options: {
43
+ owner: options.owner,
44
+ status: options.status,
45
+ team: parseCsvList(options.team),
46
+ client: options.client,
47
+ tags: parseCsvList(options.tags),
48
+ description: options.description,
49
+ deadline: options.deadline,
50
+ repo: options.repo,
51
+ url: options.url
52
+ }
53
+ });
54
+ } catch (err) {
55
+ console.error(chalk.red(`Error: ${err.message}`));
56
+ process.exit(1);
57
+ }
58
+ });
59
+
60
+ projectCmd
61
+ .command('update <slug>')
62
+ .description('Update a project')
63
+ .option('-v, --vault <path>', 'Vault path')
64
+ .option('--status <status>', 'Project status (active, paused, completed, archived)')
65
+ .option('--owner <owner>', 'Project owner')
66
+ .option('--team <team>', 'Comma-separated team members')
67
+ .option('--client <client>', 'Client name')
68
+ .option('--tags <tags>', 'Comma-separated tags')
69
+ .option('--description <description>', 'One-line project summary')
70
+ .option('--deadline <date>', 'Deadline (YYYY-MM-DD)')
71
+ .option('--repo <url>', 'Repository URL')
72
+ .option('--url <url>', 'Production URL')
73
+ .action(async (slug, options) => {
74
+ try {
75
+ const vaultPath = resolveVaultPath(options.vault);
76
+ const { projectCommand } = await import('../dist/commands/project.js');
77
+ await projectCommand(vaultPath, 'update', {
78
+ slug,
79
+ options: {
80
+ status: options.status,
81
+ owner: options.owner,
82
+ team: parseCsvList(options.team),
83
+ client: options.client,
84
+ tags: parseCsvList(options.tags),
85
+ description: options.description,
86
+ deadline: options.deadline,
87
+ repo: options.repo,
88
+ url: options.url
89
+ }
90
+ });
91
+ } catch (err) {
92
+ console.error(chalk.red(`Error: ${err.message}`));
93
+ process.exit(1);
94
+ }
95
+ });
96
+
97
+ projectCmd
98
+ .command('archive <slug>')
99
+ .description('Archive a project')
100
+ .option('-v, --vault <path>', 'Vault path')
101
+ .option('--reason <reason>', 'Reason for archiving')
102
+ .action(async (slug, options) => {
103
+ try {
104
+ const vaultPath = resolveVaultPath(options.vault);
105
+ const { projectCommand } = await import('../dist/commands/project.js');
106
+ await projectCommand(vaultPath, 'archive', {
107
+ slug,
108
+ options: {
109
+ reason: options.reason
110
+ }
111
+ });
112
+ } catch (err) {
113
+ console.error(chalk.red(`Error: ${err.message}`));
114
+ process.exit(1);
115
+ }
116
+ });
117
+
118
+ projectCmd
119
+ .command('list')
120
+ .description('List projects')
121
+ .option('-v, --vault <path>', 'Vault path')
122
+ .option('--status <status>', 'Filter by status')
123
+ .option('--owner <owner>', 'Filter by owner')
124
+ .option('--client <client>', 'Filter by client')
125
+ .option('--tag <tag>', 'Filter by tag')
126
+ .option('--json', 'Output as JSON')
127
+ .action(async (options) => {
128
+ try {
129
+ const vaultPath = resolveVaultPath(options.vault);
130
+ const { projectCommand } = await import('../dist/commands/project.js');
131
+ await projectCommand(vaultPath, 'list', {
132
+ options: {
133
+ status: options.status,
134
+ owner: options.owner,
135
+ client: options.client,
136
+ tag: options.tag,
137
+ json: options.json
138
+ }
139
+ });
140
+ } catch (err) {
141
+ console.error(chalk.red(`Error: ${err.message}`));
142
+ process.exit(1);
143
+ }
144
+ });
145
+
146
+ projectCmd
147
+ .command('show <slug>')
148
+ .description('Show project details')
149
+ .option('-v, --vault <path>', 'Vault path')
150
+ .option('--json', 'Output as JSON')
151
+ .action(async (slug, options) => {
152
+ try {
153
+ const vaultPath = resolveVaultPath(options.vault);
154
+ const { projectCommand } = await import('../dist/commands/project.js');
155
+ await projectCommand(vaultPath, 'show', {
156
+ slug,
157
+ options: {
158
+ json: options.json
159
+ }
160
+ });
161
+ } catch (err) {
162
+ console.error(chalk.red(`Error: ${err.message}`));
163
+ process.exit(1);
164
+ }
165
+ });
166
+
167
+ projectCmd
168
+ .command('tasks <slug>')
169
+ .description('List tasks for a project')
170
+ .option('-v, --vault <path>', 'Vault path')
171
+ .option('--json', 'Output as JSON')
172
+ .action(async (slug, options) => {
173
+ try {
174
+ const vaultPath = resolveVaultPath(options.vault);
175
+ const { projectCommand } = await import('../dist/commands/project.js');
176
+ await projectCommand(vaultPath, 'tasks', {
177
+ slug,
178
+ options: {
179
+ json: options.json
180
+ }
181
+ });
182
+ } catch (err) {
183
+ console.error(chalk.red(`Error: ${err.message}`));
184
+ process.exit(1);
185
+ }
186
+ });
187
+
188
+ projectCmd
189
+ .command('board')
190
+ .description('Generate project kanban board')
191
+ .option('-v, --vault <path>', 'Vault path')
192
+ .option('--output <path>', 'Board markdown path (default: Projects-Board.md)')
193
+ .option('--group-by <field>', 'Grouping field (status, owner, client)')
194
+ .action(async (options) => {
195
+ try {
196
+ const vaultPath = resolveVaultPath(options.vault);
197
+ const { projectCommand } = await import('../dist/commands/project.js');
198
+ await projectCommand(vaultPath, 'board', {
199
+ options: {
200
+ output: options.output,
201
+ groupBy: options.groupBy
202
+ }
203
+ });
204
+ } catch (err) {
205
+ console.error(chalk.red(`Error: ${err.message}`));
206
+ process.exit(1);
207
+ }
208
+ });
209
+ }
@@ -0,0 +1,201 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { Command } from 'commander';
3
+ import { registerProjectCommands } from './register-project-commands.js';
4
+ import { chalkStub } from './test-helpers/cli-command-fixtures.js';
5
+
6
+ const { projectCommandMock } = vi.hoisted(() => ({
7
+ projectCommandMock: vi.fn()
8
+ }));
9
+
10
+ vi.mock('../dist/commands/project.js', () => ({
11
+ projectCommand: projectCommandMock
12
+ }));
13
+
14
+ function buildProgram() {
15
+ const program = new Command();
16
+ registerProjectCommands(program, {
17
+ chalk: chalkStub,
18
+ resolveVaultPath: (value) => value ?? '/vault'
19
+ });
20
+ return program;
21
+ }
22
+
23
+ async function runCommand(args) {
24
+ const program = buildProgram();
25
+ await program.parseAsync(args, { from: 'user' });
26
+ }
27
+
28
+ describe('register-project-commands', () => {
29
+ beforeEach(() => {
30
+ vi.clearAllMocks();
31
+ });
32
+
33
+ it('registers add/update/archive/list/show/tasks/board subcommands', () => {
34
+ const program = buildProgram();
35
+ const projectCommand = program.commands.find((command) => command.name() === 'project');
36
+ expect(projectCommand).toBeDefined();
37
+
38
+ const subcommandNames = projectCommand?.commands.map((command) => command.name()) ?? [];
39
+ expect(subcommandNames).toEqual(expect.arrayContaining([
40
+ 'add',
41
+ 'update',
42
+ 'archive',
43
+ 'list',
44
+ 'show',
45
+ 'tasks',
46
+ 'board'
47
+ ]));
48
+
49
+ const addCommand = projectCommand?.commands.find((command) => command.name() === 'add');
50
+ const addFlags = addCommand?.options.map((option) => option.flags) ?? [];
51
+ expect(addFlags).toEqual(expect.arrayContaining([
52
+ '--owner <owner>',
53
+ '--status <status>',
54
+ '--team <team>',
55
+ '--client <client>',
56
+ '--tags <tags>',
57
+ '--description <description>',
58
+ '--deadline <date>',
59
+ '--repo <url>',
60
+ '--url <url>'
61
+ ]));
62
+
63
+ const listCommand = projectCommand?.commands.find((command) => command.name() === 'list');
64
+ const listFlags = listCommand?.options.map((option) => option.flags) ?? [];
65
+ expect(listFlags).toEqual(expect.arrayContaining([
66
+ '--status <status>',
67
+ '--owner <owner>',
68
+ '--client <client>',
69
+ '--tag <tag>',
70
+ '--json'
71
+ ]));
72
+
73
+ const boardCommand = projectCommand?.commands.find((command) => command.name() === 'board');
74
+ const boardFlags = boardCommand?.options.map((option) => option.flags) ?? [];
75
+ expect(boardFlags).toEqual(expect.arrayContaining([
76
+ '--output <path>',
77
+ '--group-by <field>'
78
+ ]));
79
+ });
80
+
81
+ it('dispatches project subcommands to project command handler', async () => {
82
+ await runCommand([
83
+ 'project',
84
+ 'add',
85
+ 'Apollo Launch',
86
+ '--owner',
87
+ 'alice',
88
+ '--status',
89
+ 'active',
90
+ '--team',
91
+ 'alice,bob',
92
+ '--client',
93
+ 'Acme',
94
+ '--tags',
95
+ 'platform,release',
96
+ '--description',
97
+ 'Launch project',
98
+ '--deadline',
99
+ '2026-03-01',
100
+ '--repo',
101
+ 'https://github.com/acme/apollo',
102
+ '--url',
103
+ 'https://apollo.acme.dev'
104
+ ]);
105
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'add', {
106
+ title: 'Apollo Launch',
107
+ options: {
108
+ owner: 'alice',
109
+ status: 'active',
110
+ team: ['alice', 'bob'],
111
+ client: 'Acme',
112
+ tags: ['platform', 'release'],
113
+ description: 'Launch project',
114
+ deadline: '2026-03-01',
115
+ repo: 'https://github.com/acme/apollo',
116
+ url: 'https://apollo.acme.dev'
117
+ }
118
+ });
119
+
120
+ await runCommand([
121
+ 'project',
122
+ 'update',
123
+ 'apollo-launch',
124
+ '--status',
125
+ 'paused',
126
+ '--owner',
127
+ 'carol',
128
+ '--team',
129
+ 'carol,dave',
130
+ '--client',
131
+ 'Acme',
132
+ '--tags',
133
+ 'ops',
134
+ '--description',
135
+ 'Paused pending review',
136
+ '--deadline',
137
+ '2026-03-10',
138
+ '--repo',
139
+ 'https://github.com/acme/apollo-v2',
140
+ '--url',
141
+ 'https://staging.acme.dev'
142
+ ]);
143
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'update', {
144
+ slug: 'apollo-launch',
145
+ options: {
146
+ status: 'paused',
147
+ owner: 'carol',
148
+ team: ['carol', 'dave'],
149
+ client: 'Acme',
150
+ tags: ['ops'],
151
+ description: 'Paused pending review',
152
+ deadline: '2026-03-10',
153
+ repo: 'https://github.com/acme/apollo-v2',
154
+ url: 'https://staging.acme.dev'
155
+ }
156
+ });
157
+
158
+ await runCommand(['project', 'archive', 'apollo-launch', '--reason', 'Client offboarded']);
159
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'archive', {
160
+ slug: 'apollo-launch',
161
+ options: {
162
+ reason: 'Client offboarded'
163
+ }
164
+ });
165
+
166
+ await runCommand(['project', 'list', '--status', 'active', '--owner', 'alice', '--client', 'Acme', '--tag', 'platform', '--json']);
167
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'list', {
168
+ options: {
169
+ status: 'active',
170
+ owner: 'alice',
171
+ client: 'Acme',
172
+ tag: 'platform',
173
+ json: true
174
+ }
175
+ });
176
+
177
+ await runCommand(['project', 'show', 'apollo-launch']);
178
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'show', {
179
+ slug: 'apollo-launch',
180
+ options: {
181
+ json: undefined
182
+ }
183
+ });
184
+
185
+ await runCommand(['project', 'tasks', 'apollo-launch', '--json']);
186
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'tasks', {
187
+ slug: 'apollo-launch',
188
+ options: {
189
+ json: true
190
+ }
191
+ });
192
+
193
+ await runCommand(['project', 'board', '--output', 'Projects-Board.md', '--group-by', 'client']);
194
+ expect(projectCommandMock).toHaveBeenCalledWith('/vault', 'board', {
195
+ options: {
196
+ output: 'Projects-Board.md',
197
+ groupBy: 'client'
198
+ }
199
+ });
200
+ });
201
+ });
@@ -168,6 +168,46 @@ export function registerQueryCommands(
168
168
  }
169
169
  });
170
170
 
171
+ // === INJECT ===
172
+ program
173
+ .command('inject <message>')
174
+ .description('Inject relevant rules, decisions, and preferences for prompt context')
175
+ .option('-n, --max-results <n>', 'Maximum injected items')
176
+ .option('--scope <scope>', 'Comma-separated scope filter override')
177
+ .option('--enable-llm', 'Enable optional LLM fuzzy intent matching')
178
+ .option('--disable-llm', 'Disable optional LLM fuzzy intent matching')
179
+ .option('--format <format>', 'Output format (markdown|json)', 'markdown')
180
+ .option('--model <model>', 'Override LLM model when fuzzy matching is enabled')
181
+ .option('-v, --vault <path>', 'Vault path')
182
+ .action(async (message, options) => {
183
+ try {
184
+ const parsedMaxResults = options.maxResults
185
+ ? Number.parseInt(options.maxResults, 10)
186
+ : undefined;
187
+ if (options.maxResults && (!Number.isFinite(parsedMaxResults) || parsedMaxResults <= 0)) {
188
+ throw new Error(`Invalid --max-results value: ${options.maxResults}`);
189
+ }
190
+ const useLlm = options.enableLlm
191
+ ? true
192
+ : options.disableLlm
193
+ ? false
194
+ : undefined;
195
+
196
+ const { injectCommand } = await import('../dist/commands/inject.js');
197
+ await injectCommand(message, {
198
+ vaultPath: resolveVaultPath(options.vault),
199
+ maxResults: parsedMaxResults,
200
+ useLlm,
201
+ scope: options.scope,
202
+ format: options.format === 'json' ? 'json' : 'markdown',
203
+ model: options.model
204
+ });
205
+ } catch (err) {
206
+ console.error(chalk.red(`Error: ${err.message}`));
207
+ process.exit(1);
208
+ }
209
+ });
210
+
171
211
  // === OBSERVE ===
172
212
  program
173
213
  .command('observe')
@@ -333,28 +333,12 @@ export function registerTaskCommands(
333
333
  .description('Generate Obsidian canvas dashboard')
334
334
  .option('-v, --vault <path>', 'Vault path')
335
335
  .option('--output <path>', 'Output file path (default: dashboard.canvas)')
336
- .option('--template <id>', 'Canvas template ID (default, project-board, brain, sprint)')
337
- .option('--project <project>', 'Project filter for template-aware canvases')
338
- .option('--owner <owner>', 'Filter tasks by owner (agent name or human)')
339
- .option('--width <pixels>', 'Canvas width in pixels', parseInt)
340
- .option('--height <pixels>', 'Canvas height in pixels', parseInt)
341
- .option('--include-done', 'Include completed tasks (default: limited)')
342
- .option('--list-templates', 'List available canvas templates and exit')
343
336
  .action(async (options) => {
344
337
  try {
345
- const vaultPath = options.listTemplates
346
- ? (options.vault || '.')
347
- : resolveVaultPath(options.vault);
338
+ const vaultPath = resolveVaultPath(options.vault);
348
339
  const { canvasCommand } = await import('../dist/commands/canvas.js');
349
340
  await canvasCommand(vaultPath, {
350
- output: options.output,
351
- template: options.template,
352
- project: options.project,
353
- owner: options.owner,
354
- width: options.width,
355
- height: options.height,
356
- includeDone: options.includeDone,
357
- listTemplates: options.listTemplates
341
+ output: options.output
358
342
  });
359
343
  } catch (err) {
360
344
  console.error(chalk.red(`Error: ${err.message}`));
@@ -50,7 +50,7 @@ describe('register-task-commands', () => {
50
50
  ]));
51
51
  });
52
52
 
53
- it('adds canvas template and listing flags', () => {
53
+ it('adds simplified canvas flags', () => {
54
54
  const program = new Command();
55
55
  registerTaskCommands(program, {
56
56
  chalk: chalkStub,
@@ -62,9 +62,8 @@ describe('register-task-commands', () => {
62
62
 
63
63
  const optionFlags = canvasCommand?.options.map((option) => option.flags) ?? [];
64
64
  expect(optionFlags).toEqual(expect.arrayContaining([
65
- '--template <id>',
66
- '--list-templates',
67
- '--project <project>'
65
+ '-v, --vault <path>',
66
+ '--output <path>'
68
67
  ]));
69
68
  });
70
69
  });
@@ -12,6 +12,7 @@ import { registerConfigCommands } from '../register-config-commands.js';
12
12
  import { registerRouteCommands } from '../register-route-commands.js';
13
13
  import { registerTaskCommands } from '../register-task-commands.js';
14
14
  import { registerKanbanCommands } from '../register-kanban-commands.js';
15
+ import { registerProjectCommands } from '../register-project-commands.js';
15
16
 
16
17
  export const chalkStub = {
17
18
  cyan: (value) => value,
@@ -109,6 +110,10 @@ export function registerAllCommandModules(program = new Command()) {
109
110
  chalk: chalkStub,
110
111
  resolveVaultPath: stubResolveVaultPath
111
112
  });
113
+ registerProjectCommands(program, {
114
+ chalk: chalkStub,
115
+ resolveVaultPath: stubResolveVaultPath
116
+ });
112
117
 
113
118
  return program;
114
119
  }
@@ -1,5 +1,6 @@
1
1
  // src/types.ts
2
2
  var DEFAULT_CATEGORIES = [
3
+ "rules",
3
4
  "preferences",
4
5
  "decisions",
5
6
  "patterns",
@@ -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-H6WQUUNK.js";
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-NAMFB7ZA.js";
15
+ } from "./chunk-Z2XBWN7A.js";
16
16
 
17
17
  // src/commands/observe.ts
18
18
  import * as fs3 from "fs";