overlord-cli 4.14.0 → 4.16.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.
@@ -69,7 +69,7 @@ function resolveProtocolMetadata(flags = {}, base = {}) {
69
69
  const metadata = { ...base };
70
70
 
71
71
  if (flags['metadata-json']) {
72
- const parsed = JSON.parse(String(flags['metadata-json']));
72
+ const parsed = parseJsonFlag('--metadata-json', flags['metadata-json']);
73
73
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
74
74
  throw new Error('--metadata-json must be a JSON object');
75
75
  }
@@ -215,6 +215,15 @@ function requireFlag(flags, name, envAlias) {
215
215
  return String(value);
216
216
  }
217
217
 
218
+ function parseJsonFlag(flagName, rawValue) {
219
+ try {
220
+ return JSON.parse(String(rawValue));
221
+ } catch (err) {
222
+ const detail = err instanceof Error ? err.message : String(err);
223
+ throw new Error(`${flagName} must be valid JSON: ${detail}`);
224
+ }
225
+ }
226
+
218
227
  function readTextFile(filePath, label) {
219
228
  try {
220
229
  return fs.readFileSync(filePath, 'utf8');
@@ -286,11 +295,7 @@ async function resolveChangeRationales(flags) {
286
295
  );
287
296
  }
288
297
  if (flags['change-rationales-json']) {
289
- try {
290
- return JSON.parse(String(flags['change-rationales-json']));
291
- } catch {
292
- throw new Error('--change-rationales-json must be valid JSON');
293
- }
298
+ return parseJsonFlag('--change-rationales-json', flags['change-rationales-json']);
294
299
  }
295
300
  return [];
296
301
  }
@@ -534,7 +539,7 @@ async function protocolUpdate(args) {
534
539
  : {}),
535
540
  ...(flags.phase ? { phase: String(flags.phase) } : {}),
536
541
  ...(flags['event-type'] ? { eventType: String(flags['event-type']) } : {}),
537
- ...(flags['payload-json'] ? { payload: JSON.parse(String(flags['payload-json'])) } : {}),
542
+ ...(flags['payload-json'] ? { payload: parseJsonFlag('--payload-json', flags['payload-json']) } : {}),
538
543
  ...(changeRationales.length > 0 ? { changeRationales } : {})
539
544
  };
540
545
 
@@ -613,7 +618,7 @@ async function protocolAsk(args) {
613
618
  ticketId,
614
619
  question,
615
620
  ...(flags.phase ? { phase: String(flags.phase) } : {}),
616
- ...(flags['payload-json'] ? { payload: JSON.parse(String(flags['payload-json'])) } : {})
621
+ ...(flags['payload-json'] ? { payload: parseJsonFlag('--payload-json', flags['payload-json']) } : {})
617
622
  };
618
623
 
619
624
  const data = await apiPost(platformUrl, agentToken, localSecret, '/api/protocol/ask', body, timeoutMs);
