@sireai/optimus 0.1.2 → 0.1.3

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.
@@ -28,12 +28,13 @@ import { TaskDeliveryService } from "../task-environment/delivery/task-delivery-
28
28
  import { FeishuAnalysisDocService } from "../task-environment/delivery/feishu-analysis-doc-service.js";
29
29
  import { TaskPublicationService } from "../task-environment/delivery/task-publication-service.js";
30
30
  import { resolveDefaultConfigPath, resolveDefaultEnvPath, resolveOptimusHomeDir } from "../config/optimus-paths.js";
31
- import { checkForSelfUpdate, installSelfUpdate, readSelfUpdateState } from "./self-update.js";
31
+ import { checkForSelfUpdate, installSelfUpdate, recordSkippedSelfUpdate, readSelfUpdateState } from "./self-update.js";
32
32
  const CLI_ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
33
33
  const PACKAGE_JSON_PATH = join(CLI_ROOT_DIR, "package.json");
34
34
  const execFileAsync = promisify(execFile);
35
35
  const PACKAGE_NAME = "@sireai/optimus";
36
36
  const JIRA_CLI_ENTRY = join(CLI_ROOT_DIR, "dist", "integrations", "jira", "jira-cli.js");
37
+ const RELEASE_NOTES_URL = "https://github.com/SireAI/optimus/releases/latest";
37
38
  function renderSetupResult(result) {
38
39
  const lines = [];
39
40
  lines.push("Setup Complete");
@@ -131,39 +132,41 @@ function renderCliHelp() {
131
132
  "Usage:",
132
133
  " optimus <command> [options]",
133
134
  "",
134
- "First-time setup:",
135
+ "Get started:",
135
136
  " optimus setup",
136
- " optimus doctor --quick",
137
+ " optimus start",
137
138
  " optimus submit --title \"Login crash\" --description \"Crash on startup\"",
138
139
  "",
139
- "Core commands:",
140
- " start Start the resident runtime",
141
- " submit Submit a manual task",
142
- " upgrade Check for or install the latest Optimus version",
143
- " retry-task Requeue an existing task",
144
- " cancel-task Cancel a queued or running task",
145
- " triage-only Run resident triage without enqueuing a task",
146
- "",
147
- "Repository commands:",
140
+ "User commands:",
141
+ " help [command] Show CLI help or command help",
142
+ " setup Configure Optimus for first use or update config",
143
+ " start Start the resident runtime",
144
+ " submit Submit a manual task",
145
+ " retry-task Requeue an existing task",
146
+ " cancel-task Cancel a queued or running task",
147
+ " task-status Show task execution status",
148
+ " task-result Show persisted result, delivery, and publication data",
149
+ " task-events Show task timeline",
148
150
  " repo add|remove|update|list|doctor",
151
+ " doctor Show runtime, skills, and environment diagnostics",
152
+ " health-check Validate Codex/provider readiness",
153
+ " upgrade Check for or install the latest Optimus version",
154
+ " version Show the installed package version",
149
155
  "",
150
- "Inspection commands:",
151
- " health-check Validate Codex/provider readiness",
152
- " doctor Show runtime, skills, and environment diagnostics",
153
- " task-status Show task execution status",
154
- " task-result Show persisted result, delivery, and publication data",
156
+ "Advanced commands:",
155
157
  " delivery-status Show focused delivery/publication state",
156
- " task-events Show task timeline",
157
- " db-inspect Show raw persisted task data",
158
- "",
159
- "Delivery commands:",
160
158
  " notify-test Send or preview a Feishu test notification",
161
159
  " delivery-retry Replay notification delivery from persisted bundle data",
162
160
  " postrun-retry Rebuild postrun artifacts and optionally replay publication",
163
161
  "",
164
162
  "Integration commands:",
165
- " intake-status|intake-run|intake-disable|intake-enable|intake-reset-checkpoint",
166
163
  " jira-search|jira-get-issue|jira-search-assigned-open|jira-submit-issue|jira-submit-assigned-open|jira-add-comment|jira-poll-once|jira-poll-daemon",
164
+ " Example: optimus help jira-submit-issue",
165
+ "",
166
+ "Developer commands:",
167
+ " triage-only Run resident triage without enqueuing a task",
168
+ " intake-status|intake-run|intake-disable|intake-enable|intake-reset-checkpoint",
169
+ " db-inspect Show raw persisted task data",
167
170
  "",
168
171
  "Global options:",
169
172
  " -h, --help Show this help",
@@ -174,6 +177,242 @@ function renderCliHelp() {
174
177
  ` - Run \`optimus setup\` once; Optimus stores config and runtime data under ${resolveOptimusHomeDir()}.`
175
178
  ].join("\n");
176
179
  }
180
+ function renderCommandHelp(command) {
181
+ const normalized = command.trim();
182
+ const helpByCommand = {
183
+ submit: [
184
+ "optimus submit",
185
+ "",
186
+ "Usage:",
187
+ " optimus submit --title <title> --description <text> [options]",
188
+ "",
189
+ "Required:",
190
+ " --title <title> Task title",
191
+ " --description <text> Task description",
192
+ "",
193
+ "Common options:",
194
+ " --repo <alias> Target registered repository alias",
195
+ " --task-type <type> Task type, default bugfix",
196
+ " --source-ref <id> External issue key or source reference",
197
+ " --allow-duplicate Allow duplicate submission",
198
+ "",
199
+ "Example:",
200
+ " optimus submit --title \"Login crash\" --description \"Crash on startup\" --repo ohos-pre"
201
+ ].join("\n"),
202
+ "retry-task": [
203
+ "optimus retry-task",
204
+ "",
205
+ "Usage:",
206
+ " optimus retry-task --task-id <task-id> [options]",
207
+ "",
208
+ "Required:",
209
+ " --task-id <task-id> Existing task id",
210
+ "",
211
+ "Optional:",
212
+ " --reason <text> Retry reason"
213
+ ].join("\n"),
214
+ "cancel-task": [
215
+ "optimus cancel-task",
216
+ "",
217
+ "Usage:",
218
+ " optimus cancel-task --task-id <task-id> [options]",
219
+ "",
220
+ "Required:",
221
+ " --task-id <task-id> Existing task id",
222
+ "",
223
+ "Optional:",
224
+ " --reason <text> Cancel reason",
225
+ " --keep-artifacts Keep task artifacts after cancel",
226
+ " --force Force cancel metadata flag"
227
+ ].join("\n"),
228
+ "task-status": [
229
+ "optimus task-status",
230
+ "",
231
+ "Usage:",
232
+ " optimus task-status [options]",
233
+ "",
234
+ "Optional:",
235
+ " --task-id <task-id> Filter by task id",
236
+ " --status <status> Filter by task status",
237
+ " --active-only Show active tasks only",
238
+ " --limit <n> Max tasks to show, default 20"
239
+ ].join("\n"),
240
+ "task-result": [
241
+ "optimus task-result",
242
+ "",
243
+ "Usage:",
244
+ " optimus task-result --task-id <task-id> [options]",
245
+ "",
246
+ "Required:",
247
+ " --task-id <task-id> Existing task id",
248
+ "",
249
+ "Optional:",
250
+ " --format text Print compact text report",
251
+ " --print Alias for text output"
252
+ ].join("\n"),
253
+ "delivery-status": [
254
+ "optimus delivery-status",
255
+ "",
256
+ "Usage:",
257
+ " optimus delivery-status --task-id <task-id> [options]",
258
+ "",
259
+ "Required:",
260
+ " --task-id <task-id> Existing task id",
261
+ "",
262
+ "Optional:",
263
+ " --format text Print compact text report",
264
+ " --print Alias for text output"
265
+ ].join("\n"),
266
+ repo: [
267
+ "optimus repo",
268
+ "",
269
+ "Usage:",
270
+ " optimus repo add <path> [options]",
271
+ " optimus repo remove <path|alias>",
272
+ " optimus repo update <path|alias> [options]",
273
+ " optimus repo list",
274
+ " optimus repo doctor",
275
+ "",
276
+ "Common options:",
277
+ " --alias <name> Repository alias",
278
+ " --mode <copy|inplace> Execution mode",
279
+ "",
280
+ "Examples:",
281
+ " optimus repo add /path/to/repo --alias ohos-pre",
282
+ " optimus repo update ohos-pre --mode inplace",
283
+ " optimus repo remove ohos-pre"
284
+ ].join("\n"),
285
+ doctor: [
286
+ "optimus doctor",
287
+ "",
288
+ "Usage:",
289
+ " optimus doctor [options]",
290
+ "",
291
+ "Optional:",
292
+ " --quick Run compact readiness check",
293
+ " --json Print JSON when used with --quick",
294
+ " --format text Print text report"
295
+ ].join("\n"),
296
+ "jira-submit-issue": [
297
+ "optimus jira-submit-issue",
298
+ "",
299
+ "Usage:",
300
+ " optimus jira-submit-issue --issue-key <key> [options]",
301
+ "",
302
+ "Required:",
303
+ " --issue-key <key> Jira issue key, for example MICARAPPI-13178",
304
+ "",
305
+ "Common options:",
306
+ " --repo <alias> Target registered repository alias",
307
+ " --branch <name> Override target branch",
308
+ " --task-type <type> Task type, default bugfix",
309
+ " --allow-duplicate Allow duplicate submission",
310
+ " --comment-limit <n> Comment lines fetched from Jira, default 3",
311
+ " --dry-run Preview without creating a task",
312
+ "",
313
+ "Example:",
314
+ " optimus jira-submit-issue --issue-key MICARAPPI-13178 --repo ohos-pre --allow-duplicate"
315
+ ].join("\n"),
316
+ "jira-submit-assigned-open": [
317
+ "optimus jira-submit-assigned-open",
318
+ "",
319
+ "Usage:",
320
+ " optimus jira-submit-assigned-open [options]",
321
+ "",
322
+ "Common options:",
323
+ " --assignee <jira-user> Jira assignee, default current user",
324
+ " --repo <alias> Target registered repository alias",
325
+ " --projects-filter <csv> Restrict Jira projects",
326
+ " --limit <n> Max issues to submit, default 50",
327
+ " --allow-duplicate Allow duplicate submission",
328
+ " --dry-run Preview without creating tasks"
329
+ ].join("\n"),
330
+ "jira-get-issue": [
331
+ "optimus jira-get-issue",
332
+ "",
333
+ "Usage:",
334
+ " optimus jira-get-issue --issue-key <key> [options]",
335
+ "",
336
+ "Required:",
337
+ " --issue-key <key> Jira issue key",
338
+ "",
339
+ "Optional:",
340
+ " --fields <csv> Requested Jira fields",
341
+ " --expand <csv> Jira expand parameters",
342
+ " --properties <csv> Jira properties",
343
+ " --comment-limit <n> Comment limit, default 3",
344
+ " --update-history true|false Include update history"
345
+ ].join("\n"),
346
+ "jira-search": [
347
+ "optimus jira-search",
348
+ "",
349
+ "Usage:",
350
+ " optimus jira-search --jql <jql> [options]",
351
+ "",
352
+ "Required:",
353
+ " --jql <jql> Jira search JQL",
354
+ "",
355
+ "Optional:",
356
+ " --limit <n> Max issues, default 50",
357
+ " --start-at <n> Start offset, default 0",
358
+ " --fields <csv> Requested Jira fields",
359
+ " --expand <csv> Jira expand parameters",
360
+ " --projects-filter <csv> Restrict Jira projects"
361
+ ].join("\n"),
362
+ "jira-search-assigned-open": [
363
+ "optimus jira-search-assigned-open",
364
+ "",
365
+ "Usage:",
366
+ " optimus jira-search-assigned-open [options]",
367
+ "",
368
+ "Optional:",
369
+ " --assignee <jira-user> Jira assignee, default current user",
370
+ " --limit <n> Max issues, default 50",
371
+ " --start-at <n> Start offset, default 0",
372
+ " --fields <csv> Requested Jira fields"
373
+ ].join("\n"),
374
+ "jira-add-comment": [
375
+ "optimus jira-add-comment",
376
+ "",
377
+ "Usage:",
378
+ " optimus jira-add-comment --issue-key <key> --body <text>",
379
+ "",
380
+ "Required:",
381
+ " --issue-key <key> Jira issue key",
382
+ " --body <text> Comment body"
383
+ ].join("\n"),
384
+ "jira-poll-once": [
385
+ "optimus jira-poll-once",
386
+ "",
387
+ "Usage:",
388
+ " optimus jira-poll-once [options]",
389
+ "",
390
+ "Common options:",
391
+ " --assignee <jira-user> Jira assignee, default current user",
392
+ " --repo <alias> Target registered repository alias",
393
+ " --projects-filter <csv> Restrict Jira projects",
394
+ " --limit <n> Max issues, default 50",
395
+ " --allow-duplicate Allow duplicate submission",
396
+ " --dry-run Preview without creating tasks"
397
+ ].join("\n"),
398
+ "jira-poll-daemon": [
399
+ "optimus jira-poll-daemon",
400
+ "",
401
+ "Usage:",
402
+ " optimus jira-poll-daemon [options]",
403
+ "",
404
+ "Common options:",
405
+ " --interval-ms <ms> Poll interval, default 300000",
406
+ " --assignee <jira-user> Jira assignee, default current user",
407
+ " --repo <alias> Target registered repository alias",
408
+ " --projects-filter <csv> Restrict Jira projects",
409
+ " --limit <n> Max issues per poll, default 50",
410
+ " --allow-duplicate Allow duplicate submission",
411
+ " --dry-run Preview without creating tasks"
412
+ ].join("\n")
413
+ };
414
+ return helpByCommand[normalized];
415
+ }
177
416
  function mapJiraCommand(command) {
178
417
  switch (command) {
179
418
  case "jira-search":
@@ -276,18 +515,27 @@ function isInteractiveSelfUpdatePromptAllowed() {
276
515
  async function promptForStartupSelfUpdate(currentVersion, latestVersion) {
277
516
  const rl = createInterface({ input, output });
278
517
  try {
279
- console.log(`[optimus] update available ${currentVersion} -> ${latestVersion}.`);
280
- console.log("1. Upgrade now and exit");
281
- console.log("2. Continue with current version");
518
+ console.log("");
519
+ console.log(`Update available! ${currentVersion} -> ${latestVersion}`);
520
+ console.log("");
521
+ console.log(`Release notes: ${RELEASE_NOTES_URL}`);
522
+ console.log("");
523
+ console.log(`1. Update now (runs \`npm install -g ${PACKAGE_NAME}\`)`);
524
+ console.log("2. Skip");
525
+ console.log("3. Skip until next version");
526
+ console.log("");
282
527
  while (true) {
283
- const answer = (await rl.question("Select [1/2] (default 2): ")).trim().toLowerCase();
284
- if (!answer || answer === "2" || answer === "c" || answer === "continue") {
285
- return "continue";
528
+ const answer = (await rl.question("Press Enter to continue, or select [1/2/3] (default 2): ")).trim().toLowerCase();
529
+ if (!answer || answer === "2" || answer === "s" || answer === "skip") {
530
+ return "skip";
531
+ }
532
+ if (answer === "3" || answer === "n" || answer === "next") {
533
+ return "skip_until_next_version";
286
534
  }
287
535
  if (answer === "1" || answer === "u" || answer === "upgrade") {
288
536
  return "upgrade";
289
537
  }
290
- console.log("Invalid selection. Enter 1 to upgrade or 2 to continue.");
538
+ console.log("Invalid selection. Enter 1, 2, or 3.");
291
539
  }
292
540
  }
293
541
  finally {
@@ -324,6 +572,14 @@ async function maybeHandleStartupSelfUpdate(input) {
324
572
  if (!check.updateAvailable || !check.latestVersion) {
325
573
  return { handled: false };
326
574
  }
575
+ if (check.reason === "skipped_version") {
576
+ await input.logger.info("self_update.skipped_version", {
577
+ command: input.command,
578
+ currentVersion: check.currentVersion,
579
+ latestVersion: check.latestVersion
580
+ });
581
+ return { handled: false };
582
+ }
327
583
  const selfUpdateConfig = input.config.selfUpdate ?? buildDefaultConfig().selfUpdate;
328
584
  if (input.command !== "start") {
329
585
  console.log(`[optimus] update available ${check.currentVersion} -> ${check.latestVersion}. Run \`optimus upgrade\`.`);
@@ -341,7 +597,20 @@ async function maybeHandleStartupSelfUpdate(input) {
341
597
  latestVersion: check.latestVersion,
342
598
  choice
343
599
  });
344
- if (choice === "continue") {
600
+ if (choice === "skip") {
601
+ return { handled: false };
602
+ }
603
+ if (choice === "skip_until_next_version") {
604
+ await recordSkippedSelfUpdate({
605
+ currentVersion: check.currentVersion,
606
+ latestVersion: check.latestVersion,
607
+ packageRoot: CLI_ROOT_DIR
608
+ });
609
+ await input.logger.info("self_update.skip_until_next_version", {
610
+ command: input.command,
611
+ currentVersion: check.currentVersion,
612
+ latestVersion: check.latestVersion
613
+ });
345
614
  return { handled: false };
346
615
  }
347
616
  }
@@ -556,8 +825,14 @@ async function promptSetupAnswers(defaults) {
556
825
  const jiraBaseUrl = enableJira
557
826
  ? (await ask(`Jira base URL [${defaults.jiraBaseUrl ?? ""}]: `)).trim() || defaults.jiraBaseUrl
558
827
  : undefined;
559
- const jiraToken = enableJira
560
- ? (await ask(`Jira token [${defaults.jiraToken ? "configured" : ""}]: `)).trim() || defaults.jiraToken
828
+ const jiraMcpUrl = enableJira
829
+ ? (await ask(`Jira MCP URL [${defaults.jiraMcpUrl ?? ""}]: `)).trim() || defaults.jiraMcpUrl
830
+ : undefined;
831
+ const jiraUsername = enableJira
832
+ ? (await ask(`Jira username [${defaults.jiraUsername ?? ""}]: `)).trim() || defaults.jiraUsername
833
+ : undefined;
834
+ const jiraPersonalToken = enableJira
835
+ ? (await ask(`Jira personal token [${defaults.jiraPersonalToken ? "configured" : ""}]: `)).trim() || defaults.jiraPersonalToken
561
836
  : undefined;
562
837
  return {
563
838
  repoPath,
@@ -573,7 +848,9 @@ async function promptSetupAnswers(defaults) {
573
848
  ...(feishuSecret ? { feishuSecret } : {}),
574
849
  enableJira,
575
850
  ...(jiraBaseUrl ? { jiraBaseUrl } : {}),
576
- ...(jiraToken ? { jiraToken } : {})
851
+ ...(jiraMcpUrl ? { jiraMcpUrl } : {}),
852
+ ...(jiraUsername ? { jiraUsername } : {}),
853
+ ...(jiraPersonalToken ? { jiraPersonalToken } : {})
577
854
  };
578
855
  }
579
856
  finally {
@@ -618,7 +895,9 @@ async function resolveDefaultSetupAnswers() {
618
895
  ...(config.delivery.feishu.secret ? { feishuSecret: config.delivery.feishu.secret } : {}),
619
896
  enableJira: config.jira.enabled,
620
897
  ...(config.jira.baseUrl ? { jiraBaseUrl: config.jira.baseUrl } : {}),
621
- ...(config.jira.bearerToken ? { jiraToken: config.jira.bearerToken } : {})
898
+ ...(config.jira.mcpUrl ? { jiraMcpUrl: config.jira.mcpUrl } : {}),
899
+ ...(config.jira.httpHeaders["X-Atlassian-Username"] ? { jiraUsername: config.jira.httpHeaders["X-Atlassian-Username"] } : {}),
900
+ ...(config.jira.httpHeaders["X-Atlassian-Jira-Personal-Token"] ? { jiraPersonalToken: config.jira.httpHeaders["X-Atlassian-Jira-Personal-Token"] } : {})
622
901
  };
623
902
  }
624
903
  function normalizeSetupCodexAuthMode(value) {
@@ -685,8 +964,14 @@ function validateSetupAnswers(answers) {
685
964
  if (answers.enableJira && !answers.jiraBaseUrl?.trim()) {
686
965
  return "setup requires a Jira base URL when Jira integration is enabled.";
687
966
  }
688
- if (answers.enableJira && !answers.jiraToken?.trim()) {
689
- return "setup requires a Jira token when Jira integration is enabled.";
967
+ if (answers.enableJira && !answers.jiraMcpUrl?.trim()) {
968
+ return "setup requires a Jira MCP URL when Jira integration is enabled.";
969
+ }
970
+ if (answers.enableJira && !answers.jiraUsername?.trim()) {
971
+ return "setup requires a Jira username when Jira integration is enabled.";
972
+ }
973
+ if (answers.enableJira && !answers.jiraPersonalToken?.trim()) {
974
+ return "setup requires a Jira personal token when Jira integration is enabled.";
690
975
  }
691
976
  return undefined;
692
977
  }
@@ -752,11 +1037,19 @@ function buildSetupConfig(answers) {
752
1037
  config.jira.enabled = answers.enableJira;
753
1038
  if (answers.enableJira) {
754
1039
  config.jira.baseUrl = answers.jiraBaseUrl ?? config.jira.baseUrl;
755
- config.jira.authType = "bearer";
756
- if (answers.jiraToken) {
757
- config.jira.bearerToken = answers.jiraToken;
758
- }
759
- config.jira.httpHeaders = {};
1040
+ if (answers.jiraMcpUrl) {
1041
+ config.jira.mcpUrl = answers.jiraMcpUrl;
1042
+ }
1043
+ config.jira.authType = "http_headers";
1044
+ config.jira.httpHeaders = {
1045
+ "X-Atlassian-Jira-Url": answers.jiraBaseUrl ?? config.jira.baseUrl,
1046
+ "X-Atlassian-Username": answers.jiraUsername ?? "",
1047
+ "X-Atlassian-Jira-Personal-Token": answers.jiraPersonalToken ?? ""
1048
+ };
1049
+ delete config.jira.bearerToken;
1050
+ delete config.jira.username;
1051
+ delete config.jira.apiToken;
1052
+ delete config.jira.password;
760
1053
  }
761
1054
  return JSON.stringify(config, null, 2);
762
1055
  }
@@ -1234,6 +1527,9 @@ function parseArgs(argv) {
1234
1527
  }
1235
1528
  return parsed;
1236
1529
  }
1530
+ function parsedCommandHasHelp(argv) {
1531
+ return argv.some((token) => token === "-h" || token === "--help");
1532
+ }
1237
1533
  async function main() {
1238
1534
  const argv = process.argv.slice(2);
1239
1535
  const [firstArg, ...rest] = argv;
@@ -1248,6 +1544,12 @@ async function main() {
1248
1544
  : firstArg;
1249
1545
  const commandArgs = rest;
1250
1546
  if (command === "help") {
1547
+ const requestedCommand = commandArgs[0]?.trim();
1548
+ if (requestedCommand) {
1549
+ console.log(renderCommandHelp(requestedCommand) ?? renderCliHelp());
1550
+ process.exitCode = renderCommandHelp(requestedCommand) ? 0 : 1;
1551
+ return;
1552
+ }
1251
1553
  console.log(renderCliHelp());
1252
1554
  return;
1253
1555
  }
@@ -1255,6 +1557,17 @@ async function main() {
1255
1557
  console.log(await readPackageVersion());
1256
1558
  return;
1257
1559
  }
1560
+ if (parsedCommandHasHelp(commandArgs)) {
1561
+ if (command === "repo") {
1562
+ const repoSubcommand = commandArgs.find((token) => !token.startsWith("--"))?.trim();
1563
+ console.log(renderCommandHelp("repo") ?? renderCliHelp());
1564
+ process.exitCode = repoSubcommand && !["add", "remove", "update", "list", "doctor"].includes(repoSubcommand) ? 1 : 0;
1565
+ return;
1566
+ }
1567
+ console.log(renderCommandHelp(command) ?? renderCliHelp());
1568
+ process.exitCode = renderCommandHelp(command) ? 0 : 1;
1569
+ return;
1570
+ }
1258
1571
  if (command === "upgrade") {
1259
1572
  const args = parseArgs(commandArgs);
1260
1573
  const currentVersion = await readPackageVersion();