Compare commits

..

1 Commits

Author SHA1 Message Date
YeonGyu-Kim
ef8f22caba fix(boulder-state): treat plans without checkboxes as incomplete (fixes #2648)
GPT/Gemini Prometheus plans sometimes lack markdown checkboxes.
Previously getPlanProgress() returned isComplete=true for 0/0,
causing /start-work to skip Atlas execution.

Now total=0 correctly returns isComplete=false so start-work
detects the invalid plan format.

🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:06:24 +09:00
7 changed files with 9 additions and 44 deletions

View File

@@ -9,11 +9,8 @@ import { loadAvailableModelsFromCache } from "./model-resolution-cache"
import { getModelResolutionInfoWithOverrides } from "./model-resolution"
import type { OmoConfig } from "./model-resolution-types"
const PACKAGE_NAME_ALT = "oh-my-openagent"
const USER_CONFIG_BASE = join(getOpenCodeConfigDir({ binary: "opencode" }), PACKAGE_NAME)
const USER_CONFIG_BASE_ALT = join(getOpenCodeConfigDir({ binary: "opencode" }), PACKAGE_NAME_ALT)
const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME)
const PROJECT_CONFIG_BASE_ALT = join(process.cwd(), ".opencode", PACKAGE_NAME_ALT)
interface ConfigValidationResult {
exists: boolean
@@ -27,15 +24,9 @@ function findConfigPath(): string | null {
const projectConfig = detectConfigFile(PROJECT_CONFIG_BASE)
if (projectConfig.format !== "none") return projectConfig.path
const projectConfigAlt = detectConfigFile(PROJECT_CONFIG_BASE_ALT)
if (projectConfigAlt.format !== "none") return projectConfigAlt.path
const userConfig = detectConfigFile(USER_CONFIG_BASE)
if (userConfig.format !== "none") return userConfig.path
const userConfigAlt = detectConfigFile(USER_CONFIG_BASE_ALT)
if (userConfigAlt.format !== "none") return userConfigAlt.path
return null
}

View File

@@ -351,7 +351,7 @@ describe("boulder-state", () => {
expect(progress.isComplete).toBe(true)
})
test("should return isComplete true for empty plan", () => {
test("should return isComplete false for plan with content but no checkboxes", () => {
// given - plan with no checkboxes
const planPath = join(TEST_DIR, "empty-plan.md")
writeFileSync(planPath, "# Plan\nNo tasks here")
@@ -361,7 +361,7 @@ describe("boulder-state", () => {
// then
expect(progress.total).toBe(0)
expect(progress.isComplete).toBe(true)
expect(progress.isComplete).toBe(false)
})
test("should handle non-existent file", () => {

View File

@@ -133,7 +133,7 @@ export function getPlanProgress(planPath: string): PlanProgress {
return {
total,
completed,
isComplete: total === 0 || completed === total,
isComplete: total > 0 && completed === total,
}
} catch {
return { total: 0, completed: 0, isComplete: true }

View File

@@ -160,32 +160,18 @@ export function loadPluginConfig(
directory: string,
ctx: unknown
): OhMyOpenCodeConfig {
// User-level config path - prefer .jsonc over .json, try oh-my-openagent as fallback
// User-level config path - prefer .jsonc over .json
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
const userBasePath = path.join(configDir, "oh-my-opencode");
let userDetected = detectConfigFile(userBasePath);
if (userDetected.format === "none") {
const altUserBasePath = path.join(configDir, "oh-my-openagent");
const altDetected = detectConfigFile(altUserBasePath);
if (altDetected.format !== "none") {
userDetected = altDetected;
}
}
const userDetected = detectConfigFile(userBasePath);
const userConfigPath =
userDetected.format !== "none"
? userDetected.path
: userBasePath + ".json";
// Project-level config path - prefer .jsonc over .json, try oh-my-openagent as fallback
// Project-level config path - prefer .jsonc over .json
const projectBasePath = path.join(directory, ".opencode", "oh-my-opencode");
let projectDetected = detectConfigFile(projectBasePath);
if (projectDetected.format === "none") {
const altProjectBasePath = path.join(directory, ".opencode", "oh-my-openagent");
const altDetected = detectConfigFile(altProjectBasePath);
if (altDetected.format !== "none") {
projectDetected = altDetected;
}
}
const projectDetected = detectConfigFile(projectBasePath);
const projectConfigPath =
projectDetected.format !== "none"
? projectDetected.path

View File

@@ -12,5 +12,4 @@ export type OpenCodeConfigPaths = {
configJsonc: string
packageJson: string
omoConfig: string
omoConfigAlt: string
}

View File

@@ -84,7 +84,6 @@ export function getOpenCodeConfigPaths(options: OpenCodeConfigDirOptions): OpenC
configJsonc: join(configDir, "opencode.jsonc"),
packageJson: join(configDir, "package.json"),
omoConfig: join(configDir, "oh-my-opencode.json"),
omoConfigAlt: join(configDir, "oh-my-openagent.json"),
}
}

View File

@@ -37,19 +37,9 @@ export function loadJsonFile<T>(path: string): T | null {
export function getConfigPaths(): { project: string; user: string; opencode: string } {
const cwd = process.cwd()
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
const projectDetected = detectConfigFile(join(cwd, ".opencode", "oh-my-opencode"))
const projectPath = projectDetected.format !== "none"
? projectDetected.path
: detectConfigFile(join(cwd, ".opencode", "oh-my-openagent")).path
const userDetected = detectConfigFile(join(configDir, "oh-my-opencode"))
const userPath = userDetected.format !== "none"
? userDetected.path
: detectConfigFile(join(configDir, "oh-my-openagent")).path
return {
project: projectPath,
user: userPath,
project: detectConfigFile(join(cwd, ".opencode", "oh-my-opencode")).path,
user: detectConfigFile(join(configDir, "oh-my-opencode")).path,
opencode: detectConfigFile(join(configDir, "opencode")).path,
}
}