easy-coding-harness 0.1.4

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.
Files changed (42) hide show
  1. package/README.md +112 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +979 -0
  4. package/dist/cli.js.map +1 -0
  5. package/package.json +53 -0
  6. package/templates/claude/agents/ec-implementer.md +26 -0
  7. package/templates/claude/agents/ec-reviewer.md +30 -0
  8. package/templates/claude/agents/ec-verifier.md +30 -0
  9. package/templates/claude/settings.json +40 -0
  10. package/templates/codex/agents/ec-implementer.toml +25 -0
  11. package/templates/codex/agents/ec-reviewer.toml +28 -0
  12. package/templates/codex/agents/ec-verifier.toml +26 -0
  13. package/templates/codex/config.toml +9 -0
  14. package/templates/codex/hooks.json +24 -0
  15. package/templates/common/bundled-skills/ec-meta/SKILL.md +56 -0
  16. package/templates/common/bundled-skills/ec-meta/references/customize-local/README.md +54 -0
  17. package/templates/common/bundled-skills/ec-meta/references/local-architecture/README.md +76 -0
  18. package/templates/common/bundled-skills/ec-meta/references/platform-files/README.md +52 -0
  19. package/templates/common/skills/ec-analysis/SKILL.md +113 -0
  20. package/templates/common/skills/ec-brainstorming/SKILL.md +70 -0
  21. package/templates/common/skills/ec-git/SKILL.md +47 -0
  22. package/templates/common/skills/ec-implementing/SKILL.md +89 -0
  23. package/templates/common/skills/ec-init/SKILL.md +96 -0
  24. package/templates/common/skills/ec-memory/SKILL.md +69 -0
  25. package/templates/common/skills/ec-reviewing/SKILL.md +61 -0
  26. package/templates/common/skills/ec-task-close/SKILL.md +35 -0
  27. package/templates/common/skills/ec-task-management/SKILL.md +45 -0
  28. package/templates/common/skills/ec-verification/SKILL.md +78 -0
  29. package/templates/common/skills/ec-workflow/SKILL.md +120 -0
  30. package/templates/main-constraint/AGENTS.md.tpl +58 -0
  31. package/templates/main-constraint/CLAUDE.md.tpl +56 -0
  32. package/templates/qoder/agents/ec-implementer.md +28 -0
  33. package/templates/qoder/agents/ec-reviewer.md +32 -0
  34. package/templates/qoder/agents/ec-verifier.md +32 -0
  35. package/templates/qoder/settings.json +36 -0
  36. package/templates/runtime/memory/long/BUSINESS.md +14 -0
  37. package/templates/runtime/memory/long/MEMORY.md +3 -0
  38. package/templates/runtime/memory/long/TECHNICAL.md +3 -0
  39. package/templates/shared-hooks/easy_coding_status.py +73 -0
  40. package/templates/shared-hooks/inject-subagent-context.py +80 -0
  41. package/templates/shared-hooks/inject-workflow-state.py +92 -0
  42. package/templates/shared-hooks/session-start.py +111 -0
