oh-my-opencode 5.0.0-beta.16 → 5.0.0-beta.18

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 (72) hide show
  1. package/dist/cli/index.js +379 -25
  2. package/dist/cli/runtime-commands.d.ts +1 -1
  3. package/dist/cli/worktree-sweep/classify.d.ts +16 -0
  4. package/dist/cli/worktree-sweep/format.d.ts +8 -0
  5. package/dist/cli/worktree-sweep/git.d.ts +19 -0
  6. package/dist/cli/worktree-sweep/index.d.ts +7 -0
  7. package/dist/cli/worktree-sweep/options.d.ts +2 -0
  8. package/dist/cli/worktree-sweep/parse-worktree-list.d.ts +8 -0
  9. package/dist/cli/worktree-sweep/sweep.d.ts +2 -0
  10. package/dist/cli/worktree-sweep/types.d.ts +65 -0
  11. package/dist/cli/worktree-sweep/worktree-sweep.d.ts +2 -0
  12. package/dist/cli-node/index.js +379 -25
  13. package/dist/index.js +26 -23
  14. package/dist/oh-my-opencode.schema.json +1 -1
  15. package/dist/skills/review-work/SKILL.md +3 -3
  16. package/dist/tui.js +12 -9
  17. package/package.json +14 -14
  18. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  19. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  20. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +11 -8
  22. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +11 -8
  23. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  27. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  29. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  31. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  32. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  34. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  36. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  40. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  41. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  42. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  44. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  68. package/packages/omo-codex/plugin/package-lock.json +13 -13
  69. package/packages/omo-codex/plugin/package.json +1 -1
  70. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +3 -3
  71. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  72. package/packages/shared-skills/skills/review-work/SKILL.md +3 -3
