oh-my-copilot 4.13.45 → 4.13.54
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bridge/cli.cjs +254 -9
- package/dist/__tests__/auto-slash-aliases.test.js +6 -6
- package/dist/__tests__/auto-slash-aliases.test.js.map +1 -1
- package/dist/__tests__/auto-update.test.js +132 -2
- package/dist/__tests__/auto-update.test.js.map +1 -1
- package/dist/__tests__/pre-tool-enforcer.test.js +253 -0
- package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
- package/dist/__tests__/skills.test.js +77 -7
- package/dist/__tests__/skills.test.js.map +1 -1
- package/dist/__tests__/wiki-hook-output-format.test.d.ts +2 -0
- package/dist/__tests__/wiki-hook-output-format.test.d.ts.map +1 -0
- package/dist/__tests__/wiki-hook-output-format.test.js +59 -0
- package/dist/__tests__/wiki-hook-output-format.test.js.map +1 -0
- package/dist/features/auto-update.d.ts.map +1 -1
- package/dist/features/auto-update.js +53 -5
- package/dist/features/auto-update.js.map +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +109 -0
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/persistent-mode/index.d.ts +6 -0
- package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
- package/dist/hooks/persistent-mode/index.js +96 -0
- package/dist/hooks/persistent-mode/index.js.map +1 -1
- package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +115 -22
- package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
- package/docs/REFERENCE.md +5 -5
- package/docs/copilot-instructions.md +1 -1
- package/package.json +1 -1
- package/scripts/persistent-mode.cjs +71 -0
- package/scripts/persistent-mode.mjs +66 -0
- package/scripts/pre-tool-enforcer.mjs +105 -4
- package/scripts/wiki-pre-compact.mjs +1 -4
- package/skills/deep-dive/SKILL.md +52 -4
- package/skills/deep-interview/SKILL.md +109 -15
- package/skills/plan/SKILL.md +1 -1
- package/skills/ralplan/SKILL.md +2 -2
- package/templates/hooks/persistent-mode.mjs +66 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "oh-my-copilot",
|
|
12
12
|
"description": "Copilot CLI native multi-agent orchestration with intelligent model routing, 18 agent variants, and 32 powerful skills. Zero learning curve. Maximum power.",
|
|
13
|
-
"version": "4.13.
|
|
13
|
+
"version": "4.13.54",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Robin Norberg",
|
|
16
16
|
"email": "github@platypus.se"
|
|
@@ -27,5 +27,5 @@
|
|
|
27
27
|
]
|
|
28
28
|
}
|
|
29
29
|
],
|
|
30
|
-
"version": "4.13.
|
|
30
|
+
"version": "4.13.54"
|
|
31
31
|
}
|
package/bridge/cli.cjs
CHANGED
|
@@ -17581,12 +17581,56 @@ function updateLastCheckTime() {
|
|
|
17581
17581
|
saveVersionMetadata(current);
|
|
17582
17582
|
}
|
|
17583
17583
|
}
|
|
17584
|
+
function getGitHubUpdateToken() {
|
|
17585
|
+
const token = process.env.GH_TOKEN?.trim() || process.env.GITHUB_TOKEN?.trim();
|
|
17586
|
+
return token || null;
|
|
17587
|
+
}
|
|
17588
|
+
function getGitHubReleaseHeaders() {
|
|
17589
|
+
const headers = {
|
|
17590
|
+
"Accept": "application/vnd.github.v3+json",
|
|
17591
|
+
"User-Agent": "oh-my-copilot-updater"
|
|
17592
|
+
};
|
|
17593
|
+
const token = getGitHubUpdateToken();
|
|
17594
|
+
if (token) {
|
|
17595
|
+
headers.Authorization = `Bearer ${token}`;
|
|
17596
|
+
}
|
|
17597
|
+
return headers;
|
|
17598
|
+
}
|
|
17599
|
+
function getHeader(response, name) {
|
|
17600
|
+
return response.headers?.get(name) ?? response.headers?.get(name.toLowerCase()) ?? null;
|
|
17601
|
+
}
|
|
17602
|
+
function formatRateLimitReset(resetHeader) {
|
|
17603
|
+
if (!resetHeader) {
|
|
17604
|
+
return null;
|
|
17605
|
+
}
|
|
17606
|
+
const resetSeconds = Number.parseInt(resetHeader, 10);
|
|
17607
|
+
if (!Number.isFinite(resetSeconds) || resetSeconds <= 0) {
|
|
17608
|
+
return null;
|
|
17609
|
+
}
|
|
17610
|
+
return new Date(resetSeconds * 1e3).toISOString();
|
|
17611
|
+
}
|
|
17612
|
+
async function formatGitHubReleaseFetchError(response, usedToken) {
|
|
17613
|
+
let body = "";
|
|
17614
|
+
try {
|
|
17615
|
+
body = await response.text();
|
|
17616
|
+
} catch {
|
|
17617
|
+
body = "";
|
|
17618
|
+
}
|
|
17619
|
+
const remaining = getHeader(response, "x-ratelimit-remaining");
|
|
17620
|
+
const resetAt = formatRateLimitReset(getHeader(response, "x-ratelimit-reset"));
|
|
17621
|
+
const bodyLooksRateLimited = /rate limit|api rate limit|secondary rate/i.test(body);
|
|
17622
|
+
const isRateLimited = response.status === 429 || response.status === 403 && (remaining === "0" || bodyLooksRateLimited);
|
|
17623
|
+
if (!isRateLimited) {
|
|
17624
|
+
return `Failed to fetch release info: ${response.status} ${response.statusText}`;
|
|
17625
|
+
}
|
|
17626
|
+
const retrySuffix = resetAt ? ` Try again after ${resetAt}.` : "";
|
|
17627
|
+
const authHint = usedToken ? "The configured GitHub token appears to be rate limited; verify the token or try again later." : "Set GH_TOKEN or GITHUB_TOKEN to use authenticated GitHub API requests and increase rate limits.";
|
|
17628
|
+
return `Failed to fetch release info: GitHub API rate limit exceeded (${response.status} ${response.statusText}). ${authHint}${retrySuffix}`;
|
|
17629
|
+
}
|
|
17584
17630
|
async function fetchLatestRelease() {
|
|
17631
|
+
const usedToken = getGitHubUpdateToken() !== null;
|
|
17585
17632
|
const response = await fetch(`${GITHUB_API_URL}/releases/latest`, {
|
|
17586
|
-
headers:
|
|
17587
|
-
"Accept": "application/vnd.github.v3+json",
|
|
17588
|
-
"User-Agent": "oh-my-copilot-updater"
|
|
17589
|
-
}
|
|
17633
|
+
headers: getGitHubReleaseHeaders()
|
|
17590
17634
|
});
|
|
17591
17635
|
if (response.status === 404) {
|
|
17592
17636
|
const pkgResponse = await fetch(`${GITHUB_RAW_URL}/main/package.json`, {
|
|
@@ -17609,7 +17653,7 @@ async function fetchLatestRelease() {
|
|
|
17609
17653
|
throw new Error("No releases found and could not fetch package.json");
|
|
17610
17654
|
}
|
|
17611
17655
|
if (!response.ok) {
|
|
17612
|
-
throw new Error(
|
|
17656
|
+
throw new Error(await formatGitHubReleaseFetchError(response, usedToken));
|
|
17613
17657
|
}
|
|
17614
17658
|
return await response.json();
|
|
17615
17659
|
}
|
|
@@ -24647,6 +24691,7 @@ __export(persistent_mode_exports, {
|
|
|
24647
24691
|
createHookOutput: () => createHookOutput,
|
|
24648
24692
|
getIdleNotificationCooldownSeconds: () => getIdleNotificationCooldownSeconds,
|
|
24649
24693
|
getToolErrorRetryGuidance: () => getToolErrorRetryGuidance,
|
|
24694
|
+
hasPendingOwnedAsyncWork: () => hasPendingOwnedAsyncWork,
|
|
24650
24695
|
readLastToolError: () => readLastToolError,
|
|
24651
24696
|
recordIdleNotificationSent: () => recordIdleNotificationSent,
|
|
24652
24697
|
resetTodoContinuationAttempts: () => resetTodoContinuationAttempts,
|
|
@@ -24702,6 +24747,78 @@ function isStaleState(state) {
|
|
|
24702
24747
|
}
|
|
24703
24748
|
return Date.now() - mostRecent > STALE_STATE_THRESHOLD_MS;
|
|
24704
24749
|
}
|
|
24750
|
+
function parseTimestamp(value) {
|
|
24751
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
24752
|
+
return null;
|
|
24753
|
+
}
|
|
24754
|
+
const parsed = new Date(value).getTime();
|
|
24755
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
24756
|
+
}
|
|
24757
|
+
function isFreshTimestamp(value, ttlMs = PENDING_ASYNC_STATE_STALE_MS) {
|
|
24758
|
+
const parsed = parseTimestamp(value);
|
|
24759
|
+
return parsed !== null && Date.now() - parsed <= ttlMs;
|
|
24760
|
+
}
|
|
24761
|
+
function hasPendingBackgroundTask(directory, sessionId) {
|
|
24762
|
+
try {
|
|
24763
|
+
const stateRoot2 = (0, import_path56.join)(getOmcRoot(directory), "state");
|
|
24764
|
+
const hudPath = sessionId ? (0, import_path56.join)(stateRoot2, "sessions", sessionId, "hud-state.json") : (0, import_path56.join)(stateRoot2, "hud-state.json");
|
|
24765
|
+
if (!(0, import_fs49.existsSync)(hudPath)) return false;
|
|
24766
|
+
const hudState = JSON.parse((0, import_fs49.readFileSync)(hudPath, "utf-8"));
|
|
24767
|
+
return Boolean(hudState?.backgroundTasks?.some((task) => {
|
|
24768
|
+
if (task.status !== "running") return false;
|
|
24769
|
+
return isFreshTimestamp(task.startedAt ?? task.startTime);
|
|
24770
|
+
}));
|
|
24771
|
+
} catch {
|
|
24772
|
+
return false;
|
|
24773
|
+
}
|
|
24774
|
+
}
|
|
24775
|
+
function readPendingWakeupState(directory, sessionId) {
|
|
24776
|
+
const stateRoot2 = (0, import_path56.join)(getOmcRoot(directory), "state");
|
|
24777
|
+
const dirs = sessionId ? [(0, import_path56.join)(stateRoot2, "sessions", sessionId), stateRoot2] : [stateRoot2];
|
|
24778
|
+
const fileNames = [
|
|
24779
|
+
"scheduled-wakeup-state.json",
|
|
24780
|
+
"schedule-wakeup-state.json",
|
|
24781
|
+
"wakeup-state.json"
|
|
24782
|
+
];
|
|
24783
|
+
const states = [];
|
|
24784
|
+
for (const dir of dirs) {
|
|
24785
|
+
for (const fileName of fileNames) {
|
|
24786
|
+
const filePath = (0, import_path56.join)(dir, fileName);
|
|
24787
|
+
try {
|
|
24788
|
+
if (!(0, import_fs49.existsSync)(filePath)) continue;
|
|
24789
|
+
const parsed = JSON.parse((0, import_fs49.readFileSync)(filePath, "utf-8"));
|
|
24790
|
+
if (parsed && typeof parsed === "object") {
|
|
24791
|
+
states.push(parsed);
|
|
24792
|
+
}
|
|
24793
|
+
} catch {
|
|
24794
|
+
continue;
|
|
24795
|
+
}
|
|
24796
|
+
}
|
|
24797
|
+
}
|
|
24798
|
+
return states;
|
|
24799
|
+
}
|
|
24800
|
+
function hasPendingScheduledWakeup(directory, sessionId) {
|
|
24801
|
+
const now = Date.now();
|
|
24802
|
+
return readPendingWakeupState(directory, sessionId).some((state) => {
|
|
24803
|
+
const status = typeof state.status === "string" ? state.status.toLowerCase() : "";
|
|
24804
|
+
if (["completed", "complete", "cancelled", "canceled", "failed", "expired"].includes(status)) {
|
|
24805
|
+
return false;
|
|
24806
|
+
}
|
|
24807
|
+
const dueAt = parseTimestamp(
|
|
24808
|
+
state.due_at ?? state.wakeup_at ?? state.scheduled_for ?? state.deadline_at ?? state.expires_at
|
|
24809
|
+
);
|
|
24810
|
+
if (dueAt !== null) {
|
|
24811
|
+
return dueAt > now;
|
|
24812
|
+
}
|
|
24813
|
+
if (state.active === true || state.pending === true) {
|
|
24814
|
+
return isFreshTimestamp(state.created_at ?? state.updated_at ?? state.started_at);
|
|
24815
|
+
}
|
|
24816
|
+
return false;
|
|
24817
|
+
});
|
|
24818
|
+
}
|
|
24819
|
+
function hasPendingOwnedAsyncWork(directory, sessionId) {
|
|
24820
|
+
return hasPendingBackgroundTask(directory, sessionId) || hasPendingScheduledWakeup(directory, sessionId);
|
|
24821
|
+
}
|
|
24705
24822
|
function readLastToolError(directory) {
|
|
24706
24823
|
const stateDir = (0, import_path56.join)(getOmcRoot(directory), "state");
|
|
24707
24824
|
const errorPath = (0, import_path56.join)(stateDir, "last-tool-error.json");
|
|
@@ -25717,6 +25834,13 @@ async function checkPersistentModes(sessionId, directory, stopContext) {
|
|
|
25717
25834
|
if (skipHooks.includes("persistent-mode") || skipHooks.includes("stop-continuation")) {
|
|
25718
25835
|
return { shouldBlock: false, message: "", mode: "none" };
|
|
25719
25836
|
}
|
|
25837
|
+
if (hasPendingOwnedAsyncWork(workingDir, sessionId)) {
|
|
25838
|
+
return {
|
|
25839
|
+
shouldBlock: false,
|
|
25840
|
+
message: "",
|
|
25841
|
+
mode: "none"
|
|
25842
|
+
};
|
|
25843
|
+
}
|
|
25720
25844
|
try {
|
|
25721
25845
|
const { readSkillActiveStateNormalized: readSkillActiveStateNormalized2, pruneExpiredWorkflowSkillTombstones: pruneExpiredWorkflowSkillTombstones2, writeSkillActiveStateCopies: writeSkillActiveStateCopies2 } = await Promise.resolve().then(() => (init_skill_state(), skill_state_exports));
|
|
25722
25846
|
const current = readSkillActiveStateNormalized2(workingDir, sessionId);
|
|
@@ -25826,7 +25950,7 @@ function createHookOutput(result) {
|
|
|
25826
25950
|
message: result.message || void 0
|
|
25827
25951
|
};
|
|
25828
25952
|
}
|
|
25829
|
-
var import_fs49, import_path56, CANCEL_SIGNAL_TTL_MS2, STALE_STATE_THRESHOLD_MS, todoContinuationAttempts, TRANSCRIPT_TAIL_BYTES, CRITICAL_CONTEXT_STOP_PERCENT, RALPLAN_TERMINAL_PHASES, REVIEWER_TASK_TOOL_NAMES, REVIEWER_COMMAND_TOOL_NAMES, AWAITING_CONFIRMATION_TTL_MS, TEAM_PIPELINE_STOP_BLOCKER_MAX, TEAM_PIPELINE_STOP_BLOCKER_TTL_MS, RALPLAN_STOP_BLOCKER_MAX, RALPLAN_STOP_BLOCKER_TTL_MS, RALPLAN_ACTIVE_AGENT_RECENCY_WINDOW_MS;
|
|
25953
|
+
var import_fs49, import_path56, CANCEL_SIGNAL_TTL_MS2, STALE_STATE_THRESHOLD_MS, PENDING_ASYNC_STATE_STALE_MS, todoContinuationAttempts, TRANSCRIPT_TAIL_BYTES, CRITICAL_CONTEXT_STOP_PERCENT, RALPLAN_TERMINAL_PHASES, REVIEWER_TASK_TOOL_NAMES, REVIEWER_COMMAND_TOOL_NAMES, AWAITING_CONFIRMATION_TTL_MS, TEAM_PIPELINE_STOP_BLOCKER_MAX, TEAM_PIPELINE_STOP_BLOCKER_TTL_MS, RALPLAN_STOP_BLOCKER_MAX, RALPLAN_STOP_BLOCKER_TTL_MS, RALPLAN_ACTIVE_AGENT_RECENCY_WINDOW_MS;
|
|
25830
25954
|
var init_persistent_mode = __esm({
|
|
25831
25955
|
"src/hooks/persistent-mode/index.ts"() {
|
|
25832
25956
|
"use strict";
|
|
@@ -25850,6 +25974,7 @@ var init_persistent_mode = __esm({
|
|
|
25850
25974
|
init_mode_registry();
|
|
25851
25975
|
CANCEL_SIGNAL_TTL_MS2 = 3e4;
|
|
25852
25976
|
STALE_STATE_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
|
|
25977
|
+
PENDING_ASYNC_STATE_STALE_MS = 24 * 60 * 60 * 1e3;
|
|
25853
25978
|
todoContinuationAttempts = /* @__PURE__ */ new Map();
|
|
25854
25979
|
TRANSCRIPT_TAIL_BYTES = 32 * 1024;
|
|
25855
25980
|
CRITICAL_CONTEXT_STOP_PERCENT = 95;
|
|
@@ -39058,7 +39183,7 @@ async function captureWorkerPane(paneId) {
|
|
|
39058
39183
|
return "";
|
|
39059
39184
|
}
|
|
39060
39185
|
}
|
|
39061
|
-
function
|
|
39186
|
+
function isFreshTimestamp2(value, maxAgeMs = MONITOR_SIGNAL_STALE_MS) {
|
|
39062
39187
|
if (!value) return false;
|
|
39063
39188
|
const parsed = Date.parse(value);
|
|
39064
39189
|
if (!Number.isFinite(parsed)) return false;
|
|
@@ -40067,8 +40192,8 @@ async function monitorTeamV2(teamName, cwd) {
|
|
|
40067
40192
|
}
|
|
40068
40193
|
}
|
|
40069
40194
|
const paneSuggestsIdle = alive && paneLooksReady(paneCapture) && !paneHasActiveTask(paneCapture);
|
|
40070
|
-
const statusFresh =
|
|
40071
|
-
const heartbeatFresh =
|
|
40195
|
+
const statusFresh = isFreshTimestamp2(status.updated_at);
|
|
40196
|
+
const heartbeatFresh = isFreshTimestamp2(heartbeat?.last_turn_at);
|
|
40072
40197
|
const hasWorkStartEvidence = expectedTaskId !== "" && hasWorkerStatusProgress(status, expectedTaskId);
|
|
40073
40198
|
const missingDependencyIds = outstandingTask ? getMissingDependencyIds(outstandingTask, taskById) : [];
|
|
40074
40199
|
let stallReason = null;
|
|
@@ -84028,6 +84153,7 @@ var TEAM_STAGE_ALIASES = {
|
|
|
84028
84153
|
fixing: "team-fix"
|
|
84029
84154
|
};
|
|
84030
84155
|
var BACKGROUND_AGENT_ID_PATTERN = /agentId:\s*([a-zA-Z0-9_-]+)/;
|
|
84156
|
+
var BACKGROUND_BASH_ID_PATTERN = /(?:background (?:bash )?(?:command|process|task).*?(?:id|ID)|bash_id|task_id)[:=]\s*([a-zA-Z0-9_-]+)/i;
|
|
84031
84157
|
var TASK_OUTPUT_ID_PATTERN = /<task_id>([^<]+)<\/task_id>/i;
|
|
84032
84158
|
var TASK_OUTPUT_STATUS_PATTERN = /<status>([^<]+)<\/status>/i;
|
|
84033
84159
|
var SAFE_SESSION_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/;
|
|
@@ -84236,6 +84362,19 @@ function extractAsyncAgentId(toolOutput) {
|
|
|
84236
84362
|
}
|
|
84237
84363
|
return toolOutput.match(BACKGROUND_AGENT_ID_PATTERN)?.[1];
|
|
84238
84364
|
}
|
|
84365
|
+
function extractBackgroundBashId(toolOutput) {
|
|
84366
|
+
if (typeof toolOutput !== "string") {
|
|
84367
|
+
return void 0;
|
|
84368
|
+
}
|
|
84369
|
+
return toolOutput.match(BACKGROUND_BASH_ID_PATTERN)?.[1];
|
|
84370
|
+
}
|
|
84371
|
+
function bashLaunchIsBackgroundPending(toolOutput) {
|
|
84372
|
+
if (typeof toolOutput !== "string") {
|
|
84373
|
+
return false;
|
|
84374
|
+
}
|
|
84375
|
+
const normalized = toolOutput.toLowerCase();
|
|
84376
|
+
return normalized.includes("running in the background") || normalized.includes("started in the background") || normalized.includes("background command") || normalized.includes("background process") || Boolean(extractBackgroundBashId(toolOutput));
|
|
84377
|
+
}
|
|
84239
84378
|
function parseTaskOutputLifecycle(toolOutput) {
|
|
84240
84379
|
if (typeof toolOutput !== "string") {
|
|
84241
84380
|
return null;
|
|
@@ -84257,6 +84396,62 @@ function taskLaunchDidFail(toolOutput) {
|
|
|
84257
84396
|
const normalized = toolOutput.toLowerCase();
|
|
84258
84397
|
return normalized.includes("error") || normalized.includes("failed");
|
|
84259
84398
|
}
|
|
84399
|
+
function getSessionStateDir2(directory, sessionId) {
|
|
84400
|
+
const stateDir = (0, import_path90.join)(getOmcRoot(directory), "state");
|
|
84401
|
+
if (sessionId && SAFE_SESSION_ID_PATTERN.test(sessionId)) {
|
|
84402
|
+
return (0, import_path90.join)(stateDir, "sessions", sessionId);
|
|
84403
|
+
}
|
|
84404
|
+
return stateDir;
|
|
84405
|
+
}
|
|
84406
|
+
function getScheduledWakeupStatePath(directory, sessionId) {
|
|
84407
|
+
return (0, import_path90.join)(getSessionStateDir2(directory, sessionId), "scheduled-wakeup-state.json");
|
|
84408
|
+
}
|
|
84409
|
+
function parseWakeupDueAt(toolInput) {
|
|
84410
|
+
if (!toolInput || typeof toolInput !== "object") {
|
|
84411
|
+
return void 0;
|
|
84412
|
+
}
|
|
84413
|
+
const input = toolInput;
|
|
84414
|
+
const absolute = input.due_at ?? input.wakeup_at ?? input.scheduled_for ?? input.deadline_at ?? input.at;
|
|
84415
|
+
if (typeof absolute === "string") {
|
|
84416
|
+
const parsed = new Date(absolute).getTime();
|
|
84417
|
+
if (Number.isFinite(parsed)) return new Date(parsed).toISOString();
|
|
84418
|
+
}
|
|
84419
|
+
const delaySeconds = input.seconds ?? input.delay_seconds ?? input.delaySeconds;
|
|
84420
|
+
if (typeof delaySeconds === "number" && Number.isFinite(delaySeconds)) {
|
|
84421
|
+
return new Date(Date.now() + Math.max(0, delaySeconds) * 1e3).toISOString();
|
|
84422
|
+
}
|
|
84423
|
+
const delayMs = input.milliseconds ?? input.delay_ms ?? input.delayMs;
|
|
84424
|
+
if (typeof delayMs === "number" && Number.isFinite(delayMs)) {
|
|
84425
|
+
return new Date(Date.now() + Math.max(0, delayMs)).toISOString();
|
|
84426
|
+
}
|
|
84427
|
+
const delayMinutes = input.minutes ?? input.delay_minutes ?? input.delayMinutes;
|
|
84428
|
+
if (typeof delayMinutes === "number" && Number.isFinite(delayMinutes)) {
|
|
84429
|
+
return new Date(Date.now() + Math.max(0, delayMinutes) * 6e4).toISOString();
|
|
84430
|
+
}
|
|
84431
|
+
return void 0;
|
|
84432
|
+
}
|
|
84433
|
+
function recordScheduledWakeup(directory, sessionId, toolInput) {
|
|
84434
|
+
try {
|
|
84435
|
+
const statePath = getScheduledWakeupStatePath(directory, sessionId);
|
|
84436
|
+
(0, import_fs75.mkdirSync)((0, import_path90.dirname)(statePath), { recursive: true });
|
|
84437
|
+
(0, import_fs75.writeFileSync)(
|
|
84438
|
+
statePath,
|
|
84439
|
+
JSON.stringify(
|
|
84440
|
+
{
|
|
84441
|
+
active: true,
|
|
84442
|
+
pending: true,
|
|
84443
|
+
status: "pending",
|
|
84444
|
+
session_id: sessionId,
|
|
84445
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
84446
|
+
due_at: parseWakeupDueAt(toolInput)
|
|
84447
|
+
},
|
|
84448
|
+
null,
|
|
84449
|
+
2
|
|
84450
|
+
)
|
|
84451
|
+
);
|
|
84452
|
+
} catch {
|
|
84453
|
+
}
|
|
84454
|
+
}
|
|
84260
84455
|
function getModeStatePaths(directory, modeName, sessionId) {
|
|
84261
84456
|
const stateDir = (0, import_path90.join)(getOmcRoot(directory), "state");
|
|
84262
84457
|
const safeSessionId = typeof sessionId === "string" && SAFE_SESSION_ID_PATTERN.test(sessionId) ? sessionId : void 0;
|
|
@@ -85586,6 +85781,22 @@ Command blocked: ${command}`
|
|
|
85586
85781
|
);
|
|
85587
85782
|
}
|
|
85588
85783
|
}
|
|
85784
|
+
if (input.toolName === "Bash") {
|
|
85785
|
+
const toolInput = modifiedToolInput ?? input.toolInput;
|
|
85786
|
+
if (toolInput?.run_in_background === true && toolInput.command) {
|
|
85787
|
+
const taskId = getHookToolUseId(input) ?? `bash-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
85788
|
+
addBackgroundTask(
|
|
85789
|
+
taskId,
|
|
85790
|
+
toolInput.command,
|
|
85791
|
+
"bash",
|
|
85792
|
+
directory,
|
|
85793
|
+
input.sessionId
|
|
85794
|
+
);
|
|
85795
|
+
}
|
|
85796
|
+
}
|
|
85797
|
+
if ((input.toolName || "").toLowerCase() === "schedulewakeup") {
|
|
85798
|
+
recordScheduledWakeup(directory, input.sessionId, input.toolInput);
|
|
85799
|
+
}
|
|
85589
85800
|
if (input.toolName === "Edit" || input.toolName === "Write") {
|
|
85590
85801
|
const toolInput = input.toolInput;
|
|
85591
85802
|
if (toolInput?.file_path && input.sessionId) {
|
|
@@ -85726,6 +85937,40 @@ async function processPostToolUse(input) {
|
|
|
85726
85937
|
}
|
|
85727
85938
|
}
|
|
85728
85939
|
}
|
|
85940
|
+
if (input.toolName === "Bash") {
|
|
85941
|
+
const toolInput = input.toolInput;
|
|
85942
|
+
if (toolInput?.run_in_background === true) {
|
|
85943
|
+
const toolUseId = getHookToolUseId(input);
|
|
85944
|
+
const backgroundBashId = extractBackgroundBashId(input.toolOutput);
|
|
85945
|
+
const command = toolInput.command;
|
|
85946
|
+
if (backgroundBashId) {
|
|
85947
|
+
if (toolUseId) {
|
|
85948
|
+
remapBackgroundTaskId(toolUseId, backgroundBashId, directory, input.sessionId);
|
|
85949
|
+
} else if (command) {
|
|
85950
|
+
remapMostRecentMatchingBackgroundTaskId(
|
|
85951
|
+
command,
|
|
85952
|
+
backgroundBashId,
|
|
85953
|
+
directory,
|
|
85954
|
+
"bash",
|
|
85955
|
+
input.sessionId
|
|
85956
|
+
);
|
|
85957
|
+
}
|
|
85958
|
+
} else if (!bashLaunchIsBackgroundPending(input.toolOutput)) {
|
|
85959
|
+
const failed = taskLaunchDidFail(input.toolOutput);
|
|
85960
|
+
if (toolUseId) {
|
|
85961
|
+
completeBackgroundTask(toolUseId, directory, failed, input.sessionId);
|
|
85962
|
+
} else if (command) {
|
|
85963
|
+
completeMostRecentMatchingBackgroundTask(
|
|
85964
|
+
command,
|
|
85965
|
+
directory,
|
|
85966
|
+
failed,
|
|
85967
|
+
"bash",
|
|
85968
|
+
input.sessionId
|
|
85969
|
+
);
|
|
85970
|
+
}
|
|
85971
|
+
}
|
|
85972
|
+
}
|
|
85973
|
+
}
|
|
85729
85974
|
if (isDelegationToolName2(input.toolName)) {
|
|
85730
85975
|
const dashboard = getAgentDashboard(directory);
|
|
85731
85976
|
if (dashboard) {
|
|
@@ -119,8 +119,8 @@ Advanced: ambiguity ≤ 20%
|
|
|
119
119
|
writeFileSync(join(tempConfigDir, 'skills', 'deep-interview', 'SKILL.md'), `---
|
|
120
120
|
name: deep-interview
|
|
121
121
|
description: Deep interview
|
|
122
|
-
pipeline: [deep-interview,
|
|
123
|
-
next-skill:
|
|
122
|
+
pipeline: [deep-interview, plan, autopilot]
|
|
123
|
+
next-skill: plan
|
|
124
124
|
next-skill-args: --consensus --direct
|
|
125
125
|
handoff: .omcp/specs/deep-interview-{slug}.md
|
|
126
126
|
---
|
|
@@ -134,9 +134,9 @@ Deep interview body`);
|
|
|
134
134
|
});
|
|
135
135
|
expect(result.success).toBe(true);
|
|
136
136
|
expect(result.replacementText).toContain('## Skill Pipeline');
|
|
137
|
-
expect(result.replacementText).toContain('Pipeline: `deep-interview →
|
|
137
|
+
expect(result.replacementText).toContain('Pipeline: `deep-interview → plan → autopilot`');
|
|
138
138
|
expect(result.replacementText).toContain('Next skill arguments: `--consensus --direct`');
|
|
139
|
-
expect(result.replacementText).toContain('Skill("oh-my-copilot:
|
|
139
|
+
expect(result.replacementText).toContain('Skill("oh-my-copilot:plan")');
|
|
140
140
|
expect(result.replacementText).toContain('`.omcp/specs/deep-interview-{slug}.md`');
|
|
141
141
|
});
|
|
142
142
|
it.skip('discovers project-local compatibility skills from .agents/skills', async () => {
|
|
@@ -166,8 +166,8 @@ Compatibility body`);
|
|
|
166
166
|
writeFileSync(join(tempConfigDir, 'skills', 'deep-interview', 'SKILL.md'), `---
|
|
167
167
|
name: deep-interview
|
|
168
168
|
description: Deep interview
|
|
169
|
-
pipeline: [deep-interview,
|
|
170
|
-
next-skill:
|
|
169
|
+
pipeline: [deep-interview, plan, autopilot]
|
|
170
|
+
next-skill: plan
|
|
171
171
|
next-skill-args: --consensus --direct
|
|
172
172
|
handoff: .omcp/specs/deep-interview-{slug}.md
|
|
173
173
|
---
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-slash-aliases.test.js","sourceRoot":"","sources":["../../src/__tests__/auto-slash-aliases.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auto-slash-aliases.test.js","sourceRoot":"","sources":["../../src/__tests__/auto-slash-aliases.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,6 +21,7 @@ vi.mock('fs', async () => {
|
|
|
21
21
|
const actual = await vi.importActual('fs');
|
|
22
22
|
return {
|
|
23
23
|
...actual,
|
|
24
|
+
cpSync: vi.fn(),
|
|
24
25
|
existsSync: vi.fn(),
|
|
25
26
|
mkdirSync: vi.fn(),
|
|
26
27
|
readFileSync: vi.fn(),
|
|
@@ -28,12 +29,13 @@ vi.mock('fs', async () => {
|
|
|
28
29
|
};
|
|
29
30
|
});
|
|
30
31
|
import { execSync } from 'child_process';
|
|
31
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
32
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
32
33
|
import { homedir } from 'os';
|
|
33
34
|
import { join } from 'path';
|
|
34
35
|
import { install, isProjectScopedPlugin, isRunningAsPlugin, checkNodeVersion } from '../installer/index.js';
|
|
35
|
-
import { reconcileUpdateRuntime, performUpdate, } from '../features/auto-update.js';
|
|
36
|
+
import { reconcileUpdateRuntime, performUpdate, fetchLatestRelease, } from '../features/auto-update.js';
|
|
36
37
|
const mockedExecSync = vi.mocked(execSync);
|
|
38
|
+
const mockedCpSync = vi.mocked(cpSync);
|
|
37
39
|
const mockedExistsSync = vi.mocked(existsSync);
|
|
38
40
|
const mockedMkdirSync = vi.mocked(mkdirSync);
|
|
39
41
|
const mockedReadFileSync = vi.mocked(readFileSync);
|
|
@@ -42,9 +44,21 @@ const mockedInstall = vi.mocked(install);
|
|
|
42
44
|
const mockedIsProjectScopedPlugin = vi.mocked(isProjectScopedPlugin);
|
|
43
45
|
const mockedIsRunningAsPlugin = vi.mocked(isRunningAsPlugin);
|
|
44
46
|
const mockedCheckNodeVersion = vi.mocked(checkNodeVersion);
|
|
47
|
+
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
|
|
48
|
+
const originalGhToken = process.env.GH_TOKEN;
|
|
49
|
+
const originalGithubToken = process.env.GITHUB_TOKEN;
|
|
50
|
+
function mockPlatform(platform) {
|
|
51
|
+
Object.defineProperty(process, 'platform', {
|
|
52
|
+
configurable: true,
|
|
53
|
+
value: platform,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
45
56
|
describe('auto-update reconciliation', () => {
|
|
46
57
|
beforeEach(() => {
|
|
47
58
|
vi.clearAllMocks();
|
|
59
|
+
delete process.env.GH_TOKEN;
|
|
60
|
+
delete process.env.GITHUB_TOKEN;
|
|
61
|
+
mockedCpSync.mockImplementation(() => undefined);
|
|
48
62
|
mockedExistsSync.mockReturnValue(true);
|
|
49
63
|
mockedIsProjectScopedPlugin.mockReturnValue(false);
|
|
50
64
|
// Default: running as plugin so forceHooks/refreshHooksInPlugin logic works
|
|
@@ -78,6 +92,122 @@ describe('auto-update reconciliation', () => {
|
|
|
78
92
|
afterEach(() => {
|
|
79
93
|
vi.unstubAllGlobals();
|
|
80
94
|
delete process.env.CLAUDE_PLUGIN_ROOT;
|
|
95
|
+
if (originalGhToken === undefined) {
|
|
96
|
+
delete process.env.GH_TOKEN;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
process.env.GH_TOKEN = originalGhToken;
|
|
100
|
+
}
|
|
101
|
+
if (originalGithubToken === undefined) {
|
|
102
|
+
delete process.env.GITHUB_TOKEN;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
process.env.GITHUB_TOKEN = originalGithubToken;
|
|
106
|
+
}
|
|
107
|
+
if (originalPlatformDescriptor) {
|
|
108
|
+
Object.defineProperty(process, 'platform', originalPlatformDescriptor);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
it('fetches latest release without Authorization when no GitHub token is configured', async () => {
|
|
112
|
+
const release = {
|
|
113
|
+
tag_name: 'v4.1.5',
|
|
114
|
+
name: '4.1.5',
|
|
115
|
+
published_at: '2026-02-09T00:00:00.000Z',
|
|
116
|
+
html_url: 'https://example.com/release',
|
|
117
|
+
body: 'notes',
|
|
118
|
+
prerelease: false,
|
|
119
|
+
draft: false,
|
|
120
|
+
};
|
|
121
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
122
|
+
ok: true,
|
|
123
|
+
status: 200,
|
|
124
|
+
statusText: 'OK',
|
|
125
|
+
json: async () => release,
|
|
126
|
+
});
|
|
127
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
128
|
+
await expect(fetchLatestRelease()).resolves.toEqual(release);
|
|
129
|
+
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/releases/latest'), {
|
|
130
|
+
headers: {
|
|
131
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
132
|
+
'User-Agent': 'oh-my-copilot-updater',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
it('uses GITHUB_TOKEN for latest release requests when GH_TOKEN is absent', async () => {
|
|
137
|
+
process.env.GITHUB_TOKEN = 'github-token-value';
|
|
138
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
139
|
+
ok: true,
|
|
140
|
+
status: 200,
|
|
141
|
+
statusText: 'OK',
|
|
142
|
+
json: async () => ({
|
|
143
|
+
tag_name: 'v4.1.5',
|
|
144
|
+
name: '4.1.5',
|
|
145
|
+
published_at: '2026-02-09T00:00:00.000Z',
|
|
146
|
+
html_url: 'https://example.com/release',
|
|
147
|
+
body: 'notes',
|
|
148
|
+
prerelease: false,
|
|
149
|
+
draft: false,
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
153
|
+
await fetchLatestRelease();
|
|
154
|
+
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/releases/latest'), expect.objectContaining({
|
|
155
|
+
headers: expect.objectContaining({
|
|
156
|
+
Authorization: 'Bearer github-token-value',
|
|
157
|
+
}),
|
|
158
|
+
}));
|
|
159
|
+
});
|
|
160
|
+
it('prefers GH_TOKEN over GITHUB_TOKEN for latest release requests', async () => {
|
|
161
|
+
process.env.GH_TOKEN = 'gh-token-value';
|
|
162
|
+
process.env.GITHUB_TOKEN = 'github-token-value';
|
|
163
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
164
|
+
ok: true,
|
|
165
|
+
status: 200,
|
|
166
|
+
statusText: 'OK',
|
|
167
|
+
json: async () => ({
|
|
168
|
+
tag_name: 'v4.1.5',
|
|
169
|
+
name: '4.1.5',
|
|
170
|
+
published_at: '2026-02-09T00:00:00.000Z',
|
|
171
|
+
html_url: 'https://example.com/release',
|
|
172
|
+
body: 'notes',
|
|
173
|
+
prerelease: false,
|
|
174
|
+
draft: false,
|
|
175
|
+
}),
|
|
176
|
+
});
|
|
177
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
178
|
+
await fetchLatestRelease();
|
|
179
|
+
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/releases/latest'), expect.objectContaining({
|
|
180
|
+
headers: expect.objectContaining({
|
|
181
|
+
Authorization: 'Bearer gh-token-value',
|
|
182
|
+
}),
|
|
183
|
+
}));
|
|
184
|
+
});
|
|
185
|
+
it('adds a helpful rate-limit hint for unauthenticated 403 release responses', async () => {
|
|
186
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
187
|
+
ok: false,
|
|
188
|
+
status: 403,
|
|
189
|
+
statusText: 'Forbidden',
|
|
190
|
+
headers: new Headers({
|
|
191
|
+
'x-ratelimit-remaining': '0',
|
|
192
|
+
'x-ratelimit-reset': '1893456000',
|
|
193
|
+
}),
|
|
194
|
+
text: async () => JSON.stringify({ message: 'API rate limit exceeded' }),
|
|
195
|
+
}));
|
|
196
|
+
await expect(fetchLatestRelease()).rejects.toThrow(/GitHub API rate limit exceeded.*Set GH_TOKEN or GITHUB_TOKEN.*2030-01-01T00:00:00.000Z/);
|
|
197
|
+
});
|
|
198
|
+
it('does not leak a configured token in token-authenticated 403 errors', async () => {
|
|
199
|
+
process.env.GH_TOKEN = 'super-secret-token';
|
|
200
|
+
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
|
201
|
+
ok: false,
|
|
202
|
+
status: 403,
|
|
203
|
+
statusText: 'Forbidden',
|
|
204
|
+
headers: new Headers({
|
|
205
|
+
'x-ratelimit-remaining': '0',
|
|
206
|
+
}),
|
|
207
|
+
text: async () => JSON.stringify({ message: 'API rate limit exceeded' }),
|
|
208
|
+
}));
|
|
209
|
+
await expect(fetchLatestRelease()).rejects.toThrow(/configured GitHub token appears to be rate limited/);
|
|
210
|
+
await expect(fetchLatestRelease()).rejects.not.toThrow(/super-secret-token/);
|
|
81
211
|
});
|
|
82
212
|
it('reconciles runtime state without re-injecting settings hooks', () => {
|
|
83
213
|
mockedExistsSync.mockReturnValue(false);
|