oh-my-opencode 4.16.0 → 4.16.2
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.
- package/.agents/skills/codex-qa/SKILL.md +9 -7
- package/.agents/skills/codex-qa/references/logging-debug.md +6 -4
- package/.agents/skills/opencode-qa/SKILL.md +9 -9
- package/.agents/skills/opencode-qa/references/tui-tmux.md +10 -8
- package/dist/cli/codex-ulw-loop.d.ts +8 -0
- package/dist/cli/get-local-version/types.d.ts +1 -1
- package/dist/cli/index.js +260 -207
- package/dist/cli-node/index.js +260 -207
- package/dist/features/background-agent/parent-wake-dedupe.d.ts +1 -0
- package/dist/features/background-agent/parent-wake-flush-runner.d.ts +2 -0
- package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -0
- package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
- package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
- package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
- package/dist/features/monitor/process.d.ts +17 -1
- package/dist/index.js +1098 -1020
- package/dist/skills/frontend/SKILL.md +9 -9
- package/dist/skills/frontend/references/design/README.md +7 -3
- package/dist/skills/frontend/references/design/_INDEX.md +1 -1
- package/dist/skills/frontend/references/design/design-system-architecture.md +24 -2
- package/dist/skills/frontend/references/designpowers/README.md +2 -2
- package/dist/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
- package/dist/skills/review-work/SKILL.md +3 -1
- package/dist/skills/start-work/SKILL.md +4 -2
- package/dist/skills/ulw-research/SKILL.md +3 -1
- package/dist/skills/visual-qa/SKILL.md +13 -17
- package/docs/reference/web-terminal-visual-qa.md +39 -45
- package/package.json +31 -25
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -1
- package/packages/omo-codex/plugin/components/teammode/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +3 -3
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +19 -10
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +19 -10
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +13 -0
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +2 -0
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -1
- package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +19 -10
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +15 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +89 -27
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +55 -9
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.d.ts +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.js +33 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +10 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +15 -11
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +3 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +17 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +59 -11
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-snapshot.ts +38 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +11 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +15 -7
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +16 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +260 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering-snapshot.test.ts +124 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +101 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/auto-update.mjs +64 -17
- package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +10 -6
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +186 -20
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +51 -3
- package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +33 -5
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +9 -9
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +7 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +24 -2
- package/packages/omo-codex/plugin/skills/frontend/references/designpowers/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +19 -10
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +3 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +7 -7
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +13 -0
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +2 -0
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +14 -18
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -4
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +35 -1
- package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +22 -9
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +275 -19
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +33 -0
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +4 -2
- package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +52 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +65 -39
- package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
- package/packages/shared-skills/skills/frontend/SKILL.md +9 -9
- package/packages/shared-skills/skills/frontend/references/design/README.md +7 -3
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -1
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +24 -2
- package/packages/shared-skills/skills/frontend/references/designpowers/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
- package/packages/shared-skills/skills/review-work/SKILL.md +3 -1
- package/packages/shared-skills/skills/start-work/SKILL.md +4 -2
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -1
- package/packages/shared-skills/skills/visual-qa/SKILL.md +13 -17
- package/script/qa/strip-ansi.mjs +10 -0
- package/script/qa/web-terminal-visual-qa.mjs +112 -195
- package/script/qa/xterm-live-terminal.mjs +180 -0
- package/script/qa/web-terminal-renderer.mjs +0 -218
package/dist/index.js
CHANGED
|
@@ -7945,7 +7945,10 @@ function finishPromptReservation(sessionID, reservation, dispatchAttempted, post
|
|
|
7945
7945
|
function deletePromptReservation(sessionID) {
|
|
7946
7946
|
promptAsyncReservations.delete(sessionID);
|
|
7947
7947
|
}
|
|
7948
|
-
function
|
|
7948
|
+
function isTransientRetryReservationOwner(reservationSource) {
|
|
7949
|
+
return reservationSource === TRANSIENT_RETRY_RESERVATION_OWNER || reservationSource.startsWith(`${TRANSIENT_RETRY_RESERVATION_OWNER}:`);
|
|
7950
|
+
}
|
|
7951
|
+
function reservationSourceMatches(reservationSource, expectedSource, expectedPrefix, supersedeTransientRetryOwners) {
|
|
7949
7952
|
if (typeof expectedSource === "string") {
|
|
7950
7953
|
if (reservationSource === expectedSource) {
|
|
7951
7954
|
return true;
|
|
@@ -7953,13 +7956,16 @@ function reservationSourceMatches(reservationSource, expectedSource, expectedPre
|
|
|
7953
7956
|
} else if (expectedSource.includes(reservationSource)) {
|
|
7954
7957
|
return true;
|
|
7955
7958
|
}
|
|
7959
|
+
if (supersedeTransientRetryOwners && isTransientRetryReservationOwner(reservationSource)) {
|
|
7960
|
+
return true;
|
|
7961
|
+
}
|
|
7956
7962
|
if (expectedPrefix === undefined) {
|
|
7957
7963
|
return false;
|
|
7958
7964
|
}
|
|
7959
7965
|
const prefixes = typeof expectedPrefix === "string" ? [expectedPrefix] : expectedPrefix;
|
|
7960
7966
|
return prefixes.filter((prefix) => prefix.length > 0 && prefix.endsWith(":")).some((prefix) => reservationSource.startsWith(prefix));
|
|
7961
7967
|
}
|
|
7962
|
-
var promptAsyncReservations, expiredReservationHandler;
|
|
7968
|
+
var promptAsyncReservations, expiredReservationHandler, TRANSIENT_RETRY_RESERVATION_OWNER = "model-suggestion-retry";
|
|
7963
7969
|
var init_reservations = __esm(() => {
|
|
7964
7970
|
promptAsyncReservations = new Map;
|
|
7965
7971
|
});
|
|
@@ -8558,7 +8564,7 @@ function releasePromptAsyncReservation(sessionID, source, options) {
|
|
|
8558
8564
|
return false;
|
|
8559
8565
|
}
|
|
8560
8566
|
const expectedSource = options?.reservedBy ?? source;
|
|
8561
|
-
if (!reservationSourceMatches(existing.source, expectedSource, options?.reservedByPrefix)) {
|
|
8567
|
+
if (!reservationSourceMatches(existing.source, expectedSource, options?.reservedByPrefix, options?.supersedeTransientRetryOwners)) {
|
|
8562
8568
|
log("[prompt-async-gate] promptAsync reservation release skipped for different source", {
|
|
8563
8569
|
sessionID,
|
|
8564
8570
|
source,
|
|
@@ -13173,10 +13179,10 @@ var init_tolerant_fsync = __esm(() => {
|
|
|
13173
13179
|
// packages/team-core/src/team-state-store/locks.ts
|
|
13174
13180
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
13175
13181
|
import { access as access3, open, readFile as readFile4, rename as rename3, rm as rm3, unlink as unlink2 } from "fs/promises";
|
|
13176
|
-
import { dirname as
|
|
13182
|
+
import { dirname as dirname24 } from "path";
|
|
13177
13183
|
function delay4(ms) {
|
|
13178
|
-
return new Promise((
|
|
13179
|
-
setTimeout(
|
|
13184
|
+
return new Promise((resolve20) => {
|
|
13185
|
+
setTimeout(resolve20, ms);
|
|
13180
13186
|
});
|
|
13181
13187
|
}
|
|
13182
13188
|
function buildOwnerContent(ownerTag) {
|
|
@@ -13228,7 +13234,7 @@ async function assertRetryableLockOpenError(lockPath, error, deps) {
|
|
|
13228
13234
|
if (code === "EPERM") {
|
|
13229
13235
|
if (await pathMayExist(lockPath, deps))
|
|
13230
13236
|
return;
|
|
13231
|
-
if ((deps?.platform ?? process.platform) === "win32" && await pathMayExist(
|
|
13237
|
+
if ((deps?.platform ?? process.platform) === "win32" && await pathMayExist(dirname24(lockPath), deps))
|
|
13232
13238
|
return;
|
|
13233
13239
|
}
|
|
13234
13240
|
throw error;
|
|
@@ -13560,18 +13566,18 @@ __export(exports_storage, {
|
|
|
13560
13566
|
getStateFilePath: () => getStateFilePath,
|
|
13561
13567
|
clearState: () => clearState
|
|
13562
13568
|
});
|
|
13563
|
-
import { existsSync as
|
|
13564
|
-
import { dirname as
|
|
13569
|
+
import { existsSync as existsSync74, readFileSync as readFileSync48, writeFileSync as writeFileSync18, unlinkSync as unlinkSync13, mkdirSync as mkdirSync18 } from "fs";
|
|
13570
|
+
import { dirname as dirname25, join as join86 } from "path";
|
|
13565
13571
|
function getStateFilePath(directory, customPath) {
|
|
13566
|
-
return customPath ?
|
|
13572
|
+
return customPath ? join86(directory, customPath) : join86(directory, DEFAULT_STATE_FILE);
|
|
13567
13573
|
}
|
|
13568
13574
|
function readState(directory, customPath) {
|
|
13569
13575
|
const filePath = getStateFilePath(directory, customPath);
|
|
13570
|
-
if (!
|
|
13576
|
+
if (!existsSync74(filePath)) {
|
|
13571
13577
|
return null;
|
|
13572
13578
|
}
|
|
13573
13579
|
try {
|
|
13574
|
-
const content =
|
|
13580
|
+
const content = readFileSync48(filePath, "utf-8");
|
|
13575
13581
|
const { data, body } = parseFrontmatter(content);
|
|
13576
13582
|
const active = data.active;
|
|
13577
13583
|
const iteration = data.iteration;
|
|
@@ -13617,9 +13623,9 @@ function readState(directory, customPath) {
|
|
|
13617
13623
|
function writeState(directory, state3, customPath) {
|
|
13618
13624
|
const filePath = getStateFilePath(directory, customPath);
|
|
13619
13625
|
try {
|
|
13620
|
-
const dir =
|
|
13621
|
-
if (!
|
|
13622
|
-
|
|
13626
|
+
const dir = dirname25(filePath);
|
|
13627
|
+
if (!existsSync74(dir)) {
|
|
13628
|
+
mkdirSync18(dir, { recursive: true });
|
|
13623
13629
|
}
|
|
13624
13630
|
const sessionIdLine = state3.session_id ? `session_id: "${state3.session_id}"
|
|
13625
13631
|
` : "";
|
|
@@ -13651,7 +13657,7 @@ ${initialCompletionPromiseLine}${verificationAttemptLine}${verificationAttemptSt
|
|
|
13651
13657
|
${sessionIdLine}${ultraworkLine}${verificationPendingLine}${strategyLine}${messageCountAtStartLine}---
|
|
13652
13658
|
${state3.prompt}
|
|
13653
13659
|
`;
|
|
13654
|
-
|
|
13660
|
+
writeFileSync18(filePath, content, "utf-8");
|
|
13655
13661
|
return true;
|
|
13656
13662
|
} catch (error) {
|
|
13657
13663
|
if (!(error instanceof Error)) {
|
|
@@ -13663,8 +13669,8 @@ ${state3.prompt}
|
|
|
13663
13669
|
function clearState(directory, customPath) {
|
|
13664
13670
|
const filePath = getStateFilePath(directory, customPath);
|
|
13665
13671
|
try {
|
|
13666
|
-
if (
|
|
13667
|
-
|
|
13672
|
+
if (existsSync74(filePath)) {
|
|
13673
|
+
unlinkSync13(filePath);
|
|
13668
13674
|
}
|
|
13669
13675
|
return true;
|
|
13670
13676
|
} catch (error) {
|
|
@@ -81981,6 +81987,7 @@ import { existsSync as existsSync40 } from "fs";
|
|
|
81981
81987
|
|
|
81982
81988
|
// packages/omo-opencode/src/hooks/comment-checker/cli.ts
|
|
81983
81989
|
init_bun_spawn_shim();
|
|
81990
|
+
init_bun_which_shim();
|
|
81984
81991
|
import { join as join46 } from "path";
|
|
81985
81992
|
import { existsSync as existsSync39 } from "fs";
|
|
81986
81993
|
import * as fs8 from "fs";
|
|
@@ -82331,7 +82338,7 @@ function debugLog2(...args) {
|
|
|
82331
82338
|
function getBinaryName2() {
|
|
82332
82339
|
return process.platform === "win32" ? "comment-checker.exe" : "comment-checker";
|
|
82333
82340
|
}
|
|
82334
|
-
function resolveCommentCheckerPathFromPath(binaryName, which2 =
|
|
82341
|
+
function resolveCommentCheckerPathFromPath(binaryName, which2 = bunWhich) {
|
|
82335
82342
|
try {
|
|
82336
82343
|
return which2(binaryName);
|
|
82337
82344
|
} catch (error) {
|
|
@@ -87871,8 +87878,779 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
87871
87878
|
...Object.keys(accumulatedCommonFields).length > 0 ? accumulatedCommonFields : {}
|
|
87872
87879
|
};
|
|
87873
87880
|
}
|
|
87881
|
+
// packages/boulder-state/src/constants.ts
|
|
87882
|
+
var BOULDER_DIR = ".omo";
|
|
87883
|
+
var BOULDER_FILE = "boulder.json";
|
|
87884
|
+
var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
|
|
87885
|
+
var NOTEPAD_DIR = "notepads";
|
|
87886
|
+
var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
|
|
87887
|
+
var PROMETHEUS_PLANS_DIR = ".omo/plans";
|
|
87888
|
+
// packages/boulder-state/src/top-level-task.ts
|
|
87889
|
+
import { existsSync as existsSync56, readFileSync as readFileSync33 } from "fs";
|
|
87890
|
+
var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
|
|
87891
|
+
var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
|
|
87892
|
+
var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
|
|
87893
|
+
var UNCHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
|
|
87894
|
+
var TODO_TASK_PATTERN = /^(\d+)\.\s+(.+)$/;
|
|
87895
|
+
var FINAL_WAVE_TASK_PATTERN = /^(F\d+)\.\s+(.+)$/i;
|
|
87896
|
+
function buildTaskRef(section, taskLabel) {
|
|
87897
|
+
const pattern = section === "todo" ? TODO_TASK_PATTERN : FINAL_WAVE_TASK_PATTERN;
|
|
87898
|
+
const match = taskLabel.match(pattern);
|
|
87899
|
+
if (!match) {
|
|
87900
|
+
return null;
|
|
87901
|
+
}
|
|
87902
|
+
const rawLabel = match[1];
|
|
87903
|
+
const title = match[2].trim();
|
|
87904
|
+
return {
|
|
87905
|
+
key: `${section}:${rawLabel.toLowerCase()}`,
|
|
87906
|
+
section,
|
|
87907
|
+
label: rawLabel,
|
|
87908
|
+
title
|
|
87909
|
+
};
|
|
87910
|
+
}
|
|
87911
|
+
function readCurrentTopLevelTask(planPath) {
|
|
87912
|
+
if (!existsSync56(planPath)) {
|
|
87913
|
+
return null;
|
|
87914
|
+
}
|
|
87915
|
+
try {
|
|
87916
|
+
const content = readFileSync33(planPath, "utf-8");
|
|
87917
|
+
const lines = content.split(/\r?\n/);
|
|
87918
|
+
let section = "other";
|
|
87919
|
+
for (const line of lines) {
|
|
87920
|
+
if (SECOND_LEVEL_HEADING_PATTERN.test(line)) {
|
|
87921
|
+
section = TODO_HEADING_PATTERN.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN.test(line) ? "final-wave" : "other";
|
|
87922
|
+
}
|
|
87923
|
+
const uncheckedTaskMatch = line.match(UNCHECKED_CHECKBOX_PATTERN);
|
|
87924
|
+
if (!uncheckedTaskMatch || uncheckedTaskMatch[1].length > 0) {
|
|
87925
|
+
continue;
|
|
87926
|
+
}
|
|
87927
|
+
if (section !== "todo" && section !== "final-wave") {
|
|
87928
|
+
continue;
|
|
87929
|
+
}
|
|
87930
|
+
const taskRef = buildTaskRef(section, uncheckedTaskMatch[2].trim());
|
|
87931
|
+
if (taskRef) {
|
|
87932
|
+
return taskRef;
|
|
87933
|
+
}
|
|
87934
|
+
}
|
|
87935
|
+
return null;
|
|
87936
|
+
} catch {
|
|
87937
|
+
return null;
|
|
87938
|
+
}
|
|
87939
|
+
}
|
|
87940
|
+
// packages/boulder-state/src/storage/path.ts
|
|
87941
|
+
import { existsSync as existsSync57 } from "fs";
|
|
87942
|
+
import { isAbsolute as isAbsolute12, join as join67, relative as relative11, resolve as resolve17 } from "path";
|
|
87943
|
+
function getBoulderFilePath(directory) {
|
|
87944
|
+
return join67(directory, BOULDER_DIR, BOULDER_FILE);
|
|
87945
|
+
}
|
|
87946
|
+
function resolveTrackedPath(baseDirectory, trackedPath) {
|
|
87947
|
+
return isAbsolute12(trackedPath) ? resolve17(trackedPath) : resolve17(baseDirectory, trackedPath);
|
|
87948
|
+
}
|
|
87949
|
+
function resolveBoulderPlanPath(directory, state3) {
|
|
87950
|
+
const absolutePlanPath = resolveTrackedPath(directory, state3.active_plan);
|
|
87951
|
+
const worktreePath = state3.worktree_path?.trim();
|
|
87952
|
+
if (!worktreePath) {
|
|
87953
|
+
return absolutePlanPath;
|
|
87954
|
+
}
|
|
87955
|
+
const absoluteDirectory = resolve17(directory);
|
|
87956
|
+
const relativePlanPath = relative11(absoluteDirectory, absolutePlanPath);
|
|
87957
|
+
if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute12(relativePlanPath)) {
|
|
87958
|
+
return absolutePlanPath;
|
|
87959
|
+
}
|
|
87960
|
+
const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
|
|
87961
|
+
const worktreePlanPath = resolve17(absoluteWorktreePath, relativePlanPath);
|
|
87962
|
+
return existsSync57(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
|
|
87963
|
+
}
|
|
87964
|
+
function resolveBoulderPlanPathForWork(directory, work) {
|
|
87965
|
+
return resolveBoulderPlanPath(directory, work);
|
|
87966
|
+
}
|
|
87967
|
+
// packages/boulder-state/src/storage/plan-progress.ts
|
|
87968
|
+
import { existsSync as existsSync58, readFileSync as readFileSync34, readdirSync as readdirSync18, statSync as statSync8 } from "fs";
|
|
87969
|
+
import { basename as basename12, join as join68 } from "path";
|
|
87970
|
+
var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
|
|
87971
|
+
var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
|
|
87972
|
+
var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
|
|
87973
|
+
var UNCHECKED_CHECKBOX_PATTERN2 = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
|
|
87974
|
+
var CHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[[xX]\]\s*(.+)$/;
|
|
87975
|
+
var TODO_TASK_PATTERN2 = /^\d+\.\s+/;
|
|
87976
|
+
var FINAL_WAVE_TASK_PATTERN2 = /^F\d+\.\s+/i;
|
|
87977
|
+
var LEGACY_PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
|
|
87978
|
+
var PROMETHEUS_PLAN_DIRS = [PROMETHEUS_PLANS_DIR, LEGACY_PROMETHEUS_PLANS_DIR];
|
|
87979
|
+
function findPrometheusPlans(directory) {
|
|
87980
|
+
try {
|
|
87981
|
+
return PROMETHEUS_PLAN_DIRS.flatMap((planDir) => {
|
|
87982
|
+
const plansDir = join68(directory, planDir);
|
|
87983
|
+
if (!existsSync58(plansDir)) {
|
|
87984
|
+
return [];
|
|
87985
|
+
}
|
|
87986
|
+
return readdirSync18(plansDir).filter((file2) => file2.endsWith(".md")).map((file2) => join68(plansDir, file2));
|
|
87987
|
+
}).sort((left, right) => statSync8(right).mtimeMs - statSync8(left).mtimeMs);
|
|
87988
|
+
} catch {
|
|
87989
|
+
return [];
|
|
87990
|
+
}
|
|
87991
|
+
}
|
|
87992
|
+
function getPlanName(planPath) {
|
|
87993
|
+
return basename12(planPath, ".md");
|
|
87994
|
+
}
|
|
87995
|
+
function getPlanProgress(planPath) {
|
|
87996
|
+
if (!existsSync58(planPath)) {
|
|
87997
|
+
return { total: 0, completed: 0, isComplete: false };
|
|
87998
|
+
}
|
|
87999
|
+
try {
|
|
88000
|
+
const content = readFileSync34(planPath, "utf-8");
|
|
88001
|
+
const lines = content.split(/\r?\n/);
|
|
88002
|
+
const hasStructuredSections = lines.some((line) => TODO_HEADING_PATTERN2.test(line) || FINAL_VERIFICATION_HEADING_PATTERN2.test(line));
|
|
88003
|
+
if (hasStructuredSections) {
|
|
88004
|
+
return getStructuredPlanProgress(lines);
|
|
88005
|
+
}
|
|
88006
|
+
return getSimplePlanProgress(content);
|
|
88007
|
+
} catch {
|
|
88008
|
+
return { total: 0, completed: 0, isComplete: false };
|
|
88009
|
+
}
|
|
88010
|
+
}
|
|
88011
|
+
function getStructuredPlanProgress(lines) {
|
|
88012
|
+
let section = "other";
|
|
88013
|
+
let total = 0;
|
|
88014
|
+
let completed = 0;
|
|
88015
|
+
for (const line of lines) {
|
|
88016
|
+
if (SECOND_LEVEL_HEADING_PATTERN2.test(line)) {
|
|
88017
|
+
section = TODO_HEADING_PATTERN2.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN2.test(line) ? "final-wave" : "other";
|
|
88018
|
+
continue;
|
|
88019
|
+
}
|
|
88020
|
+
if (section !== "todo" && section !== "final-wave") {
|
|
88021
|
+
continue;
|
|
88022
|
+
}
|
|
88023
|
+
const checkedMatch = line.match(CHECKED_CHECKBOX_PATTERN);
|
|
88024
|
+
const uncheckedMatch = checkedMatch ? null : line.match(UNCHECKED_CHECKBOX_PATTERN2);
|
|
88025
|
+
const match = checkedMatch ?? uncheckedMatch;
|
|
88026
|
+
if (!match || match[1].length > 0) {
|
|
88027
|
+
continue;
|
|
88028
|
+
}
|
|
88029
|
+
const taskBody = match[2].trim();
|
|
88030
|
+
const labelPattern = section === "todo" ? TODO_TASK_PATTERN2 : FINAL_WAVE_TASK_PATTERN2;
|
|
88031
|
+
if (!labelPattern.test(taskBody)) {
|
|
88032
|
+
continue;
|
|
88033
|
+
}
|
|
88034
|
+
total += 1;
|
|
88035
|
+
if (checkedMatch) {
|
|
88036
|
+
completed += 1;
|
|
88037
|
+
}
|
|
88038
|
+
}
|
|
88039
|
+
return { total, completed, isComplete: total > 0 && completed === total };
|
|
88040
|
+
}
|
|
88041
|
+
function getSimplePlanProgress(content) {
|
|
88042
|
+
const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) ?? [];
|
|
88043
|
+
const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) ?? [];
|
|
88044
|
+
const total = uncheckedMatches.length + checkedMatches.length;
|
|
88045
|
+
const completed = checkedMatches.length;
|
|
88046
|
+
return { total, completed, isComplete: total > 0 && completed === total };
|
|
88047
|
+
}
|
|
88048
|
+
// packages/boulder-state/src/storage/shared.ts
|
|
88049
|
+
var RESERVED_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
88050
|
+
var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
|
|
88051
|
+
function normalizeSessionId(sessionId, platform2 = "opencode") {
|
|
88052
|
+
if (SESSION_ID_PREFIX_PATTERN.test(sessionId)) {
|
|
88053
|
+
return sessionId;
|
|
88054
|
+
}
|
|
88055
|
+
return `${platform2}:${sessionId}`;
|
|
88056
|
+
}
|
|
88057
|
+
function nowIsoString() {
|
|
88058
|
+
return new Date().toISOString();
|
|
88059
|
+
}
|
|
88060
|
+
function parseIsoToMs(value) {
|
|
88061
|
+
if (!value) {
|
|
88062
|
+
return null;
|
|
88063
|
+
}
|
|
88064
|
+
const parsed = Date.parse(value);
|
|
88065
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
88066
|
+
}
|
|
88067
|
+
function getElapsedMs(startedAt, endedAt) {
|
|
88068
|
+
const startedMs = parseIsoToMs(startedAt);
|
|
88069
|
+
const endedMs = parseIsoToMs(endedAt);
|
|
88070
|
+
if (startedMs === null || endedMs === null) {
|
|
88071
|
+
return;
|
|
88072
|
+
}
|
|
88073
|
+
return endedMs - startedMs;
|
|
88074
|
+
}
|
|
88075
|
+
function isValidWorkStatus(status) {
|
|
88076
|
+
return status === "active" || status === "completed" || status === "paused" || status === "abandoned";
|
|
88077
|
+
}
|
|
88078
|
+
function buildWorkFromMirror(state3) {
|
|
88079
|
+
const planName = state3.plan_name ?? state3.active_plan;
|
|
88080
|
+
const workId = `${planName}-legacy`;
|
|
88081
|
+
return {
|
|
88082
|
+
work_id: workId,
|
|
88083
|
+
active_plan: state3.active_plan,
|
|
88084
|
+
plan_name: planName,
|
|
88085
|
+
status: state3.status,
|
|
88086
|
+
started_at: state3.started_at,
|
|
88087
|
+
ended_at: state3.ended_at,
|
|
88088
|
+
elapsed_ms: state3.elapsed_ms,
|
|
88089
|
+
updated_at: state3.updated_at,
|
|
88090
|
+
session_ids: Array.isArray(state3.session_ids) ? [...state3.session_ids] : [],
|
|
88091
|
+
session_origins: state3.session_origins,
|
|
88092
|
+
agent: state3.agent,
|
|
88093
|
+
worktree_path: state3.worktree_path,
|
|
88094
|
+
task_sessions: state3.task_sessions
|
|
88095
|
+
};
|
|
88096
|
+
}
|
|
88097
|
+
function projectWorkToMirror(state3, work) {
|
|
88098
|
+
state3.active_plan = work.active_plan;
|
|
88099
|
+
state3.plan_name = work.plan_name;
|
|
88100
|
+
state3.status = work.status;
|
|
88101
|
+
state3.started_at = work.started_at;
|
|
88102
|
+
state3.ended_at = work.ended_at;
|
|
88103
|
+
state3.elapsed_ms = work.elapsed_ms;
|
|
88104
|
+
state3.updated_at = work.updated_at;
|
|
88105
|
+
state3.session_ids = [...work.session_ids];
|
|
88106
|
+
state3.session_origins = work.session_origins ? { ...work.session_origins } : {};
|
|
88107
|
+
state3.agent = work.agent;
|
|
88108
|
+
state3.worktree_path = work.worktree_path;
|
|
88109
|
+
state3.task_sessions = work.task_sessions ? { ...work.task_sessions } : {};
|
|
88110
|
+
}
|
|
88111
|
+
function selectMirrorWork(state3) {
|
|
88112
|
+
const works = state3.works ? Object.values(state3.works) : [];
|
|
88113
|
+
if (works.length === 0) {
|
|
88114
|
+
return null;
|
|
88115
|
+
}
|
|
88116
|
+
if (state3.active_work_id) {
|
|
88117
|
+
const matched = works.find((work) => work.work_id === state3.active_work_id);
|
|
88118
|
+
if (matched) {
|
|
88119
|
+
return matched;
|
|
88120
|
+
}
|
|
88121
|
+
}
|
|
88122
|
+
const sorted = [...works].sort((left, right) => {
|
|
88123
|
+
const leftMs = parseIsoToMs(left.updated_at ?? left.started_at) ?? 0;
|
|
88124
|
+
const rightMs = parseIsoToMs(right.updated_at ?? right.started_at) ?? 0;
|
|
88125
|
+
return rightMs - leftMs;
|
|
88126
|
+
});
|
|
88127
|
+
return sorted[0] ?? null;
|
|
88128
|
+
}
|
|
88129
|
+
// packages/boulder-state/src/storage/read-state.ts
|
|
88130
|
+
import { existsSync as existsSync59, readFileSync as readFileSync35 } from "fs";
|
|
88131
|
+
function readBoulderState(directory) {
|
|
88132
|
+
const filePath = getBoulderFilePath(directory);
|
|
88133
|
+
if (!existsSync59(filePath)) {
|
|
88134
|
+
return null;
|
|
88135
|
+
}
|
|
88136
|
+
try {
|
|
88137
|
+
const content = readFileSync35(filePath, "utf-8");
|
|
88138
|
+
const parsed = JSON.parse(content);
|
|
88139
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
88140
|
+
return null;
|
|
88141
|
+
}
|
|
88142
|
+
normalizeState(parsed);
|
|
88143
|
+
const state3 = parsed;
|
|
88144
|
+
const mirrorWork = selectMirrorWork(state3);
|
|
88145
|
+
if (mirrorWork) {
|
|
88146
|
+
state3.active_work_id = mirrorWork.work_id;
|
|
88147
|
+
projectWorkToMirror(state3, mirrorWork);
|
|
88148
|
+
}
|
|
88149
|
+
return state3;
|
|
88150
|
+
} catch {
|
|
88151
|
+
return null;
|
|
88152
|
+
}
|
|
88153
|
+
}
|
|
88154
|
+
function normalizeState(state3) {
|
|
88155
|
+
normalizeSessionFields(state3);
|
|
88156
|
+
const sessionIds = Array.isArray(state3.session_ids) ? state3.session_ids : [];
|
|
88157
|
+
const sessionOrigins = state3.session_origins && typeof state3.session_origins === "object" && !Array.isArray(state3.session_origins) ? state3.session_origins : {};
|
|
88158
|
+
state3.session_origins = sessionOrigins;
|
|
88159
|
+
if (sessionIds.length === 1) {
|
|
88160
|
+
const soleSessionId = sessionIds[0];
|
|
88161
|
+
if (typeof soleSessionId === "string" && sessionOrigins[soleSessionId] !== "appended" && sessionOrigins[soleSessionId] !== "direct") {
|
|
88162
|
+
sessionOrigins[soleSessionId] = "direct";
|
|
88163
|
+
}
|
|
88164
|
+
}
|
|
88165
|
+
if (!state3.task_sessions || typeof state3.task_sessions !== "object" || Array.isArray(state3.task_sessions)) {
|
|
88166
|
+
state3.task_sessions = {};
|
|
88167
|
+
}
|
|
88168
|
+
normalizeWorkSessionFields(state3.works);
|
|
88169
|
+
}
|
|
88170
|
+
function normalizeSessionFields(target) {
|
|
88171
|
+
const sessionIds = Array.isArray(target.session_ids) ? target.session_ids.filter((sessionId) => typeof sessionId === "string").map((sessionId) => normalizeSessionId(sessionId)) : [];
|
|
88172
|
+
target.session_ids = sessionIds;
|
|
88173
|
+
const sessionOrigins = target.session_origins && typeof target.session_origins === "object" && !Array.isArray(target.session_origins) ? normalizeSessionOrigins(target.session_origins) : {};
|
|
88174
|
+
target.session_origins = sessionOrigins;
|
|
88175
|
+
}
|
|
88176
|
+
function normalizeSessionOrigins(sessionOrigins) {
|
|
88177
|
+
return Object.fromEntries(Object.entries(sessionOrigins).map(([sessionId, origin]) => [normalizeSessionId(sessionId), origin]));
|
|
88178
|
+
}
|
|
88179
|
+
function normalizeWorkSessionFields(works) {
|
|
88180
|
+
if (!works || typeof works !== "object" || Array.isArray(works)) {
|
|
88181
|
+
return;
|
|
88182
|
+
}
|
|
88183
|
+
for (const work of Object.values(works)) {
|
|
88184
|
+
if (work && typeof work === "object" && !Array.isArray(work)) {
|
|
88185
|
+
normalizeSessionFields(work);
|
|
88186
|
+
}
|
|
88187
|
+
}
|
|
88188
|
+
}
|
|
88189
|
+
function getBoulderWorks(state3) {
|
|
88190
|
+
if (state3.works && typeof state3.works === "object") {
|
|
88191
|
+
return Object.values(state3.works).filter((work) => work != null);
|
|
88192
|
+
}
|
|
88193
|
+
if (!state3.active_plan || !state3.plan_name || !state3.started_at) {
|
|
88194
|
+
return [];
|
|
88195
|
+
}
|
|
88196
|
+
return [buildWorkFromMirror(state3)];
|
|
88197
|
+
}
|
|
88198
|
+
function getActiveWorks(directory) {
|
|
88199
|
+
const state3 = readBoulderState(directory);
|
|
88200
|
+
if (!state3) {
|
|
88201
|
+
return [];
|
|
88202
|
+
}
|
|
88203
|
+
return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned");
|
|
88204
|
+
}
|
|
88205
|
+
function getWorkById(directory, workId) {
|
|
88206
|
+
const state3 = readBoulderState(directory);
|
|
88207
|
+
if (!state3) {
|
|
88208
|
+
return null;
|
|
88209
|
+
}
|
|
88210
|
+
return getBoulderWorks(state3).find((work) => work.work_id === workId) ?? null;
|
|
88211
|
+
}
|
|
88212
|
+
function getWorkByPlanName(directory, planName, options) {
|
|
88213
|
+
const state3 = readBoulderState(directory);
|
|
88214
|
+
if (!state3) {
|
|
88215
|
+
return null;
|
|
88216
|
+
}
|
|
88217
|
+
const worktreePath = options?.worktreePath;
|
|
88218
|
+
return getBoulderWorks(state3).find((work) => {
|
|
88219
|
+
if (work.plan_name !== planName) {
|
|
88220
|
+
return false;
|
|
88221
|
+
}
|
|
88222
|
+
return worktreePath ? work.worktree_path === worktreePath : true;
|
|
88223
|
+
}) ?? null;
|
|
88224
|
+
}
|
|
88225
|
+
function getWorkForSession(directory, sessionId) {
|
|
88226
|
+
const state3 = readBoulderState(directory);
|
|
88227
|
+
if (!state3) {
|
|
88228
|
+
return null;
|
|
88229
|
+
}
|
|
88230
|
+
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
88231
|
+
let newestWork = null;
|
|
88232
|
+
let newestWorkMs = 0;
|
|
88233
|
+
for (const work of getBoulderWorks(state3)) {
|
|
88234
|
+
if (!work.session_ids.includes(normalizedSessionId)) {
|
|
88235
|
+
continue;
|
|
88236
|
+
}
|
|
88237
|
+
const workMs = parseIsoToMs(work.updated_at ?? work.started_at) ?? 0;
|
|
88238
|
+
if (!newestWork || workMs > newestWorkMs) {
|
|
88239
|
+
newestWork = work;
|
|
88240
|
+
newestWorkMs = workMs;
|
|
88241
|
+
}
|
|
88242
|
+
}
|
|
88243
|
+
if (newestWork) {
|
|
88244
|
+
return newestWork;
|
|
88245
|
+
}
|
|
88246
|
+
return state3.session_ids.includes(normalizedSessionId) ? buildWorkFromMirror(state3) : null;
|
|
88247
|
+
}
|
|
88248
|
+
function getWorkResumeOptions(directory) {
|
|
88249
|
+
const state3 = readBoulderState(directory);
|
|
88250
|
+
if (!state3) {
|
|
88251
|
+
return [];
|
|
88252
|
+
}
|
|
88253
|
+
return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned").map((work) => {
|
|
88254
|
+
const progress = getPlanProgress(resolveBoulderPlanPathForWork(directory, work));
|
|
88255
|
+
return {
|
|
88256
|
+
work_id: work.work_id,
|
|
88257
|
+
plan_name: work.plan_name,
|
|
88258
|
+
active_plan: work.active_plan,
|
|
88259
|
+
worktree_path: work.worktree_path,
|
|
88260
|
+
status: work.status && isValidWorkStatus(work.status) ? work.status : "active",
|
|
88261
|
+
started_at: work.started_at,
|
|
88262
|
+
updated_at: work.updated_at ?? work.started_at,
|
|
88263
|
+
ended_at: work.ended_at,
|
|
88264
|
+
elapsed_ms: work.elapsed_ms,
|
|
88265
|
+
session_count: work.session_ids.length,
|
|
88266
|
+
progress,
|
|
88267
|
+
is_current_mirror: state3.active_work_id === work.work_id
|
|
88268
|
+
};
|
|
88269
|
+
});
|
|
88270
|
+
}
|
|
88271
|
+
function getTaskSessionState(directory, taskKey) {
|
|
88272
|
+
const state3 = readBoulderState(directory);
|
|
88273
|
+
if (state3?.active_work_id) {
|
|
88274
|
+
const work = state3.works?.[state3.active_work_id];
|
|
88275
|
+
const taskSession = work?.task_sessions?.[taskKey];
|
|
88276
|
+
if (taskSession) {
|
|
88277
|
+
return taskSession;
|
|
88278
|
+
}
|
|
88279
|
+
}
|
|
88280
|
+
if (!state3?.task_sessions) {
|
|
88281
|
+
return null;
|
|
88282
|
+
}
|
|
88283
|
+
return state3.task_sessions[taskKey] ?? null;
|
|
88284
|
+
}
|
|
88285
|
+
// packages/boulder-state/src/storage/write-state.ts
|
|
88286
|
+
import { existsSync as existsSync60, mkdirSync as mkdirSync13, unlinkSync as unlinkSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
88287
|
+
import { dirname as dirname18, join as join69 } from "path";
|
|
88288
|
+
function writeBoulderState(directory, state3) {
|
|
88289
|
+
const filePath = getBoulderFilePath(directory);
|
|
88290
|
+
try {
|
|
88291
|
+
const dir = dirname18(filePath);
|
|
88292
|
+
if (!existsSync60(dir)) {
|
|
88293
|
+
mkdirSync13(dir, { recursive: true });
|
|
88294
|
+
writeFileSync12(join69(dir, ".gitignore"), ["*", "!/rules/", "!/rules/**", ""].join(`
|
|
88295
|
+
`), "utf-8");
|
|
88296
|
+
}
|
|
88297
|
+
const stateToWrite = { ...state3 };
|
|
88298
|
+
if (stateToWrite.works && stateToWrite.active_work_id) {
|
|
88299
|
+
const activeWork = stateToWrite.works[stateToWrite.active_work_id];
|
|
88300
|
+
if (activeWork) {
|
|
88301
|
+
stateToWrite.works = {
|
|
88302
|
+
...stateToWrite.works,
|
|
88303
|
+
[stateToWrite.active_work_id]: {
|
|
88304
|
+
...activeWork,
|
|
88305
|
+
active_plan: stateToWrite.active_plan,
|
|
88306
|
+
plan_name: stateToWrite.plan_name,
|
|
88307
|
+
status: stateToWrite.status,
|
|
88308
|
+
started_at: stateToWrite.started_at,
|
|
88309
|
+
ended_at: stateToWrite.ended_at,
|
|
88310
|
+
elapsed_ms: stateToWrite.elapsed_ms,
|
|
88311
|
+
updated_at: stateToWrite.updated_at,
|
|
88312
|
+
session_ids: [...stateToWrite.session_ids],
|
|
88313
|
+
session_origins: stateToWrite.session_origins ? { ...stateToWrite.session_origins } : {},
|
|
88314
|
+
agent: stateToWrite.agent,
|
|
88315
|
+
worktree_path: stateToWrite.worktree_path,
|
|
88316
|
+
task_sessions: stateToWrite.task_sessions ? { ...stateToWrite.task_sessions } : {}
|
|
88317
|
+
}
|
|
88318
|
+
};
|
|
88319
|
+
}
|
|
88320
|
+
}
|
|
88321
|
+
writeFileSync12(filePath, JSON.stringify(stateToWrite, null, 2), "utf-8");
|
|
88322
|
+
return true;
|
|
88323
|
+
} catch {
|
|
88324
|
+
return false;
|
|
88325
|
+
}
|
|
88326
|
+
}
|
|
88327
|
+
function clearBoulderState(directory) {
|
|
88328
|
+
const filePath = getBoulderFilePath(directory);
|
|
88329
|
+
try {
|
|
88330
|
+
if (existsSync60(filePath)) {
|
|
88331
|
+
unlinkSync7(filePath);
|
|
88332
|
+
}
|
|
88333
|
+
return true;
|
|
88334
|
+
} catch {
|
|
88335
|
+
return false;
|
|
88336
|
+
}
|
|
88337
|
+
}
|
|
88338
|
+
function generateWorkId(planName) {
|
|
88339
|
+
const slug = planName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
88340
|
+
const randomHex = Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
|
|
88341
|
+
return `${slug.length > 0 ? slug : "work"}-${randomHex}`;
|
|
88342
|
+
}
|
|
88343
|
+
function createBoulderState(planPath, sessionId, agent, worktreePath) {
|
|
88344
|
+
const startedAt = nowIsoString();
|
|
88345
|
+
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
88346
|
+
const workId = generateWorkId(getPlanName(planPath));
|
|
88347
|
+
const work = {
|
|
88348
|
+
work_id: workId,
|
|
88349
|
+
active_plan: planPath,
|
|
88350
|
+
plan_name: getPlanName(planPath),
|
|
88351
|
+
status: "active",
|
|
88352
|
+
started_at: startedAt,
|
|
88353
|
+
updated_at: startedAt,
|
|
88354
|
+
session_ids: [normalizedSessionId],
|
|
88355
|
+
session_origins: { [normalizedSessionId]: "direct" },
|
|
88356
|
+
...agent !== undefined ? { agent } : {},
|
|
88357
|
+
...worktreePath !== undefined ? { worktree_path: worktreePath } : {},
|
|
88358
|
+
task_sessions: {}
|
|
88359
|
+
};
|
|
88360
|
+
return {
|
|
88361
|
+
schema_version: 2,
|
|
88362
|
+
active_work_id: workId,
|
|
88363
|
+
works: { [workId]: work },
|
|
88364
|
+
active_plan: planPath,
|
|
88365
|
+
started_at: startedAt,
|
|
88366
|
+
status: "active",
|
|
88367
|
+
updated_at: startedAt,
|
|
88368
|
+
session_ids: [normalizedSessionId],
|
|
88369
|
+
session_origins: { [normalizedSessionId]: "direct" },
|
|
88370
|
+
plan_name: getPlanName(planPath),
|
|
88371
|
+
task_sessions: {},
|
|
88372
|
+
...agent !== undefined ? { agent } : {},
|
|
88373
|
+
...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
|
|
88374
|
+
};
|
|
88375
|
+
}
|
|
88376
|
+
function selectActiveWork(directory, workId) {
|
|
88377
|
+
const state3 = readBoulderState(directory);
|
|
88378
|
+
if (!state3) {
|
|
88379
|
+
return null;
|
|
88380
|
+
}
|
|
88381
|
+
const works = getBoulderWorks(state3);
|
|
88382
|
+
const nextWork = works.find((work) => work.work_id === workId);
|
|
88383
|
+
if (!nextWork) {
|
|
88384
|
+
return null;
|
|
88385
|
+
}
|
|
88386
|
+
const nextState = {
|
|
88387
|
+
...state3,
|
|
88388
|
+
schema_version: 2,
|
|
88389
|
+
active_work_id: workId,
|
|
88390
|
+
works: state3.works ?? Object.fromEntries(works.map((work) => [work.work_id, work]))
|
|
88391
|
+
};
|
|
88392
|
+
projectWorkToMirror(nextState, nextWork);
|
|
88393
|
+
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
88394
|
+
}
|
|
88395
|
+
function addBoulderWork(directory, input) {
|
|
88396
|
+
const state3 = readBoulderState(directory);
|
|
88397
|
+
if (!state3) {
|
|
88398
|
+
return null;
|
|
88399
|
+
}
|
|
88400
|
+
const workId = generateWorkId(getPlanName(input.planPath));
|
|
88401
|
+
const startedAt = input.startedAt ?? nowIsoString();
|
|
88402
|
+
const normalizedSessionId = normalizeSessionId(input.sessionId);
|
|
88403
|
+
const nextWork = {
|
|
88404
|
+
work_id: workId,
|
|
88405
|
+
active_plan: input.planPath,
|
|
88406
|
+
plan_name: getPlanName(input.planPath),
|
|
88407
|
+
status: "active",
|
|
88408
|
+
started_at: startedAt,
|
|
88409
|
+
updated_at: startedAt,
|
|
88410
|
+
session_ids: [normalizedSessionId],
|
|
88411
|
+
session_origins: { [normalizedSessionId]: "direct" },
|
|
88412
|
+
...input.agent !== undefined ? { agent: input.agent } : {},
|
|
88413
|
+
...input.worktreePath !== undefined ? { worktree_path: input.worktreePath } : {},
|
|
88414
|
+
task_sessions: {}
|
|
88415
|
+
};
|
|
88416
|
+
const nextState = {
|
|
88417
|
+
...state3,
|
|
88418
|
+
schema_version: 2,
|
|
88419
|
+
works: { ...Object.fromEntries(getBoulderWorks(state3).map((work) => [work.work_id, work])), [workId]: nextWork },
|
|
88420
|
+
active_work_id: workId
|
|
88421
|
+
};
|
|
88422
|
+
projectWorkToMirror(nextState, nextWork);
|
|
88423
|
+
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
88424
|
+
}
|
|
88425
|
+
function completeBoulder(directory, workId, endedAt) {
|
|
88426
|
+
const state3 = readBoulderState(directory);
|
|
88427
|
+
if (!state3) {
|
|
88428
|
+
return null;
|
|
88429
|
+
}
|
|
88430
|
+
const targetWorkId = workId ?? state3.active_work_id;
|
|
88431
|
+
if (!targetWorkId) {
|
|
88432
|
+
return null;
|
|
88433
|
+
}
|
|
88434
|
+
const work = state3.works?.[targetWorkId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === targetWorkId);
|
|
88435
|
+
if (!work) {
|
|
88436
|
+
return null;
|
|
88437
|
+
}
|
|
88438
|
+
if (work.status === "completed" && work.ended_at !== undefined && work.elapsed_ms !== undefined) {
|
|
88439
|
+
return state3;
|
|
88440
|
+
}
|
|
88441
|
+
const endAt = endedAt ?? nowIsoString();
|
|
88442
|
+
work.ended_at = endAt;
|
|
88443
|
+
work.elapsed_ms = getElapsedMs(work.started_at, endAt);
|
|
88444
|
+
work.status = "completed";
|
|
88445
|
+
work.updated_at = nowIsoString();
|
|
88446
|
+
if (state3.active_work_id === targetWorkId) {
|
|
88447
|
+
projectWorkToMirror(state3, work);
|
|
88448
|
+
}
|
|
88449
|
+
return writeBoulderState(directory, state3) ? state3 : null;
|
|
88450
|
+
}
|
|
87874
88451
|
|
|
88452
|
+
// packages/boulder-state/src/storage/session.ts
|
|
88453
|
+
function appendSessionId(directory, sessionId, origin = "direct") {
|
|
88454
|
+
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
88455
|
+
const activeWorkId = readBoulderState(directory)?.active_work_id;
|
|
88456
|
+
if (activeWorkId) {
|
|
88457
|
+
return appendSessionIdForWork(directory, activeWorkId, normalizedSessionId, origin);
|
|
88458
|
+
}
|
|
88459
|
+
const state3 = readBoulderState(directory);
|
|
88460
|
+
if (!state3) {
|
|
88461
|
+
return null;
|
|
88462
|
+
}
|
|
88463
|
+
if (!state3.session_origins || typeof state3.session_origins !== "object" || Array.isArray(state3.session_origins)) {
|
|
88464
|
+
state3.session_origins = {};
|
|
88465
|
+
}
|
|
88466
|
+
if (!state3.session_ids?.includes(normalizedSessionId)) {
|
|
88467
|
+
if (!Array.isArray(state3.session_ids)) {
|
|
88468
|
+
state3.session_ids = [];
|
|
88469
|
+
}
|
|
88470
|
+
const originalSessionIds = [...state3.session_ids];
|
|
88471
|
+
const originalSessionOrigins = { ...state3.session_origins };
|
|
88472
|
+
state3.session_ids.push(normalizedSessionId);
|
|
88473
|
+
state3.session_origins[normalizedSessionId] = origin;
|
|
88474
|
+
if (writeBoulderState(directory, state3)) {
|
|
88475
|
+
return state3;
|
|
88476
|
+
}
|
|
88477
|
+
state3.session_ids = originalSessionIds;
|
|
88478
|
+
state3.session_origins = originalSessionOrigins;
|
|
88479
|
+
return null;
|
|
88480
|
+
}
|
|
88481
|
+
if (!state3.session_origins[normalizedSessionId]) {
|
|
88482
|
+
state3.session_origins[normalizedSessionId] = origin;
|
|
88483
|
+
if (!writeBoulderState(directory, state3)) {
|
|
88484
|
+
return null;
|
|
88485
|
+
}
|
|
88486
|
+
}
|
|
88487
|
+
return state3;
|
|
88488
|
+
}
|
|
88489
|
+
function appendSessionIdForWork(directory, workId, sessionId, origin = "direct") {
|
|
88490
|
+
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
88491
|
+
const state3 = readBoulderState(directory);
|
|
88492
|
+
if (!state3) {
|
|
88493
|
+
return null;
|
|
88494
|
+
}
|
|
88495
|
+
const works = getBoulderWorks(state3);
|
|
88496
|
+
const targetWork = works.find((work) => work.work_id === workId);
|
|
88497
|
+
if (!targetWork) {
|
|
88498
|
+
return null;
|
|
88499
|
+
}
|
|
88500
|
+
const updatedWork = {
|
|
88501
|
+
...targetWork,
|
|
88502
|
+
session_ids: targetWork.session_ids.includes(normalizedSessionId) ? [...targetWork.session_ids] : [...targetWork.session_ids, normalizedSessionId],
|
|
88503
|
+
session_origins: { ...targetWork.session_origins ?? {}, [normalizedSessionId]: origin },
|
|
88504
|
+
updated_at: nowIsoString()
|
|
88505
|
+
};
|
|
88506
|
+
const nextState = {
|
|
88507
|
+
...state3,
|
|
88508
|
+
schema_version: 2,
|
|
88509
|
+
works: {
|
|
88510
|
+
...Object.fromEntries(works.map((work) => [work.work_id, work])),
|
|
88511
|
+
[workId]: updatedWork
|
|
88512
|
+
}
|
|
88513
|
+
};
|
|
88514
|
+
if (state3.active_work_id === workId) {
|
|
88515
|
+
projectWorkToMirror(nextState, updatedWork);
|
|
88516
|
+
}
|
|
88517
|
+
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
88518
|
+
}
|
|
88519
|
+
// packages/boulder-state/src/storage/task.ts
|
|
88520
|
+
function upsertTaskSessionState(directory, input) {
|
|
88521
|
+
const stateForWork = readBoulderState(directory);
|
|
88522
|
+
if (stateForWork?.active_work_id) {
|
|
88523
|
+
return upsertTaskSessionStateForWork(directory, stateForWork.active_work_id, input);
|
|
88524
|
+
}
|
|
88525
|
+
const state3 = readBoulderState(directory);
|
|
88526
|
+
if (!state3 || RESERVED_KEYS.has(input.taskKey)) {
|
|
88527
|
+
return null;
|
|
88528
|
+
}
|
|
88529
|
+
const normalizedSessionId = normalizeSessionId(input.sessionId);
|
|
88530
|
+
const taskSessions = state3.task_sessions ?? {};
|
|
88531
|
+
taskSessions[input.taskKey] = {
|
|
88532
|
+
task_key: input.taskKey,
|
|
88533
|
+
task_label: input.taskLabel,
|
|
88534
|
+
task_title: input.taskTitle,
|
|
88535
|
+
session_id: normalizedSessionId,
|
|
88536
|
+
...input.agent !== undefined ? { agent: input.agent } : {},
|
|
88537
|
+
...input.category !== undefined ? { category: input.category } : {},
|
|
88538
|
+
updated_at: nowIsoString()
|
|
88539
|
+
};
|
|
88540
|
+
state3.task_sessions = taskSessions;
|
|
88541
|
+
return writeBoulderState(directory, state3) ? state3 : null;
|
|
88542
|
+
}
|
|
88543
|
+
function upsertTaskSessionStateForWork(directory, workId, input) {
|
|
88544
|
+
if (RESERVED_KEYS.has(input.taskKey)) {
|
|
88545
|
+
return null;
|
|
88546
|
+
}
|
|
88547
|
+
const state3 = readBoulderState(directory);
|
|
88548
|
+
if (!state3) {
|
|
88549
|
+
return null;
|
|
88550
|
+
}
|
|
88551
|
+
const works = getBoulderWorks(state3);
|
|
88552
|
+
const targetWork = works.find((work) => work.work_id === workId);
|
|
88553
|
+
if (!targetWork) {
|
|
88554
|
+
return null;
|
|
88555
|
+
}
|
|
88556
|
+
const normalizedSessionId = normalizeSessionId(input.sessionId);
|
|
88557
|
+
const previousTaskSession = targetWork.task_sessions?.[input.taskKey];
|
|
88558
|
+
const nextTaskSession = {
|
|
88559
|
+
task_key: input.taskKey,
|
|
88560
|
+
task_label: input.taskLabel,
|
|
88561
|
+
task_title: input.taskTitle,
|
|
88562
|
+
session_id: normalizedSessionId,
|
|
88563
|
+
...input.agent !== undefined ? { agent: input.agent } : {},
|
|
88564
|
+
...input.category !== undefined ? { category: input.category } : {},
|
|
88565
|
+
...previousTaskSession?.started_at !== undefined ? { started_at: previousTaskSession.started_at } : {},
|
|
88566
|
+
...previousTaskSession?.ended_at !== undefined ? { ended_at: previousTaskSession.ended_at } : {},
|
|
88567
|
+
...previousTaskSession?.elapsed_ms !== undefined ? { elapsed_ms: previousTaskSession.elapsed_ms } : {},
|
|
88568
|
+
...previousTaskSession?.status !== undefined ? { status: previousTaskSession.status } : {},
|
|
88569
|
+
updated_at: nowIsoString()
|
|
88570
|
+
};
|
|
88571
|
+
const nextWork = {
|
|
88572
|
+
...targetWork,
|
|
88573
|
+
task_sessions: { ...targetWork.task_sessions ?? {}, [input.taskKey]: nextTaskSession },
|
|
88574
|
+
updated_at: nowIsoString()
|
|
88575
|
+
};
|
|
88576
|
+
const nextState = {
|
|
88577
|
+
...state3,
|
|
88578
|
+
schema_version: 2,
|
|
88579
|
+
works: {
|
|
88580
|
+
...Object.fromEntries(works.map((work) => [work.work_id, work])),
|
|
88581
|
+
[workId]: nextWork
|
|
88582
|
+
}
|
|
88583
|
+
};
|
|
88584
|
+
if (state3.active_work_id === workId) {
|
|
88585
|
+
projectWorkToMirror(nextState, nextWork);
|
|
88586
|
+
}
|
|
88587
|
+
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
88588
|
+
}
|
|
88589
|
+
function startTaskTimer(directory, workId, input) {
|
|
88590
|
+
const nextState = upsertTaskSessionStateForWork(directory, workId, {
|
|
88591
|
+
...input,
|
|
88592
|
+
sessionId: normalizeSessionId(input.sessionId)
|
|
88593
|
+
});
|
|
88594
|
+
if (!nextState) {
|
|
88595
|
+
return null;
|
|
88596
|
+
}
|
|
88597
|
+
const work = nextState.works?.[workId];
|
|
88598
|
+
const taskSession = work?.task_sessions?.[input.taskKey];
|
|
88599
|
+
if (!work || !taskSession) {
|
|
88600
|
+
return null;
|
|
88601
|
+
}
|
|
88602
|
+
const startedAt = taskSession.started_at ?? input.startedAt ?? nowIsoString();
|
|
88603
|
+
taskSession.started_at = startedAt;
|
|
88604
|
+
taskSession.status = "running";
|
|
88605
|
+
taskSession.updated_at = nowIsoString();
|
|
88606
|
+
work.updated_at = nowIsoString();
|
|
88607
|
+
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
88608
|
+
}
|
|
88609
|
+
function endTaskTimer(directory, workId, taskKey, endedAt) {
|
|
88610
|
+
const state3 = readBoulderState(directory);
|
|
88611
|
+
if (!state3) {
|
|
88612
|
+
return null;
|
|
88613
|
+
}
|
|
88614
|
+
const work = state3.works?.[workId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === workId);
|
|
88615
|
+
if (!work?.task_sessions?.[taskKey]) {
|
|
88616
|
+
return null;
|
|
88617
|
+
}
|
|
88618
|
+
const taskSession = work.task_sessions[taskKey];
|
|
88619
|
+
const endAt = endedAt ?? nowIsoString();
|
|
88620
|
+
taskSession.ended_at = endAt;
|
|
88621
|
+
taskSession.elapsed_ms = getElapsedMs(taskSession.started_at, endAt);
|
|
88622
|
+
taskSession.status = "completed";
|
|
88623
|
+
taskSession.updated_at = nowIsoString();
|
|
88624
|
+
work.updated_at = nowIsoString();
|
|
88625
|
+
if (state3.active_work_id === workId) {
|
|
88626
|
+
projectWorkToMirror(state3, work);
|
|
88627
|
+
}
|
|
88628
|
+
return writeBoulderState(directory, state3) ? state3 : null;
|
|
88629
|
+
}
|
|
88630
|
+
// packages/omo-opencode/src/features/boulder-state/format-duration.ts
|
|
88631
|
+
init_src();
|
|
87875
88632
|
// packages/omo-opencode/src/hooks/claude-code-hooks/handlers/tool-execute-before-handler.ts
|
|
88633
|
+
function nonBlankString(value) {
|
|
88634
|
+
if (typeof value !== "string") {
|
|
88635
|
+
return null;
|
|
88636
|
+
}
|
|
88637
|
+
const trimmed = value.trim();
|
|
88638
|
+
return trimmed.length > 0 ? value : null;
|
|
88639
|
+
}
|
|
88640
|
+
function resolvePreToolUseCwd(input, toolInput, ctx) {
|
|
88641
|
+
if (input.tool.trim().toLowerCase() !== "bash") {
|
|
88642
|
+
return ctx.directory;
|
|
88643
|
+
}
|
|
88644
|
+
const explicitCwd = nonBlankString(toolInput.cwd);
|
|
88645
|
+
if (explicitCwd) {
|
|
88646
|
+
return explicitCwd;
|
|
88647
|
+
}
|
|
88648
|
+
const trackedWorktree = nonBlankString(getWorkForSession(ctx.directory, input.sessionID)?.worktree_path);
|
|
88649
|
+
if (trackedWorktree) {
|
|
88650
|
+
return trackedWorktree;
|
|
88651
|
+
}
|
|
88652
|
+
return nonBlankString(ctx.worktree) ?? ctx.directory;
|
|
88653
|
+
}
|
|
87876
88654
|
function createToolExecuteBeforeHandler(ctx, config) {
|
|
87877
88655
|
return async (input, output) => {
|
|
87878
88656
|
if (input.tool.trim() === "todowrite" && typeof output.args.todos === "string") {
|
|
@@ -87909,7 +88687,7 @@ function createToolExecuteBeforeHandler(ctx, config) {
|
|
|
87909
88687
|
sessionId: input.sessionID,
|
|
87910
88688
|
toolName: input.tool,
|
|
87911
88689
|
toolInput: output.args,
|
|
87912
|
-
cwd:
|
|
88690
|
+
cwd: resolvePreToolUseCwd(input, output.args, ctx),
|
|
87913
88691
|
toolUseId: input.callID
|
|
87914
88692
|
};
|
|
87915
88693
|
const result = await executePreToolUseHooks(preCtx, claudeConfig, extendedConfig);
|
|
@@ -87952,28 +88730,28 @@ function createClaudeCodeHooksHook(ctx, config = {}, contextCollector) {
|
|
|
87952
88730
|
}
|
|
87953
88731
|
// packages/omo-opencode/src/hooks/rules-injector/storage.ts
|
|
87954
88732
|
import {
|
|
87955
|
-
existsSync as
|
|
87956
|
-
mkdirSync as
|
|
87957
|
-
readFileSync as
|
|
87958
|
-
writeFileSync as
|
|
87959
|
-
unlinkSync as
|
|
88733
|
+
existsSync as existsSync61,
|
|
88734
|
+
mkdirSync as mkdirSync14,
|
|
88735
|
+
readFileSync as readFileSync36,
|
|
88736
|
+
writeFileSync as writeFileSync13,
|
|
88737
|
+
unlinkSync as unlinkSync8
|
|
87960
88738
|
} from "fs";
|
|
87961
|
-
import { join as
|
|
88739
|
+
import { join as join71 } from "path";
|
|
87962
88740
|
|
|
87963
88741
|
// packages/omo-opencode/src/hooks/rules-injector/constants.ts
|
|
87964
|
-
import { join as
|
|
87965
|
-
var RULES_INJECTOR_STORAGE =
|
|
88742
|
+
import { join as join70 } from "path";
|
|
88743
|
+
var RULES_INJECTOR_STORAGE = join70(OPENCODE_STORAGE, "rules-injector");
|
|
87966
88744
|
|
|
87967
88745
|
// packages/omo-opencode/src/hooks/rules-injector/storage.ts
|
|
87968
88746
|
function getStoragePath(sessionID) {
|
|
87969
|
-
return
|
|
88747
|
+
return join71(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
87970
88748
|
}
|
|
87971
88749
|
function loadInjectedRules(sessionID) {
|
|
87972
88750
|
const filePath = getStoragePath(sessionID);
|
|
87973
|
-
if (!
|
|
88751
|
+
if (!existsSync61(filePath))
|
|
87974
88752
|
return { contentHashes: new Set, realPaths: new Set };
|
|
87975
88753
|
try {
|
|
87976
|
-
const content =
|
|
88754
|
+
const content = readFileSync36(filePath, "utf-8");
|
|
87977
88755
|
const data = JSON.parse(content);
|
|
87978
88756
|
return {
|
|
87979
88757
|
contentHashes: new Set(data.injectedHashes),
|
|
@@ -87993,21 +88771,21 @@ function saveInjectedRules(sessionID, data) {
|
|
|
87993
88771
|
injectedRealPaths: [...data.realPaths],
|
|
87994
88772
|
updatedAt: Date.now()
|
|
87995
88773
|
};
|
|
87996
|
-
|
|
88774
|
+
mkdirSync14(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
87997
88775
|
try {
|
|
87998
|
-
|
|
88776
|
+
writeFileSync13(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
|
|
87999
88777
|
} catch (error) {
|
|
88000
88778
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
|
|
88001
88779
|
throw error;
|
|
88002
88780
|
}
|
|
88003
|
-
|
|
88004
|
-
|
|
88781
|
+
mkdirSync14(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
88782
|
+
writeFileSync13(getStoragePath(sessionID), JSON.stringify(storageData, null, 2));
|
|
88005
88783
|
}
|
|
88006
88784
|
}
|
|
88007
88785
|
function clearInjectedRules(sessionID) {
|
|
88008
88786
|
const filePath = getStoragePath(sessionID);
|
|
88009
|
-
if (
|
|
88010
|
-
|
|
88787
|
+
if (existsSync61(filePath)) {
|
|
88788
|
+
unlinkSync8(filePath);
|
|
88011
88789
|
}
|
|
88012
88790
|
}
|
|
88013
88791
|
// packages/omo-opencode/src/hooks/rules-injector/cache.ts
|
|
@@ -88100,7 +88878,7 @@ function matchDecisionCacheKey(projectRoot, resolvedFilePath, realPath, statFing
|
|
|
88100
88878
|
}
|
|
88101
88879
|
|
|
88102
88880
|
// packages/omo-opencode/src/hooks/rules-injector/parsed-rule-cache.ts
|
|
88103
|
-
import { readFileSync as
|
|
88881
|
+
import { readFileSync as readFileSync37, statSync as statSync9 } from "fs";
|
|
88104
88882
|
var MAX_PARSED_RULE_CACHE_ENTRIES = 256;
|
|
88105
88883
|
var MAX_PARSED_RULE_CACHE_BODY_BYTES = 64 * 1024;
|
|
88106
88884
|
var parsedRuleCache = new Map;
|
|
@@ -88108,8 +88886,8 @@ function clearParsedRuleCache() {
|
|
|
88108
88886
|
parsedRuleCache.clear();
|
|
88109
88887
|
}
|
|
88110
88888
|
function createParsedRuleReader(options) {
|
|
88111
|
-
const readRuleFileSync = options?.readFileSync ??
|
|
88112
|
-
const statRuleSync = options?.statSync ??
|
|
88889
|
+
const readRuleFileSync = options?.readFileSync ?? readFileSync37;
|
|
88890
|
+
const statRuleSync = options?.statSync ?? statSync9;
|
|
88113
88891
|
return (filePath, realPath) => {
|
|
88114
88892
|
try {
|
|
88115
88893
|
const stat2 = statRuleSync(filePath);
|
|
@@ -88154,13 +88932,13 @@ function setParsedRuleCacheEntry(realPath, entry) {
|
|
|
88154
88932
|
}
|
|
88155
88933
|
|
|
88156
88934
|
// packages/omo-opencode/src/hooks/rules-injector/path-resolution.ts
|
|
88157
|
-
import { resolve as
|
|
88935
|
+
import { resolve as resolve18 } from "path";
|
|
88158
88936
|
function resolveFilePath4(workspaceDirectory, path8) {
|
|
88159
88937
|
if (!path8)
|
|
88160
88938
|
return null;
|
|
88161
88939
|
if (path8.startsWith("/"))
|
|
88162
88940
|
return path8;
|
|
88163
|
-
return
|
|
88941
|
+
return resolve18(workspaceDirectory, path8);
|
|
88164
88942
|
}
|
|
88165
88943
|
|
|
88166
88944
|
// packages/omo-opencode/src/hooks/rules-injector/rule-match-reason.ts
|
|
@@ -88720,7 +89498,7 @@ function getCachedVersion(options = {}) {
|
|
|
88720
89498
|
// package.json
|
|
88721
89499
|
var package_default = {
|
|
88722
89500
|
name: "oh-my-opencode",
|
|
88723
|
-
version: "4.16.
|
|
89501
|
+
version: "4.16.2",
|
|
88724
89502
|
description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
|
|
88725
89503
|
main: "./dist/index.js",
|
|
88726
89504
|
types: "dist/index.d.ts",
|
|
@@ -88766,10 +89544,11 @@ var package_default = {
|
|
|
88766
89544
|
"dist",
|
|
88767
89545
|
"bin",
|
|
88768
89546
|
"postinstall.mjs",
|
|
89547
|
+
"script/qa/strip-ansi.mjs",
|
|
88769
89548
|
"script/qa/web-terminal-redaction.d.mts",
|
|
88770
89549
|
"script/qa/web-terminal-redaction.mjs",
|
|
88771
|
-
"script/qa/web-terminal-renderer.mjs",
|
|
88772
89550
|
"script/qa/web-terminal-visual-qa.mjs",
|
|
89551
|
+
"script/qa/xterm-live-terminal.mjs",
|
|
88773
89552
|
"docs/reference/github-attachment-upload.md",
|
|
88774
89553
|
"docs/reference/web-terminal-visual-qa.md",
|
|
88775
89554
|
".opencode/command",
|
|
@@ -88835,6 +89614,7 @@ var package_default = {
|
|
|
88835
89614
|
"build:all": "bun run build && bun run build:binaries",
|
|
88836
89615
|
"build:binaries": "bun run script/build-binaries.ts",
|
|
88837
89616
|
"build:schema": "bun run script/build-schema.ts",
|
|
89617
|
+
"build:omo-schema": "bun run script/build-omo-schema.ts",
|
|
88838
89618
|
"build:model-capabilities": "bun run script/build-model-capabilities.ts",
|
|
88839
89619
|
clean: "rm -rf dist",
|
|
88840
89620
|
prepack: "bun run build:materialize-frontend",
|
|
@@ -88891,51 +89671,55 @@ var package_default = {
|
|
|
88891
89671
|
zod: "^4.4.3"
|
|
88892
89672
|
},
|
|
88893
89673
|
devDependencies: {
|
|
88894
|
-
"@oh-my-opencode/git-bash-mcp": "workspace:*",
|
|
88895
|
-
"@oh-my-opencode/lsp-core": "workspace:*",
|
|
88896
|
-
"@oh-my-opencode/mcp-stdio-core": "workspace:*",
|
|
88897
|
-
"@oh-my-opencode/mcp-client-core": "workspace:*",
|
|
88898
89674
|
"@oh-my-opencode/agents-md-core": "workspace:*",
|
|
88899
89675
|
"@oh-my-opencode/boulder-state": "workspace:*",
|
|
88900
89676
|
"@oh-my-opencode/claude-code-compat-core": "workspace:*",
|
|
88901
|
-
"@oh-my-opencode/skills-loader-core": "workspace:*",
|
|
88902
89677
|
"@oh-my-opencode/comment-checker-core": "workspace:*",
|
|
88903
89678
|
"@oh-my-opencode/delegate-core": "workspace:*",
|
|
89679
|
+
"@oh-my-opencode/git-bash-mcp": "workspace:*",
|
|
88904
89680
|
"@oh-my-opencode/hashline-core": "workspace:*",
|
|
88905
|
-
"@oh-my-opencode/
|
|
88906
|
-
"@oh-my-opencode/
|
|
88907
|
-
"@oh-my-opencode/
|
|
89681
|
+
"@oh-my-opencode/lsp-core": "workspace:*",
|
|
89682
|
+
"@oh-my-opencode/mcp-client-core": "workspace:*",
|
|
89683
|
+
"@oh-my-opencode/mcp-stdio-core": "workspace:*",
|
|
88908
89684
|
"@oh-my-opencode/model-core": "workspace:*",
|
|
88909
|
-
"@oh-my-opencode/omo-config-core": "workspace:*",
|
|
88910
89685
|
"@oh-my-opencode/omo-codex": "workspace:*",
|
|
89686
|
+
"@oh-my-opencode/omo-config-core": "workspace:*",
|
|
88911
89687
|
"@oh-my-opencode/omo-senpi": "workspace:*",
|
|
88912
|
-
"@oh-my-opencode/
|
|
89688
|
+
"@oh-my-opencode/openclaw-core": "workspace:*",
|
|
88913
89689
|
"@oh-my-opencode/pi-goal": "workspace:*",
|
|
88914
89690
|
"@oh-my-opencode/pi-webfetch": "workspace:*",
|
|
88915
89691
|
"@oh-my-opencode/prompts-core": "workspace:*",
|
|
88916
89692
|
"@oh-my-opencode/rules-engine": "workspace:*",
|
|
89693
|
+
"@oh-my-opencode/senpi-task": "workspace:*",
|
|
88917
89694
|
"@oh-my-opencode/shared-skills": "workspace:*",
|
|
89695
|
+
"@oh-my-opencode/skills-loader-core": "workspace:*",
|
|
89696
|
+
"@oh-my-opencode/team-core": "workspace:*",
|
|
88918
89697
|
"@oh-my-opencode/telemetry-core": "workspace:*",
|
|
89698
|
+
"@oh-my-opencode/tmux-core": "workspace:*",
|
|
88919
89699
|
"@oh-my-opencode/utils": "workspace:*",
|
|
88920
|
-
"@typescript/native-preview": "7.0.0-dev.20260518.1",
|
|
88921
89700
|
"@types/js-yaml": "^4.0.9",
|
|
88922
89701
|
"@types/picomatch": "^4.0.3",
|
|
89702
|
+
"@typescript/native-preview": "7.0.0-dev.20260518.1",
|
|
89703
|
+
"@xterm/addon-unicode11": "^0.9.0",
|
|
89704
|
+
"@xterm/xterm": "^6.0.0",
|
|
88923
89705
|
"bun-types": "1.3.14",
|
|
89706
|
+
"node-pty": "^1.1.0",
|
|
89707
|
+
"puppeteer-core": "^25.3.0",
|
|
88924
89708
|
typescript: "^6.0.3"
|
|
88925
89709
|
},
|
|
88926
89710
|
optionalDependencies: {
|
|
88927
|
-
"oh-my-opencode-darwin-arm64": "4.16.
|
|
88928
|
-
"oh-my-opencode-darwin-x64": "4.16.
|
|
88929
|
-
"oh-my-opencode-darwin-x64-baseline": "4.16.
|
|
88930
|
-
"oh-my-opencode-linux-arm64": "4.16.
|
|
88931
|
-
"oh-my-opencode-linux-arm64-musl": "4.16.
|
|
88932
|
-
"oh-my-opencode-linux-x64": "4.16.
|
|
88933
|
-
"oh-my-opencode-linux-x64-baseline": "4.16.
|
|
88934
|
-
"oh-my-opencode-linux-x64-musl": "4.16.
|
|
88935
|
-
"oh-my-opencode-linux-x64-musl-baseline": "4.16.
|
|
88936
|
-
"oh-my-opencode-windows-arm64": "4.16.
|
|
88937
|
-
"oh-my-opencode-windows-x64": "4.16.
|
|
88938
|
-
"oh-my-opencode-windows-x64-baseline": "4.16.
|
|
89711
|
+
"oh-my-opencode-darwin-arm64": "4.16.2",
|
|
89712
|
+
"oh-my-opencode-darwin-x64": "4.16.2",
|
|
89713
|
+
"oh-my-opencode-darwin-x64-baseline": "4.16.2",
|
|
89714
|
+
"oh-my-opencode-linux-arm64": "4.16.2",
|
|
89715
|
+
"oh-my-opencode-linux-arm64-musl": "4.16.2",
|
|
89716
|
+
"oh-my-opencode-linux-x64": "4.16.2",
|
|
89717
|
+
"oh-my-opencode-linux-x64-baseline": "4.16.2",
|
|
89718
|
+
"oh-my-opencode-linux-x64-musl": "4.16.2",
|
|
89719
|
+
"oh-my-opencode-linux-x64-musl-baseline": "4.16.2",
|
|
89720
|
+
"oh-my-opencode-windows-arm64": "4.16.2",
|
|
89721
|
+
"oh-my-opencode-windows-x64": "4.16.2",
|
|
89722
|
+
"oh-my-opencode-windows-x64-baseline": "4.16.2"
|
|
88939
89723
|
},
|
|
88940
89724
|
overrides: {
|
|
88941
89725
|
"@earendil-works/pi-agent-core": "0.80.3",
|
|
@@ -89116,13 +89900,13 @@ function syncCachePackageJsonToIntent(pluginInfo) {
|
|
|
89116
89900
|
};
|
|
89117
89901
|
}
|
|
89118
89902
|
// packages/omo-opencode/src/hooks/auto-update-checker/hook/background-update-check.ts
|
|
89119
|
-
import { existsSync as
|
|
89120
|
-
import { dirname as
|
|
89903
|
+
import { existsSync as existsSync70 } from "fs";
|
|
89904
|
+
import { dirname as dirname23, join as join79 } from "path";
|
|
89121
89905
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
89122
89906
|
// packages/omo-opencode/src/shared/migrate-legacy-config-file.ts
|
|
89123
89907
|
init_logger2();
|
|
89124
|
-
import { existsSync as
|
|
89125
|
-
import { join as
|
|
89908
|
+
import { existsSync as existsSync67, readFileSync as readFileSync44, renameSync as renameSync6, rmSync as rmSync3 } from "fs";
|
|
89909
|
+
import { join as join76, dirname as dirname22, basename as basename13 } from "path";
|
|
89126
89910
|
|
|
89127
89911
|
// packages/omo-opencode/src/shared/migration/migrations-sidecar.ts
|
|
89128
89912
|
init_migrations_sidecar();
|
|
@@ -89130,9 +89914,9 @@ init_migrations_sidecar();
|
|
|
89130
89914
|
// packages/omo-opencode/src/shared/migrate-legacy-config-file.ts
|
|
89131
89915
|
init_plugin_identity();
|
|
89132
89916
|
function buildCanonicalPath(legacyPath) {
|
|
89133
|
-
const dir =
|
|
89134
|
-
const ext =
|
|
89135
|
-
return
|
|
89917
|
+
const dir = dirname22(legacyPath);
|
|
89918
|
+
const ext = basename13(legacyPath).includes(".jsonc") ? ".jsonc" : ".json";
|
|
89919
|
+
return join76(dir, `${CONFIG_BASENAME}${ext}`);
|
|
89136
89920
|
}
|
|
89137
89921
|
function archiveLegacyConfigFile(legacyPath) {
|
|
89138
89922
|
const backupPath = `${legacyPath}.bak`;
|
|
@@ -89165,13 +89949,13 @@ function archiveLegacyConfigFile(legacyPath) {
|
|
|
89165
89949
|
}
|
|
89166
89950
|
function migrateLegacySidecarFile(legacyPath, canonicalPath) {
|
|
89167
89951
|
const legacySidecarPath = getSidecarPath(legacyPath);
|
|
89168
|
-
if (!
|
|
89952
|
+
if (!existsSync67(legacySidecarPath))
|
|
89169
89953
|
return true;
|
|
89170
89954
|
const canonicalSidecarPath = getSidecarPath(canonicalPath);
|
|
89171
|
-
if (
|
|
89955
|
+
if (existsSync67(canonicalSidecarPath))
|
|
89172
89956
|
return true;
|
|
89173
89957
|
try {
|
|
89174
|
-
const content =
|
|
89958
|
+
const content = readFileSync44(legacySidecarPath, "utf-8");
|
|
89175
89959
|
writeFileAtomically2(canonicalSidecarPath, content);
|
|
89176
89960
|
log2("[migrateLegacyConfigFile] Migrated legacy migration sidecar to canonical path", {
|
|
89177
89961
|
from: legacySidecarPath,
|
|
@@ -89188,15 +89972,15 @@ function migrateLegacySidecarFile(legacyPath, canonicalPath) {
|
|
|
89188
89972
|
}
|
|
89189
89973
|
}
|
|
89190
89974
|
function migrateLegacyConfigFile(legacyPath) {
|
|
89191
|
-
if (!
|
|
89975
|
+
if (!existsSync67(legacyPath))
|
|
89192
89976
|
return false;
|
|
89193
|
-
if (!
|
|
89977
|
+
if (!basename13(legacyPath).startsWith(LEGACY_CONFIG_BASENAME))
|
|
89194
89978
|
return false;
|
|
89195
89979
|
const canonicalPath = buildCanonicalPath(legacyPath);
|
|
89196
|
-
if (
|
|
89980
|
+
if (existsSync67(canonicalPath))
|
|
89197
89981
|
return false;
|
|
89198
89982
|
try {
|
|
89199
|
-
const content =
|
|
89983
|
+
const content = readFileSync44(legacyPath, "utf-8");
|
|
89200
89984
|
writeFileAtomically2(canonicalPath, content);
|
|
89201
89985
|
const migratedSidecar = migrateLegacySidecarFile(legacyPath, canonicalPath);
|
|
89202
89986
|
const archivedLegacyConfig = archiveLegacyConfigFile(legacyPath);
|
|
@@ -89232,8 +90016,8 @@ function toReadableStream2(stream) {
|
|
|
89232
90016
|
function wrapNodeProcess2(proc) {
|
|
89233
90017
|
let resolveExited;
|
|
89234
90018
|
let exitCode = null;
|
|
89235
|
-
const exited = new Promise((
|
|
89236
|
-
resolveExited =
|
|
90019
|
+
const exited = new Promise((resolve19, reject) => {
|
|
90020
|
+
resolveExited = resolve19;
|
|
89237
90021
|
proc.on("error", (error) => {
|
|
89238
90022
|
if (exitCode === null) {
|
|
89239
90023
|
exitCode = 1;
|
|
@@ -89285,13 +90069,13 @@ function spawnWithWindowsHide(command, options) {
|
|
|
89285
90069
|
return wrapNodeProcess2(proc);
|
|
89286
90070
|
}
|
|
89287
90071
|
// packages/omo-opencode/src/cli/config-manager/bun-install.ts
|
|
89288
|
-
import { existsSync as
|
|
89289
|
-
import { join as
|
|
90072
|
+
import { existsSync as existsSync68 } from "fs";
|
|
90073
|
+
import { join as join77 } from "path";
|
|
89290
90074
|
init_logger2();
|
|
89291
90075
|
var BUN_INSTALL_TIMEOUT_SECONDS = 60;
|
|
89292
90076
|
var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
|
|
89293
90077
|
function getDefaultWorkspaceDir() {
|
|
89294
|
-
return
|
|
90078
|
+
return join77(getOpenCodeCacheDir(), "packages");
|
|
89295
90079
|
}
|
|
89296
90080
|
function readProcessOutput(stream) {
|
|
89297
90081
|
if (!stream) {
|
|
@@ -89317,7 +90101,7 @@ async function runBunInstallWithDetails(options) {
|
|
|
89317
90101
|
const outputMode = options?.outputMode ?? "pipe";
|
|
89318
90102
|
const cacheDir = options?.workspaceDir ?? getDefaultWorkspaceDir();
|
|
89319
90103
|
const packageJsonPath = `${cacheDir}/package.json`;
|
|
89320
|
-
if (!
|
|
90104
|
+
if (!existsSync68(packageJsonPath)) {
|
|
89321
90105
|
return {
|
|
89322
90106
|
success: false,
|
|
89323
90107
|
error: `Workspace not initialized: ${packageJsonPath} not found. OpenCode should create this on first run.`
|
|
@@ -89332,8 +90116,8 @@ async function runBunInstallWithDetails(options) {
|
|
|
89332
90116
|
});
|
|
89333
90117
|
const outputPromise = Promise.all([readProcessOutput(proc.stdout), readProcessOutput(proc.stderr)]).then(([stdout, stderr]) => ({ stdout, stderr }));
|
|
89334
90118
|
let timeoutId;
|
|
89335
|
-
const timeoutPromise = new Promise((
|
|
89336
|
-
timeoutId = setTimeout(() =>
|
|
90119
|
+
const timeoutPromise = new Promise((resolve19) => {
|
|
90120
|
+
timeoutId = setTimeout(() => resolve19("timeout"), BUN_INSTALL_TIMEOUT_MS);
|
|
89337
90121
|
});
|
|
89338
90122
|
const exitPromise = proc.exited.then(() => "completed");
|
|
89339
90123
|
const result = await Promise.race([exitPromise, timeoutPromise]);
|
|
@@ -89534,15 +90318,15 @@ Restart OpenCode to apply.`,
|
|
|
89534
90318
|
// packages/omo-opencode/src/hooks/auto-update-checker/hook/background-update-check.ts
|
|
89535
90319
|
function defaultGetModuleHostingWorkspace() {
|
|
89536
90320
|
try {
|
|
89537
|
-
const currentDir =
|
|
90321
|
+
const currentDir = dirname23(fileURLToPath4(import.meta.url));
|
|
89538
90322
|
const pkgJsonPath = findPackageJsonUp(currentDir);
|
|
89539
90323
|
if (!pkgJsonPath)
|
|
89540
90324
|
return null;
|
|
89541
|
-
const pkgDir =
|
|
89542
|
-
const nodeModulesDir =
|
|
90325
|
+
const pkgDir = dirname23(pkgJsonPath);
|
|
90326
|
+
const nodeModulesDir = dirname23(pkgDir);
|
|
89543
90327
|
if (nodeModulesDir.split(/[\\/]/).pop() !== "node_modules")
|
|
89544
90328
|
return null;
|
|
89545
|
-
return
|
|
90329
|
+
return dirname23(nodeModulesDir);
|
|
89546
90330
|
} catch (error) {
|
|
89547
90331
|
if (error instanceof Error) {
|
|
89548
90332
|
return null;
|
|
@@ -89554,8 +90338,8 @@ function getCacheWorkspaceDir(deps) {
|
|
|
89554
90338
|
return deps.join(deps.getOpenCodeCacheDir(), "packages");
|
|
89555
90339
|
}
|
|
89556
90340
|
var defaultDeps2 = {
|
|
89557
|
-
existsSync:
|
|
89558
|
-
join:
|
|
90341
|
+
existsSync: existsSync70,
|
|
90342
|
+
join: join79,
|
|
89559
90343
|
runBunInstallWithDetails,
|
|
89560
90344
|
log: log2,
|
|
89561
90345
|
getOpenCodeCacheDir,
|
|
@@ -89827,7 +90611,7 @@ async function showSpinnerToast(ctx, version, message) {
|
|
|
89827
90611
|
duration: frameInterval + 50
|
|
89828
90612
|
}
|
|
89829
90613
|
}).catch(ignoreToastError);
|
|
89830
|
-
await new Promise((
|
|
90614
|
+
await new Promise((resolve19) => setTimeout(resolve19, frameInterval));
|
|
89831
90615
|
}
|
|
89832
90616
|
}
|
|
89833
90617
|
|
|
@@ -89926,9 +90710,9 @@ v${latestVersion} available. Restart OpenCode to apply.` : "OpenCode is now on S
|
|
|
89926
90710
|
}
|
|
89927
90711
|
// packages/omo-opencode/src/hooks/codegraph-bootstrap/hook.ts
|
|
89928
90712
|
init_src();
|
|
89929
|
-
import { existsSync as
|
|
90713
|
+
import { existsSync as existsSync71 } from "fs";
|
|
89930
90714
|
import { homedir as homedir21 } from "os";
|
|
89931
|
-
import { join as
|
|
90715
|
+
import { join as join80 } from "path";
|
|
89932
90716
|
|
|
89933
90717
|
// packages/omo-opencode/src/hooks/codegraph-bootstrap/command-runner.ts
|
|
89934
90718
|
init_src();
|
|
@@ -89950,7 +90734,7 @@ function resolveExitCode(error) {
|
|
|
89950
90734
|
async function runCodegraphCommand(projectRoot, command, args, options) {
|
|
89951
90735
|
const { execFile: execFile3 } = await import("child_process");
|
|
89952
90736
|
const invocation = resolveCodegraphCommandInvocation(command, args);
|
|
89953
|
-
return new Promise((
|
|
90737
|
+
return new Promise((resolve19) => {
|
|
89954
90738
|
execFile3(invocation.command, [...invocation.args], {
|
|
89955
90739
|
cwd: projectRoot,
|
|
89956
90740
|
encoding: "utf8",
|
|
@@ -89960,10 +90744,10 @@ async function runCodegraphCommand(projectRoot, command, args, options) {
|
|
|
89960
90744
|
windowsHide: true
|
|
89961
90745
|
}, (error, stdout, stderr) => {
|
|
89962
90746
|
if (error === null) {
|
|
89963
|
-
|
|
90747
|
+
resolve19({ exitCode: 0, stderr: toOutputText(stderr), stdout: toOutputText(stdout), timedOut: false });
|
|
89964
90748
|
return;
|
|
89965
90749
|
}
|
|
89966
|
-
|
|
90750
|
+
resolve19({
|
|
89967
90751
|
exitCode: resolveExitCode(error),
|
|
89968
90752
|
stderr: toOutputText(stderr),
|
|
89969
90753
|
stdout: toOutputText(stdout),
|
|
@@ -89985,7 +90769,7 @@ function resolveCodegraphCommandInvocation(command, args, platform2 = process.pl
|
|
|
89985
90769
|
|
|
89986
90770
|
// packages/omo-opencode/src/hooks/codegraph-bootstrap/project-root.ts
|
|
89987
90771
|
init_src();
|
|
89988
|
-
import { resolve as
|
|
90772
|
+
import { resolve as resolve19 } from "path";
|
|
89989
90773
|
var PROJECT_ROOT_KEYS = ["directory", "worktree", "cwd", "projectRoot", "projectPath"];
|
|
89990
90774
|
function readStringField(record, key) {
|
|
89991
90775
|
const value = record[key];
|
|
@@ -90018,8 +90802,8 @@ function readRecordRoot(record) {
|
|
|
90018
90802
|
}
|
|
90019
90803
|
function resolveCodegraphProjectRoot(properties, fallbackDirectory) {
|
|
90020
90804
|
if (!isRecord(properties))
|
|
90021
|
-
return
|
|
90022
|
-
return
|
|
90805
|
+
return resolve19(fallbackDirectory);
|
|
90806
|
+
return resolve19(readRecordRoot(properties) ?? fallbackDirectory);
|
|
90023
90807
|
}
|
|
90024
90808
|
|
|
90025
90809
|
// packages/omo-opencode/src/hooks/codegraph-bootstrap/status.ts
|
|
@@ -90081,14 +90865,14 @@ function defaultSchedule(task) {
|
|
|
90081
90865
|
timer.unref?.();
|
|
90082
90866
|
}
|
|
90083
90867
|
function defaultInstallDir2() {
|
|
90084
|
-
return
|
|
90868
|
+
return join80(homedir21(), ".omo", "codegraph");
|
|
90085
90869
|
}
|
|
90086
90870
|
function provisionedBinFromInstallDir(installDir) {
|
|
90087
90871
|
if (installDir === undefined)
|
|
90088
90872
|
return null;
|
|
90089
90873
|
const binaryName = process.platform === "win32" ? "codegraph.cmd" : "codegraph";
|
|
90090
|
-
const candidate =
|
|
90091
|
-
return
|
|
90874
|
+
const candidate = join80(installDir, "bin", binaryName);
|
|
90875
|
+
return existsSync71(candidate) ? candidate : null;
|
|
90092
90876
|
}
|
|
90093
90877
|
function codegraphEnv(deps, config) {
|
|
90094
90878
|
const env2 = deps.buildEnv();
|
|
@@ -90117,7 +90901,7 @@ async function resolveOrProvisionCommand(deps, config) {
|
|
|
90117
90901
|
const installDir = config.install_dir ?? defaultInstallDir2();
|
|
90118
90902
|
const provisioned = await deps.ensureProvisioned({
|
|
90119
90903
|
installDir,
|
|
90120
|
-
lockDir:
|
|
90904
|
+
lockDir: join80(installDir, "locks"),
|
|
90121
90905
|
version: CODEGRAPH_VERSION
|
|
90122
90906
|
});
|
|
90123
90907
|
if (!provisioned.provisioned || provisioned.binPath === undefined) {
|
|
@@ -90132,8 +90916,8 @@ async function resolveOrProvisionCommand(deps, config) {
|
|
|
90132
90916
|
async function runBootstrap(projectRoot, config, deps) {
|
|
90133
90917
|
try {
|
|
90134
90918
|
const autoInit = config.auto_init !== false;
|
|
90135
|
-
const codegraphPath =
|
|
90136
|
-
if (!autoInit && !
|
|
90919
|
+
const codegraphPath = join80(projectRoot, ".codegraph");
|
|
90920
|
+
if (!autoInit && !existsSync71(codegraphPath)) {
|
|
90137
90921
|
deps.log("[codegraph-bootstrap] CodeGraph auto_init disabled and .codegraph not present; skipping bootstrap", {
|
|
90138
90922
|
projectRoot
|
|
90139
90923
|
});
|
|
@@ -90221,7 +91005,7 @@ function createCodegraphBootstrapHook(ctx, config, depsOverride = {}) {
|
|
|
90221
91005
|
// packages/omo-opencode/src/hooks/ast-grep-sg-provision/hook.ts
|
|
90222
91006
|
init_src();
|
|
90223
91007
|
import { homedir as homedir22 } from "os";
|
|
90224
|
-
import { join as
|
|
91008
|
+
import { join as join81 } from "path";
|
|
90225
91009
|
var provisionedTargets = new Set;
|
|
90226
91010
|
function defaultSchedule2(task) {
|
|
90227
91011
|
const timer = setTimeout(() => {
|
|
@@ -90248,7 +91032,7 @@ var defaultDeps5 = {
|
|
|
90248
91032
|
function createAstGrepSgProvisionHook(depsOverride = {}) {
|
|
90249
91033
|
const deps = { ...defaultDeps5, ...depsOverride };
|
|
90250
91034
|
const platform2 = deps.platform ?? process.platform;
|
|
90251
|
-
const targetDir = astGrepRuntimeDir(
|
|
91035
|
+
const targetDir = astGrepRuntimeDir(join81(deps.homeDir(), ".omo"), platform2, deps.arch ?? process.arch);
|
|
90252
91036
|
return {
|
|
90253
91037
|
event(input) {
|
|
90254
91038
|
try {
|
|
@@ -90269,17 +91053,17 @@ function createAstGrepSgProvisionHook(depsOverride = {}) {
|
|
|
90269
91053
|
}
|
|
90270
91054
|
// packages/omo-opencode/src/hooks/agent-usage-reminder/storage.ts
|
|
90271
91055
|
import {
|
|
90272
|
-
existsSync as
|
|
90273
|
-
mkdirSync as
|
|
90274
|
-
readFileSync as
|
|
90275
|
-
writeFileSync as
|
|
90276
|
-
unlinkSync as
|
|
91056
|
+
existsSync as existsSync72,
|
|
91057
|
+
mkdirSync as mkdirSync16,
|
|
91058
|
+
readFileSync as readFileSync46,
|
|
91059
|
+
writeFileSync as writeFileSync16,
|
|
91060
|
+
unlinkSync as unlinkSync11
|
|
90277
91061
|
} from "fs";
|
|
90278
|
-
import { join as
|
|
91062
|
+
import { join as join83 } from "path";
|
|
90279
91063
|
|
|
90280
91064
|
// packages/omo-opencode/src/hooks/agent-usage-reminder/constants.ts
|
|
90281
|
-
import { join as
|
|
90282
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
91065
|
+
import { join as join82 } from "path";
|
|
91066
|
+
var AGENT_USAGE_REMINDER_STORAGE = join82(OPENCODE_STORAGE, "agent-usage-reminder");
|
|
90283
91067
|
var TARGET_TOOLS = new Set([
|
|
90284
91068
|
"grep",
|
|
90285
91069
|
"safe_grep",
|
|
@@ -90325,14 +91109,14 @@ ALWAYS prefer: Multiple parallel task calls > Direct tool calls
|
|
|
90325
91109
|
|
|
90326
91110
|
// packages/omo-opencode/src/hooks/agent-usage-reminder/storage.ts
|
|
90327
91111
|
function getStoragePath2(sessionID) {
|
|
90328
|
-
return
|
|
91112
|
+
return join83(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
90329
91113
|
}
|
|
90330
91114
|
function loadAgentUsageState(sessionID) {
|
|
90331
91115
|
const filePath = getStoragePath2(sessionID);
|
|
90332
|
-
if (!
|
|
91116
|
+
if (!existsSync72(filePath))
|
|
90333
91117
|
return null;
|
|
90334
91118
|
try {
|
|
90335
|
-
const content =
|
|
91119
|
+
const content = readFileSync46(filePath, "utf-8");
|
|
90336
91120
|
return JSON.parse(content);
|
|
90337
91121
|
} catch (error) {
|
|
90338
91122
|
error instanceof Error;
|
|
@@ -90340,16 +91124,16 @@ function loadAgentUsageState(sessionID) {
|
|
|
90340
91124
|
}
|
|
90341
91125
|
}
|
|
90342
91126
|
function saveAgentUsageState(state3) {
|
|
90343
|
-
if (!
|
|
90344
|
-
|
|
91127
|
+
if (!existsSync72(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
91128
|
+
mkdirSync16(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
90345
91129
|
}
|
|
90346
91130
|
const filePath = getStoragePath2(state3.sessionID);
|
|
90347
|
-
|
|
91131
|
+
writeFileSync16(filePath, JSON.stringify(state3, null, 2));
|
|
90348
91132
|
}
|
|
90349
91133
|
function clearAgentUsageState(sessionID) {
|
|
90350
91134
|
const filePath = getStoragePath2(sessionID);
|
|
90351
|
-
if (
|
|
90352
|
-
|
|
91135
|
+
if (existsSync72(filePath)) {
|
|
91136
|
+
unlinkSync11(filePath);
|
|
90353
91137
|
}
|
|
90354
91138
|
}
|
|
90355
91139
|
|
|
@@ -93963,7 +94747,7 @@ Tests are the FLOOR (always required). Surface artifact is the CEILING (also req
|
|
|
93963
94747
|
| Changes build output | Run the build. Verify the output files exist and are correct. |
|
|
93964
94748
|
| Modifies API behavior | Call the endpoint. Show the response. |
|
|
93965
94749
|
| Changes UI rendering | Use Chrome to drive the REAL page; if Chrome is not available, download and use agent-browser (https://github.com/vercel-labs/agent-browser). Capture screenshot + action log. |
|
|
93966
|
-
| Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or \`tmux capture-pane\`
|
|
94750
|
+
| Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or the xterm.js web terminal render (TUI; NEVER \`tmux capture-pane\` - it degrades color and CJK width), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
|
|
93967
94751
|
| Changes a desktop/GUI (non-page) surface | Computer use: OS-level GUI automation against the running app. Capture action log + screenshot. |
|
|
93968
94752
|
| Adds a new tool/hook/feature | Test it end-to-end in a real scenario. |
|
|
93969
94753
|
| Modifies config handling | Load the config. Verify it parses correctly. |
|
|
@@ -94325,7 +95109,7 @@ Trigger if user said "\uC5C4\uBC00"/"strictly"/"rigorously"/"properly review", o
|
|
|
94325
95109
|
| Changes build output | Run the build. Verify output files exist and are correct. |
|
|
94326
95110
|
| Modifies API behavior | Call the endpoint. Show the response. |
|
|
94327
95111
|
| Renders/changes a page | Use Chrome to drive the REAL page; if Chrome is not available, download and use agent-browser (https://github.com/vercel-labs/agent-browser). Capture screenshot + action log. |
|
|
94328
|
-
| Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or \`tmux capture-pane\`
|
|
95112
|
+
| Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or the xterm.js web terminal render (TUI; NEVER \`tmux capture-pane\` - it degrades color and CJK width), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
|
|
94329
95113
|
| Drives a desktop/GUI (non-page) surface | Computer use: OS-level GUI automation against the running app. Capture action log + screenshot. |
|
|
94330
95114
|
| Adds a new tool/hook/feature | Test it end-to-end in a real scenario. |
|
|
94331
95115
|
| Modifies config handling | Load the config. Verify it parses correctly. |
|
|
@@ -94545,7 +95329,7 @@ Tests are necessary and insufficient. Exercise the real surface.
|
|
|
94545
95329
|
| CLI | Run the command and show stdout/stderr. |
|
|
94546
95330
|
| API | Call the endpoint and show status/body. |
|
|
94547
95331
|
| UI | Drive the page in a browser and capture a screenshot or trace. |
|
|
94548
|
-
| TUI |
|
|
95332
|
+
| TUI | Render through the xterm.js web terminal and screenshot it (NEVER \`tmux capture-pane\`); verify color, layout, and CJK width. |
|
|
94549
95333
|
| Config | Load the config and verify the parsed shape. |
|
|
94550
95334
|
| Prompt or mode | Verify the prompt loads or the registry resolves it. |
|
|
94551
95335
|
| Build output | Run build and verify exit code 0. |
|
|
@@ -94740,7 +95524,7 @@ lsp_diagnostics catches type errors only. Logic bugs, missing behavior, broken f
|
|
|
94740
95524
|
| Changes build output | Run build. Verify output files. |
|
|
94741
95525
|
| Modifies API behavior | Call the endpoint. Show response. |
|
|
94742
95526
|
| Renders/changes a page | Use Chrome to drive the page; if Chrome is not available, download and use agent-browser (https://github.com/vercel-labs/agent-browser). Screenshot + action log. |
|
|
94743
|
-
| Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or \`tmux capture-pane\`
|
|
95527
|
+
| Changes UI rendering or a TUI/terminal layout (incl. CJK/Korean/Japanese/Chinese text) | Load the visual-qa skill: capture reference + actual screenshots (web) or the xterm.js web terminal render (TUI; NEVER \`tmux capture-pane\` - it degrades color and CJK width), run its bundled pixel-diff / column-width script, and get the dual read-only verdict (design-system + functional integrity, and visual fidelity + CJK precision). Record the diff/score artifact. |
|
|
94744
95528
|
| Drives a desktop GUI | Computer use: OS-level GUI automation against the running app. Action log + screenshot. |
|
|
94745
95529
|
| Adds tool/hook/feature | Test end-to-end in a real scenario. |
|
|
94746
95530
|
| Modifies config handling | Load config. Verify parsed shape. |
|
|
@@ -95299,17 +96083,17 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
95299
96083
|
}
|
|
95300
96084
|
// packages/omo-opencode/src/hooks/interactive-bash-session/storage.ts
|
|
95301
96085
|
import {
|
|
95302
|
-
existsSync as
|
|
95303
|
-
mkdirSync as
|
|
95304
|
-
readFileSync as
|
|
95305
|
-
writeFileSync as
|
|
95306
|
-
unlinkSync as
|
|
96086
|
+
existsSync as existsSync73,
|
|
96087
|
+
mkdirSync as mkdirSync17,
|
|
96088
|
+
readFileSync as readFileSync47,
|
|
96089
|
+
writeFileSync as writeFileSync17,
|
|
96090
|
+
unlinkSync as unlinkSync12
|
|
95307
96091
|
} from "fs";
|
|
95308
|
-
import { join as
|
|
96092
|
+
import { join as join85 } from "path";
|
|
95309
96093
|
|
|
95310
96094
|
// packages/omo-opencode/src/hooks/interactive-bash-session/constants.ts
|
|
95311
|
-
import { join as
|
|
95312
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
96095
|
+
import { join as join84 } from "path";
|
|
96096
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join84(OPENCODE_STORAGE, "interactive-bash-session");
|
|
95313
96097
|
var OMO_SESSION_PREFIX = "omo-";
|
|
95314
96098
|
function buildSessionReminderMessage(sessions) {
|
|
95315
96099
|
if (sessions.length === 0)
|
|
@@ -95321,14 +96105,14 @@ function buildSessionReminderMessage(sessions) {
|
|
|
95321
96105
|
|
|
95322
96106
|
// packages/omo-opencode/src/hooks/interactive-bash-session/storage.ts
|
|
95323
96107
|
function getStoragePath3(sessionID) {
|
|
95324
|
-
return
|
|
96108
|
+
return join85(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
95325
96109
|
}
|
|
95326
96110
|
function loadInteractiveBashSessionState(sessionID) {
|
|
95327
96111
|
const filePath = getStoragePath3(sessionID);
|
|
95328
|
-
if (!
|
|
96112
|
+
if (!existsSync73(filePath))
|
|
95329
96113
|
return null;
|
|
95330
96114
|
try {
|
|
95331
|
-
const content =
|
|
96115
|
+
const content = readFileSync47(filePath, "utf-8");
|
|
95332
96116
|
const serialized = JSON.parse(content);
|
|
95333
96117
|
return {
|
|
95334
96118
|
sessionID: serialized.sessionID,
|
|
@@ -95343,8 +96127,8 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
95343
96127
|
}
|
|
95344
96128
|
}
|
|
95345
96129
|
function saveInteractiveBashSessionState(state3) {
|
|
95346
|
-
if (!
|
|
95347
|
-
|
|
96130
|
+
if (!existsSync73(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
96131
|
+
mkdirSync17(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
95348
96132
|
}
|
|
95349
96133
|
const filePath = getStoragePath3(state3.sessionID);
|
|
95350
96134
|
const serialized = {
|
|
@@ -95352,12 +96136,12 @@ function saveInteractiveBashSessionState(state3) {
|
|
|
95352
96136
|
tmuxSessions: Array.from(state3.tmuxSessions),
|
|
95353
96137
|
updatedAt: state3.updatedAt
|
|
95354
96138
|
};
|
|
95355
|
-
|
|
96139
|
+
writeFileSync17(filePath, JSON.stringify(serialized, null, 2));
|
|
95356
96140
|
}
|
|
95357
96141
|
function clearInteractiveBashSessionState(sessionID) {
|
|
95358
96142
|
const filePath = getStoragePath3(sessionID);
|
|
95359
|
-
if (
|
|
95360
|
-
|
|
96143
|
+
if (existsSync73(filePath)) {
|
|
96144
|
+
unlinkSync12(filePath);
|
|
95361
96145
|
}
|
|
95362
96146
|
}
|
|
95363
96147
|
|
|
@@ -96415,7 +97199,7 @@ async function withTimeout(promise, timeoutMs) {
|
|
|
96415
97199
|
var USER_MESSAGE_IN_PROGRESS_WINDOW_MS = 2000;
|
|
96416
97200
|
var RAPID_IDLE_DEDUP_MS = 500;
|
|
96417
97201
|
function sleep2(ms) {
|
|
96418
|
-
return ms > 0 ? new Promise((
|
|
97202
|
+
return ms > 0 ? new Promise((resolve20) => setTimeout(resolve20, ms)) : Promise.resolve();
|
|
96419
97203
|
}
|
|
96420
97204
|
function hasActiveBackgroundTasks(backgroundManager, sessionID) {
|
|
96421
97205
|
return backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "pending" || task.status === "running") : false;
|
|
@@ -97299,7 +98083,7 @@ async function handleDetectedCompletion(ctx, input) {
|
|
|
97299
98083
|
init_logger2();
|
|
97300
98084
|
init_constants3();
|
|
97301
98085
|
init_constants3();
|
|
97302
|
-
import { existsSync as
|
|
98086
|
+
import { existsSync as existsSync75, readFileSync as readFileSync49 } from "fs";
|
|
97303
98087
|
function extractTranscriptEntryText(entry) {
|
|
97304
98088
|
if (typeof entry.content === "string")
|
|
97305
98089
|
return entry.content;
|
|
@@ -97337,9 +98121,9 @@ function detectCompletionInTranscript(transcriptPath, promise, startedAt) {
|
|
|
97337
98121
|
if (!transcriptPath)
|
|
97338
98122
|
return false;
|
|
97339
98123
|
try {
|
|
97340
|
-
if (!
|
|
98124
|
+
if (!existsSync75(transcriptPath))
|
|
97341
98125
|
return false;
|
|
97342
|
-
const content =
|
|
98126
|
+
const content = readFileSync49(transcriptPath, "utf-8");
|
|
97343
98127
|
const pattern = buildPromisePattern(promise);
|
|
97344
98128
|
const lines = content.split(`
|
|
97345
98129
|
`).filter((line) => line.trim());
|
|
@@ -98066,7 +98850,9 @@ function createRalphLoopEventHandlerImpl(ctx, options) {
|
|
|
98066
98850
|
const runtimeRetryActivitySessionID = getRuntimeRetryActivitySessionID(event.type, props);
|
|
98067
98851
|
if (runtimeRetryActivitySessionID) {
|
|
98068
98852
|
runtime5.runtimeErrorRetriedSessions.delete(runtimeRetryActivitySessionID);
|
|
98069
|
-
releasePromptAsyncReservation(runtimeRetryActivitySessionID, "ralph-loop"
|
|
98853
|
+
releasePromptAsyncReservation(runtimeRetryActivitySessionID, "ralph-loop", {
|
|
98854
|
+
supersedeTransientRetryOwners: true
|
|
98855
|
+
});
|
|
98070
98856
|
runtime5.recentHandledSyntheticIdleAt.delete(runtimeRetryActivitySessionID);
|
|
98071
98857
|
}
|
|
98072
98858
|
if (event.type === "session.idle") {
|
|
@@ -98436,13 +99222,13 @@ function findSlashCommandPartIndex(parts) {
|
|
|
98436
99222
|
return -1;
|
|
98437
99223
|
}
|
|
98438
99224
|
// packages/omo-opencode/src/hooks/auto-slash-command/executor.ts
|
|
98439
|
-
import { dirname as
|
|
99225
|
+
import { dirname as dirname31 } from "path";
|
|
98440
99226
|
|
|
98441
99227
|
// packages/omo-opencode/src/shared/command-executor/resolve-commands-in-text.ts
|
|
98442
99228
|
init_resolve_commands_in_text();
|
|
98443
99229
|
// packages/skills-loader-core/src/features/opencode-skill-loader/loader.ts
|
|
98444
99230
|
init_src();
|
|
98445
|
-
import { join as
|
|
99231
|
+
import { join as join93 } from "path";
|
|
98446
99232
|
|
|
98447
99233
|
// packages/shared-skills/index.mjs
|
|
98448
99234
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
@@ -98452,21 +99238,21 @@ function sharedSkillsRootPath() {
|
|
|
98452
99238
|
|
|
98453
99239
|
// packages/skills-loader-core/src/shared/claude-config-dir.ts
|
|
98454
99240
|
init_src();
|
|
98455
|
-
import { join as
|
|
99241
|
+
import { join as join87 } from "path";
|
|
98456
99242
|
function getClaudeConfigDir2() {
|
|
98457
99243
|
const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
|
|
98458
99244
|
if (envConfigDir) {
|
|
98459
99245
|
return envConfigDir;
|
|
98460
99246
|
}
|
|
98461
|
-
return
|
|
99247
|
+
return join87(getHomeDirectory(), ".claude");
|
|
98462
99248
|
}
|
|
98463
99249
|
// packages/skills-loader-core/src/shared/opencode-command-dirs.ts
|
|
98464
|
-
import { basename as
|
|
99250
|
+
import { basename as basename14, dirname as dirname26, join as join89 } from "path";
|
|
98465
99251
|
|
|
98466
99252
|
// packages/skills-loader-core/src/shared/opencode-config-dir.ts
|
|
98467
|
-
import { existsSync as
|
|
99253
|
+
import { existsSync as existsSync76, realpathSync as realpathSync12 } from "fs";
|
|
98468
99254
|
import { homedir as homedir24 } from "os";
|
|
98469
|
-
import { join as
|
|
99255
|
+
import { join as join88, posix as posix4, resolve as resolve20, win32 as win325 } from "path";
|
|
98470
99256
|
|
|
98471
99257
|
// packages/skills-loader-core/src/shared/plugin-identity.ts
|
|
98472
99258
|
init_src();
|
|
@@ -98499,15 +99285,15 @@ function getTauriConfigDir2(identifier) {
|
|
|
98499
99285
|
const platform2 = process.platform;
|
|
98500
99286
|
switch (platform2) {
|
|
98501
99287
|
case "darwin":
|
|
98502
|
-
return
|
|
99288
|
+
return join88(homedir24(), "Library", "Application Support", identifier);
|
|
98503
99289
|
case "win32": {
|
|
98504
|
-
const appData = process.env.APPDATA ||
|
|
99290
|
+
const appData = process.env.APPDATA || join88(homedir24(), "AppData", "Roaming");
|
|
98505
99291
|
return win325.join(appData, identifier);
|
|
98506
99292
|
}
|
|
98507
99293
|
case "linux":
|
|
98508
99294
|
default: {
|
|
98509
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME ||
|
|
98510
|
-
return
|
|
99295
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join88(homedir24(), ".config");
|
|
99296
|
+
return join88(xdgConfig, identifier);
|
|
98511
99297
|
}
|
|
98512
99298
|
}
|
|
98513
99299
|
}
|
|
@@ -98515,8 +99301,8 @@ function resolveConfigPath2(pathValue) {
|
|
|
98515
99301
|
if (isWslEnvironment2() && pathValue.startsWith("/")) {
|
|
98516
99302
|
return posix4.normalize(pathValue);
|
|
98517
99303
|
}
|
|
98518
|
-
const resolvedPath =
|
|
98519
|
-
if (!
|
|
99304
|
+
const resolvedPath = resolve20(pathValue);
|
|
99305
|
+
if (!existsSync76(resolvedPath))
|
|
98520
99306
|
return resolvedPath;
|
|
98521
99307
|
try {
|
|
98522
99308
|
return realpathSync12(resolvedPath);
|
|
@@ -98550,8 +99336,8 @@ function getWslLinuxHomeDir2(windowsConfigRoot) {
|
|
|
98550
99336
|
function getCliDefaultConfigDir2() {
|
|
98551
99337
|
const envXdgConfig = process.env.XDG_CONFIG_HOME?.trim();
|
|
98552
99338
|
const shouldIgnoreWindowsXdg = envXdgConfig !== undefined && envXdgConfig.length > 0 && isWslEnvironment2() && isWindowsUserConfigRoot2(envXdgConfig);
|
|
98553
|
-
const xdgConfig = shouldIgnoreWindowsXdg ? posix4.join(getWslLinuxHomeDir2(envXdgConfig) ?? "/home", ".config") : envXdgConfig ||
|
|
98554
|
-
const configDir = isWslEnvironment2() ? posix4.join(xdgConfig, "opencode") :
|
|
99339
|
+
const xdgConfig = shouldIgnoreWindowsXdg ? posix4.join(getWslLinuxHomeDir2(envXdgConfig) ?? "/home", ".config") : envXdgConfig || join88(homedir24(), ".config");
|
|
99340
|
+
const configDir = isWslEnvironment2() ? posix4.join(xdgConfig, "opencode") : join88(xdgConfig, "opencode");
|
|
98555
99341
|
return resolveConfigPath2(configDir);
|
|
98556
99342
|
}
|
|
98557
99343
|
function getCliCustomConfigDir2() {
|
|
@@ -98584,9 +99370,9 @@ function getOpenCodeConfigDir2(options) {
|
|
|
98584
99370
|
const tauriDir = process.platform === "win32" ? win325.isAbsolute(tauriDirBase) ? win325.normalize(tauriDirBase) : win325.resolve(tauriDirBase) : resolveConfigPath2(tauriDirBase);
|
|
98585
99371
|
if (checkExisting) {
|
|
98586
99372
|
const legacyDir = getCliConfigDir2();
|
|
98587
|
-
const legacyConfig =
|
|
98588
|
-
const legacyConfigC =
|
|
98589
|
-
if (
|
|
99373
|
+
const legacyConfig = join88(legacyDir, "opencode.json");
|
|
99374
|
+
const legacyConfigC = join88(legacyDir, "opencode.jsonc");
|
|
99375
|
+
if (existsSync76(legacyConfig) || existsSync76(legacyConfigC)) {
|
|
98590
99376
|
return legacyDir;
|
|
98591
99377
|
}
|
|
98592
99378
|
}
|
|
@@ -98595,11 +99381,11 @@ function getOpenCodeConfigDir2(options) {
|
|
|
98595
99381
|
|
|
98596
99382
|
// packages/skills-loader-core/src/shared/opencode-command-dirs.ts
|
|
98597
99383
|
function getParentOpencodeConfigDir2(configDir) {
|
|
98598
|
-
const parentDir =
|
|
98599
|
-
if (
|
|
99384
|
+
const parentDir = dirname26(configDir);
|
|
99385
|
+
if (basename14(parentDir) !== "profiles") {
|
|
98600
99386
|
return null;
|
|
98601
99387
|
}
|
|
98602
|
-
return
|
|
99388
|
+
return dirname26(parentDir);
|
|
98603
99389
|
}
|
|
98604
99390
|
function getOpenCodeSkillDirs(options) {
|
|
98605
99391
|
const configDirs = getOpenCodeConfigDirs2(options);
|
|
@@ -98607,21 +99393,21 @@ function getOpenCodeSkillDirs(options) {
|
|
|
98607
99393
|
...configDirs.flatMap((configDir) => {
|
|
98608
99394
|
const parentConfigDir = getParentOpencodeConfigDir2(configDir);
|
|
98609
99395
|
return [
|
|
98610
|
-
|
|
98611
|
-
|
|
98612
|
-
...parentConfigDir ? [
|
|
99396
|
+
join89(configDir, "skills"),
|
|
99397
|
+
join89(configDir, "skill"),
|
|
99398
|
+
...parentConfigDir ? [join89(parentConfigDir, "skills"), join89(parentConfigDir, "skill")] : []
|
|
98613
99399
|
];
|
|
98614
99400
|
})
|
|
98615
99401
|
]));
|
|
98616
99402
|
}
|
|
98617
99403
|
// packages/skills-loader-core/src/shared/project-discovery-dirs.ts
|
|
98618
99404
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
98619
|
-
import { existsSync as
|
|
98620
|
-
import { dirname as
|
|
99405
|
+
import { existsSync as existsSync77, realpathSync as realpathSync13 } from "fs";
|
|
99406
|
+
import { dirname as dirname27, join as join90, resolve as resolve21, win32 as win326 } from "path";
|
|
98621
99407
|
var worktreePathCache2 = new Map;
|
|
98622
99408
|
function normalizePath4(path17) {
|
|
98623
|
-
const resolvedPath = process.platform !== "win32" && win326.isAbsolute(path17) ? path17 :
|
|
98624
|
-
if (!
|
|
99409
|
+
const resolvedPath = process.platform !== "win32" && win326.isAbsolute(path17) ? path17 : resolve21(path17);
|
|
99410
|
+
if (!existsSync77(resolvedPath)) {
|
|
98625
99411
|
return resolvedPath;
|
|
98626
99412
|
}
|
|
98627
99413
|
try {
|
|
@@ -98652,8 +99438,8 @@ function findAncestorDirectories2(startDirectory, targetPaths, stopDirectory) {
|
|
|
98652
99438
|
const stopDirectoryKey = resolvedStopDirectory ? pathKey2(resolvedStopDirectory) : undefined;
|
|
98653
99439
|
while (true) {
|
|
98654
99440
|
for (const targetPath of targetPaths) {
|
|
98655
|
-
const candidateDirectory =
|
|
98656
|
-
if (!
|
|
99441
|
+
const candidateDirectory = join90(currentDirectory, ...targetPath);
|
|
99442
|
+
if (!existsSync77(candidateDirectory)) {
|
|
98657
99443
|
continue;
|
|
98658
99444
|
}
|
|
98659
99445
|
const normalizedCandidateDirectory = normalizePath4(candidateDirectory);
|
|
@@ -98667,7 +99453,7 @@ function findAncestorDirectories2(startDirectory, targetPaths, stopDirectory) {
|
|
|
98667
99453
|
if (stopDirectoryKey === pathKey2(currentDirectory)) {
|
|
98668
99454
|
return directories;
|
|
98669
99455
|
}
|
|
98670
|
-
const parentDirectory =
|
|
99456
|
+
const parentDirectory = dirname27(currentDirectory);
|
|
98671
99457
|
if (parentDirectory === currentDirectory) {
|
|
98672
99458
|
return directories;
|
|
98673
99459
|
}
|
|
@@ -98675,7 +99461,7 @@ function findAncestorDirectories2(startDirectory, targetPaths, stopDirectory) {
|
|
|
98675
99461
|
}
|
|
98676
99462
|
}
|
|
98677
99463
|
function detectWorktreePath2(directory) {
|
|
98678
|
-
const resolvedDirectory =
|
|
99464
|
+
const resolvedDirectory = resolve21(directory);
|
|
98679
99465
|
const cacheKey = pathKey2(normalizePath4(resolvedDirectory));
|
|
98680
99466
|
if (worktreePathCache2.has(cacheKey)) {
|
|
98681
99467
|
return worktreePathCache2.get(cacheKey);
|
|
@@ -98711,7 +99497,7 @@ function findProjectOpencodeSkillDirs(startDirectory, stopDirectory) {
|
|
|
98711
99497
|
], stopDirectory ?? detectWorktreePath2(startDirectory));
|
|
98712
99498
|
}
|
|
98713
99499
|
// packages/skills-loader-core/src/shared/skill-path-resolver.ts
|
|
98714
|
-
import { isAbsolute as
|
|
99500
|
+
import { isAbsolute as isAbsolute13, posix as posix5, relative as relative12, resolve as resolve22, win32 as win327 } from "path";
|
|
98715
99501
|
function toDisplayPath2(path17) {
|
|
98716
99502
|
return path17.replaceAll("\\", "/");
|
|
98717
99503
|
}
|
|
@@ -98750,9 +99536,9 @@ function resolveSkillPathReferences2(content, basePath) {
|
|
|
98750
99536
|
}
|
|
98751
99537
|
return relativePath.endsWith("/") && !resolvedPath2.endsWith("/") ? `${resolvedPath2}/` : resolvedPath2;
|
|
98752
99538
|
}
|
|
98753
|
-
const resolvedPath =
|
|
98754
|
-
const relativePathFromBase =
|
|
98755
|
-
if (relativePathFromBase.startsWith("..") ||
|
|
99539
|
+
const resolvedPath = resolve22(normalizedBase, relativePath);
|
|
99540
|
+
const relativePathFromBase = relative12(normalizedBase, resolvedPath);
|
|
99541
|
+
if (relativePathFromBase.startsWith("..") || isAbsolute13(relativePathFromBase)) {
|
|
98756
99542
|
return match;
|
|
98757
99543
|
}
|
|
98758
99544
|
const displayPath = toDisplayPath2(resolvedPath);
|
|
@@ -98875,12 +99661,12 @@ function deduplicateSkillsByName(skills) {
|
|
|
98875
99661
|
// packages/skills-loader-core/src/features/opencode-skill-loader/skill-directory-loader.ts
|
|
98876
99662
|
init_src();
|
|
98877
99663
|
import * as fs19 from "fs/promises";
|
|
98878
|
-
import { join as
|
|
99664
|
+
import { join as join92 } from "path";
|
|
98879
99665
|
|
|
98880
99666
|
// packages/skills-loader-core/src/features/opencode-skill-loader/loaded-skill-from-path.ts
|
|
98881
99667
|
init_src();
|
|
98882
99668
|
import * as fs18 from "fs/promises";
|
|
98883
|
-
import { basename as
|
|
99669
|
+
import { basename as basename15 } from "path";
|
|
98884
99670
|
|
|
98885
99671
|
// packages/skills-loader-core/src/features/opencode-skill-loader/allowed-tools-parser.ts
|
|
98886
99672
|
function parseAllowedTools(allowedTools) {
|
|
@@ -98895,7 +99681,7 @@ function parseAllowedTools(allowedTools) {
|
|
|
98895
99681
|
// packages/skills-loader-core/src/features/opencode-skill-loader/skill-mcp-config.ts
|
|
98896
99682
|
init_js_yaml();
|
|
98897
99683
|
import * as fs17 from "fs/promises";
|
|
98898
|
-
import { join as
|
|
99684
|
+
import { join as join91 } from "path";
|
|
98899
99685
|
function parseSkillMcpConfigFromFrontmatter(content) {
|
|
98900
99686
|
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
98901
99687
|
if (!frontmatterMatch)
|
|
@@ -98914,7 +99700,7 @@ function parseSkillMcpConfigFromFrontmatter(content) {
|
|
|
98914
99700
|
return;
|
|
98915
99701
|
}
|
|
98916
99702
|
async function loadMcpJsonFromDir(skillDir) {
|
|
98917
|
-
const mcpJsonPath =
|
|
99703
|
+
const mcpJsonPath = join91(skillDir, "mcp.json");
|
|
98918
99704
|
try {
|
|
98919
99705
|
const content = await fs17.readFile(mcpJsonPath, "utf-8");
|
|
98920
99706
|
const parsed = JSON.parse(content);
|
|
@@ -98996,7 +99782,7 @@ $ARGUMENTS
|
|
|
98996
99782
|
}
|
|
98997
99783
|
}
|
|
98998
99784
|
function inferSkillNameFromFileName(filePath) {
|
|
98999
|
-
return
|
|
99785
|
+
return basename15(filePath, ".md");
|
|
99000
99786
|
}
|
|
99001
99787
|
|
|
99002
99788
|
// packages/skills-loader-core/src/features/opencode-skill-loader/skill-directory-loader.ts
|
|
@@ -99030,10 +99816,10 @@ async function loadSkillsFromDir(options) {
|
|
|
99030
99816
|
const directories = entries.filter((entry) => !entry.name.startsWith(".") && (entry.isDirectory() || entry.isSymbolicLink()));
|
|
99031
99817
|
const files = entries.filter((entry) => !entry.name.startsWith(".") && !entry.isDirectory() && !entry.isSymbolicLink() && isMarkdownFile(entry));
|
|
99032
99818
|
for (const entry of directories) {
|
|
99033
|
-
const entryPath =
|
|
99819
|
+
const entryPath = join92(options.skillsDir, entry.name);
|
|
99034
99820
|
const resolvedPath = await resolveSymlinkAsync(entryPath);
|
|
99035
99821
|
const dirName = entry.name;
|
|
99036
|
-
const skillMdPath =
|
|
99822
|
+
const skillMdPath = join92(resolvedPath, "SKILL.md");
|
|
99037
99823
|
if (await canAccessFile(skillMdPath)) {
|
|
99038
99824
|
const skill = await loadSkillFromPath({
|
|
99039
99825
|
skillPath: skillMdPath,
|
|
@@ -99047,7 +99833,7 @@ async function loadSkillsFromDir(options) {
|
|
|
99047
99833
|
}
|
|
99048
99834
|
continue;
|
|
99049
99835
|
}
|
|
99050
|
-
const namedSkillMdPath =
|
|
99836
|
+
const namedSkillMdPath = join92(resolvedPath, `${dirName}.md`);
|
|
99051
99837
|
if (await canAccessFile(namedSkillMdPath)) {
|
|
99052
99838
|
const skill = await loadSkillFromPath({
|
|
99053
99839
|
skillPath: namedSkillMdPath,
|
|
@@ -99078,7 +99864,7 @@ async function loadSkillsFromDir(options) {
|
|
|
99078
99864
|
}
|
|
99079
99865
|
}
|
|
99080
99866
|
for (const entry of files) {
|
|
99081
|
-
const entryPath =
|
|
99867
|
+
const entryPath = join92(options.skillsDir, entry.name);
|
|
99082
99868
|
const baseName = inferSkillNameFromFileName(entryPath);
|
|
99083
99869
|
const skill = await loadSkillFromPath({
|
|
99084
99870
|
skillPath: entryPath,
|
|
@@ -99096,7 +99882,7 @@ async function loadSkillsFromDir(options) {
|
|
|
99096
99882
|
|
|
99097
99883
|
// packages/skills-loader-core/src/features/opencode-skill-loader/loader.ts
|
|
99098
99884
|
async function loadUserSkills() {
|
|
99099
|
-
const userSkillsDir =
|
|
99885
|
+
const userSkillsDir = join93(getClaudeConfigDir2(), "skills");
|
|
99100
99886
|
const skills = await loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
|
|
99101
99887
|
return skillsToCommandDefinitionRecord(skills);
|
|
99102
99888
|
}
|
|
@@ -99121,7 +99907,7 @@ async function loadProjectAgentsSkills(directory) {
|
|
|
99121
99907
|
return skillsToCommandDefinitionRecord(deduplicateSkillsByName(allSkills.flat()));
|
|
99122
99908
|
}
|
|
99123
99909
|
async function loadGlobalAgentsSkills(homeDirectory = getHomeDirectory()) {
|
|
99124
|
-
const agentsGlobalDir =
|
|
99910
|
+
const agentsGlobalDir = join93(homeDirectory, ".agents", "skills");
|
|
99125
99911
|
const skills = await loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
|
|
99126
99912
|
return skillsToCommandDefinitionRecord(skills);
|
|
99127
99913
|
}
|
|
@@ -99192,7 +99978,7 @@ async function discoverSkills(options = {}) {
|
|
|
99192
99978
|
]);
|
|
99193
99979
|
}
|
|
99194
99980
|
async function discoverUserClaudeSkills() {
|
|
99195
|
-
const userSkillsDir =
|
|
99981
|
+
const userSkillsDir = join93(getClaudeConfigDir2(), "skills");
|
|
99196
99982
|
return loadSkillsFromDir({ skillsDir: userSkillsDir, scope: "user" });
|
|
99197
99983
|
}
|
|
99198
99984
|
async function discoverProjectClaudeSkills(directory) {
|
|
@@ -99219,17 +100005,17 @@ async function discoverProjectAgentsSkills(directory) {
|
|
|
99219
100005
|
return deduplicateSkillsByName(allSkills.flat());
|
|
99220
100006
|
}
|
|
99221
100007
|
async function discoverGlobalAgentsSkills(homeDirectory = getHomeDirectory()) {
|
|
99222
|
-
const agentsGlobalDir =
|
|
100008
|
+
const agentsGlobalDir = join93(homeDirectory, ".agents", "skills");
|
|
99223
100009
|
return loadSkillsFromDir({ skillsDir: agentsGlobalDir, scope: "user" });
|
|
99224
100010
|
}
|
|
99225
100011
|
// packages/skills-loader-core/src/features/opencode-skill-loader/merger/builtin-skill-converter.ts
|
|
99226
|
-
import { existsSync as
|
|
99227
|
-
import { join as
|
|
100012
|
+
import { existsSync as existsSync78 } from "fs";
|
|
100013
|
+
import { join as join94 } from "path";
|
|
99228
100014
|
function resolveBuiltinSkillPath(builtin) {
|
|
99229
100015
|
if (builtin.resolvedPath !== undefined)
|
|
99230
100016
|
return builtin.resolvedPath;
|
|
99231
|
-
const sharedSkillPath =
|
|
99232
|
-
return
|
|
100017
|
+
const sharedSkillPath = join94(sharedSkillsRootPath(), builtin.name);
|
|
100018
|
+
return existsSync78(sharedSkillPath) ? sharedSkillPath : undefined;
|
|
99233
100019
|
}
|
|
99234
100020
|
function builtinToLoadedSkill(builtin) {
|
|
99235
100021
|
const definition = {
|
|
@@ -99256,8 +100042,8 @@ function builtinToLoadedSkill(builtin) {
|
|
|
99256
100042
|
|
|
99257
100043
|
// packages/skills-loader-core/src/features/opencode-skill-loader/merger/config-skill-entry-loader.ts
|
|
99258
100044
|
init_src();
|
|
99259
|
-
import { existsSync as
|
|
99260
|
-
import { dirname as
|
|
100045
|
+
import { existsSync as existsSync79, readFileSync as readFileSync50 } from "fs";
|
|
100046
|
+
import { dirname as dirname28, isAbsolute as isAbsolute14, resolve as resolve23 } from "path";
|
|
99261
100047
|
import { homedir as homedir25 } from "os";
|
|
99262
100048
|
function resolveFilePath5(from, configDir) {
|
|
99263
100049
|
let filePath = from;
|
|
@@ -99265,19 +100051,19 @@ function resolveFilePath5(from, configDir) {
|
|
|
99265
100051
|
filePath = filePath.slice(6, -1);
|
|
99266
100052
|
}
|
|
99267
100053
|
if (filePath.startsWith("~/")) {
|
|
99268
|
-
return
|
|
100054
|
+
return resolve23(homedir25(), filePath.slice(2));
|
|
99269
100055
|
}
|
|
99270
|
-
if (
|
|
100056
|
+
if (isAbsolute14(filePath)) {
|
|
99271
100057
|
return filePath;
|
|
99272
100058
|
}
|
|
99273
100059
|
const baseDir = configDir || process.cwd();
|
|
99274
|
-
return
|
|
100060
|
+
return resolve23(baseDir, filePath);
|
|
99275
100061
|
}
|
|
99276
100062
|
function loadSkillFromFile(filePath) {
|
|
99277
100063
|
try {
|
|
99278
|
-
if (!
|
|
100064
|
+
if (!existsSync79(filePath))
|
|
99279
100065
|
return null;
|
|
99280
|
-
const content =
|
|
100066
|
+
const content = readFileSync50(filePath, "utf-8");
|
|
99281
100067
|
const { data, body } = parseFrontmatter(content);
|
|
99282
100068
|
return { template: body, metadata: data };
|
|
99283
100069
|
} catch (error) {
|
|
@@ -99313,7 +100099,7 @@ function configEntryToLoadedSkill(name, entry, configDir) {
|
|
|
99313
100099
|
return null;
|
|
99314
100100
|
}
|
|
99315
100101
|
const description = entry.description || fileMetadata.description || "";
|
|
99316
|
-
const resolvedPath = sourcePath ?
|
|
100102
|
+
const resolvedPath = sourcePath ? dirname28(sourcePath) : configDir || process.cwd();
|
|
99317
100103
|
const resolvedTemplate = resolveSkillPathReferences2(template.trim(), resolvedPath);
|
|
99318
100104
|
const wrappedTemplate = `<skill-instruction>
|
|
99319
100105
|
Base directory for this skill: ${resolvedPath}/
|
|
@@ -100143,17 +100929,17 @@ playwright-cli close
|
|
|
100143
100929
|
allowedTools: ["Bash(playwright-cli:*)"]
|
|
100144
100930
|
};
|
|
100145
100931
|
// packages/skills-loader-core/src/features/builtin-skills/skill-file-loader.ts
|
|
100146
|
-
import { readFileSync as
|
|
100147
|
-
import { join as
|
|
100932
|
+
import { readFileSync as readFileSync51 } from "fs";
|
|
100933
|
+
import { join as join95 } from "path";
|
|
100148
100934
|
init_src();
|
|
100149
|
-
function createSharedSkillTemplateLoader(readFile9 =
|
|
100935
|
+
function createSharedSkillTemplateLoader(readFile9 = readFileSync51, skillsRootPath = sharedSkillsRootPath()) {
|
|
100150
100936
|
const cache2 = new Map;
|
|
100151
100937
|
return (skillName) => {
|
|
100152
100938
|
const cached = cache2.get(skillName);
|
|
100153
100939
|
if (cached !== undefined)
|
|
100154
100940
|
return cached;
|
|
100155
100941
|
try {
|
|
100156
|
-
const { body } = parseFrontmatter(readFile9(
|
|
100942
|
+
const { body } = parseFrontmatter(readFile9(join95(skillsRootPath, skillName, "SKILL.md"), "utf8"));
|
|
100157
100943
|
cache2.set(skillName, body);
|
|
100158
100944
|
return body;
|
|
100159
100945
|
} catch (error) {
|
|
@@ -101290,9 +102076,9 @@ var gitMasterSkill = {
|
|
|
101290
102076
|
template: GIT_MASTER_TEMPLATE
|
|
101291
102077
|
};
|
|
101292
102078
|
// packages/skills-loader-core/src/features/builtin-skills/skills/dev-browser.ts
|
|
101293
|
-
import { dirname as
|
|
102079
|
+
import { dirname as dirname29, join as join96 } from "path";
|
|
101294
102080
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
101295
|
-
var CURRENT_DIR =
|
|
102081
|
+
var CURRENT_DIR = dirname29(fileURLToPath6(import.meta.url));
|
|
101296
102082
|
var devBrowserSkill = {
|
|
101297
102083
|
name: "dev-browser",
|
|
101298
102084
|
description: "Browser automation with persistent page state. Use when users ask to navigate websites, fill forms, take screenshots, extract web data, test web apps, or automate browser workflows. Trigger phrases include 'go to [url]', 'click on', 'fill out the form', 'take a screenshot', 'scrape', 'automate', 'test the website', 'log into', or any browser interaction request.",
|
|
@@ -101510,7 +102296,7 @@ console.log({
|
|
|
101510
102296
|
await client.disconnect();
|
|
101511
102297
|
EOF
|
|
101512
102298
|
\`\`\``,
|
|
101513
|
-
resolvedPath:
|
|
102299
|
+
resolvedPath: join96(CURRENT_DIR, "..", "dev-browser")
|
|
101514
102300
|
};
|
|
101515
102301
|
// packages/skills-loader-core/src/features/builtin-skills/skills/review-work.ts
|
|
101516
102302
|
var reviewWorkSkill = {
|
|
@@ -102187,13 +102973,13 @@ function mergeSkills(builtinSkills, config, configSourceSkills, userClaudeSkills
|
|
|
102187
102973
|
}
|
|
102188
102974
|
// packages/skills-loader-core/src/features/opencode-skill-loader/loaded-skill-template-extractor.ts
|
|
102189
102975
|
init_src();
|
|
102190
|
-
import { readFileSync as
|
|
102976
|
+
import { readFileSync as readFileSync52 } from "fs";
|
|
102191
102977
|
function extractSkillTemplate(skill) {
|
|
102192
102978
|
if (skill.scope === "config" && skill.definition.template) {
|
|
102193
102979
|
return skill.definition.template;
|
|
102194
102980
|
}
|
|
102195
102981
|
if (skill.path) {
|
|
102196
|
-
const content =
|
|
102982
|
+
const content = readFileSync52(skill.path, "utf-8");
|
|
102197
102983
|
const { body } = parseFrontmatter(content);
|
|
102198
102984
|
return body.trim();
|
|
102199
102985
|
}
|
|
@@ -102510,7 +103296,7 @@ init_src();
|
|
|
102510
103296
|
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
102511
103297
|
import * as fs20 from "fs/promises";
|
|
102512
103298
|
import { homedir as homedir26 } from "os";
|
|
102513
|
-
import { dirname as
|
|
103299
|
+
import { dirname as dirname30, extname as extname2, isAbsolute as isAbsolute15, join as join97, relative as relative13 } from "path";
|
|
102514
103300
|
var MAX_RECURSIVE_DEPTH = 10;
|
|
102515
103301
|
function isHttpUrl(path17) {
|
|
102516
103302
|
return path17.startsWith("http://") || path17.startsWith("https://");
|
|
@@ -102520,12 +103306,12 @@ function toAbsolutePath(path17, configDir) {
|
|
|
102520
103306
|
return homedir26();
|
|
102521
103307
|
}
|
|
102522
103308
|
if (path17.startsWith("~/")) {
|
|
102523
|
-
return
|
|
103309
|
+
return join97(homedir26(), path17.slice(2));
|
|
102524
103310
|
}
|
|
102525
|
-
if (
|
|
103311
|
+
if (isAbsolute15(path17)) {
|
|
102526
103312
|
return path17;
|
|
102527
103313
|
}
|
|
102528
|
-
return
|
|
103314
|
+
return join97(configDir, path17);
|
|
102529
103315
|
}
|
|
102530
103316
|
function isMarkdownPath(path17) {
|
|
102531
103317
|
return extname2(path17).toLowerCase() === ".md";
|
|
@@ -102539,7 +103325,7 @@ function filterByGlob(skills, sourceBaseDir, globPattern) {
|
|
|
102539
103325
|
return skills.filter((skill) => {
|
|
102540
103326
|
if (!skill.path)
|
|
102541
103327
|
return false;
|
|
102542
|
-
const rel = normalizePathForGlob(
|
|
103328
|
+
const rel = normalizePathForGlob(relative13(sourceBaseDir, skill.path));
|
|
102543
103329
|
return import_picomatch2.default.isMatch(rel, globPattern, { dot: true, bash: true });
|
|
102544
103330
|
});
|
|
102545
103331
|
}
|
|
@@ -102565,13 +103351,13 @@ async function loadSourcePath(options) {
|
|
|
102565
103351
|
return [];
|
|
102566
103352
|
const loaded = await loadSkillFromPath({
|
|
102567
103353
|
skillPath: realSourcePath,
|
|
102568
|
-
resolvedPath:
|
|
103354
|
+
resolvedPath: dirname30(realSourcePath),
|
|
102569
103355
|
defaultName: inferSkillNameFromFileName(realSourcePath),
|
|
102570
103356
|
scope: "config"
|
|
102571
103357
|
});
|
|
102572
103358
|
if (!loaded)
|
|
102573
103359
|
return [];
|
|
102574
|
-
return filterByGlob([loaded],
|
|
103360
|
+
return filterByGlob([loaded], dirname30(realSourcePath), options.globPattern);
|
|
102575
103361
|
}
|
|
102576
103362
|
if (!stat5.isDirectory())
|
|
102577
103363
|
return [];
|
|
@@ -102652,8 +103438,8 @@ function readOpencodeConfigSkills(directory) {
|
|
|
102652
103438
|
return { paths, urls };
|
|
102653
103439
|
}
|
|
102654
103440
|
// packages/omo-opencode/src/tools/slashcommand/command-discovery.ts
|
|
102655
|
-
import { existsSync as
|
|
102656
|
-
import { basename as
|
|
103441
|
+
import { existsSync as existsSync81, readdirSync as readdirSync20, readFileSync as readFileSync54, statSync as statSync11 } from "fs";
|
|
103442
|
+
import { basename as basename16, join as join99 } from "path";
|
|
102657
103443
|
|
|
102658
103444
|
// packages/omo-opencode/src/tools/slashcommand/command-discovery-deps.ts
|
|
102659
103445
|
init_frontmatter2();
|
|
@@ -102893,13 +103679,15 @@ This command includes examples for the OpenCode harness. In Codex, do not call O
|
|
|
102893
103679
|
|
|
102894
103680
|
| OpenCode example | Codex tool to use |
|
|
102895
103681
|
| --- | --- |
|
|
102896
|
-
| \`call_omo_agent(subagent_type="explore", ...)\` | \`spawn_agent(
|
|
102897
|
-
| \`call_omo_agent(subagent_type="librarian", ...)\` | \`spawn_agent(
|
|
102898
|
-
| \`task(subagent_type="plan", ...)\` | \`spawn_agent(
|
|
103682
|
+
| \`call_omo_agent(subagent_type="explore", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. ...","agent_type":"explorer","fork_context":false})\` |
|
|
103683
|
+
| \`call_omo_agent(subagent_type="librarian", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a librarian. ...","agent_type":"librarian","fork_context":false})\` |
|
|
103684
|
+
| \`task(subagent_type="plan", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a planning agent. ...","agent_type":"plan","fork_context":false})\` |
|
|
102899
103685
|
| \`task(subagent_type="oracle", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})\` |
|
|
102900
|
-
| \`task(category="...", ...)\` | \`spawn_agent(
|
|
102901
|
-
| \`background_output(task_id="...")\` | \`wait_agent(...)\`
|
|
102902
|
-
| \`team_*(...)\` | Use Codex native subagents
|
|
103686
|
+
| \`task(category="...", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})\` |
|
|
103687
|
+
| \`background_output(task_id="...")\` | \`multi_agent_v1.wait_agent(...)\` for mailbox signals |
|
|
103688
|
+
| \`team_*(...)\` | Use Codex native subagents via \`multi_agent_v1.spawn_agent\`, \`multi_agent_v1.send_input\`, \`multi_agent_v1.wait_agent\`, and \`multi_agent_v1.close_agent\` |
|
|
103689
|
+
|
|
103690
|
+
Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If \`multi_agent_v1.*\` tools exist, use the table above as written. If instead a flat \`spawn_agent\` with a required \`task_name\` exists (\`multi_agent_v2\`), rewrite every \`multi_agent_v1.*\` example: \`multi_agent_v1.spawn_agent({...,"fork_context":false})\` becomes \`spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})\` (\`"all"\` only when full parent history is truly required); \`send_input\` becomes \`send_message\`; do not call \`close_agent\`/\`resume_agent\` (finished agents end on their own; \`followup_task\` re-tasks one, \`interrupt_agent\` stops one); \`wait_agent\` takes only \`timeout_ms\` and returns on any child mailbox activity. \`agent_type\` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
|
|
102903
103691
|
|
|
102904
103692
|
When translating \`load_skills=[...]\`, include the requested skill names in the spawned agent's \`message\`. If a code block below conflicts with this section, this section wins.
|
|
102905
103693
|
|
|
@@ -103523,7 +104311,7 @@ Append to the 6.6 summary a "Dispatch path" line and, when team path was used, t
|
|
|
103523
104311
|
var REFACTOR_TEMPLATE = REFACTOR_INTRO_AND_ANALYSIS + REFACTOR_CODEMAP_AND_TESTS + REFACTOR_PLAN_AND_EXECUTION + REFACTOR_VERIFICATION_AND_TOOLING;
|
|
103524
104312
|
|
|
103525
104313
|
// packages/omo-opencode/src/features/builtin-commands/templates/start-work.ts
|
|
103526
|
-
var START_WORK_TEMPLATE = `You are starting
|
|
104314
|
+
var START_WORK_TEMPLATE = `You are starting an Atlas work session.
|
|
103527
104315
|
|
|
103528
104316
|
## ARGUMENTS
|
|
103529
104317
|
|
|
@@ -103863,13 +104651,15 @@ This command includes examples for the OpenCode harness. In Codex, do not call O
|
|
|
103863
104651
|
|
|
103864
104652
|
| OpenCode example | Codex tool to use |
|
|
103865
104653
|
| --- | --- |
|
|
103866
|
-
| \`call_omo_agent(subagent_type="explore", ...)\` | \`spawn_agent(
|
|
103867
|
-
| \`call_omo_agent(subagent_type="librarian", ...)\` | \`spawn_agent(
|
|
103868
|
-
| \`task(subagent_type="plan", ...)\` | \`spawn_agent(
|
|
104654
|
+
| \`call_omo_agent(subagent_type="explore", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. ...","agent_type":"explorer","fork_context":false})\` |
|
|
104655
|
+
| \`call_omo_agent(subagent_type="librarian", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a librarian. ...","agent_type":"librarian","fork_context":false})\` |
|
|
104656
|
+
| \`task(subagent_type="plan", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a planning agent. ...","agent_type":"plan","fork_context":false})\` |
|
|
103869
104657
|
| \`task(subagent_type="oracle", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as a rigorous reviewer. ...","agent_type":"lazycodex-gate-reviewer","fork_context":false})\` |
|
|
103870
|
-
| \`task(category="...", ...)\` | \`spawn_agent(
|
|
103871
|
-
| \`background_output(task_id="...")\` | \`wait_agent(...)\`
|
|
103872
|
-
| \`team_*(...)\` | Use Codex native subagents
|
|
104658
|
+
| \`task(category="...", ...)\` | \`multi_agent_v1.spawn_agent({"message":"TASK: act as an implementation or QA worker. ...","fork_context":false})\` |
|
|
104659
|
+
| \`background_output(task_id="...")\` | \`multi_agent_v1.wait_agent(...)\` for mailbox signals |
|
|
104660
|
+
| \`team_*(...)\` | Use Codex native subagents via \`multi_agent_v1.spawn_agent\`, \`multi_agent_v1.send_input\`, \`multi_agent_v1.wait_agent\`, and \`multi_agent_v1.close_agent\` |
|
|
104661
|
+
|
|
104662
|
+
Codex exposes ONE of two subagent tool surfaces per session; check your own tool list and route accordingly. If \`multi_agent_v1.*\` tools exist, use the table above as written. If instead a flat \`spawn_agent\` with a required \`task_name\` exists (\`multi_agent_v2\`), rewrite every \`multi_agent_v1.*\` example: \`multi_agent_v1.spawn_agent({...,"fork_context":false})\` becomes \`spawn_agent({"task_name":"<lowercase_digits_underscores>","message":...,"agent_type":...,"fork_turns":"none"})\` (\`"all"\` only when full parent history is truly required); \`send_input\` becomes \`send_message\`; do not call \`close_agent\`/\`resume_agent\` (finished agents end on their own; \`followup_task\` re-tasks one, \`interrupt_agent\` stops one); \`wait_agent\` takes only \`timeout_ms\` and returns on any child mailbox activity. \`agent_type\` works the same on both surfaces. If a code block below conflicts with this section, this section wins.
|
|
103873
104663
|
|
|
103874
104664
|
When translating \`load_skills=[...]\`, include the requested skill names in the spawned agent's \`message\`. If a code block below conflicts with this section, this section wins.
|
|
103875
104665
|
|
|
@@ -104140,7 +104930,7 @@ ${refactorContent}
|
|
|
104140
104930
|
argumentHint: "<refactoring-target> [--scope=<file|module|project>] [--strategy=<safe|aggressive>]"
|
|
104141
104931
|
},
|
|
104142
104932
|
"start-work": {
|
|
104143
|
-
description: "(builtin) Start
|
|
104933
|
+
description: "(builtin) Start Atlas work session from Prometheus plan",
|
|
104144
104934
|
agent: resolveStartWorkAgent(options),
|
|
104145
104935
|
template: `<command-instruction>
|
|
104146
104936
|
${START_WORK_TEMPLATE}
|
|
@@ -104213,13 +105003,13 @@ function loadBuiltinCommands(disabledCommands, options) {
|
|
|
104213
105003
|
// packages/omo-opencode/src/tools/slashcommand/command-discovery.ts
|
|
104214
105004
|
var NESTED_COMMAND_SEPARATOR = "/";
|
|
104215
105005
|
function discoverCommandsFromDir(commandsDir, scope, prefix = "") {
|
|
104216
|
-
if (!
|
|
105006
|
+
if (!existsSync81(commandsDir))
|
|
104217
105007
|
return [];
|
|
104218
|
-
if (!
|
|
105008
|
+
if (!statSync11(commandsDir).isDirectory()) {
|
|
104219
105009
|
log2(`[command-discovery] Skipping non-directory path: ${commandsDir}`);
|
|
104220
105010
|
return [];
|
|
104221
105011
|
}
|
|
104222
|
-
const entries =
|
|
105012
|
+
const entries = readdirSync20(commandsDir, { withFileTypes: true });
|
|
104223
105013
|
const commands = [];
|
|
104224
105014
|
for (const entry of entries) {
|
|
104225
105015
|
if (entry.isDirectory()) {
|
|
@@ -104228,16 +105018,16 @@ function discoverCommandsFromDir(commandsDir, scope, prefix = "") {
|
|
|
104228
105018
|
if (entry.name.startsWith("."))
|
|
104229
105019
|
continue;
|
|
104230
105020
|
const nestedPrefix = prefix ? `${prefix}${NESTED_COMMAND_SEPARATOR}${entry.name}` : entry.name;
|
|
104231
|
-
commands.push(...discoverCommandsFromDir(
|
|
105021
|
+
commands.push(...discoverCommandsFromDir(join99(commandsDir, entry.name), scope, nestedPrefix));
|
|
104232
105022
|
continue;
|
|
104233
105023
|
}
|
|
104234
105024
|
if (!isMarkdownFile(entry))
|
|
104235
105025
|
continue;
|
|
104236
|
-
const commandPath =
|
|
104237
|
-
const baseCommandName =
|
|
105026
|
+
const commandPath = join99(commandsDir, entry.name);
|
|
105027
|
+
const baseCommandName = basename16(entry.name, ".md");
|
|
104238
105028
|
const commandName = prefix ? `${prefix}${NESTED_COMMAND_SEPARATOR}${baseCommandName}` : baseCommandName;
|
|
104239
105029
|
try {
|
|
104240
|
-
const content =
|
|
105030
|
+
const content = readFileSync54(commandPath, "utf-8");
|
|
104241
105031
|
const { data, body } = parseFrontmatter(content);
|
|
104242
105032
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
104243
105033
|
const metadata = {
|
|
@@ -104292,8 +105082,8 @@ function deduplicateCommandInfosByName(commands) {
|
|
|
104292
105082
|
return deduplicatedCommands;
|
|
104293
105083
|
}
|
|
104294
105084
|
function discoverCommandsSync(directory, options) {
|
|
104295
|
-
const userCommandsDir =
|
|
104296
|
-
const projectCommandsDir =
|
|
105085
|
+
const userCommandsDir = join99(getClaudeConfigDir(), "commands");
|
|
105086
|
+
const projectCommandsDir = join99(directory ?? process.cwd(), ".claude", "commands");
|
|
104297
105087
|
const opencodeGlobalDirs = getOpenCodeCommandDirs({ binary: "opencode" });
|
|
104298
105088
|
const opencodeProjectDirs = findProjectOpencodeCommandDirs(directory ?? process.cwd());
|
|
104299
105089
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
@@ -104397,7 +105187,7 @@ async function formatCommandTemplate(cmd, args) {
|
|
|
104397
105187
|
if (!content && cmd.lazyContentLoader) {
|
|
104398
105188
|
content = await cmd.lazyContentLoader.load();
|
|
104399
105189
|
}
|
|
104400
|
-
const commandDir = cmd.path ?
|
|
105190
|
+
const commandDir = cmd.path ? dirname31(cmd.path) : process.cwd();
|
|
104401
105191
|
const withFileRefs = await resolveFileReferencesInText(content, commandDir);
|
|
104402
105192
|
const resolvedContent = await resolveCommandsInText(withFileRefs);
|
|
104403
105193
|
const resolvedArguments = args;
|
|
@@ -104773,757 +105563,7 @@ If you skipped steps, STOP NOW. Go back and complete them.
|
|
|
104773
105563
|
`;
|
|
104774
105564
|
// packages/omo-opencode/src/hooks/prometheus-md-only/hook.ts
|
|
104775
105565
|
init_logger2();
|
|
104776
|
-
// packages/boulder-state/src/constants.ts
|
|
104777
|
-
var BOULDER_DIR = ".omo";
|
|
104778
|
-
var BOULDER_FILE = "boulder.json";
|
|
104779
|
-
var BOULDER_STATE_PATH = `${BOULDER_DIR}/${BOULDER_FILE}`;
|
|
104780
|
-
var NOTEPAD_DIR = "notepads";
|
|
104781
|
-
var NOTEPAD_BASE_PATH = `${BOULDER_DIR}/${NOTEPAD_DIR}`;
|
|
104782
|
-
var PROMETHEUS_PLANS_DIR = ".omo/plans";
|
|
104783
|
-
// packages/boulder-state/src/top-level-task.ts
|
|
104784
|
-
import { existsSync as existsSync77, readFileSync as readFileSync52 } from "fs";
|
|
104785
|
-
var TODO_HEADING_PATTERN = /^##\s+TODOs\b/i;
|
|
104786
|
-
var FINAL_VERIFICATION_HEADING_PATTERN = /^##\s+Final Verification Wave\b/i;
|
|
104787
|
-
var SECOND_LEVEL_HEADING_PATTERN = /^##\s+/;
|
|
104788
|
-
var UNCHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
|
|
104789
|
-
var TODO_TASK_PATTERN = /^(\d+)\.\s+(.+)$/;
|
|
104790
|
-
var FINAL_WAVE_TASK_PATTERN = /^(F\d+)\.\s+(.+)$/i;
|
|
104791
|
-
function buildTaskRef(section, taskLabel) {
|
|
104792
|
-
const pattern = section === "todo" ? TODO_TASK_PATTERN : FINAL_WAVE_TASK_PATTERN;
|
|
104793
|
-
const match = taskLabel.match(pattern);
|
|
104794
|
-
if (!match) {
|
|
104795
|
-
return null;
|
|
104796
|
-
}
|
|
104797
|
-
const rawLabel = match[1];
|
|
104798
|
-
const title = match[2].trim();
|
|
104799
|
-
return {
|
|
104800
|
-
key: `${section}:${rawLabel.toLowerCase()}`,
|
|
104801
|
-
section,
|
|
104802
|
-
label: rawLabel,
|
|
104803
|
-
title
|
|
104804
|
-
};
|
|
104805
|
-
}
|
|
104806
|
-
function readCurrentTopLevelTask(planPath) {
|
|
104807
|
-
if (!existsSync77(planPath)) {
|
|
104808
|
-
return null;
|
|
104809
|
-
}
|
|
104810
|
-
try {
|
|
104811
|
-
const content = readFileSync52(planPath, "utf-8");
|
|
104812
|
-
const lines = content.split(/\r?\n/);
|
|
104813
|
-
let section = "other";
|
|
104814
|
-
for (const line of lines) {
|
|
104815
|
-
if (SECOND_LEVEL_HEADING_PATTERN.test(line)) {
|
|
104816
|
-
section = TODO_HEADING_PATTERN.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN.test(line) ? "final-wave" : "other";
|
|
104817
|
-
}
|
|
104818
|
-
const uncheckedTaskMatch = line.match(UNCHECKED_CHECKBOX_PATTERN);
|
|
104819
|
-
if (!uncheckedTaskMatch || uncheckedTaskMatch[1].length > 0) {
|
|
104820
|
-
continue;
|
|
104821
|
-
}
|
|
104822
|
-
if (section !== "todo" && section !== "final-wave") {
|
|
104823
|
-
continue;
|
|
104824
|
-
}
|
|
104825
|
-
const taskRef = buildTaskRef(section, uncheckedTaskMatch[2].trim());
|
|
104826
|
-
if (taskRef) {
|
|
104827
|
-
return taskRef;
|
|
104828
|
-
}
|
|
104829
|
-
}
|
|
104830
|
-
return null;
|
|
104831
|
-
} catch {
|
|
104832
|
-
return null;
|
|
104833
|
-
}
|
|
104834
|
-
}
|
|
104835
|
-
// packages/boulder-state/src/storage/path.ts
|
|
104836
|
-
import { existsSync as existsSync78 } from "fs";
|
|
104837
|
-
import { isAbsolute as isAbsolute15, join as join97, relative as relative13, resolve as resolve23 } from "path";
|
|
104838
|
-
function getBoulderFilePath(directory) {
|
|
104839
|
-
return join97(directory, BOULDER_DIR, BOULDER_FILE);
|
|
104840
|
-
}
|
|
104841
|
-
function resolveTrackedPath(baseDirectory, trackedPath) {
|
|
104842
|
-
return isAbsolute15(trackedPath) ? resolve23(trackedPath) : resolve23(baseDirectory, trackedPath);
|
|
104843
|
-
}
|
|
104844
|
-
function resolveBoulderPlanPath(directory, state3) {
|
|
104845
|
-
const absolutePlanPath = resolveTrackedPath(directory, state3.active_plan);
|
|
104846
|
-
const worktreePath = state3.worktree_path?.trim();
|
|
104847
|
-
if (!worktreePath) {
|
|
104848
|
-
return absolutePlanPath;
|
|
104849
|
-
}
|
|
104850
|
-
const absoluteDirectory = resolve23(directory);
|
|
104851
|
-
const relativePlanPath = relative13(absoluteDirectory, absolutePlanPath);
|
|
104852
|
-
if (relativePlanPath.length === 0 || relativePlanPath.startsWith("..") || isAbsolute15(relativePlanPath)) {
|
|
104853
|
-
return absolutePlanPath;
|
|
104854
|
-
}
|
|
104855
|
-
const absoluteWorktreePath = resolveTrackedPath(directory, worktreePath);
|
|
104856
|
-
const worktreePlanPath = resolve23(absoluteWorktreePath, relativePlanPath);
|
|
104857
|
-
return existsSync78(worktreePlanPath) ? worktreePlanPath : absolutePlanPath;
|
|
104858
|
-
}
|
|
104859
|
-
function resolveBoulderPlanPathForWork(directory, work) {
|
|
104860
|
-
return resolveBoulderPlanPath(directory, work);
|
|
104861
|
-
}
|
|
104862
|
-
// packages/boulder-state/src/storage/plan-progress.ts
|
|
104863
|
-
import { existsSync as existsSync79, readFileSync as readFileSync53, readdirSync as readdirSync20, statSync as statSync11 } from "fs";
|
|
104864
|
-
import { basename as basename16, join as join98 } from "path";
|
|
104865
|
-
var TODO_HEADING_PATTERN2 = /^##\s+TODOs\b/i;
|
|
104866
|
-
var FINAL_VERIFICATION_HEADING_PATTERN2 = /^##\s+Final Verification Wave\b/i;
|
|
104867
|
-
var SECOND_LEVEL_HEADING_PATTERN2 = /^##\s+/;
|
|
104868
|
-
var UNCHECKED_CHECKBOX_PATTERN2 = /^(\s*)[-*]\s*\[\s*\]\s*(.+)$/;
|
|
104869
|
-
var CHECKED_CHECKBOX_PATTERN = /^(\s*)[-*]\s*\[[xX]\]\s*(.+)$/;
|
|
104870
|
-
var TODO_TASK_PATTERN2 = /^\d+\.\s+/;
|
|
104871
|
-
var FINAL_WAVE_TASK_PATTERN2 = /^F\d+\.\s+/i;
|
|
104872
|
-
var LEGACY_PROMETHEUS_PLANS_DIR = ".sisyphus/plans";
|
|
104873
|
-
var PROMETHEUS_PLAN_DIRS = [PROMETHEUS_PLANS_DIR, LEGACY_PROMETHEUS_PLANS_DIR];
|
|
104874
|
-
function findPrometheusPlans(directory) {
|
|
104875
|
-
try {
|
|
104876
|
-
return PROMETHEUS_PLAN_DIRS.flatMap((planDir) => {
|
|
104877
|
-
const plansDir = join98(directory, planDir);
|
|
104878
|
-
if (!existsSync79(plansDir)) {
|
|
104879
|
-
return [];
|
|
104880
|
-
}
|
|
104881
|
-
return readdirSync20(plansDir).filter((file2) => file2.endsWith(".md")).map((file2) => join98(plansDir, file2));
|
|
104882
|
-
}).sort((left, right) => statSync11(right).mtimeMs - statSync11(left).mtimeMs);
|
|
104883
|
-
} catch {
|
|
104884
|
-
return [];
|
|
104885
|
-
}
|
|
104886
|
-
}
|
|
104887
|
-
function getPlanName(planPath) {
|
|
104888
|
-
return basename16(planPath, ".md");
|
|
104889
|
-
}
|
|
104890
|
-
function getPlanProgress(planPath) {
|
|
104891
|
-
if (!existsSync79(planPath)) {
|
|
104892
|
-
return { total: 0, completed: 0, isComplete: false };
|
|
104893
|
-
}
|
|
104894
|
-
try {
|
|
104895
|
-
const content = readFileSync53(planPath, "utf-8");
|
|
104896
|
-
const lines = content.split(/\r?\n/);
|
|
104897
|
-
const hasStructuredSections = lines.some((line) => TODO_HEADING_PATTERN2.test(line) || FINAL_VERIFICATION_HEADING_PATTERN2.test(line));
|
|
104898
|
-
if (hasStructuredSections) {
|
|
104899
|
-
return getStructuredPlanProgress(lines);
|
|
104900
|
-
}
|
|
104901
|
-
return getSimplePlanProgress(content);
|
|
104902
|
-
} catch {
|
|
104903
|
-
return { total: 0, completed: 0, isComplete: false };
|
|
104904
|
-
}
|
|
104905
|
-
}
|
|
104906
|
-
function getStructuredPlanProgress(lines) {
|
|
104907
|
-
let section = "other";
|
|
104908
|
-
let total = 0;
|
|
104909
|
-
let completed = 0;
|
|
104910
|
-
for (const line of lines) {
|
|
104911
|
-
if (SECOND_LEVEL_HEADING_PATTERN2.test(line)) {
|
|
104912
|
-
section = TODO_HEADING_PATTERN2.test(line) ? "todo" : FINAL_VERIFICATION_HEADING_PATTERN2.test(line) ? "final-wave" : "other";
|
|
104913
|
-
continue;
|
|
104914
|
-
}
|
|
104915
|
-
if (section !== "todo" && section !== "final-wave") {
|
|
104916
|
-
continue;
|
|
104917
|
-
}
|
|
104918
|
-
const checkedMatch = line.match(CHECKED_CHECKBOX_PATTERN);
|
|
104919
|
-
const uncheckedMatch = checkedMatch ? null : line.match(UNCHECKED_CHECKBOX_PATTERN2);
|
|
104920
|
-
const match = checkedMatch ?? uncheckedMatch;
|
|
104921
|
-
if (!match || match[1].length > 0) {
|
|
104922
|
-
continue;
|
|
104923
|
-
}
|
|
104924
|
-
const taskBody = match[2].trim();
|
|
104925
|
-
const labelPattern = section === "todo" ? TODO_TASK_PATTERN2 : FINAL_WAVE_TASK_PATTERN2;
|
|
104926
|
-
if (!labelPattern.test(taskBody)) {
|
|
104927
|
-
continue;
|
|
104928
|
-
}
|
|
104929
|
-
total += 1;
|
|
104930
|
-
if (checkedMatch) {
|
|
104931
|
-
completed += 1;
|
|
104932
|
-
}
|
|
104933
|
-
}
|
|
104934
|
-
return { total, completed, isComplete: total > 0 && completed === total };
|
|
104935
|
-
}
|
|
104936
|
-
function getSimplePlanProgress(content) {
|
|
104937
|
-
const uncheckedMatches = content.match(/^[-*]\s*\[\s*\]/gm) ?? [];
|
|
104938
|
-
const checkedMatches = content.match(/^[-*]\s*\[[xX]\]/gm) ?? [];
|
|
104939
|
-
const total = uncheckedMatches.length + checkedMatches.length;
|
|
104940
|
-
const completed = checkedMatches.length;
|
|
104941
|
-
return { total, completed, isComplete: total > 0 && completed === total };
|
|
104942
|
-
}
|
|
104943
|
-
// packages/boulder-state/src/storage/shared.ts
|
|
104944
|
-
var RESERVED_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
104945
|
-
var SESSION_ID_PREFIX_PATTERN = /^(codex|opencode):/;
|
|
104946
|
-
function normalizeSessionId(sessionId, platform2 = "opencode") {
|
|
104947
|
-
if (SESSION_ID_PREFIX_PATTERN.test(sessionId)) {
|
|
104948
|
-
return sessionId;
|
|
104949
|
-
}
|
|
104950
|
-
return `${platform2}:${sessionId}`;
|
|
104951
|
-
}
|
|
104952
|
-
function nowIsoString() {
|
|
104953
|
-
return new Date().toISOString();
|
|
104954
|
-
}
|
|
104955
|
-
function parseIsoToMs(value) {
|
|
104956
|
-
if (!value) {
|
|
104957
|
-
return null;
|
|
104958
|
-
}
|
|
104959
|
-
const parsed = Date.parse(value);
|
|
104960
|
-
return Number.isNaN(parsed) ? null : parsed;
|
|
104961
|
-
}
|
|
104962
|
-
function getElapsedMs(startedAt, endedAt) {
|
|
104963
|
-
const startedMs = parseIsoToMs(startedAt);
|
|
104964
|
-
const endedMs = parseIsoToMs(endedAt);
|
|
104965
|
-
if (startedMs === null || endedMs === null) {
|
|
104966
|
-
return;
|
|
104967
|
-
}
|
|
104968
|
-
return endedMs - startedMs;
|
|
104969
|
-
}
|
|
104970
|
-
function isValidWorkStatus(status) {
|
|
104971
|
-
return status === "active" || status === "completed" || status === "paused" || status === "abandoned";
|
|
104972
|
-
}
|
|
104973
|
-
function buildWorkFromMirror(state3) {
|
|
104974
|
-
const planName = state3.plan_name ?? state3.active_plan;
|
|
104975
|
-
const workId = `${planName}-legacy`;
|
|
104976
|
-
return {
|
|
104977
|
-
work_id: workId,
|
|
104978
|
-
active_plan: state3.active_plan,
|
|
104979
|
-
plan_name: planName,
|
|
104980
|
-
status: state3.status,
|
|
104981
|
-
started_at: state3.started_at,
|
|
104982
|
-
ended_at: state3.ended_at,
|
|
104983
|
-
elapsed_ms: state3.elapsed_ms,
|
|
104984
|
-
updated_at: state3.updated_at,
|
|
104985
|
-
session_ids: Array.isArray(state3.session_ids) ? [...state3.session_ids] : [],
|
|
104986
|
-
session_origins: state3.session_origins,
|
|
104987
|
-
agent: state3.agent,
|
|
104988
|
-
worktree_path: state3.worktree_path,
|
|
104989
|
-
task_sessions: state3.task_sessions
|
|
104990
|
-
};
|
|
104991
|
-
}
|
|
104992
|
-
function projectWorkToMirror(state3, work) {
|
|
104993
|
-
state3.active_plan = work.active_plan;
|
|
104994
|
-
state3.plan_name = work.plan_name;
|
|
104995
|
-
state3.status = work.status;
|
|
104996
|
-
state3.started_at = work.started_at;
|
|
104997
|
-
state3.ended_at = work.ended_at;
|
|
104998
|
-
state3.elapsed_ms = work.elapsed_ms;
|
|
104999
|
-
state3.updated_at = work.updated_at;
|
|
105000
|
-
state3.session_ids = [...work.session_ids];
|
|
105001
|
-
state3.session_origins = work.session_origins ? { ...work.session_origins } : {};
|
|
105002
|
-
state3.agent = work.agent;
|
|
105003
|
-
state3.worktree_path = work.worktree_path;
|
|
105004
|
-
state3.task_sessions = work.task_sessions ? { ...work.task_sessions } : {};
|
|
105005
|
-
}
|
|
105006
|
-
function selectMirrorWork(state3) {
|
|
105007
|
-
const works = state3.works ? Object.values(state3.works) : [];
|
|
105008
|
-
if (works.length === 0) {
|
|
105009
|
-
return null;
|
|
105010
|
-
}
|
|
105011
|
-
if (state3.active_work_id) {
|
|
105012
|
-
const matched = works.find((work) => work.work_id === state3.active_work_id);
|
|
105013
|
-
if (matched) {
|
|
105014
|
-
return matched;
|
|
105015
|
-
}
|
|
105016
|
-
}
|
|
105017
|
-
const sorted = [...works].sort((left, right) => {
|
|
105018
|
-
const leftMs = parseIsoToMs(left.updated_at ?? left.started_at) ?? 0;
|
|
105019
|
-
const rightMs = parseIsoToMs(right.updated_at ?? right.started_at) ?? 0;
|
|
105020
|
-
return rightMs - leftMs;
|
|
105021
|
-
});
|
|
105022
|
-
return sorted[0] ?? null;
|
|
105023
|
-
}
|
|
105024
|
-
// packages/boulder-state/src/storage/read-state.ts
|
|
105025
|
-
import { existsSync as existsSync80, readFileSync as readFileSync54 } from "fs";
|
|
105026
|
-
function readBoulderState(directory) {
|
|
105027
|
-
const filePath = getBoulderFilePath(directory);
|
|
105028
|
-
if (!existsSync80(filePath)) {
|
|
105029
|
-
return null;
|
|
105030
|
-
}
|
|
105031
|
-
try {
|
|
105032
|
-
const content = readFileSync54(filePath, "utf-8");
|
|
105033
|
-
const parsed = JSON.parse(content);
|
|
105034
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
105035
|
-
return null;
|
|
105036
|
-
}
|
|
105037
|
-
normalizeState(parsed);
|
|
105038
|
-
const state3 = parsed;
|
|
105039
|
-
const mirrorWork = selectMirrorWork(state3);
|
|
105040
|
-
if (mirrorWork) {
|
|
105041
|
-
state3.active_work_id = mirrorWork.work_id;
|
|
105042
|
-
projectWorkToMirror(state3, mirrorWork);
|
|
105043
|
-
}
|
|
105044
|
-
return state3;
|
|
105045
|
-
} catch {
|
|
105046
|
-
return null;
|
|
105047
|
-
}
|
|
105048
|
-
}
|
|
105049
|
-
function normalizeState(state3) {
|
|
105050
|
-
normalizeSessionFields(state3);
|
|
105051
|
-
const sessionIds = Array.isArray(state3.session_ids) ? state3.session_ids : [];
|
|
105052
|
-
const sessionOrigins = state3.session_origins && typeof state3.session_origins === "object" && !Array.isArray(state3.session_origins) ? state3.session_origins : {};
|
|
105053
|
-
state3.session_origins = sessionOrigins;
|
|
105054
|
-
if (sessionIds.length === 1) {
|
|
105055
|
-
const soleSessionId = sessionIds[0];
|
|
105056
|
-
if (typeof soleSessionId === "string" && sessionOrigins[soleSessionId] !== "appended" && sessionOrigins[soleSessionId] !== "direct") {
|
|
105057
|
-
sessionOrigins[soleSessionId] = "direct";
|
|
105058
|
-
}
|
|
105059
|
-
}
|
|
105060
|
-
if (!state3.task_sessions || typeof state3.task_sessions !== "object" || Array.isArray(state3.task_sessions)) {
|
|
105061
|
-
state3.task_sessions = {};
|
|
105062
|
-
}
|
|
105063
|
-
normalizeWorkSessionFields(state3.works);
|
|
105064
|
-
}
|
|
105065
|
-
function normalizeSessionFields(target) {
|
|
105066
|
-
const sessionIds = Array.isArray(target.session_ids) ? target.session_ids.filter((sessionId) => typeof sessionId === "string").map((sessionId) => normalizeSessionId(sessionId)) : [];
|
|
105067
|
-
target.session_ids = sessionIds;
|
|
105068
|
-
const sessionOrigins = target.session_origins && typeof target.session_origins === "object" && !Array.isArray(target.session_origins) ? normalizeSessionOrigins(target.session_origins) : {};
|
|
105069
|
-
target.session_origins = sessionOrigins;
|
|
105070
|
-
}
|
|
105071
|
-
function normalizeSessionOrigins(sessionOrigins) {
|
|
105072
|
-
return Object.fromEntries(Object.entries(sessionOrigins).map(([sessionId, origin]) => [normalizeSessionId(sessionId), origin]));
|
|
105073
|
-
}
|
|
105074
|
-
function normalizeWorkSessionFields(works) {
|
|
105075
|
-
if (!works || typeof works !== "object" || Array.isArray(works)) {
|
|
105076
|
-
return;
|
|
105077
|
-
}
|
|
105078
|
-
for (const work of Object.values(works)) {
|
|
105079
|
-
if (work && typeof work === "object" && !Array.isArray(work)) {
|
|
105080
|
-
normalizeSessionFields(work);
|
|
105081
|
-
}
|
|
105082
|
-
}
|
|
105083
|
-
}
|
|
105084
|
-
function getBoulderWorks(state3) {
|
|
105085
|
-
if (state3.works && typeof state3.works === "object") {
|
|
105086
|
-
return Object.values(state3.works).filter((work) => work != null);
|
|
105087
|
-
}
|
|
105088
|
-
if (!state3.active_plan || !state3.plan_name || !state3.started_at) {
|
|
105089
|
-
return [];
|
|
105090
|
-
}
|
|
105091
|
-
return [buildWorkFromMirror(state3)];
|
|
105092
|
-
}
|
|
105093
|
-
function getActiveWorks(directory) {
|
|
105094
|
-
const state3 = readBoulderState(directory);
|
|
105095
|
-
if (!state3) {
|
|
105096
|
-
return [];
|
|
105097
|
-
}
|
|
105098
|
-
return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned");
|
|
105099
|
-
}
|
|
105100
|
-
function getWorkById(directory, workId) {
|
|
105101
|
-
const state3 = readBoulderState(directory);
|
|
105102
|
-
if (!state3) {
|
|
105103
|
-
return null;
|
|
105104
|
-
}
|
|
105105
|
-
return getBoulderWorks(state3).find((work) => work.work_id === workId) ?? null;
|
|
105106
|
-
}
|
|
105107
|
-
function getWorkByPlanName(directory, planName, options) {
|
|
105108
|
-
const state3 = readBoulderState(directory);
|
|
105109
|
-
if (!state3) {
|
|
105110
|
-
return null;
|
|
105111
|
-
}
|
|
105112
|
-
const worktreePath = options?.worktreePath;
|
|
105113
|
-
return getBoulderWorks(state3).find((work) => {
|
|
105114
|
-
if (work.plan_name !== planName) {
|
|
105115
|
-
return false;
|
|
105116
|
-
}
|
|
105117
|
-
return worktreePath ? work.worktree_path === worktreePath : true;
|
|
105118
|
-
}) ?? null;
|
|
105119
|
-
}
|
|
105120
|
-
function getWorkForSession(directory, sessionId) {
|
|
105121
|
-
const state3 = readBoulderState(directory);
|
|
105122
|
-
if (!state3) {
|
|
105123
|
-
return null;
|
|
105124
|
-
}
|
|
105125
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
105126
|
-
let newestWork = null;
|
|
105127
|
-
let newestWorkMs = 0;
|
|
105128
|
-
for (const work of getBoulderWorks(state3)) {
|
|
105129
|
-
if (!work.session_ids.includes(normalizedSessionId)) {
|
|
105130
|
-
continue;
|
|
105131
|
-
}
|
|
105132
|
-
const workMs = parseIsoToMs(work.updated_at ?? work.started_at) ?? 0;
|
|
105133
|
-
if (!newestWork || workMs > newestWorkMs) {
|
|
105134
|
-
newestWork = work;
|
|
105135
|
-
newestWorkMs = workMs;
|
|
105136
|
-
}
|
|
105137
|
-
}
|
|
105138
|
-
if (newestWork) {
|
|
105139
|
-
return newestWork;
|
|
105140
|
-
}
|
|
105141
|
-
return state3.session_ids.includes(normalizedSessionId) ? buildWorkFromMirror(state3) : null;
|
|
105142
|
-
}
|
|
105143
|
-
function getWorkResumeOptions(directory) {
|
|
105144
|
-
const state3 = readBoulderState(directory);
|
|
105145
|
-
if (!state3) {
|
|
105146
|
-
return [];
|
|
105147
|
-
}
|
|
105148
|
-
return getBoulderWorks(state3).filter((work) => work.status !== "completed" && work.status !== "abandoned").map((work) => {
|
|
105149
|
-
const progress = getPlanProgress(resolveBoulderPlanPathForWork(directory, work));
|
|
105150
|
-
return {
|
|
105151
|
-
work_id: work.work_id,
|
|
105152
|
-
plan_name: work.plan_name,
|
|
105153
|
-
active_plan: work.active_plan,
|
|
105154
|
-
worktree_path: work.worktree_path,
|
|
105155
|
-
status: work.status && isValidWorkStatus(work.status) ? work.status : "active",
|
|
105156
|
-
started_at: work.started_at,
|
|
105157
|
-
updated_at: work.updated_at ?? work.started_at,
|
|
105158
|
-
ended_at: work.ended_at,
|
|
105159
|
-
elapsed_ms: work.elapsed_ms,
|
|
105160
|
-
session_count: work.session_ids.length,
|
|
105161
|
-
progress,
|
|
105162
|
-
is_current_mirror: state3.active_work_id === work.work_id
|
|
105163
|
-
};
|
|
105164
|
-
});
|
|
105165
|
-
}
|
|
105166
|
-
function getTaskSessionState(directory, taskKey) {
|
|
105167
|
-
const state3 = readBoulderState(directory);
|
|
105168
|
-
if (state3?.active_work_id) {
|
|
105169
|
-
const work = state3.works?.[state3.active_work_id];
|
|
105170
|
-
const taskSession = work?.task_sessions?.[taskKey];
|
|
105171
|
-
if (taskSession) {
|
|
105172
|
-
return taskSession;
|
|
105173
|
-
}
|
|
105174
|
-
}
|
|
105175
|
-
if (!state3?.task_sessions) {
|
|
105176
|
-
return null;
|
|
105177
|
-
}
|
|
105178
|
-
return state3.task_sessions[taskKey] ?? null;
|
|
105179
|
-
}
|
|
105180
|
-
// packages/boulder-state/src/storage/write-state.ts
|
|
105181
|
-
import { existsSync as existsSync81, mkdirSync as mkdirSync18, unlinkSync as unlinkSync13, writeFileSync as writeFileSync18 } from "fs";
|
|
105182
|
-
import { dirname as dirname31, join as join99 } from "path";
|
|
105183
|
-
function writeBoulderState(directory, state3) {
|
|
105184
|
-
const filePath = getBoulderFilePath(directory);
|
|
105185
|
-
try {
|
|
105186
|
-
const dir = dirname31(filePath);
|
|
105187
|
-
if (!existsSync81(dir)) {
|
|
105188
|
-
mkdirSync18(dir, { recursive: true });
|
|
105189
|
-
writeFileSync18(join99(dir, ".gitignore"), ["*", "!/rules/", "!/rules/**", ""].join(`
|
|
105190
|
-
`), "utf-8");
|
|
105191
|
-
}
|
|
105192
|
-
const stateToWrite = { ...state3 };
|
|
105193
|
-
if (stateToWrite.works && stateToWrite.active_work_id) {
|
|
105194
|
-
const activeWork = stateToWrite.works[stateToWrite.active_work_id];
|
|
105195
|
-
if (activeWork) {
|
|
105196
|
-
stateToWrite.works = {
|
|
105197
|
-
...stateToWrite.works,
|
|
105198
|
-
[stateToWrite.active_work_id]: {
|
|
105199
|
-
...activeWork,
|
|
105200
|
-
active_plan: stateToWrite.active_plan,
|
|
105201
|
-
plan_name: stateToWrite.plan_name,
|
|
105202
|
-
status: stateToWrite.status,
|
|
105203
|
-
started_at: stateToWrite.started_at,
|
|
105204
|
-
ended_at: stateToWrite.ended_at,
|
|
105205
|
-
elapsed_ms: stateToWrite.elapsed_ms,
|
|
105206
|
-
updated_at: stateToWrite.updated_at,
|
|
105207
|
-
session_ids: [...stateToWrite.session_ids],
|
|
105208
|
-
session_origins: stateToWrite.session_origins ? { ...stateToWrite.session_origins } : {},
|
|
105209
|
-
agent: stateToWrite.agent,
|
|
105210
|
-
worktree_path: stateToWrite.worktree_path,
|
|
105211
|
-
task_sessions: stateToWrite.task_sessions ? { ...stateToWrite.task_sessions } : {}
|
|
105212
|
-
}
|
|
105213
|
-
};
|
|
105214
|
-
}
|
|
105215
|
-
}
|
|
105216
|
-
writeFileSync18(filePath, JSON.stringify(stateToWrite, null, 2), "utf-8");
|
|
105217
|
-
return true;
|
|
105218
|
-
} catch {
|
|
105219
|
-
return false;
|
|
105220
|
-
}
|
|
105221
|
-
}
|
|
105222
|
-
function clearBoulderState(directory) {
|
|
105223
|
-
const filePath = getBoulderFilePath(directory);
|
|
105224
|
-
try {
|
|
105225
|
-
if (existsSync81(filePath)) {
|
|
105226
|
-
unlinkSync13(filePath);
|
|
105227
|
-
}
|
|
105228
|
-
return true;
|
|
105229
|
-
} catch {
|
|
105230
|
-
return false;
|
|
105231
|
-
}
|
|
105232
|
-
}
|
|
105233
|
-
function generateWorkId(planName) {
|
|
105234
|
-
const slug = planName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
105235
|
-
const randomHex = Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
|
|
105236
|
-
return `${slug.length > 0 ? slug : "work"}-${randomHex}`;
|
|
105237
|
-
}
|
|
105238
|
-
function createBoulderState(planPath, sessionId, agent, worktreePath) {
|
|
105239
|
-
const startedAt = nowIsoString();
|
|
105240
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
105241
|
-
const workId = generateWorkId(getPlanName(planPath));
|
|
105242
|
-
const work = {
|
|
105243
|
-
work_id: workId,
|
|
105244
|
-
active_plan: planPath,
|
|
105245
|
-
plan_name: getPlanName(planPath),
|
|
105246
|
-
status: "active",
|
|
105247
|
-
started_at: startedAt,
|
|
105248
|
-
updated_at: startedAt,
|
|
105249
|
-
session_ids: [normalizedSessionId],
|
|
105250
|
-
session_origins: { [normalizedSessionId]: "direct" },
|
|
105251
|
-
...agent !== undefined ? { agent } : {},
|
|
105252
|
-
...worktreePath !== undefined ? { worktree_path: worktreePath } : {},
|
|
105253
|
-
task_sessions: {}
|
|
105254
|
-
};
|
|
105255
|
-
return {
|
|
105256
|
-
schema_version: 2,
|
|
105257
|
-
active_work_id: workId,
|
|
105258
|
-
works: { [workId]: work },
|
|
105259
|
-
active_plan: planPath,
|
|
105260
|
-
started_at: startedAt,
|
|
105261
|
-
status: "active",
|
|
105262
|
-
updated_at: startedAt,
|
|
105263
|
-
session_ids: [normalizedSessionId],
|
|
105264
|
-
session_origins: { [normalizedSessionId]: "direct" },
|
|
105265
|
-
plan_name: getPlanName(planPath),
|
|
105266
|
-
task_sessions: {},
|
|
105267
|
-
...agent !== undefined ? { agent } : {},
|
|
105268
|
-
...worktreePath !== undefined ? { worktree_path: worktreePath } : {}
|
|
105269
|
-
};
|
|
105270
|
-
}
|
|
105271
|
-
function selectActiveWork(directory, workId) {
|
|
105272
|
-
const state3 = readBoulderState(directory);
|
|
105273
|
-
if (!state3) {
|
|
105274
|
-
return null;
|
|
105275
|
-
}
|
|
105276
|
-
const works = getBoulderWorks(state3);
|
|
105277
|
-
const nextWork = works.find((work) => work.work_id === workId);
|
|
105278
|
-
if (!nextWork) {
|
|
105279
|
-
return null;
|
|
105280
|
-
}
|
|
105281
|
-
const nextState = {
|
|
105282
|
-
...state3,
|
|
105283
|
-
schema_version: 2,
|
|
105284
|
-
active_work_id: workId,
|
|
105285
|
-
works: state3.works ?? Object.fromEntries(works.map((work) => [work.work_id, work]))
|
|
105286
|
-
};
|
|
105287
|
-
projectWorkToMirror(nextState, nextWork);
|
|
105288
|
-
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
105289
|
-
}
|
|
105290
|
-
function addBoulderWork(directory, input) {
|
|
105291
|
-
const state3 = readBoulderState(directory);
|
|
105292
|
-
if (!state3) {
|
|
105293
|
-
return null;
|
|
105294
|
-
}
|
|
105295
|
-
const workId = generateWorkId(getPlanName(input.planPath));
|
|
105296
|
-
const startedAt = input.startedAt ?? nowIsoString();
|
|
105297
|
-
const normalizedSessionId = normalizeSessionId(input.sessionId);
|
|
105298
|
-
const nextWork = {
|
|
105299
|
-
work_id: workId,
|
|
105300
|
-
active_plan: input.planPath,
|
|
105301
|
-
plan_name: getPlanName(input.planPath),
|
|
105302
|
-
status: "active",
|
|
105303
|
-
started_at: startedAt,
|
|
105304
|
-
updated_at: startedAt,
|
|
105305
|
-
session_ids: [normalizedSessionId],
|
|
105306
|
-
session_origins: { [normalizedSessionId]: "direct" },
|
|
105307
|
-
...input.agent !== undefined ? { agent: input.agent } : {},
|
|
105308
|
-
...input.worktreePath !== undefined ? { worktree_path: input.worktreePath } : {},
|
|
105309
|
-
task_sessions: {}
|
|
105310
|
-
};
|
|
105311
|
-
const nextState = {
|
|
105312
|
-
...state3,
|
|
105313
|
-
schema_version: 2,
|
|
105314
|
-
works: { ...Object.fromEntries(getBoulderWorks(state3).map((work) => [work.work_id, work])), [workId]: nextWork },
|
|
105315
|
-
active_work_id: workId
|
|
105316
|
-
};
|
|
105317
|
-
projectWorkToMirror(nextState, nextWork);
|
|
105318
|
-
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
105319
|
-
}
|
|
105320
|
-
function completeBoulder(directory, workId, endedAt) {
|
|
105321
|
-
const state3 = readBoulderState(directory);
|
|
105322
|
-
if (!state3) {
|
|
105323
|
-
return null;
|
|
105324
|
-
}
|
|
105325
|
-
const targetWorkId = workId ?? state3.active_work_id;
|
|
105326
|
-
if (!targetWorkId) {
|
|
105327
|
-
return null;
|
|
105328
|
-
}
|
|
105329
|
-
const work = state3.works?.[targetWorkId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === targetWorkId);
|
|
105330
|
-
if (!work) {
|
|
105331
|
-
return null;
|
|
105332
|
-
}
|
|
105333
|
-
if (work.status === "completed" && work.ended_at !== undefined && work.elapsed_ms !== undefined) {
|
|
105334
|
-
return state3;
|
|
105335
|
-
}
|
|
105336
|
-
const endAt = endedAt ?? nowIsoString();
|
|
105337
|
-
work.ended_at = endAt;
|
|
105338
|
-
work.elapsed_ms = getElapsedMs(work.started_at, endAt);
|
|
105339
|
-
work.status = "completed";
|
|
105340
|
-
work.updated_at = nowIsoString();
|
|
105341
|
-
if (state3.active_work_id === targetWorkId) {
|
|
105342
|
-
projectWorkToMirror(state3, work);
|
|
105343
|
-
}
|
|
105344
|
-
return writeBoulderState(directory, state3) ? state3 : null;
|
|
105345
|
-
}
|
|
105346
105566
|
|
|
105347
|
-
// packages/boulder-state/src/storage/session.ts
|
|
105348
|
-
function appendSessionId(directory, sessionId, origin = "direct") {
|
|
105349
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
105350
|
-
const activeWorkId = readBoulderState(directory)?.active_work_id;
|
|
105351
|
-
if (activeWorkId) {
|
|
105352
|
-
return appendSessionIdForWork(directory, activeWorkId, normalizedSessionId, origin);
|
|
105353
|
-
}
|
|
105354
|
-
const state3 = readBoulderState(directory);
|
|
105355
|
-
if (!state3) {
|
|
105356
|
-
return null;
|
|
105357
|
-
}
|
|
105358
|
-
if (!state3.session_origins || typeof state3.session_origins !== "object" || Array.isArray(state3.session_origins)) {
|
|
105359
|
-
state3.session_origins = {};
|
|
105360
|
-
}
|
|
105361
|
-
if (!state3.session_ids?.includes(normalizedSessionId)) {
|
|
105362
|
-
if (!Array.isArray(state3.session_ids)) {
|
|
105363
|
-
state3.session_ids = [];
|
|
105364
|
-
}
|
|
105365
|
-
const originalSessionIds = [...state3.session_ids];
|
|
105366
|
-
const originalSessionOrigins = { ...state3.session_origins };
|
|
105367
|
-
state3.session_ids.push(normalizedSessionId);
|
|
105368
|
-
state3.session_origins[normalizedSessionId] = origin;
|
|
105369
|
-
if (writeBoulderState(directory, state3)) {
|
|
105370
|
-
return state3;
|
|
105371
|
-
}
|
|
105372
|
-
state3.session_ids = originalSessionIds;
|
|
105373
|
-
state3.session_origins = originalSessionOrigins;
|
|
105374
|
-
return null;
|
|
105375
|
-
}
|
|
105376
|
-
if (!state3.session_origins[normalizedSessionId]) {
|
|
105377
|
-
state3.session_origins[normalizedSessionId] = origin;
|
|
105378
|
-
if (!writeBoulderState(directory, state3)) {
|
|
105379
|
-
return null;
|
|
105380
|
-
}
|
|
105381
|
-
}
|
|
105382
|
-
return state3;
|
|
105383
|
-
}
|
|
105384
|
-
function appendSessionIdForWork(directory, workId, sessionId, origin = "direct") {
|
|
105385
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
105386
|
-
const state3 = readBoulderState(directory);
|
|
105387
|
-
if (!state3) {
|
|
105388
|
-
return null;
|
|
105389
|
-
}
|
|
105390
|
-
const works = getBoulderWorks(state3);
|
|
105391
|
-
const targetWork = works.find((work) => work.work_id === workId);
|
|
105392
|
-
if (!targetWork) {
|
|
105393
|
-
return null;
|
|
105394
|
-
}
|
|
105395
|
-
const updatedWork = {
|
|
105396
|
-
...targetWork,
|
|
105397
|
-
session_ids: targetWork.session_ids.includes(normalizedSessionId) ? [...targetWork.session_ids] : [...targetWork.session_ids, normalizedSessionId],
|
|
105398
|
-
session_origins: { ...targetWork.session_origins ?? {}, [normalizedSessionId]: origin },
|
|
105399
|
-
updated_at: nowIsoString()
|
|
105400
|
-
};
|
|
105401
|
-
const nextState = {
|
|
105402
|
-
...state3,
|
|
105403
|
-
schema_version: 2,
|
|
105404
|
-
works: {
|
|
105405
|
-
...Object.fromEntries(works.map((work) => [work.work_id, work])),
|
|
105406
|
-
[workId]: updatedWork
|
|
105407
|
-
}
|
|
105408
|
-
};
|
|
105409
|
-
if (state3.active_work_id === workId) {
|
|
105410
|
-
projectWorkToMirror(nextState, updatedWork);
|
|
105411
|
-
}
|
|
105412
|
-
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
105413
|
-
}
|
|
105414
|
-
// packages/boulder-state/src/storage/task.ts
|
|
105415
|
-
function upsertTaskSessionState(directory, input) {
|
|
105416
|
-
const stateForWork = readBoulderState(directory);
|
|
105417
|
-
if (stateForWork?.active_work_id) {
|
|
105418
|
-
return upsertTaskSessionStateForWork(directory, stateForWork.active_work_id, input);
|
|
105419
|
-
}
|
|
105420
|
-
const state3 = readBoulderState(directory);
|
|
105421
|
-
if (!state3 || RESERVED_KEYS.has(input.taskKey)) {
|
|
105422
|
-
return null;
|
|
105423
|
-
}
|
|
105424
|
-
const normalizedSessionId = normalizeSessionId(input.sessionId);
|
|
105425
|
-
const taskSessions = state3.task_sessions ?? {};
|
|
105426
|
-
taskSessions[input.taskKey] = {
|
|
105427
|
-
task_key: input.taskKey,
|
|
105428
|
-
task_label: input.taskLabel,
|
|
105429
|
-
task_title: input.taskTitle,
|
|
105430
|
-
session_id: normalizedSessionId,
|
|
105431
|
-
...input.agent !== undefined ? { agent: input.agent } : {},
|
|
105432
|
-
...input.category !== undefined ? { category: input.category } : {},
|
|
105433
|
-
updated_at: nowIsoString()
|
|
105434
|
-
};
|
|
105435
|
-
state3.task_sessions = taskSessions;
|
|
105436
|
-
return writeBoulderState(directory, state3) ? state3 : null;
|
|
105437
|
-
}
|
|
105438
|
-
function upsertTaskSessionStateForWork(directory, workId, input) {
|
|
105439
|
-
if (RESERVED_KEYS.has(input.taskKey)) {
|
|
105440
|
-
return null;
|
|
105441
|
-
}
|
|
105442
|
-
const state3 = readBoulderState(directory);
|
|
105443
|
-
if (!state3) {
|
|
105444
|
-
return null;
|
|
105445
|
-
}
|
|
105446
|
-
const works = getBoulderWorks(state3);
|
|
105447
|
-
const targetWork = works.find((work) => work.work_id === workId);
|
|
105448
|
-
if (!targetWork) {
|
|
105449
|
-
return null;
|
|
105450
|
-
}
|
|
105451
|
-
const normalizedSessionId = normalizeSessionId(input.sessionId);
|
|
105452
|
-
const previousTaskSession = targetWork.task_sessions?.[input.taskKey];
|
|
105453
|
-
const nextTaskSession = {
|
|
105454
|
-
task_key: input.taskKey,
|
|
105455
|
-
task_label: input.taskLabel,
|
|
105456
|
-
task_title: input.taskTitle,
|
|
105457
|
-
session_id: normalizedSessionId,
|
|
105458
|
-
...input.agent !== undefined ? { agent: input.agent } : {},
|
|
105459
|
-
...input.category !== undefined ? { category: input.category } : {},
|
|
105460
|
-
...previousTaskSession?.started_at !== undefined ? { started_at: previousTaskSession.started_at } : {},
|
|
105461
|
-
...previousTaskSession?.ended_at !== undefined ? { ended_at: previousTaskSession.ended_at } : {},
|
|
105462
|
-
...previousTaskSession?.elapsed_ms !== undefined ? { elapsed_ms: previousTaskSession.elapsed_ms } : {},
|
|
105463
|
-
...previousTaskSession?.status !== undefined ? { status: previousTaskSession.status } : {},
|
|
105464
|
-
updated_at: nowIsoString()
|
|
105465
|
-
};
|
|
105466
|
-
const nextWork = {
|
|
105467
|
-
...targetWork,
|
|
105468
|
-
task_sessions: { ...targetWork.task_sessions ?? {}, [input.taskKey]: nextTaskSession },
|
|
105469
|
-
updated_at: nowIsoString()
|
|
105470
|
-
};
|
|
105471
|
-
const nextState = {
|
|
105472
|
-
...state3,
|
|
105473
|
-
schema_version: 2,
|
|
105474
|
-
works: {
|
|
105475
|
-
...Object.fromEntries(works.map((work) => [work.work_id, work])),
|
|
105476
|
-
[workId]: nextWork
|
|
105477
|
-
}
|
|
105478
|
-
};
|
|
105479
|
-
if (state3.active_work_id === workId) {
|
|
105480
|
-
projectWorkToMirror(nextState, nextWork);
|
|
105481
|
-
}
|
|
105482
|
-
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
105483
|
-
}
|
|
105484
|
-
function startTaskTimer(directory, workId, input) {
|
|
105485
|
-
const nextState = upsertTaskSessionStateForWork(directory, workId, {
|
|
105486
|
-
...input,
|
|
105487
|
-
sessionId: normalizeSessionId(input.sessionId)
|
|
105488
|
-
});
|
|
105489
|
-
if (!nextState) {
|
|
105490
|
-
return null;
|
|
105491
|
-
}
|
|
105492
|
-
const work = nextState.works?.[workId];
|
|
105493
|
-
const taskSession = work?.task_sessions?.[input.taskKey];
|
|
105494
|
-
if (!work || !taskSession) {
|
|
105495
|
-
return null;
|
|
105496
|
-
}
|
|
105497
|
-
const startedAt = taskSession.started_at ?? input.startedAt ?? nowIsoString();
|
|
105498
|
-
taskSession.started_at = startedAt;
|
|
105499
|
-
taskSession.status = "running";
|
|
105500
|
-
taskSession.updated_at = nowIsoString();
|
|
105501
|
-
work.updated_at = nowIsoString();
|
|
105502
|
-
return writeBoulderState(directory, nextState) ? nextState : null;
|
|
105503
|
-
}
|
|
105504
|
-
function endTaskTimer(directory, workId, taskKey, endedAt) {
|
|
105505
|
-
const state3 = readBoulderState(directory);
|
|
105506
|
-
if (!state3) {
|
|
105507
|
-
return null;
|
|
105508
|
-
}
|
|
105509
|
-
const work = state3.works?.[workId] ?? getBoulderWorks(state3).find((candidate) => candidate.work_id === workId);
|
|
105510
|
-
if (!work?.task_sessions?.[taskKey]) {
|
|
105511
|
-
return null;
|
|
105512
|
-
}
|
|
105513
|
-
const taskSession = work.task_sessions[taskKey];
|
|
105514
|
-
const endAt = endedAt ?? nowIsoString();
|
|
105515
|
-
taskSession.ended_at = endAt;
|
|
105516
|
-
taskSession.elapsed_ms = getElapsedMs(taskSession.started_at, endAt);
|
|
105517
|
-
taskSession.status = "completed";
|
|
105518
|
-
taskSession.updated_at = nowIsoString();
|
|
105519
|
-
work.updated_at = nowIsoString();
|
|
105520
|
-
if (state3.active_work_id === workId) {
|
|
105521
|
-
projectWorkToMirror(state3, work);
|
|
105522
|
-
}
|
|
105523
|
-
return writeBoulderState(directory, state3) ? state3 : null;
|
|
105524
|
-
}
|
|
105525
|
-
// packages/omo-opencode/src/features/boulder-state/format-duration.ts
|
|
105526
|
-
init_src();
|
|
105527
105567
|
// packages/omo-opencode/src/hooks/prometheus-md-only/agent-resolution.ts
|
|
105528
105568
|
function isCompactionAgent2(agent) {
|
|
105529
105569
|
return agent.toLowerCase() === "compaction";
|
|
@@ -106350,7 +106390,7 @@ async function findRecentSessionPlanPath(input) {
|
|
|
106350
106390
|
|
|
106351
106391
|
// packages/omo-opencode/src/hooks/start-work/start-work-hook.ts
|
|
106352
106392
|
var HOOK_NAME6 = "start-work";
|
|
106353
|
-
var START_WORK_TEMPLATE_MARKER = "You are starting
|
|
106393
|
+
var START_WORK_TEMPLATE_MARKER = "You are starting an Atlas work session.";
|
|
106354
106394
|
var CONTEXT_INFO_MARKER = "<!-- omo-start-work-context -->";
|
|
106355
106395
|
function resolveWorktreeContext(explicitWorktreePath) {
|
|
106356
106396
|
if (explicitWorktreePath === null) {
|
|
@@ -110651,7 +110691,8 @@ function createAbortSessionRequest(deps) {
|
|
|
110651
110691
|
await ctx.client.session.abort({ path: { id: sessionID } });
|
|
110652
110692
|
releasePromptAsyncReservation(sessionID, `runtime-fallback-abort:${source}`, {
|
|
110653
110693
|
reservedBy: `runtime-fallback:${source}`,
|
|
110654
|
-
reservedByPrefix: "runtime-fallback:"
|
|
110694
|
+
reservedByPrefix: "runtime-fallback:",
|
|
110695
|
+
supersedeTransientRetryOwners: true
|
|
110655
110696
|
});
|
|
110656
110697
|
log2(`[${HOOK_NAME11}] Aborted in-flight session request (${source})`, { sessionID });
|
|
110657
110698
|
} catch (error) {
|
|
@@ -110897,6 +110938,7 @@ function createAutoRetryDispatcher(deps, scheduleSessionFallbackTimeout, clearSe
|
|
|
110897
110938
|
sessionStates,
|
|
110898
110939
|
sessionRetryInFlight,
|
|
110899
110940
|
sessionAwaitingFallbackResult,
|
|
110941
|
+
internallyAbortedSessions,
|
|
110900
110942
|
pluginConfig
|
|
110901
110943
|
} = deps;
|
|
110902
110944
|
return async (sessionID, newModel, resolvedAgent, source) => {
|
|
@@ -110966,6 +111008,7 @@ function createAutoRetryDispatcher(deps, scheduleSessionFallbackTimeout, clearSe
|
|
|
110966
111008
|
},
|
|
110967
111009
|
query: { directory: ctx.directory }
|
|
110968
111010
|
};
|
|
111011
|
+
const wasInternallyAborted = internallyAbortedSessions.has(sessionID);
|
|
110969
111012
|
const dispatchRetryPrompt = (retrySource, queueBehavior) => dispatchInternalPrompt({
|
|
110970
111013
|
mode: "async",
|
|
110971
111014
|
client: ctx.client,
|
|
@@ -110973,6 +111016,7 @@ function createAutoRetryDispatcher(deps, scheduleSessionFallbackTimeout, clearSe
|
|
|
110973
111016
|
source: retrySource,
|
|
110974
111017
|
settleMs: 0,
|
|
110975
111018
|
...queueBehavior ? { queueBehavior } : {},
|
|
111019
|
+
...wasInternallyAborted ? { checkToolState: false } : {},
|
|
110976
111020
|
input: retryPromptInput
|
|
110977
111021
|
});
|
|
110978
111022
|
let promptResult = await dispatchRetryPrompt(`runtime-fallback:${source}`, "defer");
|
|
@@ -111733,6 +111777,7 @@ function createEventHandler(deps, helpers) {
|
|
|
111733
111777
|
}
|
|
111734
111778
|
sessionRetryInFlight.delete(sessionID);
|
|
111735
111779
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
111780
|
+
deps.internallyAbortedSessions.delete(sessionID);
|
|
111736
111781
|
sessionStatusRetryKeys.delete(sessionID);
|
|
111737
111782
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
111738
111783
|
};
|
|
@@ -111766,6 +111811,7 @@ function createEventHandler(deps, helpers) {
|
|
|
111766
111811
|
sessionLastAccess.delete(sessionID);
|
|
111767
111812
|
sessionRetryInFlight.delete(sessionID);
|
|
111768
111813
|
sessionAwaitingFallbackResult.delete(sessionID);
|
|
111814
|
+
deps.internallyAbortedSessions.delete(sessionID);
|
|
111769
111815
|
helpers.clearSessionFallbackTimeout(sessionID);
|
|
111770
111816
|
sessionStatusRetryKeys.delete(sessionID);
|
|
111771
111817
|
SessionCategoryRegistry.remove(sessionID);
|
|
@@ -127695,6 +127741,7 @@ function cloneParentWake(wake) {
|
|
|
127695
127741
|
promptContext,
|
|
127696
127742
|
notifications: [...wake.notifications],
|
|
127697
127743
|
shouldReply: wake.shouldReply,
|
|
127744
|
+
...wake.queuedAt !== undefined ? { queuedAt: wake.queuedAt } : {},
|
|
127698
127745
|
...wake.dispatchedAt !== undefined ? { dispatchedAt: wake.dispatchedAt } : {},
|
|
127699
127746
|
...wake.noReplyAdmittedAt !== undefined ? { noReplyAdmittedAt: wake.noReplyAdmittedAt } : {},
|
|
127700
127747
|
...wake.toolCallDeferralStartedAt !== undefined ? { toolCallDeferralStartedAt: wake.toolCallDeferralStartedAt } : {},
|
|
@@ -128037,7 +128084,7 @@ async function sendParentWakePrompt(input) {
|
|
|
128037
128084
|
...input.emptyAssistantTurnRetry ? { dedupeKey: createEmptyAssistantTurnRetryDedupeKey(input.latestWake) } : {},
|
|
128038
128085
|
settleMs: 0,
|
|
128039
128086
|
queueBehavior: "defer",
|
|
128040
|
-
checkStatus: input.forceNoReply !== true,
|
|
128087
|
+
checkStatus: input.forceNoReply !== true && input.skipPromptGateStatusCheck !== true,
|
|
128041
128088
|
checkToolState: input.forceNoReply !== true && !input.toolWaitDecision.skipPromptGateToolStateCheck,
|
|
128042
128089
|
input: {
|
|
128043
128090
|
path: { id: input.sessionID },
|
|
@@ -128120,6 +128167,8 @@ function createTrackedDispatchedWake(wake, forceNoReply) {
|
|
|
128120
128167
|
}
|
|
128121
128168
|
|
|
128122
128169
|
// packages/omo-opencode/src/features/background-agent/parent-wake-flush-runner.ts
|
|
128170
|
+
var PENDING_PARENT_WAKE_MAX_ACTIVE_DEFER_MS = 60000;
|
|
128171
|
+
|
|
128123
128172
|
class ParentWakeFlushRunner {
|
|
128124
128173
|
deps;
|
|
128125
128174
|
constructor(deps) {
|
|
@@ -128149,14 +128198,16 @@ class ParentWakeFlushRunner {
|
|
|
128149
128198
|
if (await this.dropAdmittedWakeConsumedByParent(sessionID, latestWake)) {
|
|
128150
128199
|
return;
|
|
128151
128200
|
}
|
|
128152
|
-
|
|
128201
|
+
const emptyAssistantTurnRetry = latestWake.allowEmptyAssistantTurnRetry === true;
|
|
128202
|
+
const forceDispatchAfterActiveDefer = sessionActive && this.shouldForceDispatchAfterActiveDefer(latestWake);
|
|
128203
|
+
if (sessionActive && !forceDispatchAfterActiveDefer) {
|
|
128153
128204
|
this.schedulePendingParentWakeFlush(sessionID);
|
|
128154
128205
|
log2("[background-agent] Deferred parent wake because parent session is active:", {
|
|
128155
128206
|
sessionID
|
|
128156
128207
|
});
|
|
128157
128208
|
return;
|
|
128158
128209
|
}
|
|
128159
|
-
if (this.hasRecentParentSessionActivity(sessionID)) {
|
|
128210
|
+
if (!forceDispatchAfterActiveDefer && this.hasRecentParentSessionActivity(sessionID)) {
|
|
128160
128211
|
if (this.deferReplyWakeWhileUnsafe(sessionID, latestWake)) {
|
|
128161
128212
|
return;
|
|
128162
128213
|
}
|
|
@@ -128174,7 +128225,6 @@ class ParentWakeFlushRunner {
|
|
|
128174
128225
|
}
|
|
128175
128226
|
return;
|
|
128176
128227
|
}
|
|
128177
|
-
const emptyAssistantTurnRetry = latestWake.allowEmptyAssistantTurnRetry === true;
|
|
128178
128228
|
const toolWaitDecision = await this.shouldDeferParentWakeForSessionHistory(sessionID, latestWake);
|
|
128179
128229
|
if (toolWaitDecision.defer) {
|
|
128180
128230
|
if (this.deferReplyWakeWhileUnsafe(sessionID, latestWake)) {
|
|
@@ -128227,8 +128277,15 @@ class ParentWakeFlushRunner {
|
|
|
128227
128277
|
}
|
|
128228
128278
|
await this.sendParentWakePrompt(sessionID, latestWake, {
|
|
128229
128279
|
emptyAssistantTurnRetry,
|
|
128230
|
-
toolWaitDecision: finalToolWaitDecision
|
|
128280
|
+
toolWaitDecision: finalToolWaitDecision,
|
|
128281
|
+
...forceDispatchAfterActiveDefer ? { skipPromptGateStatusCheck: true } : {}
|
|
128231
128282
|
});
|
|
128283
|
+
if (forceDispatchAfterActiveDefer) {
|
|
128284
|
+
log2("[background-agent] Sent parent wake after active-session defer ceiling:", {
|
|
128285
|
+
sessionID,
|
|
128286
|
+
queuedAgeMs: this.getQueuedAgeMs(latestWake)
|
|
128287
|
+
});
|
|
128288
|
+
}
|
|
128232
128289
|
}
|
|
128233
128290
|
schedulePendingParentWakeFlush(sessionID, delayMs) {
|
|
128234
128291
|
this.deps.pendingQueue.scheduleFlush(sessionID, async () => {
|
|
@@ -128280,6 +128337,7 @@ class ParentWakeFlushRunner {
|
|
|
128280
128337
|
latestWake,
|
|
128281
128338
|
...options.forceNoReply !== undefined ? { forceNoReply: options.forceNoReply } : {},
|
|
128282
128339
|
...options.retainPendingWake !== undefined ? { retainPendingWake: options.retainPendingWake } : {},
|
|
128340
|
+
...options.skipPromptGateStatusCheck !== undefined ? { skipPromptGateStatusCheck: options.skipPromptGateStatusCheck } : {},
|
|
128283
128341
|
emptyAssistantTurnRetry: options.emptyAssistantTurnRetry,
|
|
128284
128342
|
toolWaitDecision: options.toolWaitDecision,
|
|
128285
128343
|
getDispatchedWake: () => this.deps.dispatchedTracker.getWake(sessionID),
|
|
@@ -128295,6 +128353,12 @@ class ParentWakeFlushRunner {
|
|
|
128295
128353
|
async isSessionActive(sessionID) {
|
|
128296
128354
|
return isSessionActive(this.deps.notifierDeps.client, sessionID);
|
|
128297
128355
|
}
|
|
128356
|
+
shouldForceDispatchAfterActiveDefer(wake) {
|
|
128357
|
+
return wake.shouldReply && this.getQueuedAgeMs(wake) >= PENDING_PARENT_WAKE_MAX_ACTIVE_DEFER_MS;
|
|
128358
|
+
}
|
|
128359
|
+
getQueuedAgeMs(wake) {
|
|
128360
|
+
return Date.now() - (wake.queuedAt ?? Date.now());
|
|
128361
|
+
}
|
|
128298
128362
|
hasRecentParentSessionActivity(sessionID) {
|
|
128299
128363
|
return this.deps.sessionInspector.hasRecentActivity(sessionID);
|
|
128300
128364
|
}
|
|
@@ -128339,9 +128403,11 @@ class ParentWakePendingQueue {
|
|
|
128339
128403
|
this.pendingParentWakes.delete(sessionID);
|
|
128340
128404
|
}
|
|
128341
128405
|
queueWake(sessionID, notification, promptContext, shouldReply) {
|
|
128406
|
+
const now = Date.now();
|
|
128342
128407
|
const resolvedPromptContext = resolveParentWakePromptContext(promptContext);
|
|
128343
128408
|
const pendingWake = this.pendingParentWakes.get(sessionID);
|
|
128344
128409
|
if (pendingWake) {
|
|
128410
|
+
pendingWake.queuedAt ??= now;
|
|
128345
128411
|
const mergedNotifications = mergeParentWakeNotifications(pendingWake.notifications, notification);
|
|
128346
128412
|
const notificationsChanged = mergedNotifications.length !== pendingWake.notifications.length || mergedNotifications.some((merged, index) => merged !== pendingWake.notifications[index]);
|
|
128347
128413
|
pendingWake.notifications = mergedNotifications;
|
|
@@ -128356,12 +128422,17 @@ class ParentWakePendingQueue {
|
|
|
128356
128422
|
this.pendingParentWakes.set(sessionID, {
|
|
128357
128423
|
promptContext: resolvedPromptContext,
|
|
128358
128424
|
notifications: [notification],
|
|
128359
|
-
shouldReply
|
|
128425
|
+
shouldReply,
|
|
128426
|
+
queuedAt: now
|
|
128360
128427
|
});
|
|
128361
128428
|
}
|
|
128362
128429
|
requeueWake(sessionID, latestWake) {
|
|
128430
|
+
const now = Date.now();
|
|
128363
128431
|
const pendingWake = this.pendingParentWakes.get(sessionID);
|
|
128364
128432
|
if (pendingWake) {
|
|
128433
|
+
const existingQueuedAt = pendingWake.queuedAt ?? now;
|
|
128434
|
+
const latestQueuedAt = latestWake.queuedAt ?? now;
|
|
128435
|
+
pendingWake.queuedAt = Math.min(existingQueuedAt, latestQueuedAt);
|
|
128365
128436
|
pendingWake.notifications = pendingWake.notifications.reduce((notifications, notification) => mergeParentWakeNotifications(notifications, notification), [...latestWake.notifications]);
|
|
128366
128437
|
pendingWake.shouldReply = pendingWake.shouldReply || latestWake.shouldReply;
|
|
128367
128438
|
pendingWake.promptContext = latestWake.promptContext;
|
|
@@ -128374,7 +128445,9 @@ class ParentWakePendingQueue {
|
|
|
128374
128445
|
}
|
|
128375
128446
|
return;
|
|
128376
128447
|
}
|
|
128377
|
-
|
|
128448
|
+
const clonedWake = cloneParentWake(latestWake);
|
|
128449
|
+
clonedWake.queuedAt ??= now;
|
|
128450
|
+
this.pendingParentWakes.set(sessionID, clonedWake);
|
|
128378
128451
|
}
|
|
128379
128452
|
scheduleFlush(sessionID, operation, delayMs) {
|
|
128380
128453
|
if (this.pendingParentWakeTimers.has(sessionID)) {
|
|
@@ -133073,6 +133146,7 @@ function createMonitorPipeline(components, deps) {
|
|
|
133073
133146
|
}
|
|
133074
133147
|
|
|
133075
133148
|
// packages/omo-opencode/src/features/monitor/process.ts
|
|
133149
|
+
init_bun_spawn_shim();
|
|
133076
133150
|
var KILL_GRACE_MS = 5000;
|
|
133077
133151
|
function killProcessGroup(pid, signal) {
|
|
133078
133152
|
try {
|
|
@@ -133094,7 +133168,7 @@ function spawnMonitoredProcess(opts, deps) {
|
|
|
133094
133168
|
if (argv.length === 0) {
|
|
133095
133169
|
throw new Error("Cannot spawn an empty monitor command");
|
|
133096
133170
|
}
|
|
133097
|
-
const subprocess = spawnDetachedProcess(argv, opts, deps.spawn ??
|
|
133171
|
+
const subprocess = spawnDetachedProcess(argv, opts, deps.spawn ?? spawn);
|
|
133098
133172
|
let actualExited = false;
|
|
133099
133173
|
let publicExitSettled = false;
|
|
133100
133174
|
let watchdogTimer;
|
|
@@ -133124,11 +133198,15 @@ function spawnMonitoredProcess(opts, deps) {
|
|
|
133124
133198
|
function kill(signal = "SIGTERM") {
|
|
133125
133199
|
if (actualExited)
|
|
133126
133200
|
return;
|
|
133127
|
-
|
|
133201
|
+
if (subprocess.pid !== undefined) {
|
|
133202
|
+
killProcessGroup(subprocess.pid, signal);
|
|
133203
|
+
}
|
|
133128
133204
|
if (graceTimer === undefined) {
|
|
133129
133205
|
graceTimer = deps.setTimer(() => {
|
|
133130
133206
|
if (!actualExited) {
|
|
133131
|
-
|
|
133207
|
+
if (subprocess.pid !== undefined) {
|
|
133208
|
+
killProcessGroup(subprocess.pid, "SIGKILL");
|
|
133209
|
+
}
|
|
133132
133210
|
}
|
|
133133
133211
|
}, KILL_GRACE_MS);
|
|
133134
133212
|
}
|
|
@@ -133142,7 +133220,7 @@ function spawnMonitoredProcess(opts, deps) {
|
|
|
133142
133220
|
actualExited = true;
|
|
133143
133221
|
clearWatchdog();
|
|
133144
133222
|
clearGraceTimer();
|
|
133145
|
-
settlePublicExit({ code, signal: subprocess.signalCode });
|
|
133223
|
+
settlePublicExit({ code, signal: subprocess.signalCode ?? null });
|
|
133146
133224
|
}).catch((error) => {
|
|
133147
133225
|
actualExited = true;
|
|
133148
133226
|
clearWatchdog();
|
|
@@ -160299,7 +160377,7 @@ function extractPromptText5(parts) {
|
|
|
160299
160377
|
}
|
|
160300
160378
|
|
|
160301
160379
|
// packages/omo-opencode/src/plugin/chat-message/start-work-message.ts
|
|
160302
|
-
var START_WORK_TEMPLATE_MARKER2 = "You are starting
|
|
160380
|
+
var START_WORK_TEMPLATE_MARKER2 = "You are starting an Atlas work session.";
|
|
160303
160381
|
function isStartWorkHookOutput(value) {
|
|
160304
160382
|
if (typeof value !== "object" || value === null)
|
|
160305
160383
|
return false;
|