orbitmap 0.4.2 → 0.4.4

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 (164) hide show
  1. package/README.md +112 -18
  2. package/dist/adapters/cloud.d.ts +63 -21
  3. package/dist/adapters/cloud.js +108 -29
  4. package/dist/adapters/cloud.js.map +1 -1
  5. package/dist/adapters/factory.d.ts +3 -0
  6. package/dist/adapters/factory.js +8 -1
  7. package/dist/adapters/factory.js.map +1 -1
  8. package/dist/adapters/local/adapter.d.ts +17 -20
  9. package/dist/adapters/local/adapter.js +28 -21
  10. package/dist/adapters/local/adapter.js.map +1 -1
  11. package/dist/adapters/local/context.d.ts +1 -1
  12. package/dist/adapters/local/context.js +10 -2
  13. package/dist/adapters/local/context.js.map +1 -1
  14. package/dist/adapters/local/entities/planning.d.ts +5 -1
  15. package/dist/adapters/local/entities/planning.js +16 -3
  16. package/dist/adapters/local/entities/planning.js.map +1 -1
  17. package/dist/adapters/local/entities/tasks.d.ts +9 -1
  18. package/dist/adapters/local/entities/tasks.js +47 -5
  19. package/dist/adapters/local/entities/tasks.js.map +1 -1
  20. package/dist/adapters/local/entities/work-items.d.ts +14 -30
  21. package/dist/adapters/local/entities/work-items.js +63 -47
  22. package/dist/adapters/local/entities/work-items.js.map +1 -1
  23. package/dist/adapters/local/rules.d.ts +4 -4
  24. package/dist/adapters/local/rules.js +4 -19
  25. package/dist/adapters/local/rules.js.map +1 -1
  26. package/dist/adapters/local/shapes.d.ts +27 -7
  27. package/dist/adapters/local/shapes.js +41 -5
  28. package/dist/adapters/local/shapes.js.map +1 -1
  29. package/dist/adapters/local/store.d.ts +55 -6
  30. package/dist/adapters/local/store.js +122 -8
  31. package/dist/adapters/local/store.js.map +1 -1
  32. package/dist/adapters/local/types.d.ts +11 -2
  33. package/dist/adapters/local/types.js +9 -2
  34. package/dist/adapters/local/types.js.map +1 -1
  35. package/dist/adapters/types.d.ts +30 -20
  36. package/dist/agent-instructions.d.ts +43 -12
  37. package/dist/agent-instructions.js +1133 -170
  38. package/dist/agent-instructions.js.map +1 -1
  39. package/dist/area-directories.d.ts +75 -0
  40. package/dist/area-directories.js +162 -0
  41. package/dist/area-directories.js.map +1 -0
  42. package/dist/area-grouping.d.ts +55 -0
  43. package/dist/area-grouping.js +72 -0
  44. package/dist/area-grouping.js.map +1 -0
  45. package/dist/commands/areas.js +30 -2
  46. package/dist/commands/areas.js.map +1 -1
  47. package/dist/commands/assign.d.ts +1 -0
  48. package/dist/commands/assign.js +2 -0
  49. package/dist/commands/assign.js.map +1 -1
  50. package/dist/commands/context.d.ts +95 -0
  51. package/dist/commands/context.js +104 -2
  52. package/dist/commands/context.js.map +1 -1
  53. package/dist/commands/create.d.ts +1 -0
  54. package/dist/commands/create.js +19 -14
  55. package/dist/commands/create.js.map +1 -1
  56. package/dist/commands/dep.js +5 -8
  57. package/dist/commands/dep.js.map +1 -1
  58. package/dist/commands/doc-import.d.ts +1 -0
  59. package/dist/commands/doc-import.js +8 -26
  60. package/dist/commands/doc-import.js.map +1 -1
  61. package/dist/commands/doc-patch.js +16 -52
  62. package/dist/commands/doc-patch.js.map +1 -1
  63. package/dist/commands/doc-share.js +4 -0
  64. package/dist/commands/doc-share.js.map +1 -1
  65. package/dist/commands/doc-update.js +4 -8
  66. package/dist/commands/doc-update.js.map +1 -1
  67. package/dist/commands/doc.js +7 -12
  68. package/dist/commands/doc.js.map +1 -1
  69. package/dist/commands/docs-cache.js +23 -10
  70. package/dist/commands/docs-cache.js.map +1 -1
  71. package/dist/commands/docs.js +29 -10
  72. package/dist/commands/docs.js.map +1 -1
  73. package/dist/commands/get.js +8 -10
  74. package/dist/commands/get.js.map +1 -1
  75. package/dist/commands/ideas.d.ts +3 -1
  76. package/dist/commands/ideas.js +26 -49
  77. package/dist/commands/ideas.js.map +1 -1
  78. package/dist/commands/init.d.ts +24 -1
  79. package/dist/commands/init.js +0 -0
  80. package/dist/commands/init.js.map +1 -1
  81. package/dist/commands/intent.d.ts +16 -0
  82. package/dist/commands/intent.js +61 -18
  83. package/dist/commands/intent.js.map +1 -1
  84. package/dist/commands/issues.d.ts +3 -1
  85. package/dist/commands/issues.js +47 -72
  86. package/dist/commands/issues.js.map +1 -1
  87. package/dist/commands/log.d.ts +14 -2
  88. package/dist/commands/log.js +35 -20
  89. package/dist/commands/log.js.map +1 -1
  90. package/dist/commands/mission.d.ts +8 -0
  91. package/dist/commands/mission.js +58 -18
  92. package/dist/commands/mission.js.map +1 -1
  93. package/dist/commands/orbit.d.ts +7 -1
  94. package/dist/commands/orbit.js +13 -24
  95. package/dist/commands/orbit.js.map +1 -1
  96. package/dist/commands/setup-agent.d.ts +97 -4
  97. package/dist/commands/setup-agent.js +273 -40
  98. package/dist/commands/setup-agent.js.map +1 -1
  99. package/dist/commands/setup-mcp.d.ts +2 -0
  100. package/dist/commands/setup-mcp.js +17 -40
  101. package/dist/commands/setup-mcp.js.map +1 -1
  102. package/dist/commands/start.d.ts +43 -0
  103. package/dist/commands/start.js +158 -3
  104. package/dist/commands/start.js.map +1 -1
  105. package/dist/commands/status.d.ts +1 -0
  106. package/dist/commands/status.js +5 -15
  107. package/dist/commands/status.js.map +1 -1
  108. package/dist/commands/subtask.d.ts +1 -0
  109. package/dist/commands/subtask.js +9 -2
  110. package/dist/commands/subtask.js.map +1 -1
  111. package/dist/commands/task-edit-content.js +4 -8
  112. package/dist/commands/task-edit-content.js.map +1 -1
  113. package/dist/commands/task.d.ts +7 -0
  114. package/dist/commands/task.js +5 -2
  115. package/dist/commands/task.js.map +1 -1
  116. package/dist/commands/tasks.d.ts +35 -0
  117. package/dist/commands/tasks.js +45 -9
  118. package/dist/commands/tasks.js.map +1 -1
  119. package/dist/commands/vibes.d.ts +3 -1
  120. package/dist/commands/vibes.js +41 -31
  121. package/dist/commands/vibes.js.map +1 -1
  122. package/dist/config.d.ts +11 -0
  123. package/dist/config.js +14 -1
  124. package/dist/config.js.map +1 -1
  125. package/dist/doc-cache.d.ts +74 -5
  126. package/dist/doc-cache.js +106 -22
  127. package/dist/doc-cache.js.map +1 -1
  128. package/dist/enums.d.ts +26 -0
  129. package/dist/enums.js +53 -0
  130. package/dist/enums.js.map +1 -0
  131. package/dist/errors.d.ts +67 -0
  132. package/dist/errors.js +82 -0
  133. package/dist/errors.js.map +1 -1
  134. package/dist/index.js +169 -60
  135. package/dist/index.js.map +1 -1
  136. package/dist/mcp-config.d.ts +42 -1
  137. package/dist/mcp-config.js +82 -2
  138. package/dist/mcp-config.js.map +1 -1
  139. package/dist/oauth.js +10 -10
  140. package/dist/output.d.ts +12 -0
  141. package/dist/output.js +39 -1
  142. package/dist/output.js.map +1 -1
  143. package/dist/paths.d.ts +40 -0
  144. package/dist/paths.js +174 -9
  145. package/dist/paths.js.map +1 -1
  146. package/dist/project-config.d.ts +42 -1
  147. package/dist/project-config.js +51 -0
  148. package/dist/project-config.js.map +1 -1
  149. package/dist/work-log.d.ts +104 -0
  150. package/dist/work-log.js +181 -0
  151. package/dist/work-log.js.map +1 -0
  152. package/dist/workspace-resolve.d.ts +179 -9
  153. package/dist/workspace-resolve.js +288 -20
  154. package/dist/workspace-resolve.js.map +1 -1
  155. package/dist/write-target.d.ts +56 -0
  156. package/dist/write-target.js +170 -0
  157. package/dist/write-target.js.map +1 -0
  158. package/package.json +3 -2
  159. package/dist/commands/create-project.d.ts +0 -6
  160. package/dist/commands/create-project.js +0 -28
  161. package/dist/commands/create-project.js.map +0 -1
  162. package/dist/commands/projects.d.ts +0 -3
  163. package/dist/commands/projects.js +0 -28
  164. package/dist/commands/projects.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
