overlord-cli 0.2608191441.0 → 0.2608191554.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.
@@ -85,9 +85,12 @@ const tools = [
85
85
  name: 'overlord_resolve_project',
86
86
  title: 'Resolve Overlord project',
87
87
  description:
88
- 'Resolve a project by id, slug, name, or linked repository directory metadata exposed to the MCP client.',
88
+ "Resolve a project by id, slug, name, or linked repository directory metadata exposed to the MCP client. Names are matched case-insensitively across every workspace the caller can reach; a name matching in more than one returns a 'project_selection_required' result listing the candidates with their workspaces — ask the user, then retry with workspaceId.",
89
89
  inputSchema: objectSchema({
90
90
  projectId: stringProperty('Explicit Overlord project id, slug, or project name.'),
91
+ workspaceId: stringProperty(
92
+ "Optional workspace (id, slug, or name) narrowing an ambiguous projectId. Use this to retry after a 'project_selection_required' result."
93
+ ),
91
94
  directory: stringProperty(
92
95
  'Optional repository directory path when the MCP client can expose one with .overlord/project.json.'
93
96
  )
@@ -117,7 +120,12 @@ const tools = [
117
120
  "Read one project's board columns. Status names and order are defined per project, so two projects in the same workspace can label the same lifecycle differently; each status also carries a stable type (draft, next, execute, review, complete, blocked, cancelled) that does not vary by project.",
118
121
  inputSchema: objectSchema(
119
122
  {
120
- projectId: stringProperty('Overlord project id, slug, or name.')
123
+ projectId: stringProperty(
124
+ "Overlord project id, slug, or name. An ambiguous name returns 'project_selection_required'; retry with workspaceId."
125
+ ),
126
+ workspaceId: stringProperty(
127
+ 'Optional workspace (id, slug, or name) narrowing an ambiguous projectId.'
128
+ )
121
129
  },
122
130
  ['projectId']
123
131
  )
@@ -133,12 +141,17 @@ const tools = [
133
141
  'Comma-separated status TYPES, such as draft,next,execute,review. Types are workspace-invariant (draft, next, execute, review, complete, blocked, cancelled). Project-defined status names are not accepted here.'
134
142
  ),
135
143
  projectId: stringProperty(
136
- 'Optional stable Overlord project UUID. Resolve a human project reference first.'
144
+ "Optional project reference: a stable UUID, a slug, or the project name as the user said it. Names are matched case-insensitively against the projects the caller can reach. If the name matches in more than one workspace the call returns a 'project_selection_required' result listing the candidates with their workspaces — ask the user which one, then retry with workspaceId set."
145
+ ),
146
+ workspaceId: stringProperty(
147
+ "Optional workspace (id, slug, or name) narrowing an ambiguous projectId. Use this to retry after a 'project_selection_required' result."
137
148
  ),
138
149
  resourceKey: stringProperty(
139
150
  'Optional logical resource key, matched by name within selected projects.'
140
151
  ),
141
- dateField: stringProperty('Date column for an explicit range: createdAt or updatedAt.'),
152
+ dateField: stringProperty(
153
+ 'Date column for an explicit range: createdAt, updatedAt, or dueDatetime. Use dueDatetime for questions about what is scheduled or due; missions with no due date are excluded from a dueDatetime range.'
154
+ ),
142
155
  from: stringProperty('Optional inclusive ISO-8601 date/time lower bound.'),
143
156
  to: stringProperty('Optional exclusive ISO-8601 date/time upper bound.'),
144
157
  limit: {
@@ -422,17 +435,6 @@ function requiredString(args, name) {
422
435
  return value;
423
436
  }
424
437
 
425
- function optionalProjectUuid(args) {
426
- const projectId = optionalString(args, 'projectId');
427
- if (!projectId) return null;
428
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(projectId)) {
429
- throw new Error(
430
- 'projectId must be a stable Overlord project UUID; resolve human references first'
431
- );
432
- }
433
- return projectId;
434
- }
435
-
436
438
  /**
437
439
  * Forward mission/objective addressing to the CLI. The CLI owns the shared
438
440
  * `@overlord/contract` parser and derives a mission from a display id before it
@@ -459,6 +461,9 @@ function callOverlordTool(name, args) {
459
461
  ...(optionalString(args, 'projectId')
460
462
  ? { 'project-id': requiredString(args, 'projectId') }
461
463
  : {}),
464
+ ...(optionalString(args, 'workspaceId')
465
+ ? { 'workspace-id': requiredString(args, 'workspaceId') }
466
+ : {}),
462
467
  ...(optionalString(args, 'directory') ? { directory: requiredString(args, 'directory') } : {})
463
468
  });
464
469
  }
@@ -476,7 +481,10 @@ function callOverlordTool(name, args) {
476
481
  }
477
482
  if (name === 'overlord_list_project_statuses') {
478
483
  return runProtocol('statuses', {
479
- 'project-id': requiredString(args, 'projectId')
484
+ 'project-id': requiredString(args, 'projectId'),
485
+ ...(optionalString(args, 'workspaceId')
486
+ ? { 'workspace-id': requiredString(args, 'workspaceId') }
487
+ : {})
480
488
  });
481
489
  }
482
490
  if (name === 'overlord_search_missions') {
@@ -484,7 +492,12 @@ function callOverlordTool(name, args) {
484
492
  'response-version': '2',
485
493
  ...(optionalString(args, 'query') ? { query: requiredString(args, 'query') } : {}),
486
494
  ...(optionalString(args, 'status') ? { status: requiredString(args, 'status') } : {}),
487
- ...(optionalProjectUuid(args) ? { 'project-id': optionalProjectUuid(args) } : {}),
495
+ ...(optionalString(args, 'projectId')
496
+ ? { 'project-id': requiredString(args, 'projectId') }
497
+ : {}),
498
+ ...(optionalString(args, 'workspaceId')
499
+ ? { 'workspace-id': requiredString(args, 'workspaceId') }
500
+ : {}),
488
501
  ...(optionalString(args, 'resourceKey')
489
502
  ? { 'resource-key': requiredString(args, 'resourceKey') }
490
503
  : {}),
package/dist/index.js CHANGED
@@ -6676,6 +6676,7 @@ var init_resource_paths = __esm({
6676
6676
  });
6677
6677
 
6678
6678
  // ../packages/contract/dist/index.js
6679
+ var MY_MISSIONS_COLUMNS, MY_MISSIONS_COLUMN_TYPES;
6679
6680
  var init_dist2 = __esm({
6680
6681
  "../packages/contract/dist/index.js"() {
6681
6682
  "use strict";
@@ -6683,6 +6684,13 @@ var init_dist2 = __esm({
6683
6684
  init_launch_variables();
6684
6685
  init_objective_ref();
6685
6686
  init_resource_paths();
6687
+ MY_MISSIONS_COLUMNS = [
6688
+ { type: "next", label: "Next" },
6689
+ { type: "execute", label: "Executing" },
6690
+ { type: "review", label: "Review" },
6691
+ { type: "complete", label: "Completed" }
6692
+ ];
6693
+ MY_MISSIONS_COLUMN_TYPES = MY_MISSIONS_COLUMNS.map((column) => column.type);
6686
6694
  }
6687
6695
  });
6688
6696
 
@@ -19496,7 +19504,8 @@ function discoverProjectJsonFromFilesystem({
19496
19504
  }
19497
19505
  async function resolveProjectByIdOrName({
19498
19506
  backend,
19499
- projectRef
19507
+ projectRef,
19508
+ workspaceRef
19500
19509
  }) {
19501
19510
  const trimmed8 = projectRef.trim();
19502
19511
  try {
@@ -19505,11 +19514,45 @@ async function resolveProjectByIdOrName({
19505
19514
  if (!(error instanceof CliError) || !/project not found/i.test(error.message)) throw error;
19506
19515
  }
19507
19516
  const projects = await listAccessibleProjects({ backend });
19508
- const match = projects.find((project) => project.name === trimmed8) ?? projects.find((project) => project.slug === trimmed8);
19509
- if (!match) {
19517
+ const needle = trimmed8.toLowerCase();
19518
+ const byName = projects.filter((project) => project.name.toLowerCase() === needle);
19519
+ const matches = byName.length > 0 ? byName : projects.filter((project) => project.slug.toLowerCase() === needle);
19520
+ if (matches.length === 0) {
19510
19521
  throw new CliError({ message: `Project not found: ${trimmed8}` });
19511
19522
  }
19512
- return match;
19523
+ const narrowed = await narrowByWorkspace({ backend, matches, workspaceRef });
19524
+ if (narrowed.length === 1) return narrowed[0];
19525
+ const workspaces = await workspaceLabels({ backend });
19526
+ const candidates = narrowed.map(
19527
+ (project) => ` ${project.id} (workspace: ${workspaces.get(project.workspaceId) ?? project.workspaceId})`
19528
+ ).join("\n");
19529
+ throw new CliError({
19530
+ message: `Project reference matches ${narrowed.length} projects: ${trimmed8}
19531
+ ${candidates}
19532
+ Re-run with --workspace-id <id|slug|name>, or pass the project id directly.`
19533
+ });
19534
+ }
19535
+ async function workspaceLabels({
19536
+ backend
19537
+ }) {
19538
+ const workspaces = await backend.get("/api/workspaces");
19539
+ return new Map(workspaces.map((workspace) => [workspace.id, workspace.name]));
19540
+ }
19541
+ async function narrowByWorkspace({
19542
+ backend,
19543
+ matches,
19544
+ workspaceRef
19545
+ }) {
19546
+ const hint = workspaceRef?.trim().toLowerCase();
19547
+ if (!hint || matches.length <= 1) return matches;
19548
+ const workspaces = await backend.get("/api/workspaces");
19549
+ const targets = new Set(
19550
+ workspaces.filter(
19551
+ (workspace) => workspace.id.toLowerCase() === hint || workspace.slug.toLowerCase() === hint || workspace.name.toLowerCase() === hint
19552
+ ).map((workspace) => workspace.id)
19553
+ );
19554
+ const narrowed = matches.filter((project) => targets.has(project.workspaceId));
19555
+ return narrowed.length > 0 ? narrowed : matches;
19513
19556
  }
19514
19557
  async function tryResolveProjectByIdOrName({
19515
19558
  backend,
@@ -20985,10 +21028,17 @@ search-missions:
20985
21028
  complete, blocked, cancelled. Project-defined status
20986
21029
  names (board column labels) are not accepted here \u2014
20987
21030
  use \`statuses\` to read one project's names.
20988
- --project-id <id|slug|name> Restrict to one project (CLI resolves human references)
21031
+ --project-id <id|slug|name> Restrict to one project. Names are matched
21032
+ case-insensitively across the workspaces you can reach;
21033
+ a name matching in two workspaces returns
21034
+ project_selection_required listing the candidates.
21035
+ --workspace-id <id|slug|name>
21036
+ Narrow an ambiguous --project-id
20989
21037
  --resource-key <csv> Restrict to logical resource key names
20990
- --date-field <createdAt|updatedAt>
20991
- Date column for explicit range filtering
21038
+ --date-field <createdAt|updatedAt|dueDatetime>
21039
+ Date column for explicit range filtering. Use dueDatetime
21040
+ for "scheduled"/"due" questions; missions with no due date
21041
+ are excluded from a dueDatetime range.
20992
21042
  --from <ISO-8601> Inclusive date/time lower bound
20993
21043
  --to <ISO-8601> Exclusive date/time upper bound
20994
21044
  --limit <n> Max results (default: 25)
@@ -24021,12 +24071,17 @@ async function runManagementCommand({
24021
24071
  const sub = parsed.positional[0];
24022
24072
  if (sub !== "list") {
24023
24073
  throw new CliError({
24024
- message: "Usage: ovld missions list [--status <csv>] [--project-id <id>] [--json]"
24074
+ message: "Usage: ovld missions list [--status <csv>] [--project-id <id|slug|name>] [--workspace-id <id|slug|name>] [--json]"
24025
24075
  });
24026
24076
  }
24027
24077
  const params = new URLSearchParams();
24028
24078
  const query = flagValue(parsed.flags, "--query");
24029
- const projectId = flagValue(parsed.flags, "--project-id");
24079
+ const projectRef = flagValue(parsed.flags, "--project-id");
24080
+ const projectId = projectRef ? (await resolveProjectByIdOrName({
24081
+ backend: runtime.backend,
24082
+ projectRef,
24083
+ workspaceRef: flagValue(parsed.flags, "--workspace-id")
24084
+ })).id : void 0;
24030
24085
  const limit = flagValue(parsed.flags, "--limit");
24031
24086
  const statusTypes = (flagValue(parsed.flags, "--status") ?? "").split(",").map((value) => value.trim()).filter((value) => value !== "");
24032
24087
  if (query) params.set("q", query);
@@ -24068,7 +24123,8 @@ async function runManagementCommand({
24068
24123
  }
24069
24124
  const project = await resolveProjectByIdOrName({
24070
24125
  backend: runtime.backend,
24071
- projectRef
24126
+ projectRef,
24127
+ workspaceRef: flagValue(parsed.flags, "--workspace-id")
24072
24128
  });
24073
24129
  const statuses = await runtime.backend.get(
24074
24130
  `/api/projects/${encodeURIComponent(project.id)}/statuses`