fix: resolve 25 pre-publish blockers

- postinstall.mjs: fix alias package detection
- migrate-legacy-plugin-entry: dedupe + regression tests
- task_system: default consistency across runtime paths
- task() contract: consistent tool behavior
- runtime model selection, tool cap, stale-task cancellation
- recovery sanitization, context-limit gating
- Ralph semantic DONE hardening, Atlas fallback persistence
- native-skill description/content, skill path traversal guard
- publish workflow: platform awaited via reusable workflow job
- release: version edits reapplied before commit/tag
- JSONC plugin migration: top-level plugin key safety
- cold-cache: user fallback models skip disconnected providers
- docs/version/release framing updates

Verified: bun test (4599 pass), tsc --noEmit clean, bun run build clean
This commit is contained in:
YeonGyu-Kim
2026-03-28 15:24:18 +09:00
parent 44b039bef6
commit d2c576c510
62 changed files with 1264 additions and 292 deletions
+28 -2
View File
@@ -12,7 +12,6 @@ import {
import type { BoulderState } from "../../features/boulder-state"
import * as sessionState from "../../features/claude-code-session-state"
import * as worktreeDetector from "./worktree-detector"
import * as worktreeDetector from "./worktree-detector"
describe("start-work hook", () => {
let testDir: string
@@ -26,6 +25,9 @@ describe("start-work hook", () => {
}
beforeEach(() => {
sessionState._resetForTesting()
sessionState.registerAgentName("atlas")
sessionState.registerAgentName("sisyphus")
testDir = join(tmpdir(), `start-work-test-${randomUUID()}`)
sisyphusDir = join(testDir, ".sisyphus")
if (!existsSync(testDir)) {
@@ -38,6 +40,7 @@ describe("start-work hook", () => {
})
afterEach(() => {
sessionState._resetForTesting()
clearBoulderState(testDir)
if (existsSync(testDir)) {
rmSync(testDir, { recursive: true, force: true })
@@ -409,7 +412,7 @@ describe("start-work hook", () => {
// given
const hook = createStartWorkHook(createMockPluginInput())
const output = {
message: {},
message: {} as Record<string, unknown>,
parts: [{ type: "text", text: "<session-context></session-context>" }],
}
@@ -422,6 +425,29 @@ describe("start-work hook", () => {
// then
expect(output.message.agent).toBe("Atlas (Plan Executor)")
})
test("should keep the current agent when Atlas is unavailable", async () => {
// given
sessionState._resetForTesting()
sessionState.registerAgentName("sisyphus")
sessionState.updateSessionAgent("ses-prometheus-to-sisyphus", "sisyphus")
const hook = createStartWorkHook(createMockPluginInput())
const output = {
message: {} as Record<string, unknown>,
parts: [{ type: "text", text: "<session-context></session-context>" }],
}
// when
await hook["chat.message"](
{ sessionID: "ses-prometheus-to-sisyphus" },
output
)
// then
expect(output.message.agent).toBe("Sisyphus (Ultraworker)")
expect(sessionState.getSessionAgent("ses-prometheus-to-sisyphus")).toBe("sisyphus")
})
})
describe("worktree support", () => {
+10 -11
View File
@@ -12,7 +12,7 @@ import {
} from "../../features/boulder-state"
import { log } from "../../shared/logger"
import { getAgentDisplayName } from "../../shared/agent-display-names"
import { updateSessionAgent, isAgentRegistered } from "../../features/claude-code-session-state"
import { getSessionAgent, isAgentRegistered, updateSessionAgent } from "../../features/claude-code-session-state"
import { detectWorktreePath } from "./worktree-detector"
import { parseUserRequest } from "./parse-user-request"
@@ -80,14 +80,13 @@ export function createStartWorkHook(ctx: PluginInput) {
if (!promptText.includes("<session-context>")) return
log(`[${HOOK_NAME}] Processing start-work command`, { sessionID: input.sessionID })
const atlasDisplayName = getAgentDisplayName("atlas")
if (isAgentRegistered("atlas") || isAgentRegistered(atlasDisplayName)) {
updateSessionAgent(input.sessionID, "atlas")
if (output.message) {
output.message["agent"] = atlasDisplayName
}
} else {
log(`[${HOOK_NAME}] Atlas agent not available, continuing with current agent`, { sessionID: input.sessionID })
const activeAgent = isAgentRegistered("atlas")
? "atlas"
: getSessionAgent(input.sessionID) ?? "sisyphus"
const activeAgentDisplayName = getAgentDisplayName(activeAgent)
updateSessionAgent(input.sessionID, activeAgent)
if (output.message) {
output.message["agent"] = activeAgentDisplayName
}
const existingState = readBoulderState(ctx.directory)
@@ -116,7 +115,7 @@ The requested plan "${getPlanName(matchedPlan)}" has been completed.
All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`
} else {
if (existingState) clearBoulderState(ctx.directory)
const newState = createBoulderState(matchedPlan, sessionId, "atlas", worktreePath)
const newState = createBoulderState(matchedPlan, sessionId, activeAgent, worktreePath)
writeBoulderState(ctx.directory, newState)
contextInfo = `
@@ -223,7 +222,7 @@ All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your ta
} else if (incompletePlans.length === 1) {
const planPath = incompletePlans[0]
const progress = getPlanProgress(planPath)
const newState = createBoulderState(planPath, sessionId, "atlas", worktreePath)
const newState = createBoulderState(planPath, sessionId, activeAgent, worktreePath)
writeBoulderState(ctx.directory, newState)
contextInfo += `