overlord-cli 4.15.0 → 4.17.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
  );
@@ -1047,7 +1048,9 @@ async function protocolSpawn(args) {
1047
1048
  // When --project-id is not provided, auto-send cwd as workingDirectory so
1048
1049
  // the server can resolve the project from the caller's project_user
1049
1050
  // local_working_directory setting.
1050
- const workingDirectory = flags['working-directory'] ?? (!flags['project-id'] ? process.cwd() : undefined);
1051
+ const personal = Boolean(flags.personal);
1052
+ const workingDirectory =
1053
+ flags['working-directory'] ?? (!flags['project-id'] && !personal ? process.cwd() : undefined);
1051
1054
 
1052
1055
  const body = {
1053
1056
  objective,
@@ -1057,6 +1060,7 @@ async function protocolSpawn(args) {
1057
1060
  ...(flags.title ? { title: String(flags.title) } : {}),
1058
1061
  ...(flags.priority ? { priority: String(flags.priority) } : {}),
1059
1062
  ...(flags['project-id'] ? { projectId: String(flags['project-id']) } : {}),
1063
+ ...(personal ? { personal: true } : {}),
1060
1064
  ...(workingDirectory ? { workingDirectory: String(workingDirectory) } : {}),
1061
1065
  ...(flags['acceptance-criteria'] ? { acceptanceCriteria: String(flags['acceptance-criteria']) } : {}),
1062
1066
  ...(flags['available-tools'] ? { availableTools: String(flags['available-tools']) } : {}),
@@ -1135,26 +1139,16 @@ async function protocolCreateTicket(args) {
1135
1139
  );
1136
1140
  }
1137
1141
 
1138
- const workingDirectory = String(flags['working-directory'] ?? process.cwd());
1139
- const discovered = await apiPost(
1140
- platformUrl,
1141
- agentToken,
1142
- localSecret,
1143
- '/api/protocol/discover-project',
1144
- { workingDirectory },
1145
- timeoutMs
1146
- );
1147
-
1148
- const projectId = discovered?.project?.id;
1149
- if (!projectId) {
1150
- throw new Error(
1151
- "Could not resolve project from working directory. Set your local working directory for this project in Overlord or pass --working-directory."
1152
- );
1153
- }
1142
+ const standaloneWorkingDirectory =
1143
+ !flags.personal && !flags['project-id']
1144
+ ? String(flags['working-directory'] ?? process.cwd())
1145
+ : undefined;
1154
1146
 
1155
1147
  const standaloneBody = {
1156
1148
  objective,
1157
- projectId,
1149
+ ...(flags.personal ? { personal: true } : {}),
1150
+ ...(!flags.personal && flags['project-id'] ? { projectId: String(flags['project-id']) } : {}),
1151
+ ...(standaloneWorkingDirectory ? { workingDirectory: standaloneWorkingDirectory } : {}),
1158
1152
  ...(flags.title ? { title: String(flags.title) } : {}),
1159
1153
  ...(flags.priority ? { priority: String(flags.priority) } : {}),
1160
1154
  ...(flags['acceptance-criteria'] ? { acceptanceCriteria: String(flags['acceptance-criteria']) } : {}),
@@ -1224,6 +1218,7 @@ Project discovery:
1224
1218
  ovld protocol discover-project --working-directory /path/to/repo
1225
1219
 
1226
1220
  Use --project-id to override automatic resolution on spawn or ticket creation.
1221
+ Use --personal to create a private ticket without assigning any project.
1227
1222
 
1228
1223
  Subcommands:
1229
1224
  auth-status Return machine-readable auth status for agent runtimes
@@ -1414,6 +1409,7 @@ spawn:
1414
1409
  --title <text>
1415
1410
  --priority <level> low | medium | high | urgent
1416
1411
  --project-id <id> Explicit project; skips working-directory resolution
1412
+ --personal Create the ticket without assigning a project
1417
1413
  --working-directory <path> Override cwd for project resolution (default: cwd)
1418
1414
  --acceptance-criteria <text>
1419
1415
  --available-tools <text>
@@ -1439,6 +1435,8 @@ create:
1439
1435
  --session-key <key>
1440
1436
  --ticket-id <id>
1441
1437
  --working-directory <path> Resolve project by your configured local working directory (default: cwd)
1438
+ --project-id <id> Explicit project for standalone draft creation
1439
+ --personal Create a private standalone draft without a project
1442
1440
  --title <text>
1443
1441
  --priority <level> low | medium | high | urgent
1444
1442
  --acceptance-criteria <text>
@@ -1450,7 +1448,7 @@ create:
1450
1448
  Returns:
1451
1449
  New draft ticket JSON (follow-up draft when session flags are provided)
1452
1450
  Notes:
1453
- Standalone create auto-discovers the project from the current working directory.
1451
+ Standalone create auto-discovers the project from the current working directory unless --personal is set.
1454
1452
  Follow-up create requires both --session-key and --ticket-id.
1455
1453
 
1456
1454
  artifact-prepare-upload:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overlord-cli",
3
- "version": "4.15.0",
3
+ "version": "4.17.0",
4
4
  "description": "Overlord CLI — launch AI agents on tickets from anywhere",
5
5
  "type": "module",
6
6
  "bin": {