2
+ import { Command, Option } from 'commander';
3
3
  import { initCommand } from './commands/init.js';
4
4
  import { contextCommand } from './commands/context.js';
5
5
  import { areasCommand } from './commands/areas.js';
@@ -35,7 +35,7 @@ import { depAddCommand, depRemoveCommand } from './commands/dep.js';
35
35
  import { getCommand } from './commands/get.js';
36
36
  import { createRequire } from 'node:module';
37
37
  import { createAreaCommand } from './commands/create-area.js';
38
- import { setWorkspaceFlag } from './workspace-resolve.js';
38
+ import { setWorkspaceFlag, setWorkspaceIdFlag } from './workspace-resolve.js';
39
39
  const program = new Command();
40
40
  /**
41
41
  * `--version` reads the real package version instead of a hardcoded literal, which had gone
@@ -44,25 +44,55 @@ const program = new Command();
44
44
  * always ships package.json in the tarball regardless of the `files` allow-list.
45
45
  */
46
46
  const { version } = createRequire(import.meta.url)('../package.json');
47
+ /**
48
+ * Commander collector for a REPEATABLE option: each occurrence is appended, so `--log a
49
+ * --log b` arrives as `['a', 'b']` in the order typed. Without it Commander keeps only the
50
+ * last value, and a work log batch would silently lose every entry but one.
51
+ *
52
+ * Register with an explicit `[]` default (`.option(..., collect, [])`) — the default is the
53
+ * accumulator's seed, and Commander does not supply one.
54
+ */
55
+ function collect(value, previous) {
56
+ return [...previous, value];
57
+ }
58
+ /** `--log` help text, identical on every command that carries a work log batch. */
59
+ const LOG_FLAG_HELP = 'Work log entry as <type>:<content>; repeatable, order preserved. ' +
60
+ 'Types: note, code_change, decision, blocker. Required by the API when the status changes';
47
61
  program
