easy-coding-harness 0.1.5 → 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 CHANGED
@@ -37,7 +37,66 @@ easy-coding --version
37
37
  - `y`:常规功能升级
38
38
  - `z`:日常 bug 修复
39
39
 
40
- 详细变更见 [CHANGELOG.md](CHANGELOG.md)。
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
+
54
+ ### 0.1.6
55
+
56
+ - ec-analysis 改为模板先行(template-first)工作流:分析阶段先创建 dev-spec.md 骨架,再逐步填充各章节。
57
+ - ec-analysis 模板从英文简写替换为完整中文结构化模板,12 个核心必填章节 + 3 个条件展开章节。
58
+ - 新增修订处理规则:用户修改意见必须重新输出完整方案,不允许只回差异摘要。
59
+ - 自检门禁新增"占位符是否全部替换"检查项。
60
+ - 测试策略补充人工验收和无法验证项。
61
+ - 将版本记录从 CHANGELOG.md 迁移到 README.md 统一维护。
62
+ - 新增项目级 CLAUDE.md 和 AGENTS.md 约束文件。
63
+
64
+ ### 0.1.5
65
+
66
+ - ec-analysis 模板结构化改进:Change plan 改为五项一行式,Implementation units 输出完整 unit 表,Test strategy 内嵌 testability table + test points。
67
+
68
+ ### 0.1.4
69
+
70
+ - 将宽终端 CLI banner 调整为 `ANSI Shadow` 厚重色块字形,恢复类似截图中的块状阴影质感。
71
+ - 中等宽度终端使用 `Small Shadow`,窄终端继续使用 `Small Slant`,避免标题溢出。
72
+ - 保留 0.1.3 的 cyan/blue 分层配色和副标题样式。
73
+ - 修复主约束模板中状态栏示例使用外层双反引号,可能导致 agent 复述 Ready 状态栏时在末尾多带一个反引号的问题。
74
+
75
+ ### 0.1.3
76
+
77
+ - 修复 0.1.2 CLI banner 字体过于块状、左右压缩导致难以辨认的问题。
78
+ - 默认字体改为更舒展的 `Big`,中等宽度终端使用 `Doom`,窄终端继续使用 `Small Slant`。
79
+ - 保留 0.1.2 的 cyan/blue 分层配色和副标题样式。
80
+ - 将 hook 注入的 Easy Coding 状态提示收口为单行 Markdown 状态栏,展示 Ready、Waiting init、当前任务、任务状态和 handoff 来源。
81
+
82
+ ### 0.1.2
83
+
84
+ - 改进 agent 平台选择提示,明确说明使用 Space 切换选择、Enter 进入确认。
85
+ - 平台选择后增加二次确认,避免误按回车直接按默认平台安装。
86
+ - 优化 CLI 启动标题,默认使用带阴影感的 `ANSI Shadow` 字体,并为窄终端提供 fallback。
87
+
88
+ ### 0.1.1
89
+
90
+ - 修复 `easy-coding init` 将任意 `.easy-coding` 目录误判为已安装 harness 的问题。
91
+ - 新增旧版 `easy-coding` skill 产物识别,允许在保留旧数据的前提下接入新 harness。
92
+ - `project-init` 任务会记录旧资产清单,供 `ec-init` 校验、保留和补全旧数据产物。
93
+ - 将 CLI 版本源统一到 `package.json`,避免源码常量与包版本漂移。
94
+
95
+ ### 0.1.0
96
+
97
+ - 首个内测基线版本。
98
+ - 提供 `easy-coding init`、`add-agent`、`upgrade`、`status` 基础命令。
99
+ - 支持 Claude Code、Codex、Qoder 三个平台的 skills、hooks、agents 和主约束安装。
41
100
 
42
101
  ## 命令