@@ -750,11 +755,7 @@ async function protocolDeliver(args) {
750
755
  if (flags['artifacts-file']) {
751
756
  artifacts = await readJsonFileOrStdin(String(flags['artifacts-file']), '--artifacts-file');
752
757
  } else if (flags['artifacts-json']) {
753
- try {
754
- artifacts = JSON.parse(String(flags['artifacts-json']));
755
- } catch {
756
- throw new Error('--artifacts-json must be valid JSON');
757
- }
758
+ artifacts = parseJsonFlag('--artifacts-json', flags['artifacts-json']);
758
759
  }
759
760
 
760
761
  if (deliverPayload && (flags['change-rationales-file'] || flags['change-rationales-json'])) {
@@ -805,7 +806,7 @@ async function protocolArtifactPrepareUpload(args) {
805
806
  ...(flags['artifact-type'] ? { artifactType: String(flags['artifact-type']) } : {}),
806
807
  ...(flags['content-type'] ? { contentType: String(flags['content-type']) } : {}),
807
808
  ...(flags['file-size'] ? { fileSize: parseInt(String(flags['file-size']), 10) } : {}),
808
- ...(flags['metadata-json'] ? { metadata: JSON.parse(String(flags['metadata-json'])) } : {})
809
+ ...(flags['metadata-json'] ? { metadata: parseJsonFlag('--metadata-json', flags['metadata-json']) } : {})
809
810
  };
810
811
 
811
812
  const data = await apiPost(
@@ -838,7 +839,7 @@ async function protocolArtifactFinalizeUpload(args) {
838
839
  ...(flags['artifact-type'] ? { artifactType: String(flags['artifact-type']) } : {}),
839
840
  ...(flags['content-type'] ? { contentType: String(flags['content-type']) } : {}),
840
841
  ...(flags['file-size'] ? { fileSize: parseInt(String(flags['file-size']), 10) } : {}),
841
- ...(flags['metadata-json'] ? { metadata: JSON.parse(String(flags['metadata-json'])) } : {})
842
+ ...(flags['metadata-json'] ? { metadata: parseJsonFlag('--metadata-json', flags['metadata-json']) } : {})
842
843
  };
843
844
 
844
845
  const data = await apiPost(
@@ -915,7 +916,7 @@ async function protocolArtifactUploadFile(args) {
915
916
  artifactType: String(flags['artifact-type'] ?? 'document'),
916
917
  contentType,
917
918
  fileSize: fileStats.size,
918
- ...(flags['metadata-json'] ? { metadata: JSON.parse(String(flags['metadata-json'])) } : {})
919
+ ...(flags['metadata-json'] ? { metadata: parseJsonFlag('--metadata-json', flags['metadata-json']) } : {})
919
920
  },
920
921
  timeoutMs
921
922
  );
@@ -941,7 +942,7 @@ async function protocolArtifactUploadFile(args) {
941
942
  artifactType: String(flags['artifact-type'] ?? 'document'),
942
943
  contentType,
943
944
  fileSize: fileStats.size,
944
- ...(flags['metadata-json'] ? { metadata: JSON.parse(String(flags['metadata-json'])) } : {})
945
+ ...(flags['metadata-json'] ? { metadata: parseJsonFlag('--metadata-json', flags['metadata-json']) } : {})
945
946
  },
946
947
  timeoutMs
947
948
  );
@@ -1044,8 +1045,9 @@ async function protocolSpawn(args) {
1044
1045
  const agentIdentifier = resolveProtocolAgentIdentifier(flags);
1045
1046
  const modelIdentifier = resolveProtocolModelIdentifier(flags);
1046
1047
 
1047
- // When --project-id is not provided, auto-send cwd as workingDirectory
1048
- // so the server can resolve the project from the local_working_directory setting.
1048
+ // When --project-id is not provided, auto-send cwd as workingDirectory so
1049
+ // the server can resolve the project from the caller's project_user
1050
+ // local_working_directory setting.
1049
1051
  const workingDirectory = flags['working-directory'] ?? (!flags['project-id'] ? process.cwd() : undefined);
1050
1052
 
1051
1053
  const body = {
@@ -1147,7 +1149,7 @@ async function protocolCreateTicket(args) {
1147
1149
  const projectId = discovered?.project?.id;
1148
1150
  if (!projectId) {
1149
1151
  throw new Error(
1150
- 'Could not resolve project from working directory. Set project local working directory in Overlord or pass --working-directory.'
1152
+ "Could not resolve project from working directory. Set your local working directory for this project in Overlord or pass --working-directory."
1151
1153
  );
1152
1154
  }
1153
1155
 
@@ -1215,8 +1217,8 @@ or attach to an existing ticket with \`ovld protocol attach --ticket-id <id>\`.
1215
1217
 
1216
1218
  Project discovery:
1217
1219
  When spawning or creating tickets, the CLI automatically resolves the correct
1218
- project by matching your current working directory against each project's
1219
- configured "Local working directory" (set in Project Settings in the Overlord UI).
1220
+ project by matching your current working directory against your configured
1221
+ "Local working directory" for that project (stored per user in Overlord).
1220
1222
  You can also discover the project explicitly:
1221
1223
 
1222
1224
  ovld protocol discover-project
@@ -1267,13 +1269,13 @@ auth-status:
1267
1269
  discover-project:
1268
1270
  Purpose:
1269
1271
  Resolve the Overlord project that corresponds to the current (or given) working directory.
1270
- Uses each project's "Local working directory" setting for matching.
1272
+ Uses the caller's configured "Local working directory" for matching.
1271
1273
  Optional:
1272
1274
  --working-directory <path> Directory to match (default: current working directory)
1273
1275
  Returns:
1274
1276
  Project JSON with id, name, organizationId. Prints PROJECT_ID=<id> on stderr.
1275
1277
  Notes:
1276
- Set the local working directory for a project in the Overlord UI under Project Settings.
1278
+ Set your local working directory for a project in the Overlord UI under Project Settings.
1277
1279
  When no match is found, returns a 404 with a hint.
1278
1280
 
1279
1281
  attach:
@@ -1406,7 +1408,7 @@ spawn:
1406
1408
  Purpose:
1407
1409
  Create a follow-up ticket and attach to it in one call.
1408
1410
  When --project-id is omitted, automatically resolves the project from the
1409
- current working directory (matching against each project's local_working_directory).
1411
+ current working directory (matching against the caller's project_user.local_working_directory).
1410
1412
  Required:
1411
1413
  --objective <text>
1412
1414
  Optional:
@@ -1437,7 +1439,7 @@ create:
1437
1439
  Optional:
1438
1440
  --session-key <key>
1439
1441
  --ticket-id <id>
1440
- --working-directory <path> Resolve project by local working directory (default: cwd)
1442
+ --working-directory <path> Resolve project by your configured local working directory (default: cwd)
1441
1443
  --title <text>
1442
1444
  --priority <level> low | medium | high | urgent
1443
1445
  --acceptance-criteria <text>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "4.14.0",
3
+ "version": "4.16.0",
4
4
  "description": "Overlord CLI — launch AI agents on tickets from anywhere",
5
5
  "type": "module",
6
6
  "bin": {
@@ -73,7 +73,7 @@ Use this mode when the conversation starts normally and the user asks Claude to
73
73
 
74
74
  1. If the user wants to create tickets (and does not ask to start execution), run `ovld protocol create --agent claude-code --objective "..."`.
75
75
  - When `--session-key` and `--ticket-id` are provided, it creates a follow-up draft.
76
- - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to Overlord `local_working_directory`, then creates a standalone draft.
76
+ - When session flags are omitted, it resolves the project by matching current working directory (or `--working-directory`) to the caller's configured Overlord `project_user.local_working_directory`, then creates a standalone draft.
77
77
  2. Default to `create` for new tickets. Only use `/overlord:spawn` or `ovld protocol spawn --agent claude-code --objective "..."` when the user explicitly asks to create and execute immediately.
78
78
  `spawn` creates the ticket in `execute` status and attaches immediately.
79
79
  3. If the user already has a ticket ID and only wants to inspect it, use `/overlord:load` or run `ovld protocol load-context --ticket-id <ticket-id>`.