@@ -55,7 +55,7 @@ var package_default;
55
55
  var init_package = __esm(() => {
56
56
  package_default = {
57
57
  name: "oh-my-opencode",
58
- version: "5.0.0-beta.16",
58
+ version: "5.0.0-beta.18",
59
59
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
60
60
  main: "./dist/index.js",
61
61
  types: "dist/index.d.ts",
@@ -236,7 +236,7 @@ var init_package = __esm(() => {
236
236
  zod: "^4.4.3"
237
237
  },
238
238
  devDependencies: {
239
- "@code-yeongyu/senpi": "2026.8.22",
239
+ "@code-yeongyu/senpi": "2026.8.23",
240
240
  "@oh-my-opencode/agents-md-core": "workspace:*",
241
241
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
242
242
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -277,18 +277,18 @@ var init_package = __esm(() => {
277
277
  typescript: "^7.0.2"
278
278
  },
279
279
  optionalDependencies: {
280
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.16",
281
- "oh-my-opencode-darwin-x64": "5.0.0-beta.16",
282
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.16",
283
- "oh-my-opencode-linux-arm64": "5.0.0-beta.16",
284
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.16",
285
- "oh-my-opencode-linux-x64": "5.0.0-beta.16",
286
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.16",
287
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.16",
288
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.16",
289
- "oh-my-opencode-windows-arm64": "5.0.0-beta.16",
290
- "oh-my-opencode-windows-x64": "5.0.0-beta.16",
291
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.16"
280
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.18",
281
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.18",
282
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.18",
283
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.18",
284
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.18",
285
+ "oh-my-opencode-linux-x64": "5.0.0-beta.18",
286
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.18",
287
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.18",
288
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.18",
289
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.18",
290
+ "oh-my-opencode-windows-x64": "5.0.0-beta.18",
291
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.18"
292
292
  },
293
293
  overrides: {
294
294
  "@earendil-works/pi-agent-core": "0.84.2",
@@ -20397,13 +20397,14 @@ function resolveOmoTaskSettings(input, resolveParallelism = availableParallelism
20397
20397
  const record2 = record(string2(), unknown()).parse(input);
20398
20398
  return OmoTaskSettingsSchema.parse({
20399
20399
  ...record2,
20400
- residency_max_children: record2["residency_max_children"] ?? Math.max(8, resolveParallelism() * 3)
20400
+ residency_max_children: record2["residency_max_children"] ?? Math.max(8, resolveParallelism() * 3),
20401
+ global_concurrency: record2["global_concurrency"] ?? Math.max(8, resolveParallelism() * 2)
20401
20402
  });
20402
20403
  }
20403
20404
  var ResidencyMaxChildrenInputSchema, OmoTaskWaitSchema, OmoTaskTeamSettingsSchema, OmoTaskWarningsSchema, OmoTaskDagSettingsSchema, OmoTaskSettingsSchema, OmoTaskDagSettingsLayerSchema, OmoTaskWaitLayerSchema, OmoTaskTeamSettingsLayerSchema, OmoTaskWarningsLayerSchema, OmoTaskSettingsLayerSchema;
20404
20405
  var init_task = __esm(() => {
20405
20406
  init_zod();
20406
- ResidencyMaxChildrenInputSchema = union([number2().int().positive(), literal("unlimited")]);
20407
+ ResidencyMaxChildrenInputSchema = union([number2().int().nonnegative(), literal("unlimited")]);
20407
20408
  OmoTaskWaitSchema = object({
20408
20409
  min_ms: number2().int().positive().default(5000),
20409
20410
  default_ms: number2().int().positive().default(60000),
@@ -20429,9 +20430,10 @@ var init_task = __esm(() => {
20429
20430
  }).strict();
20430
20431
  OmoTaskSettingsSchema = object({
20431
20432
  default_execution_mode: _enum2(["in-process", "process"]).default("in-process"),
20432
- default_concurrency: number2().int().positive().default(5),
20433
- provider_concurrency: record(string2(), number2().int().positive()).optional(),
20434
- model_concurrency: record(string2(), number2().int().positive()).optional(),
20433
+ default_concurrency: number2().int().nonnegative().default(5),
20434
+ global_concurrency: number2().int().nonnegative().default(8),
20435
+ provider_concurrency: record(string2(), number2().int().nonnegative()).optional(),
20436
+ model_concurrency: record(string2(), number2().int().nonnegative()).optional(),
20435
20437
  max_depth: number2().int().nonnegative().default(1),
20436
20438
  residency_max_children: ResidencyMaxChildrenInputSchema.default(8),
20437
20439
  ttl_ms: number2().int().positive().default(86400000),
@@ -20472,9 +20474,10 @@ var init_task = __esm(() => {
20472
20474
  }).strict();
20473
20475
  OmoTaskSettingsLayerSchema = object({
20474
20476
  default_execution_mode: _enum2(["in-process", "process"]).optional(),
20475
- default_concurrency: number2().int().positive().optional(),
20476
- provider_concurrency: record(string2(), number2().int().positive()).optional(),
20477
- model_concurrency: record(string2(), number2().int().positive()).optional(),
20477
+ default_concurrency: number2().int().nonnegative().optional(),
20478
+ global_concurrency: number2().int().nonnegative().optional(),
20479
+ provider_concurrency: record(string2(), number2().int().nonnegative()).optional(),
20480
+ model_concurrency: record(string2(), number2().int().nonnegative()).optional(),
20478
20481
  max_depth: number2().int().nonnegative().optional(),
20479
20482
  residency_max_children: ResidencyMaxChildrenInputSchema.optional(),
20480
20483
  ttl_ms: number2().int().positive().optional(),
@@ -94383,7 +94386,7 @@ var package_default2;
94383
94386
  var init_package2 = __esm(() => {
94384
94387
  package_default2 = {
94385
94388
  name: "@oh-my-opencode/omo-codex",
94386
- version: "5.0.0-beta.16",
94389
+ version: "5.0.0-beta.18",
94387
94390
  type: "module",
94388
94391
  private: true,
94389
94392
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -103355,7 +103358,7 @@ function localLauncherCmdPath(homeDir = homedir10()) {
103355
103358
  }
103356
103359
  function renderLocalLauncher(options) {
103357
103360
  const brand = {
103358
- name: "omo",
103361
+ name: "OmO",
103359
103362
  displayVersion: options.version ?? "local",
103360
103363
  configDir: ".omo",
103361
103364
  flatLayout: false,
@@ -107039,7 +107042,7 @@ var CircuitBreakerConfigSchema = exports_external.object({
107039
107042
  consecutiveThreshold: exports_external.number().int().min(5).optional()
107040
107043
  });
107041
107044
  var BackgroundTaskConfigSchema = exports_external.object({
107042
- defaultConcurrency: exports_external.number().min(1).optional(),
107045
+ defaultConcurrency: exports_external.number().min(0).optional(),
107043
107046
  providerConcurrency: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional(),
107044
107047
  modelConcurrency: exports_external.record(exports_external.string(), exports_external.number().min(0)).optional(),
107045
107048
  maxDepth: exports_external.number().int().min(1).optional(),
@@ -113975,6 +113978,333 @@ async function refreshModelCapabilities(options, deps = {}) {
113975
113978
  }
113976
113979
  }
113977
113980
 
113981
+ // packages/omo-opencode/src/cli/worktree-sweep/classify.ts
113982
+ import path15 from "node:path";
113983
+ var DEFAULT_EXCLUDE_PREFIXES = [
113984
+ "~/.codex/worktrees",
113985
+ "~/.codex-gui-cli-remote/worktrees"
113986
+ ];
113987
+ function expandHome(value, home) {
113988
+ if (value === "~")
113989
+ return home;
113990
+ if (value.startsWith("~/"))
113991
+ return path15.join(home, value.slice(2));
113992
+ return value;
113993
+ }
113994
+ function isExcludedPath(worktreePath, prefixes, home) {
113995
+ const target = path15.resolve(expandHome(worktreePath, home));
113996
+ return prefixes.some((prefix) => {
113997
+ const resolved = path15.resolve(expandHome(prefix, home));
113998
+ return target === resolved || target.startsWith(`${resolved}${path15.sep}`);
113999
+ });
114000
+ }
114001
+ function worktreeRef(record3) {
114002
+ return record3.branch ?? record3.head ?? "";
114003
+ }
114004
+ function classifyWorktree(input) {
114005
+ const { record: record3 } = input;
114006
+ const ref = worktreeRef(record3);
114007
+ if (record3.locked)
114008
+ return { path: record3.path, ref, decision: "KEEP", reason: "locked" };
114009
+ if (!input.pathExists)
114010
+ return { path: record3.path, ref, decision: "PRUNE" };
114011
+ if (input.external)
114012
+ return { path: record3.path, ref, decision: "KEEP", reason: "external" };
114013
+ if (!input.merged && !input.agedOut) {
114014
+ return { path: record3.path, ref, decision: "KEEP", reason: "unmerged" };
114015
+ }
114016
+ if (input.dirty)
114017
+ return { path: record3.path, ref, decision: "KEEP", reason: "dirty" };
114018
+ return { path: record3.path, ref, decision: "SWEEP" };
114019
+ }
114020
+ // packages/omo-opencode/src/cli/worktree-sweep/format.ts
114021
+ function formatClassification(classification) {
114022
+ const head = classification.decision === "KEEP" && classification.reason !== undefined ? `KEEP(${classification.reason})` : classification.decision;
114023
+ return classification.ref.length > 0 ? `${head} ${classification.path} ${classification.ref}` : `${head} ${classification.path}`;
114024
+ }
114025
+ function formatSummary(result) {
114026
+ const mode = result.apply ? "apply" : "dry-run";
114027
+ const removed2 = result.repos.reduce((total, repo) => total + repo.removed.length, 0);
114028
+ const failed = result.repos.reduce((total, repo) => total + repo.failed.length, 0);
114029
+ return `SUMMARY mode=${mode} repos=${result.repos.length} sweep=${result.sweepCount} keep=${result.keepCount} prune=${result.pruneCount} removed=${removed2} failed=${failed}`;
114030
+ }
114031
+ function formatResult(result) {
114032
+ const lines = [];
114033
+ for (const repo of result.repos) {
114034
+ lines.push(`REPO ${repo.repo} default=${repo.defaultBranch}`);
114035
+ for (const classification of repo.classifications) {
114036
+ lines.push(formatClassification(classification));
114037
+ }
114038
+ for (const failure of repo.failed) {
114039
+ lines.push(`FAILED ${failure.path} ${failure.error}`);
114040
+ }
114041
+ }
114042
+ lines.push(formatSummary(result));
114043
+ return lines;
114044
+ }
114045
+ // packages/omo-opencode/src/cli/worktree-sweep/options.ts
114046
+ function parseOlderThanDays(value) {
114047
+ const parsed = Number(value);
114048
+ if (!Number.isFinite(parsed) || parsed < 0 || !Number.isInteger(parsed)) {
114049
+ throw new Error(`--older-than must be a non-negative integer number of days, got: ${value}`);
114050
+ }
114051
+ return parsed;
114052
+ }
114053
+ // packages/omo-opencode/src/cli/worktree-sweep/parse-worktree-list.ts
114054
+ import { normalize } from "node:path";
114055
+ function emptyRecord() {
114056
+ return { detached: false, locked: false, prunable: false };
114057
+ }
114058
+ function finalize2(record3) {
114059
+ if (record3.path === undefined || record3.path.length === 0)
114060
+ return;
114061
+ return {
114062
+ path: normalize(record3.path),
114063
+ ...record3.head === undefined ? {} : { head: record3.head },
114064
+ ...record3.branch === undefined ? {} : { branch: record3.branch },
114065
+ detached: record3.detached,
114066
+ locked: record3.locked,
114067
+ ...record3.lockReason === undefined ? {} : { lockReason: record3.lockReason },
114068
+ prunable: record3.prunable
114069
+ };
114070
+ }
114071
+ function applyLine(record3, line) {
114072
+ if (line.startsWith("worktree ")) {
114073
+ record3.path = line.slice("worktree ".length);
114074
+ return;
114075
+ }
114076
+ if (line.startsWith("HEAD ")) {
114077
+ record3.head = line.slice("HEAD ".length);
114078
+ return;
114079
+ }
114080
+ if (line.startsWith("branch ")) {
114081
+ record3.branch = line.slice("branch ".length).replace(/^refs\/heads\//, "");
114082
+ return;
114083
+ }
114084
+ if (line === "detached") {
114085
+ record3.detached = true;
114086
+ return;
114087
+ }
114088
+ if (line === "locked" || line.startsWith("locked ")) {
114089
+ record3.locked = true;
114090
+ const reason = line.slice("locked".length).trim();
114091
+ if (reason.length > 0)
114092
+ record3.lockReason = reason;
114093
+ return;
114094
+ }
114095
+ if (line === "prunable" || line.startsWith("prunable ")) {
114096
+ record3.prunable = true;
114097
+ }
114098
+ }
114099
+ function parseWorktreeList(porcelain) {
114100
+ const records = [];
114101
+ let current = emptyRecord();
114102
+ for (const rawLine of porcelain.split(`
114103
+ `)) {
114104
+ const line = rawLine.replace(/\r$/, "");
114105
+ if (line.length === 0) {
114106
+ const finalized = finalize2(current);
114107
+ if (finalized)
114108
+ records.push(finalized);
114109
+ current = emptyRecord();
114110
+ continue;
114111
+ }
114112
+ applyLine(current, line);
114113
+ }
114114
+ const trailing = finalize2(current);
114115
+ if (trailing)
114116
+ records.push(trailing);
114117
+ return records;
114118
+ }
114119
+ function linkedWorktrees(records) {
114120
+ return records.slice(1);
114121
+ }
114122
+ // packages/omo-opencode/src/cli/worktree-sweep/sweep.ts
114123
+ import { existsSync as existsSync66 } from "node:fs";
114124
+ import { stat as stat8 } from "node:fs/promises";
114125
+ import os7 from "node:os";
114126
+ import { normalize as normalize2 } from "node:path";
114127
+
114128
+ // packages/omo-opencode/src/cli/worktree-sweep/git.ts
114129
+ init_runtime();
114130
+ async function runGit(cwd, args) {
114131
+ const child = spawn({ cmd: ["git", "-C", cwd, ...args], stdout: "pipe", stderr: "pipe" });
114132
+ const [code, stdout2, stderr] = await Promise.all([
114133
+ child.exited,
114134
+ new Response(child.stdout).text(),
114135
+ new Response(child.stderr).text()
114136
+ ]);
114137
+ return { code, stdout: stdout2, stderr };
114138
+ }
114139
+ async function listWorktreesPorcelain(repo) {
114140
+ const result = await runGit(repo, ["worktree", "list", "--porcelain"]);
114141
+ if (result.code !== 0) {
114142
+ throw new Error(result.stderr.trim() || `git worktree list failed in ${repo}`);
114143
+ }
114144
+ return result.stdout;
114145
+ }
114146
+ async function detectDefaultBranch(repo) {
114147
+ const symbolic = await runGit(repo, [
114148
+ "symbolic-ref",
114149
+ "--quiet",
114150
+ "--short",
114151
+ "refs/remotes/origin/HEAD"
114152
+ ]);
114153
+ if (symbolic.code === 0) {
114154
+ const value = symbolic.stdout.trim();
114155
+ if (value.length > 0)
114156
+ return value.replace(/^[^/]*\//, "");
114157
+ }
114158
+ const local = await runGit(repo, [
114159
+ "branch",
114160
+ "--list",
114161
+ "main",
114162
+ "master",
114163
+ "--format=%(refname:short)"
114164
+ ]);
114165
+ if (local.code === 0) {
114166
+ for (const candidate2 of ["main", "master"]) {
114167
+ if (local.stdout.split(`
114168
+ `).map((line) => line.trim()).includes(candidate2)) {
114169
+ return candidate2;
114170
+ }
114171
+ }
114172
+ }
114173
+ return;
114174
+ }
114175
+ async function isMerged(repo, ref, defaultBranch) {
114176
+ if (ref.length === 0)
114177
+ return false;
114178
+ const result = await runGit(repo, ["merge-base", "--is-ancestor", ref, defaultBranch]);
114179
+ return result.code === 0;
114180
+ }
114181
+ async function isDirty(worktreePath) {
114182
+ const result = await runGit(worktreePath, ["status", "--porcelain"]);
114183
+ if (result.code !== 0)
114184
+ return true;
114185
+ return result.stdout.trim().length > 0;
114186
+ }
114187
+ async function removeWorktree(repo, worktreePath) {
114188
+ return runGit(repo, ["worktree", "remove", worktreePath]);
114189
+ }
114190
+ async function pruneWorktrees(repo) {
114191
+ return runGit(repo, ["worktree", "prune"]);
114192
+ }
114193
+ async function resolveRepoRoot(cwd) {
114194
+ const result = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
114195
+ if (result.code !== 0)
114196
+ return;
114197
+ const value = result.stdout.trim();
114198
+ return value.length > 0 ? value : undefined;
114199
+ }
114200
+
114201
+ // packages/omo-opencode/src/cli/worktree-sweep/sweep.ts
114202
+ var DAY_MS = 24 * 60 * 60 * 1000;
114203
+ async function isOlderThan(worktreePath, days, now) {
114204
+ if (days <= 0)
114205
+ return false;
114206
+ try {
114207
+ const stats = await stat8(worktreePath);
114208
+ return now - stats.mtimeMs > days * DAY_MS;
114209
+ } catch {
114210
+ return false;
114211
+ }
114212
+ }
114213
+ async function classifyRecord(repo, record3, defaultBranch, options) {
114214
+ const pathExists3 = existsSync66(record3.path);
114215
+ const external2 = isExcludedPath(record3.path, options.excludePrefixes, options.home);
114216
+ if (record3.locked || !pathExists3 || external2) {
114217
+ return classifyWorktree({
114218
+ record: record3,
114219
+ pathExists: pathExists3,
114220
+ merged: false,
114221
+ agedOut: false,
114222
+ dirty: false,
114223
+ external: external2
114224
+ });
114225
+ }
114226
+ const merged = await isMerged(repo, worktreeRef(record3), defaultBranch);
114227
+ const agedOut = merged ? false : await isOlderThan(record3.path, options.olderThanDays, options.now);
114228
+ const dirty = merged || agedOut ? await isDirty(record3.path) : false;
114229
+ return classifyWorktree({ record: record3, pathExists: pathExists3, merged, agedOut, dirty, external: external2 });
114230
+ }
114231
+ async function sweepRepo(repo, options) {
114232
+ const root = normalize2(await resolveRepoRoot(repo) ?? repo);
114233
+ const defaultBranch = await detectDefaultBranch(root);
114234
+ if (defaultBranch === undefined) {
114235
+ throw new Error(`Could not determine the default branch for ${root}`);
114236
+ }
114237
+ const records = linkedWorktrees(parseWorktreeList(await listWorktreesPorcelain(root)));
114238
+ const classifications = [];
114239
+ for (const record3 of records) {
114240
+ classifications.push(await classifyRecord(root, record3, defaultBranch, options));
114241
+ }
114242
+ const removed2 = [];
114243
+ const failed = [];
114244
+ if (options.apply) {
114245
+ for (const classification of classifications) {
114246
+ if (classification.decision !== "SWEEP")
114247
+ continue;
114248
+ const result = await removeWorktree(root, classification.path);
114249
+ if (result.code === 0)
114250
+ removed2.push(classification.path);
114251
+ else {
114252
+ failed.push({
114253
+ path: classification.path,
114254
+ error: result.stderr.trim() || `git worktree remove exited with ${result.code}`
114255
+ });
114256
+ }
114257
+ }
114258
+ await pruneWorktrees(root);
114259
+ }
114260
+ return { repo: root, defaultBranch, classifications, removed: removed2, failed };
114261
+ }
114262
+ async function sweepWorktrees(options = {}) {
114263
+ const apply = options.apply === true;
114264
+ const olderThanDays = options.olderThanDays ?? 0;
114265
+ const excludePrefixes = options.excludePrefixes ?? DEFAULT_EXCLUDE_PREFIXES;
114266
+ const home = os7.homedir();
114267
+ const now = Date.now();
114268
+ const repos = options.repos && options.repos.length > 0 ? options.repos : [options.cwd ?? process.cwd()];
114269
+ const reports = [];
114270
+ for (const repo of repos) {
114271
+ reports.push(await sweepRepo(repo, { apply, olderThanDays, excludePrefixes, home, now }));
114272
+ }
114273
+ const all = reports.flatMap((report) => report.classifications);
114274
+ return {
114275
+ apply,
114276
+ olderThanDays,
114277
+ repos: reports,
114278
+ sweepCount: all.filter((item) => item.decision === "SWEEP").length,
114279
+ keepCount: all.filter((item) => item.decision === "KEEP").length,
114280
+ pruneCount: all.filter((item) => item.decision === "PRUNE").length
114281
+ };
114282
+ }
114283
+ // packages/omo-opencode/src/cli/worktree-sweep/worktree-sweep.ts
114284
+ async function worktreeSweep(options = {}) {
114285
+ let result;
114286
+ try {
114287
+ result = await sweepWorktrees(options);
114288
+ } catch (error51) {
114289
+ process.stderr.write(`[omo] worktree-sweep failed: ${error51 instanceof Error ? error51.message : String(error51)}
114290
+ `);
114291
+ return 1;
114292
+ }
114293
+ if (options.json === true) {
114294
+ process.stdout.write(JSON.stringify(result, null, 2));
114295
+ process.stdout.write(`
114296
+ `);
114297
+ } else {
114298
+ process.stdout.write(formatResult(result).join(`
114299
+ `));
114300
+ process.stdout.write(`
114301
+ `);
114302
+ }
114303
+ const failedTotal = result.repos.reduce((total, repo) => total + repo.failed.length, 0);
114304
+ if (failedTotal > 0)
114305
+ return 1;
114306
+ return 0;
114307
+ }
113978
114308
  // packages/omo-opencode/src/cli/runtime-commands.ts
113979
114309
  init_shared();
113980
114310
  init_package();
@@ -113999,6 +114329,30 @@ function configureRuntimeCommands(program2) {
113999
114329
  });
114000
114330
  process.exit(exitCode);
114001
114331
  });
114332
+ program2.command("worktree-sweep").description("Report (and optionally remove) stale linked git worktrees").option("--apply", "Actually remove SWEEP worktrees and prune stale metadata (default is dry-run)").option("--older-than <days>", "Sweep unmerged worktrees older than N days (0 = merged-only)", (value) => {
114333
+ try {
114334
+ return parseOlderThanDays(value);
114335
+ } catch (error51) {
114336
+ throw new InvalidArgumentError(error51 instanceof Error ? error51.message : String(error51));
114337
+ }
114338
+ }).option("--repo <path>", "Repository to sweep (repeatable)", (value, previous = []) => [...previous, value]).option("--json", "Output structured JSON result").addHelpText("after", `
114339
+ Examples:
114340
+ $ omo-agent-toolkit worktree-sweep
114341
+ $ omo-agent-toolkit worktree-sweep --older-than=14
114342
+ $ omo-agent-toolkit worktree-sweep --repo /path/to/repo --repo /path/to/other --apply
114343
+
114344
+ Lines: SWEEP <path> <ref> | KEEP(<reason>) <path> <ref> | PRUNE <path> <ref>
114345
+ KEEP reasons: locked, external, unmerged, dirty. Dry-run is the default; --apply
114346
+ removes with 'git worktree remove' (never --force) and then prunes.
114347
+ `).action(async (options) => {
114348
+ const exitCode = await worktreeSweep({
114349
+ apply: options.apply === true,
114350
+ olderThanDays: options.olderThan ?? 0,
114351
+ repos: options.repo,
114352
+ json: options.json ?? false
114353
+ });
114354
+ process.exit(exitCode);
114355
+ });
114002
114356
  program2.command("ulw-loop [args...]").allowUnknownOption().passThroughOptions().description("Run the Codex LazyCodex ulw-loop CLI").action(async (args = []) => {
114003
114357
  const exitCode = await codexUlwLoop(args);
114004
114358
  process.exit(exitCode);
package/dist/index.js CHANGED
@@ -5619,12 +5619,13 @@ function resolveOmoTaskSettings(input, resolveParallelism = availableParallelism
5619
5619
  const record8 = z10.record(z10.string(), z10.unknown()).parse(input);
5620
5620
  return OmoTaskSettingsSchema.parse({
5621
5621
  ...record8,
5622
- residency_max_children: record8["residency_max_children"] ?? Math.max(8, resolveParallelism() * 3)
5622
+ residency_max_children: record8["residency_max_children"] ?? Math.max(8, resolveParallelism() * 3),
5623
+ global_concurrency: record8["global_concurrency"] ?? Math.max(8, resolveParallelism() * 2)
5623
5624
  });
5624
5625
  }
5625
5626
  var ResidencyMaxChildrenInputSchema, OmoTaskWaitSchema, OmoTaskTeamSettingsSchema, OmoTaskWarningsSchema, OmoTaskDagSettingsSchema, OmoTaskSettingsSchema, OmoTaskDagSettingsLayerSchema, OmoTaskWaitLayerSchema, OmoTaskTeamSettingsLayerSchema, OmoTaskWarningsLayerSchema, OmoTaskSettingsLayerSchema;
5626
5627
  var init_task = __esm(() => {
5627
- ResidencyMaxChildrenInputSchema = z10.union([z10.number().int().positive(), z10.literal("unlimited")]);
5628
+ ResidencyMaxChildrenInputSchema = z10.union([z10.number().int().nonnegative(), z10.literal("unlimited")]);
5628
5629
  OmoTaskWaitSchema = z10.object({
5629
5630
  min_ms: z10.number().int().positive().default(5000),
5630
5631
  default_ms: z10.number().int().positive().default(60000),
@@ -5650,9 +5651,10 @@ var init_task = __esm(() => {
5650
5651
  }).strict();
5651
5652
  OmoTaskSettingsSchema = z10.object({
5652
5653
  default_execution_mode: z10.enum(["in-process", "process"]).default("in-process"),
5653
- default_concurrency: z10.number().int().positive().default(5),
5654
- provider_concurrency: z10.record(z10.string(), z10.number().int().positive()).optional(),
5655
- model_concurrency: z10.record(z10.string(), z10.number().int().positive()).optional(),
5654
+ default_concurrency: z10.number().int().nonnegative().default(5),
5655
+ global_concurrency: z10.number().int().nonnegative().default(8),
5656
+ provider_concurrency: z10.record(z10.string(), z10.number().int().nonnegative()).optional(),
5657
+ model_concurrency: z10.record(z10.string(), z10.number().int().nonnegative()).optional(),
5656
5658
  max_depth: z10.number().int().nonnegative().default(1),
5657
5659
  residency_max_children: ResidencyMaxChildrenInputSchema.default(8),
5658
5660
  ttl_ms: z10.number().int().positive().default(86400000),
@@ -5693,9 +5695,10 @@ var init_task = __esm(() => {
5693
5695
  }).strict();
5694
5696
  OmoTaskSettingsLayerSchema = z10.object({
5695
5697
  default_execution_mode: z10.enum(["in-process", "process"]).optional(),
5696
- default_concurrency: z10.number().int().positive().optional(),
5697
- provider_concurrency: z10.record(z10.string(), z10.number().int().positive()).optional(),
5698
- model_concurrency: z10.record(z10.string(), z10.number().int().positive()).optional(),
5698
+ default_concurrency: z10.number().int().nonnegative().optional(),
5699
+ global_concurrency: z10.number().int().nonnegative().optional(),
5700
+ provider_concurrency: z10.record(z10.string(), z10.number().int().nonnegative()).optional(),
5701
+ model_concurrency: z10.record(z10.string(), z10.number().int().nonnegative()).optional(),
5699
5702
  max_depth: z10.number().int().nonnegative().optional(),
5700
5703
  residency_max_children: ResidencyMaxChildrenInputSchema.optional(),
5701
5704
  ttl_ms: z10.number().int().positive().optional(),
@@ -27343,7 +27346,7 @@ var CircuitBreakerConfigSchema = z19.object({
27343
27346
  consecutiveThreshold: z19.number().int().min(5).optional()
27344
27347
  });
27345
27348
  var BackgroundTaskConfigSchema = z19.object({
27346
- defaultConcurrency: z19.number().min(1).optional(),
27349
+ defaultConcurrency: z19.number().min(0).optional(),
27347
27350
  providerConcurrency: z19.record(z19.string(), z19.number().min(0)).optional(),
27348
27351
  modelConcurrency: z19.record(z19.string(), z19.number().min(0)).optional(),
27349
27352
  maxDepth: z19.number().int().min(1).optional(),
@@ -98669,7 +98672,7 @@ function getCachedVersion(options = {}) {
98669
98672
  // package.json
98670
98673
  var package_default = {
98671
98674
  name: "oh-my-opencode",
98672
- version: "5.0.0-beta.16",
98675
+ version: "5.0.0-beta.18",
98673
98676
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
98674
98677
  main: "./dist/index.js",
98675
98678
  types: "dist/index.d.ts",
@@ -98850,7 +98853,7 @@ var package_default = {
98850
98853
  zod: "^4.4.3"
98851
98854
  },
98852
98855
  devDependencies: {
98853
- "@code-yeongyu/senpi": "2026.8.22",
98856
+ "@code-yeongyu/senpi": "2026.8.23",
98854
98857
  "@oh-my-opencode/agents-md-core": "workspace:*",
98855
98858
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
98856
98859
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -98891,18 +98894,18 @@ var package_default = {
98891
98894
  typescript: "^7.0.2"
98892
98895
  },
98893
98896
  optionalDependencies: {
98894
- "oh-my-opencode-darwin-arm64": "5.0.0-beta.16",
98895
- "oh-my-opencode-darwin-x64": "5.0.0-beta.16",
98896
- "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.16",
98897
- "oh-my-opencode-linux-arm64": "5.0.0-beta.16",
98898
- "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.16",
98899
- "oh-my-opencode-linux-x64": "5.0.0-beta.16",
98900
- "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.16",
98901
- "oh-my-opencode-linux-x64-musl": "5.0.0-beta.16",
98902
- "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.16",
98903
- "oh-my-opencode-windows-arm64": "5.0.0-beta.16",
98904
- "oh-my-opencode-windows-x64": "5.0.0-beta.16",
98905
- "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.16"
98897
+ "oh-my-opencode-darwin-arm64": "5.0.0-beta.18",
98898
+ "oh-my-opencode-darwin-x64": "5.0.0-beta.18",
98899
+ "oh-my-opencode-darwin-x64-baseline": "5.0.0-beta.18",
98900
+ "oh-my-opencode-linux-arm64": "5.0.0-beta.18",
98901
+ "oh-my-opencode-linux-arm64-musl": "5.0.0-beta.18",
98902
+ "oh-my-opencode-linux-x64": "5.0.0-beta.18",
98903
+ "oh-my-opencode-linux-x64-baseline": "5.0.0-beta.18",
98904
+ "oh-my-opencode-linux-x64-musl": "5.0.0-beta.18",
98905
+ "oh-my-opencode-linux-x64-musl-baseline": "5.0.0-beta.18",
98906
+ "oh-my-opencode-windows-arm64": "5.0.0-beta.18",
98907
+ "oh-my-opencode-windows-x64": "5.0.0-beta.18",
98908
+ "oh-my-opencode-windows-x64-baseline": "5.0.0-beta.18"
98906
98909
  },
98907
98910
  overrides: {
98908
98911
  "@earendil-works/pi-agent-core": "0.84.2",
@@ -9206,7 +9206,7 @@
9206
9206
  "properties": {
9207
9207
  "defaultConcurrency": {
9208
9208
  "type": "number",
9209
- "minimum": 1
9209
+ "minimum": 0
9210
9210
  },
9211
9211
  "providerConcurrency": {
9212
9212
  "type": "object",
@@ -90,7 +90,7 @@ Before launching agents, collect these inputs. Extract from conversation history
90
90
  </required_inputs>
91
91
 
92
92
 
93
- Review PRs and branches from a dedicated review worktree only: create or attach one with `git worktree add <path> <branch>` before collecting changed files, diff, file contents, or running checks. The main worktree is read-only context; never checkout, test, or edit the review branch there.
93
+ Review PRs and branches from a dedicated review worktree only: create or attach one with `git worktree add <path> <branch>` before collecting changed files, diff, file contents, or running checks, then immediately lock it with `git worktree lock <path> --reason "review:<pr-or-branch>"`. The main worktree is read-only context; never checkout, test, or edit the review branch there.
94
94
 
95
95
  **Auto-collection sequence:**
96
96
 
@@ -530,8 +530,6 @@ OUTPUT FORMAT:
530
530
  """)
531
531
  ```
532
532
 
533
- ---
534
-
535
533
  ## Phase 2: Wait & Collect
536
534
 
537
535
  After launching all 5 agents in one turn, wait for completions in bounded
@@ -561,6 +559,8 @@ review result with the incomplete lane named. Do not spin in repeated
561
559
  wait/followup cycles. Do not use `multi_agent_v1.send_input` as an interrupt; queued
562
560
  followups are not cancellation.
563
561
 
562
+ After ALL 5 lanes reach a terminal state and before delivering the verdict, tear down the review worktree: run `git worktree unlock <path>` followed by `git worktree remove <path>`. The lanes above run inside that worktree, so removing it earlier destroys their working directory; a crashed review leaves the locked tree as a recoverable marker for manual cleanup.
563
+
564
564
  ---
565
565
 
566
566
  ## Phase 3: Deliver Verdict