48
62
  .name('orbitmap')
49
63
  .description('CLI tool for OrbitMap Agent API')
50
64
  .version(version)
51
65
  .option('--json', 'Output in JSON format', false)
52
66
  .option('--area <id>', 'Area ID or slug to use')
53
- .option('--workspace <path>', 'Local workspace directory to use (local mode)');
54
- // The workspace flag is program-level and consumed by the resolver, not by the
55
- // command modules, so capture it once before any action runs.
56
- program.hook('preAction', (thisCommand) => {
67
+ .option('--workspace <path>', 'Local workspace directory to use (local mode)')
68
+ .option('--workspace-id <slug-or-uuid>', 'Workspace to use (slug or UUID)')
69
+ // Satisfies the write-target confirmation non-interactively (see src/write-target.ts).
70
+ .option('--yes', 'Accept the resolved target area for writes without confirming', false);
71
+ // Both workspace flags are consumed by the resolver, not by the command modules, so
72
+ // capture them once before any action runs. They are different things: `--workspace` is a
73
+ // directory on this disk (and forces local mode), `--workspace-id` names a workspace in the
74
+ // backend and does not touch the mode.
75
+ //
76
+ // `--workspace-id` is read from the SUBCOMMAND first and only then from the program level.
77
+ // Previously only `thisCommand` (the program) was consulted, so `create-area --workspace-id`,
78
+ // `doc-import --workspace-id` and `init --workspace-id` put the value in the request body
79
+ // but never sent `X-Orbitmap-Workspace` — the same flag reached the header ladder or not
80
+ // purely depending on where the user typed it.
81
+ program.hook('preAction', (thisCommand, actionCommand) => {
57
82
  setWorkspaceFlag(thisCommand.opts()['workspace']);
83
+ const subcommandWorkspaceId = actionCommand.opts()['workspaceId'];
84
+ setWorkspaceIdFlag(subcommandWorkspaceId ?? thisCommand.opts()['workspaceId']);
58
85
  });
59
86
  program
60
87
  .command('init')
61
88
  .description('Set up OrbitMap (cloud account or local files) and install the agent skills')
62
89
  .option('--key <api-key>', 'Agent API key (enables non-interactive mode)')
63
90
  .option('--token <api-key>', 'Alias for --key')
64
- .option('--area <id>', 'Default area ID (cloud) or first area name (local)')
91
+ .option('--area <id>', 'Area this directory is (cloud) or first area name (local)')
92
+ .option('--workspace-id <slug-or-uuid>', 'Cloud: workspace to set up (skips the workspace question)')
65
93
  .option('--profile <profile>', 'MCP profile: full, lite, manager (default: full)')
94
+ .option('--allow-nested', 'Cloud: set up a workspace here even though another workspace root sits above or below ' +
95
+ 'this directory')
66
96
  .option('--advanced', 'Show advanced options (e.g. custom API URL)')
67
97
  .option('--local', 'Set up local-files mode without the interactive question')
68
98
  .option('--workspace <path>', 'Local workspace directory (default: ./.orbitmap)')
@@ -70,6 +100,8 @@ program
70
100
  .option('--no-skills', 'Skip installing the OrbitMap agent skills')
71
101
  .option('--agent <type>', 'Coding agent to install the skills for: claude, gemini, codex, cursor, windsurf, generic ' +
72
102
  '(default: detected from this directory, or asked)')
103
+ .option('--scope <scope>', 'Where to install the skills: user (default, ~/.claude/skills — visible from every ' +
104
+ 'directory) or project (this directory only)')
73
105
  .option('--global', 'Cloud: write to ~/.orbitmap/config.json instead of ./.orbitmap/config.json')
74
106
  .action(async (opts, command) => {
75
107
  // `--workspace` also exists as a program-level flag, and Commander binds it there;
@@ -80,12 +112,17 @@ program
80
112
  key: opts.key,
81
113
  token: opts.token,
82
114
  area: opts.area ?? all['area'],
115
+ // `--workspace-id` is also a program-level flag; take it from whichever position it
116
+ // was given in (same pattern as `--workspace` above).
117
+ workspaceId: opts.workspaceId ?? all['workspaceId'],
83
118
  profile: opts.profile,
119
+ allowNested: opts.allowNested,
84
120
  local: opts.local,
85
121
  workspace: opts.workspace ?? all['workspace'],
86
122
  workspaceName: opts.workspaceName,
87
123
  skills: opts.skills,
88
124
  agent: opts.agent,
125
+ scope: opts.scope,
89
126
  global: opts.global,
90
127
  });
91
128
  });
@@ -121,7 +158,7 @@ program
121
158
  .description('Create a new area')
122
159
  .option('--desc <description>', 'Area description')
123
160
  .option('--slug <slug>', 'Area slug (auto-generated from name if omitted)')
