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
+55 -2
View File
@@ -1,8 +1,54 @@
import { existsSync, readFileSync, writeFileSync } from "node:fs"
import { applyEdits, modify } from "jsonc-parser"
import { parseJsoncSafe } from "./jsonc-parser"
import { log } from "./logger"
import { LEGACY_PLUGIN_NAME, PLUGIN_NAME } from "./plugin-identity"
interface OpenCodeConfig {
plugin?: string[]
}
function isLegacyEntry(entry: string): boolean {
return entry === LEGACY_PLUGIN_NAME || entry.startsWith(`${LEGACY_PLUGIN_NAME}@`)
}
function isCanonicalEntry(entry: string): boolean {
return entry === PLUGIN_NAME || entry.startsWith(`${PLUGIN_NAME}@`)
}
function toCanonicalEntry(entry: string): string {
if (entry === LEGACY_PLUGIN_NAME) return PLUGIN_NAME
if (entry.startsWith(`${LEGACY_PLUGIN_NAME}@`)) {
return `${PLUGIN_NAME}${entry.slice(LEGACY_PLUGIN_NAME.length)}`
}
return entry
}
function normalizePluginEntries(entries: string[]): string[] {
const hasCanonical = entries.some(isCanonicalEntry)
if (hasCanonical) {
return entries.filter((entry) => !isLegacyEntry(entry))
}
return entries.map((entry) => (isLegacyEntry(entry) ? toCanonicalEntry(entry) : entry))
}
function updateJsoncPluginArray(content: string, pluginEntries: string[]): string | null {
const edits = modify(content, ["plugin"], pluginEntries, {
formattingOptions: {
insertSpaces: true,
tabSize: 2,
eol: "\n",
},
getInsertionIndex: () => 0,
})
if (edits.length === 0) return null
return applyEdits(content, edits)
}
export function migrateLegacyPluginEntry(configPath: string): boolean {
if (!existsSync(configPath)) return false
@@ -10,8 +56,15 @@ export function migrateLegacyPluginEntry(configPath: string): boolean {
const content = readFileSync(configPath, "utf-8")
if (!content.includes(LEGACY_PLUGIN_NAME)) return false
const updated = content.replaceAll(LEGACY_PLUGIN_NAME, PLUGIN_NAME)
if (updated === content) return false
const parseResult = parseJsoncSafe<OpenCodeConfig>(content)
const pluginEntries = parseResult.data?.plugin
if (!pluginEntries || !pluginEntries.some(isLegacyEntry)) return false
const updatedPluginEntries = normalizePluginEntries(pluginEntries)
const updated = configPath.endsWith(".jsonc")
? updateJsoncPluginArray(content, updatedPluginEntries)
: JSON.stringify({ ...(parseResult.data as OpenCodeConfig), plugin: updatedPluginEntries }, null, 2) + "\n"
if (!updated || updated === content) return false
writeFileSync(configPath, updated, "utf-8")
log("[migrateLegacyPluginEntry] Auto-migrated opencode.json plugin entry", {