43
102
 
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
- console.log(` - ${item.id}: ${item.task.status}`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easy-coding-harness",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "CLI scaffold for installing Easy Coding harness files into agent-native directories.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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;
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: ec-analysis
3
- description: ANALYSIS-stage skill. Use when ec-workflow enters ANALYSIS. Produces the dev-spec (narrative plan), the execution plan (execution.jsonl), and the test strategy, then ends in WAITING_CONFIRM. Grounds every conclusion in real code, never restates the requirement.
3
+ description: ANALYSIS-stage skill. Use when ec-workflow enters ANALYSIS. Creates the dev-spec skeleton FIRST (template-first), then fills incrementally — producing the narrative plan, execution plan (execution.jsonl), and test strategy. Ends in WAITING_CONFIRM. Grounds every conclusion in real code, never restates the requirement.
4
4
  ---
5
5
 
6
6
  # ec-analysis — turn a requirement into a confirmable 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. The actual source files the task touches — read them. A plan that does not cite real
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,35 +47,133 @@ 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
- ## Required output: the analysis document
31
-
32
- Write `.easy-coding/tasks/{task-id}/dev-spec.md` covering these mandatory sections. Omit a
33
- conditional section entirely if it does not apply — never pad to fill the template.
34
-
35
- ```
36
- ### Project mode {startup | iterative}
37
- ### Task type {feature | bugfix | refactor | perf | frontend}
38
- ### Requirement parse goal / input / output / boundary (what it explicitly will NOT do)
39
- ### Current state real code, files, call paths, existing interfaces with evidence
40
- ### Conflicts requirement vs RULES / vs ABSTRACT / vs current code / vs dev-spec
41
- ### Decisions needed anything affecting tech route, interface, or scope; else "none"
42
- ### Impact modules / core classes / DB change y-n / interface change y-n / related memories
43
- ### Change scope table belowFILE ENCODING column is mandatory
44
- ### Change plan one-line overall approach / backend / frontend / migration / risk
45
- ### Implementation units the unit table below drives execution.jsonl
46
- ### Test strategy the testability table + test points below
47
- ### Risks concrete failure points
48
- ```
49
-
50
- **Change-scope table** (the encoding column prevents corrupting project files):
51
-
52
- | File | Change type | Encoding | Core change |
53
- |---|---|---|---|
54
- | `path` | new/modify/delete | "project encoding X, evidence: ..." or "keep original X" | ... |
55
-
56
- Encoding rule: modifying an existing file must keep its original encoding; new files declare
57
- the project encoding with evidence; if evidence conflicts or is unknown, mark "needs user
58
- confirmation". The user may override any encoding cell before confirming.
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.
72
+
73
+ ## Required output: dev-spec.md template
74
+
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.
79
+
80
+ ````markdown
81
+ [阶段:ANALYSIS]
82
+
83
+ ## 技术方案:{任务标题}
84
+
85
+ ### 项目模式
86
+ {初创项目/迭代项目}
87
+
88
+ ### 任务类型
89
+ {新功能 / Bug 修复 / 重构 / 性能优化 / 前端设计实现}
90
+
91
+ ### 需求解析
92
+ - **目标**:{待填写 — 真正要解决的问题}
93
+ - **输入**:{待填写 — 用户输入 / 系统输入 / 触发条件}
94
+ - **输出**:{待填写 — 最终交付结果}
95
+ - **边界**:{待填写 — 明确不做什么}
96
+
97
+ ### 现状
98
+ - **相关代码 / 页面 / 接口 / 模块**:{待填写 — 基于实际文件与代码的现状说明}
99
+ - **当前实现方式**:{待填写 — 现在是如何工作的}
100
+ - **现有问题 / 缺口**:{待填写 — 为什么需要改}
101
+ - **证据**:{待填写 — 引用的关键文件、类、页面、接口,含 file:line}
102
+
103
+ ### 冲突摘要
104
+ - 需求 vs RULES:{待填写 或 "无冲突"}
105
+ - 需求 vs ABSTRACT:{待填写 或 "无冲突"}
106
+ - 需求 vs 现有代码:{待填写 或 "无冲突"}
107
+ - Dev-Spec vs 现有代码:{待填写 或 "无冲突"}
108
+
109
+ ### 待用户决策
110
+ - {待填写 — 影响技术路线、接口、改动范围的问题逐条列出;若无则写"无"}
111
+
112
+ ### 影响面分析
113
+ - **涉及模块**:{待填写}
114
+ - **核心类 / 页面 / 接口**:{待填写}
115
+ - **数据库变更**:{有/无}
116
+ - **接口变更**:{有/无}
117
+ - **关联历史任务**:{待填写 — 相关短期记忆序号;无则"无"}
118
+
119
+ ### 改动范围
120
+ | 改动文件 | 改动类型 | 文件编码 | 改动核心内容 |
121
+ |----------|---------|---------|-------------|
122
+ | `{文件路径}` | 新增 | 项目编码 {X},依据:{xxx} | {核心改动} |
123
+ | `{文件路径}` | 修改 | 保持原编码 {X} | {核心改动} |
124
+ | `{文件路径}` | 删除 | — | {删除原因} |
125
+
126
+ ### 修改方案
127
+ - **总体改法**:{待填写 — 一句话说清改哪里、怎么改}
128
+ - **后端改动**:{待填写;不涉及则写"不涉及"}
129
+ - **前端改动**:{待填写;不涉及则写"不涉及"}
130
+ - **兼容处理**:{待填写 — 旧逻辑如何迁移、保留或替换}
131
+ - **风险点**:{待填写 — 最容易出问题的位置}
132
+
133
+ ### 实施拆解
134
+
135
+ | 单元 | 说明 | 类型 | 涉及文件 | 依赖 |
136
+ |------|------|------|---------|------|
137
+ | U1 | {title} | {backend/frontend/test/...} | {files} | — |
138
+ | U2 | {title} | {type} | {files} | — |
139
+ | U3 | {title} | {type} | {files} | U1, U2 |
140
+
141
+ **执行策略**:{parallel / sequential / single}
142
+ - 第一批(并行):U1 {title} | U2 {title}
143
+ - 第二批(等待第一批):U3 {title}
144
+ (若 single:单一实施单元,主 agent 直接执行)
145
+
146
+ ### 测试策略
147
+
148
+ | 测试点 | 级别 | 归属单元 | 方式 | 验证命令 |
149
+ |--------|------|---------|------|---------|
150
+ | {描述} | 必测 | U1 | 单测 | `npm test -- --filter=xxx` |
151
+ | {描述} | 应测 | U2 | 快照 | `npm test -- --snapshot` |
152
+
153
+ - **人工验收**:{待填写 — 用户需要检查的关键行为}
154
+ - **无法验证项**:{待填写 — 无 / 说明缺失环境、数据或权限}
155
+
156
+ ### 风险与注意事项
157
+ - {风险 1}
158
+ - {风险 2}
159
+ ````
160
+
161
+ > **编码规则**:修改已有文件必须保持原始编码;新文件声明项目编码并附证据;证据冲突或
162
+ > 未知时标记"需用户确认"。用户可在确认前覆盖任何编码单元格。
163
+
164
+ **条件展开章节**(仅在满足条件时添加,否则完全不出现):
165
+
166
+ - `### 背景数据应用` — 命中项目知识资产(ABSTRACT.md、BUSINESS.md、TECHNICAL.md、RULES.md)
167
+ 且其内容影响本次方案时展开。含架构参考、业务记忆、技术记忆、记忆冲突、规范约束。
168
+ - `### 核心改动明细` — 多模块且改动范围表不足以表达当前逻辑和目标逻辑时展开。
169
+ 逐文件列出"当前逻辑 → 准备怎么改"。
170
+ - `### 前端实现映射` — 涉及前端页面、组件、交互时展开。含页面/模块映射、组件映射、
171
+ 数据来源、Mock 使用计划。
172
+
173
+ **禁止输出**:
174
+ - 仅复述需求、几乎不引用代码现状的空泛方案。
175
+ - 只列"已加载哪些信息"但不给出具体可实施改法。
176
+ - 为了凑格式输出与本次任务无关的条件章节。
59
177
 
60
178
  ## Implementation units → execution.jsonl
61
179
 
@@ -95,19 +213,43 @@ owning unit and the verify command.
95
213
 
96
214
  **3. No-test reasons** — one line each; the user can overturn any verdict at confirmation.
97
215
 
216
+ **4. Human acceptance** — items requiring manual verification by the user.
217
+
218
+ **5. Cannot-verify items** — items that cannot be verified in the current environment,
219
+ with reason (missing infra, data, credentials, or API contract).
220
+
98
221
  Write the confirmed strategy to `.easy-coding/tasks/{task-id}/test-strategy.md` (the
99
222
  VERIFICATION baseline).
100
223
 
101
- ## Self-check before presenting (reject your own draft if any fails)
102
-
103
- - Does every "Current state" claim cite a real file/class/line?
104
- - Is there a concrete change plan, not just "loaded these files"?
105
- - Did you omit conditional sections that do not apply?
106
- - Are units, dependencies, and strategy internally consistent with the change scope?
224
+ ## Self-check gates (ALL must pass — reject your own output if ANY fails)
225
+
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 的完整内容(而非自创的缩略格式)?
237
+
238
+ ## Revision handling
239
+
240
+ On user revision request at WAITING_CONFIRM, do NOT reply with only a change summary.
241
+ Re-output the COMPLETE revised dev-spec.md:
242
+
243
+ 1. Prepend a `### 修订摘要` listing each user request and its impact on the plan. If the
244
+ user changed a file encoding, explain the per-file adjustment.
245
+ 2. Re-output the full plan following the template above (核心必填 + applicable conditional
246
+ sections), incorporating all revisions.
247
+ 3. Overwrite the `plan` record in execution.jsonl with the new strategy.
248
+ 4. Update test-strategy.md if test scope changed.
249
+ 5. Re-enter WAITING_CONFIRM and wait for explicit user confirmation.
107
250
 
108
251
  ## End state
109
252
 
110
- Present the full plan, then set stage to WAITING_CONFIRM and hand control back to
111
- ec-workflow. On a revision request, re-output the COMPLETE revised plan (not just a diff)
112
- and re-enter WAITING_CONFIRM; overwrite the `plan` record in execution.jsonl with the new
113
- strategy. Never start implementing from this skill.
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
- ## Relationship to ec-workflow
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). After approval, tell
64
- the user to run `{{skill_trigger}}ec-workflow` to start building.
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
- - Do not call other ec-* skills or touch state.json / the state machine.
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, `status`, `created_at`, and
17
- whether it is active (status not in {COMPLETE, CLOSED}). Group by active vs finished so the
18
- user sees at a glance what is in flight. This is read-only — change nothing.
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
- - Active task with a stage in progress resume that stage (see Resume and handoff).
28
- - No active task → scan `.easy-coding/tasks/` for tasks whose status is not in
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
- 4. **New task.** When the user states a requirement, create
32
- `.easy-coding/tasks/{MM-DD-task-slug}/task.json` with `type`, `status:"INIT"`,
33
- `created_at`, `created_by` (current agent id), set `current_task` in state.json,
34
- and enter INIT.
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-analysis` to start requirement analysis, `ec-brainstorming` to brainstorm, or `ec-task-management` to view tasks
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-analysis`) and never include
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-analysis` to start requirement analysis, `ec-brainstorming` to brainstorm, or `ec-task-management` to view tasks
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-analysis`) and never include
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-analysis` to start requirement analysis, "
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"