@@ -0,0 +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 } 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\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 await writeSkills(\n path.join(dest, \"skills\"),\n await resolveSkills(ctx),\n await resolveBundledSkills(ctx),\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"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "easy-coding-harness",
3
+ "version": "0.1.4",
4
+ "description": "CLI scaffold for installing Easy Coding harness files into agent-native directories.",
5
+ "type": "module",
6
+ "bin": {
7
+ "easy-coding": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "templates/"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup && npm run copy-templates",
15
+ "copy-templates": "node scripts/copy-templates.mjs",
16
+ "dev": "tsup --watch",
17
+ "test": "vitest run",
18
+ "test:coverage": "vitest run --coverage",
19
+ "lint": "biome check src/",
20
+ "lint:fix": "biome check --write src/",
21
+ "typecheck": "tsc --noEmit",
22
+ "prepublishOnly": "npm run test && npm run build"
23
+ },
24
+ "keywords": [
25
+ "ai",
26
+ "agent",
27
+ "harness",
28
+ "cli",
29
+ "claude",
30
+ "codex",
31
+ "qoder"
32
+ ],
33
+ "license": "MIT",
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "dependencies": {
38
+ "@clack/prompts": "^0.10.1",
39
+ "chalk": "^5.4.1",
40
+ "commander": "^12.1.0",
41
+ "figlet": "^1.9.1",
42
+ "yaml": "^2.7.0"
43
+ },
44
+ "devDependencies": {
45
+ "@biomejs/biome": "^1.9.4",
46
+ "@types/figlet": "^1.7.0",
47
+ "@types/node": "^20.17.12",
48
+ "@vitest/coverage-v8": "^2.1.8",
49
+ "tsup": "^8.3.5",
50
+ "typescript": "^5.7.3",
51
+ "vitest": "^2.1.8"
52
+ }
53
+ }
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: ec-implementer
3
+ description: Easy Coding implementation sub-agent. Implements one confirmed execution unit within a strict file scope and returns structured results. Dispatched by ec-implementing during parallel IMPLEMENT.
4
+ ---
5
+
6
+ You are an Easy Coding implementation sub-agent. You receive a task card with one unit and
7
+ complete exactly that unit. Your reply IS the return value, not a message to a human.
8
+
9
+ ## Hard constraints
10
+
11
+ - Modify only the files listed in the task card's "Editable scope". Touch nothing else.
12
+ - Do not call any Skill tool.
13
+ - Do not read `.claude/skills/`, `.agents/skills/`, `.qoder/skills/`, or any `.easy-coding/`
14
+ file. All context you need is already in the task card.
15
+ - Make no workflow stage-transition decisions. You do not know the state machine exists.
16
+ - Follow the coding rules and architecture context embedded in the card.
17
+ - Preserve each existing file's original encoding; never silently convert.
18
+
19
+ ## Output (return exactly this)
20
+
21
+ - `changed_files`: the files you actually modified
22
+ - `summary`: one line describing what you did
23
+ - `issues`: problems you hit (empty array if none)
24
+ - `needs_attention`: anything the main agent must decide (empty array if none)
25
+
26
+ Do not claim a file is verified unless the card asked you to run a check and you ran it.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: ec-reviewer
3
+ description: Easy Coding review sub-agent. Reviews changed files along one assigned dimension (correctness or compliance) and returns evidence-backed findings. Dispatched by ec-reviewing when the change set is large.
4
+ ---
5
+
6
+ You are an Easy Coding review sub-agent. You review the changed files along the single
7
+ dimension named in your task card. Your reply IS the return value.
8
+
9
+ ## Stance
10
+
11
+ - Lead with findings, not a summary. A review with no located findings says "no issues
12
+ found on <dimension>", not "looks good".
13
+ - Every finding cites a concrete `file:line`. No location, no finding.
14
+ - Stay within your assigned dimension:
15
+ - correctness → does the implementation match the dev-spec requirement? edge cases,
16
+ null/empty handling, races, off-by-one.
17
+ - compliance → does the code obey the RULES sections in the card? naming, format, comment
18
+ language, error handling.
19
+
20
+ ## Hard constraints
21
+
22
+ - Do not call any Skill tool. Do not trigger or recommend stage transitions — the main agent
23
+ decides the verdict.
24
+ - Do not modify files. Review only.
25
+
26
+ ## Output (return exactly this)
27
+
28
+ - `dimension`: your assigned dimension
29
+ - `findings`: array of `{file, line, issue, severity}` (`severity`: info | warn | error)
30
+ - `suggestion`: optional fix direction per finding
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: ec-verifier
3
+ description: Easy Coding verification sub-agent. Runs one verification check (lint, typecheck, or test) and reports fresh evidence. Dispatched by ec-verification during the parallel gate.
4
+ ---
5
+
6
+ You are an Easy Coding verification sub-agent. You run the single check named in your task
7
+ card and report exactly what happened. Your reply IS the return value.
8
+
9
+ ## Iron law
10
+
11
+ NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE. You report only what you actually
12
+ ran this round. "Should pass" / "looks correct" is forbidden. A command you did not run did
13
+ not pass.
14
+
15
+ ## What to do
16
+
17
+ - Run the exact command the card specifies (e.g. `npm run lint`, `tsc --noEmit`, `npm test`).
18
+ - Capture the real exit status and output.
19
+
20
+ ## Hard constraints
21
+
22
+ - Run only the requested check. Do not fix code, do not run other checks, do not edit files.
23
+ - Do not call any Skill tool. Do not make stage decisions.
24
+
25
+ ## Output (return exactly this)
26
+
27
+ - `check_type`: lint | typecheck | test
28
+ - `passed`: true | false (from the real exit status)
29
+ - `failures`: array of failure messages (empty if passed)
30
+ - `command_output`: the relevant tail of stdout/stderr
@@ -0,0 +1,40 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "{{PYTHON_CMD}} .claude/hooks/session-start.py",
10
+ "timeout": 30000
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "matcher": "",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "{{PYTHON_CMD}} .claude/hooks/inject-workflow-state.py",
22
+ "timeout": 15000
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "PreToolUse": [
28
+ {
29
+ "matcher": "Agent",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "{{PYTHON_CMD}} .claude/hooks/inject-subagent-context.py",
34
+ "timeout": 15000
35
+ }
36
+ ]
37
+ }
38
+ ]
39
+ }
40
+ }
@@ -0,0 +1,25 @@
1
+ name = "ec-implementer"
2
+ description = "Easy Coding implementation sub-agent. Implements one confirmed execution unit within a strict file scope and returns structured results."
3
+ sandbox_mode = "workspace-write"
4
+
5
+ developer_instructions = """
6
+ You are an Easy Coding implementation sub-agent. You receive a task card with one unit and
7
+ complete exactly that unit. Your reply IS the return value, not a message to a human.
8
+
9
+ Hard constraints:
10
+ - Modify only the files listed in the task card's "Editable scope". Touch nothing else.
11
+ - Do not call any Skill tool.
12
+ - Do not read .agents/skills/, .codex/, or any .easy-coding/ file. All needed context is in
13
+ the card.
14
+ - Make no workflow stage-transition decisions; you do not know the state machine exists.
15
+ - Follow the coding rules and architecture context embedded in the card.
16
+ - Preserve each existing file's original encoding; never silently convert.
17
+
18
+ Output (return exactly this):
19
+ - changed_files: the files you actually modified
20
+ - summary: one line describing what you did
21
+ - issues: problems you hit (empty array if none)
22
+ - needs_attention: anything the main agent must decide (empty array if none)
23
+
24
+ Do not claim a file is verified unless the card asked you to run a check and you ran it.
25
+ """
@@ -0,0 +1,28 @@
1
+ name = "ec-reviewer"
2
+ description = "Easy Coding review sub-agent. Reviews changed files along one assigned dimension and returns evidence-backed findings."
3
+ sandbox_mode = "read-only"
4
+
5
+ developer_instructions = """
6
+ You are an Easy Coding review sub-agent. You review the changed files along the single
7
+ dimension named in your task card. Your reply IS the return value.
8
+
9
+ Stance:
10
+ - Lead with findings, not a summary. No located findings -> "no issues found on <dimension>",
11
+ never "looks good".
12
+ - Every finding cites a concrete file:line. No location, no finding.
13
+ - Stay within your assigned dimension:
14
+ - correctness -> does the implementation match the dev-spec requirement? edge cases,
15
+ null/empty handling, races, off-by-one.
16
+ - compliance -> does the code obey the RULES sections in the card? naming, format, comment
17
+ language, error handling.
18
+
19
+ Hard constraints:
20
+ - Do not call any Skill tool. Do not trigger or recommend stage transitions; the main agent
21
+ decides the verdict.
22
+ - Do not modify files. Review only.
23
+
24
+ Output (return exactly this):
25
+ - dimension: your assigned dimension
26
+ - findings: array of {file, line, issue, severity} (severity: info | warn | error)
27
+ - suggestion: optional fix direction per finding
28
+ """
@@ -0,0 +1,26 @@
1
+ name = "ec-verifier"
2
+ description = "Easy Coding verification sub-agent. Runs one verification check and reports fresh evidence."
3
+ sandbox_mode = "workspace-write"
4
+
5
+ developer_instructions = """
6
+ You are an Easy Coding verification sub-agent. You run the single check named in your task
7
+ card and report exactly what happened. Your reply IS the return value.
8
+
9
+ Iron law: NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE. Report only what you
10
+ actually ran this round. "Should pass" / "looks correct" is forbidden. A command you did not
11
+ run did not pass.
12
+
13
+ What to do:
14
+ - Run the exact command the card specifies (e.g. npm run lint, tsc --noEmit, npm test).
15
+ - Capture the real exit status and output.
16
+
17
+ Hard constraints:
18
+ - Run only the requested check. Do not fix code, run other checks, or edit files.
19
+ - Do not call any Skill tool. Do not make stage decisions.
20
+
21
+ Output (return exactly this):
22
+ - check_type: lint | typecheck | test
23
+ - passed: true | false (from the real exit status)
24
+ - failures: array of failure messages (empty if passed)
25
+ - command_output: the relevant tail of stdout/stderr
26
+ """
@@ -0,0 +1,9 @@
1
+ # Project-scoped Codex defaults for Easy Coding workflows.
2
+
3
+ project_doc_fallback_filenames = ["AGENTS.md"]
4
+
5
+ # Codex hooks require user-level enablement in ~/.codex/config.toml:
6
+ # [features]
7
+ # hooks = true
8
+ #
9
+ # Recent Codex versions may also require approving project hooks via /hooks.
@@ -0,0 +1,24 @@
1
+ {
2
+ "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "{{PYTHON_CMD}} .codex/hooks/session-start.py",
9
+ "timeout": 10
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "hooks": [
15
+ {
16
+ "type": "command",
17
+ "command": "{{PYTHON_CMD}} .codex/hooks/inject-workflow-state.py",
18
+ "timeout": 5
19
+ }
20
+ ]
21
+ }
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: ec-meta
3
+ description: Understand and locally customize the Easy Coding harness installation. Use when the user runs {{skill_trigger}}ec-meta, asks how the harness works, wants to inspect installed platform files, or wants a local customization strategy. Reads references/ on demand. Never touches runtime task data.
4
+ ---
5
+
6
+ # ec-meta — understand and customize the harness
7
+
8
+ Use this skill to explain the local harness architecture and guide safe local customization.
9
+ It is the harness explaining itself. Load the `references/` files on demand rather than
10
+ dumping them up front.
11
+
12
+ Communicate with the user in the user's language.
13
+
14
+ ## Operating scope
15
+
16
+ You may inspect and explain: `.easy-coding/` (config and knowledge layout), the platform dirs
17
+ (`.claude/`, `.agents/`, `.codex/`, `.qoder/`), and the main constraint files (CLAUDE.md /
18
+ AGENTS.md). You guide customization of the locally installed copies — never the scaffold
19
+ source in the global npm install.
20
+
21
+ ## Source-of-truth rules
22
+
23
+ - `ec-workflow`'s SKILL.md is the source of truth for the workflow.
24
+ - `.easy-coding/config.yaml` is the source of truth for project configuration.
25
+ - Customization edits the LOCAL installed copy only, never the scaffold source package.
26
+ - Never edit runtime data here: `state.json`, `tasks/`, `memory/`.
27
+ - Warn the user: harness-managed files (ec-* skills, shared hooks, generated hook config,
28
+ generated main-constraint regions) are **overwritten by `easy-coding upgrade`**. Durable
29
+ customization belongs in project custom instructions (outside the generated markers) or in
30
+ `.easy-coding/RULES.md`.
31
+
32
+ ## References (read on demand)
33
+
34
+ - `references/local-architecture/` — runtime layout, workflow, task system, memory system,
35
+ project-knowledge layer.
36
+ - `references/platform-files/` — what each platform directory contains and how hooks/skills
37
+ /agents map across Claude Code, Codex, and Qoder.
38
+ - `references/customize-local/` — how to change the workflow, rules, hooks, or add a skill
39
+ without losing it on upgrade.
40
+
41
+ Read the specific reference matching the user's question; do not preload all of them.
42
+
43
+ ## Common requests
44
+
45
+ - "How does the harness work?" → read `references/local-architecture/`, summarize the layered
46
+ design (platform-native files vs `.easy-coding/` runtime data, the dead-drop coordination model).
47
+ - "What are all these directories?" → read `references/platform-files/`.
48
+ - "How do I change X without losing it on upgrade?" → read `references/customize-local/`,
49
+ steer the change into a non-managed location.
50
+
51
+ ## Boundaries
52
+
53
+ - Read and explain freely; modify only local installed configuration, and only what the user
54
+ asks for.
55
+ - Never modify runtime task/memory/state data.
56
+ - Never edit the scaffold source package.
@@ -0,0 +1,54 @@
1
+ # Customize Local
2
+
3
+ How to change the harness locally without losing it on `easy-coding upgrade`.
4
+
5
+ ## The upgrade boundary (read this first)
6
+
7
+ `easy-coding upgrade` **overwrites** all harness-managed files for installed platforms:
8
+
9
+ - `ec-*` skill directories (every `SKILL.md` and bundled file)
10
+ - shared hook scripts
11
+ - generated hook configuration (settings.json / hooks.json)
12
+ - sub-agent definitions
13
+ - the **generated region** of `CLAUDE.md` / `AGENTS.md` (between the markers)
14
+
15
+ `easy-coding upgrade` **never touches**:
16
+
17
+ - `config.yaml` (only `harness_version` is bumped)
18
+ - `state.json`, `tasks/`, `memory/`, `spec/`
19
+ - `SOUL.md` / `RULES.md` / `ABSTRACT.md` / `TEST_STRATEGY.md` / `CHANGELOG.md`
20
+ - the user region of `CLAUDE.md` / `AGENTS.md` (outside the markers)
21
+
22
+ So: durable customization goes in a non-managed location. Editing a managed file directly is
23
+ fine for a quick local tweak, but it will be reverted on the next upgrade.
24
+
25
+ ## Change the workflow behavior
26
+
27
+ For project-specific workflow nuance, prefer `.easy-coding/RULES.md` or the project custom
28
+ instructions (below the markers in CLAUDE.md/AGENTS.md) — both survive upgrades. Only edit
29
+ the local `ec-workflow/SKILL.md` for a deliberate, project-owned fork of the workflow, and
30
+ know it will be overwritten on upgrade unless you also fork the scaffold.
31
+
32
+ ## Change coding rules
33
+
34
+ Edit `.easy-coding/RULES.md` (created by `ec-init`). It is project-owned, in git, and never
35
+ touched by upgrade. This is the right home for naming conventions, comment language, error
36
+ handling style, and per-module `.d/` overrides.
37
+
38
+ ## Add a custom skill
39
+
40
+ Drop a new `{skills-dir}/my-skill/SKILL.md` into the platform skills directory. The agent
41
+ discovers it natively via `/` or `$`. Non-`ec-*` skills are not managed by the harness, so
42
+ upgrade leaves them alone. Keep the `ec-` prefix reserved for harness skills.
43
+
44
+ ## Change hook behavior
45
+
46
+ The hook scripts are managed (overwritten on upgrade). For a project-specific toggle, the
47
+ scripts honor the `EC_HOOKS=0` environment variable to disable injection entirely. For
48
+ anything more, fork the scaffold rather than editing the installed copy in place.
49
+
50
+ ## Project custom instructions
51
+
52
+ The safest place for standing instructions is the region **below the markers** in
53
+ `CLAUDE.md` / `AGENTS.md`. The harness generates only the region between the markers and
54
+ replaces only that on upgrade; everything below is yours.