124
- .option('--workspace-id <uuid>', 'Workspace to create the area in')
161
+ .option('--workspace-id <slug-or-uuid>', 'Workspace to create the area in (slug or UUID)')
125
162
  .action(async (name, opts, cmd) => {
126
163
  const globalOpts = cmd.optsWithGlobals();
127
164
  await createAreaCommand(name, {
@@ -129,7 +166,9 @@ program
129
166
  area: globalOpts.area,
130
167
  desc: opts.desc,
131
168
  slug: opts.slug,
132
- workspaceId: opts.workspaceId,
169
+ // `--workspace-id` also exists as a program-level flag; `optsWithGlobals()` picks it
170
+ // up whichever position it was given in (same pattern as `--workspace` on `init`).
171
+ workspaceId: opts.workspaceId ?? globalOpts.workspaceId,
133
172
  });
134
173
  });
135
174
  program
@@ -148,7 +187,8 @@ program
148
187
  });
149
188
  program
150
189
  .command('tasks')
151
- .description('List your assigned tasks')
190
+ .description('List tasks in the current area — or, when the context is workspace-scoped, every ' +
191
+ 'area in the workspace grouped by area')
152
192
  .option('--status <statuses>', 'Filter by status (comma-separated)', 'todo,in_progress')
153
193
  .option('--priority <priority>', 'Filter by priority')
154
194
  .option('--orbit <id-or-slug>', 'Filter by orbit (UUID or slug)')
@@ -209,6 +249,7 @@ program
209
249
  await statusCommand(taskId, status, {
210
250
  json: globalOpts.json,
211
251
  area: globalOpts.area,
252
+ yes: globalOpts.yes,
212
253
  delivery: opts.delivery,
213
254
  force: opts.force,
214
255
  });
@@ -220,17 +261,18 @@ program
220
261
  .option('--priority <priority>', 'Priority (critical, high, medium, low)')
221
262
  .option('--status <status>', 'Initial status')
222
263
  .option('--effort <effort>', 'Estimated effort (e.g. 4h, 2d)')
223
- .option('--agent-id <id>', 'Assign to agent')
264
+ .option('--agent-id <id-or-slug>', 'Assign to agent (UUID or slug)')
224
265
  .option('--branch <name>', 'Git branch name')
225
266
  .option('--instructions <text>', 'Agent instructions')
