easy-coding-harness 0.1.6 → 0.1.7
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/README.md +14 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/common/bundled-skills/ec-meta/references/local-architecture/README.md +5 -0
- package/templates/common/skills/ec-analysis/SKILL.md +62 -33
- package/templates/common/skills/ec-brainstorming/SKILL.md +16 -4
- package/templates/common/skills/ec-task-close/SKILL.md +2 -0
- package/templates/common/skills/ec-task-management/SKILL.md +5 -3
- package/templates/common/skills/ec-workflow/SKILL.md +50 -8
- package/templates/main-constraint/AGENTS.md.tpl +2 -2
- package/templates/main-constraint/CLAUDE.md.tpl +2 -2
- package/templates/shared-hooks/easy_coding_status.py +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,20 @@ easy-coding --version
|
|
|
37
37
|
- `y`:常规功能升级
|
|
38
38
|
- `z`:日常 bug 修复
|
|
39
39
|
|
|
40
|
+
### 0.1.7
|
|
41
|
+
|
|
42
|
+
- ec-workflow 新增意图路由:用户带提示词进入时,自动匹配当前任务和已有任务,不匹配则询问切换或新建。
|
|
43
|
+
- 支持任务切换:可在任何阶段挂起当前任务并切换到另一个任务,挂起任务的数据完整保留。
|
|
44
|
+
- TaskJson 新增 `title` 可选字段,用于意图匹配和任务展示。
|
|
45
|
+
- ec-task-management 任务创建和列表展示支持 title 字段。
|
|
46
|
+
- CLI `easy-coding status` 命令在活跃任务列表中展示任务标题。
|
|
47
|
+
- ec-brainstorming 设计确认后新增入口:询问用户是否立即创建任务,确认后自动衔接 ec-workflow 流程。
|
|
48
|
+
- ec-analysis 新增 spec 文档扫描:自动读取 `.easy-coding/spec/` 下匹配的设计文档作为分析输入。
|
|
49
|
+
- ec-analysis 新增 HARD RULES 强约束:首个 tool call 必须写 dev-spec.md 骨架文件,分析过程逐步回填,回复内容必须是 dev-spec.md 完整内容而非自创缩略格式。
|
|
50
|
+
- 自检门禁增加文件存在性检查(dev-spec.md、execution.jsonl、test-strategy.md)和回复格式检查。
|
|
51
|
+
- 修复 READY_LINE 引导用户使用 `ec-analysis` 启动任务的错误提示,改为 `ec-workflow`。
|
|
52
|
+
- 同步修复 CLAUDE.md.tpl、AGENTS.md.tpl 主约束模板中的状态栏示例。
|
|
53
|
+
|
|
40
54
|
### 0.1.6
|
|
41
55
|
|
|
42
56
|
- ec-analysis 改为模板先行(template-first)工作流:分析阶段先创建 dev-spec.md 骨架,再逐步填充各章节。
|
package/dist/cli.js
CHANGED
|
@@ -911,7 +911,8 @@ async function status() {
|
|
|
911
911
|
if (activeTasks.length > 0) {
|
|
912
912
|
console.log(" active:");
|
|
913
913
|
for (const item of activeTasks) {
|
|
914
|
-
|
|
914
|
+
const label = item.task.title ? `${item.task.title} (${item.task.status})` : item.task.status;
|
|
915
|
+
console.log(` - ${item.id}: ${label}`);
|
|
915
916
|
}
|
|
916
917
|
}
|
|
917
918
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/commands/add-agent.ts","../src/configurators/claude.ts","../src/types/platform.ts","../src/configurators/shared.ts","../src/constants/paths.ts","../src/utils/file-writer.ts","../src/utils/marked-region.ts","../src/utils/template-paths.ts","../src/configurators/codex.ts","../src/configurators/qoder.ts","../src/configurators/index.ts","../src/ui/banner.ts","../src/constants/version.ts","../src/utils/config-yaml.ts","../src/commands/platforms.ts","../src/commands/init.ts","../src/utils/gitignore.ts","../src/utils/install-state.ts","../src/utils/runtime-scaffold.ts","../src/utils/task-json.ts","../src/commands/status.ts","../src/utils/compare-versions.ts","../src/utils/state-json.ts","../src/commands/upgrade.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { addAgent } from \"./commands/add-agent.js\";\nimport { init } from \"./commands/init.js\";\nimport { status } from \"./commands/status.js\";\nimport { upgrade } from \"./commands/upgrade.js\";\nimport { PACKAGE_NAME, VERSION } from \"./constants/version.js\";\nimport { checkForUpgrade } from \"./utils/compare-versions.js\";\n\ntype CommandAction<T> = (opts: T) => Promise<void>;\n\nfunction withErrorHandling<T>(fn: CommandAction<T>) {\n return async (opts: T) => {\n try {\n await fn(opts);\n } catch (error) {\n console.error(chalk.red(\"Error:\"), error instanceof Error ? error.message : error);\n if (process.env.EC_DEBUG && error instanceof Error) {\n console.error(error.stack);\n }\n process.exit(1);\n }\n };\n}\n\nawait checkForUpgrade(process.cwd());\n\nconst program = new Command();\n\nprogram.name(\"easy-coding\").description(PACKAGE_NAME).version(VERSION, \"-v, --version\");\n\nprogram\n .command(\"init\")\n .description(\"Initialize easy-coding harness in current project\")\n .option(\"--agent <list>\", \"Comma-separated platforms: claude-code,codex,qoder\")\n .option(\"-y, --yes\", \"Skip prompts, use defaults\")\n .action(withErrorHandling(init));\n\nprogram\n .command(\"add-agent\")\n .description(\"Add agent platform support to an existing project\")\n .option(\"--agent <list>\", \"Comma-separated platforms to add\")\n .action(withErrorHandling(addAgent));\n\nprogram\n .command(\"upgrade\")\n .description(\"Upgrade harness files to current CLI version\")\n .option(\"--dry-run\", \"Preview changes without applying\")\n .option(\"-y, --yes\", \"Skip confirmation\")\n .action(withErrorHandling(upgrade));\n\nprogram\n .command(\"status\")\n .description(\"Show installed agents, version, and tasks\")\n .action(withErrorHandling(status));\n\nprogram.parse();\n","import path from \"node:path\";\nimport { outro } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { CONFIGURATORS } from \"../configurators/index.js\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { addAgentsToConfig, readConfigYaml } from \"../utils/config-yaml.js\";\nimport { pathExists } from \"../utils/file-writer.js\";\nimport { type PlatformOptions, resolvePlatforms } from \"./platforms.js\";\n\nexport async function addAgent(opts: PlatformOptions): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n throw new Error(\"No .easy-coding/config.yaml found. Run easy-coding init first.\");\n }\n\n const config = await readConfigYaml(configPath);\n const platforms = await resolvePlatforms(opts, [\"claude-code\"]);\n const toInstall = platforms.filter((platform) => !config.agents.includes(platform));\n\n if (toInstall.length === 0) {\n outro(chalk.yellow(\"All selected agent platforms are already installed.\"));\n return;\n }\n\n for (const platform of toInstall) {\n await CONFIGURATORS[platform](cwd);\n }\n await addAgentsToConfig(configPath, toInstall);\n\n outro(chalk.green(`Added agent platforms: ${toInstall.join(\", \")}`));\n}\n","import path from \"node:path\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport {\n copyPlatformTemplates,\n resolveBundledSkills,\n resolveSkills,\n writeMainConstraint,\n writeSharedHooks,\n writeSkills,\n} from \"./shared.js\";\n\nexport async function configureClaude(cwd: string): Promise<void> {\n const platform = \"claude-code\";\n const meta = PLATFORM_META[platform];\n const ctx = meta.templateContext;\n const dest = path.join(cwd, \".claude\");\n\n await copyPlatformTemplates(\"claude\", dest, [\"hooks\"], ctx);\n await writeSharedHooks(path.join(dest, \"hooks\"), platform);\n await writeSkills(\n path.join(cwd, meta.skillsDir),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\n );\n await writeMainConstraint(cwd, platform);\n}\n","export type AgentPlatform = \"claude-code\" | \"codex\" | \"qoder\";\n\nexport interface TemplateContext {\n sub_agent_dispatch: string;\n platform_spawn_instruction: string;\n skill_trigger: \"/\" | \"$\";\n workflow_state_path: string;\n main_constraint_file: \"CLAUDE.md\" | \"AGENTS.md\";\n python_cmd: string;\n platform_config_dir: string;\n}\n\nexport interface PlatformMeta {\n label: string;\n skillsDir: string;\n hooksDir: string;\n hookConfigFile: string;\n agentsDir: string;\n agentFileExt: \".md\" | \".toml\";\n mainConstraint: \"CLAUDE.md\" | \"AGENTS.md\";\n skillTrigger: \"/\" | \"$\";\n hookEvents: string[];\n stateInjectEvent: string[];\n hasSubagentContext: boolean;\n cnVariant?: string;\n templateContext: TemplateContext;\n}\n\nconst pythonCmd = process.platform === \"win32\" ? \"python\" : \"python3\";\n\nexport const PLATFORM_META: Record<AgentPlatform, PlatformMeta> = {\n \"claude-code\": {\n label: \"Claude Code\",\n skillsDir: \".claude/skills\",\n hooksDir: \".claude/hooks\",\n hookConfigFile: \".claude/settings.json\",\n agentsDir: \".claude/agents\",\n agentFileExt: \".md\",\n mainConstraint: \"CLAUDE.md\",\n skillTrigger: \"/\",\n hookEvents: [\"SessionStart\", \"PreToolUse\", \"UserPromptSubmit\", \"Stop\"],\n stateInjectEvent: [\"SessionStart\", \"UserPromptSubmit\"],\n hasSubagentContext: true,\n templateContext: {\n sub_agent_dispatch: \"Agent tool\",\n platform_spawn_instruction:\n 'Use the Agent tool with run_in_background when useful; use isolation: \"worktree\" for parallel file edits.',\n skill_trigger: \"/\",\n workflow_state_path: \".easy-coding/state.json\",\n main_constraint_file: \"CLAUDE.md\",\n python_cmd: pythonCmd,\n platform_config_dir: \".claude\",\n },\n },\n codex: {\n label: \"Codex\",\n skillsDir: \".agents/skills\",\n hooksDir: \".codex/hooks\",\n hookConfigFile: \".codex/hooks.json\",\n agentsDir: \".codex/agents\",\n agentFileExt: \".toml\",\n mainConstraint: \"AGENTS.md\",\n skillTrigger: \"$\",\n hookEvents: [\"UserPromptSubmit\"],\n stateInjectEvent: [\"UserPromptSubmit\"],\n hasSubagentContext: false,\n templateContext: {\n sub_agent_dispatch: \"Codex sub-agent dispatch\",\n platform_spawn_instruction:\n \"Use Codex sub-agent delegation where available; pass the full task card in the prompt.\",\n skill_trigger: \"$\",\n workflow_state_path: \".easy-coding/state.json\",\n main_constraint_file: \"AGENTS.md\",\n python_cmd: pythonCmd,\n platform_config_dir: \".codex\",\n },\n },\n qoder: {\n label: \"Qoder\",\n skillsDir: \".qoder/skills\",\n hooksDir: \".qoder/hooks\",\n hookConfigFile: \".qoder/settings.json\",\n agentsDir: \".qoder/agents\",\n agentFileExt: \".md\",\n mainConstraint: \"AGENTS.md\",\n skillTrigger: \"/\",\n hookEvents: [\"UserPromptSubmit\", \"PreToolUse\", \"Stop\"],\n stateInjectEvent: [\"UserPromptSubmit\"],\n hasSubagentContext: true,\n cnVariant: \".qodercn\",\n templateContext: {\n sub_agent_dispatch: \"Agent tool\",\n platform_spawn_instruction:\n \"Use the Agent tool with worktree isolation for parallel file edits.\",\n skill_trigger: \"/\",\n workflow_state_path: \".easy-coding/state.json\",\n main_constraint_file: \"AGENTS.md\",\n python_cmd: pythonCmd,\n platform_config_dir: \".qoder\",\n },\n },\n};\n\nexport const AGENT_PLATFORMS = Object.keys(PLATFORM_META) as AgentPlatform[];\n\nexport function isAgentPlatform(value: string): value is AgentPlatform {\n return Object.hasOwn(PLATFORM_META, value);\n}\n","import { readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { GENERATED_REGION_END, GENERATED_REGION_START } from \"../constants/paths.js\";\nimport type { AgentPlatform, TemplateContext } from \"../types/platform.js\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport {\n ensureDir,\n isDirectory,\n pathExists,\n readTextFile,\n readTextIfExists,\n writeTextFile,\n} from \"../utils/file-writer.js\";\nimport { replaceMarkedRegion } from \"../utils/marked-region.js\";\nimport { getTemplatePath } from \"../utils/template-paths.js\";\n\nexport interface SkillTemplate {\n name: string;\n content: string;\n}\n\nexport interface BundledSkillTemplate {\n name: string;\n sourceDir: string;\n context: TemplateContext;\n}\n\nexport interface SharedHookOptions {\n skipSubagentContext?: boolean;\n}\n\nexport class UnresolvedPlaceholderError extends Error {\n constructor(contentName: string, placeholders: string[]) {\n super(`Unresolved placeholders in ${contentName}: ${placeholders.join(\", \")}`);\n this.name = \"UnresolvedPlaceholderError\";\n }\n}\n\nexport function resolvePlaceholders(\n content: string,\n ctx: TemplateContext,\n contentName = \"template\",\n): string {\n const withPython = content.replace(/\\{\\{PYTHON_CMD\\}\\}/g, ctx.python_cmd);\n const resolved = withPython.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key: keyof TemplateContext) => {\n const value = ctx[key];\n return value === undefined ? match : String(value);\n });\n\n const unresolved = [...resolved.matchAll(/\\{\\{[^}]+\\}\\}/g)].map((match) => match[0]);\n if (unresolved.length > 0) {\n throw new UnresolvedPlaceholderError(contentName, unresolved);\n }\n\n return resolved;\n}\n\nexport async function resolveSkills(ctx: TemplateContext): Promise<SkillTemplate[]> {\n const skillsRoot = getTemplatePath(\"common\", \"skills\");\n const entries = await readdir(skillsRoot);\n const skills: SkillTemplate[] = [];\n\n for (const entry of entries.sort()) {\n const skillDir = path.join(skillsRoot, entry);\n if (!(await isDirectory(skillDir))) {\n continue;\n }\n const content = await readTextFile(path.join(skillDir, \"SKILL.md\"));\n skills.push({\n name: entry,\n content: resolvePlaceholders(content, ctx, `common/skills/${entry}/SKILL.md`),\n });\n }\n\n return skills;\n}\n\nexport async function resolveBundledSkills(ctx: TemplateContext): Promise<BundledSkillTemplate[]> {\n const bundledRoot = getTemplatePath(\"common\", \"bundled-skills\");\n if (!(await pathExists(bundledRoot))) {\n return [];\n }\n\n const entries = await readdir(bundledRoot);\n const bundled: BundledSkillTemplate[] = [];\n for (const entry of entries.sort()) {\n const sourceDir = path.join(bundledRoot, entry);\n if (await isDirectory(sourceDir)) {\n bundled.push({ name: entry, sourceDir, context: ctx });\n }\n }\n return bundled;\n}\n\nexport async function writeSkills(\n dir: string,\n skills: SkillTemplate[],\n bundled: BundledSkillTemplate[],\n): Promise<void> {\n await ensureDir(dir);\n\n for (const skill of skills) {\n await writeTextFile(path.join(dir, skill.name, \"SKILL.md\"), skill.content);\n }\n\n for (const skill of bundled) {\n await copyTemplateDirectory(skill.sourceDir, path.join(dir, skill.name), skill.context);\n }\n}\n\nexport async function writeSharedHooks(\n dir: string,\n platform: AgentPlatform,\n opts: SharedHookOptions = {},\n): Promise<void> {\n const hooksRoot = getTemplatePath(\"shared-hooks\");\n const ctx = PLATFORM_META[platform].templateContext;\n const entries = await readdir(hooksRoot);\n await ensureDir(dir);\n\n for (const entry of entries.sort()) {\n if (opts.skipSubagentContext && entry === \"inject-subagent-context.py\") {\n continue;\n }\n const sourcePath = path.join(hooksRoot, entry);\n if ((await stat(sourcePath)).isDirectory()) {\n continue;\n }\n const content = resolvePlaceholders(\n await readTextFile(sourcePath),\n ctx,\n `shared-hooks/${entry}`,\n );\n const destination = path.join(dir, entry);\n await writeTextFile(destination, content);\n await import(\"node:fs/promises\").then(({ chmod }) => chmod(destination, 0o755));\n }\n}\n\nexport async function copyPlatformTemplates(\n platformTemplateDir: string,\n destination: string,\n skipDirs: string[],\n ctx: TemplateContext,\n): Promise<void> {\n const source = getTemplatePath(platformTemplateDir);\n await copyTemplateDirectory(source, destination, ctx, new Set(skipDirs));\n}\n\nexport async function writeMainConstraint(cwd: string, platform: AgentPlatform): Promise<void> {\n const meta = PLATFORM_META[platform];\n const ctx = meta.templateContext;\n const templateName = `${meta.mainConstraint}.tpl`;\n const template = await readTextFile(getTemplatePath(\"main-constraint\", templateName));\n const generated = resolvePlaceholders(template, ctx, `main-constraint/${templateName}`);\n\n if (!generated.includes(GENERATED_REGION_START) || !generated.includes(GENERATED_REGION_END)) {\n throw new Error(`Main constraint template ${templateName} does not contain generated markers.`);\n }\n\n const destination = path.join(cwd, meta.mainConstraint);\n const current = await readTextIfExists(destination);\n const next = current ? replaceMarkedRegion(current, generated) : generated;\n await writeTextFile(destination, next);\n}\n\nasync function copyTemplateDirectory(\n source: string,\n destination: string,\n ctx: TemplateContext,\n skipDirs = new Set<string>(),\n): Promise<void> {\n await ensureDir(destination);\n\n for (const entry of (await readdir(source)).sort()) {\n if (skipDirs.has(entry) || shouldSkipTemplateEntry(entry)) {\n continue;\n }\n\n const sourcePath = path.join(source, entry);\n const destinationPath = path.join(destination, stripTemplateExtension(entry));\n const sourceStat = await stat(sourcePath);\n\n if (sourceStat.isDirectory()) {\n await copyTemplateDirectory(sourcePath, destinationPath, ctx, skipDirs);\n continue;\n }\n\n const content = resolvePlaceholders(await readTextFile(sourcePath), ctx, sourcePath);\n await writeTextFile(destinationPath, content);\n }\n}\n\nfunction shouldSkipTemplateEntry(entry: string): boolean {\n return (\n entry === \".DS_Store\" ||\n entry === \"__pycache__\" ||\n entry.endsWith(\".ts\") ||\n entry.endsWith(\".js\") ||\n entry.endsWith(\".map\")\n );\n}\n\nfunction stripTemplateExtension(fileName: string): string {\n return fileName.endsWith(\".tpl\") ? fileName.slice(0, -4) : fileName;\n}\n","export const EASY_CODING_DIR = \".easy-coding\";\nexport const CONFIG_FILE = \"config.yaml\";\nexport const STATE_FILE = \"state.json\";\nexport const TASKS_DIR = \"tasks\";\nexport const PROJECT_INIT_TASK_ID = \"project-init\";\nexport const MEMORY_DIR = \"memory\";\nexport const SPEC_DIR = \"spec\";\nexport const MAIN_SPEC_DIR = \"main\";\nexport const DEV_SPEC_DIR = \"dev\";\n\nexport const STATE_GITIGNORE_ENTRY = \".easy-coding/state.json\";\n\nexport const GENERATED_REGION_START =\n \"<!-- ═══ easy-coding-harness generated (DO NOT EDIT BETWEEN MARKERS) ═══ -->\";\nexport const GENERATED_REGION_END = \"<!-- ═══ end easy-coding-harness generated ═══ -->\";\n","import { constants } from \"node:fs\";\nimport { access, cp, writeFile as fsWriteFile, mkdir, readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function pathExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n\nexport async function writeTextFile(filePath: string, content: string): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fsWriteFile(filePath, content.endsWith(\"\\n\") ? content : `${content}\\n`, \"utf8\");\n}\n\nexport async function readTextFile(filePath: string): Promise<string> {\n return readFile(filePath, \"utf8\");\n}\n\nexport async function readTextIfExists(filePath: string): Promise<string | null> {\n if (!(await pathExists(filePath))) {\n return null;\n }\n return readTextFile(filePath);\n}\n\nexport async function copyDir(source: string, destination: string): Promise<void> {\n await ensureDir(destination);\n await cp(source, destination, { recursive: true });\n}\n\nexport async function isDirectory(filePath: string): Promise<boolean> {\n try {\n return (await stat(filePath)).isDirectory();\n } catch {\n return false;\n }\n}\n","import { GENERATED_REGION_END, GENERATED_REGION_START } from \"../constants/paths.js\";\n\nexport class MarkedRegionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MarkedRegionError\";\n }\n}\n\nexport function extractMarkedRegion(content: string): string | null {\n const start = content.indexOf(GENERATED_REGION_START);\n const end = content.indexOf(GENERATED_REGION_END);\n\n if (start === -1 && end === -1) {\n return null;\n }\n if (start === -1 || end === -1 || end < start) {\n throw new MarkedRegionError(\"Generated region markers are incomplete or out of order.\");\n }\n\n return content.slice(start, end + GENERATED_REGION_END.length);\n}\n\nexport function replaceMarkedRegion(existing: string, generatedRegion: string): string {\n const currentRegion = extractMarkedRegion(existing);\n if (!currentRegion) {\n return `${generatedRegion.trim()}\\n\\n${existing.trimStart()}`.trimEnd();\n }\n return existing.replace(currentRegion, generatedRegion.trim());\n}\n\nexport function hasMarkedRegion(content: string): boolean {\n return extractMarkedRegion(content) !== null;\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport function getTemplateRoot(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"../templates\"),\n path.resolve(here, \"../../templates\"),\n path.resolve(process.cwd(), \"src/templates\"),\n path.resolve(process.cwd(), \"templates\"),\n ];\n\n const found = candidates.find((candidate) => existsSync(candidate));\n if (!found) {\n throw new Error(`Unable to locate templates directory. Tried: ${candidates.join(\", \")}`);\n }\n return found;\n}\n\nexport function getTemplatePath(...segments: string[]): string {\n return path.join(getTemplateRoot(), ...segments);\n}\n","import path from \"node:path\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport {\n copyPlatformTemplates,\n resolveBundledSkills,\n resolveSkills,\n writeMainConstraint,\n writeSharedHooks,\n writeSkills,\n} from \"./shared.js\";\n\nexport async function configureCodex(cwd: string): Promise<void> {\n const platform = \"codex\";\n const meta = PLATFORM_META[platform];\n const ctx = meta.templateContext;\n\n await writeSkills(\n path.join(cwd, meta.skillsDir),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\n );\n await writeSharedHooks(path.join(cwd, meta.hooksDir), platform, { skipSubagentContext: true });\n await copyPlatformTemplates(\"codex\", path.join(cwd, \".codex\"), [\"hooks\"], ctx);\n await writeMainConstraint(cwd, platform);\n}\n","import { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { PLATFORM_META, type TemplateContext } from \"../types/platform.js\";\nimport {\n copyPlatformTemplates,\n resolveBundledSkills,\n resolveSkills,\n writeMainConstraint,\n writeSharedHooks,\n writeSkills,\n} from \"./shared.js\";\n\nfunction claudeSkillsExist(cwd: string): boolean {\n const claudeSkillsDir = path.join(cwd, \".claude\", \"skills\");\n if (!existsSync(claudeSkillsDir)) return false;\n try {\n return readdirSync(claudeSkillsDir).some((d) => d.startsWith(\"ec-\"));\n } catch {\n return false;\n }\n}\n\nexport async function configureQoder(cwd: string): Promise<void> {\n const platform = \"qoder\";\n const baseDir = detectQoderCnVariant(cwd) ? \".qodercn\" : \".qoder\";\n const ctx: TemplateContext = {\n ...PLATFORM_META[platform].templateContext,\n platform_config_dir: baseDir,\n };\n const dest = path.join(cwd, baseDir);\n\n await copyPlatformTemplates(\"qoder\", dest, [\"hooks\"], ctx);\n await writeSharedHooks(path.join(dest, \"hooks\"), platform);\n\n // Qoder scans both .qoder/skills/ and .claude/skills/ at runtime.\n // Skip writing to .qoder/skills/ when Claude Code skills already exist to avoid duplicates.\n if (!claudeSkillsExist(cwd)) {\n await writeSkills(\n path.join(dest, \"skills\"),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\n );\n }\n\n await writeMainConstraint(cwd, platform);\n}\n\nexport function detectQoderCnVariant(cwd: string): boolean {\n if (process.env.EC_QODER_VARIANT === \"cn\" || process.env.QODER_VARIANT === \"cn\") {\n return true;\n }\n return existsSync(path.join(cwd, \".qodercn\"));\n}\n","import type { AgentPlatform } from \"../types/platform.js\";\nimport { configureClaude } from \"./claude.js\";\nimport { configureCodex } from \"./codex.js\";\nimport { configureQoder } from \"./qoder.js\";\n\nexport type ConfigureFn = (cwd: string) => Promise<void>;\n\nexport const CONFIGURATORS: Record<AgentPlatform, ConfigureFn> = {\n \"claude-code\": configureClaude,\n codex: configureCodex,\n qoder: configureQoder,\n};\n","import chalk from \"chalk\";\nimport figlet from \"figlet\";\nimport { VERSION } from \"../constants/version.js\";\n\nexport interface BannerPreset {\n font: \"ANSI Shadow\" | \"Small Shadow\" | \"Small Slant\";\n horizontalLayout?: \"full\";\n}\n\nconst WIDE_TERMINAL_WIDTH = 88;\nconst MEDIUM_TERMINAL_WIDTH = 72;\n\nexport function renderBanner(): void {\n const terminalWidth = process.stdout.columns ?? 120;\n const art = figlet.textSync(\"Easy Coding\", selectBannerPreset(terminalWidth));\n console.log(colorizeBanner(art));\n console.log(chalk.bold.white(` Easy Coding Harness ${chalk.cyan(`v${VERSION}`)}\\n`));\n}\n\nexport function selectBannerPreset(width: number): BannerPreset {\n if (width >= WIDE_TERMINAL_WIDTH) {\n return { font: \"ANSI Shadow\", horizontalLayout: \"full\" };\n }\n\n if (width >= MEDIUM_TERMINAL_WIDTH) {\n return { font: \"Small Shadow\", horizontalLayout: \"full\" };\n }\n\n return { font: \"Small Slant\" };\n}\n\nexport function colorizeBanner(art: string): string {\n const colors = [chalk.cyanBright, chalk.cyan, chalk.blueBright, chalk.blue];\n return art\n .split(\"\\n\")\n .map((line, index) => colors[index % colors.length](line))\n .join(\"\\n\");\n}\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface PackageMetadata {\n name: string;\n version: string;\n}\n\nconst packageMetadata = readPackageMetadata();\n\nexport const PACKAGE_NAME = packageMetadata.name;\nexport const VERSION = packageMetadata.version;\n\nfunction readPackageMetadata(): PackageMetadata {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"../../package.json\"),\n path.resolve(here, \"../package.json\"),\n path.resolve(process.cwd(), \"package.json\"),\n ];\n\n for (const candidate of candidates) {\n try {\n const parsed = JSON.parse(readFileSync(candidate, \"utf8\")) as Partial<PackageMetadata>;\n if (parsed.name && parsed.version) {\n return { name: parsed.name, version: parsed.version };\n }\n } catch {\n // Try the next candidate; source, dist, and installed layouts differ.\n }\n }\n\n throw new Error(`Unable to locate package metadata. Tried: ${candidates.join(\", \")}`);\n}\n","import { readFile } from \"node:fs/promises\";\nimport YAML, { isScalar, isSeq, parseDocument } from \"yaml\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport { writeTextFile } from \"./file-writer.js\";\n\nexport interface EasyCodingConfig {\n version: number;\n harness_version: string;\n agents: AgentPlatform[];\n project: {\n name: string;\n mode: \"auto\" | \"startup\" | \"iterative\";\n };\n memory: {\n short_term_max: number;\n short_term_keep: number;\n schema_version: number;\n };\n test: {\n framework: string;\n command: string;\n };\n tasks: {\n auto_archive_days: number;\n };\n behavior: {\n strict_confirm: boolean;\n auto_mode: boolean;\n };\n [key: string]: unknown;\n}\n\nexport function createDefaultConfig(params: {\n projectName: string;\n harnessVersion: string;\n agents: AgentPlatform[];\n}): EasyCodingConfig {\n return {\n version: 1,\n harness_version: params.harnessVersion,\n agents: params.agents,\n project: {\n name: params.projectName,\n mode: \"auto\",\n },\n memory: {\n short_term_max: 10,\n short_term_keep: 5,\n schema_version: 2,\n },\n test: {\n framework: \"auto\",\n command: \"\",\n },\n tasks: {\n auto_archive_days: 30,\n },\n behavior: {\n strict_confirm: true,\n auto_mode: false,\n },\n };\n}\n\nexport function stringifyConfig(config: EasyCodingConfig): string {\n return YAML.stringify(config);\n}\n\nexport async function writeConfigYaml(filePath: string, config: EasyCodingConfig): Promise<void> {\n await writeTextFile(filePath, stringifyConfig(config));\n}\n\nexport async function readConfigYaml(filePath: string): Promise<EasyCodingConfig> {\n const content = await readFile(filePath, \"utf8\");\n return YAML.parse(content) as EasyCodingConfig;\n}\n\nexport async function updateConfigYaml(\n filePath: string,\n updater: (config: EasyCodingConfig) => void,\n): Promise<EasyCodingConfig> {\n const content = await readFile(filePath, \"utf8\");\n const document = parseDocument(content);\n const config = document.toJSON() as EasyCodingConfig;\n updater(config);\n\n for (const [key, value] of Object.entries(config)) {\n document.set(key, value);\n }\n\n await writeTextFile(filePath, document.toString());\n return config;\n}\n\nexport async function addAgentsToConfig(\n filePath: string,\n agents: AgentPlatform[],\n): Promise<EasyCodingConfig> {\n return updateConfigYaml(filePath, (config) => {\n const merged = new Set([...(config.agents ?? []), ...agents]);\n config.agents = [...merged];\n });\n}\n\nexport async function updateHarnessVersion(\n filePath: string,\n version: string,\n): Promise<EasyCodingConfig> {\n return updateConfigYaml(filePath, (config) => {\n config.harness_version = version;\n });\n}\n\nexport function yamlHasAgent(documentContent: string, agent: AgentPlatform): boolean {\n const document = parseDocument(documentContent);\n const agents = document.get(\"agents\", true);\n if (!isSeq(agents)) {\n return false;\n }\n return agents.items.some((item) => isScalar(item) && String(item.value) === agent);\n}\n","import { cancel, confirm, multiselect } from \"@clack/prompts\";\nimport {\n AGENT_PLATFORMS,\n type AgentPlatform,\n PLATFORM_META,\n isAgentPlatform,\n} from \"../types/platform.js\";\n\nexport interface PlatformOptions {\n agent?: string;\n yes?: boolean;\n}\n\nexport function parseAgentList(agentList: string): AgentPlatform[] {\n const values = agentList\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n\n if (values.length === 0) {\n throw new Error(\"No agent platform specified.\");\n }\n\n const invalid = values.filter((value) => !isAgentPlatform(value));\n if (invalid.length > 0) {\n throw new Error(`Unknown agent platform: ${invalid.join(\", \")}`);\n }\n\n return [...new Set(values)] as AgentPlatform[];\n}\n\nexport async function resolvePlatforms(\n opts: PlatformOptions,\n defaults: AgentPlatform[] = [\"claude-code\"],\n): Promise<AgentPlatform[]> {\n if (opts.agent) {\n return parseAgentList(opts.agent);\n }\n\n if (opts.yes) {\n return defaults;\n }\n\n let selectedDefaults = defaults;\n\n while (true) {\n const result = await multiselect({\n message: \"Select agent platforms (Space to toggle, Enter to review)\",\n options: AGENT_PLATFORMS.map((platform) => ({\n label: PLATFORM_META[platform].label,\n value: platform,\n })),\n initialValues: selectedDefaults,\n required: true,\n });\n\n if (typeof result === \"symbol\") {\n cancel(\"Platform selection cancelled.\");\n process.exit(1);\n }\n\n const labels = result.map((platform) => PLATFORM_META[platform].label).join(\", \");\n const shouldInstall = await confirm({\n message: `Install Easy Coding for: ${labels}?`,\n initialValue: true,\n });\n\n if (typeof shouldInstall === \"symbol\") {\n cancel(\"Platform selection cancelled.\");\n process.exit(1);\n }\n\n if (shouldInstall) {\n return result;\n }\n\n selectedDefaults = result;\n }\n}\n","import { note, outro } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { CONFIGURATORS } from \"../configurators/index.js\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { ensureEasyCodingStateIgnored } from \"../utils/gitignore.js\";\nimport { detectEasyCodingInstallState } from \"../utils/install-state.js\";\nimport { writeRuntimeScaffold } from \"../utils/runtime-scaffold.js\";\nimport { writeProjectInitTask } from \"../utils/task-json.js\";\nimport { type PlatformOptions, resolvePlatforms } from \"./platforms.js\";\n\nexport async function init(opts: PlatformOptions): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const installState = await detectEasyCodingInstallState(cwd);\n if (installState.kind === \"installed\") {\n throw new Error(\n \".easy-coding/config.yaml already exists. Use easy-coding add-agent or easy-coding upgrade.\",\n );\n }\n if (installState.kind === \"unknown\") {\n throw new Error(\n \".easy-coding exists but is not recognized as an easy-coding harness or legacy easy-coding skill project. Please inspect it manually before running init.\",\n );\n }\n\n const platforms = await resolvePlatforms(opts, [\"claude-code\"]);\n\n for (const platform of platforms) {\n await CONFIGURATORS[platform](cwd);\n }\n\n await writeRuntimeScaffold(cwd, platforms);\n await writeProjectInitTask(cwd, platforms, {\n initSource: installState.kind === \"legacy\" ? \"legacy-easy-coding\" : \"fresh\",\n legacyAssets: installState.kind === \"legacy\" ? installState.legacyAssets : undefined,\n legacyMissingHarnessFiles:\n installState.kind === \"legacy\" ? installState.missingHarnessFiles : undefined,\n });\n await ensureEasyCodingStateIgnored(cwd);\n\n const triggers = platforms\n .map(\n (platform) =>\n `${PLATFORM_META[platform].label}: ${PLATFORM_META[platform].skillTrigger}ec-init`,\n )\n .join(\"\\n\");\n\n note(triggers, \"Next step\");\n outro(chalk.green(\"easy-coding harness installed. Open your agent and run ec-init.\"));\n}\n","import path from \"node:path\";\nimport { STATE_GITIGNORE_ENTRY } from \"../constants/paths.js\";\nimport { readTextIfExists, writeTextFile } from \"./file-writer.js\";\n\nexport async function ensureGitignoreEntry(\n cwd: string,\n entry: string,\n heading = \"# ═══ easy-coding-harness (auto-generated) ═══\\n# Personal runtime state; do not commit\",\n): Promise<boolean> {\n const gitignorePath = path.join(cwd, \".gitignore\");\n const current = (await readTextIfExists(gitignorePath)) ?? \"\";\n const lines = current.split(/\\r?\\n/).map((line) => line.trim());\n\n if (lines.includes(entry)) {\n return false;\n }\n\n const prefix = current.trimEnd();\n const next = [prefix, heading, entry].filter(Boolean).join(\"\\n\");\n await writeTextFile(gitignorePath, next);\n return true;\n}\n\nexport function gitignoreContains(content: string, entry: string): boolean {\n return content\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .includes(entry);\n}\n\nexport async function ensureEasyCodingStateIgnored(cwd: string): Promise<boolean> {\n return ensureGitignoreEntry(cwd, STATE_GITIGNORE_ENTRY);\n}\n","import { readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n CONFIG_FILE,\n EASY_CODING_DIR,\n PROJECT_INIT_TASK_ID,\n TASKS_DIR,\n} from \"../constants/paths.js\";\nimport { isDirectory, pathExists } from \"./file-writer.js\";\n\nexport type EasyCodingInstallState =\n | { kind: \"fresh\"; easyCodingDir: string }\n | { kind: \"installed\"; easyCodingDir: string; configPath: string }\n | {\n kind: \"legacy\";\n easyCodingDir: string;\n legacyAssets: string[];\n missingHarnessFiles: string[];\n }\n | { kind: \"unknown\"; easyCodingDir: string };\n\nconst LEGACY_ROOT_FILES = [\"SOUL.md\", \"RULES.md\", \"ABSTRACT.md\"];\n\nexport async function detectEasyCodingInstallState(cwd: string): Promise<EasyCodingInstallState> {\n const easyCodingDir = path.join(cwd, EASY_CODING_DIR);\n if (!(await pathExists(easyCodingDir))) {\n return { kind: \"fresh\", easyCodingDir };\n }\n\n const configPath = path.join(easyCodingDir, CONFIG_FILE);\n if (await pathExists(configPath)) {\n return { kind: \"installed\", easyCodingDir, configPath };\n }\n\n const legacyAssets = await detectLegacyAssets(easyCodingDir);\n if (legacyAssets.length === 0) {\n return { kind: \"unknown\", easyCodingDir };\n }\n\n return {\n kind: \"legacy\",\n easyCodingDir,\n legacyAssets,\n missingHarnessFiles: [relativeConfigPath(), relativeProjectInitTaskPath()],\n };\n}\n\nasync function detectLegacyAssets(easyCodingDir: string): Promise<string[]> {\n const assets: string[] = [];\n\n for (const file of LEGACY_ROOT_FILES) {\n if (await pathExists(path.join(easyCodingDir, file))) {\n assets.push(relativeEasyCodingPath(file));\n }\n }\n\n if (await pathExists(path.join(easyCodingDir, \"memory\", \"long\", \"MEMORY.md\"))) {\n assets.push(relativeEasyCodingPath(\"memory\", \"long\", \"MEMORY.md\"));\n }\n\n const shortMemoryFiles = await listMarkdownFiles(path.join(easyCodingDir, \"memory\", \"short\"));\n assets.push(...shortMemoryFiles.map((file) => relativeEasyCodingPath(\"memory\", \"short\", file)));\n\n for (const dir of [\"spec\", \"prototype\"]) {\n if (await hasAnyDirectoryEntry(path.join(easyCodingDir, dir))) {\n assets.push(relativeEasyCodingPath(dir));\n }\n }\n\n return assets.sort();\n}\n\nasync function listMarkdownFiles(dir: string): Promise<string[]> {\n if (!(await isDirectory(dir))) {\n return [];\n }\n\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".md\"))\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function hasAnyDirectoryEntry(dir: string): Promise<boolean> {\n if (!(await isDirectory(dir))) {\n return false;\n }\n\n return (await readdir(dir)).length > 0;\n}\n\nfunction relativeEasyCodingPath(...segments: string[]): string {\n return path.posix.join(EASY_CODING_DIR, ...segments);\n}\n\nfunction relativeConfigPath(): string {\n return path.posix.join(EASY_CODING_DIR, CONFIG_FILE);\n}\n\nfunction relativeProjectInitTaskPath(): string {\n return path.posix.join(EASY_CODING_DIR, TASKS_DIR, PROJECT_INIT_TASK_ID, \"task.json\");\n}\n","import path from \"node:path\";\nimport {\n CONFIG_FILE,\n DEV_SPEC_DIR,\n EASY_CODING_DIR,\n MAIN_SPEC_DIR,\n MEMORY_DIR,\n SPEC_DIR,\n} from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport { createDefaultConfig, writeConfigYaml } from \"./config-yaml.js\";\nimport { ensureDir, pathExists, readTextFile, writeTextFile } from \"./file-writer.js\";\nimport { getTemplatePath } from \"./template-paths.js\";\n\nexport async function writeRuntimeScaffold(cwd: string, agents: AgentPlatform[]): Promise<void> {\n const easyCodingDir = path.join(cwd, EASY_CODING_DIR);\n await ensureDir(easyCodingDir);\n\n const configPath = path.join(easyCodingDir, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n const projectName = path.basename(cwd);\n await writeConfigYaml(\n configPath,\n createDefaultConfig({ projectName, harnessVersion: VERSION, agents }),\n );\n }\n\n await ensureDir(path.join(easyCodingDir, \"tasks\"));\n await ensureDir(path.join(easyCodingDir, SPEC_DIR, MAIN_SPEC_DIR));\n await ensureDir(path.join(easyCodingDir, SPEC_DIR, DEV_SPEC_DIR));\n await writeMemoryScaffold(easyCodingDir);\n}\n\nasync function writeMemoryScaffold(easyCodingDir: string): Promise<void> {\n const memoryDir = path.join(easyCodingDir, MEMORY_DIR);\n await ensureDir(path.join(memoryDir, \"short\"));\n await ensureDir(path.join(memoryDir, \"long\"));\n\n for (const file of [\"MEMORY.md\", \"BUSINESS.md\", \"TECHNICAL.md\"]) {\n const destination = path.join(memoryDir, \"long\", file);\n if (await pathExists(destination)) {\n continue;\n }\n const templatePath = getTemplatePath(\"runtime\", \"memory\", \"long\", file);\n await writeTextFile(destination, await readTextFile(templatePath));\n }\n}\n","import { readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { EASY_CODING_DIR, PROJECT_INIT_TASK_ID, TASKS_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport type { TaskJson, TaskStatus } from \"../types/task.js\";\nimport { pathExists, readTextFile, writeTextFile } from \"./file-writer.js\";\n\nexport type ProjectInitSource = \"fresh\" | \"legacy-easy-coding\";\n\nexport function createProjectInitTask(params: {\n cwd: string;\n agents: AgentPlatform[];\n initSource?: ProjectInitSource;\n legacyAssets?: string[];\n legacyMissingHarnessFiles?: string[];\n now?: Date;\n}): TaskJson {\n return {\n type: \"project-init\",\n status: \"PENDING\",\n created_at: (params.now ?? new Date()).toISOString(),\n created_by: \"cli-init\",\n context: {\n agents_installed: params.agents,\n cli_version: VERSION,\n project_path: params.cwd,\n init_source: params.initSource ?? \"fresh\",\n ...(params.legacyAssets ? { legacy_assets: params.legacyAssets } : {}),\n ...(params.legacyMissingHarnessFiles\n ? { legacy_missing_harness_files: params.legacyMissingHarnessFiles }\n : {}),\n },\n init_log: [],\n };\n}\n\nexport function getTaskJsonPath(cwd: string, taskId: string): string {\n return path.join(cwd, EASY_CODING_DIR, TASKS_DIR, taskId, \"task.json\");\n}\n\nexport async function writeTaskJson(filePath: string, task: TaskJson): Promise<void> {\n await writeTextFile(filePath, JSON.stringify(task, null, 2));\n}\n\nexport async function readTaskJson(filePath: string): Promise<TaskJson> {\n return JSON.parse(await readTextFile(filePath)) as TaskJson;\n}\n\nexport async function writeProjectInitTask(\n cwd: string,\n agents: AgentPlatform[],\n options: {\n initSource?: ProjectInitSource;\n legacyAssets?: string[];\n legacyMissingHarnessFiles?: string[];\n } = {},\n): Promise<void> {\n await writeTaskJson(\n getTaskJsonPath(cwd, PROJECT_INIT_TASK_ID),\n createProjectInitTask({\n cwd,\n agents,\n initSource: options.initSource,\n legacyAssets: options.legacyAssets,\n legacyMissingHarnessFiles: options.legacyMissingHarnessFiles,\n }),\n );\n}\n\nexport interface TaskSummary {\n id: string;\n task: TaskJson;\n}\n\nexport async function listTasks(cwd: string): Promise<TaskSummary[]> {\n const tasksDir = path.join(cwd, EASY_CODING_DIR, TASKS_DIR);\n if (!(await pathExists(tasksDir))) {\n return [];\n }\n\n const entries = await readdir(tasksDir, { withFileTypes: true });\n const tasks: TaskSummary[] = [];\n\n for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {\n if (!entry.isDirectory()) {\n continue;\n }\n\n const taskPath = getTaskJsonPath(cwd, entry.name);\n if (!(await pathExists(taskPath))) {\n continue;\n }\n\n tasks.push({ id: entry.name, task: await readTaskJson(taskPath) });\n }\n\n return tasks;\n}\n\nexport function summarizeTaskStatuses(tasks: TaskSummary[]): Record<TaskStatus, number> {\n return tasks.reduce(\n (summary, item) => {\n summary[item.task.status] = (summary[item.task.status] ?? 0) + 1;\n return summary;\n },\n {} as Record<TaskStatus, number>,\n );\n}\n\nexport function isActiveTask(task: TaskJson): boolean {\n return task.status !== \"COMPLETE\" && task.status !== \"CLOSED\";\n}\n","import path from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { compareVersions } from \"../utils/compare-versions.js\";\nimport { readConfigYaml } from \"../utils/config-yaml.js\";\nimport { pathExists } from \"../utils/file-writer.js\";\nimport { readStateJson } from \"../utils/state-json.js\";\nimport { isActiveTask, listTasks, summarizeTaskStatuses } from \"../utils/task-json.js\";\n\nexport async function status(): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n throw new Error(\"No easy-coding harness found in this project.\");\n }\n\n const config = await readConfigYaml(configPath);\n const tasks = await listTasks(cwd);\n const taskCounts = summarizeTaskStatuses(tasks);\n const activeTasks = tasks.filter((item) => isActiveTask(item.task));\n const state = await readStateJson(cwd);\n const versionRelation = compareVersions(config.harness_version, VERSION);\n\n console.log(chalk.bold(\"Harness\"));\n console.log(` version: ${config.harness_version}`);\n console.log(` cli: ${VERSION}`);\n if (versionRelation === -1) {\n console.log(chalk.yellow(\" upgrade: available\"));\n } else if (versionRelation === 1) {\n console.log(chalk.red(\" upgrade: CLI is older than project harness\"));\n } else {\n console.log(\" upgrade: up to date\");\n }\n console.log(` agents: ${config.agents.join(\", \") || \"(none)\"}`);\n console.log(` project: ${config.project.name} (${config.project.mode})`);\n console.log(\"\");\n console.log(chalk.bold(\"State\"));\n if (state) {\n console.log(` current_stage: ${state.current_stage}`);\n console.log(` current_task: ${state.current_task ?? \"(none)\"}`);\n console.log(` last_agent: ${state.last_agent}`);\n } else {\n console.log(\" state.json: not created yet\");\n }\n console.log(\"\");\n console.log(chalk.bold(\"Tasks\"));\n console.log(` total: ${tasks.length}`);\n for (const [status, count] of Object.entries(taskCounts)) {\n console.log(` ${status}: ${count}`);\n }\n if (activeTasks.length > 0) {\n console.log(\" active:\");\n for (const item of activeTasks) {\n console.log(` - ${item.id}: ${item.task.status}`);\n }\n }\n}\n","import path from \"node:path\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport { readConfigYaml } from \"./config-yaml.js\";\nimport { pathExists } from \"./file-writer.js\";\n\nexport type VersionComparison = -1 | 0 | 1;\n\nfunction normalize(version: string): number[] {\n const [core] = version.split(\"-\");\n return core.split(\".\").map((part) => {\n const parsed = Number.parseInt(part, 10);\n return Number.isNaN(parsed) ? 0 : parsed;\n });\n}\n\nexport function compareVersions(a: string, b: string): VersionComparison {\n const left = normalize(a);\n const right = normalize(b);\n const length = Math.max(left.length, right.length);\n\n for (let index = 0; index < length; index += 1) {\n const leftPart = left[index] ?? 0;\n const rightPart = right[index] ?? 0;\n if (leftPart < rightPart) return -1;\n if (leftPart > rightPart) return 1;\n }\n return 0;\n}\n\nexport function isVersionBehind(installed: string, current = VERSION): boolean {\n return compareVersions(installed, current) === -1;\n}\n\nexport async function checkForUpgrade(cwd: string): Promise<void> {\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n return;\n }\n\n const config = await readConfigYaml(configPath);\n const installed = String(config.harness_version ?? \"\");\n if (installed && isVersionBehind(installed)) {\n process.stderr.write(\n `easy-coding harness ${installed} is older than CLI ${VERSION}. Run easy-coding upgrade when ready.\\n`,\n );\n }\n}\n","import path from \"node:path\";\nimport { EASY_CODING_DIR, STATE_FILE } from \"../constants/paths.js\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport type { Stage, StateJson } from \"../types/task.js\";\nimport { readTextIfExists, writeTextFile } from \"./file-writer.js\";\n\nexport function createIdleState(agent: AgentPlatform | \"cli\" = \"cli\"): StateJson {\n return {\n current_stage: \"idle\",\n current_task: null,\n last_agent: agent,\n stage_history: [],\n confirmed_by_user: false,\n test_strategy_confirmed: false,\n repo_paths: {},\n };\n}\n\nexport function getStateJsonPath(cwd: string): string {\n return path.join(cwd, EASY_CODING_DIR, STATE_FILE);\n}\n\nexport async function readStateJson(cwd: string): Promise<StateJson | null> {\n const content = await readTextIfExists(getStateJsonPath(cwd));\n if (!content) {\n return null;\n }\n return JSON.parse(content) as StateJson;\n}\n\nexport async function writeStateJson(cwd: string, state: StateJson): Promise<void> {\n await writeTextFile(getStateJsonPath(cwd), JSON.stringify(state, null, 2));\n}\n\nexport async function ensureStateJson(\n cwd: string,\n agent: AgentPlatform | \"cli\" = \"cli\",\n): Promise<StateJson> {\n const existing = await readStateJson(cwd);\n if (existing) {\n return existing;\n }\n\n const state = createIdleState(agent);\n await writeStateJson(cwd, state);\n return state;\n}\n\nexport function enterStage(\n state: StateJson,\n stage: Stage,\n agent: string,\n taskId: string,\n): StateJson {\n return {\n ...state,\n current_stage: stage,\n current_task: taskId,\n last_agent: agent,\n stage_history: [...state.stage_history, { stage, agent, entered_at: new Date().toISOString() }],\n };\n}\n","import path from \"node:path\";\nimport { cancel, confirm, outro } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { CONFIGURATORS } from \"../configurators/index.js\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { compareVersions } from \"../utils/compare-versions.js\";\nimport { readConfigYaml, updateHarnessVersion } from \"../utils/config-yaml.js\";\nimport { pathExists } from \"../utils/file-writer.js\";\n\nexport interface UpgradeOptions {\n dryRun?: boolean;\n yes?: boolean;\n}\n\nexport async function upgrade(opts: UpgradeOptions): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n throw new Error(\"No .easy-coding/config.yaml found. Run easy-coding init first.\");\n }\n\n const config = await readConfigYaml(configPath);\n const relation = compareVersions(config.harness_version, VERSION);\n\n if (relation === 0) {\n outro(chalk.green(`easy-coding harness is already up to date (${VERSION}).`));\n return;\n }\n\n if (relation === 1) {\n throw new Error(\n `Project harness version ${config.harness_version} is newer than CLI ${VERSION}. Update the CLI first.`,\n );\n }\n\n const summary = [\n `Upgrade: ${config.harness_version} -> ${VERSION}`,\n `Installed agents: ${config.agents.join(\", \")}`,\n \"Will overwrite managed skills, hooks, agents, and generated main-constraint regions.\",\n \"Will not touch tasks, memory, state, spec, or project knowledge files.\",\n ].join(\"\\n\");\n\n if (opts.dryRun) {\n console.log(summary);\n return;\n }\n\n if (!opts.yes) {\n const shouldUpgrade = await confirm({\n message: \"Apply this harness upgrade?\",\n initialValue: true,\n });\n if (typeof shouldUpgrade === \"symbol\" || !shouldUpgrade) {\n cancel(\"Upgrade cancelled.\");\n return;\n }\n }\n\n for (const platform of config.agents) {\n await CONFIGURATORS[platform](cwd);\n }\n await updateHarnessVersion(configPath, VERSION);\n\n outro(chalk.green(`easy-coding harness upgraded to ${VERSION}.`));\n}\n"],"mappings":"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","names":["chalk","path","chalk","path","stat","path","path","path","stat","path","path","path","existsSync","path","path","existsSync","path","fileURLToPath","readFile","readFile","path","chalk","outro","chalk","path","path","readdir","path","path","readdir","path","path","readdir","path","path","readdir","outro","chalk","path","chalk","path","path","path","path","path","chalk","status","path","cancel","confirm","outro","chalk","path","outro","chalk","confirm","cancel","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/commands/add-agent.ts","../src/configurators/claude.ts","../src/types/platform.ts","../src/configurators/shared.ts","../src/constants/paths.ts","../src/utils/file-writer.ts","../src/utils/marked-region.ts","../src/utils/template-paths.ts","../src/configurators/codex.ts","../src/configurators/qoder.ts","../src/configurators/index.ts","../src/ui/banner.ts","../src/constants/version.ts","../src/utils/config-yaml.ts","../src/commands/platforms.ts","../src/commands/init.ts","../src/utils/gitignore.ts","../src/utils/install-state.ts","../src/utils/runtime-scaffold.ts","../src/utils/task-json.ts","../src/commands/status.ts","../src/utils/compare-versions.ts","../src/utils/state-json.ts","../src/commands/upgrade.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport { addAgent } from \"./commands/add-agent.js\";\nimport { init } from \"./commands/init.js\";\nimport { status } from \"./commands/status.js\";\nimport { upgrade } from \"./commands/upgrade.js\";\nimport { PACKAGE_NAME, VERSION } from \"./constants/version.js\";\nimport { checkForUpgrade } from \"./utils/compare-versions.js\";\n\ntype CommandAction<T> = (opts: T) => Promise<void>;\n\nfunction withErrorHandling<T>(fn: CommandAction<T>) {\n return async (opts: T) => {\n try {\n await fn(opts);\n } catch (error) {\n console.error(chalk.red(\"Error:\"), error instanceof Error ? error.message : error);\n if (process.env.EC_DEBUG && error instanceof Error) {\n console.error(error.stack);\n }\n process.exit(1);\n }\n };\n}\n\nawait checkForUpgrade(process.cwd());\n\nconst program = new Command();\n\nprogram.name(\"easy-coding\").description(PACKAGE_NAME).version(VERSION, \"-v, --version\");\n\nprogram\n .command(\"init\")\n .description(\"Initialize easy-coding harness in current project\")\n .option(\"--agent <list>\", \"Comma-separated platforms: claude-code,codex,qoder\")\n .option(\"-y, --yes\", \"Skip prompts, use defaults\")\n .action(withErrorHandling(init));\n\nprogram\n .command(\"add-agent\")\n .description(\"Add agent platform support to an existing project\")\n .option(\"--agent <list>\", \"Comma-separated platforms to add\")\n .action(withErrorHandling(addAgent));\n\nprogram\n .command(\"upgrade\")\n .description(\"Upgrade harness files to current CLI version\")\n .option(\"--dry-run\", \"Preview changes without applying\")\n .option(\"-y, --yes\", \"Skip confirmation\")\n .action(withErrorHandling(upgrade));\n\nprogram\n .command(\"status\")\n .description(\"Show installed agents, version, and tasks\")\n .action(withErrorHandling(status));\n\nprogram.parse();\n","import path from \"node:path\";\nimport { outro } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { CONFIGURATORS } from \"../configurators/index.js\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { addAgentsToConfig, readConfigYaml } from \"../utils/config-yaml.js\";\nimport { pathExists } from \"../utils/file-writer.js\";\nimport { type PlatformOptions, resolvePlatforms } from \"./platforms.js\";\n\nexport async function addAgent(opts: PlatformOptions): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n throw new Error(\"No .easy-coding/config.yaml found. Run easy-coding init first.\");\n }\n\n const config = await readConfigYaml(configPath);\n const platforms = await resolvePlatforms(opts, [\"claude-code\"]);\n const toInstall = platforms.filter((platform) => !config.agents.includes(platform));\n\n if (toInstall.length === 0) {\n outro(chalk.yellow(\"All selected agent platforms are already installed.\"));\n return;\n }\n\n for (const platform of toInstall) {\n await CONFIGURATORS[platform](cwd);\n }\n await addAgentsToConfig(configPath, toInstall);\n\n outro(chalk.green(`Added agent platforms: ${toInstall.join(\", \")}`));\n}\n","import path from \"node:path\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport {\n copyPlatformTemplates,\n resolveBundledSkills,\n resolveSkills,\n writeMainConstraint,\n writeSharedHooks,\n writeSkills,\n} from \"./shared.js\";\n\nexport async function configureClaude(cwd: string): Promise<void> {\n const platform = \"claude-code\";\n const meta = PLATFORM_META[platform];\n const ctx = meta.templateContext;\n const dest = path.join(cwd, \".claude\");\n\n await copyPlatformTemplates(\"claude\", dest, [\"hooks\"], ctx);\n await writeSharedHooks(path.join(dest, \"hooks\"), platform);\n await writeSkills(\n path.join(cwd, meta.skillsDir),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\n );\n await writeMainConstraint(cwd, platform);\n}\n","export type AgentPlatform = \"claude-code\" | \"codex\" | \"qoder\";\n\nexport interface TemplateContext {\n sub_agent_dispatch: string;\n platform_spawn_instruction: string;\n skill_trigger: \"/\" | \"$\";\n workflow_state_path: string;\n main_constraint_file: \"CLAUDE.md\" | \"AGENTS.md\";\n python_cmd: string;\n platform_config_dir: string;\n}\n\nexport interface PlatformMeta {\n label: string;\n skillsDir: string;\n hooksDir: string;\n hookConfigFile: string;\n agentsDir: string;\n agentFileExt: \".md\" | \".toml\";\n mainConstraint: \"CLAUDE.md\" | \"AGENTS.md\";\n skillTrigger: \"/\" | \"$\";\n hookEvents: string[];\n stateInjectEvent: string[];\n hasSubagentContext: boolean;\n cnVariant?: string;\n templateContext: TemplateContext;\n}\n\nconst pythonCmd = process.platform === \"win32\" ? \"python\" : \"python3\";\n\nexport const PLATFORM_META: Record<AgentPlatform, PlatformMeta> = {\n \"claude-code\": {\n label: \"Claude Code\",\n skillsDir: \".claude/skills\",\n hooksDir: \".claude/hooks\",\n hookConfigFile: \".claude/settings.json\",\n agentsDir: \".claude/agents\",\n agentFileExt: \".md\",\n mainConstraint: \"CLAUDE.md\",\n skillTrigger: \"/\",\n hookEvents: [\"SessionStart\", \"PreToolUse\", \"UserPromptSubmit\", \"Stop\"],\n stateInjectEvent: [\"SessionStart\", \"UserPromptSubmit\"],\n hasSubagentContext: true,\n templateContext: {\n sub_agent_dispatch: \"Agent tool\",\n platform_spawn_instruction:\n 'Use the Agent tool with run_in_background when useful; use isolation: \"worktree\" for parallel file edits.',\n skill_trigger: \"/\",\n workflow_state_path: \".easy-coding/state.json\",\n main_constraint_file: \"CLAUDE.md\",\n python_cmd: pythonCmd,\n platform_config_dir: \".claude\",\n },\n },\n codex: {\n label: \"Codex\",\n skillsDir: \".agents/skills\",\n hooksDir: \".codex/hooks\",\n hookConfigFile: \".codex/hooks.json\",\n agentsDir: \".codex/agents\",\n agentFileExt: \".toml\",\n mainConstraint: \"AGENTS.md\",\n skillTrigger: \"$\",\n hookEvents: [\"UserPromptSubmit\"],\n stateInjectEvent: [\"UserPromptSubmit\"],\n hasSubagentContext: false,\n templateContext: {\n sub_agent_dispatch: \"Codex sub-agent dispatch\",\n platform_spawn_instruction:\n \"Use Codex sub-agent delegation where available; pass the full task card in the prompt.\",\n skill_trigger: \"$\",\n workflow_state_path: \".easy-coding/state.json\",\n main_constraint_file: \"AGENTS.md\",\n python_cmd: pythonCmd,\n platform_config_dir: \".codex\",\n },\n },\n qoder: {\n label: \"Qoder\",\n skillsDir: \".qoder/skills\",\n hooksDir: \".qoder/hooks\",\n hookConfigFile: \".qoder/settings.json\",\n agentsDir: \".qoder/agents\",\n agentFileExt: \".md\",\n mainConstraint: \"AGENTS.md\",\n skillTrigger: \"/\",\n hookEvents: [\"UserPromptSubmit\", \"PreToolUse\", \"Stop\"],\n stateInjectEvent: [\"UserPromptSubmit\"],\n hasSubagentContext: true,\n cnVariant: \".qodercn\",\n templateContext: {\n sub_agent_dispatch: \"Agent tool\",\n platform_spawn_instruction:\n \"Use the Agent tool with worktree isolation for parallel file edits.\",\n skill_trigger: \"/\",\n workflow_state_path: \".easy-coding/state.json\",\n main_constraint_file: \"AGENTS.md\",\n python_cmd: pythonCmd,\n platform_config_dir: \".qoder\",\n },\n },\n};\n\nexport const AGENT_PLATFORMS = Object.keys(PLATFORM_META) as AgentPlatform[];\n\nexport function isAgentPlatform(value: string): value is AgentPlatform {\n return Object.hasOwn(PLATFORM_META, value);\n}\n","import { readdir, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { GENERATED_REGION_END, GENERATED_REGION_START } from \"../constants/paths.js\";\nimport type { AgentPlatform, TemplateContext } from \"../types/platform.js\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport {\n ensureDir,\n isDirectory,\n pathExists,\n readTextFile,\n readTextIfExists,\n writeTextFile,\n} from \"../utils/file-writer.js\";\nimport { replaceMarkedRegion } from \"../utils/marked-region.js\";\nimport { getTemplatePath } from \"../utils/template-paths.js\";\n\nexport interface SkillTemplate {\n name: string;\n content: string;\n}\n\nexport interface BundledSkillTemplate {\n name: string;\n sourceDir: string;\n context: TemplateContext;\n}\n\nexport interface SharedHookOptions {\n skipSubagentContext?: boolean;\n}\n\nexport class UnresolvedPlaceholderError extends Error {\n constructor(contentName: string, placeholders: string[]) {\n super(`Unresolved placeholders in ${contentName}: ${placeholders.join(\", \")}`);\n this.name = \"UnresolvedPlaceholderError\";\n }\n}\n\nexport function resolvePlaceholders(\n content: string,\n ctx: TemplateContext,\n contentName = \"template\",\n): string {\n const withPython = content.replace(/\\{\\{PYTHON_CMD\\}\\}/g, ctx.python_cmd);\n const resolved = withPython.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key: keyof TemplateContext) => {\n const value = ctx[key];\n return value === undefined ? match : String(value);\n });\n\n const unresolved = [...resolved.matchAll(/\\{\\{[^}]+\\}\\}/g)].map((match) => match[0]);\n if (unresolved.length > 0) {\n throw new UnresolvedPlaceholderError(contentName, unresolved);\n }\n\n return resolved;\n}\n\nexport async function resolveSkills(ctx: TemplateContext): Promise<SkillTemplate[]> {\n const skillsRoot = getTemplatePath(\"common\", \"skills\");\n const entries = await readdir(skillsRoot);\n const skills: SkillTemplate[] = [];\n\n for (const entry of entries.sort()) {\n const skillDir = path.join(skillsRoot, entry);\n if (!(await isDirectory(skillDir))) {\n continue;\n }\n const content = await readTextFile(path.join(skillDir, \"SKILL.md\"));\n skills.push({\n name: entry,\n content: resolvePlaceholders(content, ctx, `common/skills/${entry}/SKILL.md`),\n });\n }\n\n return skills;\n}\n\nexport async function resolveBundledSkills(ctx: TemplateContext): Promise<BundledSkillTemplate[]> {\n const bundledRoot = getTemplatePath(\"common\", \"bundled-skills\");\n if (!(await pathExists(bundledRoot))) {\n return [];\n }\n\n const entries = await readdir(bundledRoot);\n const bundled: BundledSkillTemplate[] = [];\n for (const entry of entries.sort()) {\n const sourceDir = path.join(bundledRoot, entry);\n if (await isDirectory(sourceDir)) {\n bundled.push({ name: entry, sourceDir, context: ctx });\n }\n }\n return bundled;\n}\n\nexport async function writeSkills(\n dir: string,\n skills: SkillTemplate[],\n bundled: BundledSkillTemplate[],\n): Promise<void> {\n await ensureDir(dir);\n\n for (const skill of skills) {\n await writeTextFile(path.join(dir, skill.name, \"SKILL.md\"), skill.content);\n }\n\n for (const skill of bundled) {\n await copyTemplateDirectory(skill.sourceDir, path.join(dir, skill.name), skill.context);\n }\n}\n\nexport async function writeSharedHooks(\n dir: string,\n platform: AgentPlatform,\n opts: SharedHookOptions = {},\n): Promise<void> {\n const hooksRoot = getTemplatePath(\"shared-hooks\");\n const ctx = PLATFORM_META[platform].templateContext;\n const entries = await readdir(hooksRoot);\n await ensureDir(dir);\n\n for (const entry of entries.sort()) {\n if (opts.skipSubagentContext && entry === \"inject-subagent-context.py\") {\n continue;\n }\n const sourcePath = path.join(hooksRoot, entry);\n if ((await stat(sourcePath)).isDirectory()) {\n continue;\n }\n const content = resolvePlaceholders(\n await readTextFile(sourcePath),\n ctx,\n `shared-hooks/${entry}`,\n );\n const destination = path.join(dir, entry);\n await writeTextFile(destination, content);\n await import(\"node:fs/promises\").then(({ chmod }) => chmod(destination, 0o755));\n }\n}\n\nexport async function copyPlatformTemplates(\n platformTemplateDir: string,\n destination: string,\n skipDirs: string[],\n ctx: TemplateContext,\n): Promise<void> {\n const source = getTemplatePath(platformTemplateDir);\n await copyTemplateDirectory(source, destination, ctx, new Set(skipDirs));\n}\n\nexport async function writeMainConstraint(cwd: string, platform: AgentPlatform): Promise<void> {\n const meta = PLATFORM_META[platform];\n const ctx = meta.templateContext;\n const templateName = `${meta.mainConstraint}.tpl`;\n const template = await readTextFile(getTemplatePath(\"main-constraint\", templateName));\n const generated = resolvePlaceholders(template, ctx, `main-constraint/${templateName}`);\n\n if (!generated.includes(GENERATED_REGION_START) || !generated.includes(GENERATED_REGION_END)) {\n throw new Error(`Main constraint template ${templateName} does not contain generated markers.`);\n }\n\n const destination = path.join(cwd, meta.mainConstraint);\n const current = await readTextIfExists(destination);\n const next = current ? replaceMarkedRegion(current, generated) : generated;\n await writeTextFile(destination, next);\n}\n\nasync function copyTemplateDirectory(\n source: string,\n destination: string,\n ctx: TemplateContext,\n skipDirs = new Set<string>(),\n): Promise<void> {\n await ensureDir(destination);\n\n for (const entry of (await readdir(source)).sort()) {\n if (skipDirs.has(entry) || shouldSkipTemplateEntry(entry)) {\n continue;\n }\n\n const sourcePath = path.join(source, entry);\n const destinationPath = path.join(destination, stripTemplateExtension(entry));\n const sourceStat = await stat(sourcePath);\n\n if (sourceStat.isDirectory()) {\n await copyTemplateDirectory(sourcePath, destinationPath, ctx, skipDirs);\n continue;\n }\n\n const content = resolvePlaceholders(await readTextFile(sourcePath), ctx, sourcePath);\n await writeTextFile(destinationPath, content);\n }\n}\n\nfunction shouldSkipTemplateEntry(entry: string): boolean {\n return (\n entry === \".DS_Store\" ||\n entry === \"__pycache__\" ||\n entry.endsWith(\".ts\") ||\n entry.endsWith(\".js\") ||\n entry.endsWith(\".map\")\n );\n}\n\nfunction stripTemplateExtension(fileName: string): string {\n return fileName.endsWith(\".tpl\") ? fileName.slice(0, -4) : fileName;\n}\n","export const EASY_CODING_DIR = \".easy-coding\";\nexport const CONFIG_FILE = \"config.yaml\";\nexport const STATE_FILE = \"state.json\";\nexport const TASKS_DIR = \"tasks\";\nexport const PROJECT_INIT_TASK_ID = \"project-init\";\nexport const MEMORY_DIR = \"memory\";\nexport const SPEC_DIR = \"spec\";\nexport const MAIN_SPEC_DIR = \"main\";\nexport const DEV_SPEC_DIR = \"dev\";\n\nexport const STATE_GITIGNORE_ENTRY = \".easy-coding/state.json\";\n\nexport const GENERATED_REGION_START =\n \"<!-- ═══ easy-coding-harness generated (DO NOT EDIT BETWEEN MARKERS) ═══ -->\";\nexport const GENERATED_REGION_END = \"<!-- ═══ end easy-coding-harness generated ═══ -->\";\n","import { constants } from \"node:fs\";\nimport { access, cp, writeFile as fsWriteFile, mkdir, readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function pathExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n\nexport async function writeTextFile(filePath: string, content: string): Promise<void> {\n await ensureDir(path.dirname(filePath));\n await fsWriteFile(filePath, content.endsWith(\"\\n\") ? content : `${content}\\n`, \"utf8\");\n}\n\nexport async function readTextFile(filePath: string): Promise<string> {\n return readFile(filePath, \"utf8\");\n}\n\nexport async function readTextIfExists(filePath: string): Promise<string | null> {\n if (!(await pathExists(filePath))) {\n return null;\n }\n return readTextFile(filePath);\n}\n\nexport async function copyDir(source: string, destination: string): Promise<void> {\n await ensureDir(destination);\n await cp(source, destination, { recursive: true });\n}\n\nexport async function isDirectory(filePath: string): Promise<boolean> {\n try {\n return (await stat(filePath)).isDirectory();\n } catch {\n return false;\n }\n}\n","import { GENERATED_REGION_END, GENERATED_REGION_START } from \"../constants/paths.js\";\n\nexport class MarkedRegionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"MarkedRegionError\";\n }\n}\n\nexport function extractMarkedRegion(content: string): string | null {\n const start = content.indexOf(GENERATED_REGION_START);\n const end = content.indexOf(GENERATED_REGION_END);\n\n if (start === -1 && end === -1) {\n return null;\n }\n if (start === -1 || end === -1 || end < start) {\n throw new MarkedRegionError(\"Generated region markers are incomplete or out of order.\");\n }\n\n return content.slice(start, end + GENERATED_REGION_END.length);\n}\n\nexport function replaceMarkedRegion(existing: string, generatedRegion: string): string {\n const currentRegion = extractMarkedRegion(existing);\n if (!currentRegion) {\n return `${generatedRegion.trim()}\\n\\n${existing.trimStart()}`.trimEnd();\n }\n return existing.replace(currentRegion, generatedRegion.trim());\n}\n\nexport function hasMarkedRegion(content: string): boolean {\n return extractMarkedRegion(content) !== null;\n}\n","import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport function getTemplateRoot(): string {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"../templates\"),\n path.resolve(here, \"../../templates\"),\n path.resolve(process.cwd(), \"src/templates\"),\n path.resolve(process.cwd(), \"templates\"),\n ];\n\n const found = candidates.find((candidate) => existsSync(candidate));\n if (!found) {\n throw new Error(`Unable to locate templates directory. Tried: ${candidates.join(\", \")}`);\n }\n return found;\n}\n\nexport function getTemplatePath(...segments: string[]): string {\n return path.join(getTemplateRoot(), ...segments);\n}\n","import path from \"node:path\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport {\n copyPlatformTemplates,\n resolveBundledSkills,\n resolveSkills,\n writeMainConstraint,\n writeSharedHooks,\n writeSkills,\n} from \"./shared.js\";\n\nexport async function configureCodex(cwd: string): Promise<void> {\n const platform = \"codex\";\n const meta = PLATFORM_META[platform];\n const ctx = meta.templateContext;\n\n await writeSkills(\n path.join(cwd, meta.skillsDir),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\n );\n await writeSharedHooks(path.join(cwd, meta.hooksDir), platform, { skipSubagentContext: true });\n await copyPlatformTemplates(\"codex\", path.join(cwd, \".codex\"), [\"hooks\"], ctx);\n await writeMainConstraint(cwd, platform);\n}\n","import { existsSync, readdirSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { PLATFORM_META, type TemplateContext } from \"../types/platform.js\";\nimport {\n copyPlatformTemplates,\n resolveBundledSkills,\n resolveSkills,\n writeMainConstraint,\n writeSharedHooks,\n writeSkills,\n} from \"./shared.js\";\n\nfunction claudeSkillsExist(cwd: string): boolean {\n const claudeSkillsDir = path.join(cwd, \".claude\", \"skills\");\n if (!existsSync(claudeSkillsDir)) return false;\n try {\n return readdirSync(claudeSkillsDir).some((d) => d.startsWith(\"ec-\"));\n } catch {\n return false;\n }\n}\n\nexport async function configureQoder(cwd: string): Promise<void> {\n const platform = \"qoder\";\n const baseDir = detectQoderCnVariant(cwd) ? \".qodercn\" : \".qoder\";\n const ctx: TemplateContext = {\n ...PLATFORM_META[platform].templateContext,\n platform_config_dir: baseDir,\n };\n const dest = path.join(cwd, baseDir);\n\n await copyPlatformTemplates(\"qoder\", dest, [\"hooks\"], ctx);\n await writeSharedHooks(path.join(dest, \"hooks\"), platform);\n\n // Qoder scans both .qoder/skills/ and .claude/skills/ at runtime.\n // Skip writing to .qoder/skills/ when Claude Code skills already exist to avoid duplicates.\n if (!claudeSkillsExist(cwd)) {\n await writeSkills(\n path.join(dest, \"skills\"),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\n );\n }\n\n await writeMainConstraint(cwd, platform);\n}\n\nexport function detectQoderCnVariant(cwd: string): boolean {\n if (process.env.EC_QODER_VARIANT === \"cn\" || process.env.QODER_VARIANT === \"cn\") {\n return true;\n }\n return existsSync(path.join(cwd, \".qodercn\"));\n}\n","import type { AgentPlatform } from \"../types/platform.js\";\nimport { configureClaude } from \"./claude.js\";\nimport { configureCodex } from \"./codex.js\";\nimport { configureQoder } from \"./qoder.js\";\n\nexport type ConfigureFn = (cwd: string) => Promise<void>;\n\nexport const CONFIGURATORS: Record<AgentPlatform, ConfigureFn> = {\n \"claude-code\": configureClaude,\n codex: configureCodex,\n qoder: configureQoder,\n};\n","import chalk from \"chalk\";\nimport figlet from \"figlet\";\nimport { VERSION } from \"../constants/version.js\";\n\nexport interface BannerPreset {\n font: \"ANSI Shadow\" | \"Small Shadow\" | \"Small Slant\";\n horizontalLayout?: \"full\";\n}\n\nconst WIDE_TERMINAL_WIDTH = 88;\nconst MEDIUM_TERMINAL_WIDTH = 72;\n\nexport function renderBanner(): void {\n const terminalWidth = process.stdout.columns ?? 120;\n const art = figlet.textSync(\"Easy Coding\", selectBannerPreset(terminalWidth));\n console.log(colorizeBanner(art));\n console.log(chalk.bold.white(` Easy Coding Harness ${chalk.cyan(`v${VERSION}`)}\\n`));\n}\n\nexport function selectBannerPreset(width: number): BannerPreset {\n if (width >= WIDE_TERMINAL_WIDTH) {\n return { font: \"ANSI Shadow\", horizontalLayout: \"full\" };\n }\n\n if (width >= MEDIUM_TERMINAL_WIDTH) {\n return { font: \"Small Shadow\", horizontalLayout: \"full\" };\n }\n\n return { font: \"Small Slant\" };\n}\n\nexport function colorizeBanner(art: string): string {\n const colors = [chalk.cyanBright, chalk.cyan, chalk.blueBright, chalk.blue];\n return art\n .split(\"\\n\")\n .map((line, index) => colors[index % colors.length](line))\n .join(\"\\n\");\n}\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ninterface PackageMetadata {\n name: string;\n version: string;\n}\n\nconst packageMetadata = readPackageMetadata();\n\nexport const PACKAGE_NAME = packageMetadata.name;\nexport const VERSION = packageMetadata.version;\n\nfunction readPackageMetadata(): PackageMetadata {\n const here = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(here, \"../../package.json\"),\n path.resolve(here, \"../package.json\"),\n path.resolve(process.cwd(), \"package.json\"),\n ];\n\n for (const candidate of candidates) {\n try {\n const parsed = JSON.parse(readFileSync(candidate, \"utf8\")) as Partial<PackageMetadata>;\n if (parsed.name && parsed.version) {\n return { name: parsed.name, version: parsed.version };\n }\n } catch {\n // Try the next candidate; source, dist, and installed layouts differ.\n }\n }\n\n throw new Error(`Unable to locate package metadata. Tried: ${candidates.join(\", \")}`);\n}\n","import { readFile } from \"node:fs/promises\";\nimport YAML, { isScalar, isSeq, parseDocument } from \"yaml\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport { writeTextFile } from \"./file-writer.js\";\n\nexport interface EasyCodingConfig {\n version: number;\n harness_version: string;\n agents: AgentPlatform[];\n project: {\n name: string;\n mode: \"auto\" | \"startup\" | \"iterative\";\n };\n memory: {\n short_term_max: number;\n short_term_keep: number;\n schema_version: number;\n };\n test: {\n framework: string;\n command: string;\n };\n tasks: {\n auto_archive_days: number;\n };\n behavior: {\n strict_confirm: boolean;\n auto_mode: boolean;\n };\n [key: string]: unknown;\n}\n\nexport function createDefaultConfig(params: {\n projectName: string;\n harnessVersion: string;\n agents: AgentPlatform[];\n}): EasyCodingConfig {\n return {\n version: 1,\n harness_version: params.harnessVersion,\n agents: params.agents,\n project: {\n name: params.projectName,\n mode: \"auto\",\n },\n memory: {\n short_term_max: 10,\n short_term_keep: 5,\n schema_version: 2,\n },\n test: {\n framework: \"auto\",\n command: \"\",\n },\n tasks: {\n auto_archive_days: 30,\n },\n behavior: {\n strict_confirm: true,\n auto_mode: false,\n },\n };\n}\n\nexport function stringifyConfig(config: EasyCodingConfig): string {\n return YAML.stringify(config);\n}\n\nexport async function writeConfigYaml(filePath: string, config: EasyCodingConfig): Promise<void> {\n await writeTextFile(filePath, stringifyConfig(config));\n}\n\nexport async function readConfigYaml(filePath: string): Promise<EasyCodingConfig> {\n const content = await readFile(filePath, \"utf8\");\n return YAML.parse(content) as EasyCodingConfig;\n}\n\nexport async function updateConfigYaml(\n filePath: string,\n updater: (config: EasyCodingConfig) => void,\n): Promise<EasyCodingConfig> {\n const content = await readFile(filePath, \"utf8\");\n const document = parseDocument(content);\n const config = document.toJSON() as EasyCodingConfig;\n updater(config);\n\n for (const [key, value] of Object.entries(config)) {\n document.set(key, value);\n }\n\n await writeTextFile(filePath, document.toString());\n return config;\n}\n\nexport async function addAgentsToConfig(\n filePath: string,\n agents: AgentPlatform[],\n): Promise<EasyCodingConfig> {\n return updateConfigYaml(filePath, (config) => {\n const merged = new Set([...(config.agents ?? []), ...agents]);\n config.agents = [...merged];\n });\n}\n\nexport async function updateHarnessVersion(\n filePath: string,\n version: string,\n): Promise<EasyCodingConfig> {\n return updateConfigYaml(filePath, (config) => {\n config.harness_version = version;\n });\n}\n\nexport function yamlHasAgent(documentContent: string, agent: AgentPlatform): boolean {\n const document = parseDocument(documentContent);\n const agents = document.get(\"agents\", true);\n if (!isSeq(agents)) {\n return false;\n }\n return agents.items.some((item) => isScalar(item) && String(item.value) === agent);\n}\n","import { cancel, confirm, multiselect } from \"@clack/prompts\";\nimport {\n AGENT_PLATFORMS,\n type AgentPlatform,\n PLATFORM_META,\n isAgentPlatform,\n} from \"../types/platform.js\";\n\nexport interface PlatformOptions {\n agent?: string;\n yes?: boolean;\n}\n\nexport function parseAgentList(agentList: string): AgentPlatform[] {\n const values = agentList\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n\n if (values.length === 0) {\n throw new Error(\"No agent platform specified.\");\n }\n\n const invalid = values.filter((value) => !isAgentPlatform(value));\n if (invalid.length > 0) {\n throw new Error(`Unknown agent platform: ${invalid.join(\", \")}`);\n }\n\n return [...new Set(values)] as AgentPlatform[];\n}\n\nexport async function resolvePlatforms(\n opts: PlatformOptions,\n defaults: AgentPlatform[] = [\"claude-code\"],\n): Promise<AgentPlatform[]> {\n if (opts.agent) {\n return parseAgentList(opts.agent);\n }\n\n if (opts.yes) {\n return defaults;\n }\n\n let selectedDefaults = defaults;\n\n while (true) {\n const result = await multiselect({\n message: \"Select agent platforms (Space to toggle, Enter to review)\",\n options: AGENT_PLATFORMS.map((platform) => ({\n label: PLATFORM_META[platform].label,\n value: platform,\n })),\n initialValues: selectedDefaults,\n required: true,\n });\n\n if (typeof result === \"symbol\") {\n cancel(\"Platform selection cancelled.\");\n process.exit(1);\n }\n\n const labels = result.map((platform) => PLATFORM_META[platform].label).join(\", \");\n const shouldInstall = await confirm({\n message: `Install Easy Coding for: ${labels}?`,\n initialValue: true,\n });\n\n if (typeof shouldInstall === \"symbol\") {\n cancel(\"Platform selection cancelled.\");\n process.exit(1);\n }\n\n if (shouldInstall) {\n return result;\n }\n\n selectedDefaults = result;\n }\n}\n","import { note, outro } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { CONFIGURATORS } from \"../configurators/index.js\";\nimport { PLATFORM_META } from \"../types/platform.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { ensureEasyCodingStateIgnored } from \"../utils/gitignore.js\";\nimport { detectEasyCodingInstallState } from \"../utils/install-state.js\";\nimport { writeRuntimeScaffold } from \"../utils/runtime-scaffold.js\";\nimport { writeProjectInitTask } from \"../utils/task-json.js\";\nimport { type PlatformOptions, resolvePlatforms } from \"./platforms.js\";\n\nexport async function init(opts: PlatformOptions): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const installState = await detectEasyCodingInstallState(cwd);\n if (installState.kind === \"installed\") {\n throw new Error(\n \".easy-coding/config.yaml already exists. Use easy-coding add-agent or easy-coding upgrade.\",\n );\n }\n if (installState.kind === \"unknown\") {\n throw new Error(\n \".easy-coding exists but is not recognized as an easy-coding harness or legacy easy-coding skill project. Please inspect it manually before running init.\",\n );\n }\n\n const platforms = await resolvePlatforms(opts, [\"claude-code\"]);\n\n for (const platform of platforms) {\n await CONFIGURATORS[platform](cwd);\n }\n\n await writeRuntimeScaffold(cwd, platforms);\n await writeProjectInitTask(cwd, platforms, {\n initSource: installState.kind === \"legacy\" ? \"legacy-easy-coding\" : \"fresh\",\n legacyAssets: installState.kind === \"legacy\" ? installState.legacyAssets : undefined,\n legacyMissingHarnessFiles:\n installState.kind === \"legacy\" ? installState.missingHarnessFiles : undefined,\n });\n await ensureEasyCodingStateIgnored(cwd);\n\n const triggers = platforms\n .map(\n (platform) =>\n `${PLATFORM_META[platform].label}: ${PLATFORM_META[platform].skillTrigger}ec-init`,\n )\n .join(\"\\n\");\n\n note(triggers, \"Next step\");\n outro(chalk.green(\"easy-coding harness installed. Open your agent and run ec-init.\"));\n}\n","import path from \"node:path\";\nimport { STATE_GITIGNORE_ENTRY } from \"../constants/paths.js\";\nimport { readTextIfExists, writeTextFile } from \"./file-writer.js\";\n\nexport async function ensureGitignoreEntry(\n cwd: string,\n entry: string,\n heading = \"# ═══ easy-coding-harness (auto-generated) ═══\\n# Personal runtime state; do not commit\",\n): Promise<boolean> {\n const gitignorePath = path.join(cwd, \".gitignore\");\n const current = (await readTextIfExists(gitignorePath)) ?? \"\";\n const lines = current.split(/\\r?\\n/).map((line) => line.trim());\n\n if (lines.includes(entry)) {\n return false;\n }\n\n const prefix = current.trimEnd();\n const next = [prefix, heading, entry].filter(Boolean).join(\"\\n\");\n await writeTextFile(gitignorePath, next);\n return true;\n}\n\nexport function gitignoreContains(content: string, entry: string): boolean {\n return content\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .includes(entry);\n}\n\nexport async function ensureEasyCodingStateIgnored(cwd: string): Promise<boolean> {\n return ensureGitignoreEntry(cwd, STATE_GITIGNORE_ENTRY);\n}\n","import { readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n CONFIG_FILE,\n EASY_CODING_DIR,\n PROJECT_INIT_TASK_ID,\n TASKS_DIR,\n} from \"../constants/paths.js\";\nimport { isDirectory, pathExists } from \"./file-writer.js\";\n\nexport type EasyCodingInstallState =\n | { kind: \"fresh\"; easyCodingDir: string }\n | { kind: \"installed\"; easyCodingDir: string; configPath: string }\n | {\n kind: \"legacy\";\n easyCodingDir: string;\n legacyAssets: string[];\n missingHarnessFiles: string[];\n }\n | { kind: \"unknown\"; easyCodingDir: string };\n\nconst LEGACY_ROOT_FILES = [\"SOUL.md\", \"RULES.md\", \"ABSTRACT.md\"];\n\nexport async function detectEasyCodingInstallState(cwd: string): Promise<EasyCodingInstallState> {\n const easyCodingDir = path.join(cwd, EASY_CODING_DIR);\n if (!(await pathExists(easyCodingDir))) {\n return { kind: \"fresh\", easyCodingDir };\n }\n\n const configPath = path.join(easyCodingDir, CONFIG_FILE);\n if (await pathExists(configPath)) {\n return { kind: \"installed\", easyCodingDir, configPath };\n }\n\n const legacyAssets = await detectLegacyAssets(easyCodingDir);\n if (legacyAssets.length === 0) {\n return { kind: \"unknown\", easyCodingDir };\n }\n\n return {\n kind: \"legacy\",\n easyCodingDir,\n legacyAssets,\n missingHarnessFiles: [relativeConfigPath(), relativeProjectInitTaskPath()],\n };\n}\n\nasync function detectLegacyAssets(easyCodingDir: string): Promise<string[]> {\n const assets: string[] = [];\n\n for (const file of LEGACY_ROOT_FILES) {\n if (await pathExists(path.join(easyCodingDir, file))) {\n assets.push(relativeEasyCodingPath(file));\n }\n }\n\n if (await pathExists(path.join(easyCodingDir, \"memory\", \"long\", \"MEMORY.md\"))) {\n assets.push(relativeEasyCodingPath(\"memory\", \"long\", \"MEMORY.md\"));\n }\n\n const shortMemoryFiles = await listMarkdownFiles(path.join(easyCodingDir, \"memory\", \"short\"));\n assets.push(...shortMemoryFiles.map((file) => relativeEasyCodingPath(\"memory\", \"short\", file)));\n\n for (const dir of [\"spec\", \"prototype\"]) {\n if (await hasAnyDirectoryEntry(path.join(easyCodingDir, dir))) {\n assets.push(relativeEasyCodingPath(dir));\n }\n }\n\n return assets.sort();\n}\n\nasync function listMarkdownFiles(dir: string): Promise<string[]> {\n if (!(await isDirectory(dir))) {\n return [];\n }\n\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".md\"))\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function hasAnyDirectoryEntry(dir: string): Promise<boolean> {\n if (!(await isDirectory(dir))) {\n return false;\n }\n\n return (await readdir(dir)).length > 0;\n}\n\nfunction relativeEasyCodingPath(...segments: string[]): string {\n return path.posix.join(EASY_CODING_DIR, ...segments);\n}\n\nfunction relativeConfigPath(): string {\n return path.posix.join(EASY_CODING_DIR, CONFIG_FILE);\n}\n\nfunction relativeProjectInitTaskPath(): string {\n return path.posix.join(EASY_CODING_DIR, TASKS_DIR, PROJECT_INIT_TASK_ID, \"task.json\");\n}\n","import path from \"node:path\";\nimport {\n CONFIG_FILE,\n DEV_SPEC_DIR,\n EASY_CODING_DIR,\n MAIN_SPEC_DIR,\n MEMORY_DIR,\n SPEC_DIR,\n} from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport { createDefaultConfig, writeConfigYaml } from \"./config-yaml.js\";\nimport { ensureDir, pathExists, readTextFile, writeTextFile } from \"./file-writer.js\";\nimport { getTemplatePath } from \"./template-paths.js\";\n\nexport async function writeRuntimeScaffold(cwd: string, agents: AgentPlatform[]): Promise<void> {\n const easyCodingDir = path.join(cwd, EASY_CODING_DIR);\n await ensureDir(easyCodingDir);\n\n const configPath = path.join(easyCodingDir, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n const projectName = path.basename(cwd);\n await writeConfigYaml(\n configPath,\n createDefaultConfig({ projectName, harnessVersion: VERSION, agents }),\n );\n }\n\n await ensureDir(path.join(easyCodingDir, \"tasks\"));\n await ensureDir(path.join(easyCodingDir, SPEC_DIR, MAIN_SPEC_DIR));\n await ensureDir(path.join(easyCodingDir, SPEC_DIR, DEV_SPEC_DIR));\n await writeMemoryScaffold(easyCodingDir);\n}\n\nasync function writeMemoryScaffold(easyCodingDir: string): Promise<void> {\n const memoryDir = path.join(easyCodingDir, MEMORY_DIR);\n await ensureDir(path.join(memoryDir, \"short\"));\n await ensureDir(path.join(memoryDir, \"long\"));\n\n for (const file of [\"MEMORY.md\", \"BUSINESS.md\", \"TECHNICAL.md\"]) {\n const destination = path.join(memoryDir, \"long\", file);\n if (await pathExists(destination)) {\n continue;\n }\n const templatePath = getTemplatePath(\"runtime\", \"memory\", \"long\", file);\n await writeTextFile(destination, await readTextFile(templatePath));\n }\n}\n","import { readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { EASY_CODING_DIR, PROJECT_INIT_TASK_ID, TASKS_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport type { TaskJson, TaskStatus } from \"../types/task.js\";\nimport { pathExists, readTextFile, writeTextFile } from \"./file-writer.js\";\n\nexport type ProjectInitSource = \"fresh\" | \"legacy-easy-coding\";\n\nexport function createProjectInitTask(params: {\n cwd: string;\n agents: AgentPlatform[];\n initSource?: ProjectInitSource;\n legacyAssets?: string[];\n legacyMissingHarnessFiles?: string[];\n now?: Date;\n}): TaskJson {\n return {\n type: \"project-init\",\n status: \"PENDING\",\n created_at: (params.now ?? new Date()).toISOString(),\n created_by: \"cli-init\",\n context: {\n agents_installed: params.agents,\n cli_version: VERSION,\n project_path: params.cwd,\n init_source: params.initSource ?? \"fresh\",\n ...(params.legacyAssets ? { legacy_assets: params.legacyAssets } : {}),\n ...(params.legacyMissingHarnessFiles\n ? { legacy_missing_harness_files: params.legacyMissingHarnessFiles }\n : {}),\n },\n init_log: [],\n };\n}\n\nexport function getTaskJsonPath(cwd: string, taskId: string): string {\n return path.join(cwd, EASY_CODING_DIR, TASKS_DIR, taskId, \"task.json\");\n}\n\nexport async function writeTaskJson(filePath: string, task: TaskJson): Promise<void> {\n await writeTextFile(filePath, JSON.stringify(task, null, 2));\n}\n\nexport async function readTaskJson(filePath: string): Promise<TaskJson> {\n return JSON.parse(await readTextFile(filePath)) as TaskJson;\n}\n\nexport async function writeProjectInitTask(\n cwd: string,\n agents: AgentPlatform[],\n options: {\n initSource?: ProjectInitSource;\n legacyAssets?: string[];\n legacyMissingHarnessFiles?: string[];\n } = {},\n): Promise<void> {\n await writeTaskJson(\n getTaskJsonPath(cwd, PROJECT_INIT_TASK_ID),\n createProjectInitTask({\n cwd,\n agents,\n initSource: options.initSource,\n legacyAssets: options.legacyAssets,\n legacyMissingHarnessFiles: options.legacyMissingHarnessFiles,\n }),\n );\n}\n\nexport interface TaskSummary {\n id: string;\n task: TaskJson;\n}\n\nexport async function listTasks(cwd: string): Promise<TaskSummary[]> {\n const tasksDir = path.join(cwd, EASY_CODING_DIR, TASKS_DIR);\n if (!(await pathExists(tasksDir))) {\n return [];\n }\n\n const entries = await readdir(tasksDir, { withFileTypes: true });\n const tasks: TaskSummary[] = [];\n\n for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {\n if (!entry.isDirectory()) {\n continue;\n }\n\n const taskPath = getTaskJsonPath(cwd, entry.name);\n if (!(await pathExists(taskPath))) {\n continue;\n }\n\n tasks.push({ id: entry.name, task: await readTaskJson(taskPath) });\n }\n\n return tasks;\n}\n\nexport function summarizeTaskStatuses(tasks: TaskSummary[]): Record<TaskStatus, number> {\n return tasks.reduce(\n (summary, item) => {\n summary[item.task.status] = (summary[item.task.status] ?? 0) + 1;\n return summary;\n },\n {} as Record<TaskStatus, number>,\n );\n}\n\nexport function isActiveTask(task: TaskJson): boolean {\n return task.status !== \"COMPLETE\" && task.status !== \"CLOSED\";\n}\n","import path from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { compareVersions } from \"../utils/compare-versions.js\";\nimport { readConfigYaml } from \"../utils/config-yaml.js\";\nimport { pathExists } from \"../utils/file-writer.js\";\nimport { readStateJson } from \"../utils/state-json.js\";\nimport { isActiveTask, listTasks, summarizeTaskStatuses } from \"../utils/task-json.js\";\n\nexport async function status(): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n throw new Error(\"No easy-coding harness found in this project.\");\n }\n\n const config = await readConfigYaml(configPath);\n const tasks = await listTasks(cwd);\n const taskCounts = summarizeTaskStatuses(tasks);\n const activeTasks = tasks.filter((item) => isActiveTask(item.task));\n const state = await readStateJson(cwd);\n const versionRelation = compareVersions(config.harness_version, VERSION);\n\n console.log(chalk.bold(\"Harness\"));\n console.log(` version: ${config.harness_version}`);\n console.log(` cli: ${VERSION}`);\n if (versionRelation === -1) {\n console.log(chalk.yellow(\" upgrade: available\"));\n } else if (versionRelation === 1) {\n console.log(chalk.red(\" upgrade: CLI is older than project harness\"));\n } else {\n console.log(\" upgrade: up to date\");\n }\n console.log(` agents: ${config.agents.join(\", \") || \"(none)\"}`);\n console.log(` project: ${config.project.name} (${config.project.mode})`);\n console.log(\"\");\n console.log(chalk.bold(\"State\"));\n if (state) {\n console.log(` current_stage: ${state.current_stage}`);\n console.log(` current_task: ${state.current_task ?? \"(none)\"}`);\n console.log(` last_agent: ${state.last_agent}`);\n } else {\n console.log(\" state.json: not created yet\");\n }\n console.log(\"\");\n console.log(chalk.bold(\"Tasks\"));\n console.log(` total: ${tasks.length}`);\n for (const [status, count] of Object.entries(taskCounts)) {\n console.log(` ${status}: ${count}`);\n }\n if (activeTasks.length > 0) {\n console.log(\" active:\");\n for (const item of activeTasks) {\n const label = item.task.title ? `${item.task.title} (${item.task.status})` : item.task.status;\n console.log(` - ${item.id}: ${label}`);\n }\n }\n}\n","import path from \"node:path\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport { readConfigYaml } from \"./config-yaml.js\";\nimport { pathExists } from \"./file-writer.js\";\n\nexport type VersionComparison = -1 | 0 | 1;\n\nfunction normalize(version: string): number[] {\n const [core] = version.split(\"-\");\n return core.split(\".\").map((part) => {\n const parsed = Number.parseInt(part, 10);\n return Number.isNaN(parsed) ? 0 : parsed;\n });\n}\n\nexport function compareVersions(a: string, b: string): VersionComparison {\n const left = normalize(a);\n const right = normalize(b);\n const length = Math.max(left.length, right.length);\n\n for (let index = 0; index < length; index += 1) {\n const leftPart = left[index] ?? 0;\n const rightPart = right[index] ?? 0;\n if (leftPart < rightPart) return -1;\n if (leftPart > rightPart) return 1;\n }\n return 0;\n}\n\nexport function isVersionBehind(installed: string, current = VERSION): boolean {\n return compareVersions(installed, current) === -1;\n}\n\nexport async function checkForUpgrade(cwd: string): Promise<void> {\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n return;\n }\n\n const config = await readConfigYaml(configPath);\n const installed = String(config.harness_version ?? \"\");\n if (installed && isVersionBehind(installed)) {\n process.stderr.write(\n `easy-coding harness ${installed} is older than CLI ${VERSION}. Run easy-coding upgrade when ready.\\n`,\n );\n }\n}\n","import path from \"node:path\";\nimport { EASY_CODING_DIR, STATE_FILE } from \"../constants/paths.js\";\nimport type { AgentPlatform } from \"../types/platform.js\";\nimport type { Stage, StateJson } from \"../types/task.js\";\nimport { readTextIfExists, writeTextFile } from \"./file-writer.js\";\n\nexport function createIdleState(agent: AgentPlatform | \"cli\" = \"cli\"): StateJson {\n return {\n current_stage: \"idle\",\n current_task: null,\n last_agent: agent,\n stage_history: [],\n confirmed_by_user: false,\n test_strategy_confirmed: false,\n repo_paths: {},\n };\n}\n\nexport function getStateJsonPath(cwd: string): string {\n return path.join(cwd, EASY_CODING_DIR, STATE_FILE);\n}\n\nexport async function readStateJson(cwd: string): Promise<StateJson | null> {\n const content = await readTextIfExists(getStateJsonPath(cwd));\n if (!content) {\n return null;\n }\n return JSON.parse(content) as StateJson;\n}\n\nexport async function writeStateJson(cwd: string, state: StateJson): Promise<void> {\n await writeTextFile(getStateJsonPath(cwd), JSON.stringify(state, null, 2));\n}\n\nexport async function ensureStateJson(\n cwd: string,\n agent: AgentPlatform | \"cli\" = \"cli\",\n): Promise<StateJson> {\n const existing = await readStateJson(cwd);\n if (existing) {\n return existing;\n }\n\n const state = createIdleState(agent);\n await writeStateJson(cwd, state);\n return state;\n}\n\nexport function enterStage(\n state: StateJson,\n stage: Stage,\n agent: string,\n taskId: string,\n): StateJson {\n return {\n ...state,\n current_stage: stage,\n current_task: taskId,\n last_agent: agent,\n stage_history: [...state.stage_history, { stage, agent, entered_at: new Date().toISOString() }],\n };\n}\n","import path from \"node:path\";\nimport { cancel, confirm, outro } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { CONFIGURATORS } from \"../configurators/index.js\";\nimport { CONFIG_FILE, EASY_CODING_DIR } from \"../constants/paths.js\";\nimport { VERSION } from \"../constants/version.js\";\nimport { renderBanner } from \"../ui/banner.js\";\nimport { compareVersions } from \"../utils/compare-versions.js\";\nimport { readConfigYaml, updateHarnessVersion } from \"../utils/config-yaml.js\";\nimport { pathExists } from \"../utils/file-writer.js\";\n\nexport interface UpgradeOptions {\n dryRun?: boolean;\n yes?: boolean;\n}\n\nexport async function upgrade(opts: UpgradeOptions): Promise<void> {\n renderBanner();\n\n const cwd = process.cwd();\n const configPath = path.join(cwd, EASY_CODING_DIR, CONFIG_FILE);\n if (!(await pathExists(configPath))) {\n throw new Error(\"No .easy-coding/config.yaml found. Run easy-coding init first.\");\n }\n\n const config = await readConfigYaml(configPath);\n const relation = compareVersions(config.harness_version, VERSION);\n\n if (relation === 0) {\n outro(chalk.green(`easy-coding harness is already up to date (${VERSION}).`));\n return;\n }\n\n if (relation === 1) {\n throw new Error(\n `Project harness version ${config.harness_version} is newer than CLI ${VERSION}. Update the CLI first.`,\n );\n }\n\n const summary = [\n `Upgrade: ${config.harness_version} -> ${VERSION}`,\n `Installed agents: ${config.agents.join(\", \")}`,\n \"Will overwrite managed skills, hooks, agents, and generated main-constraint regions.\",\n \"Will not touch tasks, memory, state, spec, or project knowledge files.\",\n ].join(\"\\n\");\n\n if (opts.dryRun) {\n console.log(summary);\n return;\n }\n\n if (!opts.yes) {\n const shouldUpgrade = await confirm({\n message: \"Apply this harness upgrade?\",\n initialValue: true,\n });\n if (typeof shouldUpgrade === \"symbol\" || !shouldUpgrade) {\n cancel(\"Upgrade cancelled.\");\n return;\n }\n }\n\n for (const platform of config.agents) {\n await CONFIGURATORS[platform](cwd);\n }\n await updateHarnessVersion(configPath, VERSION);\n\n outro(chalk.green(`easy-coding harness upgraded to ${VERSION}.`));\n}\n"],"mappings":"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","names":["chalk","path","chalk","path","stat","path","path","path","stat","path","path","path","existsSync","path","path","existsSync","path","fileURLToPath","readFile","readFile","path","chalk","outro","chalk","path","path","readdir","path","path","readdir","path","path","readdir","path","path","readdir","outro","chalk","path","chalk","path","path","path","path","path","chalk","status","path","cancel","confirm","outro","chalk","path","outro","chalk","confirm","cancel","chalk"]}
|
package/package.json
CHANGED
|
@@ -47,6 +47,11 @@ MEMORY_LONG → COMPLETE`, plus `CLOSED` (user abort, no memory flow). WAITING_C
|
|
|
47
47
|
VERIFICATION are hard gates. The current stage persists in `state.json`; hooks inject it as a
|
|
48
48
|
breadcrumb so every reply can render the status line.
|
|
49
49
|
|
|
50
|
+
Task switching: `state.json.current_task` is a single slot, but the user can switch between
|
|
51
|
+
tasks at any time. When a user's prompt doesn't match the active task, ec-workflow's intent
|
|
52
|
+
router offers to suspend the current task and switch. The suspended task retains its stage in
|
|
53
|
+
`task.json`; no data is lost. Each task folder is self-contained.
|
|
54
|
+
|
|
50
55
|
## Task persistence
|
|
51
56
|
|
|
52
57
|
Each task is a folder. `task.json` is metadata; `dev-spec.md` is the human-readable plan;
|
|
@@ -10,6 +10,23 @@ to implement, and present a plan the user can confirm. You do not write business
|
|
|
10
10
|
|
|
11
11
|
Communicate with the user in the user's language.
|
|
12
12
|
|
|
13
|
+
## HARD RULES (non-negotiable, violations = failed analysis)
|
|
14
|
+
|
|
15
|
+
1. **Your FIRST tool call** in this skill MUST be a file-write that creates
|
|
16
|
+
`.easy-coding/tasks/{task-id}/dev-spec.md` with the COMPLETE template skeleton from the
|
|
17
|
+
"Required output" section below. No reading, no thinking out loud, no "let me analyze
|
|
18
|
+
first" — skeleton file FIRST. If this file does not exist after your first tool call, you
|
|
19
|
+
have already failed.
|
|
20
|
+
2. **All subsequent analysis** fills sections of this file via edits. You do NOT hold analysis
|
|
21
|
+
in your head and dump it at the end — you write each section into the file as you go.
|
|
22
|
+
3. **Your chat output to the user** MUST be the final content of dev-spec.md (copy it into
|
|
23
|
+
your reply or reference it). You MUST NOT invent a different, abbreviated format. No
|
|
24
|
+
"执行计划" summary tables, no bullet-point plans, no freestyle answers. The template IS
|
|
25
|
+
the format. If your reply to the user does not contain every mandatory section header from
|
|
26
|
+
the template, you have failed.
|
|
27
|
+
4. **Three files must exist** in `.easy-coding/tasks/{task-id}/` when you finish:
|
|
28
|
+
`dev-spec.md`, `execution.jsonl` (plan record), `test-strategy.md`. Missing any one = failed.
|
|
29
|
+
|
|
13
30
|
## Inputs to load (in this order)
|
|
14
31
|
|
|
15
32
|
1. `.easy-coding/SOUL.md`, `.easy-coding/RULES.md` (always).
|
|
@@ -17,7 +34,10 @@ Communicate with the user in the user's language.
|
|
|
17
34
|
adds a feature. A single-file bugfix or doc edit can skip it.
|
|
18
35
|
3. Long memory: read `MEMORY.md` index, then only the `BUSINESS.md`/`TECHNICAL.md` entries
|
|
19
36
|
whose domain/tags/related_files match this task. No unbounded full scans.
|
|
20
|
-
4.
|
|
37
|
+
4. `.easy-coding/spec/` — scan for design docs (`*-design.md`) whose topic matches this task.
|
|
38
|
+
If found, use the design as a primary input for the analysis. The design doc defines the
|
|
39
|
+
direction; your job is to turn it into a concrete implementation plan.
|
|
40
|
+
5. The actual source files the task touches — read them. A plan that does not cite real
|
|
21
41
|
files, classes, and call paths is rejected by your own self-check below.
|
|
22
42
|
|
|
23
43
|
## Cross-repo handling
|
|
@@ -27,32 +47,35 @@ by **name**, never local paths). For each involved repo, read its ABSTRACT to un
|
|
|
27
47
|
interface. Cache any local path the user provides only in `state.json.repo_paths`. If a repo
|
|
28
48
|
name cannot be located locally, ask the user for the path before proceeding.
|
|
29
49
|
|
|
30
|
-
## Analysis procedure (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
39
|
-
4.
|
|
40
|
-
|
|
41
|
-
5.
|
|
42
|
-
6.
|
|
43
|
-
7.
|
|
44
|
-
8.
|
|
45
|
-
9. **
|
|
46
|
-
|
|
47
|
-
10. **
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
## Analysis procedure (mandatory sequence)
|
|
51
|
+
|
|
52
|
+
You MUST execute these steps in exact order. Do not rearrange, skip, or combine steps.
|
|
53
|
+
|
|
54
|
+
1. **Write skeleton** — FIRST tool call: write `.easy-coding/tasks/{task-id}/dev-spec.md`
|
|
55
|
+
using the full template from the next section. Every section header and every `{待填写}`
|
|
56
|
+
placeholder must be present. This is not optional.
|
|
57
|
+
2. **Fill 项目模式 + 任务类型** — edit dev-spec.md in place.
|
|
58
|
+
3. **Fill 需求解析** — edit dev-spec.md: 目标 / 输入 / 输出 / 边界.
|
|
59
|
+
4. **Read source code, fill 现状** — read the actual files, then edit dev-spec.md with
|
|
60
|
+
evidence. Every claim must cite file:line. No file references = invalid section.
|
|
61
|
+
5. **Fill 冲突摘要 + 待用户决策** — edit dev-spec.md.
|
|
62
|
+
6. **Fill 影响面分析 + 改动范围** — edit dev-spec.md, fill the table with encoding evidence.
|
|
63
|
+
7. **Fill 修改方案 + 实施拆解** — edit dev-spec.md, design approach and decompose units.
|
|
64
|
+
8. **Fill 测试策略 + 风险与注意事项** — edit dev-spec.md.
|
|
65
|
+
9. **Fill conditional sections** — edit dev-spec.md: 背景数据应用 / 核心改动明细 /
|
|
66
|
+
前端实现映射 only if applicable. Remove inapplicable conditional sections entirely.
|
|
67
|
+
10. **Write execution.jsonl** — append the plan record (see section below).
|
|
68
|
+
11. **Write test-strategy.md** — write the testability table (see section below).
|
|
69
|
+
12. **Self-check** — run the gates below. Fix any failure in the files.
|
|
70
|
+
13. **Present to user** — output the FINAL content of dev-spec.md to the user as your reply.
|
|
71
|
+
Do not summarize, abbreviate, or reformat. The dev-spec.md content IS your reply.
|
|
51
72
|
|
|
52
73
|
## Required output: dev-spec.md template
|
|
53
74
|
|
|
54
|
-
The skeleton below is the
|
|
55
|
-
|
|
75
|
+
The skeleton below is the EXACT file you MUST write as step 1. Copy it character-for-character
|
|
76
|
+
into `.easy-coding/tasks/{task-id}/dev-spec.md`. Do not rephrase headers, do not omit
|
|
77
|
+
sections, do not invent alternative formats. Later steps fill the `{待填写}` placeholders
|
|
78
|
+
via edits. Conditional sections are added only if applicable.
|
|
56
79
|
|
|
57
80
|
````markdown
|
|
58
81
|
[阶段:ANALYSIS]
|
|
@@ -198,14 +221,19 @@ with reason (missing infra, data, credentials, or API contract).
|
|
|
198
221
|
Write the confirmed strategy to `.easy-coding/tasks/{task-id}/test-strategy.md` (the
|
|
199
222
|
VERIFICATION baseline).
|
|
200
223
|
|
|
201
|
-
## Self-check gates (reject your own
|
|
224
|
+
## Self-check gates (ALL must pass — reject your own output if ANY fails)
|
|
202
225
|
|
|
203
|
-
-
|
|
204
|
-
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
226
|
+
- [ ] `dev-spec.md` 文件是否已写入 `.easy-coding/tasks/{task-id}/`?
|
|
227
|
+
- [ ] `execution.jsonl` 文件是否已写入?
|
|
228
|
+
- [ ] `test-strategy.md` 文件是否已写入?
|
|
229
|
+
- [ ] dev-spec.md 是否包含全部 12 个核心章节标题?
|
|
230
|
+
- [ ] 每个"现状"断言是否引用了真实文件/类/行号?
|
|
231
|
+
- [ ] 是否有具体的修改方案,而非仅罗列"已加载的文件"?
|
|
232
|
+
- [ ] 不适用的条件章节是否已完全省略(而非留空)?
|
|
233
|
+
- [ ] 实施拆解的单元、依赖、策略是否与改动范围表一致?
|
|
234
|
+
- [ ] 改动范围表中每行是否填写了文件编码及证据?
|
|
235
|
+
- [ ] 所有 `{待填写}` 占位符是否已替换为实际内容?
|
|
236
|
+
- [ ] 回复给用户的内容是否是 dev-spec.md 的完整内容(而非自创的缩略格式)?
|
|
209
237
|
|
|
210
238
|
## Revision handling
|
|
211
239
|
|
|
@@ -222,5 +250,6 @@ Re-output the COMPLETE revised dev-spec.md:
|
|
|
222
250
|
|
|
223
251
|
## End state
|
|
224
252
|
|
|
225
|
-
|
|
226
|
-
|
|
253
|
+
Output the COMPLETE content of dev-spec.md as your reply to the user — not a summary, not a
|
|
254
|
+
different format, not a table you invented. Then set stage to WAITING_CONFIRM and hand
|
|
255
|
+
control back to ec-workflow. Never start implementing from this skill.
|
|
@@ -57,14 +57,26 @@ can be three sentences for a truly simple idea, but you MUST present it and get
|
|
|
57
57
|
the user to review the written doc before proceeding. Make requested changes and re-run
|
|
58
58
|
the self-review. Proceed only on explicit approval.
|
|
59
59
|
|
|
60
|
-
##
|
|
60
|
+
## After approval — handoff to ec-workflow
|
|
61
61
|
|
|
62
62
|
The confirmed design doc is an INPUT to ec-workflow's ANALYSIS stage — it does not replace
|
|
63
|
-
ANALYSIS (brainstorming is design; ANALYSIS is the implementation plan).
|
|
64
|
-
|
|
63
|
+
ANALYSIS (brainstorming is design; ANALYSIS is the implementation plan).
|
|
64
|
+
|
|
65
|
+
After saving the design doc, ask the user: "Design confirmed. Start a task based on this
|
|
66
|
+
design now?" Offer two options:
|
|
67
|
+
|
|
68
|
+
1. **Start task now** — invoke `{{skill_trigger}}ec-workflow` with the design topic as the
|
|
69
|
+
task prompt. ec-workflow will create the task (step 4) and enter INIT → ANALYSIS.
|
|
70
|
+
ec-analysis will discover the design doc in `.easy-coding/spec/` and use it as input.
|
|
71
|
+
2. **Later** — tell the user the design is saved and they can run
|
|
72
|
+
`{{skill_trigger}}ec-workflow` whenever ready.
|
|
73
|
+
|
|
74
|
+
If `current_task` is set when the user chooses option 1, mention that the current task will
|
|
75
|
+
be suspended (ec-workflow's intent routing handles the rest).
|
|
65
76
|
|
|
66
77
|
## Boundaries
|
|
67
78
|
|
|
68
79
|
- Write no implementation code, scaffold nothing, take no implementation action.
|
|
69
|
-
-
|
|
80
|
+
- The only ec-* skill you may invoke is `ec-workflow` (on user confirmation after approval).
|
|
81
|
+
Do not touch state.json or the state machine directly.
|
|
70
82
|
- One question per message — never overwhelm.
|
|
@@ -31,5 +31,7 @@ when you recognize abandonment intent in the user's message.
|
|
|
31
31
|
|
|
32
32
|
- Never delete task folders — CLOSED tasks stay as a record.
|
|
33
33
|
- Never run the memory/archive flow.
|
|
34
|
+
- This skill closes the `current_task`. If the user wants to close a different (suspended)
|
|
35
|
+
task, they should first switch to it via ec-workflow, then invoke ec-task-close.
|
|
34
36
|
- Division of labor: ec-task-management lists/creates (read-only panel), ec-workflow runs the
|
|
35
37
|
stage machine, ec-task-close owns interruption. Stay in your lane.
|
|
@@ -13,9 +13,10 @@ Communicate with the user in the user's language.
|
|
|
13
13
|
## Capabilities
|
|
14
14
|
|
|
15
15
|
### List tasks
|
|
16
|
-
Scan `.easy-coding/tasks/`. For each task folder show: id, `
|
|
17
|
-
whether it is active (status not in {COMPLETE, CLOSED}). Group by active
|
|
18
|
-
user sees at a glance what is in flight. This is read-only — change
|
|
16
|
+
Scan `.easy-coding/tasks/`. For each task folder show: id, `title` (if present), `status`,
|
|
17
|
+
`created_at`, and whether it is active (status not in {COMPLETE, CLOSED}). Group by active
|
|
18
|
+
vs finished so the user sees at a glance what is in flight. This is read-only — change
|
|
19
|
+
nothing.
|
|
19
20
|
|
|
20
21
|
### Create a task
|
|
21
22
|
Create `.easy-coding/tasks/{MM-DD-short-name}/task.json` using the runtime schema:
|
|
@@ -23,6 +24,7 @@ Create `.easy-coding/tasks/{MM-DD-short-name}/task.json` using the runtime schem
|
|
|
23
24
|
```json
|
|
24
25
|
{
|
|
25
26
|
"type": "feature | bugfix | refactor | perf",
|
|
27
|
+
"title": "<one-line summary of the task>",
|
|
26
28
|
"status": "INIT",
|
|
27
29
|
"created_at": "<ISO 8601>",
|
|
28
30
|
"created_by": "<agent id>",
|
|
@@ -23,15 +23,38 @@ replies are English.
|
|
|
23
23
|
- `.easy-coding/RULES.md` — coding rules; re-checked before every write.
|
|
24
24
|
- Latest 5 entries in `.easy-coding/memory/short/` — recent task context.
|
|
25
25
|
Do NOT bulk-read ABSTRACT.md or long memory here; ec-analysis loads them on demand.
|
|
26
|
-
3. **State check.** Read `{{workflow_state_path}}
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
3. **State check + Intent routing.** Read `{{workflow_state_path}}`, then decide based on
|
|
27
|
+
whether the user's message carries a task-related prompt beyond the bare skill trigger.
|
|
28
|
+
|
|
29
|
+
**No prompt (bare trigger):**
|
|
30
|
+
- `current_task` set with an active stage → resume that stage (see Resume and handoff).
|
|
31
|
+
- `current_task` null → scan `.easy-coding/tasks/` for tasks whose status is not in
|
|
29
32
|
{COMPLETE, CLOSED}. Found → list them, let the user pick one to resume or start new.
|
|
30
33
|
None → ready for a new task.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
**With prompt — intent routing:**
|
|
36
|
+
1. Collect all non-terminal tasks in `.easy-coding/tasks/`: read each task's folder name,
|
|
37
|
+
`task.json` fields (`title`, `type`, `status`). If a task passed ANALYSIS, also read
|
|
38
|
+
its dev-spec title line as extra matching signal.
|
|
39
|
+
2. Match the user's prompt against these identifiers (semantic match — the user saying
|
|
40
|
+
"继续做搜索" should match a task titled "添加搜索功能").
|
|
41
|
+
3. Route:
|
|
42
|
+
- Prompt matches `current_task` → resume with the prompt as additional context (the
|
|
43
|
+
user may be providing supplementary info, a revision request, or an answer to a
|
|
44
|
+
previous question).
|
|
45
|
+
- Prompt matches a different non-terminal task → ask the user: "Switch to «{matched
|
|
46
|
+
task title}»? Current task «{current task title}» will be suspended at {stage}."
|
|
47
|
+
On confirmation, perform a task switch (see Task switching below).
|
|
48
|
+
- Prompt matches no existing task AND `current_task` is set → ask the user: "Start a
|
|
49
|
+
new task? Current task «{current task title}» will be suspended at {stage}."
|
|
50
|
+
On confirmation, create the new task (step 4).
|
|
51
|
+
- Prompt matches no existing task AND `current_task` is null → create the new task
|
|
52
|
+
directly (step 4).
|
|
53
|
+
|
|
54
|
+
4. **New task.** When creating a task (from step 3), create
|
|
55
|
+
`.easy-coding/tasks/{MM-DD-task-slug}/task.json` with `type`, `title` (one-line summary
|
|
56
|
+
of the user's prompt), `status:"INIT"`, `created_at`, `created_by` (current agent id),
|
|
57
|
+
set `current_task` in state.json, and enter INIT.
|
|
35
58
|
|
|
36
59
|
## State machine
|
|
37
60
|
|
|
@@ -59,6 +82,20 @@ any stage --[user abort via ec-task-close]--> CLOSED
|
|
|
59
82
|
| COMPLETE | ec-workflow | clear current_task, set task status, summary | terminal |
|
|
60
83
|
| CLOSED | ec-task-close | user abort; no memory flow | terminal |
|
|
61
84
|
|
|
85
|
+
## Task switching
|
|
86
|
+
|
|
87
|
+
When the user confirms switching from task A to task B:
|
|
88
|
+
1. Task A's status is already persisted in its `task.json` — nothing extra to save.
|
|
89
|
+
2. Set `current_task` to task B's id in state.json.
|
|
90
|
+
3. Set `current_stage` to task B's `status` (from task B's `task.json`).
|
|
91
|
+
4. Append a `{stage, agent, entered_at}` entry to `stage_history`.
|
|
92
|
+
5. Set `last_agent` to the current agent id.
|
|
93
|
+
6. Resume task B's stage via the appropriate stage skill.
|
|
94
|
+
|
|
95
|
+
No data is lost — task A's dev-spec, execution.jsonl, and test-strategy.md stay intact on
|
|
96
|
+
disk. To return to task A later, the same intent routing applies: the user mentions it,
|
|
97
|
+
routing matches, and switching happens again.
|
|
98
|
+
|
|
62
99
|
## Transition rules (hard)
|
|
63
100
|
|
|
64
101
|
- **Never skip a stage.** ANALYSIS cannot jump to VERIFICATION; IMPLEMENT cannot start before
|
|
@@ -77,6 +114,8 @@ any stage --[user abort via ec-task-close]--> CLOSED
|
|
|
77
114
|
- **Scope guard** (repair loop): if the user's fix request falls outside the dev-spec scope
|
|
78
115
|
(features or files absent from the change-scope table), say so explicitly and propose a
|
|
79
116
|
new task with `spawned_from` set to the current task id. Never silently absorb scope creep.
|
|
117
|
+
- **Task switching is allowed at any stage.** The suspended task retains its stage in
|
|
118
|
+
task.json. Do not run memory flows for suspended tasks — only completed tasks get archived.
|
|
80
119
|
- **Archive only after user acceptance.** VERIFICATION passing does not complete the task.
|
|
81
120
|
After the user accepts, MEMORY_SHORT → MEMORY_LONG → COMPLETE run automatically.
|
|
82
121
|
- **COMPLETE closeout:** set task.json `status:"COMPLETE"`, clear `current_task` in
|
|
@@ -87,7 +126,7 @@ any stage --[user abort via ec-task-close]--> CLOSED
|
|
|
87
126
|
Hook breadcrumbs you may receive: `[workflow-state:X]`, `[current-task:Y]`,
|
|
88
127
|
`[easy-coding:handoff-from:Z]`, `[easy-coding:init-required]`.
|
|
89
128
|
|
|
90
|
-
Resuming an active task:
|
|
129
|
+
Resuming an active task (whether from session restart, handoff, or task switch):
|
|
91
130
|
1. Read `task.json` and the dev-spec sections relevant to the current stage.
|
|
92
131
|
2. Read the tail of `execution.jsonl` — the latest `plan` / `result` / `verify` / `handoff`
|
|
93
132
|
records tell you exactly where work stopped.
|
|
@@ -95,6 +134,9 @@ Resuming an active task:
|
|
|
95
134
|
latest `handoff` record first for the fast summary, then set `last_agent` to yourself.
|
|
96
135
|
4. Tell the user what is being resumed and from which stage, then continue.
|
|
97
136
|
|
|
137
|
+
After a task switch, the same resume flow applies — the only difference is that `current_task`
|
|
138
|
+
was just changed by the switching procedure rather than being loaded from a prior session.
|
|
139
|
+
|
|
98
140
|
Offering handoff — at WAITING_CONFIRM, after presenting the plan, offer exactly:
|
|
99
141
|
1. Start implementation
|
|
100
142
|
2. Hand off to another agent
|
|
@@ -11,12 +11,12 @@ user in the user's language.
|
|
|
11
11
|
Start every work reply with the single Markdown blockquote status line injected by the hook,
|
|
12
12
|
then a blank line. Do not render the machine breadcrumbs to the user.
|
|
13
13
|
|
|
14
|
-
- Ready: > **Easy Coding** · Ready · Use `ec-
|
|
14
|
+
- Ready: > **Easy Coding** · Ready · Use `ec-workflow` to start or resume a task, `ec-brainstorming` to brainstorm, or `ec-task-management` to view tasks
|
|
15
15
|
- Waiting init: > **Easy Coding** · Waiting init · Use `ec-init` to initialize
|
|
16
16
|
- Active task: > **Easy Coding** · `{current-task}` · `{workflow-state}`
|
|
17
17
|
- Handoff: > **Easy Coding** · `{current-task}` · `{workflow-state}` · Handoff -> `{source-agent}`
|
|
18
18
|
|
|
19
|
-
Skill names in the status line are bare names (`ec-init`, `ec-
|
|
19
|
+
Skill names in the status line are bare names (`ec-init`, `ec-workflow`) and never include
|
|
20
20
|
platform prefixes such as `/` or `$`. If no status line is injected, do not invent one.
|
|
21
21
|
|
|
22
22
|
## Skills
|
|
@@ -11,12 +11,12 @@ user in the user's language.
|
|
|
11
11
|
Start every work reply with the single Markdown blockquote status line injected by the hook,
|
|
12
12
|
then a blank line. Do not render the machine breadcrumbs to the user.
|
|
13
13
|
|
|
14
|
-
- Ready: > **Easy Coding** · Ready · Use `ec-
|
|
14
|
+
- Ready: > **Easy Coding** · Ready · Use `ec-workflow` to start or resume a task, `ec-brainstorming` to brainstorm, or `ec-task-management` to view tasks
|
|
15
15
|
- Waiting init: > **Easy Coding** · Waiting init · Use `ec-init` to initialize
|
|
16
16
|
- Active task: > **Easy Coding** · `{current-task}` · `{workflow-state}`
|
|
17
17
|
- Handoff: > **Easy Coding** · `{current-task}` · `{workflow-state}` · Handoff -> `{source-agent}`
|
|
18
18
|
|
|
19
|
-
Skill names in the status line are bare names (`ec-init`, `ec-
|
|
19
|
+
Skill names in the status line are bare names (`ec-init`, `ec-workflow`) and never include
|
|
20
20
|
platform prefixes such as `/` or `$`. If no status line is injected, do not invent one.
|
|
21
21
|
|
|
22
22
|
## Skills
|
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
READY_LINE = (
|
|
6
|
-
"> **Easy Coding** · Ready · Use `ec-
|
|
6
|
+
"> **Easy Coding** · Ready · Use `ec-workflow` to start or resume a task, "
|
|
7
7
|
"`ec-brainstorming` to brainstorm, or `ec-task-management` to view tasks"
|
|
8
8
|
)
|
|
9
9
|
WAITING_INIT_LINE = "> **Easy Coding** · Waiting init · Use `ec-init` to initialize"
|