226
- .option('--from-issue <id-or-number>', 'Link task to an existing issue (UUID, number, or IS-prefixed)')
227
- .option('--intent <id>', 'Link task to an intent')
228
- .option('--orbit <id>', 'Attach task to an orbit (omit to inherit the intent orbit)')
267
+ .option('--from-issue <id-or-number>', 'Link task to an existing issue (UUID or display number, e.g. IS-8hv2wn)')
268
+ .option('--intent <id-or-number>', 'Link task to an intent (UUID or display number, e.g. IN-rwb5qu)')
269
+ .option('--orbit <id-or-slug>', 'Attach task to an orbit (UUID or slug; omit to inherit the intent orbit)')
229
270
  .action(async (title, opts, cmd) => {
230
271
  const globalOpts = cmd.optsWithGlobals();
231
272
  await createCommand(title, {
232
273
  json: globalOpts.json,
233
274
  area: globalOpts.area,
275
+ yes: globalOpts.yes,
234
276
  desc: opts.desc,
235
277
  priority: opts.priority,
236
278
  status: opts.status,
@@ -254,14 +296,15 @@ program
254
296
  await subtaskCommand(parentId, title, {
255
297
  json: globalOpts.json,
256
298
  area: globalOpts.area,
299
+ yes: globalOpts.yes,
257
300
  desc: opts.desc,
258
301
  priority: opts.priority,
259
302
  effort: opts.effort,
260
303
  });
261
304
  });
262
305
  program
263
- .command('assign <id-or-number> [agent-id]')
264
- .description('Assign a task, issue, idea, or intent to an agent or user')
306
+ .command('assign <id-or-number> [agent-id-or-slug]')
307
+ .description('Assign a task, issue, idea, or intent to an agent (UUID or slug) or user')
265
308
  .option('--user-id <id>', 'Assign to a user (by user ID) instead of an agent')
266
309
  .option('--type <type>', 'Object type: task, issue, idea, intent (auto-detected from prefixed IDs; default task)')
267
310
  .action(async (objectId, agentId, opts, cmd) => {
@@ -269,21 +312,26 @@ program
269
312
  await assignCommand(objectId, agentId, {
270
313
  json: globalOpts.json,
271
314
  area: globalOpts.area,
315
+ yes: globalOpts.yes,
272
316
  userId: opts.userId,
273
317
  type: opts.type,
274
318
  });
275
319
  });
276
320
  program
277
321
  .command('log <id-or-number> <message>')
278
- .description('Log work on a task')
279
- .option('--type <type>', 'Log type: note, code_change, decision, blocker, discovery, exploration, fix', 'note')
322
+ .description('Log work on a task, intent or mission (TS-/IN-/MS- number, or a UUID)')
323
+ .option('--type <type>', 'Log type: note, code_change, decision, blocker', 'note')
324
+ .option('--target <kind>', 'Which object a bare UUID names: task, intent, mission (default: read from the ' +
325
+ 'TS-/IN-/MS- prefix, else task)')
280
326
  .option('--meta <json>', 'JSON metadata (e.g. \'{"files":["..."]}\')')
281
- .action(async (taskId, message, opts, cmd) => {
327
+ .action(async (idOrNumber, message, opts, cmd) => {
282
328
  const globalOpts = cmd.optsWithGlobals();
283
- await logCommand(taskId, message, {
329
+ await logCommand(idOrNumber, message, {
284
330
  json: globalOpts.json,
285
331
  area: globalOpts.area,
332
+ yes: globalOpts.yes,
286
333
  type: opts.type,
334
+ target: opts.target,
287
335
  meta: opts.meta,
288
336
  });
289
337
  });
@@ -343,17 +391,20 @@ program
343
391
  .requiredOption('--file <path>', 'Path to markdown file')
344
392
  .option('--source-path <path>', 'Original source path for reference')
345
393
  .option('--context <text>', 'Short description of when this document is relevant')
346
- .option('--workspace-id <uuid>', 'Create as workspace-level document (cross-area knowledge)')
394
+ .option('--workspace-id <slug-or-uuid>', 'Create as workspace-level document (cross-area knowledge); slug or UUID')
347
395
  .action(async (title, opts, cmd) => {
348
396
  const globalOpts = cmd.optsWithGlobals();
349
397
  await docImportCommand(title, {
350
398
  json: globalOpts.json,
351
399
  area: globalOpts.area,
400
+ yes: globalOpts.yes,
352
401
  type: opts.type,
353
402
  file: opts.file,
354
403
  sourcePath: opts.sourcePath,
355
404
  context: opts.context,
356
- workspaceId: opts.workspaceId,
405
+ // Program-level `--workspace-id` is the fallback when it was given before the
406
+ // subcommand name (see `create-area`).
407
+ workspaceId: opts.workspaceId ?? globalOpts.workspaceId,
357
408
  });
358
409
  });
359
410
  program
@@ -437,21 +488,33 @@ program
437
488
  .description('Refresh the OrbitMap agent instructions/skills, or add another agent type ' +
438
489
  '(`init` already installs them once)')
439
490
  .option('--agent <type>', 'Agent type: claude, gemini, codex, cursor, windsurf, generic')
491
+ .option('--scope <scope>', 'Where to install: user (default, ~/.claude/skills — visible from every directory) ' +
492
+ 'or project (this directory only)')
493
+ .option('--clean-project', 'With --scope user: remove an older project-scope install in this directory ' +
494
+ '(its four OrbitMap skill dirs + the OrbitMap block in the config file) without asking')
440
495
  .action(async (opts) => {
441
- await setupAgentCommand({ agent: opts.agent });
496
+ await setupAgentCommand({
497
+ agent: opts.agent,
498
+ scope: opts.scope,
499
+ cleanProject: opts.cleanProject,
500
+ });
442
501
  });
443
502
  program
444
503
  .command('setup-mcp')
445
504
  .description('Install OrbitMap MCP entry into .mcp.json in the current directory (no API calls)')
446
505
  .option('--token <api-key>', 'Agent API key (Bearer auth)')
447
506
  .option('--oauth', 'Use OAuth (no Authorization header; auth happens on first MCP use)')
448
- .option('--area <slug>', 'Area slug or UUID (required for --token, optional for --oauth)')
507
+ .option('--workspace-id <slug-or-uuid>', 'Workspace to pin in the generated block (X-Orbitmap-Workspace)')
508
+ .option('--area <slug>', 'Area slug or UUID to pin as well (X-Orbitmap-Area, optional)')
449
509
  .option('--profile <profile>', 'MCP profile: full, lite, manager (default: full)')
450
510
  .action(async (opts, cmd) => {
511
+ // `--workspace-id` also exists as a program-level flag; `optsWithGlobals()` picks it up
512
+ // whichever position it was given in (same pattern as `--area`).
451
513
  const globalOpts = cmd.optsWithGlobals();
452
514
  await setupMcpCommand({
453
515
  token: opts.token,
454
516
  oauth: opts.oauth,
517
+ workspaceId: opts.workspaceId || globalOpts.workspaceId,
455
518
  area: opts.area || globalOpts.area,
456
519
  profile: opts.profile,
457
520
  });
@@ -538,19 +601,26 @@ const issue = program
538
601
  .command('issue')
539
602
  .description('Manage issues');
540
603
  issue
541
- .command('register')
604
+ .command('register [title]')
542
605
  .description('Register a new issue')
543
- .requiredOption('--title <title>', 'Issue title')
606
+ // Title is positional, like `create`, `intent create`, `mission create` and `orbit create`.
607
+ // `--title` stays as a hidden back-compat alias.
608
+ .addOption(new Option('--title <title>', 'Deprecated: pass the title positionally').hideHelp())
544
609
  .requiredOption('--description <text>', 'Issue description')
545
610
  .option('--severity <level>', 'Severity: critical, high, medium, low')
546
- .option('--related-task <id>', 'Related task UUID')
547
- .option('--intent <id>', 'Link issue to an intent')
548
- .action(async (opts, cmd) => {
611
+ // These two stay UUID-only: TS-av6332 relaxed `intent_id`/`orbit_id`/`source_issue_id` on
612
+ // POST /tasks to `App\Rules\EntityRef`, but IssueController still validates
613
+ // `related_task_id` and `intent_id` as `uuid`. Do not translate client-side — see the
614
+ // OrbitMap issue linked from TS-zhmvnc.
615
+ .option('--related-task <uuid>', 'Related task UUID (this endpoint does not accept display numbers)')
616
+ .option('--intent <uuid>', 'Link issue to an intent, by UUID (no display numbers here)')
617
+ .action(async (title, opts, cmd) => {
549
618
  const globalOpts = cmd.optsWithGlobals();
550
619
  await issueRegisterCommand({
551
620
  json: globalOpts.json,
552
621
  area: globalOpts.area,
553
- title: opts.title,
622
+ yes: globalOpts.yes,
623
+ title: title ?? opts.title,
554
624
  description: opts.description,
555
625
  severity: opts.severity,
556
626
  relatedTask: opts.relatedTask,
@@ -586,9 +656,11 @@ issue
586
656
  issue
587
657
  .command('resolve <id-or-number>')
588
658
  .description('Resolve an issue')
589
- .requiredOption('--status <status>', 'Resolve status: resolved, wont_fix, duplicate')
659
+ // The command is named `resolve`, so `resolved` is the obvious default; --status stays
660
+ // available for the other two terminal states (wont_fix, duplicate).
661
+ .option('--status <status>', 'Resolve status: resolved, wont_fix, duplicate', 'resolved')
590
662
  .option('--resolution <text>', 'Resolution description (required for resolved/wont_fix)')
591
- .option('--resolved-task <id>', 'Task UUID that resolved this issue')
663
+ .option('--resolved-task <uuid>', 'Task UUID that resolved this issue (no display numbers here)')
592
664
  .action(async (idOrNumber, opts, cmd) => {
593
665
  const globalOpts = cmd.optsWithGlobals();
594
666
  await issueResolveCommand(idOrNumber, {
@@ -601,9 +673,11 @@ issue
601
673
  });
602
674
  issue
603
675
  .command('status <id-or-number> <status>')
604
- .description('Update issue status (open, investigating, backlog, resolved, wont_fix, duplicate)')
676
+ .description('Update issue status. Which move is legal depends on the current status ' +
677
+ '(open <-> investigating <-> backlog, any of them -> resolved/wont_fix/duplicate, ' +
678
+ 'and those back to open); the server rejects anything else with INVALID_TRANSITION')
605
679
  .option('--resolution <text>', 'Resolution description (required for resolved/wont_fix)')
606
- .option('--resolved-task <id>', 'Task UUID that resolved this issue')
680
+ .option('--resolved-task <uuid>', 'Task UUID that resolved this issue (no display numbers here)')
607
681
  .action(async (idOrNumber, status, opts, cmd) => {
608
682
  const globalOpts = cmd.optsWithGlobals();
609
683
  await issueStatusCommand(idOrNumber, status, {
@@ -618,18 +692,21 @@ const vibe = program
618
692
  .command('vibe')
619
693
  .description('Manage vibes');
620
694
  vibe
621
- .command('log')
695
+ .command('log [content]')
622
696
  .description('Log a new vibe')
623
- .requiredOption('--content <text>', 'Vibe content')
624
- .requiredOption('--type <type>', 'Type: note, code_change, decision, discovery, idea, exploration, fix')
697
+ // Content is positional, matching every other "create something" command.
698
+ // `--content` stays as a hidden back-compat alias.
699
+ .addOption(new Option('--content <text>', 'Deprecated: pass the content positionally').hideHelp())
700
+ .requiredOption('--type <type>', 'Type: note, code_change, decision, discovery, exploration, fix')
625
701
  .option('--title <title>', 'Optional title')
626
- .option('--task <id>', 'Related task UUID')
627
- .action(async (opts, cmd) => {
702
+ .option('--task <uuid>', 'Related task UUID (this endpoint does not accept display numbers)')
703
+ .action(async (content, opts, cmd) => {
628
704
  const globalOpts = cmd.optsWithGlobals();
629
705
  await vibeLogCommand({
630
706
  json: globalOpts.json,
631
707
  area: globalOpts.area,
632
- content: opts.content,
708
+ yes: globalOpts.yes,
709
+ content: content ?? opts.content,
633
710
  type: opts.type,
634
711
  title: opts.title,
635
712
  task: opts.task,
@@ -652,8 +729,8 @@ vibe
652
729
  });
653
730
  });
654
731
  vibe
655
- .command('show <id>')
656
- .description('Show full vibe details')
732
+ .command('show <id-or-number>')
733
+ .description('Show full vibe details (UUID or display number, e.g. VB-a1b2c3)')
657
734
  .action(async (id, _opts, cmd) => {
658
735
  const globalOpts = cmd.optsWithGlobals();
659
736
  await vibeShowCommand(id, {
@@ -662,8 +739,8 @@ vibe
662
739
  });
663
740
  });
664
741
  vibe
665
- .command('update <id>')
666
- .description('Update a vibe')
742
+ .command('update <id-or-number>')
743
+ .description('Update a vibe (UUID or display number, e.g. VB-a1b2c3)')
667
744
  .option('--type <type>', 'New type')
668
745
  .option('--title <title>', 'New title')
669
746
  .option('--content <text>', 'New content')
@@ -710,17 +787,20 @@ idea
710
787
  });
711
788
  });
712
789
  idea
713
- .command('add')
790
+ .command('add [content]')
714
791
  .description('Add a new idea')
715
- .requiredOption('--content <text>', 'Idea content')
792
+ // Content is positional, matching every other "create something" command.
793
+ // `--content` stays as a hidden back-compat alias.
794
+ .addOption(new Option('--content <text>', 'Deprecated: pass the content positionally').hideHelp())
716
795
  .option('--title <title>', 'Optional short title')
717
796
  .option('--priority <priority>', 'Priority (critical, high, medium, low)')
718
- .action(async (opts, cmd) => {
797
+ .action(async (content, opts, cmd) => {
719
798
  const globalOpts = cmd.optsWithGlobals();
720
799
  await ideaAddCommand({
721
800
  json: globalOpts.json,
722
801
  area: globalOpts.area,
723
- content: opts.content,
802
+ yes: globalOpts.yes,
803
+ content: content ?? opts.content,
724
804
  title: opts.title,
725
805
  priority: opts.priority,
726
806
  });
@@ -744,13 +824,23 @@ const orbit = program
744
824
  orbit
745
825
  .command('list')
746
826
  .description('List orbits (workspace-scoped from the area context)')
747
- .option('--workspace <id>', 'Filter by workspace UUID')
827
+ .option('--workspace-id <slug-or-uuid>', 'Filter by workspace (slug or UUID)')
828
+ // Back-compat: `--workspace <id>` was this filter's original spelling, which collided with
829
+ // the program-level `--workspace <path>` (a local directory that forces local mode).
830
+ // Hidden, still accepted, warns once.
831
+ .addOption(new Option('--workspace <slug-or-uuid>', 'Deprecated alias for --workspace-id').hideHelp())
748
832
  .action(async (opts, cmd) => {
749
833
  const globalOpts = cmd.optsWithGlobals();
834
+ if (opts.workspace && !opts.workspaceId) {
835
+ console.error('Warning: `orbit list --workspace` is deprecated (it collides with the global ' +
836
+ '`--workspace <path>`). Use `--workspace-id` instead.');
837
+ }
750
838
  await orbitListCommand({
751
839
  json: globalOpts.json,
752
840
  area: globalOpts.area,
753
- workspace: opts.workspace,
841
+ // Only the *subcommand* flags are considered: the global `--workspace` is a directory,
842
+ // never a filter. The global `--workspace-id` is picked up as the last fallback.
843
+ workspaceId: opts.workspaceId ?? opts.workspace ?? globalOpts.workspaceId,
754
844
  });
755
845
  });
756
846
  orbit
@@ -813,7 +903,10 @@ orbit
813
903
  .command('attach <orbit-id-or-slug>')
814
904
  .description('Attach an object to an orbit')
815
905
  .requiredOption('--type <type>', 'Object type: task, issue, vibe, idea, document')
816
- .requiredOption('--id <uuid>', 'Object UUID')
906
+ // OrbitController::findOrbitItemObject now resolves display numbers via entityRefColumn()
907
+ // for the numbered types (task/issue/vibe/idea). Documents have no display number, so
908
+ // `--type document` still requires a UUID.
909
+ .requiredOption('--id <id-or-number>', 'Object UUID or display number (TS-/IS-/VB-/ID-); documents are UUID-only')
817
910
  .option('--note <text>', 'Optional note for the attachment')
818
911
  .action(async (orbitIdOrSlug, opts, cmd) => {
819
912
  const globalOpts = cmd.optsWithGlobals();
@@ -829,7 +922,7 @@ orbit
829
922
  .command('detach <orbit-id-or-slug>')
830
923
  .description('Detach an object from an orbit')
831
924
  .requiredOption('--type <type>', 'Object type: task, issue, vibe, idea, document')
832
- .requiredOption('--id <uuid>', 'Object UUID')
925
+ .requiredOption('--id <id-or-number>', 'Object UUID or display number (TS-/IS-/VB-/ID-); documents are UUID-only')
833
926
  .action(async (orbitIdOrSlug, opts, cmd) => {
834
927
  const globalOpts = cmd.optsWithGlobals();
835
928
  await orbitDetachCommand(orbitIdOrSlug, {
@@ -906,24 +999,33 @@ intent
906
999
  .command('create <title>')
907
1000
  .description('Create a new intent')
908
1001
  .option('--outcome <text>', 'Desired outcome')
1002
+ .option('--design <text>', 'Design notes (markdown)')
1003
+ .option('--plan <text>', 'Plan notes (markdown)')
909
1004
  .option('--status <status>', 'Initial status: new, design, plan, build, done, cancelled (default new)')
910
- .option('--orbit <id>', 'Attach the intent to an orbit after creation')
1005
+ // PUT /intents/{id}/orbit validates `orbit_id` as `uuid|exists:orbits,id` unlike
1006
+ // POST /tasks it takes no slug. `orbit list` prints the UUID (TS-adanc8).
1007
+ .option('--orbit <uuid>', 'Attach the intent to an orbit after creation (UUID, not a slug)')
911
1008
  .action(async (title, opts, cmd) => {
912
1009
  const globalOpts = cmd.optsWithGlobals();
913
1010
  await intentCreateCommand(title, {
914
1011
  json: globalOpts.json,
915
1012
  area: globalOpts.area,
916
1013
  outcome: opts.outcome,
1014
+ design: opts.design,
1015
+ plan: opts.plan,
917
1016
  status: opts.status,
918
1017
  orbit: opts.orbit,
919
1018
  });
920
1019
  });
921
1020
  intent
922
1021
  .command('update <id-or-number>')
923
- .description('Update an intent (title, outcome, status)')
1022
+ .description('Update an intent (title, outcome, design, plan, status)')
924
1023
  .option('--title <title>', 'New title')
925
1024
  .option('--outcome <text>', 'New outcome')
1025
+ .option('--design <text>', 'New design notes (markdown)')
1026
+ .option('--plan <text>', 'New plan notes (markdown)')
926
1027
  .option('--status <status>', 'Status: new, design, plan, build, done, cancelled')
1028
+ .option('--log <type:content>', LOG_FLAG_HELP, collect, [])
927
1029
  .action(async (idOrNumber, opts, cmd) => {
928
1030
  const globalOpts = cmd.optsWithGlobals();
929
1031
  await intentUpdateCommand(idOrNumber, {
@@ -931,14 +1033,17 @@ intent
931
1033
  area: globalOpts.area,
932
1034
  title: opts.title,
933
1035
  outcome: opts.outcome,
1036
+ design: opts.design,
1037
+ plan: opts.plan,
934
1038
  status: opts.status,
1039
+ log: opts.log,
935
1040
  });
936
1041
  });
937
1042
  intent
938
1043
  .command('link <id-or-number>')
939
1044
  .description('Link a task or issue to an intent')
940
1045
  .requiredOption('--type <type>', 'Object type: task, issue')
941
- .requiredOption('--id <uuid>', 'Object UUID')
1046
+ .requiredOption('--id <uuid>', 'Object UUID (this endpoint does not accept display numbers)')
942
1047
  .action(async (idOrNumber, opts, cmd) => {
943
1048
  const globalOpts = cmd.optsWithGlobals();
944
1049
  await intentLinkCommand(idOrNumber, {
@@ -952,7 +1057,7 @@ intent
952
1057
  .command('unlink <id-or-number>')
953
1058
  .description('Unlink a task or issue from an intent')
954
1059
  .requiredOption('--type <type>', 'Object type: task, issue')
955
- .requiredOption('--id <uuid>', 'Object UUID')
1060
+ .requiredOption('--id <uuid>', 'Object UUID (this endpoint does not accept display numbers)')
956
1061
  .action(async (idOrNumber, opts, cmd) => {
957
1062
  const globalOpts = cmd.optsWithGlobals();
958
1063
  await intentUnlinkCommand(idOrNumber, {
@@ -963,8 +1068,8 @@ intent
963
1068
  });
964
1069
  });
965
1070
  intent
966
- .command('orbit <id-or-number> [orbit-id]')
967
- .description('Set or clear the orbit of an intent')
1071
+ .command('orbit <id-or-number> [orbit-uuid]')
1072
+ .description('Set or clear the orbit of an intent (the orbit is named by UUID, not by slug)')
968
1073
  .option('--clear', 'Clear the orbit instead of setting one')
969
1074
  .action(async (idOrNumber, orbitId, opts, cmd) => {
970
1075
  const globalOpts = cmd.optsWithGlobals();
@@ -1025,6 +1130,7 @@ mission
1025
1130
  .option('--desc <description>', 'New description')
1026
1131
  .option('--outcome <text>', 'New outcome')
1027
1132
  .option('--end-date <date>', 'New end date (YYYY-MM-DD)')
1133
+ .option('--log <type:content>', LOG_FLAG_HELP, collect, [])
1028
1134
  .action(async (idOrNumber, opts, cmd) => {
1029
1135
  const globalOpts = cmd.optsWithGlobals();
1030
1136
  await missionUpdateCommand(idOrNumber, {
@@ -1034,25 +1140,28 @@ mission
1034
1140
  desc: opts.desc,
1035
1141
  outcome: opts.outcome,
1036
1142
  endDate: opts.endDate,
1143
+ log: opts.log,
1037
1144
  });
1038
1145
  });
1039
1146
  mission
1040
1147
  .command('status <id-or-number> <status>')
1041
1148
  .description('Set mission lifecycle status (active, completed, cancelled; only one mission can be active)')
1042
1149
  .option('--move-to <mission-id>', 'Mission to move unfinished work to when completing/cancelling')
1150
+ .option('--log <type:content>', LOG_FLAG_HELP, collect, [])
1043
1151
  .action(async (idOrNumber, status, opts, cmd) => {
1044
1152
  const globalOpts = cmd.optsWithGlobals();
1045
1153
  await missionStatusCommand(idOrNumber, status, {
1046
1154
  json: globalOpts.json,
1047
1155
  area: globalOpts.area,
1048
1156
  moveTo: opts.moveTo,
1157
+ log: opts.log,
1049
1158
  });
1050
1159
  });
1051
1160
  mission
1052
1161
  .command('link <id-or-number>')
1053
1162
  .description('Link a task, issue, idea, or intent to a mission (clears intent_id on tasks/issues)')
1054
1163
  .requiredOption('--type <type>', 'Object type: task, issue, idea, intent')
1055
- .requiredOption('--id <uuid>', 'Object UUID')
1164
+ .requiredOption('--id <uuid>', 'Object UUID (this endpoint does not accept display numbers)')
1056
1165
  .action(async (idOrNumber, opts, cmd) => {
1057
1166
  const globalOpts = cmd.optsWithGlobals();
1058
1167
  await missionLinkCommand(idOrNumber, {
@@ -1066,7 +1175,7 @@ mission
1066
1175
  .command('unlink <id-or-number>')
1067
1176
  .description('Unlink a directly-linked item from a mission')
1068
1177
  .requiredOption('--type <type>', 'Object type: task, issue, idea, intent')
1069
- .requiredOption('--id <uuid>', 'Object UUID')
1178
+ .requiredOption('--id <uuid>', 'Object UUID (this endpoint does not accept display numbers)')
1070
1179
  .action(async (idOrNumber, opts, cmd) => {
1071
1180
  const globalOpts = cmd.optsWithGlobals();
1072
1181
  await missionUnlinkCommand(idOrNumber, {