cleargate 0.6.1 → 0.6.2
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/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/tsup/assets/cjs_shims.js","../src/config.ts","../src/auth/keychain-store.ts","../src/auth/file-store.ts","../src/auth/factory.ts","../src/auth/acquire.ts","../src/commands/whoami.ts","../src/commands/bootstrap-root.ts","../src/cli.ts","../package.json","../src/commands/scaffold-lint.ts","../src/lib/scaffold-blocklist.ts","../src/commands/join.ts","../src/auth/identity-flow.ts","../src/commands/stamp.ts","../src/lib/codebase-version.ts","../src/lib/stamp-frontmatter.ts","../src/wiki/parse-frontmatter.ts","../src/lib/frontmatter-yaml.ts","../src/commands/init.ts","../src/init/copy-payload.ts","../src/init/merge-settings.ts","../src/init/inject-claude-md.ts","../src/commands/wiki-build.ts","../src/wiki/scan.ts","../src/wiki/derive-bucket.ts","../src/wiki/derive-repo.ts","../src/wiki/git-sha.ts","../src/wiki/page-schema.ts","../src/wiki/synthesis/active-sprint.ts","../src/wiki/synthesis/render.ts","../src/wiki/synthesis/open-gates.ts","../src/wiki/synthesis/product-state.ts","../src/wiki/synthesis/roadmap.ts","../src/lib/manifest.ts","../src/lib/sha256.ts","../src/lib/prompts.ts","../src/lib/identity.ts","../src/commands/wiki-ingest.ts","../src/commands/wiki-lint.ts","../src/wiki/load-wiki.ts","../src/wiki/lint-checks.ts","../src/lib/work-item-type.ts","../src/lib/wiki-config.ts","../src/commands/wiki-query.ts","../src/commands/wiki-audit-status.ts","../src/commands/doctor.ts","../src/lib/pricing.ts","../src/commands/gate.ts","../src/commands/execution-mode.ts","../src/lib/readiness-predicates.ts","../src/lib/frontmatter-cache.ts","../src/commands/gate-run.ts","../src/commands/sprint.ts","../src/commands/story.ts","../src/commands/state.ts","../src/commands/stamp-tokens.ts","../src/lib/ledger-reader.ts","../src/commands/upgrade.ts","../src/lib/claude-md-surgery.ts","../src/lib/settings-json-surgery.ts","../src/lib/merge-ui.ts","../src/lib/editor.ts","../src/commands/uninstall.ts","../src/commands/sync.ts","../src/lib/sync-log.ts","../src/lib/conflict-detector.ts","../src/lib/merge-helper.ts","../src/lib/mcp-client.ts","../src/lib/intake.ts","../src/lib/slug.ts","../src/lib/active-criteria.ts","../src/lib/comments-cache.ts","../src/lib/wiki-comments-render.ts","../src/commands/pull.ts","../src/commands/push.ts","../src/commands/conflicts.ts","../src/commands/sync-log.ts","../src/commands/admin-login.ts","../src/commands/hotfix.ts"],"sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\nexport const ConfigSchema = z\n .object({\n mcpUrl: z.string().url().optional(),\n profile: z.string().min(1).default('default'),\n logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n })\n .strict();\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/** Partial raw config used for each layer before merge */\ntype RawConfig = Partial<{\n mcpUrl: string | undefined;\n profile: string | undefined;\n logLevel: string | undefined;\n}>;\n\nexport interface LoadConfigOptions {\n flags?: RawConfig;\n env?: NodeJS.ProcessEnv;\n configPath?: string;\n}\n\n/**\n * Synchronously loads and merges config from all layers:\n * flags > env > config file > zod defaults\n */\nexport function loadConfig(opts: LoadConfigOptions = {}): Config {\n const {\n flags = {},\n env = process.env,\n configPath,\n } = opts;\n\n // Resolve config file path\n const resolvedConfigPath =\n configPath ??\n (() => {\n const home = os.homedir();\n if (!home) return null;\n return path.join(home, '.cleargate', 'config.json');\n })();\n\n // Layer: file\n let fileLayer: RawConfig = {};\n if (resolvedConfigPath) {\n try {\n const raw = fs.readFileSync(resolvedConfigPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\n `Failed to parse config file at ${resolvedConfigPath}: invalid JSON`,\n );\n }\n // Validate file contents strictly (unknown keys will throw here)\n const fileResult = ConfigSchema.safeParse(parsed);\n if (!fileResult.success) {\n throw new Error(\n `Invalid config file at ${resolvedConfigPath}: ${fileResult.error.message}`,\n );\n }\n fileLayer = fileResult.data;\n } catch (err) {\n // Re-throw parse/validation errors; silently skip only ENOENT\n if (\n err instanceof Error &&\n 'code' in err &&\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ) {\n // file doesn't exist — skip silently\n } else {\n throw err;\n }\n }\n }\n\n // Layer: env\n const envLayer: RawConfig = {};\n if (env['CLEARGATE_MCP_URL']) {\n envLayer.mcpUrl = env['CLEARGATE_MCP_URL'];\n }\n if (env['CLEARGATE_PROFILE']) {\n envLayer.profile = env['CLEARGATE_PROFILE'];\n }\n if (env['CLEARGATE_LOG_LEVEL']) {\n envLayer.logLevel = env['CLEARGATE_LOG_LEVEL'];\n }\n\n // Merge: flags > env > file (start from {} so zod defaults fill in missing fields)\n const merged: Record<string, unknown> = {\n ...fileLayer,\n ...envLayer,\n ...(flags.mcpUrl !== undefined ? { mcpUrl: flags.mcpUrl } : {}),\n ...(flags.profile !== undefined ? { profile: flags.profile } : {}),\n ...(flags.logLevel !== undefined ? { logLevel: flags.logLevel } : {}),\n };\n\n // Remove undefined values so zod defaults apply properly\n for (const key of Object.keys(merged)) {\n if (merged[key] === undefined) {\n delete merged[key];\n }\n }\n\n const result = ConfigSchema.safeParse(merged);\n if (!result.success) {\n throw new Error(`Config validation failed: ${result.error.message}`);\n }\n\n return result.data;\n}\n\n/**\n * Asserts mcpUrl is present, throws a user-friendly error if not.\n */\nexport function requireMcpUrl(cfg: Config): string {\n if (cfg.mcpUrl === undefined) {\n throw new Error(\n 'mcpUrl not configured. Run `cleargate join <invite-url>` first.',\n );\n }\n return cfg.mcpUrl;\n}\n","import { Entry } from '@napi-rs/keyring';\nimport type { TokenStore } from './token-store.js';\n\nexport class KeychainTokenStore implements TokenStore {\n readonly backend = 'keychain' as const;\n\n constructor(private readonly service: string) {}\n\n async save(profile: string, token: string): Promise<void> {\n new Entry(this.service, profile).setPassword(token);\n }\n\n async load(profile: string): Promise<string | null> {\n try {\n const result = new Entry(this.service, profile).getPassword();\n // getPassword() returns string | null per @napi-rs/keyring@1.2.0 index.d.ts:124\n // Despite the docstring claiming it throws NoEntry, the return type wins.\n // Handle both: null return AND potential thrown NoEntry (platform-specific).\n return result ?? null;\n } catch {\n // NoEntry or other keychain error — treat as absent\n return null;\n }\n }\n\n async remove(profile: string): Promise<void> {\n try {\n new Entry(this.service, profile).deletePassword();\n } catch {\n // Entry didn't exist or other keychain error — idempotent, swallow\n }\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport type { TokenStore } from './token-store.js';\n\nconst ProfileEntrySchema = z.object({ refreshToken: z.string().min(1) }).strict();\n\nexport const AuthFileSchema = z\n .object({\n version: z.literal(1),\n profiles: z.record(z.string().min(1), ProfileEntrySchema),\n })\n .strict();\n\ntype AuthFile = z.infer<typeof AuthFileSchema>;\n\nconst EMPTY_AUTH_FILE: AuthFile = { version: 1, profiles: {} };\n\nexport class FileTokenStore implements TokenStore {\n readonly backend = 'file' as const;\n\n constructor(private readonly filePath: string) {}\n\n async save(profile: string, token: string): Promise<void> {\n const current = await this.readFile();\n const updated: AuthFile = {\n ...current,\n profiles: {\n ...current.profiles,\n [profile]: { refreshToken: token },\n },\n };\n await this.writeFile(updated);\n }\n\n async load(profile: string): Promise<string | null> {\n const data = await this.readFile();\n return data.profiles[profile]?.refreshToken ?? null;\n }\n\n async remove(profile: string): Promise<void> {\n let current: AuthFile;\n try {\n current = await this.readFile();\n } catch {\n // File doesn't exist or unreadable — no-op since there's nothing to remove\n return;\n }\n if (!(profile in current.profiles)) {\n return; // Profile doesn't exist — idempotent\n }\n const { [profile]: _removed, ...rest } = current.profiles;\n const updated: AuthFile = { ...current, profiles: rest };\n await this.writeFile(updated);\n }\n\n private async readFile(): Promise<AuthFile> {\n let raw: string;\n try {\n raw = await fs.readFile(this.filePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return EMPTY_AUTH_FILE;\n }\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\n `Failed to parse auth file at ${this.filePath}: invalid JSON`,\n );\n }\n\n const result = AuthFileSchema.safeParse(parsed);\n if (!result.success) {\n // Check for version mismatch specifically\n const versionCheck = (parsed as Record<string, unknown>)?.['version'];\n if (versionCheck !== 1) {\n throw new Error(\n `Invalid auth file at ${this.filePath}: unsupported version ${String(versionCheck)}. Please upgrade \\`cleargate\\` to read this file.`,\n );\n }\n throw new Error(\n `Invalid auth file at ${this.filePath}: ${result.error.message}`,\n );\n }\n\n return result.data;\n }\n\n private async writeFile(data: AuthFile): Promise<void> {\n const dir = path.dirname(this.filePath);\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n // Explicit chmod after mkdir — mkdir only sets mode on newly created dirs\n await fs.chmod(dir, 0o700).catch(() => {\n // If chmod fails on existing dir, that's acceptable — we don't want to\n // surprise users who have set custom modes on ~/.cleargate/\n });\n\n const json = JSON.stringify(data, null, 2);\n const tmpPath = path.join(dir, '.auth.json.tmp');\n\n // Atomic write: write to tmp then rename to avoid partial-write corruption\n await fs.writeFile(tmpPath, json, { mode: 0o600 });\n // Explicit chmod after writeFile — writeFile only sets mode on file creation\n await fs.chmod(tmpPath, 0o600);\n await fs.rename(tmpPath, this.filePath);\n // After rename, chmod the final path to ensure it stays 0600\n await fs.chmod(this.filePath, 0o600);\n }\n}\n","import * as os from 'node:os';\nimport * as path from 'node:path';\nimport { KeychainTokenStore } from './keychain-store.js';\nimport { FileTokenStore } from './file-store.js';\nimport type { TokenStore, TokenStoreFactoryOptions } from './token-store.js';\n\nconst DEFAULT_KEYCHAIN_SERVICE = 'cleargate';\n\nfunction resolveFilePath(opts: TokenStoreFactoryOptions): string {\n if (opts.filePath) return opts.filePath;\n const home = os.homedir();\n if (!home) {\n throw new Error(\n 'Cannot determine home directory. Set opts.filePath explicitly or ensure os.homedir() returns a non-empty string.',\n );\n }\n return path.join(home, '.cleargate', 'auth.json');\n}\n\nfunction defaultWarn(msg: string): void {\n process.stderr.write(msg + '\\n');\n}\n\n/**\n * Creates a TokenStore, selecting the keychain backend when available and\n * falling back to file storage with a stderr warning when the OS keychain\n * cannot be accessed.\n */\nexport async function createTokenStore(\n opts: TokenStoreFactoryOptions = {},\n): Promise<TokenStore> {\n const filePath = resolveFilePath(opts);\n const service = opts.keychainService ?? DEFAULT_KEYCHAIN_SERVICE;\n const warn = opts.warn ?? defaultWarn;\n\n // Short-circuit if backend is forced (test seam, skips probe)\n if (opts.forceBackend === 'file') {\n return new FileTokenStore(filePath);\n }\n if (opts.forceBackend === 'keychain') {\n return new KeychainTokenStore(service);\n }\n\n // Probe the keychain to determine availability\n try {\n const { Entry } = await import('@napi-rs/keyring');\n new Entry(service, '__cleargate_probe__').getPassword();\n // Probe succeeded (returned string | null cleanly) — use keychain\n return new KeychainTokenStore(service);\n } catch {\n // Constructor threw (native module load failed, libsecret missing on Linux)\n // OR getPassword() threw (dbus not running, prompt cancelled)\n // Either way, keychain is unavailable for this CLI invocation\n warn(\n `cleargate: OS keychain unavailable, falling back to file storage at ${filePath}. Run with --log-level=debug for details.`,\n );\n return new FileTokenStore(filePath);\n }\n}\n","/**\n * acquireAccessToken — resolve a short-lived MCP access-token JWT.\n *\n * Resolution order (first success wins):\n * 1. CLEARGATE_MCP_TOKEN env var — CI / dev short-circuit (assumed JWT, not verified locally).\n * 2. In-memory single-flight cache (keyed by `${profile}::${mcpUrl}`) — returns cached token\n * if still valid (expires 60s before access token's `exp` claim).\n * 3. Stored refresh token (keychain/file) + POST /auth/refresh → rotates refresh token, returns access token.\n *\n * Errors surface to caller with a clear message so command handlers can exit cleanly.\n *\n * Lives here (not in mcp-client.ts) because the refresh flow needs TokenStore + mcpUrl and\n * mcp-client.ts is kept thin (just: host, bearer, JSON-RPC).\n */\nimport { createTokenStore } from './factory.js';\nimport type { TokenStore } from './token-store.js';\n\n// ── Single-flight in-memory cache ─────────────────────────────────────────────\n// Process-local; naturally cleared when the Node CLI exits.\n// Key: `${profile}::${mcpUrl}` — R1 mitigation: two profiles in same process never collide.\n// Env-token path (CLEARGATE_MCP_TOKEN) bypasses this cache entirely.\n\nconst CACHE = new Map<string, { accessToken: string; expiresAtMs: number }>();\n\n/** Test seam: clear the acquire cache between tests. */\nexport function __resetAcquireCache(): void {\n CACHE.clear();\n}\n\n/** Decode a JWT payload without verifying the signature (CLI-side only). */\nfunction decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n const padded = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const json = Buffer.from(padded, 'base64').toString('utf8');\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nexport interface AcquireOptions {\n mcpUrl: string;\n profile: string;\n /** Force a fresh /auth/refresh even if the cache has a valid entry. */\n forceRefresh?: boolean;\n /** Test seam: overrides globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n /** Test seam: overrides createTokenStore */\n createStore?: () => Promise<TokenStore>;\n /** Test seam: overrides process.env lookup */\n env?: NodeJS.ProcessEnv;\n /** Test seam: overrides Date.now() for expiry calculations. */\n now?: () => number;\n}\n\nexport class AcquireError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'env_token'\n | 'no_stored_token'\n | 'invalid_token'\n | 'token_revoked'\n | 'transport'\n | 'unexpected_status'\n | 'bad_response',\n ) {\n super(message);\n this.name = 'AcquireError';\n }\n}\n\n/**\n * Returns a bearer string suitable for Authorization headers against /mcp and\n * /admin-api. Rotates the stored refresh token on success.\n */\nexport async function acquireAccessToken(opts: AcquireOptions): Promise<string> {\n const env = opts.env ?? process.env;\n const nowFn = opts.now ?? Date.now;\n\n // 1. Env short-circuit — CI / dev / manual paste. Assumed to be a valid JWT.\n // Env tokens are NOT cached — they have no known exp without decoding + the\n // env is set per-invocation in CI anyway.\n const envToken = env['CLEARGATE_MCP_TOKEN'];\n if (envToken && envToken.length > 0) {\n return envToken;\n }\n\n // 2. Single-flight cache check (skip when forceRefresh is set).\n const cacheKey = `${opts.profile}::${opts.mcpUrl}`;\n if (!opts.forceRefresh) {\n const cached = CACHE.get(cacheKey);\n if (cached && nowFn() < cached.expiresAtMs) {\n return cached.accessToken;\n }\n }\n\n // 3. Stored refresh token → POST /auth/refresh.\n const store = await (opts.createStore ?? createTokenStore)();\n const stored = await store.load(opts.profile);\n if (!stored) {\n throw new AcquireError(\n `No stored credentials for profile '${opts.profile}'. Run \\`cleargate join <invite-url>\\` first, or export CLEARGATE_MCP_TOKEN.`,\n 'no_stored_token',\n );\n }\n\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n let response: Response;\n try {\n response = await fetchFn(`${opts.mcpUrl}/auth/refresh`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ refresh_token: stored }),\n });\n } catch (err) {\n throw new AcquireError(\n `cannot reach ${opts.mcpUrl} (${err instanceof Error ? err.message : String(err)})`,\n 'transport',\n );\n }\n\n if (response.status === 401) {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n if (body.error === 'token_revoked') {\n throw new AcquireError(\n 'refresh token was revoked. Run `cleargate join <invite-url>` to re-authenticate.',\n 'token_revoked',\n );\n }\n throw new AcquireError(\n 'refresh token is invalid or expired. Run `cleargate join <invite-url>` to re-authenticate.',\n 'invalid_token',\n );\n }\n\n if (!response.ok) {\n throw new AcquireError(`unexpected status ${response.status} from /auth/refresh`, 'unexpected_status');\n }\n\n const body = (await response.json().catch(() => null)) as\n | { access_token?: unknown; refresh_token?: unknown }\n | null;\n if (\n !body ||\n typeof body.access_token !== 'string' ||\n typeof body.refresh_token !== 'string' ||\n body.access_token.length === 0 ||\n body.refresh_token.length === 0\n ) {\n throw new AcquireError('server returned unexpected /auth/refresh response shape', 'bad_response');\n }\n\n // Rotate — store the new refresh token so the next call uses a fresh jti.\n await store.save(opts.profile, body.refresh_token);\n\n const accessToken = body.access_token;\n\n // 4. Cache the new access token (expire 60s before the JWT exp claim).\n const payload = decodeJwtPayload(accessToken);\n const exp = payload?.exp;\n if (typeof exp === 'number' && Number.isFinite(exp)) {\n const expiresAtMs = (exp - 60) * 1000;\n CACHE.set(cacheKey, { accessToken, expiresAtMs });\n }\n // If exp is missing or non-numeric, do NOT cache — next call will re-refresh.\n\n return accessToken;\n}\n","/**\n * cleargate whoami — smoke-test command that proves the stored refresh token\n * can be exchanged for a short-lived MCP access token, decodes the JWT payload\n * (no verification — just introspection), and prints identity fields.\n *\n * This is the first command to exercise the acquireAccessToken flow end-to-end;\n * sync/pull/push will be wired in follow-up stories.\n */\nimport { Buffer } from 'node:buffer';\nimport { loadConfig, requireMcpUrl } from '../config.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\n\nexport interface WhoamiOptions {\n profile: string;\n mcpUrlFlag?: string;\n /** Test seam: replaces globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n}\n\nfunction decodeJwtPayload(jwt: string): Record<string, unknown> | null {\n const parts = jwt.split('.');\n if (parts.length !== 3) return null;\n try {\n const json = Buffer.from(parts[1]!, 'base64url').toString('utf8');\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\nexport async function whoamiHandler(opts: WhoamiOptions): Promise<void> {\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n const cfg = loadConfig({ flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag } });\n let mcpUrl: string;\n try {\n mcpUrl = requireMcpUrl(cfg);\n } catch (err) {\n stderr(`cleargate: ${err instanceof Error ? err.message : String(err)}\\n`);\n exit(5);\n return;\n }\n\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl,\n profile: opts.profile,\n fetch: opts.fetch,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`cleargate: ${err.message}\\n`);\n exit(err.code === 'transport' ? 2 : 5);\n return;\n }\n stderr(`cleargate: internal error: ${err instanceof Error ? err.message : String(err)}\\n`);\n exit(99);\n return;\n }\n\n const claims = decodeJwtPayload(accessToken);\n if (!claims) {\n stderr('cleargate: access token received but could not decode payload.\\n');\n exit(7);\n return;\n }\n\n stdout(\n [\n `mcp_url: ${mcpUrl}`,\n `profile: ${opts.profile}`,\n `member_id: ${claims.sub ?? '?'}`,\n `project_id: ${claims.project_id ?? '?'}`,\n `role: ${claims.role ?? '?'}`,\n `expires_at: ${typeof claims.exp === 'number' ? new Date(claims.exp * 1000).toISOString() : '?'}`,\n '',\n ].join('\\n'),\n );\n}\n","/**\n * `cleargate admin bootstrap-root <handle>` — seed the first root admin.\n *\n * Idempotent: running the same handle twice is safe. Provides a `--force` flag\n * to either promote an existing non-root user or insert a second root when one\n * already exists.\n *\n * Exit codes:\n * 0 — success (including idempotent no-op)\n * 1 — validation error (bad handle, second-root guard refused)\n * 2 — missing DATABASE_URL\n * 3 — connection / query failure\n *\n * DATABASE_URL password is scrubbed from all error output.\n *\n * STORY-011-03.\n */\n\nimport pg from 'pg';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Handle validation\n// ─────────────────────────────────────────────────────────────────────────────\n\n// GitHub handle grammar: starts with alphanumeric, ends with alphanumeric,\n// allows hyphens in the middle, max 39 chars total.\n// Single-char handles (no middle section) are allowed.\n// Trailing hyphens are rejected by requiring the last char to be alphanumeric\n// when the handle is longer than 1 char.\nconst HANDLE_RE = /^[A-Za-z0-9]([A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;\n\nexport function isValidHandle(handle: string): boolean {\n return HANDLE_RE.test(handle);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Password scrubber\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SCRUB = /(postgres(?:ql)?:\\/\\/[^:/@]+):[^@/]+@/gi;\n\nexport function scrubPassword(s: string): string {\n return s.replace(SCRUB, '$1:***@');\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Minimal pg client surface needed by this handler. */\nexport interface PgClientLike {\n // connect() returns Promise<void> or Promise<Client> depending on pg version.\n // We accept either by using Promise<unknown>.\n connect(): Promise<unknown>;\n query(text: string, values?: unknown[]): Promise<{ rows: Record<string, unknown>[] }>;\n end(): Promise<void>;\n}\n\nexport interface BootstrapRootOptions {\n handle: string;\n databaseUrl?: string;\n force?: boolean;\n env?: NodeJS.ProcessEnv;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam — factory receives the resolved connection string */\n pgClientFactory?: (url: string) => PgClientLike;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal result — avoids exception-based control flow bleeding into the\n// SQL error catch block (exit seam throws in tests; we must not catch those).\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface SqlResult {\n exitCode: number;\n kind: 'stdout' | 'stderr';\n message: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main handler\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function bootstrapRootHandler(opts: BootstrapRootOptions): Promise<void> {\n const env = opts.env ?? process.env;\n const stdoutFn = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn = opts.exit ?? ((code: number): never => process.exit(code));\n const force = opts.force ?? false;\n\n // ── 1. Handle validation ────────────────────────────────────────────────────\n const { handle } = opts;\n if (!isValidHandle(handle)) {\n stderrFn(`cleargate: error: '${handle}' is not a valid GitHub handle`);\n return exitFn(1);\n }\n\n // ── 2. Resolve DATABASE_URL ─────────────────────────────────────────────────\n const url = opts.databaseUrl ?? env['DATABASE_URL'];\n if (!url) {\n stderrFn('cleargate: error: DATABASE_URL is required (set env or pass --database-url)');\n return exitFn(2);\n }\n\n // ── 3. Connect ──────────────────────────────────────────────────────────────\n const clientFactory =\n opts.pgClientFactory ??\n ((connStr: string): PgClientLike => new pg.Client({ connectionString: connStr }));\n\n const client = clientFactory(url);\n\n try {\n await client.connect();\n } catch (err) {\n const raw = err instanceof Error ? `${err.message} ${err.stack ?? ''}` : String(err);\n stderrFn(`cleargate: error: cannot reach database (${scrubPassword(raw.trim())})`);\n return exitFn(3);\n }\n\n // ── 4. Run SQL (returns a SqlResult, never throws for logic branches) ────────\n // By returning a value rather than calling exitFn() inside runSql(), we ensure\n // that if the test exit-seam throws, the throw propagates out of the whole\n // handler rather than being caught here as a \"database error\".\n let result: SqlResult;\n let sqlError: unknown = undefined;\n\n try {\n result = await runSql(client, handle, force);\n } catch (err) {\n sqlError = err;\n result = { exitCode: 3, kind: 'stderr', message: '' }; // filled below\n }\n\n // ── 5. Close connection ─────────────────────────────────────────────────────\n try {\n await client.end();\n } catch {\n // ignore cleanup error\n }\n\n // ── 6. Handle SQL error ─────────────────────────────────────────────────────\n if (sqlError !== undefined) {\n const err = sqlError;\n const raw = err instanceof Error ? `${err.message} ${err.stack ?? ''}` : String(err);\n stderrFn(`cleargate: error: cannot reach database (${scrubPassword(raw.trim())})`);\n return exitFn(3);\n }\n\n // ── 7. Emit message and exit ────────────────────────────────────────────────\n if (result!.kind === 'stdout') {\n stdoutFn(result!.message);\n } else {\n stderrFn(result!.message);\n }\n return exitFn(result!.exitCode);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SQL logic — returns SqlResult, never calls exitFn\n// ─────────────────────────────────────────────────────────────────────────────\n\nasync function runSql(\n client: PgClientLike,\n handle: string,\n force: boolean,\n): Promise<SqlResult> {\n await client.query('BEGIN');\n\n // Step 1: does the handle already exist?\n const existing = await client.query(\n 'SELECT id, is_root FROM admin_users WHERE github_handle = $1',\n [handle],\n );\n\n if (existing.rows.length > 0) {\n const row = existing.rows[0]!;\n\n if (row['is_root'] === true) {\n // Branch A: already a root admin — idempotent no-op\n await client.query('COMMIT');\n return {\n exitCode: 0,\n kind: 'stdout',\n message: `Root admin '${handle}' already exists; no change.`,\n };\n }\n\n // Row exists but is_root=false\n if (force) {\n // Branch B: promote\n await client.query(\n 'UPDATE admin_users SET is_root = true WHERE github_handle = $1',\n [handle],\n );\n await client.query('COMMIT');\n return {\n exitCode: 0,\n kind: 'stdout',\n message: `Promoted '${handle}' to root admin.`,\n };\n }\n\n // Branch C: existing non-root, no --force\n await client.query('ROLLBACK');\n return {\n exitCode: 1,\n kind: 'stderr',\n message: `cleargate: error: '${handle}' exists but is not a root admin; pass --force to promote`,\n };\n }\n\n // No row for this handle — check if another root exists\n if (!force) {\n const rootCount = await client.query(\n 'SELECT count(*) AS cnt FROM admin_users WHERE is_root = true',\n );\n const cnt = Number((rootCount.rows[0] as { cnt: string })['cnt'] ?? 0);\n\n if (cnt >= 1) {\n // Branch D: refuse second root without --force\n await client.query('ROLLBACK');\n return {\n exitCode: 1,\n kind: 'stderr',\n message:\n 'cleargate: error: refusing to create a second root admin; pass --force to override',\n };\n }\n }\n\n // Branch E: insert (first root, or --force allows a second root)\n await client.query(\n `INSERT INTO admin_users (github_handle, is_root)\n VALUES ($1, true)\n ON CONFLICT (github_handle) DO UPDATE SET is_root = EXCLUDED.is_root`,\n [handle],\n );\n await client.query('COMMIT');\n return {\n exitCode: 0,\n kind: 'stdout',\n message: `Bootstrapped root admin '${handle}'.`,\n };\n}\n","// SPRINT-14 M5 dogfood smoke — STORY-099-01\nimport { Command } from 'commander';\nimport pkg from '../package.json' with { type: 'json' };\nimport { scaffoldLintHandler } from './commands/scaffold-lint.js';\nimport { joinHandler } from './commands/join.js';\nimport { stampHandler } from './commands/stamp.js';\nimport { initHandler } from './commands/init.js';\nimport { wikiBuildHandler } from './commands/wiki-build.js';\nimport { wikiIngestHandler } from './commands/wiki-ingest.js';\nimport { wikiLintHandler } from './commands/wiki-lint.js';\nimport { wikiQueryHandler } from './commands/wiki-query.js';\nimport { wikiAuditStatusHandler } from './commands/wiki-audit-status.js';\nimport { doctorHandler } from './commands/doctor.js';\nimport { gateCheckHandler, gateExplainHandler, gateQaHandler, gateArchHandler } from './commands/gate.js';\nimport { gateRunHandler } from './commands/gate-run.js';\nimport { sprintInitHandler, sprintCloseHandler, sprintArchiveHandler } from './commands/sprint.js';\nimport { storyStartHandler, storyCompleteHandler } from './commands/story.js';\nimport { stateUpdateHandler, stateValidateHandler } from './commands/state.js';\nimport { stampTokensHandler } from './commands/stamp-tokens.js';\nimport { upgradeHandler } from './commands/upgrade.js';\nimport { uninstallHandler } from './commands/uninstall.js';\nimport { syncHandler, syncCheckHandler } from './commands/sync.js';\nimport { pullHandler } from './commands/pull.js';\nimport { pushHandler } from './commands/push.js';\nimport { conflictsHandler } from './commands/conflicts.js';\nimport { syncLogHandler } from './commands/sync-log.js';\nimport { adminLoginHandler } from './commands/admin-login.js';\nimport { hotfixNewHandler } from './commands/hotfix.js';\n\nconst program = new Command();\n\nprogram\n .name('cleargate')\n .description('ClearGate CLI — connects AI agent teams to the ClearGate MCP server')\n .version(pkg.version, '-V, --version')\n .option('--profile <name>', 'configuration profile to use', 'default')\n .option('--mcp-url <url>', 'MCP server URL (overrides config file and env)')\n .showHelpAfterError('(use `cleargate --help`)');\n\nprogram\n .command('join <invite-url>')\n .description('join a ClearGate workspace using an invite URL')\n .option('--auth <provider>', 'identity provider: github | email')\n .option('--non-interactive', 'fail instead of prompting (CI mode)')\n .option('--code <code>', 'OTP code for non-interactive email auth')\n .action(async (inviteUrl: string, _opts: Record<string, unknown>, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n const cmdOpts = command.opts<{ auth?: string; nonInteractive?: boolean; code?: string }>();\n await joinHandler({\n inviteUrl,\n profile: globals.profile,\n mcpUrlFlag: globals.mcpUrl,\n // FLASHCARD #cli #commander #optional-key: only set keys when defined\n ...(cmdOpts.auth !== undefined ? { auth: cmdOpts.auth } : {}),\n ...(cmdOpts.nonInteractive === true ? { nonInteractive: true } : {}),\n ...(cmdOpts.code !== undefined ? { code: cmdOpts.code } : {}),\n });\n });\n\nprogram\n .command('init')\n .description('initialise a repo with ClearGate scaffold (CLAUDE.md block, hook config, agents, templates)')\n .option('--force', 'overwrite existing files that differ from the bundled payload')\n .option('--yes', 'non-interactive: accept all defaults without prompting')\n .option('--pin <ver>', 'CR-009: pin hook resolver to a specific cleargate CLI version (default: package version)')\n .action(async (opts: { force?: boolean; yes?: boolean; pin?: string }) => {\n await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false, pin: opts.pin });\n });\n\nprogram\n .command('whoami')\n .description('print the currently authenticated agent identity')\n .action(async () => {\n const { whoamiHandler } = await import('./commands/whoami.js');\n const parentOpts = program.opts<{ profile: string; mcpUrl?: string }>();\n await whoamiHandler({\n profile: parentOpts.profile,\n mcpUrlFlag: parentOpts.mcpUrl,\n });\n });\n\nprogram\n .command('stamp <file>')\n .description('stamp ClearGate metadata fields into a file\\'s frontmatter')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampHandler(file, { dryRun: opts.dryRun });\n });\n\nconst wiki = program\n .command('wiki')\n .description('query or update the workspace wiki');\n\nwiki\n .command('build')\n .description('full rebuild of .cleargate/wiki/ from raw delivery items')\n .action(async () => {\n await wikiBuildHandler();\n });\n\nwiki\n .command('ingest <file>')\n .description('ingest a single raw delivery file into the wiki')\n .action(async (file: string) => {\n await wikiIngestHandler({ rawPath: file });\n });\n\nwiki\n .command('lint')\n .description('check wiki pages for drift vs raw sources')\n .option('--suggest', 'advisory mode — exit 0, emit suggestions only')\n .helpOption('--help', [\n 'Usage: cleargate wiki lint [--suggest]',\n '',\n 'Enforcement mode (default): exits 1 on any finding.',\n 'Suggest mode (--suggest): exits 0, prefixes findings with [advisory],',\n ' and emits Karpathy cross-ref discovery candidates.',\n ].join('\\n'))\n .action(async (_opts: Record<string, unknown>, command: Command) => {\n const cmdOpts = command.opts<{ suggest?: boolean }>();\n await wikiLintHandler({\n mode: cmdOpts.suggest ? 'suggest' : 'enforce',\n });\n });\n\nwiki\n .command('query <terms...>')\n .description('search the wiki index for matching work items')\n .option('--persist', 'write result as a topic page under wiki/topics/')\n .addHelpText('after', [\n '',\n 'NOTE: CLI synthesis is grep-and-list. For NL synthesis with the',\n 'cleargate-wiki-query subagent, invoke from a Claude Code session.',\n 'This diverges from PROPOSAL-002 §2.2 intentionally for testability',\n 'and offline/scripted use.',\n ].join('\\n'))\n .action(async (terms: string[], opts: { persist?: boolean }) => {\n await wikiQueryHandler({\n query: terms.join(' '),\n persist: opts.persist ?? false,\n });\n });\n\nwiki\n .command('audit-status')\n .description('detect raw-item status/location drift; --fix applies safe corrections')\n .option('--fix', 'apply safe status corrections to frontmatter')\n .option('--yes', 'required together with --fix to confirm writes')\n .option('--quiet', 'suppress diff output')\n .action(async (opts: { fix?: boolean; yes?: boolean; quiet?: boolean }) => {\n await wikiAuditStatusHandler(opts);\n });\n\nconst gate = program\n .command('gate')\n .description('evaluate readiness gates for a ClearGate work-item file');\n\ngate\n .command('check <file>')\n .description('evaluate readiness criteria and write result to frontmatter')\n .option('-v, --verbose', 'show full expected-vs-actual detail per criterion')\n .option('--transition <name>', 'override auto-detected transition name')\n .action(async (file: string, opts: { verbose?: boolean; transition?: string }) => {\n await gateCheckHandler(file, { verbose: opts.verbose, transition: opts.transition });\n });\n\ngate\n .command('explain <file>')\n .description('render cached gate result in ≤50 agent tokens (read-only)')\n .action(async (file: string) => {\n await gateExplainHandler(file);\n });\n\ngate\n .command('qa <worktree> <branch>')\n .description('run QA pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateQaHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\ngate\n .command('arch <worktree> <branch>')\n .description('run Architect pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateArchHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\n// STORY-018-03: config-driven gates. Commander v12 does NOT treat `<name>` as a\n// catch-all fallback when sibling literal subcommands exist (QA'd on 2026-04-25\n// — it emits \"unknown command\" before a parameterized handler can fire). Since\n// the gate names are a closed set, enumerate them explicitly.\nfor (const gateName of ['precommit', 'test', 'typecheck', 'lint'] as const) {\n gate\n .command(gateName)\n .description(`run the configured ${gateName} gate command`)\n .option('--strict', 'exit non-zero if gate not configured')\n .action((opts: { strict?: boolean }) => {\n gateRunHandler(gateName, { strict: opts.strict === true ? true : undefined });\n });\n}\n\nprogram\n .command('scaffold-lint')\n .description('grep cleargate-planning/ for stack-specific strings; fail on leaks')\n .option('--fix-hint', 'emit placeholder suggestions per finding')\n .option('--versions', 'also flag semver-shaped strings')\n .option('--quiet', 'suppress per-finding output; exit code only')\n .action(async (opts: { fixHint?: boolean; versions?: boolean; quiet?: boolean }) => {\n await scaffoldLintHandler(opts);\n });\n\nconst sprint = program\n .command('sprint')\n .description('sprint lifecycle commands (v2 only — inert under v1)');\n\nsprint\n .command('init <sprint-id>')\n .description('initialise a new sprint — creates state.json and worktree skeleton')\n .requiredOption('--stories <csv>', 'comma-separated story IDs for this sprint')\n .action((sprintId: string, opts: { stories: string }) => {\n sprintInitHandler({ sprintId, stories: opts.stories });\n });\n\nsprint\n .command('close <sprint-id>')\n .description('close a sprint — validates all stories are terminal, runs prefill + suggest_improvements')\n .option('--assume-ack', 'skip the \"waiting for Reporter\" gate and flip state to Completed directly')\n .action((sprintId: string, opts: { assumeAck?: boolean }) => {\n const handlerOpts: { sprintId: string; assumeAck?: boolean } = { sprintId };\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n if (opts.assumeAck === true) {\n handlerOpts.assumeAck = true;\n }\n sprintCloseHandler(handlerOpts);\n });\n\nsprint\n .command('archive <sprint-id>')\n .description('archive a completed sprint — move pending-sync files, clear .active, merge + delete sprint branch')\n .option('--dry-run', 'print the archive plan without making any changes')\n .action(async (sprintId: string, opts: { dryRun?: boolean }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const handlerOpts: { sprintId: string; dryRun?: boolean } = { sprintId };\n if (opts.dryRun === true) {\n handlerOpts.dryRun = true;\n }\n await sprintArchiveHandler(handlerOpts);\n });\n\nconst story = program\n .command('story')\n .description('story lifecycle commands (v2 only — inert under v1)');\n\nstory\n .command('start <story-id>')\n .description('create a git worktree for a story on the sprint branch')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyStartHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nstory\n .command('complete <story-id>')\n .description('mark a story complete and clean up its worktree (stub — requires complete_story.mjs)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyCompleteHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nconst state = program\n .command('state')\n .description('state.json management commands (v2 only — inert under v1)');\n\nstate\n .command('update <story-id> <new-state>')\n .description('update a story\\'s state in state.json')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup (overrides .active sentinel)')\n .action((storyId: string, newState: string, opts: { sprint?: string }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const cliOpts: Parameters<typeof stateUpdateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateUpdateHandler({ storyId, newState }, cliOpts);\n });\n\nstate\n .command('validate <sprint-id>')\n .description('validate all story states in a sprint\\'s state.json')\n .option('--sprint <id>', 'override sprint ID for execution_mode lookup')\n .action((sprintId: string, opts: { sprint?: string }) => {\n const cliOpts: Parameters<typeof stateValidateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateValidateHandler({ sprintId: opts.sprint ?? sprintId }, cliOpts);\n });\n\nprogram\n .command('stamp-tokens <file>')\n .description('stamp draft_tokens from token-ledger into a work-item file (hook-invoked)')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampTokensHandler(file, { dryRun: opts.dryRun });\n });\n\nconst admin = program\n .command('admin')\n .description('administrative operations (login, create-project, invite, issue-token, revoke-token)');\n\nadmin\n .command('login')\n .description('log in as a ClearGate admin via GitHub OAuth device flow')\n .option('--mcp-url <url>', 'MCP server URL (overrides CLEARGATE_MCP_URL and config file)')\n .action(async (opts: { mcpUrl?: string }, command: Command) => {\n const globals = command.parent!.parent!.opts<{ mcpUrl?: string }>();\n await adminLoginHandler({\n mcpUrl: opts.mcpUrl ?? globals.mcpUrl,\n });\n });\n\nadmin\n .command('bootstrap-root <handle>')\n .description('seed the first root admin in admin_users (idempotent)')\n .option('--database-url <url>', 'Postgres connection string; falls back to DATABASE_URL env')\n .option('--force', 'override second-root guard / promote non-root user to root')\n .action(async (handle: string, opts: { databaseUrl?: string; force?: boolean }) => {\n const { bootstrapRootHandler } = await import('./commands/bootstrap-root.js');\n await bootstrapRootHandler({ handle, databaseUrl: opts.databaseUrl, force: opts.force ?? false });\n });\n\nprogram\n .command('doctor')\n .description('diagnose scaffold drift, hook health, blocked items, and token cost')\n .option('--check-scaffold', 'check scaffold files for drift against install snapshot')\n .option('--session-start-mode', 'hidden: enables daily throttle (used by session-start hook)', false)\n .option('--session-start', 'emit blocked pending-sync items summary (used by SessionStart hook)')\n .option('--pricing <file>', 'compute USD cost estimate from a work item\\'s draft_tokens')\n .option('--can-edit <file>', 'CR-008: exit 0 if editing file is allowed, exit 1 if planning required')\n .option('--cwd <dir>', 'working directory for the doctor check (default: process.cwd())')\n .option('-v, --verbose', 'show per-file drift detail')\n .addHelpText('after', [\n '',\n 'Modes (mutually exclusive):',\n ' --check-scaffold Compute drift for all tracked scaffold files.',\n ' Writes .cleargate/.drift-state.json.',\n ' --session-start List blocked pending-sync items (≤10, ≤100 tokens).',\n ' --pricing <file> Compute USD estimate from a work item\\'s draft_tokens.',\n ' --can-edit <file> Check if editing a file requires a planning work item.',\n ' (default) Print a minimal hook-config health report.',\n '',\n 'Exit codes:',\n ' 0 Clean — no blockers, no config errors.',\n ' 1 Blocked items or advisory issues — see stdout.',\n ' 2 ClearGate misconfigured or partially installed — see stdout for remediation.',\n ].join('\\n'))\n .action(async (opts: { checkScaffold?: boolean; sessionStartMode?: boolean; sessionStart?: boolean; pricing?: string; canEdit?: string; cwd?: string; verbose?: boolean }) => {\n await doctorHandler({\n checkScaffold: opts.checkScaffold,\n sessionStartMode: opts.sessionStartMode,\n sessionStart: opts.sessionStart,\n pricing: !!opts.pricing,\n pricingFile: opts.pricing,\n canEdit: !!opts.canEdit,\n canEditFile: opts.canEdit,\n verbose: opts.verbose,\n }, opts.cwd ? { cwd: opts.cwd } : undefined);\n });\n\nprogram\n .command('upgrade')\n .description('three-way merge scaffold files with upstream changes')\n .option('--dry-run', 'print plan without making any changes')\n .option('--yes', 'auto-accept \"take theirs\" for all merge-3way files (non-interactive)')\n .option('--only <tier>', 'restrict to a specific scaffold tier (protocol/template/agent/hook/skill/cli-config)')\n .addHelpText('after', [\n '',\n 'Overwrite policies:',\n ' always — silent overwrite with package content',\n ' never — silent skip',\n ' preserve — silent skip',\n ' merge-3way — interactive: [k]eep mine / [t]ake theirs / [e]dit in $EDITOR',\n '',\n '--yes auto-accepts [t]ake theirs for all merge-3way files.',\n ].join('\\n'))\n .action(async (opts: { dryRun?: boolean; yes?: boolean; only?: string }) => {\n await upgradeHandler({ dryRun: opts.dryRun, yes: opts.yes, only: opts.only });\n });\n\nprogram\n .command('uninstall')\n .description('remove ClearGate scaffold from a project (preservation-first)')\n .option('--dry-run', 'preview planned actions without making any changes (CI-safe)')\n .option('--preserve <tiers>', 'comma-separated tier ids to force-preserve (default: user-artifact)')\n .option('--remove <tiers>', 'comma-separated tier ids to force-remove; use \"all\" to remove everything including user artifacts (DANGEROUS)')\n .option('--yes', 'skip typed project-name confirmation (dangerous — use in scripts/CI)')\n .option('--path <dir>', 'target directory (must contain .cleargate/.install-manifest.json); defaults to cwd')\n .option('--force', 'bypass uncommitted-changes safety check (not applicable for non-git targets)')\n .addHelpText('after', [\n '',\n 'Preservation defaults:',\n ' user-artifact tier → kept (FLASHCARD.md, archive, pending-sync, sprint REPORT.md)',\n ' framework tiers → removed (protocol, template, agent, hook, skill, cli-config)',\n '',\n 'Always removed (no prompt): .claude/agents/*.md, ClearGate hooks,',\n ' .claude/skills/flashcard/, CLAUDE.md CLEARGATE block,',\n ' `cleargate` from package.json, .install-manifest.json, .drift-state.json.',\n '',\n 'Non-git targets: uncommitted-changes check is skipped silently.',\n ].join('\\n'))\n .action(async (opts: {\n dryRun?: boolean;\n preserve?: string;\n remove?: string;\n yes?: boolean;\n path?: string;\n force?: boolean;\n }) => {\n await uninstallHandler({\n dryRun: opts.dryRun,\n preserve: opts.preserve ? opts.preserve.split(',').map((s) => s.trim()) : undefined,\n remove: opts.remove ? opts.remove.split(',').map((s) => s.trim()) : undefined,\n yes: opts.yes,\n path: opts.path,\n force: opts.force,\n });\n });\n\nprogram\n .command('sync')\n .description('pull remote updates, resolve conflicts, push local changes')\n .option('--dry-run', 'print plan without making any changes or sync-log entries')\n .option('--check', 'read-only drift probe — prints JSON, no mutation, hook-safe')\n .action(async (opts: { dryRun?: boolean; check?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n if (opts.check) {\n await syncCheckHandler({ profile: globals.profile });\n return;\n }\n await syncHandler({ dryRun: opts.dryRun ?? false, profile: globals.profile });\n });\n\nprogram\n .command('pull <id-or-remote-id>')\n .description('pull a single item from the remote PM tool by local ID or remote_id')\n .option('--comments', 'also pull comments for this item (STORY-010-06; not yet implemented)')\n .action(async (idOrRemoteId: string, opts: { comments?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pullHandler(idOrRemoteId, { comments: opts.comments, profile: globals.profile });\n });\n\nprogram\n .command('push <file>')\n .description('push a local work item to the MCP server (requires approved: true in frontmatter)')\n .option('--revert <id-or-remote-id>', 'soft-revert a pushed item by setting status to archived-without-shipping')\n .option('--force', 'bypass the \"done\" status guard when reverting')\n .addHelpText('after', [\n '',\n 'Push mode:',\n ' Reads local frontmatter. Requires approved: true — exits 1 without network call otherwise.',\n ' On success: writes pushed_by + pushed_at from server back to local frontmatter.',\n ' Appends sync-log entry op=push.',\n '',\n 'Revert mode (--revert <id-or-remote-id>):',\n ' Calls cleargate_sync_status with status=archived-without-shipping.',\n ' Does NOT delete the remote item or remove local remote_id.',\n ' Refuses if local status=done unless --force is passed.',\n ' Appends sync-log entry op=push-revert.',\n ].join('\\n'))\n .action(async (file: string, opts: { revert?: string; force?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pushHandler(file, { revert: opts.revert, force: opts.force, profile: globals.profile });\n });\n\nprogram\n .command('conflicts')\n .description('list unresolved sync conflicts from .cleargate/.conflicts.json')\n .option('--refresh', 'force a new /auth/refresh even if the cached token is still valid')\n .action(async (opts: { refresh?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await conflictsHandler({ refresh: opts.refresh, profile: globals.profile });\n });\n\nprogram\n .command('sync-log')\n .description('filter and print sync-log entries')\n .option('--actor <email>', 'filter by actor email')\n .option('--op <op>', 'filter by operation (push|pull|pull-intake|...)')\n .option('--target <id>', 'filter by target work item ID')\n .option('--limit <n>', 'maximum number of entries to show (default 50)', '50')\n .action(async (opts: { actor?: string; op?: string; target?: string; limit?: string }) => {\n await syncLogHandler({\n actor: opts.actor,\n op: opts.op,\n target: opts.target,\n limit: opts.limit !== undefined ? parseInt(opts.limit, 10) : 50,\n });\n });\n\nconst hotfix = program\n .command('hotfix')\n .description('hotfix lane commands (off-sprint trivial fix scaffolding)');\n\n// FLASHCARD #cli #commander #subcommand-routing (2026-04-25): Commander v12\n// does NOT treat `<verb>` as a catch-all fallback when sibling literal\n// subcommands exist. Since `new` is the only verb for now, enumerate it\n// explicitly as a literal subcommand.\nhotfix\n .command('new <slug>')\n .description('scaffold a new HOTFIX-NNN_<slug>.md in pending-sync/')\n .action((slug: string) => {\n hotfixNewHandler({ slug });\n });\n\nvoid program.parseAsync(process.argv);\n","{\n \"name\": \"cleargate\",\n \"version\": \"0.6.1\",\n \"private\": false,\n \"type\": \"module\",\n \"description\": \"Planning framework for Claude Code agents — sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.\",\n \"license\": \"MIT\",\n \"bin\": {\n \"cleargate\": \"dist/cli.js\"\n },\n \"main\": \"./dist/cli.cjs\",\n \"module\": \"./dist/cli.js\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/cli.d.ts\",\n \"import\": \"./dist/cli.js\",\n \"require\": \"./dist/cli.cjs\"\n },\n \"./admin-api\": {\n \"types\": \"./dist/admin-api/index.d.ts\",\n \"import\": \"./dist/admin-api/index.js\",\n \"require\": \"./dist/admin-api/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"scripts\": {\n \"prebuild\": \"tsx scripts/build-manifest.ts && node scripts/copy-planning-payload.mjs\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"pretest\": \"npm run build\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@napi-rs/keyring\": \"^1.2.0\",\n \"commander\": \"^12\",\n \"diff\": \"^5.2.2\",\n \"js-yaml\": \"^4.1.0\",\n \"pg\": \"^8.12.0\",\n \"zod\": \"^4.3.0\"\n },\n \"devDependencies\": {\n \"@types/diff\": \"^5.2.3\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"@types/pg\": \"^8.11.10\",\n \"tsup\": \"^8\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.8.0\",\n \"vitest\": \"^2.1.0\"\n }\n}\n","/**\n * scaffold-lint.ts — `cleargate scaffold-lint` command handler.\n *\n * STORY-018-04: Scans cleargate-planning/ for stack-specific strings that\n * should not appear in the installable scaffold.\n *\n * Exit codes: 0 = clean, 1 = findings, 2 = config/parse error.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n DEFAULT_BLOCKLIST,\n getTermCategory,\n CATEGORY_PLACEHOLDERS,\n parseAllowlist,\n parseUserBlocklist,\n type AllowlistEntry,\n} from '../lib/scaffold-blocklist.js';\n\n// ─── Scan extensions ──────────────────────────────────────────────────────────\n\nconst SCAN_EXTENSIONS = new Set(['.md', '.sh', '.mjs', '.json']);\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ScaffoldLintOptions {\n fixHint?: boolean;\n versions?: boolean;\n quiet?: boolean;\n cwd?: string;\n planningDir?: string; // override for tests; defaults to path.join(cwd, 'cleargate-planning')\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n}\n\ninterface Finding {\n file: string; // relative to planningDir parent\n line: number;\n term: string;\n context: string; // matched line truncated to 80 chars\n}\n\n// ─── scaffoldLintHandler ──────────────────────────────────────────────────────\n\nexport function scaffoldLintHandler(opts: ScaffoldLintOptions): void {\n const stdoutFn = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n opts.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = opts.cwd ?? process.cwd();\n const planningDir = opts.planningDir ?? path.join(cwd, 'cleargate-planning');\n\n const result = runScaffoldLint(opts, cwd, planningDir, stdoutFn, stderrFn);\n\n if (result.exitCode === 0) {\n stdoutFn('scaffold-lint: clean');\n }\n\n if (result.exitCode !== 0) {\n exitFn(result.exitCode);\n }\n}\n\ninterface LintResult {\n exitCode: number;\n}\n\nfunction runScaffoldLint(\n opts: ScaffoldLintOptions,\n cwd: string,\n planningDir: string,\n stdoutFn: (s: string) => void,\n stderrFn: (s: string) => void,\n): LintResult {\n // ── 1. Load user blocklist (fail-fast on malformed) ─────────────────────────\n const userBlocklistPath = path.join(cwd, '.cleargate', 'scaffold-blocklist.txt');\n let userTerms: string[] = [];\n\n if (fs.existsSync(userBlocklistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(userBlocklistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: error reading ${userBlocklistPath}: ${String(err)}`);\n return { exitCode: 2 };\n }\n const parsed = parseUserBlocklist(raw, userBlocklistPath, stderrFn);\n if (parsed === null) {\n return { exitCode: 2 };\n }\n userTerms = parsed;\n }\n\n // ── 2. Load allowlist (skip + warn on malformed) ──────────────────────────\n const allowlistPath = path.join(cwd, '.cleargate', 'scaffold-allowlist.txt');\n let allowlistEntries: AllowlistEntry[] = [];\n\n if (fs.existsSync(allowlistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(allowlistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: warning: error reading ${allowlistPath}: ${String(err)}`);\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n allowlistEntries = parseAllowlist(raw!, allowlistPath, stderrFn);\n } catch {\n // parseAllowlist is synchronous and doesn't throw in normal usage\n }\n }\n\n // ── 3. Build combined blocklist ───────────────────────────────────────────\n const allTerms = [...DEFAULT_BLOCKLIST, ...userTerms];\n\n // ── 4. Walk cleargate-planning/ ───────────────────────────────────────────\n if (!fs.existsSync(planningDir)) {\n // No planning dir — nothing to scan, exit clean\n stdoutFn('scaffold-lint: clean');\n return { exitCode: 0 };\n }\n\n const files = walkDir(planningDir);\n\n // ── 5. Scan files ─────────────────────────────────────────────────────────\n const findings: Finding[] = [];\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!SCAN_EXTENSIONS.has(ext)) continue;\n\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf8');\n } catch {\n continue; // skip unreadable files\n }\n\n // Relative path from cwd for reporting\n const relPath = path.relative(cwd, filePath).replace(/\\\\/g, '/');\n\n const lines = content.split('\\n');\n for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {\n const lineNum = lineIdx + 1;\n const lineText = lines[lineIdx];\n\n for (const term of allTerms) {\n const re = new RegExp(escapeRegex(term), 'gi');\n if (!re.test(lineText)) continue;\n\n // Check allowlist\n if (isAllowlisted(relPath, term, allowlistEntries)) continue;\n\n findings.push({\n file: relPath,\n line: lineNum,\n term: term.toLowerCase(),\n context: lineText.slice(0, 80),\n });\n // Only record the FIRST matching term per line per occurrence\n // (break inner term loop to avoid duplicate lines for the same match)\n break;\n }\n }\n }\n\n // ── 6. Sort findings by file asc, line asc ───────────────────────────────\n findings.sort((a, b) => {\n if (a.file < b.file) return -1;\n if (a.file > b.file) return 1;\n return a.line - b.line;\n });\n\n // ── 7. Emit findings ──────────────────────────────────────────────────────\n if (findings.length === 0) {\n return { exitCode: 0 };\n }\n\n if (!opts.quiet) {\n for (const f of findings) {\n const line = `${f.file}:${f.line}: ${f.term} — example context: ${f.context}`;\n stderrFn(line);\n\n if (opts.fixHint) {\n const category = getTermCategory(f.term);\n const placeholder = category ? CATEGORY_PLACEHOLDERS.get(category) : undefined;\n if (placeholder) {\n stderrFn(` hint: replace with ${placeholder}`);\n } else {\n stderrFn(` hint: replace with <your-replacement>`);\n }\n }\n }\n }\n\n return { exitCode: 1 };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction walkDir(dir: string): string[] {\n const results: string[] = [];\n\n function recurse(current: string): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true }) as fs.Dirent[];\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n recurse(fullPath);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n }\n\n recurse(dir);\n return results;\n}\n\nfunction escapeRegex(term: string): string {\n return term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction isAllowlisted(\n relPath: string,\n term: string,\n entries: AllowlistEntry[],\n): boolean {\n const termLower = term.toLowerCase();\n\n for (const entry of entries) {\n if (entry.term !== termLower) continue;\n\n if (!entry.glob) {\n // Global suppression for this term\n return true;\n }\n\n // Glob is relative to repo root (cwd)\n // Use simple star matching via path.matchesGlob (Node 24)\n if (matchesGlob(relPath, entry.glob)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Minimal glob matcher using Node 24's path.matchesGlob when available,\n * with a fallback star-replace regex for older environments.\n */\nfunction matchesGlob(filePath: string, glob: string): boolean {\n // Node 24 built-in\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pathModule = path as any;\n if (typeof pathModule.matchesGlob === 'function') {\n return pathModule.matchesGlob(filePath, glob);\n }\n } catch {\n // fall through\n }\n\n // Fallback: simple star matching\n const regexStr = glob\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '§DSTAR§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§DSTAR§/g, '.*');\n\n return new RegExp(`^${regexStr}$`).test(filePath);\n}\n","/**\n * scaffold-blocklist.ts — Default blocklist for `cleargate scaffold-lint`.\n *\n * STORY-018-04: Stack-leak detection. These terms should not appear in the\n * installable scaffold under cleargate-planning/ — they indicate ClearGate's\n * own dogfooding vocabulary leaking into the template that downstream users\n * receive.\n *\n * Categories: ORMs, web frameworks, infra, DB engines, cache/queue, styling.\n */\n\n// ─── Blocklist categories ─────────────────────────────────────────────────────\n\nconst ORMS = ['drizzle', 'prisma', 'sequelize', 'typeorm'] as const;\n\nconst WEB_FRAMEWORKS = [\n 'fastify',\n 'express',\n 'hono',\n 'svelte',\n 'sveltekit',\n 'react',\n 'next.js',\n 'nextjs',\n 'nuxt',\n 'remix',\n 'vue',\n] as const;\n\nconst INFRA = ['coolify', 'vercel', 'netlify', 'heroku', 'render.com', 'fly.io'] as const;\n\nconst DB_ENGINES = ['postgres', 'postgresql', 'mysql', 'sqlite', 'mongodb', 'dynamodb'] as const;\n\nconst CACHE_QUEUE = ['redis', 'ioredis', 'memcached', 'rabbitmq', 'kafka'] as const;\n\nconst STYLING = ['daisyui', 'tailwind', 'bootstrap', 'mui'] as const;\n\n// ─── Category map (for --fix-hint placeholders) ───────────────────────────────\n\nexport type BlocklistCategory = 'orm' | 'framework' | 'infra' | 'db' | 'cache' | 'styling';\n\nconst TERM_CATEGORY_MAP: ReadonlyMap<string, BlocklistCategory> = new Map<string, BlocklistCategory>([\n ...ORMS.map((t): [string, BlocklistCategory] => [t, 'orm']),\n ...WEB_FRAMEWORKS.map((t): [string, BlocklistCategory] => [t, 'framework']),\n ...INFRA.map((t): [string, BlocklistCategory] => [t, 'infra']),\n ...DB_ENGINES.map((t): [string, BlocklistCategory] => [t, 'db']),\n ...CACHE_QUEUE.map((t): [string, BlocklistCategory] => [t, 'cache']),\n ...STYLING.map((t): [string, BlocklistCategory] => [t, 'styling']),\n]);\n\nexport const CATEGORY_PLACEHOLDERS: ReadonlyMap<BlocklistCategory, string> = new Map([\n ['orm', '<your-orm>'],\n ['framework', '<your-framework>'],\n ['infra', '<your-infra>'],\n ['db', '<your-db>'],\n ['cache', '<your-cache>'],\n ['styling', '<your-styling>'],\n]);\n\n// ─── Public exports ───────────────────────────────────────────────────────────\n\nexport const DEFAULT_BLOCKLIST: readonly string[] = [\n ...ORMS,\n ...WEB_FRAMEWORKS,\n ...INFRA,\n ...DB_ENGINES,\n ...CACHE_QUEUE,\n ...STYLING,\n];\n\n/**\n * Get the category for a blocklist term (default or user-supplied).\n * Returns undefined for user-supplied terms that don't map to a category.\n */\nexport function getTermCategory(term: string): BlocklistCategory | undefined {\n return TERM_CATEGORY_MAP.get(term.toLowerCase());\n}\n\n// ─── Allowlist parser ─────────────────────────────────────────────────────────\n\nexport interface AllowlistEntry {\n term: string;\n glob?: string;\n}\n\nexport interface ParsedAllowlist {\n entries: AllowlistEntry[];\n warnings: string[];\n}\n\n/**\n * Parse `.cleargate/scaffold-allowlist.txt` content.\n *\n * Format per line: `<term>[ <file-glob>]`\n * - Lines starting with `#` are comments.\n * - Blank lines are skipped.\n * - Term is case-insensitive substring.\n * - Glob is optional; when absent, suppresses ALL files.\n *\n * Returns parsed entries + any warnings (for malformed lines — per orchestrator\n * decision: skip + warn rather than fail; malformed-allowlist exit-2 is\n * handled by the blocklist file, not the allowlist).\n *\n * NOTE: Per M2.md \"malformed lines: fail fast\" applies to the *blocklist* file\n * format only. For allowlist, skip + warn to stderr per orchestrator Q2 decision.\n */\nexport function parseAllowlist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): AllowlistEntry[] {\n const entries: AllowlistEntry[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n // Skip blank lines and comments\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // Split on whitespace — first token is term, optional second is glob\n const parts = trimmed.split(/\\s+/);\n\n if (parts.length === 0 || parts[0] === '') {\n stderrFn(`scaffold-lint: warning: malformed line ${i + 1} in ${filePath}: ${raw}`);\n continue;\n }\n\n const entry: AllowlistEntry = { term: parts[0].toLowerCase() };\n if (parts.length >= 2) {\n entry.glob = parts[1];\n }\n entries.push(entry);\n }\n\n return entries;\n}\n\n/**\n * Parse `.cleargate/scaffold-blocklist.txt` (user-extensible).\n * Fail-fast on malformed lines (exit code 2).\n * Returns null if a parse error was encountered (caller should exit 2).\n */\nexport function parseUserBlocklist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): string[] | null {\n const terms: string[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // A valid term is a single non-whitespace token\n if (/\\s/.test(trimmed)) {\n stderrFn(`scaffold-lint: malformed line ${i + 1} in ${filePath}: ${raw}`);\n return null; // signal exit 2\n }\n\n terms.push(trimmed.toLowerCase());\n }\n\n return terms;\n}\n","/**\n * `cleargate join` — two-step identity-bound workspace invitation redemption.\n *\n * Flow:\n * 1. Parse invite URL/token.\n * 2. Pick provider (--auth flag, interactive picker, or error in non-interactive mode).\n * 3. POST /join/:token/challenge { provider } → get challengeId + clientHints.\n * 4. Drive provider flow:\n * - GitHub: client-side device-flow polling (via identity-flow.startDeviceFlow).\n * - Email: OTP prompt with 3-in-process retries (OD-3 resolution).\n * 5. POST /join/:token/complete { challenge_id, proof } → get refresh_token.\n * 6. Seat token via TokenStore.\n *\n * CR-006 EPIC-019.\n */\nimport * as os from 'node:os';\nimport { loadConfig } from '../config.js';\nimport { createTokenStore } from '../auth/factory.js';\nimport {\n pickProvider,\n startDeviceFlow,\n mapProviderError,\n DeviceFlowError,\n IdentityFlowError,\n type Provider,\n} from '../auth/identity-flow.js';\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\nconst UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// GitHub device-flow token endpoint (same as admin-login)\nconst GITHUB_DEVICE_FLOW_URL = 'https://github.com/login/oauth/access_token';\n\nexport interface JoinOptions {\n inviteUrl: string;\n profile: string;\n mcpUrlFlag?: string;\n /** Identity provider (--auth flag): 'github' | 'email'. */\n auth?: string;\n /** Non-interactive CI mode (--non-interactive flag). */\n nonInteractive?: boolean;\n /** Pre-seeded OTP code for non-interactive email auth (--code flag). */\n code?: string;\n /** Test seam: replaces globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n /** Test seam: replaces createTokenStore */\n createStore?: typeof createTokenStore;\n /** Test seam: replaces os.hostname() */\n hostname?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: replaces process.stdin for interactive prompts */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: whether stdin is a TTY (default: process.stdin.isTTY) */\n isTTY?: boolean;\n /** Test seam: sleepFn for device-flow polling (default: real setTimeout) */\n sleepFn?: (ms: number) => Promise<void>;\n /** Test seam: override device-flow poll interval in ms (0 = instant in tests) */\n intervalOverrideMs?: number;\n}\n\nexport async function joinHandler(opts: JoinOptions): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const hostname = opts.hostname ?? (() => os.hostname());\n const isTTY = opts.isTTY ?? (process.stdin.isTTY === true);\n\n // ── Parse inviteUrl → { token, baseUrl } ──────────────────────────────────\n let token: string;\n let baseUrl: string;\n\n try {\n if (UUID_V4_RE.test(opts.inviteUrl)) {\n // Bare UUID form — requires mcpUrl from config\n token = opts.inviteUrl;\n const cfg = loadConfig({\n flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },\n });\n if (!cfg.mcpUrl) {\n stderr(\n 'cleargate: bare invite token requires mcpUrl. Pass --mcp-url <url> or set CLEARGATE_MCP_URL.\\n',\n );\n exit(5);\n return;\n }\n baseUrl = cfg.mcpUrl;\n } else {\n // Full URL form: https://host/join/<uuid>\n const url = new URL(opts.inviteUrl);\n const m = url.pathname.match(/^\\/join\\/([0-9a-f-]{36})$/i);\n if (!m || !UUID_V4_RE.test(m[1]!)) {\n throw new Error('bad path');\n }\n token = m[1]!;\n baseUrl = url.origin;\n }\n } catch {\n stderr('cleargate: invalid invite URL or token format.\\n');\n exit(5);\n return;\n }\n\n // ── Non-interactive guards ────────────────────────────────────────────────\n if (opts.nonInteractive && !opts.auth) {\n stderr('cleargate: --auth required in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'email' && !opts.code) {\n stderr('cleargate: --code required for email provider in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'github') {\n stderr('cleargate: GitHub auth requires browser interaction; use `--auth email` for non-interactive flows\\n');\n exit(1);\n return;\n }\n\n // ── Pick provider ─────────────────────────────────────────────────────────\n let provider: Provider;\n try {\n provider = await pickProvider({\n flag: opts.auth,\n isTTY: !opts.nonInteractive && isTTY,\n available: ['github', 'email'],\n stdin: opts.stdin,\n stdout,\n });\n } catch (err) {\n if (err instanceof IdentityFlowError) {\n stderr(`${err.message}\\n`);\n exit(1);\n return;\n }\n throw err;\n }\n\n // ── POST /challenge ────────────────────────────────────────────────────────\n let challengeRes: Response;\n try {\n challengeRes = await fetchFn(`${baseUrl}/join/${token}/challenge`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ provider }),\n });\n } catch (err) {\n stderr(\n `cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`,\n );\n exit(2);\n return;\n }\n\n if (!challengeRes.ok) {\n const body = (await challengeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n challengeRes.status,\n body.error ?? '',\n parseRetryAfter(challengeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n let challengeBody: {\n challenge_id: string;\n provider: Provider;\n expires_in: number;\n client_hints: Record<string, unknown>;\n };\n try {\n challengeBody = (await challengeRes.json()) as typeof challengeBody;\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n\n const challengeId = challengeBody.challenge_id;\n const clientHints = challengeBody.client_hints;\n\n // ── Drive provider-specific completion + POST /complete ────────────────────\n let completeRawBody: unknown;\n\n if (provider === 'github') {\n // ── GitHub device-flow: poll GitHub client-side, then POST /complete ──────\n const deviceCode = clientHints['device_code'] as string;\n const userCode = clientHints['user_code'] as string;\n const verificationUri = clientHints['verification_uri'] as string;\n const expiresIn = typeof clientHints['expires_in'] === 'number' ? clientHints['expires_in'] as number : 900;\n const interval = typeof clientHints['interval'] === 'number' ? clientHints['interval'] as number : 5;\n\n stdout(`Open the following URL in your browser and enter the code:\\n`);\n stdout(` URL: ${verificationUri}\\n`);\n stdout(` Code: ${userCode}\\n`);\n stdout(` (Code expires in ${Math.floor(expiresIn / 60)} minutes)\\n`);\n stdout('Waiting for authorization...\\n');\n\n let accessToken: string;\n try {\n const result = await startDeviceFlow({\n deviceCode,\n interval,\n expiresIn,\n fetchPoll: async (dc) => {\n const res = await fetchFn(GITHUB_DEVICE_FLOW_URL, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: dc,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n });\n return {\n status: res.status,\n json: () => res.json() as Promise<unknown>,\n };\n },\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n });\n accessToken = result.accessToken;\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: access denied — you declined authorization in the browser.\\n');\n exit(5);\n return;\n case 'expired_token':\n stderr('cleargate: device code expired — please re-run `cleargate join <url>`.\\n');\n exit(5);\n return;\n case 'unreachable':\n stderr('cleargate: cannot reach GitHub. Check your connection and retry.\\n');\n exit(2);\n return;\n default:\n stderr(`cleargate: GitHub device flow error: ${err.code}\\n`);\n exit(6);\n return;\n }\n }\n stderr('cleargate: unexpected error during GitHub device flow\\n');\n exit(6);\n return;\n }\n\n // POST /complete with access_token proof (OD-2 fix: server member-mode accepts access_token)\n // FLASHCARD 2026-04-18 #cli #plaintext-redact: named field access only, never log accessToken\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { access_token: accessToken } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // ── Email magic-link: prompt for OTP with 3 retries (OD-3) ──────────────\n const sentTo = typeof clientHints['sent_to'] === 'string' ? clientHints['sent_to'] as string : '(unknown)';\n const maxRetries = 3;\n\n stdout(`We sent a 6-digit code to ${sentTo}.\\n`);\n\n // CI mode: use pre-seeded code directly\n if (opts.code !== undefined) {\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: opts.code } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // Interactive: prompt up to maxRetries times\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Queue-based line reading (pattern from identity-flow.ts)\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) return Promise.resolve(lineQueue.shift()!);\n if (rlClosed) return Promise.resolve('');\n return new Promise<string>((resolve) => { lineWaiters.push(resolve); });\n }\n\n let succeeded = false;\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n stdout('Enter code: ');\n const otpCode = (await readNextLine()).trim();\n\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: otpCode } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (completeRes.ok) {\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n succeeded = true;\n break;\n }\n\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const errorCode = body.error ?? '';\n\n // Terminal errors (don't retry)\n if (completeRes.status === 410 || errorCode === 'challenge_expired') {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n if (completeRes.status === 403 || (completeRes.status >= 400 && completeRes.status < 500 && errorCode !== 'provider_error')) {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n // Retryable (502 provider_error = wrong code)\n if (attempt < maxRetries) {\n stderr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n if (!succeeded) {\n stderr(`cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.\\n`);\n exit(12);\n return;\n }\n }\n }\n\n // ── Seat the refresh token ─────────────────────────────────────────────────\n const b = completeRawBody as {\n refresh_token?: unknown;\n project_name?: unknown;\n member_role?: unknown;\n };\n\n if (typeof b.refresh_token !== 'string' || typeof b.project_name !== 'string') {\n stderr('cleargate: server returned unexpected response shape.\\n');\n exit(7);\n return;\n }\n\n // Named field access — b.refresh_token is a bare string, never logged\n const refreshToken: string = b.refresh_token;\n const projectName: string = b.project_name;\n\n try {\n const store = await (opts.createStore ?? createTokenStore)();\n await store.save(opts.profile, refreshToken);\n\n // ── Success output ─────────────────────────────────────────────────────\n stdout(`joined project '${projectName}' as '${hostname()}'\\n`);\n stdout(`refresh token saved to ${store.backend}.\\n`);\n } catch (err) {\n stderr(\n `cleargate: internal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n exit(99);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const hdr = res.headers?.get?.('retry-after');\n if (!hdr) return undefined;\n const n = parseInt(hdr, 10);\n return isNaN(n) ? undefined : n;\n}\n","/**\n * Shared identity-flow helpers for `cleargate join` and `cleargate admin login`.\n *\n * Exposes:\n * - pickProvider — flag > required_provider pin > interactive picker > error\n * - promptPicker — numbered list via node:readline (no new dep)\n * - startDeviceFlow — GitHub device-flow poll loop (admin-login + member join)\n * - promptEmailOTP — 6-digit OTP prompt with up to 3 retries (OD-3)\n * - mapProviderError — HTTP-status + error-code → { message, exitCode, retryable }\n * - IdentityFlowError, DeviceFlowError typed error classes\n *\n * Test seams: functions that read from stdin or prompt accept\n * `{ stdin: NodeJS.ReadableStream, stdout: (s: string) => void }` as an\n * options object so tests can inject a pre-seeded Readable and capture output\n * without real TTY interaction.\n *\n * No new npm dependencies — uses only node:readline (built-in).\n *\n * CR-006 EPIC-019.\n */\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error classes\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class IdentityFlowError extends Error {\n constructor(\n public readonly code: string,\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'IdentityFlowError';\n }\n}\n\nexport class DeviceFlowError extends Error {\n constructor(\n public readonly code:\n | 'access_denied'\n | 'expired_token'\n | 'not_admin'\n | 'timeout'\n | 'unreachable'\n | 'server_error',\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'DeviceFlowError';\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type Provider = 'github' | 'email';\n\nexport interface PickProviderOptions {\n /** Explicit --auth flag value (wins over everything). */\n flag?: string;\n /** Is the process running in a real TTY? */\n isTTY?: boolean;\n /** Available providers (default: ['github', 'email']). */\n available?: Provider[];\n /** Stdin stream for interactive picker (test seam). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function for interactive picker (test seam). */\n stdout?: (s: string) => void;\n}\n\nexport interface StartDeviceFlowOptions {\n /** The device_code from the challenge client_hints. */\n deviceCode: string;\n /** Poll interval in seconds (from client_hints). */\n interval: number;\n /** How long until the device code expires in seconds. */\n expiresIn: number;\n /**\n * Function to POST to the poll endpoint.\n * Returns a Response-like object with status + json().\n */\n fetchPoll: (deviceCode: string) => Promise<{ status: number; json: () => Promise<unknown> }>;\n /**\n * Sleep implementation — injected in tests to avoid real waits.\n * Defaults to real setTimeout.\n */\n sleepFn?: (ms: number) => Promise<void>;\n /**\n * Override the base interval in milliseconds (used in tests — set to 0 to skip waits).\n * When provided, overrides `interval` from the server. Bump logic still applies if sleepFn is also provided.\n */\n intervalOverrideMs?: number;\n /**\n * Grace period beyond expiresIn before declaring timeout.\n * Default: 10_000ms (10 seconds for round-trip latency).\n */\n deadlineGraceMs?: number;\n}\n\nexport interface StartDeviceFlowResult {\n /** GitHub access_token returned by device-flow polling. */\n accessToken: string;\n}\n\nexport interface PromptEmailOTPOptions {\n /** Masked email shown to user, e.g. \"a***@example.com\". */\n sentTo: string;\n /** Pre-seeded code (CI / --code flag). If provided, skips prompt entirely. */\n code?: string;\n /** Number of in-process retries. Default: 3. */\n maxRetries?: number;\n /**\n * Function that performs one POST /complete attempt with the given OTP code.\n * Returns an object indicating success or a known error code.\n */\n attemptComplete: (code: string) => Promise<{ success: boolean; errorCode?: string }>;\n /** Stdin stream (test seam — default: process.stdin). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function (test seam). */\n stdout?: (s: string) => void;\n /** Stderr function (test seam). */\n stderr?: (s: string) => void;\n}\n\nexport interface MapProviderErrorResult {\n message: string;\n exitCode: number;\n retryable: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// pickProvider\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolves which provider to use for a join flow.\n *\n * Priority: flag > interactive picker > error (non-TTY)\n * Throws IdentityFlowError('provider_required') when non-interactive and no flag.\n * Throws IdentityFlowError('provider_unknown') when flag names an unrecognised provider.\n */\nexport async function pickProvider(opts: PickProviderOptions): Promise<Provider> {\n const available: Provider[] = opts.available ?? ['github', 'email'];\n\n if (opts.flag !== undefined) {\n const flagLower = opts.flag.toLowerCase() as Provider;\n if (!available.includes(flagLower)) {\n throw new IdentityFlowError(\n 'provider_unknown',\n `cleargate: unknown provider '${opts.flag}'. Available: ${available.join(', ')}`,\n );\n }\n return flagLower;\n }\n\n // No flag — need TTY for interactive picker\n if (!opts.isTTY) {\n throw new IdentityFlowError(\n 'provider_required',\n 'cleargate: --auth required in non-interactive mode',\n );\n }\n\n // Auto-select if only one provider\n if (available.length === 1) {\n return available[0]!;\n }\n\n return promptPicker(available, opts);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptPicker\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PROVIDER_LABELS: Record<Provider, string> = {\n github: 'GitHub OAuth',\n email: 'Email magic-link',\n};\n\n/**\n * Interactive numbered-list picker using node:readline.\n * Renders \"How would you like to verify your email?\" + numbered list.\n */\nexport async function promptPicker(\n options: Provider[],\n { stdin, stdout }: { stdin?: NodeJS.ReadableStream; stdout?: (s: string) => void } = {},\n): Promise<Provider> {\n const write = stdout ?? ((s: string) => process.stdout.write(s));\n\n write('How would you like to verify your email?\\n');\n options.forEach((p, i) => {\n write(` ${i + 1}. ${PROVIDER_LABELS[p]}\\n`);\n });\n write(`Choice [1-${options.length}]: `);\n\n const inputStream = (stdin as Readable | undefined) ?? process.stdin;\n\n return new Promise<Provider>((resolve, reject) => {\n let settled = false;\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n rl.once('line', (line) => {\n settled = true;\n rl.close();\n const idx = parseInt(line.trim(), 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n reject(\n new IdentityFlowError(\n 'invalid_choice',\n `cleargate: invalid choice '${line.trim()}'. Enter a number between 1 and ${options.length}.`,\n ),\n );\n return;\n }\n resolve(options[idx]!);\n });\n\n rl.once('error', (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n rl.once('close', () => {\n // Only reject if we haven't already resolved/rejected from the line event\n if (!settled) {\n settled = true;\n reject(new IdentityFlowError('provider_required', 'cleargate: no provider selected'));\n }\n });\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// startDeviceFlow\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Polls fetchPoll until the device is authorized or an error terminal state is reached.\n *\n * Used for both admin-login (polls /admin-api/v1/auth/device/poll) and\n * member-join GitHub flow (polls https://github.com/login/oauth/access_token).\n *\n * Returns { accessToken } on success.\n * Throws DeviceFlowError on terminal failure.\n */\nexport async function startDeviceFlow(opts: StartDeviceFlowOptions): Promise<StartDeviceFlowResult> {\n const sleepFn = opts.sleepFn ?? defaultSleep;\n let currentIntervalMs =\n opts.intervalOverrideMs !== undefined\n ? opts.intervalOverrideMs\n : Math.max(opts.interval, 5) * 1000;\n\n const expiresAtMs = Date.now() + opts.expiresIn * 1000;\n const deadline = expiresAtMs + (opts.deadlineGraceMs ?? 10_000);\n\n while (Date.now() < deadline) {\n await sleepFn(currentIntervalMs);\n\n let pollRes: { status: number; json: () => Promise<unknown> };\n try {\n pollRes = await opts.fetchPoll(opts.deviceCode);\n } catch {\n throw new DeviceFlowError('unreachable');\n }\n\n if (pollRes.status === 403) {\n let body: Record<string, unknown> = {};\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n // ignore parse failure\n }\n if (body['error'] === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n // not_admin\n throw new DeviceFlowError('not_admin');\n }\n\n if (pollRes.status === 410) {\n throw new DeviceFlowError('expired_token');\n }\n\n if (!pollRes.status || pollRes.status < 200 || pollRes.status >= 300) {\n if (pollRes.status >= 500 || pollRes.status < 100) {\n throw new DeviceFlowError('server_error');\n }\n }\n\n let body: Record<string, unknown>;\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n throw new DeviceFlowError('server_error');\n }\n\n // Handle GitHub's device-flow pending responses\n const errorField = body['error'];\n if (typeof errorField === 'string') {\n if (errorField === 'authorization_pending') {\n // keep polling\n continue;\n }\n if (errorField === 'slow_down') {\n // bump interval if retry_after is larger\n const retryAfter = body['interval'];\n if (typeof retryAfter === 'number') {\n const bumped = retryAfter * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n } else {\n // default slow_down bump: +5s\n currentIntervalMs += 5_000;\n }\n continue;\n }\n if (errorField === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n if (errorField === 'expired_token') {\n throw new DeviceFlowError('expired_token');\n }\n // unknown error — treat as server error\n throw new DeviceFlowError('server_error');\n }\n\n // Handle MCP server's pending response shape { pending: true, retry_after? }\n if (body['pending'] === true) {\n const shouldApplyBump =\n opts.sleepFn !== undefined || opts.intervalOverrideMs === undefined;\n if (shouldApplyBump && typeof body['retry_after'] === 'number') {\n const bumped = (body['retry_after'] as number) * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n }\n continue;\n }\n\n // Success — extract access_token (GitHub device-flow: access_token field)\n if (typeof body['access_token'] === 'string') {\n return { accessToken: body['access_token'] as string };\n }\n\n // MCP server success shape (admin_token for admin-login)\n if (typeof body['admin_token'] === 'string') {\n return { accessToken: body['admin_token'] as string };\n }\n\n throw new DeviceFlowError('server_error');\n }\n\n throw new DeviceFlowError('timeout');\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptEmailOTP\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prompts user for a 6-digit OTP code and attempts /complete up to maxRetries (default: 3) times.\n *\n * OD-3 resolution: 3 in-process retries for interactive UX; same challenge_id is reused each time.\n * Server MagicLinkProvider treats each /complete as independent — 502 on first attempt\n * does NOT burn the challenge (verified mcp/src/routes/join.ts:244-255).\n *\n * If `code` is provided (CI / --code flag), it is used directly without any prompt or retry.\n *\n * Returns the final code string on success.\n * Throws IdentityFlowError('otp_max_retries') after maxRetries failures.\n * Throws IdentityFlowError('otp_expired') on 410 challenge_expired.\n */\nexport async function promptEmailOTP(opts: PromptEmailOTPOptions): Promise<string> {\n const write = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const writeErr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const maxRetries = opts.maxRetries ?? 3;\n\n write(`We sent a 6-digit code to ${opts.sentTo}.\\n`);\n\n // CI mode: use provided code directly (no prompt, no retry)\n if (opts.code !== undefined) {\n const result = await opts.attemptComplete(opts.code);\n if (result.success) {\n return opts.code;\n }\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n throw new IdentityFlowError(\n 'otp_failed',\n `cleargate: code didn't match. Re-run \\`cleargate join <url>\\` to try again`,\n );\n }\n\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n // Create a single readline interface that persists across all retry attempts.\n // Each readLineFromRl() call reads exactly one line from the interface.\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Collect queued lines from the rl interface\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n // Drain any pending waiters with empty string (stream ended)\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) {\n return Promise.resolve(lineQueue.shift()!);\n }\n if (rlClosed) {\n return Promise.resolve('');\n }\n return new Promise<string>((resolve) => {\n lineWaiters.push(resolve);\n });\n }\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n write('Enter code: ');\n const code = (await readNextLine()).trim();\n\n const result = await opts.attemptComplete(code);\n\n if (result.success) {\n return code;\n }\n\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n\n if (attempt < maxRetries) {\n writeErr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n throw new IdentityFlowError(\n 'otp_max_retries',\n `cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.`,\n );\n}\n\n// readLine is no longer used (replaced by shared rl in promptEmailOTP)\n// Kept as a dead-code stub for any future one-shot callers.\n\n// ─────────────────────────────────────────────────────────────────────────────\n// mapProviderError\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Maps server HTTP status + error code pairs into user-readable messages and exit codes.\n *\n * Error table per M3 §2:\n * 400 invalid_request → exit 7\n * 400 provider_not_allowed → exit 9\n * 400 provider_unknown → exit 9\n * 400 identity_proof_required → exit 11 (stale CLI upgrade hint)\n * 403 email_mismatch → exit 10\n * 404 not_found → exit 4\n * 410 invite_expired → exit 3\n * 410 invite_already_consumed → exit 3\n * 410 challenge_expired → exit 3\n * 429 → exit 8\n * 502 provider_error → exit 12\n * >=500 (other) → exit 6\n */\nexport function mapProviderError(\n httpStatus: number,\n errorCode: string,\n retryAfterSeconds?: number,\n): MapProviderErrorResult {\n if (httpStatus === 400) {\n switch (errorCode) {\n case 'provider_not_allowed':\n return {\n message:\n 'cleargate: this invite requires a different provider — re-run with `--auth <pinned>`',\n exitCode: 9,\n retryable: true,\n };\n case 'provider_unknown':\n return {\n message:\n 'cleargate: server does not have that provider registered — contact the project admin',\n exitCode: 9,\n retryable: false,\n };\n case 'identity_proof_required':\n return {\n message: 'cleargate: this CLI is out of date — please upgrade and retry (`npm i -g cleargate@latest`)',\n exitCode: 11,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invalid request to server (please file a bug)',\n exitCode: 7,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 403 && errorCode === 'email_mismatch') {\n return {\n message:\n 'cleargate: verified email does not match the invitee — ask your admin to re-issue the invite',\n exitCode: 10,\n retryable: false,\n };\n }\n\n if (httpStatus === 404) {\n return {\n message: 'cleargate: invite not found',\n exitCode: 4,\n retryable: false,\n };\n }\n\n if (httpStatus === 410) {\n switch (errorCode) {\n case 'invite_expired':\n return {\n message: 'cleargate: invite expired. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'invite_already_consumed':\n return {\n message: 'cleargate: invite already consumed. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'challenge_expired':\n return {\n message: 'cleargate: code expired. Re-run `cleargate join <url>` to start over',\n exitCode: 3,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invite no longer valid. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 429) {\n const retryHint = retryAfterSeconds !== undefined ? `${retryAfterSeconds}` : '900';\n return {\n message: `cleargate: too many requests. Retry after ${retryHint}s`,\n exitCode: 8,\n retryable: true,\n };\n }\n\n if (httpStatus === 502 && errorCode === 'provider_error') {\n return {\n message: \"cleargate: code didn't match. Try again, or restart with `cleargate join <url>`\",\n exitCode: 12,\n retryable: true,\n };\n }\n\n if (httpStatus >= 500) {\n return {\n message: `cleargate: server error ${httpStatus}`,\n exitCode: 6,\n retryable: false,\n };\n }\n\n return {\n message: `cleargate: unexpected error ${httpStatus} ${errorCode}`,\n exitCode: 7,\n retryable: false,\n };\n}\n","/**\n * stamp.ts — `cleargate stamp <file>` command handler\n *\n * Wraps M1 helpers: getCodebaseVersion + stampFrontmatter.\n * Do NOT hand-roll YAML or shell git — use the M1 helpers.\n *\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #commander #optional-key: conditionally assign `now`/`version` opts.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { getCodebaseVersion, type CodebaseVersion } from '../lib/codebase-version.js';\nimport { stampFrontmatter, type StampOptions } from '../lib/stamp-frontmatter.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\n\nexport interface StampCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: inject a fixed CodebaseVersion instead of calling getCodebaseVersion. */\n getVersion?: () => CodebaseVersion;\n}\n\n/**\n * Build a unified-diff-style preview of the frontmatter changes.\n * Prints context lines (unchanged) with a leading space,\n * removed lines with `-`, added lines with `+`.\n */\nfunction buildDiffPreview(\n filePath: string,\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const lines: string[] = [`--- ${filePath}`, `+++ ${filePath} (after stamp)`];\n\n // Use insertion order of keys — include both before and after\n const seenKeys = new Set<string>();\n for (const key of [...Object.keys(before), ...Object.keys(after)]) {\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n const bVal = before[key];\n const aVal = after[key];\n if (bVal === aVal) {\n lines.push(` ${key}: ${String(bVal)}`);\n } else {\n if (key in before) {\n lines.push(`-${key}: ${String(bVal)}`);\n }\n if (key in after) {\n lines.push(`+${key}: ${String(aVal)}`);\n }\n }\n }\n return lines.join('\\n');\n}\n\nexport async function stampHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Verify file exists before calling helpers\n if (!fs.existsSync(absPath)) {\n process.stderr.write(`[cleargate stamp] error: file not found: ${absPath}\\n`);\n return exitFn(1);\n }\n\n const version = cli?.getVersion ? cli.getVersion() : getCodebaseVersion({ cwd });\n\n if (opts.dryRun) {\n // For dry-run: operate on a temp file copy so the real file is never written.\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-stamp-dry-'));\n try {\n const tmpFile = path.join(tmpDir, path.basename(absPath));\n fs.copyFileSync(absPath, tmpFile);\n\n // Read current frontmatter for the diff's before-side\n let before: Record<string, unknown> = {};\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n if (raw.trimStart().startsWith('---')) {\n ({ fm: before } = parseFrontmatter(raw));\n }\n } catch {\n // before stays empty if parse fails\n }\n\n // Build stamp options — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(tmpFile, stampOpts);\n\n const diff = buildDiffPreview(file, before, result.frontmatterAfter);\n stdoutFn(diff);\n if (result.reason === 'noop-archive' || result.reason === 'noop-unchanged') {\n stdoutFn(`[dry-run] no changes (${result.reason})`);\n }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n return;\n }\n\n // Real stamp — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(absPath, stampOpts);\n stdoutFn(`[stamped] ${file} (${result.reason})`);\n}\n","import { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface CodebaseVersion {\n sha: string | null;\n dirty: boolean;\n tag: string | null;\n package_version: string | null;\n version_string: string; // e.g. \"a3f2e91\", \"a3f2e91-dirty\", \"1.4.2\", \"unknown\"\n}\n\nexport type ExecFn = (cmd: string, args: string[]) => { stdout: string; code: number };\n\nexport interface CodebaseVersionOpts {\n cwd?: string;\n exec?: ExecFn;\n}\n\nfunction makeDefaultExec(cwd: string): ExecFn {\n return (cmd: string, args: string[]): { stdout: string; code: number } => {\n try {\n const stdout = execSync([cmd, ...args].join(' '), {\n cwd,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return { stdout: stdout.trim(), code: 0 };\n } catch (err: unknown) {\n const e = err as { stdout?: string | Buffer; status?: number };\n return {\n stdout: typeof e.stdout === 'string' ? e.stdout.trim() : '',\n code: typeof e.status === 'number' ? e.status : 1,\n };\n }\n };\n}\n\nfunction findPackageJson(startDir: string): string | null {\n let current = startDir;\n while (true) {\n const candidate = path.join(current, 'package.json');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n return null;\n }\n current = parent;\n }\n}\n\nexport function getCodebaseVersion(opts?: CodebaseVersionOpts): CodebaseVersion {\n const cwd = opts?.cwd ?? process.cwd();\n const execFn = opts?.exec ?? makeDefaultExec(cwd);\n\n // Try git SHA\n const shaResult = execFn('git', ['rev-parse', '--short', 'HEAD']);\n if (shaResult.code === 0 && shaResult.stdout.length > 0) {\n const sha = shaResult.stdout.trim();\n\n // Check dirty\n const statusResult = execFn('git', ['status', '--porcelain']);\n const dirty = statusResult.code === 0 && statusResult.stdout.trim().length > 0;\n\n const version_string = dirty ? `${sha}-dirty` : sha;\n\n return {\n sha,\n dirty,\n tag: null,\n package_version: null,\n version_string,\n };\n }\n\n // Fallback: find nearest package.json\n const pkgPath = findPackageJson(cwd);\n if (pkgPath !== null) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: string };\n const package_version = typeof pkg.version === 'string' ? pkg.version : null;\n if (package_version !== null) {\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version,\n version_string: package_version,\n };\n }\n } catch {\n // fall through to unknown\n }\n }\n\n // Unknown\n console.warn('[cleargate] codebase-version: could not determine version (no git, no package.json)');\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version: null,\n version_string: 'unknown',\n };\n}\n","import * as fs from 'fs/promises';\nimport type { CodebaseVersion } from './codebase-version.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface StampOptions {\n now?: () => Date;\n version?: CodebaseVersion;\n /** Default: /\\/\\.cleargate\\/delivery\\/archive\\// */\n archivePathMatcher?: (absPath: string) => boolean;\n}\n\nexport interface StampResult {\n changed: boolean;\n frontmatterBefore: Record<string, unknown>;\n frontmatterAfter: Record<string, unknown>;\n reason: 'created' | 'updated' | 'noop-archive' | 'noop-unchanged';\n}\n\n/** Write-template marker keys (epic/story/bug/CR/proposal). */\nconst WRITE_TEMPLATE_KEYS = new Set([\n 'story_id',\n 'epic_id',\n 'proposal_id',\n 'cr_id',\n 'bug_id',\n]);\n\nconst DEFAULT_ARCHIVE_MATCHER = (absPath: string): boolean =>\n /\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath);\n\nexport async function stampFrontmatter(absPath: string, opts?: StampOptions): Promise<StampResult> {\n const isArchive = (opts?.archivePathMatcher ?? DEFAULT_ARCHIVE_MATCHER)(absPath);\n if (isArchive) {\n // Read to get frontmatter for result shape, but do not write\n const raw = await fs.readFile(absPath, 'utf8');\n let fm: Record<string, unknown> = {};\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n // If parse fails, return empty frontmatter snapshot\n }\n return {\n changed: false,\n frontmatterBefore: fm,\n frontmatterAfter: fm,\n reason: 'noop-archive',\n };\n }\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n // Determine if the file has frontmatter at all\n const hasFrontmatter = raw.trimStart().startsWith('---');\n\n let fm: Record<string, unknown> = {};\n let body = raw;\n\n if (hasFrontmatter) {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n }\n\n const frontmatterBefore = { ...fm };\n\n const nowFn = opts?.now ?? (() => new Date());\n const now = nowFn();\n const nowIso = toIsoSecond(now);\n\n const version = opts?.version ?? { sha: null, dirty: false, tag: null, package_version: null, version_string: 'unknown' };\n const versionString = version.version_string;\n\n // Determine if this is a first stamp or re-stamp\n const hasCreatedAt = 'created_at' in fm && fm['created_at'] !== undefined && fm['created_at'] !== '' && fm['created_at'] !== null;\n\n // Determine if it's a write-template (needs server_pushed_at_version)\n const isWriteTemplate = WRITE_TEMPLATE_KEYS.has(Object.keys(fm).find((k) => WRITE_TEMPLATE_KEYS.has(k)) ?? '');\n\n // Build the new frontmatter:\n // 1. Preserve existing key order\n // 2. Append new keys in canonical order: created_at, updated_at, created_at_version, updated_at_version, server_pushed_at_version\n const newFm: Record<string, unknown> = {};\n\n // Copy all existing keys first (preserves order)\n for (const [k, v] of Object.entries(fm)) {\n newFm[k] = v;\n }\n\n if (!hasCreatedAt) {\n // First stamp: set all 4 fields\n // If the keys exist already (placeholder values), update them in-place order\n // If not, append at the end in canonical order\n newFm['created_at'] = nowIso;\n newFm['updated_at'] = nowIso;\n newFm['created_at_version'] = versionString;\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n } else {\n // Re-stamp: preserve created_at + created_at_version, advance updated_at + updated_at_version\n // created_at stays\n newFm['updated_at'] = nowIso;\n // created_at_version stays\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n }\n\n // Check noop-unchanged: if nothing changed, return early\n const unchanged =\n newFm['updated_at'] === fm['updated_at'] &&\n newFm['updated_at_version'] === fm['updated_at_version'] &&\n newFm['created_at'] === fm['created_at'] &&\n newFm['created_at_version'] === fm['created_at_version'];\n\n if (unchanged && hasCreatedAt) {\n return {\n changed: false,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: 'noop-unchanged',\n };\n }\n\n // Serialize and write\n const fmBlock = serializeFrontmatter(newFm);\n // Reconstruct: frontmatter block + newline + body\n // body from parseFrontmatter does NOT have a leading blank line (it strips one)\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n\n return {\n changed: true,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: hasCreatedAt ? 'updated' : 'created',\n };\n}\n","/**\n * YAML frontmatter parser backed by js-yaml with CORE_SCHEMA (YAML 1.2 core).\n *\n * Parses `---\\n<yaml>\\n---\\n<body>` into a typed frontmatter map + body string.\n * Preserves native types (null, boolean, number, string), nested maps, and\n * arrays. Uses CORE_SCHEMA so ISO-8601 timestamp strings are NOT coerced to\n * Date objects (YAML 1.1's quirk).\n *\n * Historical note: an earlier hand-rolled parser flattened indented nested\n * maps into top-level keys and stringified null/boolean scalars. See\n * BUG-001 and FLASHCARD entry `#yaml #frontmatter`.\n */\n\nimport yaml from 'js-yaml';\n\nexport function parseFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') {\n throw new Error('parseFrontmatter: input does not start with ---');\n }\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) {\n throw new Error('parseFrontmatter: missing closing ---');\n }\n\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n // strip one leading blank line if present\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n\n if (yamlText.trim() === '') {\n return { fm: {}, body };\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`parseFrontmatter: invalid YAML: ${(err as Error).message}`);\n }\n\n if (parsed === null || parsed === undefined) {\n return { fm: {}, body };\n }\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('parseFrontmatter: frontmatter is not a YAML mapping');\n }\n\n return { fm: parsed as Record<string, unknown>, body };\n}\n","/**\n * Frontmatter YAML serializer backed by js-yaml with CORE_SCHEMA.\n *\n * Emits a `---\\n<yaml>\\n---` block. Preserves key insertion order (JS\n * objects iterate non-numeric keys in insertion order by spec, and js-yaml\n * follows Object.keys), nested maps, arrays, and native scalar types.\n *\n * Partner of parse-frontmatter.ts — the two round-trip losslessly.\n */\n\nimport yaml from 'js-yaml';\n\n/**\n * Serialize a frontmatter record to a YAML block (including the --- delimiters).\n * Key order is preserved as supplied.\n */\nexport function serializeFrontmatter(fm: Record<string, unknown>): string {\n // Empty object → emit a two-delimiter block with no body\n if (Object.keys(fm).length === 0) {\n return '---\\n---';\n }\n\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n\n // js-yaml always ends with \\n; trim so we control the framing\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---`;\n}\n\n/**\n * Format a Date as ISO 8601 UTC with second precision: \"YYYY-MM-DDTHH:MM:SSZ\"\n */\nexport function toIsoSecond(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n","/**\n * init.ts — `cleargate init` command handler\n *\n * Steps:\n * 1. Validate cwd exists and is writable\n * 2. Resolve payloadDir (bundled cleargate-planning/ templates)\n * 3. copyPayload: copy scaffold files to target cwd\n * 4. mergeSettings: merge PostToolUse hook into .claude/settings.json\n * 5. injectClaudeMd: bounded-block inject into CLAUDE.md\n * 6. Bootstrap pass: if delivery/ has items, run wiki build\n * 7. Print Done\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawnSync } from 'node:child_process';\nimport { copyPayload } from '../init/copy-payload.js';\nimport { mergeSettings, type SettingsJson } from '../init/merge-settings.js';\nimport { injectClaudeMd, extractBlock } from '../init/inject-claude-md.js';\nimport { wikiBuildHandler, type WikiBuildOptions } from './wiki-build.js';\nimport { loadPackageManifest, type ManifestFile } from '../lib/manifest.js';\nimport { promptYesNo as defaultPromptYesNo, promptEmail as defaultPromptEmail } from '../lib/prompts.js';\nimport { resolveIdentity, readParticipant, writeParticipant, type ResolveIdentityOpts } from '../lib/identity.js';\n\n/**\n * The PostToolUse hook config to merge — updated in STORY-008-06 to use\n * stamp-and-gate.sh (replaces legacy SPRINT-04 inline wiki ingest command).\n * Uses ${CLAUDE_PROJECT_DIR} so the path is project-relative at runtime.\n * mergeSettings deduplicates by exact command string — safe to re-run.\n */\nconst HOOK_ADDITION: SettingsJson = {\n hooks: {\n PreToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-edit-gate.sh',\n },\n ],\n },\n ],\n PostToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/stamp-and-gate.sh',\n },\n ],\n },\n ],\n },\n};\n\nexport interface InitOptions {\n /** Target working directory (the repo being initialised). Default: process.cwd() */\n cwd?: string;\n /** Overwrite files that differ from payload. Default: false */\n force?: boolean;\n /** Accept all defaults non-interactively (same as stdin not a TTY). */\n yes?: boolean;\n /** Test seam: path to bundled cleargate-planning/ payload directory */\n payloadDir?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: runs wiki build (default: wikiBuildHandler) */\n runWikiBuild?: (opts: WikiBuildOptions) => Promise<void>;\n /**\n * Test seam: replaces the real Y/n prompt for restore flow.\n * STORY-009-03: injectable so integration tests don't block on stdin.\n */\n promptYesNo?: (question: string, defaultYes: boolean) => Promise<boolean>;\n /**\n * Test seam: replaces loadPackageManifest() call for snapshot step.\n * STORY-009-03: allows tests to supply a known ManifestFile without a real MANIFEST.json.\n */\n readInstallManifest?: () => ManifestFile;\n /**\n * Test seam: replaces the email prompt for participant identity flow.\n * STORY-010-01: injectable so tests don't block on stdin.\n */\n promptEmail?: (question: string, defaultValue: string) => Promise<string>;\n /**\n * Test seam: identity resolver options (gitEmail, hostname, username, env overrides).\n * STORY-010-01: used to inject a deterministic git email in tests.\n */\n identityOpts?: ResolveIdentityOpts;\n /**\n * Test seam: override process.stdin.isTTY for participant prompt decision.\n * STORY-010-01: in test environment stdin is not a TTY; inject true to force interactive path.\n */\n stdinIsTTY?: boolean;\n /**\n * CR-009: pin version to stamp into hook scripts. Overrides the default of\n * reading the version from `cleargate-cli/package.json`. Supports\n * `cleargate init --pin 0.6.0-beta` and test seam injection.\n */\n pin?: string;\n /**\n * Test seam: replaces child_process.spawnSync for the resolver probe (Step 7.6).\n * Injected in tests to avoid real npx invocations.\n */\n spawnSyncFn?: typeof spawnSync;\n}\n\n/** Shape of the .cleargate/.uninstalled marker written by STORY-009-07 `uninstall`. */\ninterface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n}\n\n/** Resolve default payloadDir from the installed package structure.\n *\n * tsup bundles all modules into dist/cli.js (single entry point).\n * As a result, import.meta.url inside ANY module resolves to dist/cli.js.\n * So dirname(import.meta.url) = dist/. One level up = package root.\n * See flashcard: #tsup #bundle #import-meta.\n */\nexport function resolveDefaultPayloadDir(): string {\n const thisFile = fileURLToPath(import.meta.url);\n // dist/cli.js → dirname = dist/ → one level up = package root\n const pkgRoot = path.resolve(path.dirname(thisFile), '..');\n return path.join(pkgRoot, 'templates', 'cleargate-planning');\n}\n\n/** Glob delivery dir for .md files, excluding .gitkeep */\nfunction countDeliveryItems(cwd: string): number {\n const pendingSync = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(cwd, '.cleargate', 'delivery', 'archive');\n let count = 0;\n for (const dir of [pendingSync, archive]) {\n if (!fs.existsSync(dir)) continue;\n const entries = fs.readdirSync(dir);\n for (const f of entries) {\n if (f.endsWith('.md') && f !== '.gitkeep') count++;\n }\n }\n return count;\n}\n\n/** Write file atomically: write to tmp, then rename. */\nfunction writeAtomic(filePath: string, content: string): void {\n const tmpPath = filePath + '.tmp.' + Date.now();\n fs.writeFileSync(tmpPath, content, 'utf8');\n fs.renameSync(tmpPath, filePath);\n}\n\n/**\n * CR-009: Read the version from a package.json file at `packageJsonPath`.\n * Returns null when the file is absent or malformed.\n */\nfunction readPackageVersion(packageJsonPath: string): string | null {\n try {\n const raw = fs.readFileSync(packageJsonPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: unknown };\n if (typeof pkg.version === 'string' && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nexport async function initHandler(opts: InitOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const force = opts.force ?? false;\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const runWikiBuild = opts.runWikiBuild ?? wikiBuildHandler;\n const promptYesNoFn = opts.promptYesNo ?? defaultPromptYesNo;\n const promptEmailFn = opts.promptEmail ?? defaultPromptEmail;\n const spawnSyncFn = opts.spawnSyncFn ?? spawnSync;\n\n // Step 1: Validate cwd\n if (!fs.existsSync(cwd)) {\n stderr(`[cleargate init] ERROR: target directory does not exist: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n // Check writable by attempting to create a tmp file\n const testWritePath = path.join(cwd, `.cleargate-init-write-test-${Date.now()}`);\n try {\n fs.writeFileSync(testWritePath, '');\n fs.unlinkSync(testWritePath);\n } catch {\n stderr(`[cleargate init] ERROR: target directory is not writable: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n stdout(`[cleargate init] Target: ${cwd}\\n`);\n\n // Step 2: Resolve payloadDir\n const payloadDir = opts.payloadDir ?? resolveDefaultPayloadDir();\n\n if (!fs.existsSync(payloadDir)) {\n stderr(`[cleargate init] ERROR: payload directory not found: ${payloadDir}\\n`);\n stderr(`[cleargate init] Run \\`npm run prebuild\\` to copy the payload first.\\n`);\n exit(1);\n return;\n }\n\n // Step 3: Copy scaffold payload\n // Step 3.5 (pre-copy): Detect .uninstalled marker and prompt restore.\n // Must run before any writes so the user sees the restore prompt first.\n const uninstalledMarkerPath = path.join(cwd, '.cleargate', '.uninstalled');\n let uninstalledMarker: UninstalledMarker | null = null;\n let userChoseRestore = false;\n\n if (fs.existsSync(uninstalledMarkerPath)) {\n try {\n const raw = fs.readFileSync(uninstalledMarkerPath, 'utf8');\n uninstalledMarker = JSON.parse(raw) as UninstalledMarker;\n } catch {\n stderr(`[cleargate init] WARNING: .uninstalled marker is malformed; ignoring it.\\n`);\n }\n\n if (uninstalledMarker !== null) {\n const { uninstalled_at, prior_version, preserved } = uninstalledMarker;\n const question =\n `[cleargate init] Detected previous ClearGate install` +\n ` (uninstalled ${uninstalled_at}, prior version ${prior_version}).` +\n ` Restore preserved items? [Y/n]`;\n userChoseRestore = await promptYesNoFn(question, true);\n\n if (userChoseRestore) {\n // Blind copy: just verify each preserved file still exists on disk\n // (it was preserved as intended). Log status; never touch content.\n for (const preservedPath of preserved) {\n const absPreserved = path.isAbsolute(preservedPath)\n ? preservedPath\n : path.join(cwd, preservedPath);\n if (fs.existsSync(absPreserved)) {\n stdout(`[cleargate init] [preserved] ${preservedPath}\\n`);\n } else {\n stdout(`[cleargate init] [warn] preserved path missing on disk: ${preservedPath}\\n`);\n }\n }\n } else {\n stdout(\n `[cleargate init] discarding preservation; preserved files untouched on disk\\n`,\n );\n }\n // Marker removal happens AFTER bootstrap (Step 6) completes — tracked below.\n }\n }\n\n // CR-009: Resolve pin version for hook script substitution.\n // Priority: explicit --pin flag → package.json next to payloadDir → package.json next to dist → fallback 'latest'\n let pinVersion: string | undefined;\n if (opts.pin) {\n pinVersion = opts.pin;\n } else {\n // payloadDir is `.../templates/cleargate-planning`; package.json is at `.../package.json`\n const payloadParent = path.resolve(payloadDir, '..', '..');\n pinVersion =\n readPackageVersion(path.join(payloadParent, 'package.json')) ??\n readPackageVersion(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json')) ??\n 'latest';\n }\n\n const copyReport = copyPayload(payloadDir, cwd, { force, pinVersion });\n for (const action of copyReport.actions) {\n const verb =\n action.action === 'created'\n ? 'Created'\n : action.action === 'overwritten'\n ? 'Overwritten'\n : 'Skipped (exists)';\n stdout(`[cleargate init] ${verb} ${action.relPath}\\n`);\n }\n\n // Step 4: Merge PostToolUse hook into .claude/settings.json\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n let existingSettings: SettingsJson | null = null;\n if (fs.existsSync(settingsPath)) {\n try {\n existingSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as SettingsJson;\n } catch {\n stderr(`[cleargate init] WARNING: could not parse ${settingsPath}; treating as empty.\\n`);\n }\n }\n\n const mergedSettings = mergeSettings(existingSettings, HOOK_ADDITION);\n fs.mkdirSync(path.dirname(settingsPath), { recursive: true });\n writeAtomic(settingsPath, JSON.stringify(mergedSettings, null, 2) + '\\n');\n stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook\\n`);\n\n // Step 5: Inject bounded block into CLAUDE.md\n const claudeMdPath = path.join(cwd, 'CLAUDE.md');\n const claudeMdSrcPath = path.join(payloadDir, 'CLAUDE.md');\n\n let claudeMdBlock: string;\n try {\n const claudeMdSrc = fs.readFileSync(claudeMdSrcPath, 'utf8');\n claudeMdBlock = extractBlock(claudeMdSrc);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not read CLAUDE.md block from payload: ${String(e)}\\n`);\n claudeMdBlock = '<!-- CLEARGATE:START -->\\n<!-- CLEARGATE:END -->';\n }\n\n const existingClaudeMd = fs.existsSync(claudeMdPath)\n ? fs.readFileSync(claudeMdPath, 'utf8')\n : null;\n\n const newClaudeMd = injectClaudeMd(existingClaudeMd, claudeMdBlock);\n writeAtomic(claudeMdPath, newClaudeMd);\n\n if (existingClaudeMd === null) {\n stdout(`[cleargate init] Created CLAUDE.md (with bounded block)\\n`);\n } else if (existingClaudeMd !== newClaudeMd) {\n stdout(`[cleargate init] Updated CLAUDE.md (bounded block injected/replaced)\\n`);\n } else {\n stdout(`[cleargate init] CLAUDE.md unchanged (block already up to date)\\n`);\n }\n\n // Step 6: Bootstrap pass\n const itemCount = countDeliveryItems(cwd);\n if (itemCount > 0) {\n stdout(`[cleargate init] Bootstrap: running wiki build (${itemCount} items found)...\\n`);\n await runWikiBuild({ cwd, now });\n stdout(`[cleargate init] Bootstrap: ran wiki build (${itemCount} items ingested)\\n`);\n } else {\n stdout(`[cleargate init] Bootstrap: no items to ingest, skipping build\\n`);\n }\n\n // Step 7: Write install snapshot atomically to .cleargate/.install-manifest.json.\n // Must be the FINAL step after all scaffold files are written (blueprint §1.2).\n const cleargateDir = path.join(cwd, '.cleargate');\n fs.mkdirSync(cleargateDir, { recursive: true });\n\n const snapshotPath = path.join(cleargateDir, '.install-manifest.json');\n try {\n const readManifest = opts.readInstallManifest ?? (() => loadPackageManifest({ packageRoot: payloadDir }));\n const pkgManifest = readManifest();\n const snapshot: ManifestFile = {\n ...pkgManifest,\n installed_at: now(),\n };\n writeAtomic(snapshotPath, JSON.stringify(snapshot, null, 2) + '\\n');\n stdout(`[cleargate init] Wrote install snapshot: .cleargate/.install-manifest.json\\n`);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not write install snapshot: ${String(e)}\\n`);\n }\n\n // Remove .uninstalled marker AFTER bootstrap + snapshot complete (whether user chose Y or N).\n // This prevents repeated prompting on subsequent init runs.\n if (uninstalledMarker !== null && fs.existsSync(uninstalledMarkerPath)) {\n try {\n fs.unlinkSync(uninstalledMarkerPath);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not remove .uninstalled marker: ${String(e)}\\n`);\n }\n }\n\n // Step 7.6 (CR-009): Resolver probe — run the three-branch resolver and print\n // a visible green/red status line. Converts \"invisible silent no-op at first\n // hook fire\" into \"loud failure at install time, when the user is watching\".\n {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n type ResolverBranch = { cmd: string; args: string[] } | null;\n\n // Mirror the bash resolver order: dist first (dogfood), then PATH, then npx.\n let branch: ResolverBranch = null;\n let branchLabel = '';\n\n if (fs.existsSync(distCliPath)) {\n branch = { cmd: 'node', args: [distCliPath, '--version'] };\n branchLabel = `local dist (${distCliPath})`;\n } else {\n // Try `cleargate --version` via PATH\n const whichResult = spawnSyncFn('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n branch = { cmd: 'cleargate', args: ['--version'] };\n branchLabel = 'PATH (global install)';\n } else {\n // Fall back to npx invocation\n branch = { cmd: 'npx', args: ['-y', `cleargate@${pinVersion}`, '--version'] };\n branchLabel = `npx cleargate@${pinVersion} (cold-start ~600ms first call)`;\n }\n }\n\n if (branch !== null) {\n const probeResult = spawnSyncFn(branch.cmd, branch.args, {\n encoding: 'utf8',\n timeout: 15000,\n });\n\n if (probeResult.status === 0) {\n stdout(`[cleargate init] \\u{1F7E2} cleargate CLI resolved via ${branchLabel}\\n`);\n } else {\n // Resolver chain exhausted — the hooks will no-op until cleargate is reachable.\n // Per BUG-015 (2026-04-27): convert from exit(1) to warn-not-block. The probe is a\n // best-effort signal; transient registry issues (CI race conditions, network blips)\n // shouldn't hard-fail init. Hooks will surface their own resolver-failure banners\n // at runtime if the issue persists. User can run `cleargate doctor` to investigate.\n stdout(\n `[cleargate init] \\u{1F7E1} cleargate CLI: not resolvable in this environment.\\n` +\n `[cleargate init] Attempted: ${branchLabel}\\n` +\n `[cleargate init] This is a warning, not a fatal error. Hooks will no-op until resolved.\\n` +\n `[cleargate init] Fix: npm i -g cleargate@${pinVersion} or npx cleargate@${pinVersion} doctor\\n`,\n );\n // Continue init. The resolver-status was emitted; hooks will surface their own\n // failure banners at runtime if needed.\n }\n }\n }\n\n // Step 7.5: Participant identity\n // Skip if .cleargate/.participant.json already exists (idempotent re-init).\n const existingParticipant = readParticipant(cwd);\n if (existingParticipant === null) {\n // Resolve git email as default (no env / host fallback during init — init needs a concrete prompt).\n const identityOpts = opts.identityOpts ?? {};\n\n // Resolve just the git rung: call resolveIdentity with env={} to skip env rung,\n // then check the source.\n const gitOnlyIdentity = resolveIdentity(cwd, {\n ...identityOpts,\n env: {}, // force skip env rung\n });\n const gitEmail =\n gitOnlyIdentity.source === 'git' ? gitOnlyIdentity.email : null;\n\n const stdinIsTTY = opts.stdinIsTTY ?? process.stdin.isTTY ?? false;\n const isNonInteractive = opts.yes === true || !stdinIsTTY;\n\n if (isNonInteractive) {\n // Non-interactive: use git email; if unavailable use host fallback via resolveIdentity\n const finalEmail =\n gitEmail ??\n resolveIdentity(cwd, identityOpts).email;\n\n await writeParticipant(cwd, finalEmail, 'inferred', now);\n stdout(`[cleargate init] Participant identity: ${finalEmail} (inferred)\\n`);\n } else {\n // Interactive: prompt for email.\n // BUG-007: the prior prompt reused the `[cleargate init]` info-log prefix\n // and was followed by a newline, so it visually merged with preceding log\n // lines and the cursor sat on a blank line below — users walked away\n // believing install had finished. Fix:\n // 1. Drop the `[cleargate init]` prefix on the prompt itself so it\n // reads as an interactive question, not a status message.\n // 2. Print a blank separator line above so the eye catches the change.\n // 3. Reject GitHub `users.noreply.github.com` git emails as a default —\n // they're unrouteable identities; users who blindly press Enter end\n // up with a participant identity that can't receive invites.\n const isNoreply = gitEmail !== null && /@users\\.noreply\\.github\\.com$/i.test(gitEmail);\n const defaultEmail = (gitEmail !== null && !isNoreply) ? gitEmail : 'user@localhost';\n stdout('\\n');\n const question = `Participant email (press Enter for default) [${defaultEmail}]:`;\n const answer = await promptEmailFn(question, defaultEmail);\n await writeParticipant(cwd, answer, 'prompted', now);\n stdout(`[cleargate init] Participant identity: ${answer} (prompted)\\n`);\n }\n }\n\n // Step 8: Done\n stdout(\n `[cleargate init] Done. Read CLAUDE.md and .cleargate/knowledge/cleargate-protocol.md to learn the protocol.\\n`,\n );\n\n void now; // suppress unused warning if not used after this\n}\n","/**\n * copy-payload.ts — recursively copy cleargate-planning/ payload to target cwd.\n *\n * Handles overwrite policy:\n * - by default: skip files that already exist with identical content\n * - force=true: overwrite all\n *\n * Does NOT prompt interactively; skip-or-overwrite is determined by `force`.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface CopyReport {\n created: number;\n skipped: number;\n overwritten: number;\n /** Per-file action lines for verbose printing */\n actions: Array<{ action: 'created' | 'skipped' | 'overwritten'; relPath: string }>;\n}\n\nexport interface CopyPayloadOptions {\n force: boolean;\n /**\n * CR-009: When set, substitute `__CLEARGATE_VERSION__` placeholder in hook scripts\n * with this version string. Applies to `.claude/hooks/stamp-and-gate.sh` and\n * `.claude/hooks/session-start.sh`. Use a sed-friendly format: `0.5.0`.\n */\n pinVersion?: string;\n}\n\n/** Hook files that carry the `__CLEARGATE_VERSION__` placeholder (CR-009). */\nconst PIN_PLACEHOLDER = '__CLEARGATE_VERSION__';\nconst HOOK_FILES_WITH_PIN = new Set([\n '.claude/hooks/stamp-and-gate.sh',\n '.claude/hooks/session-start.sh',\n]);\n\n/**\n * Recursively enumerate files under `dir`.\n * Returns paths relative to `dir`.\n */\nfunction listFilesRecursive(dir: string): string[] {\n const results: string[] = [];\n function walk(current: string, rel: string): void {\n const entries = fs.readdirSync(current, { withFileTypes: true });\n for (const entry of entries) {\n const entryRel = rel ? `${rel}/${entry.name}` : entry.name;\n const entryAbs = path.join(current, entry.name);\n if (entry.isDirectory()) {\n walk(entryAbs, entryRel);\n } else {\n results.push(entryRel);\n }\n }\n }\n walk(dir, '');\n return results;\n}\n\n/**\n * Copy all files from `payloadDir` to `targetCwd`, preserving directory structure.\n * Dotfiles and dot-directories (e.g. `.claude/`, `.cleargate/`) are preserved.\n */\nexport function copyPayload(\n payloadDir: string,\n targetCwd: string,\n opts: CopyPayloadOptions,\n): CopyReport {\n const report: CopyReport = { created: 0, skipped: 0, overwritten: 0, actions: [] };\n\n if (!fs.existsSync(payloadDir)) {\n throw new Error(`copyPayload: payloadDir does not exist: ${payloadDir}`);\n }\n\n const files = listFilesRecursive(payloadDir);\n\n for (const relPath of files) {\n const srcPath = path.join(payloadDir, relPath);\n const dstPath = path.join(targetCwd, relPath);\n\n // Ensure target directory exists\n fs.mkdirSync(path.dirname(dstPath), { recursive: true });\n\n let srcContent: Buffer | string = fs.readFileSync(srcPath);\n\n // CR-009: substitute __CLEARGATE_VERSION__ placeholder in hook scripts\n if (opts.pinVersion && HOOK_FILES_WITH_PIN.has(relPath)) {\n const text = srcContent.toString('utf8').replaceAll(PIN_PLACEHOLDER, opts.pinVersion);\n srcContent = text;\n }\n\n // Compare: convert srcContent to Buffer for comparison when it's a string\n const srcBuffer = typeof srcContent === 'string' ? Buffer.from(srcContent, 'utf8') : srcContent;\n\n if (fs.existsSync(dstPath)) {\n const dstContent = fs.readFileSync(dstPath);\n if (srcBuffer.equals(dstContent)) {\n // Identical — skip silently even with force (idempotent)\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n if (!opts.force) {\n // Different content, no force — skip\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n // Different + force — overwrite\n fs.writeFileSync(dstPath, srcBuffer);\n report.overwritten++;\n report.actions.push({ action: 'overwritten', relPath });\n } else {\n // New file\n fs.writeFileSync(dstPath, srcBuffer);\n report.created++;\n report.actions.push({ action: 'created', relPath });\n }\n }\n\n return report;\n}\n","/**\n * merge-settings.ts — JSON merge for .claude/settings.json\n *\n * Algorithm (from M4 blueprint):\n * - if existing is null: return addition\n * - otherwise deep-clone existing and merge addition.hooks into result.hooks\n * - For each event (e.g. PostToolUse):\n * - find matching entry by `matcher` field\n * - if absent: push entire new entry\n * - if present: de-dup merge inner hooks[] by exact `command` string match\n * - Preserves all other top-level keys (SubagentStop, permissions, etc.)\n */\n\nexport interface HookCommand {\n type: string;\n command: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n [key: string]: unknown;\n}\n\nexport interface HooksConfig {\n [event: string]: HookEntry[];\n}\n\nexport interface SettingsJson {\n hooks?: HooksConfig;\n [key: string]: unknown;\n}\n\n/**\n * Deep-clone a plain JSON-serializable object.\n */\nfunction deepClone<T>(obj: T): T {\n return JSON.parse(JSON.stringify(obj)) as T;\n}\n\n/**\n * Merge `addition` hook config into `existing` settings.\n * Returns a new merged object; does not mutate either argument.\n *\n * @param existing - parsed .claude/settings.json content, or null if file absent\n * @param addition - the hook config to merge in (must have `hooks` key)\n */\nexport function mergeSettings(\n existing: SettingsJson | null,\n addition: SettingsJson,\n): SettingsJson {\n if (existing === null) {\n return deepClone(addition);\n }\n\n const result: SettingsJson = deepClone(existing);\n\n // Ensure result.hooks exists\n if (!result.hooks) {\n result.hooks = {};\n }\n\n // Merge each event from addition\n for (const [eventName, eventArray] of Object.entries(addition.hooks ?? {})) {\n if (!result.hooks[eventName]) {\n result.hooks[eventName] = [];\n }\n\n for (const newEntry of eventArray) {\n const matchingIdx = result.hooks[eventName].findIndex(\n (e) => e.matcher === newEntry.matcher,\n );\n\n if (matchingIdx === -1) {\n // No matching entry — push entire new entry\n result.hooks[eventName].push(deepClone(newEntry));\n } else {\n // Matcher exists — merge inner hooks[] by de-dup on `command`\n const existingEntry = result.hooks[eventName][matchingIdx];\n const existingInner: HookCommand[] = Array.isArray(existingEntry.hooks)\n ? (existingEntry.hooks as HookCommand[])\n : [];\n\n for (const newInner of newEntry.hooks ?? []) {\n if (!existingInner.some((h) => h.command === newInner.command)) {\n existingInner.push(deepClone(newInner) as HookCommand);\n }\n }\n\n result.hooks[eventName][matchingIdx] = {\n ...existingEntry,\n hooks: existingInner,\n };\n }\n }\n }\n\n return result;\n}\n","/**\n * inject-claude-md.ts — bounded-block injection for CLAUDE.md\n *\n * Block format: <!-- CLEARGATE:START -->\\n<content>\\n<!-- CLEARGATE:END -->\n * Detection regex: /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/ (greedy, see below)\n *\n * Rules:\n * - If existing === null: create file with block as full content (+ trailing newline)\n * - If existing matches: replace the bounded block in place (idempotent)\n * - If existing no match: append block with 2 leading newlines (preserve user content above)\n */\n\n// Greedy match: from first <!-- CLEARGATE:START --> to LAST <!-- CLEARGATE:END -->.\n// Greedy is correct here because:\n// (a) cleargate-planning/CLAUDE.md body text mentions both markers inline as code references,\n// and non-greedy would stop at the inline END before the real one.\n// (b) We assume at most one cleargate block per file (idempotency requires it).\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/;\n\n/**\n * Extract the bounded block from a source file (e.g. cleargate-planning/CLAUDE.md).\n * Returns the text from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive.\n * Throws if the markers are not found.\n */\nexport function extractBlock(sourceContent: string): string {\n const match = BLOCK_REGEX.exec(sourceContent);\n if (!match) {\n throw new Error('inject-claude-md: CLEARGATE:START/END markers not found in source content');\n }\n return match[0];\n}\n\n/**\n * Inject or update the bounded block in an existing CLAUDE.md.\n *\n * @param existing - current content of CLAUDE.md, or null if file doesn't exist\n * @param block - the full block to inject, from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive\n * @returns - new file content (ready to write)\n */\nexport function injectClaudeMd(existing: string | null, block: string): string {\n if (existing === null) {\n // Create new file with block as full content\n return block + '\\n';\n }\n\n if (BLOCK_REGEX.test(existing)) {\n // Replace existing block in place\n return existing.replace(BLOCK_REGEX, block);\n }\n\n // Append block with 2 leading newlines\n return existing.trimEnd() + '\\n\\n' + block + '\\n';\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\n\nexport interface WikiBuildOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp for last_ingest field (defaults to new Date().toISOString()) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\n/** Terminal statuses — items with these statuses go to the Archive section. */\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n/**\n * Rollup threshold: if an active epic has >= ROLLUP_THRESHOLD active stories,\n * collapse them into a single summary line instead of individual rows.\n */\nconst ROLLUP_THRESHOLD = 3;\n\n/**\n * Active index bucket order: epics → sprints → proposals → crs → bugs → orphan stories.\n * Topics always skipped (written by query --persist only).\n */\nconst ACTIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\nexport async function wikiBuildHandler(opts: WikiBuildOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const templateDir = opts.templateDir;\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`wiki build: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n // Ensure wiki directory structure exists\n for (const bucket of BUCKET_ORDER) {\n fs.mkdirSync(path.join(wikiRoot, bucket), { recursive: true });\n }\n\n // Step 2: scan raw items\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Step 3: write per-item wiki pages\n const timestamp = now();\n let pagesWritten = 0;\n\n for (const item of items) {\n const sha = getGitSha(item.rawPath, gitRunner) ?? '';\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n\n const wikiPage: WikiPage = {\n type: item.type,\n id: item.id,\n parent,\n children,\n status: String(item.fm['status'] ?? ''),\n remote_id: String(item.fm['remote_id'] ?? ''),\n raw_path: item.rawPath,\n last_ingest: timestamp,\n last_ingest_commit: sha,\n repo: item.repo,\n };\n\n const body = buildPageBody(item, wikiPage);\n const content = serializePage(wikiPage, body);\n\n const pageDir = path.join(wikiRoot, item.bucket);\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(path.join(pageDir, `${item.id}.md`), content, 'utf8');\n pagesWritten++;\n }\n\n // Step 4: build index.md\n const indexContent = buildIndex(items);\n fs.writeFileSync(path.join(wikiRoot, 'index.md'), indexContent, 'utf8');\n\n // Step 5: build log.md\n const logContent = buildLog(items, timestamp);\n fs.writeFileSync(path.join(wikiRoot, 'log.md'), logContent, 'utf8');\n\n // Step 6: write synthesis pages\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n\n // Step 7: report\n stdout(`wiki build: OK (${pagesWritten} pages written)\\n`);\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: RawItem, page: WikiPage): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.').slice(0, 200);\n\n const blastParts: string[] = [];\n if (page.parent) blastParts.push(page.parent);\n for (const child of page.children) blastParts.push(child);\n\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction buildIndex(items: RawItem[]): string {\n const header = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n ];\n\n // Empty-delivery case\n if (items.length === 0) {\n return [\n ...header,\n '## Active',\n '',\n '_No active items._',\n '',\n '## Archive',\n '',\n '_No archived items._',\n '',\n ].join('\\n');\n }\n\n // 1. Partition items into active and archived\n const active: RawItem[] = [];\n const archived: RawItem[] = [];\n for (const item of items) {\n const status = String(item.fm['status'] ?? '');\n if (TERMINAL_STATUSES.has(status)) {\n archived.push(item);\n } else {\n active.push(item);\n }\n }\n\n // 2. Build storiesByEpic: active stories grouped by parent epic id.\n // Only include stories whose parent epic is itself active.\n const activeEpicIds = new Set(\n active.filter((i) => i.bucket === 'epics').map((i) => i.id),\n );\n const storiesByEpic = new Map<string, RawItem[]>();\n\n for (const item of active) {\n if (item.bucket !== 'stories') continue;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n // Strip [[ ]] wrappers if present\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n if (epicId && activeEpicIds.has(epicId)) {\n const list = storiesByEpic.get(epicId) ?? [];\n list.push(item);\n storiesByEpic.set(epicId, list);\n }\n }\n\n // Orphan active stories: no parent_epic_ref OR parent epic not active\n const orphanStories = active.filter((item) => {\n if (item.bucket !== 'stories') return false;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n return !epicId || !activeEpicIds.has(epicId);\n });\n\n // 3. Emit ## Active section\n const activeLines: string[] = ['## Active', ''];\n\n for (const bucket of ACTIVE_BUCKET_ORDER) {\n if (bucket === 'stories') {\n // Orphan stories only\n const sorted = orphanStories.slice().sort((a, b) => a.id.localeCompare(b.id));\n for (const item of sorted) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n } else if (bucket === 'epics') {\n const epicItems = active\n .filter((i) => i.bucket === 'epics')\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const epic of epicItems) {\n const status = String(epic.fm['status'] ?? '');\n activeLines.push(`- [[${epic.id}]] (${epic.type}) — ${status}`);\n\n // 4. Emit story rollup or individual rows under the epic\n const epicStories = (storiesByEpic.get(epic.id) ?? [])\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n if (epicStories.length >= ROLLUP_THRESHOLD) {\n // Build status breakdown: count per status, sort by count desc then status asc\n const counts = new Map<string, number>();\n for (const s of epicStories) {\n const st = String(s.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n const breakdown = [...counts.entries()]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n // Extract epic numeric prefix for rollup label (e.g. EPIC-014 → 014)\n const epicNum = epic.id.replace(/^EPIC-/, '');\n activeLines.push(` - STORY-${epicNum}-xx (${epicStories.length} stories) — ${breakdown}`);\n } else {\n for (const story of epicStories) {\n const st = String(story.fm['status'] ?? '');\n activeLines.push(` - [[${story.id}]] (${story.type}) — ${st}`);\n }\n }\n }\n } else {\n const bucketItems = active\n .filter((i) => i.bucket === bucket)\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const item of bucketItems) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n }\n }\n\n activeLines.push('');\n\n // 5. Emit ## Archive section — per-bucket summary only\n const archiveLines: string[] = ['## Archive', ''];\n // Bucket order for archive summary (excluding topics and stories — stories\n // don't get their own archive summary line per plan)\n const ARCHIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\n for (const bucket of ARCHIVE_BUCKET_ORDER) {\n const bucketArchived = archived.filter((i) => i.bucket === bucket);\n if (bucketArchived.length === 0) continue;\n\n // Count per status\n const counts = new Map<string, number>();\n for (const item of bucketArchived) {\n const st = String(item.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n // Sort by count desc then status asc, omit zero-count statuses\n const parts = [...counts.entries()]\n .filter(([, n]) => n > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n\n const label = BUCKET_LABELS[bucket] ?? bucket;\n archiveLines.push(`- ${label}: ${parts} · [expand](archive/${bucket}.md)`);\n }\n\n archiveLines.push('');\n\n return [...header, ...activeLines, ...archiveLines].join('\\n');\n}\n\nfunction buildLog(items: RawItem[], timestamp: string): string {\n if (items.length === 0) {\n return '# Wiki Event Log\\n\\n';\n }\n\n const entries = items.map((item) =>\n [\n `- timestamp: \"${timestamp}\"`,\n ` actor: \"cleargate wiki build\"`,\n ` action: \"create\"`,\n ` target: \"${item.id}\"`,\n ` path: \"${item.rawPath}\"`,\n ].join('\\n'),\n );\n\n return ['# Wiki Event Log', '', ...entries, ''].join('\\n');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { deriveBucket } from './derive-bucket.js';\nimport { deriveRepo } from './derive-repo.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface RawItem {\n /** Absolute path on disk */\n absPath: string;\n /** Path relative to repo root */\n rawPath: string;\n id: string;\n bucket: string;\n type: WikiPageType;\n repo: RepoTag;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/**\n * Scan pending-sync/ + archive/ under deliveryRoot for markdown work items.\n * Returns a sorted (by id) list of parsed raw items.\n */\nexport function scanRawItems(deliveryRoot: string, repoRoot: string): RawItem[] {\n const results: RawItem[] = [];\n\n for (const subdir of ['pending-sync', 'archive']) {\n const dir = path.join(deliveryRoot, subdir);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { recursive: true, encoding: 'utf8' }) as string[];\n for (const rel of entries) {\n if (!rel.endsWith('.md')) continue;\n if (rel.includes('~') || rel.startsWith('.')) continue;\n\n const absPath = path.join(dir, rel);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n // Compute rawPath relative to repo root\n const rawPath = path.relative(repoRoot, absPath).replace(/\\\\/g, '/');\n\n // Check exclusions\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) continue;\n\n // Derive bucket info from filename\n const filename = path.basename(absPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch {\n // Not a recognized work-item filename — skip silently\n continue;\n }\n\n // Derive repo from path\n let repo: RepoTag;\n try {\n repo = deriveRepo(rawPath);\n } catch {\n continue;\n }\n\n // Parse frontmatter\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n // Malformed frontmatter — skip\n continue;\n }\n\n results.push({\n absPath,\n rawPath,\n id: bucketInfo.id,\n bucket: bucketInfo.bucket,\n type: bucketInfo.type,\n repo,\n fm,\n body,\n });\n }\n }\n\n // Sort deterministically by id (alphanumeric ascending)\n results.sort((a, b) => a.id.localeCompare(b.id));\n return results;\n}\n","import type { WikiPageType } from './page-schema.js';\n\nexport interface BucketInfo {\n type: WikiPageType;\n id: string;\n bucket: string;\n}\n\nconst PREFIX_MAP: Array<{ prefix: string; type: WikiPageType; bucket: string }> = [\n { prefix: 'EPIC-', type: 'epic', bucket: 'epics' },\n { prefix: 'STORY-', type: 'story', bucket: 'stories' },\n { prefix: 'SPRINT-', type: 'sprint', bucket: 'sprints' },\n { prefix: 'PROPOSAL-', type: 'proposal', bucket: 'proposals' },\n { prefix: 'CR-', type: 'cr', bucket: 'crs' },\n { prefix: 'BUG-', type: 'bug', bucket: 'bugs' },\n];\n\n/**\n * Derive bucket, type, and id from a filename stem.\n * Filename `STORY-042-01_name.md` → `{ type: 'story', id: 'STORY-042-01', bucket: 'stories' }`.\n */\nexport function deriveBucket(filename: string): BucketInfo {\n // Strip path if any\n const base = filename.includes('/') ? filename.split('/').pop()! : filename;\n // Remove .md suffix\n const stem = base.endsWith('.md') ? base.slice(0, -3) : base;\n // id = everything before the first `_`\n const underscoreIdx = stem.indexOf('_');\n const id = underscoreIdx === -1 ? stem : stem.slice(0, underscoreIdx);\n\n for (const { prefix, type, bucket } of PREFIX_MAP) {\n if (id.startsWith(prefix)) {\n return { type, id, bucket };\n }\n }\n\n throw new Error(`deriveBucket: cannot determine bucket for filename: ${filename}`);\n}\n","import type { RepoTag } from './page-schema.js';\n\n/**\n * A1 helper: derive the `repo` tag from a raw file path prefix.\n * Mapping is per §10.4 field notes.\n */\nexport function deriveRepo(rawPath: string): RepoTag {\n if (rawPath.startsWith('cleargate-cli/')) return 'cli';\n if (rawPath.startsWith('mcp/')) return 'mcp';\n if (rawPath.startsWith('.cleargate/') || rawPath.startsWith('cleargate-planning/')) return 'planning';\n throw new Error(`cannot derive repo for path: ${rawPath}`);\n}\n","import { spawnSync } from 'node:child_process';\n\nexport type GitRunner = (cmd: string, args: string[]) => string;\n\n/**\n * A2 helper: return the git SHA of the last commit touching rawPath.\n * Returns null when the file is untracked (empty stdout, exit 0).\n * Accepts an optional `runner` test seam.\n */\nexport function getGitSha(rawPath: string, runner?: GitRunner): string | null {\n const run = runner ?? defaultRunner;\n const out = run('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n return out.length > 0 ? out : null;\n}\n\nfunction defaultRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n return result.stdout ?? '';\n}\n","/**\n * §10.4 Wiki Page Schema — exactly nine frontmatter fields.\n * Lint will flag any extra or missing fields.\n */\n\nexport type WikiPageType = 'epic' | 'story' | 'sprint' | 'proposal' | 'cr' | 'bug' | 'topic';\nexport type RepoTag = 'cli' | 'mcp' | 'planning';\n\n/** The nine-field frontmatter shape every wiki page must satisfy. */\nexport interface WikiPage {\n type: WikiPageType;\n id: string;\n parent: string; // \"[[EPIC-042]]\" or \"\" if none\n children: string[]; // [\"[[STORY-042-01]]\", ...]\n status: string;\n remote_id: string;\n raw_path: string;\n last_ingest: string; // ISO 8601 UTC\n last_ingest_commit: string; // git SHA or \"\"\n repo: RepoTag;\n}\n\n/** Serialise a WikiPage frontmatter + body into a markdown string. */\nexport function serializePage(page: WikiPage, body: string): string {\n const childrenYaml =\n page.children.length === 0\n ? '[]'\n : '\\n' + page.children.map((c) => ` - \"${c}\"`).join('\\n');\n\n const fm = [\n '---',\n `type: ${page.type}`,\n `id: \"${page.id}\"`,\n `parent: \"${page.parent}\"`,\n `children: ${childrenYaml}`,\n `status: \"${page.status}\"`,\n `remote_id: \"${page.remote_id}\"`,\n `raw_path: \"${page.raw_path}\"`,\n `last_ingest: \"${page.last_ingest}\"`,\n `last_ingest_commit: \"${page.last_ingest_commit}\"`,\n `repo: \"${page.repo}\"`,\n '---',\n ].join('\\n');\n\n return `${fm}\\n\\n${body}`;\n}\n\n/** Parse a serialised wiki page back into a WikiPage. Throws on schema violations. */\nexport function parsePage(raw: string): WikiPage {\n const { fm } = parseFmRaw(raw);\n\n const type = fm['type'] as WikiPageType;\n const id = String(fm['id'] ?? '');\n const parent = String(fm['parent'] ?? '');\n const rawChildren = fm['children'];\n const children: string[] = Array.isArray(rawChildren)\n ? (rawChildren as unknown[]).map(String)\n : [];\n const status = String(fm['status'] ?? '');\n const remote_id = String(fm['remote_id'] ?? '');\n const raw_path = String(fm['raw_path'] ?? '');\n const last_ingest = String(fm['last_ingest'] ?? '');\n const last_ingest_commit = String(fm['last_ingest_commit'] ?? '');\n const repo = fm['repo'] as RepoTag;\n\n return { type, id, parent, children, status, remote_id, raw_path, last_ingest, last_ingest_commit, repo };\n}\n\nfunction parseFmRaw(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') throw new Error('parsePage: missing opening ---');\n let close = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { close = i; break; }\n }\n if (close === -1) throw new Error('parsePage: missing closing ---');\n const fmLines = lines.slice(1, close);\n const body = lines.slice(close + 1).join('\\n').replace(/^\\n/, '');\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '[]') { fm[key] = []; continue; }\n if (val === '') { fm[key] = []; continue; }\n // inline list check\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1);\n fm[key] = inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return { fm, body };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the active-sprint synthesis page.\n * Loads template from templates/synthesis/active-sprint.md.\n * Partitions sprints by activated_at / completed_at frontmatter values.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'active-sprint.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n\n // Partition sprints:\n // - active: activated_at is set (non-null, non-empty) AND completed_at is not set\n // - completed: completed_at is set\n // - planned: neither activated_at nor completed_at is set\n const active = sprints.filter((s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n const completed = sprints.filter((s) => isSet(s.fm['completed_at']));\n const planned = sprints.filter((s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n\n const data: Record<string, unknown> = {\n active: active.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_active: active.length === 0 ? [{}] : [],\n planned: planned.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_planned: planned.length === 0 ? [{}] : [],\n completed: completed.slice(0, 3).map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_completed: completed.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // tsup bundles all modules into dist/cli.js.\n // When running the built bundle: import.meta.url = file://.../cleargate-cli/dist/cli.js\n // __dirname = cleargate-cli/dist/\n // ../templates/synthesis = cleargate-cli/templates/synthesis ✓ (source)\n // AND dist/templates/synthesis is also available (copied by onSuccess) ✓\n //\n // When vitest runs source (test seam): templateDir is always passed explicitly,\n // so this default is only used for the built/production case.\n //\n // Strategy: go one level up from the file containing this code (works for both\n // the dist/ bundle and npm-published dist/ layout), then into templates/synthesis.\n // For dist/cli.js: one up = package root → templates/synthesis. ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * Tiny Mustache-lite template renderer.\n * Supports:\n * {{var}} — variable substitution (missing → empty string)\n * {{#section}}...{{/section}} — array iteration; inner {{field}} resolves\n * against the current array element\n *\n * Anything more complex throws an Error.\n * No external dependencies.\n */\n\n/** Render a template string with the given data context. */\nexport function renderTemplate(template: string, data: Record<string, unknown>): string {\n // Validate: no nested sections or unsupported tags\n // We only support {{var}}, {{#section}}...{{/section}}\n const tagRe = /\\{\\{([^}]+)\\}\\}/g;\n const matches = [...template.matchAll(tagRe)].map((m) => m[1].trim());\n for (const tag of matches) {\n if (tag.startsWith('^') || tag.startsWith('>') || tag.startsWith('!') || tag.startsWith('=')) {\n throw new Error(`renderTemplate: unsupported tag type: {{${tag}}}`);\n }\n }\n\n return renderSection(template, data);\n}\n\nfunction renderSection(template: string, ctx: Record<string, unknown>): string {\n // Process {{#section}}...{{/section}} blocks first\n const sectionRe = /\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g;\n\n let result = template.replace(sectionRe, (_match, key: string, inner: string) => {\n const val = ctx[key];\n if (!Array.isArray(val)) {\n // Non-array truthy: render inner once with same ctx; falsy: skip\n if (!val) return '';\n return renderSection(inner, ctx);\n }\n if (val.length === 0) return '';\n return val\n .map((item: unknown) => {\n const itemCtx =\n item !== null && typeof item === 'object'\n ? (item as Record<string, unknown>)\n : { '.': item };\n return renderSection(inner, itemCtx);\n })\n .join('');\n });\n\n // Then substitute remaining {{var}} tokens\n result = result.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key: string) => {\n const val = ctx[key];\n if (val === undefined || val === null) return '';\n return String(val);\n });\n\n return result;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the open-gates (blocked items) synthesis page.\n * Loads template from templates/synthesis/open-gates.md.\n *\n * Three gate buckets (matching real corpus textual statuses):\n * Gate 1 — proposals with approved: false OR status: \"Draft\" / \"Approved\" (not yet shipped)\n * Gate 2 — stories with ambiguity starting with 🟡 or 🔴\n * Gate 3 — any item with status: \"Ready\" AND remote_id empty / null\n *\n * NOTE: The previous implementation filtered on status.includes('🔴') which matched\n * zero items in the real corpus (actual statuses are textual: Draft, Ready, Planned,\n * Active, Completed, Approved). This is the corpus-shape fix for STORY-002-09.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'open-gates.md'), 'utf8');\n\n // Gate 1: proposals pending approval\n const gate1 = state.filter((i) => {\n if (i.bucket !== 'proposals') return false;\n const status = String(i.fm['status'] ?? '');\n const approved = i.fm['approved'];\n // Proposals that are Draft or explicitly not approved\n return status === 'Draft' || approved === false || approved === 'false';\n });\n\n // Gate 2: stories with elevated ambiguity (🟡 Medium or 🔴 High)\n const gate2 = state.filter((i) => {\n if (i.bucket !== 'stories') return false;\n const ambiguity = String(i.fm['ambiguity'] ?? '');\n return ambiguity.startsWith('🟡') || ambiguity.startsWith('🔴');\n });\n\n // Gate 3: items that are Ready but not yet pushed (remote_id empty or null)\n const gate3 = state.filter((i) => {\n const status = String(i.fm['status'] ?? '');\n if (status !== 'Ready') return false;\n const remoteId = i.fm['remote_id'];\n return remoteId === null || remoteId === undefined || String(remoteId).trim() === '';\n });\n\n const data: Record<string, unknown> = {\n gate1: gate1.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate1: gate1.length === 0 ? [{}] : [],\n gate2: gate2.map((i) => ({ id: i.id, ambiguity: String(i.fm['ambiguity'] ?? '') })),\n no_gate2: gate2.length === 0 ? [{}] : [],\n gate3: gate3.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate3: gate3.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the product-state synthesis page.\n * Loads template from templates/synthesis/product-state.md.\n *\n * Shipped = items in archive/ subdir (rawPath contains '/archive/')\n * Active = status is Active, In Progress, or 🟢-prefixed\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'product-state.md'), 'utf8');\n\n function countBucket(bucket: string) {\n return state.filter((i) => i.bucket === bucket);\n }\n\n function isShipped(item: RawItem) {\n return item.rawPath.includes('/archive/');\n }\n\n function isActive(item: RawItem) {\n const status = String(item.fm['status'] ?? '');\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n }\n\n const buckets = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs'];\n\n function countFor(bucket: string, predicate: (i: RawItem) => boolean): number {\n return countBucket(bucket).filter(predicate).length;\n }\n\n const epics = countBucket('epics');\n const activeEpicsList = epics.filter(isActive);\n const shippedItems = state.filter(isShipped);\n\n const data: Record<string, unknown> = {\n // Totals\n total_epics: epics.length,\n total_stories: countBucket('stories').length,\n total_sprints: countBucket('sprints').length,\n total_proposals: countBucket('proposals').length,\n total_crs: countBucket('crs').length,\n total_bugs: countBucket('bugs').length,\n\n // Active counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`active_${b}`, countFor(b, isActive)])),\n\n // Shipped counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`shipped_${b}`, countFor(b, isShipped)])),\n\n // Active epics list\n active_epics_list: activeEpicsList.map((i) => ({\n id: i.id,\n status: String(i.fm['status'] ?? ''),\n })),\n no_active_epics: activeEpicsList.length === 0 ? [{}] : [],\n\n // Shipped items list\n shipped_items: shippedItems.map((i) => ({\n id: i.id,\n bucket: i.bucket,\n status: String(i.fm['status'] ?? ''),\n })),\n no_shipped: shippedItems.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the roadmap synthesis page.\n * Loads template from templates/synthesis/roadmap.md.\n *\n * Sprint buckets (by activated_at / completed_at):\n * in-flight: activated_at set AND completed_at not set\n * planned: neither set\n * shipped: completed_at set\n *\n * Epic buckets (by status):\n * active: Active / In Progress / 🟢-prefixed\n * planned: Ready / Planned / Draft\n * shipped: Completed / Approved\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'roadmap.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n const epics = state.filter((i) => i.bucket === 'epics');\n\n // Sprint partitions\n const inFlightSprints = sprints.filter(\n (s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const plannedSprints = sprints.filter(\n (s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const shippedSprints = sprints.filter((s) => isSet(s.fm['completed_at']));\n\n // Epic partitions\n const activeEpics = epics.filter((e) => isActiveStatus(String(e.fm['status'] ?? '')));\n const plannedEpics = epics.filter((e) => isPlannedStatus(String(e.fm['status'] ?? '')));\n const shippedEpics = epics.filter((e) => isShippedStatus(String(e.fm['status'] ?? '')));\n\n const data: Record<string, unknown> = {\n in_flight_sprints: inFlightSprints.map((s) => ({\n id: s.id,\n activated_at: String(s.fm['activated_at'] ?? ''),\n })),\n no_in_flight_sprints: inFlightSprints.length === 0 ? [{}] : [],\n\n planned_sprints: plannedSprints.map((s) => ({\n id: s.id,\n status: String(s.fm['status'] ?? ''),\n })),\n no_planned_sprints: plannedSprints.length === 0 ? [{}] : [],\n\n shipped_sprints: shippedSprints.map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_shipped_sprints: shippedSprints.length === 0 ? [{}] : [],\n\n active_epics: activeEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_active_epics: activeEpics.length === 0 ? [{}] : [],\n\n planned_epics: plannedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_planned_epics: plannedEpics.length === 0 ? [{}] : [],\n\n shipped_epics: shippedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_shipped_epics: shippedEpics.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction isActiveStatus(status: string): boolean {\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n}\n\nfunction isPlannedStatus(status: string): boolean {\n return status === 'Ready' || status === 'Planned' || status === 'Draft';\n}\n\nfunction isShippedStatus(status: string): boolean {\n return status === 'Completed' || status === 'Approved';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * manifest.ts — STORY-009-01\n *\n * Scaffold manifest loading, drift classification, and atomic drift-state writing.\n * Node built-ins only: fs/promises, path.\n *\n * MANIFEST.json does not exist until STORY-009-02 runs `npm run build`.\n * loadPackageManifest throws a clear error when the file is absent — not raw ENOENT.\n */\n\nimport { readFile, writeFile, rename, mkdir } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { hashNormalized } from './sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type Tier =\n | 'protocol'\n | 'template'\n | 'agent'\n | 'hook'\n | 'skill'\n | 'cli-config'\n | 'user-artifact'\n | 'derived';\n\nexport type DriftState =\n | 'clean'\n | 'user-modified'\n | 'upstream-changed'\n | 'both-changed'\n | 'untracked';\n\nexport interface ManifestEntry {\n path: string;\n sha256: string | null;\n tier: Tier;\n overwrite_policy: 'always' | 'merge-3way' | 'skip' | 'preserve' | 'pin-aware';\n preserve_on_uninstall: boolean;\n}\n\nexport interface ManifestFile {\n cleargate_version: string;\n generated_at: string;\n files: ManifestEntry[];\n /**\n * Present only in `.cleargate/.install-manifest.json` (the install snapshot).\n * Stamped by `cleargate init` as the FINAL step (STORY-009-03).\n * Not present in the package-shipped MANIFEST.json.\n */\n installed_at?: string;\n}\n\nexport interface DriftMapEntry {\n state: DriftState;\n entry: ManifestEntry;\n install_sha: string | null;\n current_sha: string | null;\n package_sha: string | null;\n}\n\nexport interface DriftMap {\n [filePath: string]: DriftMapEntry;\n}\n\n/**\n * The on-disk shape of `.cleargate/.drift-state.json`.\n * Wraps the DriftMap with a `last_refreshed` timestamp so the daily-throttle\n * logic in `cleargate doctor --check-scaffold` can skip re-computation.\n */\nexport interface DriftStateFile {\n last_refreshed: string;\n drift: DriftMap;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\nexport interface LoadPackageManifestOpts {\n /**\n * Override the root directory where MANIFEST.json is resolved.\n * Default: resolved via import.meta.url (1 level up from dist/ in prod,\n * or cleargate-planning/ in dev).\n *\n * This seam is mandatory per FLASHCARD #tsup #bundle #import-meta — the\n * bundle collapses import.meta.url to the bundle file so default resolution\n * must never be relied upon in tests.\n */\n packageRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveDefaultPackageRoot(): string {\n // In production (dist/cli.js), import.meta.url points to dist/cli.js.\n // MANIFEST.json is copied to dist/ by the build step — so 0 levels up.\n // In dev, import.meta.url is cleargate-cli/src/lib/manifest.ts — 3 levels up\n // to cleargate-cli/, then ../cleargate-planning/ for the fixture source.\n // Rather than guessing, we prefer the dist/ sibling first; fall back to the\n // source-tree dev path.\n const here = new URL('.', import.meta.url).pathname;\n\n // Try: same directory (dist/ scenario)\n const distCandidate = path.join(here, 'MANIFEST.json');\n if (existsSync(distCandidate)) {\n return here;\n }\n\n // Try: 1 level up (also dist/ scenario when emitted as dist/lib/manifest.js)\n const oneLevelUp = path.join(here, '..', 'MANIFEST.json');\n if (existsSync(oneLevelUp)) {\n return path.join(here, '..');\n }\n\n // Dev fallback: from src/lib walk up to repo root, then cleargate-planning/\n // src/lib → src → cleargate-cli → repo-root → cleargate-planning\n const devCandidate = path.join(here, '..', '..', '..', 'cleargate-planning', 'MANIFEST.json');\n if (existsSync(devCandidate)) {\n return path.join(here, '..', '..', '..', 'cleargate-planning');\n }\n\n // Cannot determine — caller will get a clear error from loadPackageManifest\n return here;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Load MANIFEST.json from the installed package root.\n *\n * Uses `opts.packageRoot` (required in tests) or default resolution.\n * Throws a descriptive error when the file is absent rather than a raw ENOENT.\n */\nexport function loadPackageManifest(opts?: LoadPackageManifestOpts): ManifestFile {\n const packageRoot = opts?.packageRoot ?? resolveDefaultPackageRoot();\n const manifestPath = path.join(packageRoot, 'MANIFEST.json');\n\n if (!existsSync(manifestPath)) {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n let raw: string;\n try {\n // Synchronous read — callers treat loadPackageManifest as synchronous (startup-time).\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n return JSON.parse(raw) as ManifestFile;\n}\n\n/**\n * Load the install-time snapshot from `<projectRoot>/.cleargate/.install-manifest.json`.\n * Returns null if the file does not exist (first install or pre-manifest era).\n */\nexport async function loadInstallSnapshot(projectRoot: string): Promise<ManifestFile | null> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n try {\n const raw = await readFile(snapshotPath, 'utf-8');\n return JSON.parse(raw) as ManifestFile;\n } catch {\n return null;\n }\n}\n\n/**\n * Compute the SHA256 of a tracked file in the current working tree.\n * Returns null when the file does not exist on disk.\n */\nexport async function computeCurrentSha(\n file: ManifestEntry,\n projectRoot: string\n): Promise<string | null> {\n const filePath = path.join(projectRoot, file.path);\n try {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Classify the drift state of a single tracked file.\n *\n * Decision table (PROP-006 §2.4):\n *\n * | Tier | Result |\n * |---------------|-------------|\n * | user-artifact | untracked |\n *\n * | pkgSha | installSha | currentSha | Result |\n * |--------|------------|------------|-------------------|\n * | any | any | null | untracked |\n * | A | A | A | clean |\n * | A | A | B (≠A) | user-modified |\n * | B (≠A) | A | A | upstream-changed |\n * | all differ pairwise | both-changed |\n */\nexport function classify(\n pkgSha: string | null,\n installSha: string | null,\n currentSha: string | null,\n tier: Tier\n): DriftState {\n // user-artifact short-circuit (EPIC-009 §6 Q8)\n if (tier === 'user-artifact') {\n return 'untracked';\n }\n\n // Missing current file\n if (currentSha === null) {\n return 'untracked';\n }\n\n const installEqualsPackage = installSha === pkgSha;\n const currentEqualsInstall = currentSha === installSha;\n\n if (installEqualsPackage && currentEqualsInstall) {\n // install == current == package\n return 'clean';\n }\n\n if (installEqualsPackage && !currentEqualsInstall) {\n // install == package, current != install => user modified\n return 'user-modified';\n }\n\n if (!installEqualsPackage && currentEqualsInstall) {\n // install == current, package != install => upstream changed\n return 'upstream-changed';\n }\n\n // All three differ pairwise\n return 'both-changed';\n}\n\n/**\n * Options for writeDriftState.\n */\nexport interface WriteDriftStateOpts {\n /**\n * ISO-8601 timestamp to record as `last_refreshed` in the output file.\n * When omitted, the current time is used (new Date().toISOString()).\n * This seam is mandatory for deterministic tests.\n */\n lastRefreshed?: string;\n}\n\n/**\n * Atomically write the drift-state map to `<projectRoot>/.cleargate/.drift-state.json`.\n *\n * The on-disk format is wrapped: `{ last_refreshed: string, drift: DriftMap }`.\n * This allows the daily-throttle logic in `cleargate doctor --check-scaffold`\n * to read the timestamp without re-computing all SHAs.\n *\n * Uses write-temp-then-rename (atomic on POSIX; best-effort on Windows).\n * Ensures parent directory exists before writing.\n *\n * STORY-009-04 extended the signature from `(projectRoot, DriftMap)` to\n * `(projectRoot, DriftMap, opts?)` — callers passing only two args continue to work.\n */\nexport async function writeDriftState(\n projectRoot: string,\n state: DriftMap,\n opts?: WriteDriftStateOpts\n): Promise<void> {\n const cleargatDir = path.join(projectRoot, '.cleargate');\n const finalPath = path.join(cleargatDir, '.drift-state.json');\n const tmpPath = `${finalPath}.tmp`;\n\n const lastRefreshed = opts?.lastRefreshed ?? new Date().toISOString();\n const fileContent: DriftStateFile = { last_refreshed: lastRefreshed, drift: state };\n\n await mkdir(cleargatDir, { recursive: true });\n await writeFile(tmpPath, JSON.stringify(fileContent, null, 2) + '\\n', 'utf-8');\n await rename(tmpPath, finalPath);\n}\n\n/**\n * Read the drift-state file written by `writeDriftState`.\n * Returns null when the file does not exist or is malformed.\n */\nexport async function readDriftState(projectRoot: string): Promise<DriftStateFile | null> {\n const driftPath = path.join(projectRoot, '.cleargate', '.drift-state.json');\n try {\n const raw = await readFile(driftPath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n // Accept both the new wrapped format {last_refreshed, drift} and the old flat format\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'last_refreshed' in parsed &&\n 'drift' in parsed\n ) {\n return parsed as DriftStateFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * sha256.ts — STORY-009-01\n *\n * Deterministic SHA256 hasher with cross-platform content normalization.\n * Node built-ins only: crypto, fs/promises, path.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Hash normalized content (string or Buffer) — 64 hex chars.\n *\n * Normalization steps applied in order:\n * 1. Convert Buffer to UTF-8 string.\n * 2. Strip leading BOM (U+FEFF).\n * 3. Normalize CRLF → LF.\n * 4. Enforce trailing newline (append `\\n` if missing).\n */\nexport function hashNormalized(content: string | Buffer): string {\n let text: string =\n Buffer.isBuffer(content) ? content.toString('utf-8') : content;\n\n // 1. Strip leading BOM\n if (text.startsWith('\\ufeff')) {\n text = text.slice(1);\n }\n\n // 2. CRLF → LF\n text = text.replace(/\\r\\n/g, '\\n');\n\n // 3. Enforce trailing newline\n if (!text.endsWith('\\n')) {\n text += '\\n';\n }\n\n return createHash('sha256').update(text, 'utf-8').digest('hex');\n}\n\n/**\n * Read a file, normalize its content, and return the SHA256 hex digest.\n */\nexport async function hashFile(filePath: string): Promise<string> {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n}\n\n/**\n * Return the first 8 hex characters of a full SHA256 digest for human-readable output.\n */\nexport function shortHash(full: string): string {\n return full.slice(0, 8);\n}\n","/**\n * prompts.ts — minimal readline-based prompt helpers\n *\n * STORY-009-03: created here (cited in story §3 as \"existing\"; verified absent).\n * STORY-010-01: added promptEmail for participant identity flow.\n * Used by init.ts restore flow and future commands that need interactive input.\n */\nimport * as readline from 'node:readline';\n\nexport interface PromptOptions {\n /** Override stdin for testing */\n stdin?: NodeJS.ReadableStream;\n /** Override stdout write for testing */\n stdout?: (s: string) => void;\n}\n\n/**\n * Prompt the user with a yes/no question.\n *\n * @param question The question text to display (without [Y/n] suffix — caller includes it)\n * @param defaultYes Whether Enter with no input means yes\n * @param opts Test seams for stdin/stdout\n * @returns true for yes, false for no\n */\nexport async function promptYesNo(\n question: string,\n defaultYes: boolean,\n opts?: PromptOptions,\n): Promise<boolean> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) keeps the cursor inline with the prompt so\n // the user's typed input is visible adjacent to the question — matches every\n // other CLI prompt convention. See BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<boolean>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim().toLowerCase();\n if (trimmed === '') {\n resolve(defaultYes);\n } else if (trimmed === 'y' || trimmed === 'yes') {\n resolve(true);\n } else {\n resolve(false);\n }\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — treat as default\n resolve(defaultYes);\n }\n });\n });\n}\n\n/**\n * Prompt the user for a text value with an optional default.\n *\n * @param question The question text to display\n * @param defaultValue The value used when the user presses Enter without typing\n * @param opts Test seams for stdin/stdout\n * @returns The entered string, or `defaultValue` if Enter is pressed with no input\n */\nexport async function promptEmail(\n question: string,\n defaultValue: string,\n opts?: PromptOptions,\n): Promise<string> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) — see promptYesNo above and BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<string>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim();\n resolve(trimmed === '' ? defaultValue : trimmed);\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — use default\n resolve(defaultValue);\n }\n });\n });\n}\n","/**\n * identity.ts — participant identity resolver.\n *\n * STORY-010-01: resolveIdentity precedence ladder + writeParticipant atomic write.\n *\n * Resolution order (highest-priority first):\n * 1. .cleargate/.participant.json\n * 2. CLEARGATE_USER env var\n * 3. git config user.email (via child_process.spawnSync — NOT simple-git; see flashcard #cli #simple-git #deps)\n * 4. \"{username}@{hostname}\" host fallback\n *\n * All external sources (env, git, host) are injectable as opts for test hermetics.\n * Do NOT reach into process.env / os.hostname() directly in the happy path.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as fsPromises from 'node:fs/promises';\nimport * as os from 'node:os';\nimport { spawnSync } from 'node:child_process';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type IdentitySource = 'participant-json' | 'env' | 'git' | 'host';\n\nexport interface Identity {\n email: string;\n source: IdentitySource;\n}\n\nexport interface ParticipantFile {\n email: string;\n set_at: string;\n source: 'prompted' | 'inferred';\n}\n\n// ── Read participant file ─────────────────────────────────────────────────────\n\n/**\n * Read .cleargate/.participant.json.\n * Returns null on ENOENT or malformed JSON — caller decides what to do.\n */\nexport function readParticipant(projectRoot: string): ParticipantFile | null {\n const filePath = path.join(projectRoot, '.cleargate', '.participant.json');\n try {\n const raw = fs.readFileSync(filePath, 'utf8');\n return JSON.parse(raw) as ParticipantFile;\n } catch {\n return null;\n }\n}\n\n// ── Write participant file ────────────────────────────────────────────────────\n\n/**\n * Write .cleargate/.participant.json atomically (tmp + rename).\n * Creates .cleargate/ directory if it does not exist.\n */\nexport async function writeParticipant(\n projectRoot: string,\n email: string,\n source: 'prompted' | 'inferred',\n now: () => string = () => new Date().toISOString(),\n): Promise<void> {\n const cleargateDir = path.join(projectRoot, '.cleargate');\n await fsPromises.mkdir(cleargateDir, { recursive: true });\n\n const filePath = path.join(cleargateDir, '.participant.json');\n const tmpPath = filePath + '.tmp.' + Date.now();\n\n const content: ParticipantFile = {\n email,\n set_at: now(),\n source,\n };\n\n await fsPromises.writeFile(tmpPath, JSON.stringify(content, null, 2) + '\\n', 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Resolve identity ──────────────────────────────────────────────────────────\n\nexport interface ResolveIdentityOpts {\n /** Override process.env for test hermetics */\n env?: NodeJS.ProcessEnv;\n /** Override git config user.email resolution. Return null when git unavailable. */\n gitEmail?: () => string | null;\n /** Override os.hostname() */\n hostname?: () => string;\n /** Override os.userInfo().username */\n username?: () => string;\n /** Override new Date().toISOString() (unused here; reserved for callers) */\n now?: () => string;\n}\n\n/**\n * Resolve caller identity via precedence ladder.\n * All external lookups go through opts test seams — never reach process.env\n * or os.hostname() directly so tests can assert precedence without env pollution.\n */\nexport function resolveIdentity(\n projectRoot: string,\n opts: ResolveIdentityOpts = {},\n): Identity {\n // 1. participant-json\n const participant = readParticipant(projectRoot);\n if (participant !== null && participant.email) {\n return { email: participant.email, source: 'participant-json' };\n }\n\n // 2. env\n const envValue = (opts.env ?? process.env)['CLEARGATE_USER'];\n if (envValue && envValue.trim()) {\n return { email: envValue.trim(), source: 'env' };\n }\n\n // 3. git\n const gitEmailFn =\n opts.gitEmail ??\n (() => {\n const result = spawnSync('git', ['config', 'user.email'], {\n encoding: 'utf8',\n timeout: 3000,\n });\n if (result.status === 0 && result.stdout) {\n const trimmed = result.stdout.trim();\n if (trimmed) return trimmed;\n }\n return null;\n });\n\n const gitEmail = gitEmailFn();\n if (gitEmail && gitEmail.trim()) {\n return { email: gitEmail.trim(), source: 'git' };\n }\n\n // 4. host fallback\n const hostnameFn = opts.hostname ?? (() => os.hostname());\n const usernameFn =\n opts.username ??\n (() => {\n try {\n return os.userInfo().username;\n } catch {\n return 'user';\n }\n });\n\n const hostname = hostnameFn();\n const username = usernameFn();\n return { email: `${username}@${hostname}`, source: 'host' };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { deriveBucket } from '../wiki/derive-bucket.js';\nimport { deriveRepo } from '../wiki/derive-repo.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, parsePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\n\nexport interface WikiIngestOptions {\n /** Absolute path to the raw delivery file to ingest */\n rawPath: string;\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: replaces fs.rename (for atomic index.md write test) */\n rename?: (src: string, dst: string) => void;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\nexport async function wikiIngestHandler(opts: WikiIngestOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const rename = opts.rename ?? fs.renameSync;\n const templateDir = opts.templateDir;\n\n const rawPath = opts.rawPath;\n\n // Resolve rawPath: if relative, resolve against cwd\n const absRawPath = path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n // Compute relative path from cwd (repo root)\n const relRawPath = path.relative(cwd, absRawPath).replace(/\\\\/g, '/');\n\n // Step 1: Validate path resolves under <cwd>/.cleargate/delivery/\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const deliveryRootNorm = deliveryRoot.replace(/\\\\/g, '/');\n const absDeliveryRoot = deliveryRoot;\n\n // Check: absRawPath must be under absDeliveryRoot\n const relToDelivery = path.relative(absDeliveryRoot, absRawPath);\n if (relToDelivery.startsWith('..') || path.isAbsolute(relToDelivery)) {\n stderr(`wiki ingest: ${rawPath} not under .cleargate/delivery/\\n`);\n exit(2);\n return;\n }\n\n void deliveryRootNorm; // suppress lint warning\n\n // Step 2: Exclusion check (defense-in-depth)\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => relRawPath.startsWith(excl));\n if (isExcluded) {\n stdout(`wiki ingest: ${rawPath} excluded (skip)\\n`);\n exit(0);\n return;\n }\n\n // Step 3: Derive bucket + id + type + repo\n const filename = path.basename(absRawPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch (e) {\n stderr(`wiki ingest: cannot determine bucket for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let repo: ReturnType<typeof deriveRepo>;\n try {\n repo = deriveRepo(relRawPath);\n } catch (e) {\n stderr(`wiki ingest: cannot derive repo for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n const { type, id, bucket } = bucketInfo;\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const pageDir = path.join(wikiRoot, bucket);\n const pagePath = path.join(pageDir, `${id}.md`);\n\n // Read and parse the raw file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absRawPath, 'utf8');\n } catch (e) {\n stderr(`wiki ingest: cannot read ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch (e) {\n stderr(`wiki ingest: malformed frontmatter in ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // Step 4: Idempotency guard (A2)\n const currentSha = getGitSha(absRawPath, gitRunner) ?? '';\n const pageExists = fs.existsSync(pagePath);\n\n if (pageExists && currentSha !== '') {\n let isNoOp = false;\n try {\n const existingPageContent = fs.readFileSync(pagePath, 'utf8');\n const existingPage = parsePage(existingPageContent);\n\n if (existingPage.last_ingest_commit === currentSha) {\n // Check if raw file content matches what git shows for that SHA\n const contentUnchanged = checkContentUnchanged(absRawPath, currentSha, relRawPath, gitRunner);\n if (contentUnchanged) {\n isNoOp = true;\n }\n }\n } catch {\n // If we can't parse the existing page, proceed with ingest\n }\n\n if (isNoOp) {\n stdout(`wiki ingest: ${id} unchanged (no-op)\\n`);\n exit(0);\n return;\n }\n }\n\n // Determine action\n const action = pageExists ? 'update' : 'create';\n\n // Step 5: Build new WikiPage and write it\n const parent = buildParentRef(fm);\n const children = buildChildrenRefs(fm);\n const timestamp = now();\n\n const wikiPage: WikiPage = {\n type,\n id,\n parent,\n children,\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: relRawPath,\n last_ingest: timestamp,\n last_ingest_commit: currentSha,\n repo,\n };\n\n const pageBody = buildPageBody({ id, fm, body });\n const pageContent = serializePage(wikiPage, pageBody);\n\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(pagePath, pageContent, 'utf8');\n\n // Step 6: Append one log entry to wiki/log.md\n appendLogEntry(wikiRoot, { timestamp, action, id, relRawPath });\n\n // Step 7: Update wiki/index.md (atomic write-temp-then-rename)\n updateIndex(wikiRoot, { id, type, status: wikiPage.status, relRawPath, rename });\n\n // Step 8: Recompile affected synthesis pages (all four — M3 over-recompiles)\n recompileSynthesis(wikiRoot, cwd, templateDir);\n\n // Step 9: Print result\n stdout(`wiki ingest: ${action} ${bucket}/${id}.md\\n`);\n}\n\nfunction checkContentUnchanged(\n absRawPath: string,\n sha: string,\n relRawPath: string,\n gitRunner?: GitRunner,\n): boolean {\n try {\n const run = gitRunner ?? defaultGitRunner;\n // git show <sha>:<relRawPath> returns file content at that commit\n const gitContent = run('git', ['show', `${sha}:${relRawPath}`]);\n // Non-zero exit is handled by runner returning empty string (defaultRunner returns stdout)\n // If empty string returned from show when sha is valid, treat as changed\n if (!gitContent && gitContent !== '') return false;\n const currentContent = fs.readFileSync(absRawPath, 'utf8');\n return gitContent === currentContent;\n } catch {\n return false;\n }\n}\n\nfunction defaultGitRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n if (result.status !== 0) return '\\0__NONZERO__'; // sentinel for non-zero exit\n return result.stdout ?? '';\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: { id: string; fm: Record<string, unknown>; body: string }): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(\n item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.',\n ).slice(0, 200);\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n const blastParts: string[] = [];\n if (parent) blastParts.push(parent);\n for (const child of children) blastParts.push(child);\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction appendLogEntry(\n wikiRoot: string,\n entry: { timestamp: string; action: string; id: string; relRawPath: string },\n): void {\n const logPath = path.join(wikiRoot, 'log.md');\n const logEntry = [\n `- timestamp: \"${entry.timestamp}\"`,\n ` actor: \"cleargate wiki ingest\"`,\n ` action: \"${entry.action}\"`,\n ` target: \"${entry.id}\"`,\n ` path: \"${entry.relRawPath}\"`,\n ].join('\\n');\n\n if (fs.existsSync(logPath)) {\n const existing = fs.readFileSync(logPath, 'utf8');\n // Append to existing log\n const newContent = existing.trimEnd() + '\\n' + logEntry + '\\n';\n fs.writeFileSync(logPath, newContent, 'utf8');\n } else {\n fs.mkdirSync(wikiRoot, { recursive: true });\n fs.writeFileSync(logPath, `# Wiki Event Log\\n\\n${logEntry}\\n`, 'utf8');\n }\n}\n\nfunction updateIndex(\n wikiRoot: string,\n opts: {\n id: string;\n type: string;\n status: string;\n relRawPath: string;\n rename: (src: string, dst: string) => void;\n },\n): void {\n const indexPath = path.join(wikiRoot, 'index.md');\n const tmpPath = `${indexPath}.tmp`;\n\n const newRow = `| [[${opts.id}]] | ${opts.type} | ${opts.status} | ${opts.relRawPath} |`;\n\n let content: string;\n if (fs.existsSync(indexPath)) {\n content = fs.readFileSync(indexPath, 'utf8');\n // Check if a row with this id already exists; if so, replace it\n const idPattern = `[[${opts.id}]]`;\n const lines = content.split('\\n');\n let replaced = false;\n const newLines = lines.map((line) => {\n if (line.includes(idPattern) && line.startsWith('|')) {\n replaced = true;\n return newRow;\n }\n return line;\n });\n\n if (replaced) {\n content = newLines.join('\\n');\n } else {\n // Insert into the correct bucket section\n content = insertIntoSection(content, opts.id, newRow);\n }\n } else {\n // Build a minimal index.md with the item\n content = buildMinimalIndex(opts.id, opts.type, opts.status, opts.relRawPath);\n }\n\n fs.writeFileSync(tmpPath, content, 'utf8');\n opts.rename(tmpPath, indexPath);\n}\n\nfunction insertIntoSection(content: string, id: string, newRow: string): string {\n // Determine which bucket section to insert into\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n const sectionHeader = `## ${label}`;\n\n const lines = content.split('\\n');\n let sectionStart = -1;\n let nextSectionStart = -1;\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === sectionHeader) {\n sectionStart = i;\n // Find next section\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j].startsWith('## ')) {\n nextSectionStart = j;\n break;\n }\n }\n break;\n }\n }\n\n if (sectionStart === -1) {\n // Section doesn't exist — append new section at end\n const sectionContent = [\n '',\n sectionHeader,\n '',\n newRow,\n '',\n ].join('\\n');\n return content.trimEnd() + sectionContent;\n }\n\n // Find insertion point: after any existing rows in this section, sorted by id\n const sectionEnd = nextSectionStart === -1 ? lines.length : nextSectionStart;\n const sectionLines = lines.slice(sectionStart + 1, sectionEnd);\n\n // Find existing row entries and insert in sorted order\n let insertAt = -1;\n for (let i = 0; i < sectionLines.length; i++) {\n const line = sectionLines[i];\n if (line.startsWith('|') && !line.startsWith('|---|')) {\n // Extract the id from the row: | [[ID]] | ...\n const match = /\\|\\s*\\[\\[([^\\]]+)\\]\\]/.exec(line);\n if (match) {\n const rowId = match[1];\n if (id.localeCompare(rowId) <= 0) {\n insertAt = sectionStart + 1 + i;\n break;\n }\n }\n }\n }\n\n if (insertAt === -1) {\n // Append before the next section or at end of section\n // Find last row in section\n let lastRowIdx = sectionStart + 1;\n for (let i = sectionStart + 1; i < sectionEnd; i++) {\n if (lines[i].startsWith('|')) {\n lastRowIdx = i + 1;\n }\n }\n lines.splice(lastRowIdx, 0, newRow);\n } else {\n lines.splice(insertAt, 0, newRow);\n }\n\n return lines.join('\\n');\n}\n\nfunction getBucketFromId(id: string): string {\n if (id.startsWith('EPIC-')) return 'epics';\n if (id.startsWith('STORY-')) return 'stories';\n if (id.startsWith('SPRINT-')) return 'sprints';\n if (id.startsWith('PROPOSAL-')) return 'proposals';\n if (id.startsWith('CR-')) return 'crs';\n if (id.startsWith('BUG-')) return 'bugs';\n return 'topics';\n}\n\nfunction buildMinimalIndex(id: string, type: string, status: string, relRawPath: string): string {\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n\n const lines: string[] = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n '| ID | Type | Status | Raw Path |',\n '|---|---|---|---|',\n ];\n\n for (const b of BUCKET_ORDER) {\n if (b === 'topics') continue;\n lines.push('', `## ${BUCKET_LABELS[b]}`, '');\n if (b === bucket) {\n lines.push(`| [[${id}]] | ${type} | ${status} | ${relRawPath} |`);\n } else {\n lines.push('_No items._');\n }\n }\n lines.push('');\n\n void label; // suppress\n return lines.join('\\n');\n}\n\nfunction recompileSynthesis(wikiRoot: string, cwd: string, templateDir?: string): void {\n // Recompile all four synthesis pages\n // Gather current state from wiki pages to pass to recipes\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n let items: RawItem[] = [];\n if (fs.existsSync(deliveryRoot)) {\n try {\n items = scanRawItems(deliveryRoot, cwd);\n } catch {\n // If scan fails, pass empty state — synthesis pages will reflect empty\n }\n }\n\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n}\n","/**\n * STORY-002-08: cleargate wiki lint\n *\n * Scans .cleargate/wiki/ pages against their raw source files.\n * Enforcement mode (default): exits non-zero on any drift finding.\n * Suggest mode (--suggest): exits 0, prefixes flags with [advisory].\n *\n * Output format matches cleargate-wiki-lint subagent def lines 220-227:\n * <category>: <primary-path> -> <secondary-path-or-detail> (<optional context>)\n * Summary line always last:\n * lint: <OK|FAIL> (N pages checked, M findings)\n */\n\nimport * as path from 'node:path';\nimport { loadWikiPages } from '../wiki/load-wiki.js';\nimport type { GitRunner } from '../wiki/git-sha.js';\nimport {\n checkOrphan,\n checkRepoMismatch,\n checkStaleCommit,\n checkMissingIngest,\n checkBrokenBacklinks,\n checkInvalidatedCitations,\n checkExcludedPathIngested,\n checkPaginationNeeded,\n checkGateFailure,\n checkGateStaleness,\n checkIndexBudget,\n discoverPlainTextMentions,\n type LintFinding,\n} from '../wiki/lint-checks.js';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\n\nexport interface WikiLintOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to git-sha calls */\n gitRunner?: GitRunner;\n /** Lint mode: 'enforce' (default, exits 1 on findings) or 'suggest' (always exits 0) */\n mode?: 'enforce' | 'suggest';\n}\n\nexport async function wikiLintHandler(opts: WikiLintOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n // stderr seam is wired but lint outputs everything to stdout per subagent def\n opts.stderr;\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const mode = opts.mode ?? 'enforce';\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const repoRoot = cwd;\n\n // Step 1: load all wiki pages (single discovery pass)\n let pages = loadWikiPages(wikiRoot);\n\n const findings: LintFinding[] = [];\n\n // Meta-check: pagination-needed (fires on bucket > 50 entries)\n const paginationFindings = checkPaginationNeeded(pages);\n findings.push(...paginationFindings);\n\n // Step 2: per-page checks (O(n))\n for (const page of pages) {\n // (a) orphan\n const orphan = checkOrphan(page, repoRoot);\n if (orphan) findings.push(orphan);\n\n // (b) repo-mismatch\n const repoMismatch = checkRepoMismatch(page, repoRoot);\n if (repoMismatch) findings.push(repoMismatch);\n\n // (c) stale-commit\n const staleCommit = checkStaleCommit(page, repoRoot, gitRunner);\n if (staleCommit) findings.push(staleCommit);\n\n // (d) missing-ingest\n const missingIngest = checkMissingIngest(page, repoRoot);\n if (missingIngest) findings.push(missingIngest);\n\n // (e) excluded-path-ingested\n const excludedPath = checkExcludedPathIngested(page, repoRoot);\n if (excludedPath) findings.push(excludedPath);\n\n // (f) gate-failure — enforcing for Epic/Story/CR/Bug\n const gateFail = checkGateFailure(page, repoRoot);\n if (gateFail) findings.push(gateFail);\n\n // (g) gate-stale — applies to ALL types\n const gateStale = checkGateStaleness(page, repoRoot);\n if (gateStale) findings.push(gateStale);\n }\n\n // Step 3: single index cross-check pass — broken backlinks\n const backlinkFindings = checkBrokenBacklinks(pages, repoRoot);\n findings.push(...backlinkFindings);\n\n // Step 4: topic-page invalidated-citation check\n const citationFindings = checkInvalidatedCitations(pages, repoRoot);\n findings.push(...citationFindings);\n\n // Step 4.5: index token-budget check (STORY-015-03)\n const wikiConfig = loadWikiConfig(cwd);\n const indexBudget = checkIndexBudget(repoRoot, wikiConfig.wiki.index_token_ceiling);\n\n // In suggest mode, always emit the usage line unconditionally (before advisory loop)\n if (mode === 'suggest' && indexBudget.tokens !== undefined) {\n const tokens = indexBudget.tokens;\n const ceiling = indexBudget.ceiling!;\n const pct = Math.round((tokens / ceiling) * 100);\n stdout(`index token usage: ${tokens} / ${ceiling} (${pct}%)\\n`);\n }\n\n // In enforce mode, push finding into findings array (will be emitted below)\n if (mode === 'enforce' && indexBudget.finding !== null) {\n findings.push(indexBudget.finding);\n }\n\n const pageCount = pages.length;\n const findingCount = findings.length;\n\n // Step 5: emit results\n if (mode === 'suggest') {\n // Advisory mode: prefix flags with [advisory] + do Karpathy discovery pass\n for (const finding of findings) {\n stdout(`[advisory] ${finding.line}\\n`);\n }\n\n // Karpathy discovery pass\n const suggestions = discoverPlainTextMentions(pages, repoRoot);\n for (const suggestion of suggestions) {\n stdout(`${suggestion}\\n`);\n }\n\n stdout(`lint: OK (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(0);\n return;\n }\n\n // Enforce mode\n for (const finding of findings) {\n stdout(`${finding.line}\\n`);\n }\n\n if (findingCount > 0) {\n stdout(`lint: FAIL (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(1);\n } else {\n stdout(`lint: OK (${pageCount} pages checked, 0 findings)\\n`);\n exit(0);\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport type { WikiPage } from './page-schema.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface LoadedWikiPage {\n absPath: string;\n page: WikiPage;\n body: string;\n}\n\nconst BUCKET_DIRS = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'];\n\n/**\n * Glob+Read every wiki page from wikiRoot once.\n * Shared by wiki-lint and wiki-query (STORY-002-07 and STORY-002-08).\n */\nexport function loadWikiPages(wikiRoot: string): LoadedWikiPage[] {\n const results: LoadedWikiPage[] = [];\n\n for (const bucket of BUCKET_DIRS) {\n const dir = path.join(wikiRoot, bucket);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { encoding: 'utf8' }) as string[];\n for (const filename of entries) {\n if (!filename.endsWith('.md')) continue;\n const absPath = path.join(dir, filename);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n continue;\n }\n\n const page: WikiPage = {\n type: (fm['type'] as WikiPageType) ?? 'epic',\n id: String(fm['id'] ?? ''),\n parent: String(fm['parent'] ?? ''),\n children: Array.isArray(fm['children'])\n ? (fm['children'] as unknown[]).map(String)\n : [],\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: String(fm['raw_path'] ?? ''),\n last_ingest: String(fm['last_ingest'] ?? ''),\n last_ingest_commit: String(fm['last_ingest_commit'] ?? ''),\n repo: (fm['repo'] as RepoTag) ?? 'planning',\n };\n\n results.push({ absPath, page, body });\n }\n }\n\n return results;\n}\n","/**\n * Pure-function lint check implementations.\n * One exported function per category so they can be unit-tested in isolation.\n * Categories must use these exact strings (subagent + CLI agree):\n * orphan, repo-mismatch, stale-commit, missing-ingest, broken-backlink,\n * invalidated-citation, excluded-path-ingested, pagination-needed, index-budget\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport type { GitRunner } from './git-sha.js';\nimport type { LoadedWikiPage } from './load-wiki.js';\nimport { deriveRepo } from './derive-repo.js';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { detectWorkItemTypeFromFm } from '../lib/work-item-type.js';\n\nexport interface LintFinding {\n category: string;\n line: string;\n}\n\n/** §10.3 excluded directories — wiki pages must not exist for raw files under these. */\nconst EXCLUDED_DIRS = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/** Maximum entries per bucket before pagination-needed fires. */\nconst MAX_BUCKET_ENTRIES = 50;\n\n/**\n * Check (a): Orphan — wiki page's raw_path doesn't exist on disk.\n * Skips pages whose raw_path is under an excluded directory (caught by check 7).\n */\nexport function checkOrphan(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n // Don't flag orphan for excluded paths — that's caught by excluded-path-ingested\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'orphan',\n line: `orphan: ${relPage} -> missing ${rawPath} (raw missing)`,\n };\n }\n return null;\n}\n\n/**\n * Check (b): repo-mismatch — stored repo field doesn't match raw_path prefix.\n */\nexport function checkRepoMismatch(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n let derivedRepo: string;\n try {\n derivedRepo = deriveRepo(rawPath);\n } catch {\n return null; // Can't derive — not our responsibility to flag here\n }\n\n if (page.page.repo !== derivedRepo) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'repo-mismatch',\n line: `repo-mismatch: ${relPage} declares repo:${page.page.repo} but raw_path implies repo:${derivedRepo}`,\n };\n }\n return null;\n}\n\n/**\n * Check (c): stale-commit — stored last_ingest_commit differs from current git HEAD SHA.\n * Empty stored SHA is tolerated (untracked file).\n */\nexport function checkStaleCommit(\n page: LoadedWikiPage,\n repoRoot: string,\n gitRunner?: GitRunner,\n): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const storedSha = page.page.last_ingest_commit;\n // If stored SHA is empty, file was untracked at ingest time — tolerate\n if (!storedSha) return null;\n\n // Run git log -1\n let currentSha: string;\n if (gitRunner) {\n currentSha = gitRunner('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n } else {\n const result = spawnSync('git', ['log', '-1', '--format=%H', '--', rawPath], {\n encoding: 'utf8',\n cwd: repoRoot,\n });\n currentSha = (result.stdout ?? '').trim();\n }\n\n if (!currentSha) return null; // untracked now — don't flag\n if (storedSha !== currentSha) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'stale-commit',\n line: `stale-commit: ${relPage} at ${storedSha}, current ${currentSha}`,\n };\n }\n return null;\n}\n\n/**\n * Check (d): missing-ingest — raw file mtime newer than wiki page mtime.\n */\nexport function checkMissingIngest(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null; // orphan check handles this\n\n const rawStat = fs.statSync(absRaw);\n const pageStat = fs.statSync(page.absPath);\n\n // Use > 2s gap to avoid HFS+ mtime resolution flakiness (per blueprint gotcha)\n const rawMtimeMs = rawStat.mtimeMs;\n const pageMtimeMs = pageStat.mtimeMs;\n\n if (rawMtimeMs - pageMtimeMs > 2000) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n const rawMtime = rawStat.mtime.toISOString();\n const pageMtime = pageStat.mtime.toISOString();\n return {\n category: 'missing-ingest',\n line: `missing-ingest: ${rawPath} newer than ${relPage} (raw mtime: ${rawMtime}, page mtime: ${pageMtime})`,\n };\n }\n return null;\n}\n\n/**\n * Check (backlink): broken backlink — child's parent exists but parent doesn't list the child.\n * O(n) linear scan over collected parent declarations.\n */\nexport function checkBrokenBacklinks(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n // Build a map of id -> page for O(1) lookup\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n for (const childPage of pages) {\n const parentRef = childPage.page.parent;\n if (!parentRef) continue;\n\n // Extract parent ID from \"[[PARENT-ID]]\" form\n const match = parentRef.match(/\\[\\[(.+?)\\]\\]/);\n if (!match) continue;\n const parentId = match[1];\n\n const parentPage = byId.get(parentId);\n if (!parentPage) {\n // Parent page missing — flag\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n continue;\n }\n\n // Check that parent's children list contains [[childId]]\n const childId = childPage.page.id;\n const childRef = `[[${childId}]]`;\n const parentHasChild = parentPage.page.children.some(\n (c) => c === childRef || c === childId,\n );\n\n if (!parentHasChild) {\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n }\n }\n\n return findings;\n}\n\n/**\n * Check: invalidated-citation — topic page cites a cancelled or missing item.\n */\nexport function checkInvalidatedCitations(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n const topicPages = pages.filter((p) => p.page.type === 'topic');\n\n for (const topicPage of topicPages) {\n // WikiPage doesn't have a cites field — re-parse raw frontmatter to get it.\n const relTopic = path.relative(wikiRoot, topicPage.absPath).replace(/\\\\/g, '/');\n\n let citesList: string[] = [];\n try {\n const raw = fs.readFileSync(topicPage.absPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n const rawCites = fm['cites'];\n if (Array.isArray(rawCites)) {\n citesList = (rawCites as unknown[]).map(String);\n }\n } catch {\n continue;\n }\n\n for (const cite of citesList) {\n const match = cite.match(/\\[\\[(.+?)\\]\\]/);\n const id = match ? match[1] : cite;\n\n const citedPage = byId.get(id);\n if (!citedPage) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (missing)`,\n });\n continue;\n }\n\n const status = citedPage.page.status;\n if (status === 'cancelled' || status.toLowerCase().includes('cancelled')) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (cancelled)`,\n });\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Check: excluded-path-ingested — wiki page exists for a raw file under an excluded directory.\n */\nexport function checkExcludedPathIngested(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'excluded-path-ingested',\n line: `excluded-path-ingested: ${relPage} (raw_path ${rawPath} is under an excluded directory)`,\n };\n }\n return null;\n}\n\n/**\n * Meta-check: pagination-needed — fires if any bucket has more than 50 entries.\n */\nexport function checkPaginationNeeded(pages: LoadedWikiPage[]): LintFinding[] {\n // Count by bucket (derived from absPath directory name)\n const bucketCounts = new Map<string, number>();\n for (const p of pages) {\n const bucket = path.basename(path.dirname(p.absPath));\n bucketCounts.set(bucket, (bucketCounts.get(bucket) ?? 0) + 1);\n }\n\n const findings: LintFinding[] = [];\n for (const [bucket, count] of bucketCounts) {\n if (count > MAX_BUCKET_ENTRIES) {\n findings.push({\n category: 'pagination-needed',\n line: `pagination-needed: ${bucket} (${count} entries, max ${MAX_BUCKET_ENTRIES} per bucket)`,\n });\n }\n }\n return findings;\n}\n\n/** Work-item types that trigger enforcing gate-failure lint (not advisory). */\nconst ENFORCING_TYPES = new Set(['epic', 'story', 'cr', 'bug']);\n\n/** Status values considered \"ready\" (🟢-candidate). */\nconst READY_STATUSES = new Set(['Ready', 'Active']);\n\n/**\n * Parse the cached_gate_result from a raw frontmatter record.\n * parseFrontmatter stores nested YAML objects as opaque strings starting with '{'.\n * This helper resolves either form into a plain object or null.\n */\nfunction parseCachedGateResult(\n raw: unknown,\n): { pass: unknown; failing_criteria: unknown; last_gate_check: unknown } | null {\n if (!raw || raw === null) return null;\n\n // Opaque string form — inline flow YAML written by writeCachedGate\n if (typeof raw === 'string') {\n if (!raw.startsWith('{')) return null;\n try {\n const parsed = yaml.load(raw);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n } catch {\n return null;\n }\n }\n\n // Already-parsed object form (future-proofing)\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n const p = raw as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n }\n\n return null;\n}\n\n/**\n * Check: gate-failure — 🟢-candidate Epic/Story/CR/Bug with cached_gate_result.pass === false.\n * Reads the raw work-item file (not the wiki page).\n * Proposal / Sprint / Initiative are advisory only → returns null (no enforcing block).\n */\nexport function checkGateFailure(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr || cgr.pass !== false) return null;\n\n // Check if the work-item type is enforcing\n const wiType = detectWorkItemTypeFromFm(rawFm);\n if (!wiType || !ENFORCING_TYPES.has(wiType)) return null;\n\n // Check if this is a 🟢-candidate (status Ready/Active or ambiguity 🟢 Low)\n const status = String(rawFm['status'] ?? '');\n const ambiguity = String(rawFm['ambiguity'] ?? '');\n const isReadyCandidate = READY_STATUSES.has(status) || ambiguity === '🟢 Low';\n if (!isReadyCandidate) return null;\n\n // Collect failing criteria IDs\n const failingCriteria = cgr.failing_criteria;\n const criteriaIds: string[] = [];\n if (Array.isArray(failingCriteria)) {\n for (const criterion of failingCriteria as unknown[]) {\n if (criterion && typeof criterion === 'object' && 'id' in (criterion as object)) {\n criteriaIds.push(String((criterion as Record<string, unknown>)['id']));\n } else if (typeof criterion === 'string') {\n criteriaIds.push(criterion);\n }\n }\n }\n\n const criteriaStr = criteriaIds.length > 0 ? criteriaIds.join(', ') : 'unknown';\n return {\n category: 'gate-failure',\n line: `gate-failure: ${rawPath} failed criteria: ${criteriaStr}`,\n };\n}\n\n/**\n * Check: gate-stale — cached_gate_result.last_gate_check < updated_at (ISO-8601 lexical compare).\n * Applies to ALL work-item types (including Proposal/Sprint/Initiative).\n * Reads the raw work-item file (not the wiki page).\n */\nexport function checkGateStaleness(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr) return null;\n\n const lastGateCheck = cgr.last_gate_check;\n if (!lastGateCheck || lastGateCheck === null) return null;\n\n const updatedAt = rawFm['updated_at'];\n if (!updatedAt) return null;\n\n const lastCheckStr = String(lastGateCheck);\n const updatedAtStr = String(updatedAt);\n\n // ISO-8601 lexical compare: if last_gate_check < updated_at → stale\n if (lastCheckStr < updatedAtStr) {\n return {\n category: 'gate-stale',\n line: `gate-stale: ${rawPath} last_gate_check=${lastCheckStr} < updated_at=${updatedAtStr}`,\n };\n }\n return null;\n}\n\n/**\n * Karpathy discovery pass: scan page bodies for plain-text ID mentions\n * (not wrapped in [[]]). Emit suggest lines.\n */\nexport function discoverPlainTextMentions(pages: LoadedWikiPage[], repoRoot: string): string[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, boolean>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, true);\n }\n\n const suggestions: string[] = [];\n const ID_PATTERN = /\\b((?:EPIC|STORY|SPRINT|PROPOSAL|CR|BUG)-[\\w-]+)\\b/g;\n const LINK_PATTERN = /\\[\\[[\\w-]+\\]\\]/g;\n\n for (const page of pages) {\n const relPage = path.relative(wikiRoot, page.absPath).replace(/\\\\/g, '/');\n // Find all [[...]] wrapped references to exclude\n const wrappedRefs = new Set<string>();\n for (const m of page.body.matchAll(LINK_PATTERN)) {\n const inner = m[0].slice(2, -2);\n wrappedRefs.add(inner);\n }\n\n // Find plain-text ID mentions\n for (const m of page.body.matchAll(ID_PATTERN)) {\n const mentionedId = m[1];\n if (!byId.has(mentionedId)) continue;\n if (wrappedRefs.has(mentionedId)) continue;\n if (mentionedId === page.page.id) continue; // self-reference\n suggestions.push(`suggest: ${relPage} mentions ${mentionedId} in plain text, consider [[${mentionedId}]] wrap`);\n }\n }\n\n return suggestions;\n}\n\n/**\n * Check: index-budget — wiki/index.md approximate token count exceeds configured ceiling.\n * Token heuristic: Math.round(bytes / 4). Returns null when index.md is absent.\n * This check is a structural check, not a per-page check, so it takes repoRoot directly.\n */\nexport interface IndexBudgetResult {\n /** Present when tokens > ceiling. Push this into findings array. */\n finding: LintFinding | null;\n /** Always populated when index.md exists; undefined when file absent. */\n tokens?: number;\n ceiling?: number;\n}\n\nexport function checkIndexBudget(repoRoot: string, indexTokenCeiling: number): IndexBudgetResult {\n const indexPath = path.join(repoRoot, '.cleargate', 'wiki', 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n return { finding: null };\n }\n\n const bytes = fs.statSync(indexPath).size;\n const tokens = Math.round(bytes / 4);\n const ceiling = indexTokenCeiling;\n\n if (tokens > ceiling) {\n return {\n finding: {\n category: 'index-budget',\n line: `index-budget: wiki/index.md exceeds token ceiling: ${tokens} > ${ceiling}. Shard or prune (see EPIC-015).`,\n },\n tokens,\n ceiling,\n };\n }\n\n return { finding: null, tokens, ceiling };\n}\n","/**\n * work-item-type.ts — Shared work-item type detection utility.\n *\n * STORY-008-03: extracted here for STORY-008-05 to import without duplication.\n * Maps frontmatter ID keys and filename patterns to canonical work-item types.\n */\n\nexport type WorkItemType = 'story' | 'epic' | 'proposal' | 'cr' | 'bug';\n\n/**\n * Frontmatter key → work-item type mapping.\n * Keys are checked in order; first match wins.\n */\nconst FM_KEY_MAP: Array<{ key: string; type: WorkItemType }> = [\n { key: 'story_id', type: 'story' },\n { key: 'epic_id', type: 'epic' },\n { key: 'proposal_id', type: 'proposal' },\n { key: 'cr_id', type: 'cr' },\n { key: 'bug_id', type: 'bug' },\n];\n\n/**\n * Filename / ID prefix → work-item type mapping.\n */\nconst PREFIX_MAP: Array<{ prefix: string; type: WorkItemType }> = [\n { prefix: 'STORY-', type: 'story' },\n { prefix: 'EPIC-', type: 'epic' },\n { prefix: 'PROPOSAL-', type: 'proposal' },\n { prefix: 'CR-', type: 'cr' },\n { prefix: 'BUG-', type: 'bug' },\n];\n\n/**\n * Detect the work-item type from a parsed frontmatter record.\n * Returns null if no recognized ID key is found.\n */\nexport function detectWorkItemTypeFromFm(\n fm: Record<string, unknown>,\n): WorkItemType | null {\n for (const { key, type } of FM_KEY_MAP) {\n if (fm[key] !== undefined && fm[key] !== null && fm[key] !== '') {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Detect the work-item type from an ID string or file path.\n * Matches against the uppercase prefix (STORY-, EPIC-, etc.).\n * Returns null if no prefix matches.\n */\nexport function detectWorkItemType(idOrPath: string): WorkItemType | null {\n const upper = idOrPath.toUpperCase();\n // Strip leading directory path components\n const basename = upper.split('/').pop() ?? upper;\n for (const { prefix, type } of PREFIX_MAP) {\n if (basename.includes(prefix)) {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Canonical transitions per work-item type.\n * Epic has 2; all others have 1.\n */\nexport const WORK_ITEM_TRANSITIONS: Record<WorkItemType, string[]> = {\n proposal: ['ready-for-decomposition'],\n epic: ['ready-for-decomposition', 'ready-for-coding'],\n story: ['ready-for-execution'],\n cr: ['ready-to-apply'],\n bug: ['ready-for-fix'],\n};\n","/**\n * STORY-015-03: Per-repo wiki configuration loader.\n * STORY-018-03: Extended to include `gates` map.\n *\n * Reads `.cleargate/config.yml` from the repo root.\n * Single responsibility: surface `wiki.index_token_ceiling` and `gates` map.\n * Missing file → defaults. Malformed YAML → throws with file path in message.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\n\nexport interface GatesConfig {\n precommit?: string;\n test?: string;\n typecheck?: string;\n lint?: string;\n}\n\nexport interface WikiConfig {\n wiki: {\n index_token_ceiling: number;\n };\n gates: GatesConfig;\n}\n\nconst DEFAULT_INDEX_TOKEN_CEILING = 8000;\n\n/**\n * Load per-repo wiki config from `<repoRoot>/.cleargate/config.yml`.\n * Returns defaults when file is absent.\n * Throws a descriptive error on malformed YAML.\n */\nexport function loadWikiConfig(repoRoot: string): WikiConfig {\n const configPath = path.join(repoRoot, '.cleargate', 'config.yml');\n\n if (!fs.existsSync(configPath)) {\n return { wiki: { index_token_ceiling: DEFAULT_INDEX_TOKEN_CEILING }, gates: {} };\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch (err) {\n throw new Error(`Failed to read ${configPath}: ${String(err)}`);\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`Malformed YAML in ${configPath}: ${String(err)}`);\n }\n\n const ceiling = extractCeiling(parsed);\n const gates = extractGates(parsed);\n\n return {\n wiki: {\n index_token_ceiling: ceiling,\n },\n gates,\n };\n}\n\nfunction extractCeiling(parsed: unknown): number {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const root = parsed as Record<string, unknown>;\n const wiki = root['wiki'];\n\n if (wiki === null || wiki === undefined || typeof wiki !== 'object' || Array.isArray(wiki)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const wikiObj = wiki as Record<string, unknown>;\n const ceiling = wikiObj['index_token_ceiling'];\n\n if (typeof ceiling === 'number' && Number.isFinite(ceiling) && ceiling > 0) {\n return ceiling;\n }\n\n return DEFAULT_INDEX_TOKEN_CEILING;\n}\n\nfunction extractGates(parsed: unknown): GatesConfig {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {};\n }\n\n const root = parsed as Record<string, unknown>;\n const gates = root['gates'];\n\n if (gates === null || gates === undefined || typeof gates !== 'object' || Array.isArray(gates)) {\n return {};\n }\n\n const gatesObj = gates as Record<string, unknown>;\n const result: GatesConfig = {};\n\n if (typeof gatesObj['precommit'] === 'string') result.precommit = gatesObj['precommit'];\n if (typeof gatesObj['test'] === 'string') result.test = gatesObj['test'];\n if (typeof gatesObj['typecheck'] === 'string') result.typecheck = gatesObj['typecheck'];\n if (typeof gatesObj['lint'] === 'string') result.lint = gatesObj['lint'];\n\n return result;\n}\n","/**\n * STORY-002-08: cleargate wiki query [--persist]\n *\n * Read-only by default: grep .cleargate/wiki/index.md for query terms,\n * return matching [[ID]] list with one-line excerpts to stdout. Exit 0.\n *\n * --persist: compute slug, write wiki/topics/<slug>.md with frontmatter\n * type: topic, id, created_by, created_at, cites. Append to wiki/index.md\n * ## Topics section.\n *\n * NOTE: CLI synthesis is grep-and-list. For NL synthesis with the\n * cleargate-wiki-query subagent, invoke from a Claude Code session.\n * This diverges from PROPOSAL-002 §2.2 intentionally for testability and\n * offline/scripted use.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface WikiQueryOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: frozen ISO timestamp (defaults to new Date().toISOString()) */\n now?: () => string;\n /** The query string */\n query: string;\n /** If true, write result as a topic page under wiki/topics/ */\n persist?: boolean;\n}\n\n/**\n * Compute a slug from a query string.\n * Lowercase, replace spaces and punctuation with hyphens,\n * strip consecutive hyphens, truncate to ≤40 chars.\n */\nexport function computeSlug(query: string): string {\n return query\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') // non-alphanumeric → hyphen\n .replace(/^-+|-+$/g, '') // strip leading/trailing hyphens\n .replace(/-{2,}/g, '-') // collapse consecutive hyphens\n .slice(0, 40)\n .replace(/-+$/, ''); // strip trailing hyphens after truncation\n}\n\n/**\n * Parse index.md and extract matching IDs for the given query terms.\n * Returns array of { id, line } matching lines.\n */\nfunction searchIndex(indexContent: string, query: string): Array<{ id: string; excerpt: string }> {\n const terms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 0);\n\n const results: Array<{ id: string; excerpt: string }> = [];\n const seenIds = new Set<string>();\n\n for (const line of indexContent.split('\\n')) {\n const lower = line.toLowerCase();\n const matchesAll = terms.every((term) => lower.includes(term));\n if (!matchesAll) continue;\n\n // Extract [[ID]] from line\n const match = line.match(/\\[\\[([^\\]]+)\\]\\]/);\n if (!match) continue;\n const id = match[1];\n if (seenIds.has(id)) continue;\n seenIds.add(id);\n\n results.push({ id, excerpt: line.trim() });\n }\n\n return results;\n}\n\nexport async function wikiQueryHandler(opts: WikiQueryOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const now = opts.now ?? (() => new Date().toISOString());\n const query = opts.query;\n const persist = opts.persist ?? false;\n\n void stderr; // suppress unused warning\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const indexPath = path.join(wikiRoot, 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n stdout(`wiki query: no index.md found at ${indexPath}\\n`);\n stdout(`Run \\`cleargate wiki build\\` first.\\n`);\n exit(1);\n return;\n }\n\n const indexContent = fs.readFileSync(indexPath, 'utf8');\n const matches = searchIndex(indexContent, query);\n\n if (matches.length === 0) {\n stdout(`wiki query: no matches for \"${query}\"\\n`);\n exit(0);\n return;\n }\n\n // Build output body\n const bodyLines: string[] = [\n `# Query: ${query}`,\n '',\n `Found ${matches.length} match(es):`,\n '',\n ];\n\n for (const { id, excerpt } of matches) {\n bodyLines.push(`- [[${id}]] — ${excerpt}`);\n }\n bodyLines.push('');\n\n const body = bodyLines.join('\\n');\n\n // Output to stdout (read-only mode: always output to stdout)\n stdout(body);\n\n if (!persist) {\n exit(0);\n return;\n }\n\n // Persist mode: write topic page\n const slug = computeSlug(query);\n const topicsDir = path.join(wikiRoot, 'topics');\n fs.mkdirSync(topicsDir, { recursive: true });\n\n const citesArray = matches.map(({ id }) => `\"[[${id}]]\"`);\n const createdAt = now();\n\n // Build topic page frontmatter\n const frontmatter = [\n '---',\n `type: topic`,\n `id: \"${slug}\"`,\n `created_by: \"cleargate-wiki-query\"`,\n `created_at: \"${createdAt}\"`,\n `cites: [${citesArray.join(', ')}]`,\n '---',\n ].join('\\n');\n\n const topicContent = `${frontmatter}\\n\\n${body}`;\n const topicPath = path.join(topicsDir, `${slug}.md`);\n\n // Overwrite if exists (slug collision → overwrite per subagent def line 136)\n fs.writeFileSync(topicPath, topicContent, 'utf8');\n\n // Update wiki/index.md Topics section\n updateIndexTopicsSection(indexPath, slug, query, createdAt);\n\n exit(0);\n}\n\n/**\n * Append one row to the ## Topics section of wiki/index.md.\n * Creates the section header if absent.\n */\nfunction updateIndexTopicsSection(\n indexPath: string,\n slug: string,\n query: string,\n createdAt: string,\n): void {\n let content = fs.readFileSync(indexPath, 'utf8');\n\n const row = `| ${slug} | ${query} | ${createdAt} |`;\n\n if (content.includes('## Topics')) {\n // Append after the last line of the Topics section (end of file or before next ##)\n // Find the Topics section and append the row at the end\n const topicsIdx = content.indexOf('## Topics');\n const afterTopics = content.slice(topicsIdx);\n\n // Find the next ## section or end of file\n const nextSectionMatch = afterTopics.slice('## Topics'.length).match(/\\n## /);\n if (nextSectionMatch && nextSectionMatch.index !== undefined) {\n const insertPos = topicsIdx + '## Topics'.length + nextSectionMatch.index;\n content = content.slice(0, insertPos) + `\\n${row}` + content.slice(insertPos);\n } else {\n // Topics is the last section — append at end\n content = content.trimEnd() + `\\n${row}\\n`;\n }\n } else {\n // Create Topics section at end of file\n content = content.trimEnd() + `\\n\\n## Topics\\n\\n${row}\\n`;\n }\n\n fs.writeFileSync(indexPath, content, 'utf8');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline';\nimport { scanRawItems } from '../wiki/scan.js';\n\nexport interface WikiAuditStatusOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp (not used for output; reserved for future use) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Apply safe status corrections to frontmatter */\n fix?: boolean;\n /** Required together with --fix to confirm writes in non-interactive mode */\n yes?: boolean;\n /** Suppress diff output */\n quiet?: boolean;\n /** Test seam: override isTTY detection */\n isTTY?: boolean;\n /** Test seam: override readline for confirmation prompt */\n promptReader?: () => Promise<string>;\n}\n\nconst TERMINAL = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\ninterface DriftItem {\n id: string;\n rawPath: string;\n absPath: string;\n bucket: string;\n currentStatus: string;\n rule: 'A' | 'B' | 'C';\n /** Suggested new status (undefined for Rule B — file move needed) */\n suggestedStatus?: string;\n /** Human-readable description for the report */\n description: string;\n}\n\nexport async function wikiAuditStatusHandler(opts: WikiAuditStatusOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => { process.stdout.write(s); });\n const stderr = opts.stderr ?? ((s: string) => { process.stderr.write(s); });\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`audit-status: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Build a lookup of epic id → child stories (with terminal status check)\n const storiesByEpic = new Map<string, typeof items>();\n for (const item of items) {\n if (item.bucket !== 'stories') continue;\n const epicRef = String(item.fm['parent_epic_ref'] ?? '').replace(/^\\[\\[|\\]\\]$/g, '');\n if (!epicRef) continue;\n if (!storiesByEpic.has(epicRef)) storiesByEpic.set(epicRef, []);\n storiesByEpic.get(epicRef)!.push(item);\n }\n\n const driftItems: DriftItem[] = [];\n\n for (const item of items) {\n const currentStatus = String(item.fm['status'] ?? '');\n const isTerminal = TERMINAL.has(currentStatus);\n const inArchive = item.rawPath.includes('/archive/');\n const inPendingSync = item.rawPath.includes('/pending-sync/');\n\n // Rule A: in archive/ but status is non-terminal\n if (inArchive && !isTerminal) {\n // Determine suggested fix: Completed if all child stories terminal (epics/sprints), else Abandoned\n let suggestedStatus = 'Abandoned';\n if (item.bucket === 'epics' || item.bucket === 'sprints') {\n const childStories = storiesByEpic.get(item.id) ?? [];\n if (childStories.length > 0 && childStories.every((s) => TERMINAL.has(String(s.fm['status'] ?? '')))) {\n suggestedStatus = 'Completed';\n }\n }\n\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'A',\n suggestedStatus,\n description: `Rule A — archived with non-terminal status '${currentStatus}'`,\n });\n }\n\n // Rule B: in pending-sync/ but status is terminal\n if (inPendingSync && isTerminal) {\n const archivePath = item.rawPath.replace('/pending-sync/', '/archive/');\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'B',\n // No suggestedStatus — Rule B requires file move, not status change\n description: `Rule B — pending-sync with terminal status '${currentStatus}'; run: git mv ${item.rawPath} ${archivePath.replace(/\\/[^/]+$/, '/')}`,\n });\n }\n\n // Rule C: sprint file, non-terminal status, all children of its epics are terminal\n if (item.bucket === 'sprints' && !isTerminal) {\n const epicRefs = item.fm['epics'];\n if (!epicRefs) continue; // No epics key → Rule C does not fire\n const epicsArr = Array.isArray(epicRefs) ? epicRefs : [epicRefs];\n if (epicsArr.length === 0) continue;\n\n let totalChildren = 0;\n let terminalChildren = 0;\n\n for (const epicRef of epicsArr) {\n const epicId = String(epicRef).replace(/^\\[\\[|\\]\\]$/g, '');\n const children = storiesByEpic.get(epicId) ?? [];\n totalChildren += children.length;\n terminalChildren += children.filter((s) => TERMINAL.has(String(s.fm['status'] ?? ''))).length;\n }\n\n if (totalChildren > 0 && totalChildren === terminalChildren) {\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'C',\n suggestedStatus: 'Completed',\n description: `Rule C — ${terminalChildren}/${totalChildren} child stories terminal; suggest Completed`,\n });\n }\n }\n }\n\n // Print report\n if (driftItems.length === 0) {\n stdout('audit-status: clean (0 drift)\\n');\n exit(0);\n return;\n }\n\n for (const d of driftItems) {\n if (d.rule === 'B') {\n // Rule B: emit git mv hint\n const archivePath = d.rawPath.replace('/pending-sync/', '/archive/');\n const archiveDir = archivePath.replace(/\\/[^/]+$/, '/');\n stdout(`${d.id}: ${d.description}\\n`);\n stdout(` git mv ${d.rawPath} ${archiveDir}\\n`);\n } else {\n stdout(`${d.id}: ${d.description}\\n`);\n }\n }\n\n if (!opts.fix) {\n exit(1);\n return;\n }\n\n // --fix mode\n const fixable = driftItems.filter((d) => d.rule !== 'B' && d.suggestedStatus !== undefined);\n const ruleB = driftItems.filter((d) => d.rule === 'B');\n\n if (ruleB.length > 0) {\n for (const d of ruleB) {\n stdout(` (skipping ${d.id}: Rule B requires manual file move, not a status change)\\n`);\n }\n }\n\n if (fixable.length === 0) {\n stdout('audit-status: no auto-fixable items (Rule B items require manual git mv)\\n');\n exit(0);\n return;\n }\n\n // Confirmation\n if (!opts.yes) {\n if (!isTTY) {\n stderr('audit-status: --fix requires --yes in non-interactive mode\\n');\n exit(2);\n return;\n }\n\n // TTY: prompt\n let answer: string;\n if (opts.promptReader) {\n answer = await opts.promptReader();\n } else {\n answer = await new Promise<string>((resolve) => {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`apply ${fixable.length} changes? [y/N] `, (ans) => {\n rl.close();\n resolve(ans);\n });\n });\n }\n\n if (!answer.trim().toLowerCase().startsWith('y')) {\n stdout('aborted\\n');\n exit(2);\n return;\n }\n }\n\n // Apply fixes via line-surgery regex (do NOT round-trip through parseFrontmatter)\n for (const d of fixable) {\n const rawText = fs.readFileSync(d.absPath, 'utf8');\n const updated = applyStatusFix(rawText, d.suggestedStatus!);\n\n if (!opts.quiet) {\n stdout(`--- ${d.rawPath}\\n`);\n stdout(`+++ ${d.rawPath}\\n`);\n stdout(`@@ status change @@\\n`);\n // Show the old and new status line\n const oldLine = rawText.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n const newLine = updated.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n stdout(`-${oldLine}\\n`);\n stdout(`+${newLine}\\n`);\n }\n\n fs.writeFileSync(d.absPath, updated, 'utf8');\n }\n\n stdout(`audit-status: applied ${fixable.length} fix(es)\\n`);\n exit(0);\n}\n\n/**\n * Replace the first `status:` line inside the first `---` YAML front-matter block.\n * Everything else is byte-identical (no round-trip through parseFrontmatter).\n */\nfunction applyStatusFix(rawText: string, newStatus: string): string {\n // Find the closing --- of the frontmatter\n const lines = rawText.split('\\n');\n if (lines[0] !== '---') return rawText; // no frontmatter — leave untouched\n\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return rawText; // malformed — leave untouched\n\n // Replace only the first `status:` line within the frontmatter block\n let replaced = false;\n for (let i = 1; i < closeIdx; i++) {\n if (!replaced && /^status:/.test(lines[i])) {\n lines[i] = `status: \"${newStatus}\"`;\n replaced = true;\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * doctor.ts — STORY-009-04 + STORY-008-06\n *\n * `cleargate doctor` base command + `--check-scaffold` / `--session-start` / `--pricing` modes.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { computeUsd, type DraftTokensInput } from '../lib/pricing.js';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n readDriftState,\n type DriftMap,\n type DriftMapEntry,\n type DriftState,\n} from '../lib/manifest.js';\nimport { shortHash } from '../lib/sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface DoctorCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override the package root for loadPackageManifest (test seam). */\n packageRoot?: string;\n}\n\n/**\n * STORY-014-01: Accumulator passed by reference through all mode handlers.\n * Top-level doctorHandler reads at the end and exits per §3.2 pseudocode:\n * configError → exit(2), blocker → exit(1), else → exit(0).\n */\nexport interface DoctorOutcome {\n configError: boolean;\n blocker: boolean;\n}\n\n/**\n * Flags for `cleargate doctor`.\n *\n * Reserved keys for 008-06 (M3): sessionStart, pricing.\n * All flags are mutually exclusive — selectMode throws when >1 is set.\n */\nexport interface DoctorFlags {\n checkScaffold?: boolean;\n /** Hidden flag: used by the M3 session-start hook; enables daily throttle. */\n sessionStartMode?: boolean;\n verbose?: boolean;\n /** --session-start: emit blocked pending-sync items summary */\n sessionStart?: boolean;\n /** --pricing: compute USD estimate for a work item */\n pricing?: boolean;\n /** File path passed to --pricing <file> */\n pricingFile?: string;\n /** CR-008: --can-edit <file>: exits 0 if allowed, 1 if would-block */\n canEdit?: boolean;\n /** CR-008: the file path argument for --can-edit */\n canEditFile?: string;\n}\n\nexport type DoctorMode = 'check-scaffold' | 'session-start' | 'pricing' | 'hook-health' | 'can-edit';\n\n// ─── Mode dispatcher ──────────────────────────────────────────────────────────\n\n/**\n * Determine which doctor mode to run based on flags.\n *\n * Throws when multiple mutually-exclusive mode flags are set.\n * Returns 'hook-health' when no mode flag is set (default).\n *\n * Exported so STORY-008-06 can add cases without re-editing the switch.\n */\nexport function selectMode(flags: DoctorFlags): DoctorMode {\n const modes: DoctorMode[] = [];\n if (flags.checkScaffold) modes.push('check-scaffold');\n if (flags.sessionStart) modes.push('session-start');\n if (flags.pricing) modes.push('pricing');\n if (flags.canEdit) modes.push('can-edit');\n\n if (modes.length > 1) {\n throw new Error(\n `cleargate doctor: mutually exclusive flags set: ${modes.join(', ')}. Use only one mode flag at a time.`\n );\n }\n\n if (modes.length === 1) {\n return modes[0]!;\n }\n\n return 'hook-health';\n}\n\n// ─── Hook-health default mode ─────────────────────────────────────────────────\n\nconst HOOK_LOG_24H_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Parse a single gate-check.log line.\n * Format: [ISO_TS] stamp=N gate=N ingest=N file=<path>\n * Returns null if the line does not match.\n */\nexport interface HookLogEntry {\n ts: string;\n stamp: number;\n gate: number;\n ingest: number;\n file: string;\n}\n\nexport function parseHookLogLine(line: string): HookLogEntry | null {\n // [2026-04-19T12:00:00Z] stamp=0 gate=1 ingest=0 file=/some/path\n const m = line.match(\n /^\\[([^\\]]+)\\]\\s+stamp=(\\d+)\\s+gate=(\\d+)\\s+ingest=(\\d+)\\s+file=(.+)$/\n );\n if (!m) return null;\n return {\n ts: m[1]!,\n stamp: parseInt(m[2]!, 10),\n gate: parseInt(m[3]!, 10),\n ingest: parseInt(m[4]!, 10),\n file: m[5]!.trim(),\n };\n}\n\nfunction runHookHealth(\n stdout: (s: string) => void,\n cwd: string,\n now?: Date,\n outcome?: DoctorOutcome\n): void {\n // STORY-014-01: config-error — missing .cleargate/ directory\n const cleargateDir = path.join(cwd, '.cleargate');\n if (!fs.existsSync(cleargateDir)) {\n stdout('cleargate misconfigured: no .cleargate/ found. Run: cleargate init');\n if (outcome) outcome.configError = true;\n return;\n }\n\n // STORY-014-01: config-error — missing cleargate-planning/MANIFEST.json\n const manifestPath = path.join(cwd, 'cleargate-planning', 'MANIFEST.json');\n if (!fs.existsSync(manifestPath)) {\n stdout(`cleargate misconfigured: cleargate-planning/MANIFEST.json not found. Run: cleargate init`);\n if (outcome) outcome.configError = true;\n // Do not return — continue with remaining checks\n }\n\n // Minimal hook-config report: check that .claude/settings.json has the\n // SubagentStop hook wired (if the .claude directory exists).\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n if (!fs.existsSync(settingsPath)) {\n stdout('[doctor] No .claude/settings.json found — hook config unavailable.');\n return;\n }\n\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as unknown;\n const hasHooks =\n typeof settings === 'object' &&\n settings !== null &&\n 'hooks' in settings;\n if (hasHooks) {\n stdout('[doctor] Hook config present in .claude/settings.json.');\n } else {\n stdout('[doctor] .claude/settings.json found but no hooks key — SubagentStop hook not wired.');\n }\n } catch {\n stdout('[doctor] .claude/settings.json is not valid JSON — cannot verify hook config.');\n }\n\n // Scan gate-check.log for recent failures\n const logPath = path.join(cwd, '.cleargate', 'hook-log', 'gate-check.log');\n if (!fs.existsSync(logPath)) {\n return;\n }\n\n let logContent: string;\n try {\n logContent = fs.readFileSync(logPath, 'utf-8');\n } catch {\n return;\n }\n\n const nowMs = (now ?? new Date()).getTime();\n const lines = logContent.split('\\n').filter((l) => l.trim().length > 0);\n\n for (const line of lines) {\n const entry = parseHookLogLine(line);\n if (!entry) continue;\n\n const entryMs = new Date(entry.ts).getTime();\n if (isNaN(entryMs)) continue;\n\n // Only consider entries within the last 24h\n if (nowMs - entryMs > HOOK_LOG_24H_MS) continue;\n\n // A failure means ANY step exit code is non-zero\n const isFailing = entry.stamp !== 0 || entry.gate !== 0 || entry.ingest !== 0;\n if (!isFailing) continue;\n\n stdout(\n `\\u26a0 hook failure at ${entry.ts}: stamp=${entry.stamp} gate=${entry.gate} ingest=${entry.ingest} file=${entry.file}`\n );\n }\n}\n\n// ─── Check-scaffold mode ──────────────────────────────────────────────────────\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Throttle decision: returns true when the cache is fresh enough to skip\n * re-computation.\n *\n * Fresh = `now - lastRefreshed < 24h`.\n * Throttle only applies when `sessionStartMode` is set (non-interactive invocation).\n * Exported for unit tests.\n */\nexport function shouldUseCache(\n lastRefreshed: string,\n now: Date,\n sessionStartMode: boolean\n): boolean {\n if (!sessionStartMode) {\n // Interactive mode always recomputes.\n return false;\n }\n const age = now.getTime() - new Date(lastRefreshed).getTime();\n return age < TWENTY_FOUR_HOURS_MS;\n}\n\n/**\n * Format a single non-clean file line for verbose output.\n * `<path> <state> (<installed>→<current> vs <package>)`\n * with 6-char short hashes.\n */\nexport function formatVerboseLine(\n filePath: string,\n entry: DriftMapEntry\n): string {\n const inst = entry.install_sha ? shortHash(entry.install_sha).slice(0, 6) : 'null';\n const curr = entry.current_sha ? shortHash(entry.current_sha).slice(0, 6) : 'null';\n const pkg = entry.package_sha ? shortHash(entry.package_sha).slice(0, 6) : 'null';\n return ` ${filePath} ${entry.state} (${inst}→${curr} vs ${pkg})`;\n}\n\ntype CountsByState = Record<Exclude<DriftState, 'untracked'>, number> & { untracked: number };\n\nfunction zeroCounts(): CountsByState {\n return {\n 'clean': 0,\n 'user-modified': 0,\n 'upstream-changed': 0,\n 'both-changed': 0,\n 'untracked': 0,\n };\n}\n\nasync function runCheckScaffold(\n flags: DoctorFlags,\n cli: DoctorCliOptions,\n cwd: string,\n now: Date,\n stdout: (s: string) => void,\n _stderr: (s: string) => void\n): Promise<void> {\n // 1. Check daily throttle when in session-start mode\n const sessionStartMode = flags.sessionStartMode ?? false;\n const existingState = await readDriftState(cwd);\n\n if (existingState && shouldUseCache(existingState.last_refreshed, now, sessionStartMode)) {\n // Reuse cached result — emit summary from cached data\n emitSummary(existingState.drift, flags.verbose ?? false, stdout);\n return;\n }\n\n // 2. Load manifests\n const pkgManifest = loadPackageManifest({ packageRoot: cli.packageRoot });\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // 3. Compute SHAs + classify\n const driftMap: DriftMap = {};\n\n await Promise.all(\n pkgManifest.files.map(async (entry) => {\n // Silently skip user-artifact tier (EPIC-009 §6 Q8)\n if (entry.tier === 'user-artifact') {\n return;\n }\n\n const currentSha = await computeCurrentSha(entry, cwd);\n const installSha =\n installSnapshot?.files.find((f) => f.path === entry.path)?.sha256 ?? null;\n const pkgSha = entry.sha256;\n const state = classify(pkgSha, installSha, currentSha, entry.tier);\n\n driftMap[entry.path] = {\n state,\n entry,\n install_sha: installSha,\n current_sha: currentSha,\n package_sha: pkgSha,\n };\n })\n );\n\n // 4. Write drift state atomically\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n // 5. Emit summary\n emitSummary(driftMap, flags.verbose ?? false, stdout);\n}\n\nfunction emitSummary(\n driftMap: DriftMap,\n verbose: boolean,\n stdout: (s: string) => void\n): void {\n const counts = zeroCounts();\n for (const entry of Object.values(driftMap)) {\n counts[entry.state]++;\n }\n\n stdout(\n `Scaffold drift: ${counts['user-modified']} user-modified, ` +\n `${counts['upstream-changed']} upstream-changed, ` +\n `${counts['both-changed']} both-changed, ` +\n `${counts['clean']} clean`\n );\n\n if (counts['upstream-changed'] > 0 || counts['both-changed'] > 0) {\n stdout('Run cleargate upgrade to review.');\n }\n\n if (verbose) {\n for (const [filePath, entry] of Object.entries(driftMap)) {\n if (entry.state !== 'clean' && entry.state !== 'untracked') {\n stdout(formatVerboseLine(filePath, entry));\n }\n }\n }\n}\n\n// ─── Session-start mode ───────────────────────────────────────────────────────\n\nconst SESSION_START_MAX_ITEMS = 10;\nconst SESSION_START_MAX_CHARS = 400;\n\ninterface BlockedItem {\n id: string;\n firstCriterionId: string;\n}\n\n/**\n * Coerce `cached_gate_result` into a typed shape.\n * Accepts both the current native-object form (parseFrontmatter via js-yaml)\n * and the legacy JSON-in-a-string form (pre-BUG-001 files).\n */\nfunction parseCachedGateResult(\n raw: unknown\n): { pass: boolean | null; failing_criteria: Array<{ id: string }> } | null {\n if (raw == null) return null;\n\n let parsed: { pass?: boolean | null; failing_criteria?: Array<{ id: string }> } | null = null;\n\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n parsed = raw as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } else if (typeof raw === 'string') {\n try {\n parsed = JSON.parse(raw) as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } catch {\n return null;\n }\n } else {\n return null;\n }\n\n return {\n pass: parsed.pass ?? null,\n failing_criteria: parsed.failing_criteria ?? [],\n };\n}\n\n/**\n * CR-009: Probe the three-branch resolver chain at runtime and emit one\n * `cleargate CLI: <branch> — <one-line>` status line to stdout.\n * This line is emitted ALWAYS (success or failure) so Claude sees which resolver\n * the hooks will use before any hook fires.\n */\nexport function emitResolverStatusLine(\n cwd: string,\n stdout: (s: string) => void\n): void {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n if (fs.existsSync(distCliPath)) {\n stdout(`cleargate CLI: local dist — ${distCliPath}`);\n return;\n }\n\n // Check PATH\n const whichResult = spawnSync('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n stdout('cleargate CLI: PATH (global install) — cleargate');\n return;\n }\n\n // Try to read the pinned version from the live hook script\n let pinVersion = 'unknown';\n const hookPath = path.join(cwd, '.claude', 'hooks', 'stamp-and-gate.sh');\n if (fs.existsSync(hookPath)) {\n try {\n const hookContent = fs.readFileSync(hookPath, 'utf-8');\n // Pattern: # cleargate-pin: 0.5.0\n const pinMatch = hookContent.match(/^#\\s*cleargate-pin:\\s*(\\S+)\\s*$/m);\n if (pinMatch?.[1]) {\n pinVersion = pinMatch[1];\n } else {\n // Fallback: look for npx -y \"@cleargate/cli@X.Y.Z\"\n const npxMatch = hookContent.match(/@cleargate\\/cli@([^\\s\"']+)/);\n if (npxMatch?.[1]) pinVersion = npxMatch[1];\n }\n } catch {\n // ignore\n }\n }\n\n if (pinVersion === 'unknown') {\n stdout('cleargate CLI: \\u{1F534} not resolvable — hooks will no-op. Fix: npm i -g cleargate or npx cleargate doctor');\n } else {\n stdout(`cleargate CLI: npx @cleargate/cli@${pinVersion} (cold-start ~600ms first call)`);\n }\n}\n\n/**\n * CR-008: planning-first reminder block text.\n * Emitted when pending-sync has zero approved stories AND no sprint-active sentinel.\n */\nexport const PLANNING_FIRST_REMINDER = `Triage first, draft second:\nBefore any Edit/Write that creates user-facing code, you must:\n (1) classify the request (Epic / Story / CR / Bug),\n (2) draft a work item under .cleargate/delivery/pending-sync/ from .cleargate/templates/,\n (3) halt at Gate 1 (Proposal approval) for human sign-off.\nBypass this only if the user has explicitly waived planning in this conversation.`;\n\nexport async function runSessionStart(\n cwd: string,\n stdout: (s: string) => void,\n outcome?: DoctorOutcome\n): Promise<void> {\n // CR-009: emit resolver-status line ALWAYS (before the blocked-items list).\n // STORY-014-01: if resolver is \"not resolvable\", set configError.\n const resolverLines: string[] = [];\n emitResolverStatusLine(cwd, (line) => {\n stdout(line);\n resolverLines.push(line);\n });\n // Check if resolver completely failed (🔴 not resolvable branch)\n if (outcome && resolverLines.some((l) => l.includes('\\u{1F534}'))) {\n outcome.configError = true;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // Directory doesn't exist or unreadable — nothing to report\n return;\n }\n\n const blocked: BlockedItem[] = [];\n let hasApprovedStory = false;\n\n for (const filePath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n // CR-008: track approved stories for planning-first gate\n if (fm['approved'] === true) {\n hasApprovedStory = true;\n }\n\n const gate = parseCachedGateResult(fm['cached_gate_result']);\n if (!gate || gate.pass !== false) continue;\n\n // Determine item ID from frontmatter\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id', 'sprint_id'];\n let itemId = '';\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim()) {\n itemId = val.trim();\n break;\n }\n }\n if (!itemId) {\n // Fallback: use filename stem\n itemId = path.basename(filePath, '.md');\n }\n\n const firstCriterionId =\n gate.failing_criteria.length > 0 ? (gate.failing_criteria[0]?.id ?? '') : '';\n\n blocked.push({ id: itemId, firstCriterionId });\n }\n\n // CR-008: check sprint-active sentinel\n const activesentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n const sprintActive = fs.existsSync(activesentinel);\n\n // CR-008: emit planning-first reminder when no approved stories AND no active sprint\n const shouldRemind = !hasApprovedStory && !sprintActive;\n if (shouldRemind) {\n stdout(PLANNING_FIRST_REMINDER);\n if (blocked.length > 0) {\n // Separator before blocked items\n stdout('');\n }\n }\n\n if (blocked.length === 0) {\n return;\n }\n\n // STORY-014-01: blocked items present → set blocker flag\n if (outcome) outcome.blocker = true;\n\n const overflow = blocked.length > SESSION_START_MAX_ITEMS\n ? blocked.length - SESSION_START_MAX_ITEMS\n : 0;\n const visible = blocked.slice(0, SESSION_START_MAX_ITEMS);\n\n const lines: string[] = [`${blocked.length} items blocked:`];\n for (const item of visible) {\n const line = item.firstCriterionId\n ? ` ${item.id}: ${item.firstCriterionId}`\n : ` ${item.id}`;\n lines.push(line);\n }\n if (overflow > 0) {\n lines.push(`…and ${overflow} more — run cleargate doctor for full list`);\n }\n\n let output = lines.join('\\n');\n\n // Cap at SESSION_START_MAX_CHARS (100-token proxy)\n if (output.length > SESSION_START_MAX_CHARS) {\n output = output.slice(0, SESSION_START_MAX_CHARS - 3) + '...';\n }\n\n stdout(output);\n}\n\n// ─── Pricing mode ─────────────────────────────────────────────────────────────\n\nexport async function runPricing(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n if (!filePath) {\n // STORY-014-01: missing <file> argument is a config/input error → exit(2)\n stderr('cleargate doctor --pricing: missing <file> argument');\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);\n\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf-8');\n } catch {\n // STORY-014-01: cannot read file is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot read file: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n if (!raw.trimStart().startsWith('---')) {\n // STORY-014-01: file has no frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: file has no frontmatter: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n // STORY-014-01: cannot parse frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot parse frontmatter in: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const draftTokensRaw = fm['draft_tokens'];\n if (!draftTokensRaw) {\n // STORY-014-01: draft_tokens unpopulated is a blocker (content exists, state incomplete) → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let draftTokens: DraftTokensInput & { model: string | null };\n if (typeof draftTokensRaw === 'object' && !Array.isArray(draftTokensRaw)) {\n draftTokens = draftTokensRaw as DraftTokensInput & { model: string | null };\n } else if (typeof draftTokensRaw === 'string') {\n try {\n draftTokens = JSON.parse(draftTokensRaw) as DraftTokensInput & { model: string | null };\n } catch {\n // STORY-014-01: unparseable draft_tokens is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n } else {\n // STORY-014-01: unexpected type is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n // Check if tokens are actually populated\n if (\n draftTokens.input === null &&\n draftTokens.output === null &&\n draftTokens.cache_read === null &&\n draftTokens.cache_creation === null\n ) {\n // STORY-014-01: all null values → blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n const { usd, unknownModel } = computeUsd(draftTokens);\n const model = draftTokens.model ?? 'unknown';\n\n if (unknownModel) {\n stderr(`cleargate doctor --pricing: unknown model '${model}' — no pricing data available`);\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n const fileName = path.basename(absPath);\n\n stdout(\n `${fileName}: ${model} — input:${input} output:${output} cache_read:${cacheRead} cache_creation:${cacheCreation} ≈ $${usd.toFixed(4)}`\n );\n}\n\n// ─── Can-edit mode (CR-008 Phase B) ──────────────────────────────────────────\n\n/**\n * CR-008: reasons why an edit would be blocked.\n */\nexport type CanEditBlockReason = 'no_approved_stories' | 'file_not_in_implementation_files';\n\n/**\n * CR-008: result of the can-edit check.\n */\nexport interface CanEditResult {\n allowed: boolean;\n reason?: CanEditBlockReason;\n}\n\n/**\n * CR-008: simple glob-style match.\n * Supports `*` (any characters except `/`) and `**` (any characters including `/`).\n */\nexport function globMatch(pattern: string, filePath: string): boolean {\n // Normalise separators\n const normalPattern = pattern.replace(/\\\\/g, '/');\n const normalFile = filePath.replace(/\\\\/g, '/');\n\n // Escape regex specials except * and ?\n const regexStr = normalPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '\u0000') // placeholder for **\n .replace(/\\*/g, '[^/]*')\n .replace(/\u0000/g, '.*');\n\n const re = new RegExp(`^${regexStr}$`);\n return re.test(normalFile);\n}\n\n/**\n * CR-008: check whether editing `filePath` is permitted.\n *\n * Logic:\n * 1. If sprint-active sentinel exists → always allowed.\n * 2. Read pending-sync/*.md; for each with approved: true:\n * a. If no implementation_files field → treat as \"any approved story → allow\".\n * b. If implementation_files present → glob-match filePath against each pattern.\n * 3. If zero approved stories → block with reason 'no_approved_stories'.\n * 4. If approved stories exist but filePath not covered → block with 'file_not_in_implementation_files'.\n */\nexport async function runCanEdit(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n // Sprint-active sentinel → always allow\n const activeSentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n if (fs.existsSync(activeSentinel)) {\n stdout('allowed: sprint active');\n return;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // No pending-sync dir → no approved stories → blocker → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let hasApprovedStory = false;\n let coveredByStory = false;\n\n for (const storyPath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(storyPath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n if (fm['approved'] !== true) continue;\n\n hasApprovedStory = true;\n\n const implFilesRaw = fm['implementation_files'];\n if (implFilesRaw === undefined || implFilesRaw === null) {\n // No implementation_files field → any approved story covers any file\n coveredByStory = true;\n break;\n }\n\n if (Array.isArray(implFilesRaw)) {\n for (const pattern of implFilesRaw) {\n if (typeof pattern !== 'string') continue;\n if (globMatch(pattern, filePath)) {\n coveredByStory = true;\n break;\n }\n }\n }\n\n if (coveredByStory) break;\n }\n\n if (!hasApprovedStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n if (!coveredByStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: file_not_in_implementation_files');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n stdout('allowed');\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function doctorHandler(\n flags: DoctorFlags,\n cli?: DoctorCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n\n // STORY-014-01: outcome accumulator — each mode pushes booleans here.\n // At the end, we exit per §3.2 pseudocode:\n // configError → exit(2), blocker → exit(1), else → exit(0).\n const outcome: DoctorOutcome = { configError: false, blocker: false };\n\n // Track whether exit() was already called by a mode (e.g. runPricing early error)\n // so we don't double-exit from the final outcome block.\n let exitedEarly = false;\n const wrappedExit = (code: number): never => {\n exitedEarly = true;\n return exit(code);\n };\n\n let mode: DoctorMode;\n try {\n mode = selectMode(flags);\n } catch (err) {\n // STORY-014-01: mutually exclusive flags is a config error → exit(2)\n stderr((err as Error).message);\n exit(2);\n return;\n }\n\n switch (mode) {\n case 'check-scaffold':\n await runCheckScaffold(flags, cli ?? {}, cwd, now, stdout, stderr);\n break;\n\n case 'hook-health':\n runHookHealth(stdout, cwd, now, outcome);\n break;\n\n case 'session-start':\n await runSessionStart(cwd, stdout, outcome);\n break;\n\n case 'pricing':\n await runPricing(flags.pricingFile ?? '', cwd, stdout, stderr, wrappedExit, outcome);\n break;\n\n case 'can-edit':\n await runCanEdit(flags.canEditFile ?? '', cwd, stdout, wrappedExit, outcome);\n break;\n\n default: {\n const exhaustiveCheck: never = mode;\n stderr(`cleargate doctor: unknown mode '${String(exhaustiveCheck)}'`);\n // STORY-014-01: unknown mode is a config error → exit(2)\n exit(2);\n return;\n }\n }\n\n // STORY-014-01: §3.2 pseudocode exit-code computation.\n // If a mode called exit() early (e.g. runPricing on bad input), don't double-exit.\n if (exitedEarly) return;\n\n if (outcome.configError) {\n exit(2);\n } else if (outcome.blocker) {\n exit(1);\n } else {\n exit(0);\n }\n}\n","/**\n * pricing.ts — STORY-008-06\n *\n * USD pricing table for Claude models (per 1M tokens).\n * Numbers from Anthropic public pricing as of 2026-04-19.\n * No network. No config file. Numbers live in source.\n */\n\nexport interface ModelPricing {\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n}\n\n/**\n * Pricing table: USD per 1,000,000 tokens.\n *\n * claude-opus-4-7: $15 input / $75 output / $1.50 cache_read / $18.75 cache_creation\n * claude-sonnet-4-5: $3 input / $15 output / $0.30 cache_read / $3.75 cache_creation\n * claude-haiku-4-5: $0.80 input / $4 output / $0.08 cache_read / $1 cache_creation\n */\nexport const PRICING_TABLE: Record<string, ModelPricing> = {\n 'claude-opus-4-7': {\n input: 15.0,\n output: 75.0,\n cache_read: 1.5,\n cache_creation: 18.75,\n },\n 'claude-sonnet-4-5': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-sonnet-4-6': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-haiku-4-5': {\n input: 0.8,\n output: 4.0,\n cache_read: 0.08,\n cache_creation: 1.0,\n },\n};\n\nexport interface DraftTokensInput {\n input: number | null;\n output: number | null;\n cache_read: number | null;\n cache_creation: number | null;\n model: string | null;\n}\n\nexport interface ComputeUsdResult {\n usd: number;\n unknownModel: boolean;\n}\n\n/**\n * Compute USD cost from draft_tokens and model.\n *\n * If modelOverride is provided, it takes precedence over draftTokens.model.\n * Unknown model → {usd: 0, unknownModel: true}.\n * All token counts default to 0 if null.\n */\nexport function computeUsd(\n draftTokens: DraftTokensInput,\n modelOverride?: string\n): ComputeUsdResult {\n const model = modelOverride ?? draftTokens.model ?? '';\n const pricing = PRICING_TABLE[model];\n\n if (!pricing) {\n return { usd: 0, unknownModel: true };\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n\n const usd =\n (input * pricing.input +\n output * pricing.output +\n cacheRead * pricing.cache_read +\n cacheCreation * pricing.cache_creation) /\n 1_000_000;\n\n return { usd, unknownModel: false };\n}\n","/**\n * gate.ts — `cleargate gate check|explain|qa|arch` command handlers.\n *\n * STORY-008-03: Wires readiness-predicates.evaluate() + frontmatter-cache into\n * two Commander subcommands (check + explain).\n *\n * STORY-013-08: Extends with gate qa|arch subcommands that shell out via\n * run_script.sh to pre_gate_runner.sh. Both are v1-inert.\n *\n * FLASHCARD #cli #commander #optional-key: opts.transition may be undefined — strip key.\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout`, `stderr` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * Output is agent-facing: only ❌ / ⚠ / ✅ emoji; no ANSI color codes.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { evaluate } from '../lib/readiness-predicates.js';\nimport type { ParsedDoc } from '../lib/readiness-predicates.js';\nimport { readCachedGate, writeCachedGate } from '../lib/frontmatter-cache.js';\nimport type { CachedGate } from '../lib/frontmatter-cache.js';\nimport {\n detectWorkItemTypeFromFm,\n WORK_ITEM_TRANSITIONS,\n} from '../lib/work-item-type.js';\nimport type { WorkItemType } from '../lib/work-item-type.js';\nimport { toIsoSecond } from '../lib/frontmatter-yaml.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to readiness-gates.md (test seam). */\n gatesDocPath?: string;\n /** Override path to wiki index (test seam). */\n wikiIndexPath?: string;\n}\n\n// ─── Internal gate-block shape ────────────────────────────────────────────────\n\ninterface GateCriterion {\n id: string;\n check: string;\n}\n\ninterface GateBlock {\n work_item_type: string;\n transition: string;\n severity: 'advisory' | 'enforcing';\n criteria: GateCriterion[];\n}\n\n// ─── Gate document loader ─────────────────────────────────────────────────────\n\n/**\n * Load and parse all fenced ```yaml blocks from readiness-gates.md.\n * Each block's yaml.load() returns an array — unwrap [0] per FLASHCARD.\n */\nfunction loadGateBlocks(gatesDocPath: string): GateBlock[] {\n const raw = fs.readFileSync(gatesDocPath, 'utf8');\n const blocks: GateBlock[] = [];\n\n // Match all fenced ```yaml ... ``` blocks\n const fenceRe = /^```yaml\\n([\\s\\S]*?)^```/gm;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(raw)) !== null) {\n const yamlContent = match[1]!;\n const parsed = yaml.load(yamlContent);\n // Per FLASHCARD: readiness-gates.md fenced yaml blocks are YAML lists; unwrap [0]\n const block = Array.isArray(parsed) ? parsed[0] : parsed;\n if (\n block &&\n typeof block === 'object' &&\n 'work_item_type' in block &&\n 'transition' in block &&\n 'severity' in block &&\n 'criteria' in block\n ) {\n blocks.push(block as GateBlock);\n }\n }\n return blocks;\n}\n\n/**\n * Find the gate block matching a work-item type + transition.\n */\nfunction findGate(\n blocks: GateBlock[],\n type: WorkItemType,\n transition: string,\n): GateBlock | null {\n return blocks.find(\n (b) => b.work_item_type === type && b.transition === transition,\n ) ?? null;\n}\n\n/**\n * Infer the default transition for a work-item type given the current cached gate state.\n * - If cached_gate_result is absent or failing → return first transition.\n * - If cached_gate_result.pass === true and there's a next transition → return next.\n * - Otherwise return first transition.\n */\nfunction inferTransition(\n type: WorkItemType,\n cachedGate: CachedGate | null,\n): string {\n const transitions = WORK_ITEM_TRANSITIONS[type];\n if (!cachedGate || !cachedGate.pass) {\n return transitions[0]!;\n }\n // Find next unpassed transition\n // We don't know which transition was last checked from cache alone;\n // for Epic: if cached pass=true, assume first is done → pick second.\n // For types with only one transition: always return that one.\n if (transitions.length === 1) {\n return transitions[0]!;\n }\n // Multi-transition (Epic): if cached gate passes, infer next\n return transitions[1]!;\n}\n\n// ─── gateCheckHandler ─────────────────────────────────────────────────────────\n\nexport async function gateCheckHandler(\n file: string,\n opts: { verbose?: boolean; transition?: string },\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n const nowFn = cli?.now ?? (() => new Date());\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Parse the document\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch (err) {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Detect work-item type from frontmatter\n const detectedType = detectWorkItemTypeFromFm(fm);\n if (!detectedType) {\n stderrFn(`[cleargate gate] error: unable to detect work-item type from frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Load gates document\n const projectRoot = cwd;\n const gatesDocPath = cli?.gatesDocPath\n ?? path.join(projectRoot, '.cleargate', 'knowledge', 'readiness-gates.md');\n\n if (!fs.existsSync(gatesDocPath)) {\n stderrFn(`[cleargate gate] error: readiness-gates.md not found at: ${gatesDocPath}`);\n return exitFn(1);\n }\n\n let gateBlocks: GateBlock[];\n try {\n gateBlocks = loadGateBlocks(gatesDocPath);\n } catch (err) {\n stderrFn(`[cleargate gate] error: failed to parse readiness-gates.md: ${String(err)}`);\n return exitFn(1);\n }\n\n // Read current cached gate for transition inference\n const cachedGate = await readCachedGate(absPath);\n\n // Determine transition\n const transition = opts.transition ?? inferTransition(detectedType, cachedGate);\n\n // Find the matching gate\n const gate = findGate(gateBlocks, detectedType, transition);\n if (!gate) {\n stderrFn(\n `[cleargate gate] error: no gate definition found for ${detectedType}.${transition}`,\n );\n return exitFn(1);\n }\n\n const wikiIndexPath = cli?.wikiIndexPath;\n const parsedDoc: ParsedDoc = { fm, body, absPath };\n const evalOpts = { projectRoot, ...(wikiIndexPath ? { wikiIndexPath } : {}) };\n\n // Evaluate each criterion\n const failingCriteria: { id: string; detail: string }[] = [];\n const allResults: Array<{ id: string; pass: boolean; detail: string }> = [];\n\n for (const criterion of gate.criteria) {\n let result: { pass: boolean; detail: string };\n try {\n result = evaluate(criterion.check, parsedDoc, evalOpts);\n } catch (err) {\n result = { pass: false, detail: `predicate error: ${String(err)}` };\n }\n allResults.push({ id: criterion.id, ...result });\n if (!result.pass) {\n failingCriteria.push({ id: criterion.id, detail: result.detail });\n }\n }\n\n const overallPass = failingCriteria.length === 0;\n const lastGateCheck = toIsoSecond(nowFn());\n\n // Write cached gate result\n const cacheResult: CachedGate = {\n pass: overallPass,\n failing_criteria: failingCriteria,\n last_gate_check: lastGateCheck,\n };\n await writeCachedGate(absPath, cacheResult, { now: nowFn });\n\n // Format and emit output\n const isAdvisory = gate.severity === 'advisory';\n const headerLine = `Gate: ${detectedType}.${transition} (${gate.severity})`;\n stdoutFn(headerLine);\n\n if (overallPass) {\n stdoutFn(`\\u2705 ${detectedType}.${transition} passed (${gate.criteria.length} criteria)`);\n } else {\n for (const r of allResults) {\n if (!r.pass) {\n if (isAdvisory) {\n stdoutFn(`\\u26A0 ${r.id}: ${r.detail} (advisory)`);\n } else {\n stdoutFn(`\\u274C ${r.id}: ${r.detail}`);\n }\n }\n if (opts.verbose) {\n // In verbose mode, emit full detail per criterion\n stdoutFn(` [${r.pass ? 'pass' : 'fail'}] ${r.id}: ${r.detail}`);\n }\n }\n }\n\n // Severity-based exit routing\n if (!overallPass && !isAdvisory) {\n return exitFn(1);\n }\n // advisory or pass → exit 0 (implicit return)\n}\n\n// ─── gateExplainHandler ───────────────────────────────────────────────────────\n\nexport async function gateExplainHandler(\n file: string,\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Read cached gate result — read-only, no evaluate calls\n const cached = await readCachedGate(absPath);\n\n if (!cached) {\n stdoutFn('no gate check cached; run: cleargate gate check <file>');\n return;\n }\n\n // Parse frontmatter to get type info (read-only — no writes)\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n const detectedType = detectWorkItemTypeFromFm(fm) ?? 'unknown';\n\n // Render ≤50-LLM-token summary\n const failingIds = cached.failing_criteria.map((c) => c.id).join(', ');\n const statusStr = cached.pass ? 'pass' : 'fail';\n const summary = failingIds\n ? `${detectedType}: ${statusStr} at ${cached.last_gate_check}; ${cached.failing_criteria.length} failing: ${failingIds}`\n : `${detectedType}: ${statusStr} at ${cached.last_gate_check}`;\n\n stdoutFn(summary);\n}\n\n// ─── v2 gate qa|arch handlers ─────────────────────────────────────────────────\n\n/**\n * Options for v2 gate subcommands (qa + arch).\n * Extends GateCliOptions with execution-mode seams.\n */\nexport interface GateV2CliOptions extends GateCliOptions, ExecutionModeOptions {\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Sprint ID for execution_mode discovery. */\n sprintId?: string;\n}\n\nfunction resolveRunScriptForGate(opts: GateV2CliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * `cleargate gate qa <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh qa <worktree> <branch>`\n */\nexport function gateQaHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'qa', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate qa] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n/**\n * `cleargate gate arch <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh arch <worktree> <branch>`\n */\nexport function gateArchHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'arch', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate arch] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * execution-mode.ts — reads `execution_mode` from a Sprint Plan frontmatter.\n *\n * STORY-013-08: provides a test seam via `sprintFilePath` override so tests\n * can inject synthetic SPRINT-99.md fixture without touching live state.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests — keep exitFn\n * only at handler top-level (not inside helper functions).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nconst V1_INERT_MESSAGE =\n 'v1 mode active — command inert. Set execution_mode: v2 in sprint frontmatter to enable.';\n\nexport type ExecutionMode = 'v1' | 'v2';\n\nexport interface ExecutionModeOptions {\n /** Absolute path to the sprint file. Overrides auto-discovery. */\n sprintFilePath?: string;\n /** Working directory for relative-path resolution. Defaults to process.cwd(). */\n cwd?: string;\n /**\n * When true and sprintId is absent or 'SPRINT-UNKNOWN', read\n * `.cleargate/sprint-runs/.active` for the sprint ID before falling through\n * to v1-inert. Callers can also use `resolveSprintIdFromSentinel` directly.\n */\n sentinelFallback?: boolean;\n}\n\n/**\n * Parse just the YAML frontmatter from a markdown file.\n * Returns the raw frontmatter block as a plain object.\n * On any parse failure, returns an empty object.\n */\nfunction parseFrontmatterSimple(raw: string): Record<string, unknown> {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(raw);\n if (!match) return {};\n const block = match[1]!;\n const result: Record<string, unknown> = {};\n for (const line of block.split('\\n')) {\n const kv = /^([^:]+):\\s*(.*)$/.exec(line.trim());\n if (!kv) continue;\n const key = kv[1]!.trim();\n const val = kv[2]!.trim().replace(/^[\"']|[\"']$/g, '');\n result[key] = val;\n }\n return result;\n}\n\n/**\n * Discover the sprint file for a given sprint ID.\n * Looks in `.cleargate/delivery/pending-sync/SPRINT-{id}_*.md`\n * and `.cleargate/delivery/archive/SPRINT-{id}_*.md`.\n *\n * Returns null if no matching file is found.\n */\nfunction discoverSprintFile(sprintId: string, cwd: string): string | null {\n const searchDirs = [\n path.join(cwd, '.cleargate', 'delivery', 'pending-sync'),\n path.join(cwd, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of searchDirs) {\n if (!fs.existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n const prefix = `${sprintId}_`;\n for (const entry of entries) {\n if (entry.startsWith(prefix) && entry.endsWith('.md')) {\n return path.join(dir, entry);\n }\n // Also allow exact match like SPRINT-99.md (test fixtures)\n if (entry === `${sprintId}.md`) {\n return path.join(dir, entry);\n }\n }\n }\n\n return null;\n}\n\n/**\n * Read the active sprint ID from `.cleargate/sprint-runs/.active`.\n * Returns null if the file does not exist or is empty after trim.\n *\n * This is the primary API for sentinel-based sprint discovery. Callers that\n * need a fallback chain use:\n * const sprintId = argSprintId ?? resolveSprintIdFromSentinel(cwd);\n * const mode = readSprintExecutionMode(sprintId ?? 'SPRINT-UNKNOWN', { cwd });\n */\nexport function resolveSprintIdFromSentinel(cwd?: string): string | null {\n const resolvedCwd = cwd ?? process.cwd();\n const sentinelPath = path.join(resolvedCwd, '.cleargate', 'sprint-runs', '.active');\n try {\n const content = fs.readFileSync(sentinelPath, 'utf8').trim();\n return content.length > 0 ? content : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read the `execution_mode` field from a sprint file.\n *\n * Resolution order:\n * 1. If `opts.sprintFilePath` is set, use that directly.\n * 2. Otherwise, discover the file by sprintId in `.cleargate/delivery/`.\n * 3. If `opts.sentinelFallback` is true and sprintId is absent or\n * 'SPRINT-UNKNOWN', read `.cleargate/sprint-runs/.active` and substitute.\n * 4. If no file found, return \"v1\" (safe default per §19.5).\n */\nexport function readSprintExecutionMode(\n sprintId: string,\n opts: ExecutionModeOptions = {},\n): ExecutionMode {\n const cwd = opts.cwd ?? process.cwd();\n\n // Sentinel fallback: when sprintId is absent or unknown, try .active\n let resolvedSprintId = sprintId;\n if (opts.sentinelFallback && (!resolvedSprintId || resolvedSprintId === 'SPRINT-UNKNOWN')) {\n const sentinelId = resolveSprintIdFromSentinel(cwd);\n if (sentinelId) {\n resolvedSprintId = sentinelId;\n }\n }\n\n let filePath: string | null = opts.sprintFilePath ?? null;\n if (!filePath) {\n filePath = discoverSprintFile(resolvedSprintId, cwd);\n }\n\n if (!filePath || !fs.existsSync(filePath)) {\n // Default to v1 — safe, no behavioral change (§19.5)\n return 'v1';\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf8');\n } catch {\n return 'v1';\n }\n\n const fm = parseFrontmatterSimple(raw);\n const mode = fm['execution_mode'];\n\n if (mode === 'v2') return 'v2';\n return 'v1';\n}\n\n/**\n * Print the v1-inert message and exit 0.\n * The caller is responsible for calling this when execution_mode is v1.\n */\nexport function printInertAndExit(\n stdoutFn: (s: string) => void,\n exitFn: (code: number) => never,\n): never {\n stdoutFn(V1_INERT_MESSAGE);\n return exitFn(0);\n}\n\nexport { V1_INERT_MESSAGE };\n","/**\n * STORY-008-02: Predicate evaluator for ClearGate readiness gates.\n * Supports exactly 6 closed-set predicate shapes. Any other shape throws.\n * Sandboxed: no shell-out, no network, read-only FS limited to projectRoot.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ParsedPredicate =\n | { kind: 'frontmatter'; ref: string; field: string; op: '==' | '!=' | '>=' | '<='; value: string | number | boolean }\n | { kind: 'body-contains'; needle: string; negated: boolean }\n | { kind: 'marker-absence'; marker: 'TBD' | 'TODO' | 'FIXME' }\n | { kind: 'section'; index: number; count: { op: '>=' | '==' | '>'; n: number }; itemType: 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item' }\n | { kind: 'file-exists'; path: string }\n | { kind: 'link-target-exists'; id: string }\n | { kind: 'status-of'; id: string; value: string };\n\nexport interface ParsedDoc {\n fm: Record<string, unknown>;\n body: string;\n absPath: string;\n}\n\nexport interface EvalOptions {\n projectRoot?: string;\n wikiIndexPath?: string;\n}\n\n// ─── Parser ───────────────────────────────────────────────────────────────────\n\n/**\n * Parse a predicate string into a typed ParsedPredicate.\n * Throws with \"unsupported predicate shape: <src>\" on any unrecognized input.\n * Target: ≤150 LoC, hand-rolled tokenizer + switch on first token.\n */\nexport function parsePredicate(src: string): ParsedPredicate {\n const s = src.trim();\n\n // 1. frontmatter(<ref>).<field> <op> <value>\n const fmMatch = s.match(\n /^frontmatter\\(([^)]*)\\)\\.(\\w+)\\s*(==|!=|>=|<=)\\s*(.+)$/\n );\n if (fmMatch) {\n const ref = fmMatch[1]!.trim();\n if (ref === '') throw new Error(`unsupported predicate shape: ${src}`);\n const field = fmMatch[2]!;\n const op = fmMatch[3] as '==' | '!=' | '>=' | '<=';\n const rawVal = fmMatch[4]!.trim();\n const value = parseValue(rawVal);\n return { kind: 'frontmatter', ref, field, op, value };\n }\n\n // 2a. body does not contain marker '<id>' — new marker-absence shape\n const markerNotMatch = s.match(/^body does not contain marker ['\"]([A-Z]+)['\"]$/);\n if (markerNotMatch) {\n const marker = markerNotMatch[1]!;\n if (marker !== 'TBD' && marker !== 'TODO' && marker !== 'FIXME') {\n throw new Error(`unsupported predicate shape: ${src}`);\n }\n return { kind: 'marker-absence', marker };\n }\n\n // 2b. body does not contain '<needle>'\n const bodyNotMatch = s.match(/^body does not contain ['\"](.+)['\"]$/);\n if (bodyNotMatch) {\n return { kind: 'body-contains', needle: bodyNotMatch[1]!, negated: true };\n }\n\n // 2c. body contains '<needle>'\n const bodyMatch = s.match(/^body contains ['\"](.+)['\"]$/);\n if (bodyMatch) {\n return { kind: 'body-contains', needle: bodyMatch[1]!, negated: false };\n }\n\n // 3. section(<N>) has <count> <item-type>\n const sectionMatch = s.match(\n /^section\\((\\d+)\\) has (≥|>=|==|>)(\\d+) (checked-checkbox|unchecked-checkbox|listed-item)$/\n );\n if (sectionMatch) {\n const index = parseInt(sectionMatch[1]!, 10);\n const opChar = sectionMatch[2]!;\n const n = parseInt(sectionMatch[3]!, 10);\n const itemType = sectionMatch[4] as 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item';\n let countOp: '>=' | '==' | '>';\n if (opChar === '≥' || opChar === '>=') countOp = '>=';\n else if (opChar === '>') countOp = '>';\n else countOp = '==';\n return { kind: 'section', index, count: { op: countOp, n }, itemType };\n }\n\n // 4. file-exists(<path>)\n const fileExistsMatch = s.match(/^file-exists\\((.+)\\)$/);\n if (fileExistsMatch) {\n const filePath = fileExistsMatch[1]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'file-exists', path: filePath };\n }\n\n // 5. link-target-exists([[ID]])\n const linkMatch = s.match(/^link-target-exists\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)$/);\n if (linkMatch) {\n return { kind: 'link-target-exists', id: linkMatch[1]! };\n }\n\n // 6. status-of([[ID]]) == <value>\n const statusMatch = s.match(/^status-of\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)\\s*==\\s*(.+)$/);\n if (statusMatch) {\n const id = statusMatch[1]!;\n const value = statusMatch[2]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'status-of', id, value };\n }\n\n throw new Error(`unsupported predicate shape: ${src}`);\n}\n\n/** Parse a YAML scalar value string to string | number | boolean. */\nfunction parseValue(raw: string): string | number | boolean {\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n if (raw === 'null') return 'null'; // treat null as string \"null\" for comparison\n const num = Number(raw);\n if (!isNaN(num) && raw !== '') return num;\n // Strip quotes\n return raw.replace(/^['\"]|['\"]$/g, '');\n}\n\n// ─── Evaluator ────────────────────────────────────────────────────────────────\n\n/**\n * Evaluate a predicate string against a document. Returns {pass, detail}.\n * Throws on malformed predicate (delegate to parsePredicate).\n */\nexport function evaluate(\n predicate: string,\n doc: ParsedDoc,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const parsed = parsePredicate(predicate);\n const projectRoot = opts?.projectRoot ?? process.cwd();\n\n switch (parsed.kind) {\n case 'frontmatter':\n return evalFrontmatter(parsed, doc, projectRoot);\n case 'body-contains':\n return evalBodyContains(parsed, doc);\n case 'marker-absence':\n return evalMarkerAbsence(parsed, doc);\n case 'section':\n return evalSection(parsed, doc);\n case 'file-exists':\n return evalFileExists(parsed, projectRoot);\n case 'link-target-exists':\n return evalLinkTargetExists(parsed, opts);\n case 'status-of':\n return evalStatusOf(parsed, opts, projectRoot);\n }\n}\n\n// ─── Frontmatter evaluator ────────────────────────────────────────────────────\n\nfunction evalFrontmatter(\n parsed: Extract<ParsedPredicate, { kind: 'frontmatter' }>,\n doc: ParsedDoc,\n projectRoot: string\n): { pass: boolean; detail: string } {\n let fm: Record<string, unknown>;\n\n if (parsed.ref === '.') {\n fm = doc.fm;\n } else {\n // ref is a frontmatter key whose value is a path to another document\n const refVal = doc.fm[parsed.ref];\n if (refVal === undefined || refVal === null) {\n return {\n pass: false,\n detail: `frontmatter key '${parsed.ref}' is missing or null in ${doc.absPath}`,\n };\n }\n\n // Sub-fix #1 (BUG-008): prose-vs-path heuristic.\n // If the value looks like prose (contains a space, em-dash, en-dash, colon, parens,\n // newline, or exceeds 200 chars), it is not a file path. In that case, pass the gate\n // only when the parent document declares an explicit proposal-gate waiver via any of:\n // - proposal_gate_waiver: <truthy> — explicit opt-in waiver field\n // - approved_by: <non-empty> AND approved_at: <non-empty> — existing approval fields\n // A plain path like \"PROPOSAL-999.md\" (no spaces, ≤200 chars, no special prose chars)\n // still falls through to the existing resolveLinkedPath logic — preserving the R-08\n // regression guarantee that broken file references still fail.\n const refStr = String(refVal);\n const looksLikeProse =\n refStr.length > 200 ||\n /[ —–:()\\n]/.test(refStr); // space, em-dash, en-dash, colon, parens, newline\n if (looksLikeProse) {\n // Signal 1: explicit proposal_gate_waiver field\n const waiver = doc.fm['proposal_gate_waiver'];\n const hasExplicitWaiver =\n waiver !== null && waiver !== undefined && waiver !== false &&\n String(waiver).trim() !== '' && String(waiver).trim() !== 'false';\n // Signal 2: approved_by + approved_at both set (existing approval fields)\n const approvedBy = doc.fm['approved_by'];\n const approvedAt = doc.fm['approved_at'];\n const hasApprovalFields =\n approvedBy !== null && approvedBy !== undefined && String(approvedBy).trim() !== '' &&\n approvedAt !== null && approvedAt !== undefined && String(approvedAt).trim() !== '';\n const hasWaiver = hasExplicitWaiver || hasApprovalFields;\n if (hasWaiver) {\n return {\n pass: true,\n detail: `context_source is prose; proposal-gate waiver per frontmatter approved_by/approved_at`,\n };\n }\n return {\n pass: false,\n detail: `context_source is prose but no proposal_gate_waiver (approved_by + approved_at) found in frontmatter`,\n };\n }\n\n // Resolve the path\n const linkedPath = resolveLinkedPath(String(refVal), doc.absPath, projectRoot);\n if (!linkedPath) {\n return {\n pass: false,\n detail: `linked file not found: ${refVal}`,\n };\n }\n fm = readFrontmatterFromFile(linkedPath);\n }\n\n const actual = fm[parsed.field];\n\n // Compare\n const pass = compareValues(actual, parsed.op, parsed.value);\n const detail = pass\n ? `frontmatter(${parsed.ref}).${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)} → actual: ${JSON.stringify(actual)}`\n : `expected ${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)}, got ${JSON.stringify(actual)}`;\n\n return { pass, detail };\n}\n\nfunction compareValues(\n actual: unknown,\n op: '==' | '!=' | '>=' | '<=',\n expected: string | number | boolean\n): boolean {\n // null check for != null\n if (expected === 'null') {\n const isNull = actual === null || actual === undefined || actual === '' || actual === 'null';\n return op === '==' ? isNull : !isNull;\n }\n // Normalize actual: strip quotes\n let a: unknown = actual;\n if (typeof a === 'string') {\n a = a.replace(/^[\"']|[\"']$/g, '');\n // Try to coerce to bool/number for comparison\n if (a === 'true') a = true;\n else if (a === 'false') a = false;\n else {\n const n = Number(a);\n if (!isNaN(n) && (a as string) !== '') a = n;\n }\n }\n\n switch (op) {\n case '==': return a === expected || String(a) === String(expected);\n case '!=': return a !== expected && String(a) !== String(expected);\n case '>=': return Number(a) >= Number(expected);\n case '<=': return Number(a) <= Number(expected);\n }\n}\n\n/** Resolve a path reference relative to the document or project root. */\nfunction resolveLinkedPath(\n ref: string,\n docAbsPath: string,\n projectRoot: string\n): string | null {\n // Try relative to doc first, then relative to projectRoot\n const candidates = [\n path.resolve(path.dirname(docAbsPath), ref),\n path.resolve(projectRoot, ref),\n ];\n for (const candidate of candidates) {\n // Sandbox check\n if (!candidate.startsWith(projectRoot)) continue;\n if (fs.existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** Read frontmatter from a file as a plain Record. Does not throw on body. */\nfunction readFrontmatterFromFile(absPath: string): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return {};\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return {};\n const fmLines = lines.slice(1, closeIdx);\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n if (line.trim() === '' || line.trim().startsWith('#')) continue;\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '' || val === '[]') { fm[key] = []; continue; }\n if (val.startsWith('{')) { fm[key] = val; continue; }\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1).trim();\n fm[key] = inner === '' ? [] : inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return fm;\n } catch {\n return {};\n }\n}\n\n// ─── Body-contains evaluator ──────────────────────────────────────────────────\n\nfunction evalBodyContains(\n parsed: Extract<ParsedPredicate, { kind: 'body-contains' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const body = doc.body;\n const needle = parsed.needle;\n\n // Count occurrences and find section context\n let count = 0;\n let pos = 0;\n const sections: number[] = []; // 1-indexed section numbers for each occurrence\n const bodySections = body.split(/^## /m);\n\n // Simple occurrence count\n while ((pos = body.indexOf(needle, pos)) !== -1) {\n count++;\n // Find which section this occurrence is in\n const before = body.slice(0, pos);\n const sectionCount = (before.match(/^## /gm) || []).length;\n sections.push(sectionCount + 1); // 1-indexed\n pos += needle.length;\n }\n\n const present = count > 0;\n void bodySections; // suppress unused warning\n\n if (parsed.negated) {\n // \"body does not contain\" → pass when absent\n if (present) {\n const sectionList = [...new Set(sections)].map((s) => `§${s}`).join(', ');\n return {\n pass: false,\n detail: `${count} occurrence${count === 1 ? '' : 's'} at ${sectionList}`,\n };\n }\n return { pass: true, detail: `'${needle}' not found in body` };\n } else {\n // \"body contains\" → pass when present\n if (present) {\n return { pass: true, detail: `'${needle}' found ${count} time${count === 1 ? '' : 's'}` };\n }\n return { pass: false, detail: `'${needle}' not found in body` };\n }\n}\n\n// ─── Marker-absence evaluator ─────────────────────────────────────────────────\n\n/**\n * Sub-fix #2 (BUG-008): Evaluates \"body does not contain marker '<id>'\".\n * A marker is counted only when it appears in a syntactic role:\n * 1. Followed immediately by a colon: TBD: ...\n * 2. Wrapped in parens: (TBD) or square brackets: [TBD]\n * 3. The entire trimmed line equals the marker: bare TBD on its own line\n * 4. Preceded by a code-comment prefix: // TBD or # TBD\n *\n * NOT counted:\n * - TBD as part of another word (TBDs, TBDish, TBD's)\n * - TBD inside quotes in prose (\"TBD resolution\")\n * - Template self-reference lines: \"- [x] 0 \"TBDs\" exist\" / \"- [ ] 0 \"TBDs\" exist\"\n */\nfunction evalMarkerAbsence(\n parsed: Extract<ParsedPredicate, { kind: 'marker-absence' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const { marker } = parsed;\n const lines = doc.body.split('\\n');\n\n // Template self-reference lines to exclude (BUG-008 spec: \"- [x] 0 \"TBDs\" exist\")\n const templateSelfRefRe = /^\\s*-\\s*\\[[x ]\\]\\s*0\\s*\"TBDs?\"\\s*exist/i;\n\n // Regex: marker in a syntactic role.\n // Matches: (MARKER) | [MARKER] | MARKER: | // MARKER | # MARKER | bare MARKER line\n // The (?<!\\w) and (?!\\w) prevent matching inside longer words.\n const markerRe = new RegExp(\n `(?:^|(?<=\\\\())${marker}(?=:)|` + // MARKER: (colon follows)\n `\\\\(${marker}\\\\)|` + // (MARKER) parens\n `\\\\[${marker}\\\\]|` + // [MARKER] square brackets\n `(?<=//\\\\s*)${marker}(?!\\\\w)|` + // // MARKER (comment)\n `(?<=#\\\\s*)${marker}(?!\\\\w)`, // # MARKER (comment)\n 'g'\n );\n\n // Also check bare-line: entire trimmed line is just the marker\n const bareLineRe = new RegExp(`^${marker}$`);\n\n const violations: number[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n\n // Skip template self-reference boilerplate\n if (templateSelfRefRe.test(line)) continue;\n\n const trimmed = line.trim();\n\n // Check bare line\n if (bareLineRe.test(trimmed)) {\n violations.push(i + 1);\n continue;\n }\n\n // Check syntactic marker roles via regex\n markerRe.lastIndex = 0;\n if (markerRe.test(line)) {\n violations.push(i + 1);\n }\n }\n\n if (violations.length > 0) {\n return {\n pass: false,\n detail: `${violations.length} marker occurrence${violations.length === 1 ? '' : 's'} of '${marker}' at line${violations.length === 1 ? '' : 's'} ${violations.join(', ')}`,\n };\n }\n return { pass: true, detail: `no '${marker}' markers found in body` };\n}\n\n// ─── Section evaluator ────────────────────────────────────────────────────────\n\nfunction evalSection(\n parsed: Extract<ParsedPredicate, { kind: 'section' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n // Split body on ## headings (1-indexed).\n // Use lookahead so each part starts with \"## \" (or is preamble if body doesn't start with ##).\n const body = doc.body;\n\n const rawParts = body.split(/^(?=## )/m);\n // If body starts with \"## \", rawParts[0] = \"## Section 1\\n...\", rawParts[1] = \"## Section 2\\n...\", etc.\n // Section N is rawParts[N-1] (0-based array, 1-indexed sections).\n // If body has preamble before first ##, rawParts[0] = preamble (section 0), rawParts[1] = section 1, etc.\n\n // Detect if there is a preamble (content before first ##)\n const hasPreamble = rawParts.length > 0 && !rawParts[0]!.startsWith('## ');\n // Section N → rawParts index: with preamble, index = N; without, index = N - 1\n const arrayIndex = hasPreamble ? parsed.index : parsed.index - 1;\n const sectionContent = rawParts[arrayIndex];\n const totalSections = hasPreamble ? rawParts.length - 1 : rawParts.length;\n\n if (!sectionContent) {\n return {\n pass: false,\n detail: `section ${parsed.index} not found (body has ${totalSections} sections)`,\n };\n }\n\n let actualCount: number;\n switch (parsed.itemType) {\n case 'checked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[x\\]/gim) || []).length;\n break;\n case 'unchecked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[ \\]/gim) || []).length;\n break;\n case 'listed-item':\n actualCount = (sectionContent.match(/^\\s*- /gm) || []).length;\n break;\n }\n\n const pass = applyCountOp(actualCount, parsed.count.op, parsed.count.n);\n const opStr = parsed.count.op === '>=' ? '≥' : parsed.count.op;\n const detail = pass\n ? `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`\n : `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`;\n\n return { pass, detail };\n}\n\nfunction applyCountOp(actual: number, op: '>=' | '==' | '>', n: number): boolean {\n switch (op) {\n case '>=': return actual >= n;\n case '==': return actual === n;\n case '>': return actual > n;\n }\n}\n\n// ─── File-exists evaluator ────────────────────────────────────────────────────\n\nfunction evalFileExists(\n parsed: Extract<ParsedPredicate, { kind: 'file-exists' }>,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const resolved = path.resolve(projectRoot, parsed.path);\n\n // Sandbox check: must be inside projectRoot\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n return {\n pass: false,\n detail: `path '${parsed.path}' resolves outside project root (sandbox violation)`,\n };\n }\n\n const exists = fs.existsSync(resolved);\n return {\n pass: exists,\n detail: exists ? `${parsed.path} exists` : `${parsed.path} not found`,\n };\n}\n\n// ─── Link-target-exists evaluator ────────────────────────────────────────────\n\nfunction evalLinkTargetExists(\n parsed: Extract<ParsedPredicate, { kind: 'link-target-exists' }>,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const projectRoot = opts?.projectRoot ?? process.cwd();\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n const found = indexContent.includes(`[[${parsed.id}]]`);\n return {\n pass: found,\n detail: found\n ? `[[${parsed.id}]] found in wiki index`\n : `[[${parsed.id}]] not found in wiki index`,\n };\n}\n\n// ─── Status-of evaluator ─────────────────────────────────────────────────────\n\nfunction evalStatusOf(\n parsed: Extract<ParsedPredicate, { kind: 'status-of' }>,\n opts: EvalOptions | undefined,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n // Find the raw path for this ID in the wiki index\n // Wiki index format: | [[STORY-003-13]] | story | Draft | .cleargate/delivery/... |\n const rowMatch = indexContent.match(\n new RegExp(`\\\\[\\\\[${parsed.id}\\\\]\\\\]\\\\s*\\\\|[^|]+\\\\|[^|]+\\\\|\\\\s*([^|\\\\n]+)`)\n );\n if (!rowMatch) {\n return { pass: false, detail: `[[${parsed.id}]] not found in wiki index` };\n }\n\n const rawPath = rowMatch[1]!.trim();\n const fullPath = path.resolve(projectRoot, rawPath);\n\n // Sandbox check\n if (!fullPath.startsWith(projectRoot)) {\n return { pass: false, detail: `wiki path for ${parsed.id} resolves outside project root` };\n }\n\n const linkedFm = readFrontmatterFromFile(fullPath);\n const status = linkedFm['status'];\n if (status === undefined) {\n return { pass: false, detail: `[[${parsed.id}]] has no status field` };\n }\n\n const pass = String(status).replace(/^[\"']|[\"']$/g, '') === parsed.value;\n return {\n pass,\n detail: pass\n ? `status-of([[${parsed.id}]]) == ${parsed.value}`\n : `status-of([[${parsed.id}]]) is '${status}', expected '${parsed.value}'`,\n };\n}\n","/**\n * STORY-008-02: Idempotent cached_gate_result frontmatter writer.\n * Reuses the shared frontmatter serializer from frontmatter-yaml.ts.\n *\n * writeCachedGate is byte-identical on re-run with identical inputs (same now + same result).\n *\n * Post-BUG-001: cached_gate_result is stored as a native YAML mapping\n * (parseFrontmatter returns it as an object). Legacy flow-style strings are\n * still accepted on read for backwards-compat with old files.\n */\n\nimport * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface CachedGate {\n pass: boolean;\n failing_criteria: { id: string; detail: string }[];\n last_gate_check: string;\n}\n\n/**\n * Read the cached_gate_result from a file's frontmatter.\n * Returns null if the key is absent or the file has no valid frontmatter.\n */\nexport async function readCachedGate(absPath: string): Promise<CachedGate | null> {\n let raw: string;\n try {\n raw = await fs.readFile(absPath, 'utf8');\n } catch {\n return null;\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n return null;\n }\n\n return coerceCachedGate(fm['cached_gate_result']);\n}\n\n/**\n * Write (or update) cached_gate_result in a file's frontmatter.\n * Idempotent: if the result deep-equals the existing cached value AND the same\n * now() is supplied, the file bytes are left untouched.\n *\n * @param absPath Absolute path to the markdown file.\n * @param result The gate result to cache.\n * @param opts Optional: inject `now` for test determinism.\n */\nexport async function writeCachedGate(\n absPath: string,\n result: CachedGate,\n opts?: { now?: () => Date }\n): Promise<void> {\n const nowFn = opts?.now ?? (() => new Date());\n const lastGateCheck = result.last_gate_check || toIsoSecond(nowFn());\n\n const newResult: CachedGate = {\n pass: result.pass,\n failing_criteria: result.failing_criteria,\n last_gate_check: lastGateCheck,\n };\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n throw new Error(`writeCachedGate: failed to parse frontmatter in ${absPath}`);\n }\n\n // Idempotency check: compare existing cached_gate_result\n const existing = coerceCachedGate(fm['cached_gate_result']);\n if (existing && JSON.stringify(existing) === JSON.stringify(newResult)) {\n return;\n }\n\n // Build new frontmatter: preserve all existing keys, inject/update cached_gate_result\n const newFm: Record<string, unknown> = {};\n let inserted = false;\n for (const [k, v] of Object.entries(fm)) {\n if (k === 'cached_gate_result') {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n inserted = true;\n } else {\n newFm[k] = v;\n }\n }\n if (!inserted) {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n }\n\n const fmBlock = serializeFrontmatter(newFm);\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Coerce a frontmatter value (native object or legacy flow-style string) into\n * a CachedGate. Returns null if absent or unrecognizable.\n */\nfunction coerceCachedGate(val: unknown): CachedGate | null {\n if (val === undefined || val === null) return null;\n\n // Native object (current format)\n if (typeof val === 'object' && !Array.isArray(val)) {\n const c = val as Record<string, unknown>;\n return {\n pass: Boolean(c['pass']),\n failing_criteria: Array.isArray(c['failing_criteria'])\n ? (c['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(c['last_gate_check'] ?? ''),\n };\n }\n\n // Legacy flow-style string \"{pass: true, ...}\" — parse via js-yaml\n if (typeof val === 'string' && val.startsWith('{')) {\n try {\n const parsed = yaml.load(val, { schema: yaml.CORE_SCHEMA });\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return {\n pass: Boolean(p['pass']),\n failing_criteria: Array.isArray(p['failing_criteria'])\n ? (p['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(p['last_gate_check'] ?? ''),\n };\n } catch {\n return null;\n }\n }\n\n return null;\n}\n","/**\n * gate-run.ts — `cleargate gate <name>` command handler.\n *\n * STORY-018-03: Config-driven gates. Runs a shell command configured in\n * `.cleargate/config.yml` under `gates.<name>`. Known gate names:\n * precommit, test, typecheck, lint.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n * FLASHCARD #cli #commander #optional-key: opts.strict undefined must be\n * checked with === true, not truthy check.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\nimport type { WikiConfig } from '../lib/wiki-config.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateRunCliOptions {\n cwd?: string;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n spawnFn?: typeof spawnSync;\n configLoader?: (repoRoot: string) => WikiConfig;\n}\n\nconst KNOWN_GATES = ['precommit', 'test', 'typecheck', 'lint'] as const;\ntype KnownGate = typeof KNOWN_GATES[number];\n\n// ─── gateRunHandler ───────────────────────────────────────────────────────────\n\n/**\n * Handle `cleargate gate <name>`.\n *\n * - Validates name is in KNOWN_GATES (exit 2 if not).\n * - Loads config via configLoader (defaults to loadWikiConfig from cwd).\n * - If gate not configured: friendly message + exit 0 (default) or exit 1 (--strict).\n * - If configured: spawnSync with shell:true, propagate exit code.\n */\nexport function gateRunHandler(\n name: string,\n opts: { strict?: boolean },\n cli?: GateRunCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n const configLoaderFn = cli?.configLoader ?? loadWikiConfig;\n\n // Validate gate name\n if (!(KNOWN_GATES as readonly string[]).includes(name)) {\n stderrFn(\n `unknown gate name '${name}' — must be one of: precommit, test, typecheck, lint`,\n );\n return exitFn(2);\n }\n\n // Load config\n const config = configLoaderFn(cwd);\n const cmd = config.gates[name as KnownGate];\n\n if (cmd == null) {\n const msg = `gate \"${name}\" not configured — add gates.${name} to .cleargate/config.yml (see cleargate-planning/.cleargate/config.example.yml)`;\n if (opts.strict === true) {\n stderrFn(msg);\n return exitFn(1);\n } else {\n stdoutFn(msg);\n return exitFn(0);\n }\n }\n\n // Run the configured command\n const result = spawnFn(cmd, { shell: true, stdio: 'inherit', cwd });\n\n if (result.error) {\n stderrFn(`[cleargate gate ${name}] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n return exitFn(result.status ?? 0);\n}\n","/**\n * sprint.ts — `cleargate sprint init|close|archive` command handlers.\n *\n * STORY-013-08: CLI wrappers for sprint lifecycle scripts.\n * STORY-014-08: sprintArchiveHandler — final sprint close-out.\n * STORY-015-04: stampSprintClose + rollback on wiki build/lint failure.\n *\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh\n * or orchestrate filesystem + git operations directly.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly —\n * always route through `run_script.sh`.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport { wikiBuildHandler } from './wiki-build.js';\nimport { wikiLintHandler } from './wiki-lint.js';\n\n// Terminal statuses — re-declared locally to avoid cross-module runtime import.\n// Keep in sync with TERMINAL_STATUSES in wiki-build.ts.\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface SprintCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Test seam: override wiki build invocation. Defaults to wikiBuildHandler. */\n wikiBuildFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n /** Test seam: override wiki lint invocation. Defaults to wikiLintHandler. */\n wikiLintFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n}\n\n// ─── Shared run_script.sh resolution ─────────────────────────────────────────\n\nfunction resolveRunScript(opts: SprintCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\n// ─── sprintInitHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint init <sprint-id> --stories <csv>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh init_sprint.mjs <sprint-id> --stories <csv>`\n */\nexport function sprintInitHandler(\n opts: { sprintId: string; stories: string },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['init_sprint.mjs', opts.sprintId, '--stories', opts.stories];\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint init] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── sprintCloseHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint close <sprint-id> [--assume-ack]`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh close_sprint.mjs <sprint-id> [--assume-ack]`\n */\nexport function sprintCloseHandler(\n opts: { sprintId: string; assumeAck?: boolean },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['close_sprint.mjs', opts.sprintId];\n // FLASHCARD #cli #commander #optional-key: omit the key when undefined; only\n // append --assume-ack when the flag was explicitly set (opts.assumeAck === true).\n if (opts.assumeAck === true) {\n args.push('--assume-ack');\n }\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint close] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── sprintArchiveHandler ─────────────────────────────────────────────────────\n\n/**\n * Parse just the frontmatter from a markdown file using js-yaml CORE_SCHEMA.\n * Returns { fm, body } where body is the content after the closing `---`.\n * FLASHCARD #cli #frontmatter #parse: body may start with blank line; we strip\n * one leading blank to match parseFrontmatter.ts convention.\n */\nfunction parseFileFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return { fm: {}, body: raw };\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return { fm: {}, body: raw };\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n if (yamlText.trim() === '') return { fm: {}, body };\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch {\n return { fm: {}, body };\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return { fm: {}, body };\n return { fm: parsed as Record<string, unknown>, body };\n}\n\n/**\n * Serialize frontmatter + body back to a markdown string.\n * Always adds blank separator between `---` and body.\n */\nfunction serializeFileContent(fm: Record<string, unknown>, body: string): string {\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---\\n\\n${body}`;\n}\n\n/**\n * Atomic write via tmp + rename (same pattern as story.ts / close_sprint.mjs).\n */\nfunction atomicWriteStr(filePath: string, content: string): void {\n const tmp = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmp, content, 'utf8');\n fs.renameSync(tmp, filePath);\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranchForArchive(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Stamp a file's frontmatter with status + completed_at.\n * Returns the stamped content string (does NOT write to disk).\n */\nfunction stampFile(raw: string, status: string, completedAt: string): string {\n const { fm, body } = parseFileFrontmatter(raw);\n fm['status'] = status;\n fm['completed_at'] = completedAt;\n return serializeFileContent(fm, body);\n}\n\n/**\n * STORY-015-04: Stamp the sprint file's frontmatter with status=\"Completed\"\n * (if not already terminal) and completed_at (if absent). Writes atomically.\n *\n * Returns:\n * previousContent — original file bytes for rollback\n * stampedContent — the content written to disk\n * didChange — false if file was already terminal + completed_at set\n */\nexport function stampSprintClose(\n sprintPath: string,\n now: () => string,\n): { previousContent: string; stampedContent: string; didChange: boolean } {\n const previousContent = fs.readFileSync(sprintPath, 'utf8');\n const { fm, body } = parseFileFrontmatter(previousContent);\n\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const alreadyTerminal = TERMINAL_STATUSES.has(currentStatus);\n const hasCompletedAt = typeof fm['completed_at'] === 'string' && fm['completed_at'].length > 0;\n\n if (alreadyTerminal && hasCompletedAt) {\n return { previousContent, stampedContent: previousContent, didChange: false };\n }\n\n if (!alreadyTerminal) {\n fm['status'] = 'Completed';\n }\n if (!hasCompletedAt) {\n fm['completed_at'] = now();\n }\n\n const stampedContent = serializeFileContent(fm, body);\n atomicWriteStr(sprintPath, stampedContent);\n return { previousContent, stampedContent, didChange: true };\n}\n\n/**\n * STORY-015-04: Atomically restore a sprint file from a previously-snapshotted\n * string (rollback after wiki build/lint failure).\n */\nexport function restoreSprintFile(sprintPath: string, original: string): void {\n atomicWriteStr(sprintPath, original);\n}\n\n/** Return state.json story keys that belong to a given epic. */\nfunction storyKeysForEpic(\n stateStories: Record<string, unknown>,\n epicId: string,\n): string[] {\n const epicNum = epicId.replace('EPIC-', '');\n return Object.keys(stateStories).filter((k) => k.startsWith(`STORY-${epicNum}-`));\n}\n\n/**\n * `cleargate sprint archive <sprint-id> [--dry-run]`\n *\n * v1: print inert message, exit 0.\n * v2:\n * 1. Read state.json — refuse if sprint_status !== 'Completed'.\n * 2. Resolve file set from sprint frontmatter + state.json + orphan scan.\n * 3. --dry-run: print plan; no writes.\n * 4. Live: stamp sprint file, wiki build+lint (if wiki initialised), then\n * move files, clear .active, git checkout main, merge, branch -d.\n */\nexport async function sprintArchiveHandler(\n opts: { sprintId: string; dryRun?: boolean },\n cli?: SprintCliOptions,\n): Promise<void> {\n try {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Wiki build/lint seam wrappers.\n // wikiBuildHandler on success returns (no exit call); on failure calls exit(1) which throws.\n // wikiLintHandler on success calls exit(0) which throws; on findings calls exit(1) which throws.\n const wikiBuildFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiBuildFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-build-exit:${code}`); };\n try {\n await wikiBuildHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-build-exit:0')) return;\n throw err;\n }\n });\n const wikiLintFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiLintFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-lint-exit:${code}`); };\n try {\n await wikiLintHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-lint-exit:0')) return;\n throw err;\n }\n });\n\n // Step 1: v1-inert check\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // Step 2: Read state.json — refuse if not Completed\n const stateFile = path.join(cwd, '.cleargate', 'sprint-runs', opts.sprintId, 'state.json');\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate sprint archive] state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n sprint_status?: string;\n stories?: Record<string, unknown>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate sprint archive] failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n if (state.sprint_status !== 'Completed') {\n stderrFn(\n `sprint not closed — run \\`cleargate sprint close ${opts.sprintId} --assume-ack\\` first`,\n );\n return exitFn(1);\n }\n\n const stateStories: Record<string, unknown> = state.stories ?? {};\n\n // Step 3: Resolve file set\n const pendingDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(cwd, '.cleargate', 'delivery', 'archive');\n\n // Sprint file\n let sprintFile: string | null = null;\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${opts.sprintId}_`) || entry === `${opts.sprintId}.md`) && entry.endsWith('.md')) {\n sprintFile = path.join(pendingDir, entry);\n break;\n }\n }\n\n // Sprint frontmatter → epics list\n let epicIds: string[] = [];\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { fm } = parseFileFrontmatter(fs.readFileSync(sprintFile, 'utf8'));\n const epics = fm['epics'];\n if (Array.isArray(epics)) {\n epicIds = epics.map(String);\n }\n }\n\n // Plan entries: { src, destName, status }\n interface FilePlan {\n src: string;\n destName: string;\n status: string;\n }\n const plan: FilePlan[] = [];\n\n if (sprintFile) {\n plan.push({\n src: sprintFile,\n destName: path.basename(sprintFile),\n status: 'Completed',\n });\n }\n\n for (const epicId of epicIds) {\n // Epic file\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${epicId}_`) || entry === `${epicId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Approved',\n });\n break;\n }\n }\n\n // Story files (authoritative: state.json keys for this epic)\n const storyKeys = storyKeysForEpic(stateStories, epicId);\n for (const storyId of storyKeys) {\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${storyId}_`) || entry === `${storyId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Done',\n });\n break;\n }\n }\n }\n }\n\n // Orphan scan: any STORY-*.md in pending-sync with parent_epic_ref matching\n // one of our epics but NOT in state.json keys.\n const storyIdsInState = new Set(Object.keys(stateStories));\n const planSrcs = new Set(plan.map((p) => p.src));\n const orphans: string[] = [];\n for (const entry of fs.readdirSync(pendingDir)) {\n if (!entry.startsWith('STORY-') || !entry.endsWith('.md')) continue;\n const candidate = path.join(pendingDir, entry);\n if (planSrcs.has(candidate)) continue;\n let raw: string;\n try {\n raw = fs.readFileSync(candidate, 'utf8');\n } catch { continue; }\n const { fm } = parseFileFrontmatter(raw);\n const parentRef = String(fm['parent_epic_ref'] ?? '');\n if (epicIds.includes(parentRef)) {\n // Derive story ID from filename (STORY-014-01_Something.md → STORY-014-01)\n const storyId = entry.replace(/\\.md$/, '').replace(/_.*$/, '');\n if (!storyIdsInState.has(storyId)) {\n orphans.push(candidate);\n stderrFn(`WARN: orphan story ${entry} matches epic ${parentRef} but is not in state.json — archiving anyway`);\n plan.push({ src: candidate, destName: entry, status: 'Done' });\n }\n }\n }\n\n // Step 4: --dry-run: print plan + exit 0\n const completedAt = new Date().toISOString();\n const sprintBranch = deriveSprintBranchForArchive(opts.sprintId);\n const activePath = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n\n if (opts.dryRun) {\n stdoutFn(`[dry-run] Sprint archive plan for ${opts.sprintId}:`);\n stdoutFn(` Sprint branch: ${sprintBranch}`);\n stdoutFn(` Files to archive (${plan.length}):`);\n for (const entry of plan) {\n stdoutFn(\n ` ${path.basename(entry.src)} → archive/${entry.destName} [stamp: status=${entry.status}, completed_at=<now>]`,\n );\n }\n if (orphans.length > 0) {\n stdoutFn(` Orphan files (${orphans.length}): ${orphans.map((o) => path.basename(o)).join(', ')}`);\n }\n stdoutFn(` .active → \"\" (truncate)`);\n stdoutFn(` git checkout main`);\n stdoutFn(` git merge --no-ff -m \"merge: ${sprintBranch} → main\" ${sprintBranch}`);\n stdoutFn(` git branch -d ${sprintBranch}`);\n return exitFn(0);\n }\n\n // Step 5a: Stamp the sprint file's frontmatter (status + completed_at)\n let sprintFileSnapshot: string | null = null;\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const wikiInitialised = fs.existsSync(wikiRoot);\n\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { previousContent } = stampSprintClose(sprintFile, () => completedAt);\n sprintFileSnapshot = previousContent;\n\n // Step 5b: wiki build + lint — only if wiki has been initialised.\n // Skip gracefully when .cleargate/wiki/ doesn't exist yet (wiki not built).\n if (wikiInitialised) {\n for (const [stepName, stepFn] of [\n ['wiki build', () => wikiBuildFn(cwd, stdoutFn)] as const,\n ['wiki lint', () => wikiLintFn(cwd, stdoutFn)] as const,\n ]) {\n try {\n await stepFn();\n } catch (err) {\n // Rollback sprint file frontmatter\n atomicWriteStr(sprintFile!, sprintFileSnapshot!);\n stderrFn(\n `[cleargate sprint archive] post-stamp ${stepName} failed — sprint frontmatter reverted`,\n );\n if (err instanceof Error) stderrFn(err.message);\n return exitFn(1);\n }\n }\n }\n }\n\n // Step 5c: Live run — stamp + move each file\n for (const entry of plan) {\n if (!fs.existsSync(entry.src)) {\n stderrFn(`[cleargate sprint archive] source not found: ${entry.src} — skipping`);\n continue;\n }\n const raw = fs.readFileSync(entry.src, 'utf8');\n const stamped = stampFile(raw, entry.status, completedAt);\n const dest = path.join(archiveDir, entry.destName);\n atomicWriteStr(entry.src, stamped);\n fs.renameSync(entry.src, dest);\n stdoutFn(`archived: ${entry.destName}`);\n }\n\n // Step 6: Truncate .active\n try {\n atomicWriteStr(activePath, '');\n } catch {\n // .active may not exist — not fatal\n }\n\n // Step 7: git checkout main\n const step7 = spawnFn('git', ['checkout', 'main'], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step7.error || (step7.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git checkout main failed`);\n if (step7.stderr) stderrFn(String(step7.stderr));\n return exitFn(step7.status ?? 1);\n }\n\n // Step 8: git merge --no-ff -m \"merge: sprint/<branch> → main\" sprint/<branch>\n const mergeMsg = `merge: ${sprintBranch} → main`;\n const step8 = spawnFn(\n 'git',\n ['merge', '--no-ff', '-m', mergeMsg, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step8.error || (step8.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git merge failed — run \\`git merge --abort\\` if needed`);\n if (step8.stderr) stderrFn(String(step8.stderr));\n return exitFn(step8.status ?? 1);\n }\n const mergeSha = String(step8.stdout ?? '').trim().split('\\n')[0] ?? '';\n\n // Step 9: git branch -d sprint/<branch>\n const step9 = spawnFn('git', ['branch', '-d', sprintBranch], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step9.error || (step9.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git branch -d ${sprintBranch} failed`);\n if (step9.stderr) stderrFn(String(step9.stderr));\n return exitFn(step9.status ?? 1);\n }\n\n stdoutFn(\n `archive complete: ${plan.length} files moved, branch ${sprintBranch} deleted` +\n (mergeSha ? `, merge SHA: ${mergeSha}` : ''),\n );\n return exitFn(0);\n } catch (e) {\n // The exitFn seam throws `Error(\"exit:<n>\")` as a synchronous control-flow\n // shortcut so the handler bails without running further steps. Under an\n // async handler that escapes as an unhandled rejection even when tests\n // `getCode()` confirms the expected exit. Swallow the sentinel here; any\n // other error re-throws.\n if (e instanceof Error && /^exit:\\d+$/.test(e.message)) return;\n throw e;\n }\n}\n","/**\n * story.ts — `cleargate story start|complete` command handlers.\n *\n * STORY-014-07: atomic orchestration of worktree + branch + merge + state.\n *\n * Both handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they orchestrate the full\n * worktree/merge sequence via `spawnSync` + a second-pass `state.json` write.\n *\n * `story start <story-id>`:\n * 1. `git worktree add <cwd>/.worktrees/<ID> -b story/<ID> <sprintBranch>`\n * 2. `bash run_script.sh update_state.mjs <ID> Bouncing`\n * 3. Re-read `state.json`, set `stories[<ID>].worktree` field, atomic write.\n *\n * `story complete <story-id>`:\n * 1. `git rev-list --count <sprintBranch>..story/<ID>` — if 0 → abort.\n * 2. `git -C <cwd> checkout <sprintBranch>`\n * 3. `git merge story/<ID> --no-ff -m \"merge: story/<ID> → <sprintBranch>\"`\n * — on conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n * 4. `git worktree remove .worktrees/<ID>`\n * 5. `git branch -d story/<ID>`\n * 6. `bash run_script.sh update_state.mjs <ID> Done`\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StoryCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID override for execution_mode discovery.\n * If absent, the handler reads `.cleargate/sprint-runs/.active`.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StoryCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranch(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Atomic write: tmp + rename. Inlined per plan (do not import from .mjs).\n * Caller passes the raw JSON-stringified text (or any string content).\n */\nfunction atomicWriteString(filePath: string, text: string): void {\n const tmpFile = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmpFile, text, 'utf8');\n fs.renameSync(tmpFile, filePath);\n}\n\n/**\n * Path to the sprint's state.json. The file may not exist yet under v2\n * when the sprint was just initialized — callers must guard.\n */\nfunction stateJsonPath(cwd: string, sprintId: string): string {\n return path.join(cwd, '.cleargate', 'sprint-runs', sprintId, 'state.json');\n}\n\n// ─── storyStartHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate story start <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 3-step spawn sequence + second-pass state.json mutation.\n */\nexport function storyStartHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve sprint ID: explicit override, else sentinel-fallback via .active.\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 3-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const worktreePath = path.join(cwd, '.worktrees', opts.storyId);\n const storyBranch = `story/${opts.storyId}`;\n\n // Step 1: git worktree add <path> -b story/<ID> <sprintBranch>\n // FLASHCARD (plan): flag order matters on macOS git — PATH before -b.\n const step1 = spawnFn(\n 'git',\n ['worktree', 'add', worktreePath, '-b', storyBranch, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n\n // Step 2: run_script.sh update_state.mjs <ID> Bouncing\n const runScript = resolveRunScript(cli ?? { cwd });\n const step2 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Bouncing'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: re-read state.json (fresh bytes AFTER update_state.mjs), set\n // stories[<ID>].worktree = \".worktrees/<ID>\", atomic write.\n const stateFile = stateJsonPath(cwd, sprintId);\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate story start] step 3: state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n stories?: Record<string, { worktree?: string | null } & Record<string, unknown>>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n state.stories = state.stories ?? {};\n const existing = state.stories[opts.storyId] ?? {\n state: 'Bouncing',\n qa_bounces: 0,\n arch_bounces: 0,\n worktree: null,\n updated_at: new Date().toISOString(),\n notes: '',\n };\n existing.worktree = `.worktrees/${opts.storyId}`;\n state.stories[opts.storyId] = existing;\n\n try {\n atomicWriteString(stateFile, JSON.stringify(state, null, 2) + '\\n');\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: atomic write failed: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n stdoutFn(`worktree created at .worktrees/${opts.storyId} on branch ${storyBranch}`);\n return exitFn(0);\n}\n\n// ─── storyCompleteHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate story complete <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 6-step orchestration (pre-flight, checkout, merge, worktree remove,\n * branch -d, update_state.mjs Done).\n */\nexport function storyCompleteHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 6-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const storyBranch = `story/${opts.storyId}`;\n const worktreeRel = path.join('.worktrees', opts.storyId);\n\n // Step 1: pre-flight rev-list — refuse if 0 commits on story branch.\n const step1 = spawnFn(\n 'git',\n ['rev-list', '--count', `${sprintBranch}..${storyBranch}`],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n const count = parseInt(String(step1.stdout ?? '0').trim(), 10);\n if (!Number.isFinite(count) || count <= 0) {\n stderrFn('no commits on story branch — nothing to merge');\n return exitFn(1);\n }\n\n // Step 2: checkout sprint branch.\n const step2 = spawnFn(\n 'git',\n ['-C', cwd, 'checkout', sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story complete] step 2 (checkout) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 2 (checkout ${sprintBranch}) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: merge story/<ID> --no-ff -m \"...\"\n // On conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n const mergeMsg = `merge: ${storyBranch} → ${sprintBranch}`;\n const step3 = spawnFn(\n 'git',\n ['merge', storyBranch, '--no-ff', '-m', mergeMsg],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step3.error) {\n stderrFn(`[cleargate story complete] step 3 (merge) error: ${step3.error.message}`);\n return exitFn(1);\n }\n if ((step3.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] merge conflict — run \\`git merge --abort\\` then re-run after resolution`);\n if (step3.stderr) stderrFn(String(step3.stderr));\n return exitFn(1);\n }\n\n // Step 4: git worktree remove .worktrees/<ID>\n const step4 = spawnFn(\n 'git',\n ['worktree', 'remove', worktreeRel],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step4.error) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove) error: ${step4.error.message}`);\n return exitFn(1);\n }\n if ((step4.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove ${worktreeRel}) failed with exit ${step4.status}`);\n if (step4.stderr) stderrFn(String(step4.stderr));\n return exitFn(step4.status ?? 1);\n }\n\n // Step 5: git branch -d story/<ID>\n const step5 = spawnFn(\n 'git',\n ['branch', '-d', storyBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step5.error) {\n stderrFn(`[cleargate story complete] step 5 (branch -d) error: ${step5.error.message}`);\n return exitFn(1);\n }\n if ((step5.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 5 (branch -d ${storyBranch}) failed with exit ${step5.status}`);\n if (step5.stderr) stderrFn(String(step5.stderr));\n return exitFn(step5.status ?? 1);\n }\n\n // Step 6: run_script.sh update_state.mjs <ID> Done\n const runScript = resolveRunScript(cli ?? { cwd });\n const step6 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Done'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step6.error) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) error: ${step6.error.message}`);\n return exitFn(1);\n }\n if ((step6.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) failed with exit ${step6.status}`);\n if (step6.stderr) stderrFn(String(step6.stderr));\n return exitFn(step6.status ?? 1);\n }\n\n stdoutFn(`merged ${storyBranch} → ${sprintBranch}; worktree + branch removed; state = Done`);\n return exitFn(0);\n}\n","/**\n * state.ts — `cleargate state update|validate` command handlers.\n *\n * STORY-013-08: CLI wrappers for state management scripts.\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StateCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID for execution_mode discovery. Required for state update/validate.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StateCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n// ─── stateUpdateHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate state update <story-id> <new-state>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh update_state.mjs <story-id> <new-state>`\n */\nexport function stateUpdateHandler(\n opts: { storyId: string; newState: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n // Sprint ID resolution order:\n // 1. Explicit --sprint <id> from CLI (cli?.sprintId)\n // 2. .cleargate/sprint-runs/.active sentinel\n // 3. Fall through to SPRINT-UNKNOWN (v1-inert)\n const cwd = cli?.cwd;\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, opts.newState],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state update] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── stateValidateHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate state validate <sprint-id>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh validate_state.mjs <sprint-id>`\n */\nexport function stateValidateHandler(\n opts: { sprintId: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'validate_state.mjs', opts.sprintId],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state validate] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * stamp-tokens.ts — STORY-008-05\n *\n * `cleargate stamp-tokens <file>` CLI command.\n * Reads token-ledger rows for a work item, aggregates per-session totals, and\n * stamps `draft_tokens:` into the file's YAML frontmatter.\n *\n * Hook-invoked. Idempotent within a session (last_stamp check), accumulative\n * across sessions (sessions[] array).\n *\n * No top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from '../lib/frontmatter-yaml.js';\nimport { readLedgerForWorkItem } from '../lib/ledger-reader.js';\nimport { detectWorkItemType } from '../lib/work-item-type.js';\nimport type { SessionBucket } from '../lib/ledger-reader.js';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport interface StampTokensCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n sprintRunsRoot?: string;\n}\n\nexport interface DraftTokensSession {\n session: string;\n model: string;\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n ts: string;\n}\n\nexport interface DraftTokens {\n input: number | null;\n output: number | null;\n cache_creation: number | null;\n cache_read: number | null;\n model: string | null;\n last_stamp: string;\n sessions: DraftTokensSession[];\n}\n\nexport async function stampTokensHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampTokensCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn =\n cli?.exit ??\n ((code: number) => {\n process.exit(code);\n });\n const nowFn = cli?.now ?? (() => new Date());\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Archive freeze: if path contains /.cleargate/delivery/archive/, noop\n if (/\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath)) {\n stdoutFn(`[frozen] ${absPath}`);\n exitFn(0);\n return;\n }\n\n // Read the file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absPath, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot read file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Parse frontmatter\n let fm: Record<string, unknown> = {};\n let body = '';\n\n const hasFrontmatter = rawContent.trimStart().startsWith('---');\n if (hasFrontmatter) {\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot parse frontmatter in: ${absPath}`);\n exitFn(1);\n return;\n }\n } else {\n body = rawContent;\n }\n\n // Extract work_item_id from frontmatter ID key or filename fallback\n const workItemId = extractWorkItemId(fm, absPath);\n if (!workItemId) {\n stdoutFn(`[stamp-tokens] error: cannot determine work_item_id from frontmatter or filename: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Read existing draft_tokens from frontmatter (parsed as native nested object\n // since parseFrontmatter now returns typed values)\n const existingDraftTokens = coerceDraftTokens(fm['draft_tokens']);\n const existingLastStamp = existingDraftTokens?.last_stamp ?? null;\n\n // Read ledger\n const buckets = readLedgerForWorkItem(workItemId, { sprintRunsRoot: cli?.sprintRunsRoot });\n\n // Idempotency check: if all ledger rows are older than last_stamp, and we\n // already have sessions, no-op.\n if (existingLastStamp && buckets.length > 0) {\n const allRowsOlderThanLastStamp = buckets.every((bucket) =>\n bucket.rows.every((row) => row.ts < existingLastStamp),\n );\n if (allRowsOlderThanLastStamp && existingDraftTokens !== null) {\n // No new rows since last stamp — no-op\n exitFn(0);\n return;\n }\n }\n\n const nowIso = toIsoSecond(nowFn());\n\n let newFm: Record<string, unknown>;\n let stampError: string | undefined;\n\n if (buckets.length === 0) {\n // Missing ledger: write all-null draft_tokens + stamp_error\n stampError = `no ledger rows for work_item_id ${workItemId}`;\n const nullTokens: DraftTokens = {\n input: null,\n output: null,\n cache_creation: null,\n cache_read: null,\n model: null,\n last_stamp: nowIso,\n sessions: [],\n };\n newFm = buildNewFrontmatter(fm, nullTokens, stampError);\n } else {\n // Aggregate across all sessions\n const tokens = aggregateBuckets(buckets, nowIso);\n newFm = buildNewFrontmatter(fm, tokens, undefined);\n // Remove stale stamp_error if ledger now has rows\n delete newFm['stamp_error'];\n }\n\n const serialized = buildSerializedContent(newFm, body);\n\n if (opts.dryRun) {\n // Print planned diff without writing\n stdoutFn(`[dry-run] stamp-tokens would write draft_tokens for ${workItemId}:`);\n const draftTokensVal = newFm['draft_tokens'];\n stdoutFn(` draft_tokens: ${JSON.stringify(draftTokensVal)}`);\n if (stampError) {\n stdoutFn(` stamp_error: \"${stampError}\"`);\n }\n exitFn(0);\n return;\n }\n\n // Write the file\n try {\n fs.writeFileSync(absPath, serialized, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot write file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n stdoutFn(`[stamped] ${absPath} (${workItemId})`);\n exitFn(0);\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Extract the work_item_id from frontmatter ID fields or filename regex fallback.\n */\nfunction extractWorkItemId(fm: Record<string, unknown>, absPath: string): string | null {\n // Try frontmatter ID keys in order\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id'];\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim() !== '') {\n return val.trim();\n }\n }\n\n // Fallback: extract from filename\n const basename = path.basename(absPath);\n const match = basename.match(/^(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/i);\n if (match) {\n return match[0].toUpperCase();\n }\n\n // Also try detectWorkItemType for any prefix match\n const typeFromPath = detectWorkItemType(absPath);\n if (typeFromPath) {\n // Try to find the ID segment in the basename\n const idMatch = basename.match(/((?:STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(?:-\\d+)?)/i);\n if (idMatch) {\n return idMatch[1].toUpperCase();\n }\n }\n\n return null;\n}\n\n/**\n * Coerce the existing draft_tokens value from frontmatter into a DraftTokens shape.\n * Handles two on-disk shapes:\n * 1. Native nested YAML map (current format, parsed by js-yaml as an object)\n * 2. Legacy JSON-in-a-string, from pre-fix files written before BUG-001\n */\nfunction coerceDraftTokens(val: unknown): DraftTokens | null {\n if (val == null) return null;\n\n if (typeof val === 'object' && !Array.isArray(val)) {\n const o = val as Record<string, unknown>;\n return {\n input: typeof o['input'] === 'number' ? o['input'] : null,\n output: typeof o['output'] === 'number' ? o['output'] : null,\n cache_creation: typeof o['cache_creation'] === 'number' ? o['cache_creation'] : null,\n cache_read: typeof o['cache_read'] === 'number' ? o['cache_read'] : null,\n model: typeof o['model'] === 'string' ? o['model'] : null,\n last_stamp: typeof o['last_stamp'] === 'string' ? o['last_stamp'] : '',\n sessions: Array.isArray(o['sessions']) ? (o['sessions'] as DraftTokensSession[]) : [],\n };\n }\n\n if (typeof val === 'string') {\n try {\n const parsed = JSON.parse(val) as DraftTokens;\n return parsed;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Aggregate ledger buckets into a DraftTokens object.\n * model: comma-joined sorted unique models across all buckets' rows.\n * sessions[]: one entry per bucket, using bucket totals.\n */\nexport function aggregateBuckets(buckets: SessionBucket[], nowIso: string): DraftTokens {\n let totalInput = 0;\n let totalOutput = 0;\n let totalCacheCreation = 0;\n let totalCacheRead = 0;\n\n const uniqueModels = new Set<string>();\n const sessions: DraftTokensSession[] = [];\n\n for (const bucket of buckets) {\n totalInput += bucket.totals.input;\n totalOutput += bucket.totals.output;\n totalCacheCreation += bucket.totals.cache_creation;\n totalCacheRead += bucket.totals.cache_read;\n\n // Collect unique models from rows and derive session model\n const sessionModels = new Set<string>();\n for (const row of bucket.rows) {\n if (row.model) {\n uniqueModels.add(row.model);\n sessionModels.add(row.model);\n }\n }\n\n // Session model: comma-joined unique models for this session\n const sessionModel = Array.from(sessionModels).sort().join(', ');\n\n sessions.push({\n session: bucket.session_id,\n model: sessionModel,\n input: bucket.totals.input,\n output: bucket.totals.output,\n cache_read: bucket.totals.cache_read,\n cache_creation: bucket.totals.cache_creation,\n ts: bucket.rows[0]?.ts ?? '',\n });\n }\n\n const model = Array.from(uniqueModels).sort().join(', ') || null;\n\n return {\n input: totalInput,\n output: totalOutput,\n cache_creation: totalCacheCreation,\n cache_read: totalCacheRead,\n model,\n last_stamp: nowIso,\n sessions,\n };\n}\n\n/**\n * Build the new frontmatter record with draft_tokens as a native nested\n * object. serializeFrontmatter (js-yaml) emits it as a block-style YAML map.\n */\nfunction buildNewFrontmatter(\n existingFm: Record<string, unknown>,\n tokens: DraftTokens,\n stampError: string | undefined,\n): Record<string, unknown> {\n const newFm: Record<string, unknown> = {};\n\n // Preserve all existing keys except draft_tokens and stamp_error (we'll re-add)\n for (const [k, v] of Object.entries(existingFm)) {\n if (k !== 'draft_tokens' && k !== 'stamp_error') {\n newFm[k] = v;\n }\n }\n\n if (stampError) {\n newFm['stamp_error'] = stampError;\n }\n\n // Store as a plain object; serializer emits block-style YAML\n newFm['draft_tokens'] = tokens as unknown as Record<string, unknown>;\n\n return newFm;\n}\n\n/**\n * Build the final file content: frontmatter block + body.\n */\nfunction buildSerializedContent(fm: Record<string, unknown>, body: string): string {\n const fmBlock = serializeFrontmatter(fm);\n if (body.length > 0) {\n return `${fmBlock}\\n\\n${body}`;\n }\n return `${fmBlock}\\n`;\n}\n\n","/**\n * ledger-reader.ts — STORY-008-04\n *\n * Read-only library for scanning token-ledger.jsonl files across all sprint-run\n * directories and grouping rows by session for per-work-item cost attribution.\n *\n * Node built-ins only. No runtime deps.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface LedgerRow {\n ts: string;\n sprint_id: string;\n agent_type: string;\n /** Populated only for STORY-* items (backward compat; empty string for non-story items) */\n story_id: string;\n /** Always populated when detection succeeded. Equals story_id for STORY items. */\n work_item_id: string;\n session_id: string;\n transcript: string;\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n model: string;\n turns: number;\n}\n\nexport interface SessionBucket {\n session_id: string;\n rows: LedgerRow[];\n totals: {\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n turns: number;\n };\n}\n\nexport interface ReadLedgerOptions {\n /** ISO timestamp string; rows with ts < since are excluded */\n since?: string;\n /**\n * Root directory for sprint-runs/. Defaults to\n * <repo_root>/.cleargate/sprint-runs where repo_root is resolved by\n * walking up from cwd to find .cleargate/.\n * Override in tests to avoid touching the real repo.\n */\n sprintRunsRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Walk up from cwd until we find a directory containing `.cleargate/sprint-runs/`.\n * Returns the sprint-runs path or null if not found.\n */\nfunction findSprintRunsRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const candidate = path.join(dir, '.cleargate', 'sprint-runs');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction normalizeRow(raw: Record<string, unknown>): LedgerRow {\n // work_item_id may be absent in pre-STORY-008-04 rows; default from story_id.\n const story_id = typeof raw['story_id'] === 'string' ? raw['story_id'] : '';\n const work_item_id =\n typeof raw['work_item_id'] === 'string' && raw['work_item_id'] !== ''\n ? raw['work_item_id']\n : story_id;\n\n return {\n ts: typeof raw['ts'] === 'string' ? raw['ts'] : '',\n sprint_id: typeof raw['sprint_id'] === 'string' ? raw['sprint_id'] : '',\n agent_type: typeof raw['agent_type'] === 'string' ? raw['agent_type'] : 'unknown',\n story_id,\n work_item_id,\n session_id: typeof raw['session_id'] === 'string' ? raw['session_id'] : '',\n transcript: typeof raw['transcript'] === 'string' ? raw['transcript'] : '',\n input: typeof raw['input'] === 'number' ? raw['input'] : 0,\n output: typeof raw['output'] === 'number' ? raw['output'] : 0,\n cache_creation: typeof raw['cache_creation'] === 'number' ? raw['cache_creation'] : 0,\n cache_read: typeof raw['cache_read'] === 'number' ? raw['cache_read'] : 0,\n model: typeof raw['model'] === 'string' ? raw['model'] : '',\n turns: typeof raw['turns'] === 'number' ? raw['turns'] : 0,\n };\n}\n\nfunction rowMatchesWorkItem(row: LedgerRow, workItemId: string): boolean {\n return row.work_item_id === workItemId || row.story_id === workItemId;\n}\n\nfunction buildBucket(session_id: string, rows: LedgerRow[]): SessionBucket {\n const totals = rows.reduce(\n (acc, r) => ({\n input: acc.input + r.input,\n output: acc.output + r.output,\n cache_creation: acc.cache_creation + r.cache_creation,\n cache_read: acc.cache_read + r.cache_read,\n turns: acc.turns + r.turns,\n }),\n { input: 0, output: 0, cache_creation: 0, cache_read: 0, turns: 0 }\n );\n return { session_id, rows, totals };\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Scan all sprint-runs/<sprint>/token-ledger.jsonl files and return rows\n * matching the given workItemId, grouped by session_id.\n *\n * Rows are compared by work_item_id first, then story_id (for backward compat\n * with pre-STORY-008-04 rows that lack work_item_id).\n *\n * Pre-fix rows (missing work_item_id) default work_item_id from story_id.\n *\n * @param workItemId e.g. \"STORY-008-04\", \"EPIC-008\", \"PROPOSAL-042\"\n * @param opts optional filters and path overrides\n * @returns array of SessionBucket, one per unique session_id, sorted\n * by the ts of the earliest row in each bucket\n */\nexport function readLedgerForWorkItem(\n workItemId: string,\n opts: ReadLedgerOptions = {}\n): SessionBucket[] {\n // Resolve sprint-runs root\n let sprintRunsRoot: string;\n if (opts.sprintRunsRoot) {\n sprintRunsRoot = opts.sprintRunsRoot;\n } else {\n const found = findSprintRunsRoot(process.cwd());\n if (!found) {\n return [];\n }\n sprintRunsRoot = found;\n }\n\n if (!fs.existsSync(sprintRunsRoot)) {\n return [];\n }\n\n // Collect all token-ledger.jsonl files across all sprint dirs\n let ledgerFiles: string[];\n try {\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n ledgerFiles = entries\n .filter((e) => e.isDirectory())\n .map((e) => path.join(sprintRunsRoot, e.name, 'token-ledger.jsonl'))\n .filter((f) => fs.existsSync(f));\n } catch {\n return [];\n }\n\n // Parse matching rows from each file\n const matchingRows: LedgerRow[] = [];\n\n for (const ledgerFile of ledgerFiles) {\n let content: string;\n try {\n content = fs.readFileSync(ledgerFile, 'utf-8');\n } catch {\n continue;\n }\n\n const lines = content.split('\\n').filter((l) => l.trim() !== '');\n for (const line of lines) {\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(line) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n const row = normalizeRow(raw);\n\n // Apply since filter\n if (opts.since && row.ts < opts.since) {\n continue;\n }\n\n if (rowMatchesWorkItem(row, workItemId)) {\n matchingRows.push(row);\n }\n }\n }\n\n // Group by session_id\n const sessionMap = new Map<string, LedgerRow[]>();\n for (const row of matchingRows) {\n const key = row.session_id || '(unknown-session)';\n const existing = sessionMap.get(key);\n if (existing) {\n existing.push(row);\n } else {\n sessionMap.set(key, [row]);\n }\n }\n\n // Build buckets and sort by earliest row ts within each bucket\n const buckets = Array.from(sessionMap.entries()).map(([session_id, rows]) => {\n rows.sort((a, b) => a.ts.localeCompare(b.ts));\n return buildBucket(session_id, rows);\n });\n\n // Sort buckets by earliest row ts\n buckets.sort((a, b) => {\n const aTs = a.rows[0]?.ts ?? '';\n const bTs = b.rows[0]?.ts ?? '';\n return aTs.localeCompare(bTs);\n });\n\n return buckets;\n}\n\n","/**\n * upgrade.ts — STORY-009-05\n *\n * `cleargate upgrade [--dry-run] [--yes] [--only <tier>]`\n *\n * Three-way merge driver: walks each tracked scaffold file, classifies its\n * drift state, routes by overwrite_policy, and applies the user-chosen merge\n * action. Snapshot is updated atomically after every successfully handled file\n * so the command is resumable (re-run continues from the last clean state).\n *\n * Special-case handling:\n * - CLAUDE.md tier (cli-config): uses claude-md-surgery to merge only the\n * CLEARGATE:START…CLEARGATE:END bounded block, leaving user prose intact.\n * - settings.json tier (cli-config): uses settings-json-surgery to merge\n * only ClearGate-owned hooks, leaving user hooks intact.\n *\n * CRITICAL: all file mutations are atomic (write .tmp → fs.rename).\n * INCREMENTAL: each file is independent. Failure on file N does not roll back\n * file N-1. Re-running re-classifies against the updated snapshot.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n * Inject `promptMergeChoice` + `openInEditor` via `UpgradeCliOptions` for tests\n * (FLASHCARD #cli #determinism #test-seam).\n */\n\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n type ManifestFile,\n type ManifestEntry,\n type DriftMap,\n type Tier,\n} from '../lib/manifest.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { readBlock, writeBlock } from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\nimport {\n promptMergeChoice as defaultPromptMergeChoice,\n type MergeChoice,\n} from '../lib/merge-ui.js';\nimport {\n openInEditor as defaultOpenInEditor,\n containsConflictMarkers,\n} from '../lib/editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface UpgradeCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: override the package root for loadPackageManifest. */\n packageRoot?: string;\n /** Test seam: inject a custom promptMergeChoice (avoids interactive stdin). */\n promptMergeChoice?: typeof defaultPromptMergeChoice;\n /** Test seam: inject a custom openInEditor (avoids forking real editors). */\n openInEditor?: typeof defaultOpenInEditor;\n /** Test seam: inject a custom stdin for promptMergeChoice. */\n stdin?: NodeJS.ReadableStream;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/** Write file atomically: write to .tmp, then rename. Never leaves partial writes. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = filePath + '.tmp.' + Date.now();\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/**\n * Update a single file's sha256 in the install snapshot atomically.\n * Reads the snapshot file, patches the matching entry, and re-writes atomically.\n */\nasync function updateSnapshotEntry(\n projectRoot: string,\n filePath: string,\n newSha: string | null\n): Promise<void> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n let snapshot: ManifestFile;\n\n try {\n const raw = await fsp.readFile(snapshotPath, 'utf-8');\n snapshot = JSON.parse(raw) as ManifestFile;\n } catch {\n // If no snapshot exists yet, cannot update — silently skip.\n return;\n }\n\n const updated: ManifestFile = {\n ...snapshot,\n files: snapshot.files.map((entry) =>\n entry.path === filePath ? { ...entry, sha256: newSha } : entry\n ),\n };\n\n await writeAtomic(snapshotPath, JSON.stringify(updated, null, 2) + '\\n');\n}\n\n/**\n * Determine if this is a CLAUDE.md file (needs block surgery).\n */\nfunction isClaudeMd(filePath: string): boolean {\n return path.basename(filePath) === 'CLAUDE.md';\n}\n\n/**\n * Determine if this is a settings.json file (needs hook surgery).\n */\nfunction isSettingsJson(filePath: string): boolean {\n return path.basename(filePath) === 'settings.json' && filePath.includes('.claude');\n}\n\n/**\n * Merge-3way handler: always-policy — overwrite file with package content.\n * Used for `always` overwrite_policy files.\n */\nasync function applyAlwaysOverwrite(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n stdout: (s: string) => void\n): Promise<void> {\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n try {\n const pkgContent = await fsp.readFile(sourcePath, 'utf-8');\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, pkgContent);\n await updateSnapshotEntry(projectRoot, entry.path, entry.sha256);\n stdout(`[always] overwritten: ${entry.path}`);\n } catch (err) {\n stdout(`[always] error overwriting ${entry.path}: ${(err as Error).message}`);\n }\n}\n\n/**\n * Merge-3way handler for `merge-3way` overwrite_policy files.\n * Supports k/t/e choices with conflict-marker semantics for 'e'.\n * Returns the post-merge sha or null if skipped/failed.\n */\nasync function applyMerge3Way(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n installSha: string | null,\n currentSha: string | null,\n flags: { yes?: boolean; dryRun?: boolean },\n opts: {\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n promptMergeChoiceFn: typeof defaultPromptMergeChoice;\n openInEditorFn: typeof defaultOpenInEditor;\n stdin?: NodeJS.ReadableStream;\n }\n): Promise<{ updated: boolean; newSha: string | null }> {\n const { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin } = opts;\n\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n // Read current (ours) and package (theirs) content\n let ours = '';\n let theirs = '';\n\n try {\n ours = await fsp.readFile(targetPath, 'utf-8');\n } catch {\n // File missing on disk — treat as empty\n ours = '';\n }\n\n try {\n theirs = await fsp.readFile(sourcePath, 'utf-8');\n } catch {\n // Package file missing — skip\n stdout(`[merge] skip: package file not found for ${entry.path}`);\n return { updated: false, newSha: null };\n }\n\n // Compute drift state for display\n const state = classify(entry.sha256, installSha, currentSha, entry.tier);\n\n let choice: MergeChoice;\n\n if (flags.yes) {\n // --yes: auto-take-theirs\n choice = 't';\n stdout(`[yes] taking theirs: ${entry.path} state=${state}`);\n } else {\n // Interactive prompt\n choice = await promptMergeChoiceFn({\n path: entry.path,\n state,\n ours,\n theirs,\n stdin,\n stdout,\n });\n }\n\n if (choice === 'k') {\n // Keep mine: file unchanged, snapshot records current_sha as installed_sha\n stdout(`[keep] ${entry.path}`);\n await updateSnapshotEntry(projectRoot, entry.path, currentSha);\n return { updated: true, newSha: currentSha };\n }\n\n if (choice === 't') {\n // Take theirs: apply CLAUDE.md or settings.json surgery if needed, else raw overwrite\n let mergedContent = theirs;\n\n if (isClaudeMd(entry.path)) {\n // Merge only the bounded block; preserve user prose\n try {\n const ourBlock = readBlock(ours);\n const theirBlock = readBlock(theirs);\n if (ourBlock !== null && theirBlock !== null) {\n mergedContent = writeBlock(ours, theirBlock);\n } else if (theirBlock !== null) {\n // No block in ours — full overwrite\n mergedContent = theirs;\n }\n // If no block in theirs, skip (no ClearGate content to take)\n } catch {\n // Surgery failed — fall back to full overwrite\n mergedContent = theirs;\n }\n } else if (isSettingsJson(entry.path)) {\n // Merge only ClearGate-owned hooks; preserve user hooks\n try {\n const ourSettings = JSON.parse(ours) as ClaudeSettings;\n const theirSettings = JSON.parse(theirs) as ClaudeSettings;\n // Remove ClearGate hooks from ours, then add ClearGate hooks from theirs\n const withoutOurCg = removeClearGateHooks(ourSettings);\n // Build merged: user hooks from ours + ClearGate hooks from theirs\n const cgHooks = theirSettings.hooks ?? {};\n const merged: ClaudeSettings = { ...withoutOurCg };\n if (Object.keys(cgHooks).length > 0) {\n merged.hooks = { ...(withoutOurCg.hooks ?? {}), ...cgHooks };\n }\n mergedContent = JSON.stringify(merged, null, 2) + '\\n';\n } catch {\n // Surgery failed — full overwrite\n mergedContent = theirs;\n }\n }\n\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, mergedContent);\n const newSha = hashNormalized(mergedContent);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[take] ${entry.path}`);\n return { updated: true, newSha };\n }\n\n // choice === 'e': write conflict-marker file + open editor\n const mergeFilePath = targetPath + '.cleargate-merge';\n const conflictContent =\n `<<<<<<< ours (installed)\\n${ours}=======\\n${theirs}>>>>>>> theirs (upstream)\\n`;\n\n await fsp.mkdir(path.dirname(mergeFilePath), { recursive: true });\n await writeAtomic(mergeFilePath, conflictContent);\n\n try {\n const result = await openInEditorFn(mergeFilePath);\n if (result.exitCode !== 0) {\n stderr(`[edit] editor exited with code ${result.exitCode}; markers may remain in ${mergeFilePath}`);\n }\n } catch (err) {\n stderr(`[edit] could not open editor: ${(err as Error).message}`);\n stderr(`[edit] resolve markers manually in: ${mergeFilePath}`);\n return { updated: false, newSha: null };\n }\n\n // Read post-edit content\n let edited = '';\n try {\n edited = await fsp.readFile(mergeFilePath, 'utf-8');\n } catch {\n stderr(`[edit] could not read ${mergeFilePath} after editor exit`);\n return { updated: false, newSha: null };\n }\n\n if (containsConflictMarkers(edited)) {\n stderr(`[edit] unresolved conflict markers remain in ${mergeFilePath}`);\n stderr(`[edit] file NOT updated. Resolve manually and re-run upgrade.`);\n // Leave .cleargate-merge in place for manual resolution\n return { updated: false, newSha: null };\n }\n\n // Markers resolved — overwrite target file, remove merge file\n await writeAtomic(targetPath, edited);\n try {\n await fsp.unlink(mergeFilePath);\n } catch {\n // Non-fatal if cleanup fails\n }\n\n const newSha = hashNormalized(edited);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[edit] resolved: ${entry.path}`);\n return { updated: true, newSha };\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function upgradeHandler(\n flags: { dryRun?: boolean; yes?: boolean; only?: string },\n cli?: UpgradeCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n const promptMergeChoiceFn = cli?.promptMergeChoice ?? defaultPromptMergeChoice;\n const openInEditorFn = cli?.openInEditor ?? defaultOpenInEditor;\n const stdin = cli?.stdin;\n\n // ─── 1. Load manifests + compute drift ──────────────────────────────────────\n\n let pkgManifest: ManifestFile;\n try {\n pkgManifest = loadPackageManifest({ packageRoot: cli?.packageRoot });\n } catch (err) {\n stderr(`[upgrade] ${(err as Error).message}`);\n exit(1);\n return;\n }\n\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // Build a lookup from the snapshot\n const snapshotByPath = new Map<string, string | null>();\n for (const entry of installSnapshot?.files ?? []) {\n snapshotByPath.set(entry.path, entry.sha256);\n }\n\n // ─── 2. Apply --only <tier> filter ──────────────────────────────────────────\n\n const onlyTier: Tier | undefined = flags.only as Tier | undefined;\n const filteredFiles = onlyTier\n ? pkgManifest.files.filter((e) => e.tier === onlyTier)\n : pkgManifest.files;\n\n // ─── 3. Classify all files ──────────────────────────────────────────────────\n\n interface FileWork {\n entry: ManifestEntry;\n currentSha: string | null;\n installSha: string | null;\n action: 'overwrite' | 'skip' | 'merge-3way';\n }\n\n const workItems: FileWork[] = [];\n\n await Promise.all(\n filteredFiles.map(async (entry) => {\n if (entry.tier === 'user-artifact') {\n // Always skip user-artifact tier (never tracked)\n return;\n }\n\n const currentSha = await computeCurrentSha(entry, cwd);\n const installSha = snapshotByPath.get(entry.path) ?? null;\n\n let action: FileWork['action'];\n switch (entry.overwrite_policy) {\n case 'always':\n action = 'overwrite';\n break;\n case 'skip':\n case 'preserve':\n action = 'skip';\n break;\n case 'merge-3way':\n default:\n action = 'merge-3way';\n break;\n }\n\n workItems.push({ entry, currentSha, installSha, action });\n })\n );\n\n // Sort for deterministic output\n workItems.sort((a, b) => a.entry.path.localeCompare(b.entry.path));\n\n // ─── 4. --dry-run: print plan and exit ──────────────────────────────────────\n\n if (flags.dryRun) {\n let count = 0;\n for (const item of workItems) {\n const state = classify(item.entry.sha256, item.installSha, item.currentSha, item.entry.tier);\n stdout(`[dry-run] ${item.entry.path} action=${item.action} state=${state}`);\n count++;\n }\n stdout(`[dry-run] ${count} files planned. No changes made.`);\n return;\n }\n\n // ─── 5. Execute per file ─────────────────────────────────────────────────────\n\n // Determine package root for reading source files.\n // cli.packageRoot is the test seam (always injected in tests).\n // In production, use the same default resolution as loadPackageManifest.\n // Since we already loaded the manifest above (which resolves the path internally),\n // we simply use cli.packageRoot when provided, otherwise fall back to cwd\n // (in production the actual resolution is inside loadPackageManifest).\n const packageRoot = cli?.packageRoot ?? cwd;\n\n const driftMap: DriftMap = {};\n\n for (const item of workItems) {\n const { entry, currentSha, installSha, action } = item;\n\n switch (action) {\n case 'skip': {\n // never / preserve — no prompt, no write\n stdout(`[skip] ${entry.path} policy=${entry.overwrite_policy}`);\n break;\n }\n\n case 'overwrite': {\n // always — overwrite silently\n await applyAlwaysOverwrite(entry, cwd, packageRoot, stdout);\n break;\n }\n\n case 'merge-3way': {\n // Interactive 3-way merge (unless --yes)\n await applyMerge3Way(\n entry,\n cwd,\n packageRoot,\n installSha,\n currentSha,\n { yes: flags.yes, dryRun: false },\n { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin }\n );\n break;\n }\n }\n\n // Re-compute current sha after potential mutation (for drift map)\n const postSha = await computeCurrentSha(entry, cwd);\n driftMap[entry.path] = {\n state: classify(entry.sha256, installSha, postSha, entry.tier),\n entry,\n install_sha: installSha,\n current_sha: postSha,\n package_sha: entry.sha256,\n };\n }\n\n // ─── 6. Refresh .drift-state.json ────────────────────────────────────────────\n\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n stdout('[upgrade] complete.');\n}\n","export const CLEARGATE_START = '<!-- CLEARGATE:START -->';\nexport const CLEARGATE_END = '<!-- CLEARGATE:END -->';\n\n// IMPORTANT: regex MUST be GREEDY ([\\s\\S]* not [\\s\\S]*?)\n// The block body itself may reference both markers in prose (FLASHCARD 2026-04-19 #init #inject-claude-md #regex).\n// Non-greedy would stop at the first inline END marker in prose, cutting off the real block.\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->([\\s\\S]*)<!-- CLEARGATE:END -->/;\n\n/**\n * Returns the content between CLEARGATE:START and CLEARGATE:END markers.\n * Returns null if either marker is missing.\n */\nexport function readBlock(content: string): string | null {\n const match = BLOCK_REGEX.exec(content);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Replaces the content between CLEARGATE:START and CLEARGATE:END markers,\n * preserving the markers themselves and all surrounding content.\n * Throws if markers are missing.\n */\nexport function writeBlock(content: string, newBlockBody: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, `${CLEARGATE_START}${newBlockBody}${CLEARGATE_END}`);\n}\n\n/**\n * Removes both markers AND the content between them, leaving surrounding content intact.\n * Throws if markers are missing.\n */\nexport function removeBlock(content: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, '');\n}\n","export interface HookCommand {\n type: 'command';\n command: string;\n if?: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n}\n\nexport interface ClaudeSettings {\n hooks?: {\n PostToolUse?: HookEntry[];\n SessionStart?: HookEntry[];\n SubagentStop?: HookEntry[];\n [k: string]: HookEntry[] | undefined;\n };\n [k: string]: unknown;\n}\n\n/**\n * Returns true if the given command string belongs to ClearGate.\n * Matches:\n * - .claude/hooks/token-ledger.sh\n * - .claude/hooks/stamp-and-gate.sh\n * - .claude/hooks/session-start.sh\n * - .claude/hooks/wiki-ingest.sh (legacy)\n * - .claude/hooks/cleargate-*.sh (catch-all for future hooks)\n * - inline commands containing 'wiki ingest' (legacy SPRINT-04 PostToolUse inline)\n */\nfunction isClearGateCommand(command: string): boolean {\n if (command.includes('wiki ingest')) return true;\n return /\\/\\.claude\\/hooks\\/(token-ledger|stamp-and-gate|session-start|wiki-ingest|cleargate-[^/]*)\\.sh/.test(command);\n}\n\n/**\n * Removes ClearGate-owned hook entries from a ClaudeSettings object.\n * - Removes individual inner `hooks[]` sub-entries whose `command` matches ClearGate patterns.\n * - If the parent HookEntry's `hooks[]` array becomes empty, removes that HookEntry.\n * - If an event-category array (e.g. hooks.PostToolUse) becomes empty, removes the key.\n * - Preserves all other hook entries and all other top-level keys.\n */\nexport function removeClearGateHooks(settings: ClaudeSettings): ClaudeSettings {\n if (!settings.hooks) return { ...settings };\n\n const newHooks: NonNullable<ClaudeSettings['hooks']> = {};\n\n for (const [eventName, entries] of Object.entries(settings.hooks)) {\n if (!entries) continue;\n\n const filteredEntries: HookEntry[] = [];\n\n for (const entry of entries) {\n if (!entry.hooks || entry.hooks.length === 0) {\n // No inner hooks — keep as-is (not a ClearGate entry)\n filteredEntries.push(entry);\n continue;\n }\n\n const remainingInnerHooks = entry.hooks.filter(\n (h) => !isClearGateCommand(h.command)\n );\n\n if (remainingInnerHooks.length === 0) {\n // All inner hooks were ClearGate — drop this HookEntry entirely\n continue;\n }\n\n if (remainingInnerHooks.length === entry.hooks.length) {\n // No change — keep original entry reference\n filteredEntries.push(entry);\n } else {\n // Some removed — keep entry with remaining inner hooks\n filteredEntries.push({ ...entry, hooks: remainingInnerHooks });\n }\n }\n\n if (filteredEntries.length > 0) {\n newHooks[eventName] = filteredEntries;\n }\n // If filteredEntries is empty, skip the key entirely\n }\n\n const result: ClaudeSettings = { ...settings };\n\n if (Object.keys(newHooks).length > 0) {\n result.hooks = newHooks;\n } else {\n delete result.hooks;\n }\n\n return result;\n}\n\n/**\n * Returns true if settings contains any ClearGate-owned hook entries.\n */\nexport function hasClearGateHooks(settings: ClaudeSettings): boolean {\n if (!settings.hooks) return false;\n\n for (const entries of Object.values(settings.hooks)) {\n if (!entries) continue;\n for (const entry of entries) {\n if (!entry.hooks) continue;\n for (const h of entry.hooks) {\n if (isClearGateCommand(h.command)) return true;\n }\n }\n }\n\n return false;\n}\n","/**\n * merge-ui.ts — STORY-009-05\n *\n * Diff renderer + 3-choice interactive prompt for `cleargate upgrade`.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { createPatch } from 'diff';\nimport type { DriftState } from './manifest.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeChoice = 'k' | 't' | 'e';\n\n// ─── Diff renderer ────────────────────────────────────────────────────────────\n\n/**\n * Render an inline unified diff of `ours` vs `theirs` for the given `filePath`.\n * Uses the `diff` npm package's `createPatch`.\n */\nexport function renderInlineDiff(ours: string, theirs: string, filePath: string): string {\n return createPatch(filePath, ours, theirs, 'installed', 'upstream');\n}\n\n// ─── Prompt ───────────────────────────────────────────────────────────────────\n\n/**\n * Print file info + inline diff, then prompt the user for a merge choice.\n * Returns one of: 'k' (keep mine), 't' (take theirs), 'e' (edit in $EDITOR).\n *\n * Test seam: pass `stdin` to drive from a buffer instead of process.stdin.\n * Test seam: pass `stdout` to capture output instead of process.stdout.write.\n */\nexport async function promptMergeChoice(opts: {\n path: string;\n state: DriftState;\n ours: string;\n theirs: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n}): Promise<MergeChoice> {\n const { path: filePath, state, ours, theirs } = opts;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stdin = opts.stdin ?? process.stdin;\n\n // Print file header\n stdout(`\\n[merge] ${filePath} state=${state}\\n`);\n\n // Print diff\n const patch = renderInlineDiff(ours, theirs, filePath);\n stdout(patch + '\\n');\n\n // Prompt\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR: ');\n\n return new Promise<MergeChoice>((resolve, reject) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n const answer = buf.slice(0, newline).trim().toLowerCase();\n stdin.removeListener('data', onData);\n stdin.removeListener('error', onError);\n if (answer === 'k' || answer === 't' || answer === 'e') {\n resolve(answer as MergeChoice);\n } else {\n // Default to 'k' (keep mine) for unrecognised input\n stdout(`Unknown choice '${answer}'; defaulting to [k]eep mine.\\n`);\n resolve('k');\n }\n }\n };\n\n const onError = (err: Error) => {\n stdin.removeListener('data', onData);\n reject(err);\n };\n\n stdin.on('data', onData);\n stdin.once('error', onError);\n });\n}\n","/**\n * editor.ts — STORY-009-05\n *\n * Safely spawn $EDITOR for the `cleargate upgrade` edit-in-editor flow.\n * Uses child_process.spawn with stdio: 'inherit' — does NOT use execSync\n * (would block the event loop and prevent test injection).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { spawn } from 'node:child_process';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Spawn $EDITOR for `filePath`, waiting for the editor process to exit.\n *\n * Returns the editor's exit code.\n * If `opts.editor` is provided, uses it instead of $EDITOR.\n * If neither is set, returns `{ exitCode: -1 }` with an error (handled by caller).\n *\n * Uses `stdio: 'inherit'` so the terminal is fully connected to the editor.\n * Do NOT use execSync — it blocks the event loop and breaks tests.\n */\nexport async function openInEditor(\n filePath: string,\n opts?: { editor?: string; env?: NodeJS.ProcessEnv }\n): Promise<{ exitCode: number }> {\n const env = opts?.env ?? process.env;\n const editor = opts?.editor ?? env['EDITOR'] ?? env['VISUAL'];\n\n if (!editor) {\n throw new Error('$EDITOR not set; cannot [e]dit option. Set the EDITOR environment variable.');\n }\n\n return new Promise<{ exitCode: number }>((resolve, reject) => {\n const child = spawn(editor, [filePath], {\n stdio: 'inherit',\n env: { ...env },\n });\n\n child.on('error', (err) => {\n reject(new Error(`Failed to start editor '${editor}': ${err.message}`));\n });\n\n child.on('close', (code) => {\n resolve({ exitCode: code ?? 0 });\n });\n });\n}\n\n/**\n * Return true if `content` contains unresolved conflict markers.\n *\n * Checks for any of:\n * <<<<<<< ours\n * =======\n * >>>>>>> theirs\n */\nexport function containsConflictMarkers(content: string): boolean {\n return (\n content.includes('<<<<<<< ours') ||\n content.includes('>>>>>>> theirs') ||\n // Also catch generic git-style markers in case user merged manually\n /^<<<<<<< /m.test(content) ||\n /^>>>>>>> /m.test(content)\n );\n}\n","/**\n * uninstall.ts — STORY-009-07\n *\n * `cleargate uninstall [--dry-run] [--preserve <tiers>] [--remove <tiers>] [--yes] [--path <dir>] [--force]`\n *\n * Most-destructive command in the CLI. Preservation-first: user artifacts\n * (FLASHCARD, archive, pending-sync, sprint retrospectives) are kept by default.\n * Framework files (knowledge, templates, wiki, hook-log, agents, hooks, skills)\n * are removed. CLAUDE.md and settings.json are surgically stripped — the rest\n * of those files is left intact.\n *\n * Safety rails:\n * - Typed confirmation: user must type the project name (or pass --yes).\n * - Uncommitted-changes check: git status --porcelain on manifest-tracked files.\n * - --dry-run: preview only, zero disk writes.\n * - Single-target: does NOT recurse into nested .cleargate/ directories.\n * - Idempotency: if .uninstalled marker exists and .install-manifest.json is absent → \"already uninstalled\".\n *\n * All test-facing behaviours are injectable via UninstallOptions seams.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport {\n loadInstallSnapshot,\n type ManifestFile,\n type ManifestEntry,\n type Tier,\n} from '../lib/manifest.js';\nimport {\n removeBlock,\n CLEARGATE_START,\n CLEARGATE_END,\n} from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Tiers that belong to \"user-artifact\" bucket (preserved by default). */\nconst USER_ARTIFACT_TIERS: Tier[] = ['user-artifact'];\n\n/** Framework tiers that are removed by default. */\nconst FRAMEWORK_TIERS: Tier[] = ['protocol', 'template', 'agent', 'hook', 'skill', 'cli-config', 'derived'];\n\nexport interface UninstallOptions {\n cwd?: string;\n path?: string; // resolved target dir (defaults to cwd)\n dryRun?: boolean;\n yes?: boolean; // skip typed confirmation\n force?: boolean; // override uncommitted-changes refusal\n preserve?: string[]; // tier ids to force-preserve (comma-split at CLI level)\n remove?: string[]; // tier ids to force-remove (comma-split at CLI level, 'all' = all tiers)\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: prompt for typed project-name confirmation. */\n promptName?: () => Promise<string>;\n /** Test seam: prompt yes/no for interactive category decisions. */\n promptYesNo?: (q: string) => Promise<boolean>;\n /** Test seam: injectable clock. */\n now?: () => Date;\n /** Test seam: git status --porcelain runner. Returns { stdout, code }. */\n git?: (args: string[]) => { stdout: string; code: number };\n}\n\nexport interface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n removed: string[];\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse a tier list string (comma-separated) into a Set.\n * 'all' expands to all known tiers.\n */\nfunction parseTierList(raw: string[]): Set<Tier> {\n const result = new Set<Tier>();\n for (const item of raw) {\n for (const t of item.split(',')) {\n const tier = t.trim();\n if (tier === 'all') {\n for (const f of FRAMEWORK_TIERS) result.add(f);\n for (const u of USER_ARTIFACT_TIERS) result.add(u);\n } else {\n result.add(tier as Tier);\n }\n }\n }\n return result;\n}\n\n/**\n * Determine if an entry should be preserved (true) or removed (false).\n *\n * Rules (highest priority first):\n * 1. --remove tier override → remove\n * 2. --preserve tier override → preserve\n * 3. user-artifact tier → preserve (default)\n * 4. Framework tiers (protocol/template/agent/hook/skill/cli-config/derived) → remove (default)\n */\nexport function shouldPreserve(\n entry: ManifestEntry,\n preserveSet: Set<Tier>,\n removeSet: Set<Tier>\n): boolean {\n if (removeSet.has(entry.tier)) return false;\n if (preserveSet.has(entry.tier)) return true;\n if (USER_ARTIFACT_TIERS.includes(entry.tier)) return true;\n return false;\n}\n\n/**\n * Read the project name from package.json (name field), or fall back to\n * the basename of the target directory.\n */\nfunction resolveProjectName(target: string): string {\n const pkgPath = path.join(target, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf-8');\n const parsed = JSON.parse(raw) as { name?: string };\n if (parsed.name && typeof parsed.name === 'string') {\n return parsed.name;\n }\n } catch {\n // Fall through to basename\n }\n }\n return path.basename(target);\n}\n\n/**\n * Run git status --porcelain in the target directory, filtered to manifest-tracked paths.\n * Returns the list of uncommitted files that overlap with the manifest.\n *\n * Non-git targets return an empty array (skip silently per orchestrator decision).\n */\nfunction detectUncommittedChanges(\n target: string,\n manifestPaths: string[],\n gitRunner?: UninstallOptions['git']\n): string[] {\n const run = gitRunner ?? ((args: string[]) => {\n try {\n const out = execSync(['git', ...args].join(' '), {\n cwd: target,\n stdio: ['pipe', 'pipe', 'pipe'],\n encoding: 'utf-8',\n });\n return { stdout: out, code: 0 };\n } catch (e: unknown) {\n const err = e as { stdout?: string; status?: number };\n return { stdout: err.stdout ?? '', code: err.status ?? 1 };\n }\n });\n\n // First check if this is a git repo at all\n const isGit = run(['-C', target, 'rev-parse', '--is-inside-work-tree']);\n if (isGit.code !== 0) {\n // Not a git repo — skip silently (orchestrator decision)\n return [];\n }\n\n const result = run(['-C', target, 'status', '--porcelain']);\n if (result.code !== 0) {\n return [];\n }\n\n // Parse porcelain output — each line: XY path\n const changedFiles = result.stdout\n .split('\\n')\n .filter(Boolean)\n .map((line) => line.slice(3).trim());\n\n // Only flag files that are listed in the install manifest\n const manifestSet = new Set(manifestPaths);\n return changedFiles.filter((f) => manifestSet.has(f));\n}\n\n/**\n * Remove @cleargate/cli from package.json dependencies/devDependencies.\n * Writes back if modified. Returns true if modified.\n */\nasync function removeFromPackageJson(target: string, dryRun: boolean): Promise<boolean> {\n const pkgPath = path.join(target, 'package.json');\n if (!fs.existsSync(pkgPath)) return false;\n\n let raw: string;\n try {\n raw = await fsp.readFile(pkgPath, 'utf-8');\n } catch {\n return false;\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n let modified = false;\n\n for (const key of ['dependencies', 'devDependencies'] as const) {\n const deps = parsed[key];\n if (deps && typeof deps === 'object' && '@cleargate/cli' in (deps as Record<string, unknown>)) {\n const updated = { ...(deps as Record<string, unknown>) };\n delete updated['@cleargate/cli'];\n parsed[key] = updated;\n modified = true;\n }\n }\n\n if (modified && !dryRun) {\n await fsp.writeFile(pkgPath, JSON.stringify(parsed, null, 2) + '\\n', 'utf-8');\n }\n\n return modified;\n}\n\n/** Atomically write a file (tmp → rename). */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/** Remove a file, silently ignoring missing files. */\nasync function removeFile(filePath: string): Promise<void> {\n try {\n await fsp.unlink(filePath);\n } catch {\n // Ignore ENOENT and other errors\n }\n}\n\n/** Remove a directory tree, silently ignoring missing directories. */\nasync function removeDir(dirPath: string): Promise<void> {\n try {\n fs.rmSync(dirPath, { recursive: true, force: true });\n } catch {\n // Ignore\n }\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function uninstallHandler(opts: UninstallOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = opts.exit ?? ((code: number) => process.exit(code) as never);\n const now = opts.now ?? (() => new Date());\n const dryRun = opts.dryRun ?? false;\n const yes = opts.yes ?? false;\n const force = opts.force ?? false;\n\n // Parse tier overrides\n const preserveSet = parseTierList(opts.preserve ?? []);\n const removeSet = parseTierList(opts.remove ?? []);\n const removeAll = (opts.remove ?? []).some((r) => r === 'all');\n if (removeAll) {\n for (const t of FRAMEWORK_TIERS) removeSet.add(t);\n for (const u of USER_ARTIFACT_TIERS) removeSet.add(u);\n }\n\n // ─── 1. Resolve target ────────────────────────────────────────────────────────\n\n const target = opts.path ? path.resolve(opts.path) : cwd;\n const cleargateDir = path.join(target, '.cleargate');\n const manifestPath = path.join(cleargateDir, '.install-manifest.json');\n const uninstalledPath = path.join(cleargateDir, '.uninstalled');\n\n // ─── 2. Missing manifest → no install detected ────────────────────────────────\n\n if (!fs.existsSync(manifestPath)) {\n // Check idempotency: if .uninstalled exists without manifest → already done\n if (fs.existsSync(uninstalledPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 3. Idempotency short-circuit ────────────────────────────────────────────\n\n if (fs.existsSync(uninstalledPath) && !fs.existsSync(manifestPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n\n // ─── 4. Load install manifest ────────────────────────────────────────────────\n\n const snapshot: ManifestFile | null = await loadInstallSnapshot(target);\n if (!snapshot) {\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 5. Uncommitted-changes check ────────────────────────────────────────────\n\n if (!force) {\n const manifestPaths = snapshot.files.map((e) => e.path);\n const uncommitted = detectUncommittedChanges(target, manifestPaths, opts.git);\n if (uncommitted.length > 0) {\n stderr(\n `Uncommitted changes to tracked files: ${uncommitted.slice(0, 5).join(', ')}${uncommitted.length > 5 ? ` and ${uncommitted.length - 5} more` : ''}. Commit, stash, or pass --force.`\n );\n exit(1);\n return;\n }\n }\n\n // ─── 6. CLAUDE.md marker check ───────────────────────────────────────────────\n\n const claudeMdPath = path.join(target, 'CLAUDE.md');\n let claudeMdContent: string | null = null;\n\n if (fs.existsSync(claudeMdPath)) {\n claudeMdContent = fs.readFileSync(claudeMdPath, 'utf-8');\n if (!claudeMdContent.includes(CLEARGATE_START)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n exit(1);\n return;\n }\n if (!claudeMdContent.includes(CLEARGATE_END)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n exit(1);\n return;\n }\n }\n\n // ─── 7. Classify entries ──────────────────────────────────────────────────────\n\n const toRemove: ManifestEntry[] = [];\n const toPreserve: ManifestEntry[] = [];\n const toSkip: ManifestEntry[] = []; // missing from disk\n\n for (const entry of snapshot.files) {\n const filePath = path.join(target, entry.path);\n if (!fs.existsSync(filePath)) {\n toSkip.push(entry);\n continue;\n }\n if (shouldPreserve(entry, preserveSet, removeSet)) {\n toPreserve.push(entry);\n } else {\n toRemove.push(entry);\n }\n }\n\n // ─── 8. Preview summary ───────────────────────────────────────────────────────\n\n stdout(`Will remove ${toRemove.length} files, keep ${toPreserve.length} files, update CLAUDE.md to strip CLEARGATE block, remove @cleargate/cli from package.json.`);\n\n if (dryRun) {\n stdout('');\n stdout('[dry-run] Planned removals:');\n for (const e of toRemove) {\n stdout(` [remove] ${e.path}`);\n }\n stdout('');\n stdout('[dry-run] Planned preservations:');\n for (const e of toPreserve) {\n stdout(` [keep] ${e.path}`);\n }\n if (toSkip.length > 0) {\n stdout('');\n stdout('[dry-run] Untracked (already missing on disk):');\n for (const e of toSkip) {\n stdout(` [skip] ${e.path}`);\n }\n }\n stdout('');\n stdout('[dry-run] No files changed.');\n exit(0);\n return;\n }\n\n // ─── 9. Typed confirmation (skipped with --yes) ───────────────────────────────\n\n if (!yes) {\n const projectName = resolveProjectName(target);\n\n const promptNameFn = opts.promptName ?? (() => {\n // Real interactive prompt — readline\n return new Promise<string>((resolve) => {\n process.stdout.write(`Type the project name \"${projectName}\" to confirm uninstall: `);\n let buf = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.once('data', (chunk: Buffer | string) => {\n buf = chunk.toString().trim();\n resolve(buf);\n });\n });\n });\n\n const typed = await promptNameFn();\n if (typed !== projectName) {\n stdout('name mismatch — aborting');\n exit(1);\n return;\n }\n }\n\n // ─── 10. Execute removal ──────────────────────────────────────────────────────\n\n const removedPaths: string[] = [];\n const preservedPaths: string[] = [];\n\n // Remove classified entries\n for (const entry of toRemove) {\n const filePath = path.join(target, entry.path);\n await removeFile(filePath);\n removedPaths.push(entry.path);\n }\n\n // Collect preserved paths\n for (const entry of toPreserve) {\n preservedPaths.push(entry.path);\n }\n\n // Surgery: CLAUDE.md — strip only the CLEARGATE block\n if (claudeMdContent !== null) {\n try {\n const stripped = removeBlock(claudeMdContent);\n await writeAtomic(claudeMdPath, stripped);\n removedPaths.push('CLAUDE.md (CLEARGATE block)');\n } catch (err) {\n stderr(`Warning: could not strip CLAUDE.md block: ${(err as Error).message}`);\n }\n }\n\n // Surgery: settings.json — remove ClearGate hooks only\n const settingsPath = path.join(target, '.claude', 'settings.json');\n if (fs.existsSync(settingsPath)) {\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as ClaudeSettings;\n const cleaned = removeClearGateHooks(settings);\n await writeAtomic(settingsPath, JSON.stringify(cleaned, null, 2) + '\\n');\n removedPaths.push('.claude/settings.json (ClearGate hooks)');\n } catch (err) {\n stderr(`Warning: could not update settings.json: ${(err as Error).message}`);\n }\n }\n\n // Remove @cleargate/cli from package.json\n const pkgModified = await removeFromPackageJson(target, false);\n if (pkgModified) {\n removedPaths.push('package.json (@cleargate/cli dep)');\n stdout('Removed @cleargate/cli from package.json. Run `npm install` to update package-lock.json.');\n }\n\n // Remove the install manifest itself (and drift-state)\n await removeFile(manifestPath);\n await removeFile(path.join(cleargateDir, '.drift-state.json'));\n\n // ─── 11. Write .uninstalled marker ───────────────────────────────────────────\n\n const marker: UninstalledMarker = {\n uninstalled_at: now().toISOString(),\n prior_version: snapshot.cleargate_version,\n preserved: preservedPaths,\n removed: removedPaths,\n };\n\n // Ensure .cleargate/ dir still exists (may have had files removed from it)\n await fsp.mkdir(cleargateDir, { recursive: true });\n await writeAtomic(uninstalledPath, JSON.stringify(marker, null, 2) + '\\n');\n\n // ─── 12. Empty .cleargate/ cleanup ───────────────────────────────────────────\n\n // Per story Gherkin scenario 11: when --remove all removes all user-artifact\n // items too (nothing preserved inside .cleargate/), the directory itself is\n // removed (including the .uninstalled marker we just wrote).\n // In the default case (preservations exist), we leave .cleargate/ with the\n // preserved files + .uninstalled marker.\n if (removeAll) {\n const hasPreservedInsideCleargate = preservedPaths.some((p) =>\n p.startsWith('.cleargate/')\n );\n if (!hasPreservedInsideCleargate) {\n // Remove the entire .cleargate/ directory (including the marker we wrote)\n await removeDir(cleargateDir);\n }\n }\n\n // ─── 13. Restore hint ────────────────────────────────────────────────────────\n\n if (preservedPaths.length > 0) {\n stdout(`Preserved ${preservedPaths.length} items. Run cleargate init in this directory to restore.`);\n }\n}\n","/**\n * sync.ts — STORY-010-04\n *\n * `cleargate sync` — canonical 6-step driver:\n * 1. Identity + sprint resolve\n * 2. List remote updates + pull each item\n * (3. STORY-010-05 insertion point: runIntakeBranch)\n * 4. Classify conflicts per local work items\n * 5. Resolve (merge prompt / silent merge / remote-wins / refuse)\n * 6. Apply + log\n *\n * R2 invariant: ALL pulls complete before ANY push begins.\n *\n * --dry-run: steps 1–4 only; zero fs writes; zero sync-log entries.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { classify } from '../lib/conflict-detector.js';\nimport type { SinceLastSync, LocalSnapshot, RemoteSnapshot } from '../lib/conflict-detector.js';\nimport { promptThreeWayMerge } from '../lib/merge-helper.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteUpdateRef } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { runIntakeBranch } from '../lib/intake.js';\nimport type { IntakeResult } from '../lib/intake.js';\nimport { resolveActiveItems } from '../lib/active-criteria.js';\nimport type { LocalWorkItemRef } from '../lib/active-criteria.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\nimport type { RemoteComment } from '../lib/mcp-client.js';\n\n// ── Public Options ─────────────────────────────────────────────────────────────\n\nexport interface SyncOptions {\n dryRun?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (bypasses acquireAccessToken) */\n mcp?: McpClient;\n /** Test seam: override process.stdin for merge prompt */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── SyncCheckOptions ──────────────────────────────────────────────────────────\n\nexport interface SyncCheckOptions {\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n/**\n * syncCheckHandler — `cleargate sync --check`\n *\n * Hook-safe read-only drift probe. Exits 0 on ALL failure paths.\n * Emits a single JSON line to stdout: {\"updates\":<N>,\"since\":\"<iso>\"} on success\n * or {\"updates\":0,\"error\":\"<msg>\"} on failure.\n *\n * Never writes sync-log entries, never touches .conflicts.json, never pushes.\n * Updates .cleargate/.sync-marker.json with last_check timestamp (even on error,\n * to throttle repeat attempts).\n *\n * FLASHCARD: #hook-safe #sync-check #exit-code\n * syncHandler exits 2 on missing token/URL/adapter. This handler MUST NOT.\n */\nexport async function syncCheckHandler(opts: SyncCheckOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n const markerPath = path.join(projectRoot, '.cleargate', '.sync-marker.json');\n\n // Helper: write marker atomically, even on error (throttle repeat calls)\n const updateMarker = async (nowIso: string): Promise<void> => {\n try {\n const content = JSON.stringify({ last_check: nowIso });\n await fsPromises.mkdir(path.dirname(markerPath), { recursive: true });\n const tmpPath = `${markerPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, markerPath);\n } catch {\n // Ignore marker write failures — do not surface to caller\n }\n };\n\n // Helper: emit error JSON and exit 0\n const emitError = async (msg: string, nowIso: string): Promise<void> => {\n stdout(JSON.stringify({ updates: 0, error: msg.slice(0, 200) }) + '\\n');\n await updateMarker(nowIso);\n };\n\n const nowIso = nowFn();\n\n // Read last_check from marker; fall back to epoch\n let since = '1970-01-01T00:00:00.000Z';\n try {\n const raw = await fsPromises.readFile(markerPath, 'utf8');\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (typeof parsed['last_check'] === 'string') {\n since = parsed['last_check'];\n }\n } catch {\n // No marker file or parse error — use epoch\n }\n\n // Resolve MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n // Acquire token — hook-safe: any AcquireError → emitError, exit 0\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Pre-flight: adapter info (but don't exit 2 — emit error JSON instead)\n try {\n const adapterInfo = await mcp.adapterInfo();\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n } catch {\n // If adapterInfo fails, proceed — older MCP may not have this tool\n }\n\n // Call list_remote_updates\n let refs: RemoteUpdateRef[];\n try {\n refs = await mcp.call<RemoteUpdateRef[]>('cleargate_list_remote_updates', { since });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n await emitError(msg, nowIso);\n return;\n }\n\n // Success path\n stdout(JSON.stringify({ updates: refs.length, since }) + '\\n');\n await updateMarker(nowIso);\n}\n\n// ── ConflictJson shape ────────────────────────────────────────────────────────\n\nexport interface ConflictEntry {\n item_id: string;\n remote_id: string;\n state: string;\n resolution: string;\n reason: string;\n local_path: string;\n}\n\nexport interface ConflictsJson {\n generated_at: string;\n sprint_id: string;\n unresolved: ConflictEntry[];\n}\n\n// ── Main handler ──────────────────────────────────────────────────────────────\n\nexport async function syncHandler(opts: SyncOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const dryRun = opts.dryRun ?? false;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // ── Step 1: Identity + sprint resolve ───────────────────────────────────────\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintRoot);\n\n // ── MCP client setup ────────────────────────────────────────────────────────\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n if (err.code === 'token_revoked') {\n stderr(`Error: ${err.message}\\n`);\n } else if (err.code === 'no_stored_token' || err.code === 'invalid_token') {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${err.message}\\n`);\n }\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Pre-flight: adapter-info probe ──────────────────────────────────────────\n let adapterInfo: { configured: boolean; name: string };\n try {\n adapterInfo = await mcp.adapterInfo();\n } catch {\n // Tool may not exist on older MCP versions — warn and proceed\n adapterInfo = { configured: true, name: 'unknown' };\n }\n\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n stderr(\n 'Error: ClearGate MCP has no PM adapter configured (LINEAR_API_KEY missing server-side). ' +\n 'Sync cannot proceed.\\n',\n );\n exit(2);\n return;\n }\n\n // ── Step 2: List remote updates + pull ──────────────────────────────────────\n // Read last_remote_sync from wiki meta or use epoch\n const wikiMetaPath = path.join(projectRoot, '.cleargate', 'wiki', 'meta.json');\n let lastRemoteSync = '1970-01-01T00:00:00.000Z';\n try {\n const metaRaw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n const meta = JSON.parse(metaRaw) as Record<string, unknown>;\n if (typeof meta['last_remote_sync'] === 'string') {\n lastRemoteSync = meta['last_remote_sync'];\n }\n } catch {\n // No meta file — start from epoch\n }\n\n const remoteRefs = await mcp.call<RemoteUpdateRef[]>(\n 'cleargate_list_remote_updates',\n { since: lastRemoteSync },\n );\n\n // Pull each item — all awaited BEFORE any push (R2 invariant)\n const pulled: RemoteItem[] = [];\n for (const ref of remoteRefs) {\n const item = await mcp.call<RemoteItem | null>(\n 'cleargate_pull_item',\n { remote_id: ref.remote_id },\n );\n if (item !== null) {\n pulled.push(item);\n }\n }\n\n // ── Step 3: Stakeholder proposal intake (STORY-010-05) ──────────────────────\n const labelFilter = (opts.env ?? process.env)['CLEARGATE_PROPOSAL_LABEL'] ?? 'cleargate:proposal';\n let intakeResult: IntakeResult = { created: 0, items: [] };\n try {\n intakeResult = await runIntakeBranch({\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter,\n now: nowFn,\n });\n } catch (err) {\n // Non-fatal: intake errors do not abort the main sync loop\n stderr(`warn: intake branch failed: ${String(err)}\\n`);\n }\n\n // Emit R10 warning if present\n if (intakeResult.warning) {\n stderr(`${intakeResult.warning}\\n`);\n }\n\n // ── Step 3b: Pull comments for active items (STORY-010-06) ─────────────────\n // Load all local items first (needed for active-criteria + wiki render).\n const localItems = await scanLocalItems(projectRoot);\n\n if (!dryRun) {\n const localRefs: LocalWorkItemRef[] = localItems.map(({ fm }) => ({\n primaryId: getItemId(fm),\n remoteId: typeof fm['remote_id'] === 'string' && fm['remote_id'] ? fm['remote_id'] : undefined,\n lastRemoteUpdate: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : undefined,\n }));\n\n const activeSet = await resolveActiveItems(projectRoot, localRefs, nowFn);\n\n for (const remoteId of activeSet) {\n try {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n await renderCommentsSection({ projectRoot, remoteId, comments, localItems });\n } catch (err: unknown) {\n // R4 mitigation: per-item try/catch — do NOT break the outer loop\n const errMsg = err instanceof Error ? err.message : String(err);\n if (/MCP HTTP 429/.test(errMsg)) {\n // Find primary ID for logging\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'skipped-rate-limit',\n detail: '429',\n });\n } else {\n // Other errors: log as error-transport, continue\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'error-transport',\n detail: errMsg.slice(0, 200),\n });\n }\n }\n }\n }\n\n // ── Step 4: Classify local work items ───────────────────────────────────────\n // localItems already loaded above for comment-pull; reuse here.\n const conflictsJson: ConflictEntry[] = [];\n\n // Maps for tracking what to apply\n type QueuedPull = { item: RemoteItem; localPath: string; fm: Record<string, unknown>; body: string };\n type QueuedPush = { localPath: string; fm: Record<string, unknown>; body: string; itemId: string };\n\n const pullQueue: QueuedPull[] = [];\n const pushQueue: QueuedPush[] = [];\n\n const pulledByRemoteId = new Map<string, RemoteItem>();\n for (const item of pulled) {\n pulledByRemoteId.set(item.remote_id, item);\n }\n\n let dryRunPulls = 0;\n let dryRunPushes = 0;\n let dryRunConflicts = 0;\n\n for (const { localPath, fm, body } of localItems) {\n const remoteIdVal = fm['remote_id'];\n if (typeof remoteIdVal !== 'string' || !remoteIdVal) continue;\n\n const remoteItem = pulledByRemoteId.get(remoteIdVal);\n if (!remoteItem) continue;\n\n const lastBodySha = typeof fm['last_synced_body_sha'] === 'string' ? fm['last_synced_body_sha'] : null;\n const localBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n\n const localSnap: LocalSnapshot = {\n updated_at: typeof fm['updated_at'] === 'string' ? fm['updated_at'] : '1970-01-01T00:00:00Z',\n body_sha: localBodySha,\n status: typeof fm['status'] === 'string' ? fm['status'] : '',\n deleted: false,\n };\n\n const remoteSnap: RemoteSnapshot = {\n updated_at: remoteItem.updated_at,\n body_sha: remoteBodySha,\n status: remoteItem.status,\n deleted: false,\n };\n\n const since: SinceLastSync = {\n last_pushed_at: typeof fm['pushed_at'] === 'string' ? fm['pushed_at'] : null,\n last_pulled_at: typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null,\n last_remote_update: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : null,\n last_body_sha: lastBodySha,\n last_synced_status: typeof fm['last_synced_status'] === 'string' ? fm['last_synced_status'] : null,\n };\n\n const classification = classify(localSnap, remoteSnap, since);\n\n if (dryRun) {\n if (classification.resolution === 'pull') dryRunPulls++;\n else if (classification.resolution === 'push') dryRunPushes++;\n else if (classification.resolution === 'refuse' || classification.resolution === 'halt') dryRunConflicts++;\n continue;\n }\n\n // ── Step 5: Resolve ──────────────────────────────────────────────────────\n switch (classification.resolution) {\n case 'pull':\n case 'merge-silent':\n case 'remote-wins':\n pullQueue.push({ item: remoteItem, localPath, fm, body });\n break;\n\n case 'push':\n if (fm['approved'] === true) {\n pushQueue.push({ localPath, fm, body, itemId: getItemId(fm) });\n }\n break;\n\n case 'merge': {\n // Three-way merge prompt\n const mergeResult = await promptThreeWayMerge({\n local: body,\n remote: remoteItem.body ?? '',\n base: '',\n itemId: getItemId(fm),\n stdin: opts.stdin ?? process.stdin,\n stdout,\n });\n if (mergeResult.resolution === 'aborted') {\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n } else {\n // Apply merge result\n pullQueue.push({\n item: { ...remoteItem, body: mergeResult.body },\n localPath,\n fm,\n body,\n });\n }\n break;\n }\n\n case 'refuse':\n case 'halt':\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n // Do NOT abort — continue processing remaining items (AC §2.1)\n break;\n\n default:\n break;\n }\n }\n\n // ── dry-run summary and early exit ──────────────────────────────────────────\n if (dryRun) {\n stdout(\n `Would pull: ${dryRunPulls}, push: ${dryRunPushes}, ` +\n `intake: ${intakeResult.created}, conflicts: ${dryRunConflicts}\\n`,\n );\n return;\n }\n\n // ── Step 6: Apply + log ──────────────────────────────────────────────────────\n // R2: ALL pulls applied before pushes\n for (const { item, localPath, fm } of pullQueue) {\n await applyPull(item, localPath, fm, identity.email, nowFn);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n for (const { localPath, fm, body, itemId } of pushQueue) {\n // Push item to MCP\n await mcp.call('push_item', {\n cleargate_id: itemId,\n type: typeof fm['story_id'] === 'string' ? 'story'\n : typeof fm['epic_id'] === 'string' ? 'epic'\n : typeof fm['proposal_id'] === 'string' ? 'proposal'\n : 'story',\n payload: fm,\n });\n\n // Stamp last_synced_status + last_synced_body_sha so classify() sees a clean\n // baseline on the next sync run (mirrors the pull-apply path at applyPull:393-394).\n const pushedFm: Record<string, unknown> = {\n ...fm,\n last_synced_status: fm['status'],\n last_synced_body_sha: hashNormalized(body),\n };\n const newContent = serializeFrontmatter(pushedFm) + '\\n\\n' + body;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Log conflicts\n for (const c of conflictsJson) {\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'conflict-refused',\n target: c.item_id,\n remote_id: c.remote_id,\n result: 'halted',\n detail: c.reason,\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Atomic-write .conflicts.json\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n const conflictsContent: ConflictsJson = {\n generated_at: nowFn(),\n sprint_id: sprintId,\n unresolved: conflictsJson,\n };\n await writeAtomic(conflictsFile, JSON.stringify(conflictsContent, null, 2) + '\\n');\n\n // Update wiki meta last_remote_sync\n try {\n await fsPromises.mkdir(path.dirname(wikiMetaPath), { recursive: true });\n let meta: Record<string, unknown> = {};\n try {\n const raw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n meta = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // New meta\n }\n meta['last_remote_sync'] = nowFn();\n await writeAtomic(wikiMetaPath, JSON.stringify(meta, null, 2) + '\\n');\n } catch {\n // Non-fatal\n }\n\n const totalPulls = pullQueue.length;\n const totalPushes = pushQueue.length;\n const totalConflicts = conflictsJson.length;\n stdout(`sync: pulled ${totalPulls}, pushed ${totalPushes}, conflicts ${totalConflicts}\\n`);\n\n // Print intake summary (orchestrator stdout format)\n if (intakeResult.created > 0) {\n const plural = intakeResult.created === 1 ? 'proposal' : 'proposals';\n const inlineList = intakeResult.items\n .map((item) => `${item.proposalId} (${item.remoteId} '${item.title}')`)\n .join(', ');\n stdout(`📥 ${intakeResult.created} new stakeholder ${plural} pulled: ${inlineList}\\n`);\n stdout(` — review at .cleargate/delivery/pending-sync/\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Apply a remote item to the local file: update frontmatter + body. */\nasync function applyPull(\n item: RemoteItem,\n localPath: string,\n fm: Record<string, unknown>,\n actorEmail: string,\n nowFn: () => string,\n): Promise<void> {\n const now = nowFn();\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: item.status,\n last_pulled_by: actorEmail,\n last_pulled_at: now,\n last_remote_update: item.updated_at,\n last_synced_status: item.status,\n last_synced_body_sha: hashNormalized(item.body ?? ''),\n };\n\n const newBody = item.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n}\n\n/** Atomic write: write to .tmp file then rename. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\ninterface LocalWorkItem {\n localPath: string;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Scan .cleargate/delivery/pending-sync/ for tracked work items with remote_id. */\nasync function scanLocalItems(projectRoot: string): Promise<LocalWorkItem[]> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const results: LocalWorkItem[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm, body } = parseFrontmatter(raw);\n if (typeof fm['remote_id'] === 'string' && fm['remote_id']) {\n results.push({ localPath: fullPath, fm, body });\n }\n } catch {\n // Skip malformed files\n }\n }\n\n return results;\n}\n\n/** Extract primary item ID from frontmatter. */\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * sync-log.ts — append-only JSONL sync audit log.\n *\n * STORY-010-01: defines types + appendSyncLog + readSyncLog + resolveActiveSprintDir.\n *\n * Atomicity guarantee for appendSyncLog:\n * Uses fs.promises.appendFile with default flag 'a' (O_APPEND).\n * POSIX guarantees that concurrent O_APPEND writes ≤ PIPE_BUF (4 KB) are atomic\n * — a sync-log line is < 500 bytes so lines from concurrent writers never interleave.\n * We deliberately use appendFile (not read-then-write) to preserve this guarantee.\n *\n * Token redaction:\n * detail fields containing JWT tokens (eyJ…) are redacted before writing.\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\n/** R-014 rule ID constant (defined here for STORY-010-08 to wire into lint pipeline). */\nexport const R014 = 'sync-attribution-missing' as const;\n\nexport type SyncLogOp =\n | 'push'\n | 'pull'\n | 'pull-intake' // STORY-010-05: stakeholder proposal intake\n | 'pull-comments' // STORY-010-06: comment snapshot pull\n | 'push-revert'\n | 'sync-status'\n | 'conflict-remote-wins'\n | 'conflict-refused';\n\nexport type SyncLogResult =\n | 'ok'\n | 'no-op'\n | 'error-not-found'\n | 'error-transport'\n | 'skipped-rate-limit'\n | 'halted';\n\nexport interface SyncLogEntry {\n ts: string;\n actor: string;\n op: SyncLogOp;\n target: string;\n remote_id?: string;\n result: SyncLogResult;\n detail?: string;\n}\n\n// ── Active-sprint resolution ─────────────────────────────────────────────────\n\n/**\n * Resolve the active sprint directory under .cleargate/sprint-runs/.\n *\n * Strategy: scan sprint-runs/* (excluding _off-sprint), pick the entry with the\n * newest mtime. If none exist, create and return _off-sprint/.\n *\n * Open Decision (flagged to orchestrator): story §1.2 says \"read INDEX.md for\n * active sprint\", but INDEX.md has no machine-readable status:active field —\n * only execution_order integers inside sprint frontmatter. Newest-mtime dir is\n * the reliable signal available today.\n */\nexport function resolveActiveSprintDir(\n projectRoot: string,\n _opts?: { now?: () => string },\n): string {\n const sprintRunsRoot = path.join(projectRoot, '.cleargate', 'sprint-runs');\n const offSprint = path.join(sprintRunsRoot, '_off-sprint');\n\n if (!fs.existsSync(sprintRunsRoot)) {\n fs.mkdirSync(sprintRunsRoot, { recursive: true });\n fs.mkdirSync(offSprint, { recursive: true });\n return offSprint;\n }\n\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n const sprintDirs = entries\n .filter((e) => e.isDirectory() && e.name !== '_off-sprint')\n .map((e) => {\n const fullPath = path.join(sprintRunsRoot, e.name);\n const stat = fs.statSync(fullPath);\n return { name: e.name, fullPath, mtimeMs: stat.mtimeMs };\n })\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n\n if (sprintDirs.length === 0) {\n if (!fs.existsSync(offSprint)) {\n fs.mkdirSync(offSprint, { recursive: true });\n }\n return offSprint;\n }\n\n return sprintDirs[0].fullPath;\n}\n\n// ── JWT redaction ─────────────────────────────────────────────────────────────\n\nfunction redactDetail(detail: string | undefined): string | undefined {\n if (detail === undefined) return undefined;\n return detail.replace(/eyJ[A-Za-z0-9._-]+/g, '[REDACTED]');\n}\n\n// ── Append ────────────────────────────────────────────────────────────────────\n\n/**\n * Append one JSONL entry to <sprintRoot>/sync-log.jsonl.\n * Creates the file and parent directory if absent.\n * Uses O_APPEND for POSIX atomicity — never read-modify-write.\n */\nexport async function appendSyncLog(\n sprintRoot: string,\n entry: SyncLogEntry,\n): Promise<void> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n // Ensure directory exists\n await fsPromises.mkdir(sprintRoot, { recursive: true });\n\n const safeEntry: SyncLogEntry = {\n ...entry,\n detail: redactDetail(entry.detail),\n };\n\n const line = JSON.stringify(safeEntry) + '\\n';\n\n // appendFile with flag 'a' → O_APPEND; POSIX guarantees line-atomicity for <PIPE_BUF writes\n await fsPromises.appendFile(logPath, line, { encoding: 'utf8' });\n}\n\n// ── Read ──────────────────────────────────────────────────────────────────────\n\n/**\n * Read and optionally filter sync-log entries, returning newest-first.\n * Skips malformed lines silently — partial writes never crash the reader.\n */\nexport async function readSyncLog(\n sprintRoot: string,\n filters?: { actor?: string; op?: SyncLogOp; target?: string },\n): Promise<SyncLogEntry[]> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(logPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw err;\n }\n\n const entries: SyncLogEntry[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n try {\n const parsed = JSON.parse(trimmed) as SyncLogEntry;\n entries.push(parsed);\n } catch {\n // malformed line — skip silently\n }\n }\n\n // Apply filters\n let result = entries;\n if (filters?.actor !== undefined) {\n result = result.filter((e) => e.actor === filters.actor);\n }\n if (filters?.op !== undefined) {\n result = result.filter((e) => e.op === filters.op);\n }\n if (filters?.target !== undefined) {\n result = result.filter((e) => e.target === filters.target);\n }\n\n // Newest first (entries are appended oldest-first)\n return result.reverse();\n}\n","/**\n * conflict-detector.ts — STORY-010-03\n *\n * Pure classifier for local-vs-remote sync conflicts.\n * Implements the 8-state PROP-007 §2.3 matrix + explicit 9th \"unknown\" fallthrough.\n *\n * No I/O. No imports from node:fs, node:child_process, node:os.\n * No imports from commands/ or bin/.\n */\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type ConflictState =\n | 'no-change'\n | 'local-only' // local content edit, no remote change\n | 'remote-only' // remote status/metadata change, local untouched\n | 'content-content' // both bodies diverged since last sync\n | 'content-status' // local body + remote status\n | 'status-status' // both statuses diverged\n | 'local-delete-remote-edit'\n | 'remote-delete-local-edit'\n | 'unknown'; // R3: explicit fallthrough — never silently wrong\n\nexport type Resolution =\n | 'push'\n | 'pull'\n | 'merge' // three-way merge prompt\n | 'merge-silent' // content+status: no prompt, apply both sides\n | 'remote-wins' // status+status: silent remote takes, log conflict-remote-wins\n | 'refuse' // halt, surface to human\n | 'halt'; // unknown state — halt sync with explicit message\n\nexport interface LocalSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface RemoteSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface SinceLastSync {\n last_pushed_at: string | null;\n last_pulled_at: string | null;\n last_remote_update: string | null; // ISO-8601 string or null; opaque per M1 lock\n last_body_sha: string | null; // sha at last successful sync (merge-base)\n last_synced_status: string | null; // status recorded at last successful sync (rule 6)\n}\n\nexport interface Classification {\n state: ConflictState;\n resolution: Resolution;\n reason: string; // human-readable; used for sync-log detail + halt messages\n}\n\n// ─── Classifier ───────────────────────────────────────────────────────────────\n\n/**\n * classify — pure function; maps a (local, remote, since) triple to a Classification.\n *\n * Decision table follows PROP-007 §2.3 + M2 plan \"8-state + 9th fallthrough\" exactly.\n * States are evaluated in priority order; first match wins.\n */\nexport function classify(\n local: LocalSnapshot,\n remote: RemoteSnapshot,\n since: SinceLastSync,\n): Classification {\n const baseSha = since.last_body_sha ?? '';\n const lastPulled = since.last_pulled_at ?? '0';\n const lastPushed = since.last_pushed_at ?? '0';\n\n // Rule 7 — local-delete-remote-edit (check deletes first — highest priority)\n if (local.deleted && remote.updated_at > lastPulled) {\n return {\n state: 'local-delete-remote-edit',\n resolution: 'refuse',\n reason: 'local deletion conflicts with remote edit',\n };\n }\n\n // Rule 8 — remote-delete-local-edit\n if (remote.deleted && local.updated_at > lastPushed) {\n return {\n state: 'remote-delete-local-edit',\n resolution: 'refuse',\n reason: 'remote deletion conflicts with local edit',\n };\n }\n\n // Rule 1 — no-change: bodies and status are identical at sync base; nothing deleted\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status === remote.status &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'no-change',\n resolution: 'pull',\n reason: 'no change since last sync',\n };\n }\n\n // Rule 4 — content-content: both bodies diverged\n if (\n local.body_sha !== baseSha &&\n remote.body_sha !== baseSha &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'content-content',\n resolution: 'merge',\n reason: 'both bodies diverged — three-way merge required',\n };\n }\n\n // Rule 5 — content-status: local body changed + remote status changed, bodies otherwise aligned\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status !== local.status\n ) {\n return {\n state: 'content-status',\n resolution: 'merge-silent',\n reason: 'local body edit + remote status change — merged without prompt',\n };\n }\n\n // Rule 2 — local-only: local content edited, remote unchanged, same status\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status === local.status &&\n !remote.deleted\n ) {\n return {\n state: 'local-only',\n resolution: 'push',\n reason: 'local content edit only',\n };\n }\n\n // Rule 6 — status-status: both sides changed status since last sync\n // Requires since.last_synced_status to distinguish from remote-only.\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status !== remote.status &&\n since.last_synced_status !== null &&\n local.status !== since.last_synced_status &&\n remote.status !== since.last_synced_status\n ) {\n return {\n state: 'status-status',\n resolution: 'remote-wins',\n reason: 'status diverged on both sides; remote authoritative',\n };\n }\n\n // Rule 3 — remote-only: local body unchanged, remote status or metadata changed\n if (\n local.body_sha === baseSha &&\n (remote.status !== local.status || remote.updated_at > lastPulled)\n ) {\n return {\n state: 'remote-only',\n resolution: 'pull',\n reason: 'remote status/metadata change only',\n };\n }\n\n // Rule 9 — unknown fallthrough (R3): explicitly refuse to guess\n return {\n state: 'unknown',\n resolution: 'halt',\n reason:\n 'conflict shape not recognized — please resolve manually and file a ClearGate bug with this sync-log entry',\n };\n}\n","/**\n * merge-helper.ts — STORY-010-03\n *\n * Three-way merge prompt for `cleargate sync` content-content conflicts.\n * Reuses EPIC-009 primitives: renderInlineDiff (merge-ui.ts), openInEditor +\n * containsConflictMarkers (editor.ts).\n *\n * Adds the fourth [a]bort branch on top of merge-ui's k/t/e set.\n * Never writes to the caller's file — returns MergeResult; caller applies.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { renderInlineDiff } from './merge-ui.js';\nimport { openInEditor, containsConflictMarkers } from './editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeResolution = 'keep' | 'take' | 'edited' | 'aborted';\n\nexport interface MergeResult {\n resolution: MergeResolution;\n body: string; // the chosen/edited body\n}\n\nexport interface PromptThreeWayMergeOpts {\n local: string;\n remote: string;\n base: string; // merge-base body; used to construct git-merge-markers on [e]dit\n itemId: string; // for diff header + temp-file name\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n editor?: string; // override $EDITOR for tests\n now?: () => string; // temp-file name determinism seam\n}\n\n// ─── Four-choice prompt (extends merge-ui's k/t/e with [a]bort) ──────────────\n\ntype FourChoice = 'k' | 't' | 'e' | 'a';\n\n/**\n * promptFourChoice — renders the four-option prompt and reads one line from stdin.\n * Ctrl-C (stream 'close' without data) and 'a' both resolve to 'a' (abort).\n */\nfunction promptFourChoice(opts: {\n stdin: NodeJS.ReadableStream;\n stdout: (s: string) => void;\n}): Promise<FourChoice> {\n const { stdin, stdout } = opts;\n\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR / [a]bort: ');\n\n return new Promise<FourChoice>((resolve) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n cleanup();\n const answer = buf.slice(0, newline).trim().toLowerCase();\n if (answer === 'k' || answer === 't' || answer === 'e' || answer === 'a') {\n resolve(answer as FourChoice);\n } else {\n stdout(`Unknown choice '${answer}'; treating as [a]bort.\\n`);\n resolve('a');\n }\n }\n };\n\n // Ctrl-C or stdin close without data → abort\n const onClose = () => {\n cleanup();\n resolve('a');\n };\n\n const onEnd = () => {\n cleanup();\n resolve('a');\n };\n\n const onError = () => {\n cleanup();\n resolve('a');\n };\n\n function cleanup() {\n stdin.removeListener('data', onData);\n stdin.removeListener('close', onClose);\n stdin.removeListener('end', onEnd);\n stdin.removeListener('error', onError);\n }\n\n stdin.on('data', onData);\n stdin.once('close', onClose);\n stdin.once('end', onEnd);\n stdin.once('error', onError);\n });\n}\n\n// ─── Main export ──────────────────────────────────────────────────────────────\n\n/**\n * promptThreeWayMerge — interactive three-way merge UX.\n *\n * Flow:\n * 1. Renders unified diff of local vs remote.\n * 2. Prompts [k]eep / [t]ake / [e]dit / [a]bort.\n * 3. On [e]dit: writes a temp file with git-merge-marker content, spawns $EDITOR,\n * re-reads on close, re-prompts if conflict markers remain.\n * Temp file is always unlinked (finally block).\n * 4. On [a]bort or Ctrl-C: returns { resolution: 'aborted', body: local }.\n * 5. Never writes to caller's file.\n */\nexport async function promptThreeWayMerge(opts: PromptThreeWayMergeOpts): Promise<MergeResult> {\n const {\n local,\n remote,\n itemId,\n stdin = process.stdin,\n editor,\n } = opts;\n\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const now = opts.now ?? (() => Date.now().toString());\n\n // 1. Render diff\n const patch = renderInlineDiff(local, remote, itemId);\n stdout(`\\n[merge] ${itemId}\\n`);\n stdout(patch + '\\n');\n\n // 2–3. Prompt loop (re-prompt if editor leaves unresolved markers)\n for (;;) {\n const choice = await promptFourChoice({ stdin, stdout });\n\n switch (choice) {\n case 'k':\n return { resolution: 'keep', body: local };\n\n case 't':\n return { resolution: 'take', body: remote };\n\n case 'a':\n return { resolution: 'aborted', body: local };\n\n case 'e': {\n const tmpFile = path.join(os.tmpdir(), `cleargate-merge-${itemId}-${now()}.md`);\n const markerContent = `<<<<<<< local\\n${local}\\n=======\\n${remote}\\n>>>>>>> remote\\n`;\n\n try {\n await fs.writeFile(tmpFile, markerContent, 'utf-8');\n\n await openInEditor(tmpFile, { editor: editor ?? process.env['EDITOR'] ?? 'vi' });\n\n const edited = await fs.readFile(tmpFile, 'utf-8');\n\n if (containsConflictMarkers(edited)) {\n stdout('File still contains conflict markers — please resolve all conflicts.\\n');\n // re-prompt\n continue;\n }\n\n return { resolution: 'edited', body: edited };\n } finally {\n // Always clean up temp file even on error\n await fs.unlink(tmpFile).catch(() => {/* already gone — ignore */});\n }\n }\n }\n }\n}\n","/**\n * mcp-client.ts — STORY-010-04 / updated STORY-011-01\n *\n * Minimal JSON-RPC-over-HTTP client for ClearGate's MCP server.\n *\n * Token acquisition: callers obtain a token via acquireAccessToken() from\n * cleargate-cli/src/auth/acquire.ts and pass it via McpClientOptions.token.\n * This file does NOT read CLEARGATE_MCP_TOKEN or call acquireAccessToken.\n *\n * MCP endpoint: passed directly as McpClientOptions.baseUrl by the caller.\n *\n * Flashcard: no pre-existing MCP client in cleargate-cli — built from scratch here.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\n// ── Wire types (re-exported for consumers) ───────────────────────────────────\n\nexport interface RemoteUpdateRef {\n remote_id: string;\n updated_at: string;\n}\n\nexport interface RemoteItem {\n remote_id: string;\n title: string;\n body: string | null;\n status: string;\n assignees: string[];\n labels: string[];\n updated_at: string;\n source_tool: string;\n raw: unknown;\n}\n\nexport interface RemoteComment {\n id: string;\n author_email: string | null;\n author_name: string;\n body: string;\n created_at: string;\n remote_id: string;\n}\n\nexport interface AdapterInfo {\n configured: boolean;\n name: 'linear' | 'jira' | 'github-projects' | 'no-adapter-configured';\n}\n\n// ── McpClient interface ───────────────────────────────────────────────────────\n\nexport interface McpClient {\n call<T>(tool: string, args: Record<string, unknown>): Promise<T>;\n adapterInfo(): Promise<AdapterInfo>;\n}\n\nexport interface McpClientOptions {\n baseUrl: string;\n token: string;\n /** Test seam: override globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n}\n\n// ── JSON-RPC envelope ─────────────────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n method: 'tools/call';\n params: {\n name: string;\n arguments: Record<string, unknown>;\n };\n id: number;\n}\n\ninterface JsonRpcResponse<T> {\n jsonrpc: '2.0';\n id: number;\n result?: {\n content?: Array<{ type: 'text'; text: string }>;\n structuredContent?: T;\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\nlet _reqId = 1;\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * createMcpClient — build a JSON-RPC client for the ClearGate MCP server.\n *\n * Sends POST to ${baseUrl}/mcp with Authorization: Bearer <token>.\n * Parses the StreamableHTTP response (may be plain JSON or SSE stream).\n */\nexport function createMcpClient(opts: McpClientOptions): McpClient {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n async function call<T>(tool: string, args: Record<string, unknown>): Promise<T> {\n const body: JsonRpcRequest = {\n jsonrpc: '2.0',\n method: 'tools/call',\n params: { name: tool, arguments: args },\n id: _reqId++,\n };\n\n let response: Response;\n try {\n response = await fetchFn(`${opts.baseUrl}/mcp`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json, text/event-stream',\n 'Authorization': `Bearer ${opts.token}`,\n },\n body: JSON.stringify(body),\n });\n } catch (err) {\n throw new Error(`MCP transport error calling ${tool}: ${String(err)}`);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`MCP HTTP ${response.status} calling ${tool}: ${text.slice(0, 256)}`);\n }\n\n const text = await response.text();\n\n // Handle SSE stream: extract last JSON from event-stream data lines\n let jsonText = text;\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/event-stream')) {\n const dataLines = text\n .split('\\n')\n .filter((l) => l.startsWith('data: '))\n .map((l) => l.slice('data: '.length).trim())\n .filter((l) => l !== '' && l !== '[DONE]');\n if (dataLines.length === 0) {\n throw new Error(`MCP SSE response for ${tool} contained no data lines`);\n }\n jsonText = dataLines[dataLines.length - 1];\n }\n\n let parsed: JsonRpcResponse<T>;\n try {\n parsed = JSON.parse(jsonText) as JsonRpcResponse<T>;\n } catch {\n throw new Error(`MCP response for ${tool} is not valid JSON: ${jsonText.slice(0, 256)}`);\n }\n\n if (parsed.error) {\n throw new Error(`MCP tool ${tool} returned error ${parsed.error.code}: ${parsed.error.message}`);\n }\n\n // structuredContent preferred; fall back to parsing text content\n if (parsed.result?.structuredContent !== undefined) {\n return parsed.result.structuredContent as T;\n }\n\n const textContent = parsed.result?.content?.find((c) => c.type === 'text')?.text;\n if (textContent !== undefined) {\n try {\n return JSON.parse(textContent) as T;\n } catch {\n throw new Error(`MCP tool ${tool} text content is not valid JSON: ${textContent.slice(0, 256)}`);\n }\n }\n\n throw new Error(`MCP tool ${tool} returned no content`);\n }\n\n async function adapterInfo(): Promise<AdapterInfo> {\n return call<AdapterInfo>('cleargate_adapter_info', {});\n }\n\n return { call, adapterInfo };\n}\n\n","/**\n * intake.ts — STORY-010-05\n *\n * Stakeholder proposal intake branch for `cleargate sync`.\n *\n * `runIntakeBranch` is called from `syncHandler` at step 3 (after pull, before\n * conflict classification). It:\n * 1. Calls `cleargate_detect_new_items({ label })` to get remote proposals.\n * 2. Deduplicates against pending-sync + archive by `remote_id` frontmatter.\n * 3. For each new item: allocates a `PROP-NNN` ID, slugifies the title,\n * writes a draft proposal file from the template, and appends a sync-log\n * entry with op:'pull-intake'.\n * 4. Emits an R10 warning to stderr if zero items matched AND this appears to\n * be the first intake run for this workspace (no prior `source: remote-authored`\n * files detected).\n * 5. Returns a summary for the end-of-sync stdout print.\n *\n * Respects dryRun: zero fs writes, zero sync-log entries, returns the plan.\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { slugify, nextProposalId, findByRemoteId } from './slug.js';\nimport { appendSyncLog, type SyncLogEntry } from './sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from './frontmatter-yaml.js';\nimport type { McpClient, RemoteItem } from './mcp-client.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface IntakeItem {\n proposalId: string;\n remoteId: string;\n title: string;\n path: string;\n}\n\nexport interface IntakeResult {\n created: number;\n items: IntakeItem[];\n warning?: string;\n}\n\nexport interface IntakeBranchOptions {\n mcp: McpClient;\n identity: { email: string };\n sprintRoot: string;\n projectRoot: string;\n dryRun: boolean;\n labelFilter?: string;\n /** Test seam: override now() */\n now?: () => string;\n}\n\n// ── runIntakeBranch ───────────────────────────────────────────────────────────\n\nexport async function runIntakeBranch(opts: IntakeBranchOptions): Promise<IntakeResult> {\n const {\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter = 'cleargate:proposal',\n now = () => new Date().toISOString(),\n } = opts;\n\n const pendingSyncDir = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n\n // Detect new remote items with the cleargate:proposal label\n let remoteItems: RemoteItem[] = [];\n try {\n remoteItems = await mcp.call<RemoteItem[]>(\n 'cleargate_detect_new_items',\n { label: labelFilter },\n );\n if (!Array.isArray(remoteItems)) {\n remoteItems = [];\n }\n } catch {\n // Non-fatal: if the tool doesn't exist yet, treat as zero items\n remoteItems = [];\n }\n\n // R10: zero-label warning — fires when zero items returned AND this is the\n // first intake run (no existing `source: remote-authored` files found)\n let warning: string | undefined;\n if (remoteItems.length === 0) {\n const hasExistingIntake = await hasAnyRemoteAuthored(projectRoot);\n if (!hasExistingIntake) {\n warning =\n `warn: no Linear issues match label '${labelFilter}' — ` +\n `confirm the label exists in your workspace. See EPIC-010 R10.`;\n }\n }\n\n const createdItems: IntakeItem[] = [];\n\n for (const item of remoteItems) {\n // Idempotency: skip if a local counterpart already exists (in either dir)\n const existingPath = await findByRemoteId(projectRoot, item.remote_id);\n if (existingPath !== null) {\n continue;\n }\n\n if (dryRun) {\n // In dry-run mode: plan the intake without writing anything\n const proposalId = await nextProposalId(projectRoot);\n const slug = slugify(item.title ?? 'untitled');\n const num = proposalId.replace('PROP-', '');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n continue;\n }\n\n // Compute the next proposal ID and filename\n // Note: we recompute after each write so IDs are consistent even when multiple\n // items are being created in the same run\n const proposalId = await nextProposalId(projectRoot);\n const num = proposalId.replace('PROP-', '');\n const slug = slugify(item.title ?? 'untitled');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n const nowTs = now();\n\n // Build frontmatter from template + our sync fields\n const fm: Record<string, unknown> = {\n proposal_id: proposalId,\n remote_id: item.remote_id,\n status: 'Draft',\n approved: false,\n source: 'remote-authored',\n last_pulled_by: identity.email,\n last_pulled_at: nowTs,\n last_remote_update: item.updated_at,\n created_at: nowTs,\n updated_at: nowTs,\n pushed_by: null,\n pushed_at: null,\n last_synced_status: null,\n last_synced_body_sha: null,\n };\n\n // Build body from the proposal template + pre-fill §1 body from remote item\n const body = buildProposalBody(item, projectRoot);\n\n // Atomic write: .tmp + rename\n await fsPromises.mkdir(pendingSyncDir, { recursive: true });\n const content = serializeFrontmatter(fm) + '\\n\\n' + body;\n const tmpPath = `${targetPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, targetPath);\n\n // Sync-log entry\n const logEntry: SyncLogEntry = {\n ts: nowTs,\n actor: identity.email,\n op: 'pull-intake',\n target: proposalId,\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, logEntry);\n\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n }\n\n return {\n created: createdItems.length,\n items: createdItems,\n warning,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Build the body for an intake proposal file.\n * Seeds §1 \"Initiative & Context\" with the remote item body;\n * leaves other sections as template placeholders.\n */\nfunction buildProposalBody(item: RemoteItem, _projectRoot: string): string {\n const title = item.title ?? '(untitled)';\n const remoteBody = item.body ?? '';\n\n return `# ${title}\n\n## 1. Initiative & Context\n\n### 1.1 Objective\n\n${remoteBody || '(pre-filled from Linear issue body)'}\n\n### 1.2 The \"Why\"\n\n{Reason 1}\n{Reason 2}\n\n## 2. Technical Architecture & Constraints\n\n### 2.1 Dependencies\n\n{List required external APIs, packages, or systems}\n\n### 2.2 System Constraints\n\n| Constraint | Details |\n|---|---|\n| Architectural Rules | {e.g., Must use purely functional components} |\n| Security | {e.g., Data must be encrypted at rest.} |\n\n## 3. Scope Impact (Touched Files & Data)\n\n### 3.1 Known Files\n\npath/to/existing/file.ext - {Explanation of expected change}\n\n### 3.2 Expected New Entities\n\npath/to/new/file.ext - {Explanation of purpose}\n\n## Approval Gate\n\n(Vibe Coder: Review this proposal. If the architecture and context are correct, change approved: false to approved: true in the YAML frontmatter. Only then is the AI authorized to proceed with Epic/Story decomposition.)\n`;\n}\n\n/**\n * Check if any `source: remote-authored` file already exists in pending-sync or archive.\n * Used to gate the R10 zero-label warning.\n */\nasync function hasAnyRemoteAuthored(projectRoot: string): Promise<boolean> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n // Quick scan without full parse — look for source: remote-authored in frontmatter\n const fmEnd = raw.indexOf('\\n---', 4);\n if (fmEnd === -1) continue;\n const fmBlock = raw.slice(0, fmEnd);\n if (/source:\\s*['\"]?remote-authored['\"]?/.test(fmBlock)) {\n return true;\n }\n } catch {\n // Skip\n }\n }\n }\n\n return false;\n}\n\n// Re-export parseFrontmatter for consumers that read template files\nexport { parseFrontmatter };\n","/**\n * slug.ts — STORY-010-05\n *\n * Filename slug helper + proposal ID scanner for stakeholder intake.\n *\n * `slugify(title, max)` — deterministic, locale-free slug from a title string.\n * `nextProposalId(projectRoot)` — scans pending-sync + archive for max PROP-NNN\n * and returns the next sequential ID as a zero-padded string.\n * `findByRemoteId(projectRoot, remoteId)` — dedup helper; returns local path on\n * hit, null on miss. Scans frontmatter block only (first --- ... --- delimiters).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── slugify ──────────────────────────────────────────────────────────────────\n\n/**\n * Convert a title string into a URL-safe slug.\n *\n * Algorithm:\n * 1. NFKD-normalize → strip combining marks (é→e, ü→u, etc.)\n * 2. Lowercase.\n * 3. Replace runs of [^a-z0-9]+ with a single dash.\n * 4. Trim leading/trailing dashes.\n * 5. Truncate to `max` characters; re-trim trailing dash.\n * 6. If empty/all-dashes after step 5 → return \"untitled\".\n */\nexport function slugify(title: string, max: number = 40): string {\n // Step 1: NFKD normalize + strip combining marks (Unicode category M)\n const normalized = title.normalize('NFKD').replace(/\\p{M}/gu, '');\n // Step 2: lowercase\n const lowered = normalized.toLowerCase();\n // Step 3: replace non-alphanumeric runs with dash\n const dashed = lowered.replace(/[^a-z0-9]+/g, '-');\n // Step 4: trim leading/trailing dashes\n const trimmed = dashed.replace(/^-+|-+$/g, '');\n // Step 5: truncate to max; re-trim trailing dash\n const truncated = trimmed.slice(0, max).replace(/-+$/, '');\n // Step 6: fallback for empty result\n if (!truncated) {\n return 'untitled';\n }\n return truncated;\n}\n\n// ── nextProposalId ────────────────────────────────────────────────────────────\n\n/** Pattern matching `proposal_id: \"PROP-NNN\"` or `proposal_id: PROP-NNN` */\nconst PROPOSAL_ID_RE = /^proposal_id:\\s*\"?PROP-(\\d+)\"?/m;\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for `.md` files whose frontmatter contains `proposal_id: \"PROP-NNN\"`.\n * Returns the next ID as `\"PROP-<max+1, zero-padded to 3>\"`.\n *\n * Gap-tolerant: returns max+1, NOT the first gap.\n * Empty dirs or no proposals found → `\"PROP-001\"`.\n */\nexport async function nextProposalId(projectRoot: string): Promise<string> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n let maxN = 0;\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n // Read only the frontmatter block (first --- ... ---)\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fmEnd = extractFrontmatterBlock(raw);\n if (!fmEnd) continue;\n const match = PROPOSAL_ID_RE.exec(fmEnd);\n if (!match) continue;\n const n = parseInt(match[1]!, 10);\n if (n > maxN) maxN = n;\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n const next = maxN + 1;\n return `PROP-${String(next).padStart(3, '0')}`;\n}\n\n// ── findByRemoteId ────────────────────────────────────────────────────────────\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for a `.md` file whose frontmatter contains `remote_id: \"<remoteId>\"`.\n *\n * Returns the absolute path of the first match, or `null` if not found.\n * Reads only the frontmatter block (first --- ... ---) for efficiency.\n */\nexport async function findByRemoteId(\n projectRoot: string,\n remoteId: string,\n): Promise<string | null> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n // Build a regex matching `remote_id: \"LIN-NNN\"` or `remote_id: LIN-NNN`\n const escaped = remoteId.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`^remote_id:\\\\s*\"?${escaped}\"?\\\\s*$`, 'm');\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fm = extractFrontmatterBlock(raw);\n if (!fm) continue;\n if (re.test(fm)) return fullPath;\n } catch {\n // Skip\n }\n }\n }\n\n return null;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Extract the YAML content between the first `---` delimiters.\n * Returns the raw YAML text (without the delimiters), or null if not found.\n */\nfunction extractFrontmatterBlock(raw: string): string | null {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return null;\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return null;\n return lines.slice(1, closeIdx).join('\\n');\n}\n","/**\n * active-criteria.ts — STORY-010-06\n *\n * Resolves which work items are \"active\" for the purpose of comment-pull.\n * Active = (item is referenced in the current sprint) OR (item's last_remote_update is within 30 days).\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but NO `stories:` list.\n * Body-regex scan is the only reliable signal available today.\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveActiveSprintDir } from './sync-log.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface LocalWorkItemRef {\n /** Primary cleargate ID (e.g. STORY-010-06, EPIC-010). */\n primaryId: string;\n /** Remote ID in the PM tool (e.g. LIN-1042). May be undefined if not yet pushed. */\n remoteId: string | undefined;\n /** ISO string from `last_remote_update` frontmatter field. */\n lastRemoteUpdate: string | undefined;\n}\n\n// ── Active set resolver ───────────────────────────────────────────────────────\n\n/**\n * Resolve which items (by remote_id) are \"active\" and should have comments fetched.\n *\n * @param projectRoot - absolute path to the project root\n * @param localItems - all local work items with their IDs\n * @param nowFn - injectable clock (for 30-day window)\n * @returns - Set of remote_id values that are active\n */\nexport async function resolveActiveItems(\n projectRoot: string,\n localItems: LocalWorkItemRef[],\n nowFn: () => string = () => new Date().toISOString(),\n): Promise<Set<string>> {\n const active = new Set<string>();\n const now = Date.parse(nowFn());\n const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;\n\n // ── Branch 1: items referenced in the current sprint ─────────────────────\n const inSprintIds = await resolveInSprintIds(projectRoot);\n\n // ── Branch 2: items updated in last 30 days + union of sprint ─────────────\n for (const item of localItems) {\n if (!item.remoteId) continue;\n\n // In-sprint check\n if (inSprintIds.has(item.primaryId)) {\n active.add(item.remoteId);\n continue;\n }\n\n // 30-day window check\n if (item.lastRemoteUpdate) {\n const itemMs = Date.parse(item.lastRemoteUpdate);\n if (!isNaN(itemMs) && (now - itemMs) <= thirtyDaysMs) {\n active.add(item.remoteId);\n }\n }\n }\n\n return active;\n}\n\n// ── Internal: resolve sprint item references ──────────────────────────────────\n\n/**\n * Read the active sprint file and extract all work-item ID references from its body.\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but no `stories:` array.\n * We scan the full body text for IDs matching:\n * (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n *\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n */\nasync function resolveInSprintIds(projectRoot: string): Promise<Set<string>> {\n const ids = new Set<string>();\n\n try {\n const sprintDir = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintDir);\n\n if (sprintId === '_off-sprint') return ids;\n\n // Try pending-sync first, then archive\n const sprintFile = await findSprintFile(projectRoot, sprintId);\n if (!sprintFile) return ids;\n\n const content = await fsPromises.readFile(sprintFile, 'utf8');\n\n // Scan body for work-item ID patterns\n // (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n const pattern = /(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/g;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(content)) !== null) {\n ids.add(match[0]);\n }\n } catch {\n // Non-fatal: if we can't resolve sprint, return empty set\n }\n\n return ids;\n}\n\nasync function findSprintFile(projectRoot: string, sprintId: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isFile() && entry.name.startsWith(sprintId) && entry.name.endsWith('.md')) {\n return path.join(dir, entry.name);\n }\n }\n } catch {\n // Directory not found — try next\n }\n }\n\n return null;\n}\n","/**\n * comments-cache.ts — STORY-010-06\n *\n * Atomic read/write for comment cache files at\n * .cleargate/.comments-cache/<remote_id>.json\n *\n * Contents = raw RemoteComment[] array from cleargate_pull_comments.\n * Write is atomic via .tmp + rename (mirrors sync.ts:writeAtomic).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Cache directory ───────────────────────────────────────────────────────────\n\nfunction cacheDir(projectRoot: string): string {\n return path.join(projectRoot, '.cleargate', '.comments-cache');\n}\n\nfunction cachePath(projectRoot: string, remoteId: string): string {\n return path.join(cacheDir(projectRoot), `${remoteId}.json`);\n}\n\n// ── Write ──────────────────────────────────────────────────────────────────────\n\n/**\n * Atomically write a RemoteComment[] array to the cache for `remoteId`.\n * Creates the cache directory if absent.\n */\nexport async function writeCommentCache(\n projectRoot: string,\n remoteId: string,\n comments: RemoteComment[],\n): Promise<void> {\n const dir = cacheDir(projectRoot);\n await fsPromises.mkdir(dir, { recursive: true });\n\n const filePath = cachePath(projectRoot, remoteId);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n const content = JSON.stringify(comments, null, 2) + '\\n';\n\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Read ───────────────────────────────────────────────────────────────────────\n\n/**\n * Read cached comments for `remoteId`.\n * Returns null if the cache file is absent or contains malformed JSON.\n */\nexport async function readCommentCache(\n projectRoot: string,\n remoteId: string,\n): Promise<RemoteComment[] | null> {\n const filePath = cachePath(projectRoot, remoteId);\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(filePath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n\n try {\n return JSON.parse(raw) as RemoteComment[];\n } catch {\n return null;\n }\n}\n","/**\n * wiki-comments-render.ts — STORY-010-06\n *\n * Renders (inserts / replaces / removes) the \"## Remote comments\" section\n * on an existing wiki page at .cleargate/wiki/<bucket>/<primaryId>.md.\n *\n * DELIBERATELY separate from commands/wiki-ingest.ts which owns full-page\n * rebuild from raw. Section overlay is a different concern and would fight\n * wiki-ingest's SHA-idempotency guard.\n *\n * Delimiter matching uses literal-string indexOf, NOT regex.\n * FLASHCARD #regex #inject-claude-md: fuzzy whitespace regex breaks when the\n * block body itself references both markers in prose — use indexOf exclusively.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Delimiters (literal strings — never change to regex) ───────────────────────\n\nconst START = '<!-- cleargate:comments:start -->';\nconst END = '<!-- cleargate:comments:end -->';\n\n// ── Bucket resolution ─────────────────────────────────────────────────────────\n\n/**\n * Map a frontmatter record to the wiki bucket directory name.\n * Returns null if the item type cannot be determined.\n */\nexport function resolveBucket(fm: Record<string, unknown>): string | null {\n if (typeof fm['story_id'] === 'string' && fm['story_id']) return 'stories';\n if (typeof fm['epic_id'] === 'string' && fm['epic_id']) return 'epics';\n if (typeof fm['proposal_id'] === 'string' && fm['proposal_id']) return 'proposals';\n if (typeof fm['cr_id'] === 'string' && fm['cr_id']) return 'crs';\n if (typeof fm['bug_id'] === 'string' && fm['bug_id']) return 'bugs';\n return null;\n}\n\n/**\n * Extract the primary item ID (e.g. STORY-010-06) from frontmatter.\n */\nexport function getPrimaryId(fm: Record<string, unknown>): string | null {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return null;\n}\n\n// ── Section builder ───────────────────────────────────────────────────────────\n\n/**\n * Build the full delimited comment section string.\n * Sorts comments by created_at ascending.\n */\nexport function buildCommentSection(comments: RemoteComment[]): string {\n const sorted = [...comments].sort((a, b) => {\n return a.created_at < b.created_at ? -1 : a.created_at > b.created_at ? 1 : 0;\n });\n\n const entries = sorted.map((c) => {\n const author = c.author_email\n ? `${c.author_name} (${c.author_email})`\n : c.author_name;\n\n // Multi-line body: prefix every line with \"> \"\n const bodyLines = c.body.split('\\n').map((line) => `> ${line}`).join('\\n');\n\n return `### ${author} · ${c.created_at}\\n${bodyLines}`;\n });\n\n return (\n `${START}\\n` +\n `## Remote comments\\n` +\n `\\n` +\n `_Read-only snapshot. Comments live in the PM tool — reply there, not here._\\n` +\n `\\n` +\n entries.join('\\n\\n') +\n `\\n${END}`\n );\n}\n\n// ── Main export ───────────────────────────────────────────────────────────────\n\nexport interface RenderCommentsSectionOpts {\n /** Absolute path to project root */\n projectRoot: string;\n /** Remote ID (e.g. LIN-1042) */\n remoteId: string;\n /** Comment array from cleargate_pull_comments */\n comments: RemoteComment[];\n /** Local work items to resolve wiki path from */\n localItems: Array<{ fm: Record<string, unknown> }>;\n}\n\n/**\n * Insert / replace / remove the ## Remote comments section on the wiki page\n * corresponding to the given remote_id.\n *\n * - If the wiki page does not exist: no-op (wiki-ingest may not have run yet).\n * - Byte-idempotent: running twice with identical input produces identical output.\n * - Atomic write via .tmp + rename.\n */\nexport async function renderCommentsSection(\n opts: RenderCommentsSectionOpts,\n): Promise<void> {\n const { projectRoot, remoteId, comments, localItems } = opts;\n\n // Find the local item whose remote_id matches\n const localItem = localItems.find(\n (item) => item.fm['remote_id'] === remoteId,\n );\n if (!localItem) return;\n\n const bucket = resolveBucket(localItem.fm);\n const primaryId = getPrimaryId(localItem.fm);\n if (!bucket || !primaryId) return;\n\n const wikiPath = path.join(\n projectRoot,\n '.cleargate',\n 'wiki',\n bucket,\n `${primaryId}.md`,\n );\n\n // Read existing wiki page\n let existing: string;\n try {\n existing = await fsPromises.readFile(wikiPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return; // wiki page not yet built\n throw err;\n }\n\n const startIdx = existing.indexOf(START);\n const endIdx = existing.indexOf(END);\n\n let updated: string;\n\n if (startIdx === -1 && comments.length === 0) {\n // No section, no comments — no-op\n return;\n } else if (startIdx === -1 && comments.length > 0) {\n // Insert new section at end of file\n const section = buildCommentSection(comments);\n const base = existing.endsWith('\\n\\n')\n ? existing.slice(0, -1) // trim one trailing newline\n : existing.endsWith('\\n')\n ? existing\n : existing + '\\n';\n updated = base + '\\n' + section + '\\n';\n } else if (startIdx !== -1 && comments.length > 0) {\n // Replace existing section\n const section = buildCommentSection(comments);\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n\\n' + section + '\\n' + (after ? '\\n' + after : '');\n } else {\n // startIdx !== -1 && comments.length === 0: remove section\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n' + (after ? after : '');\n }\n\n // Atomic write\n await writeAtomic(wikiPath, updated);\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n","/**\n * pull.ts — STORY-010-04\n *\n * `cleargate pull <ID-or-remote_id>` — targeted single-item pull.\n *\n * --comments flag: reserved for STORY-010-06; emits a warn-level message and\n * proceeds without comment pull. Does NOT error. Does NOT accept silently.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteComment } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\n\nexport interface PullOptions {\n comments?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\nexport async function pullHandler(idOrRemoteId: string, opts: PullOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Resolve the remote_id to pull\n // If idOrRemoteId looks like a remote ID (e.g. LIN-1042) use directly;\n // otherwise try to find local file with matching story_id / epic_id / etc.\n const remoteId = await resolveRemoteId(idOrRemoteId, projectRoot);\n if (!remoteId) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a remote_id. Check that the item has been pushed first.\\n`);\n exit(1);\n return;\n }\n\n // Pull from MCP\n const remoteItem = await mcp.call<RemoteItem | null>('cleargate_pull_item', { remote_id: remoteId });\n if (!remoteItem) {\n stderr(`Error: item ${remoteId} not found on MCP server.\\n`);\n exit(1);\n return;\n }\n\n // Find the local file\n const localPath = await findLocalFile(remoteId, projectRoot);\n\n if (!localPath) {\n stderr(`Error: no local file found with remote_id \"${remoteId}\".\\n`);\n exit(1);\n return;\n }\n\n // Read current state and check idempotency\n const rawContent = await fsPromises.readFile(localPath, 'utf8');\n const { fm, body } = parseFrontmatter(rawContent);\n\n const currentBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const lastPulledAt = typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null;\n\n // Idempotency check: if nothing changed since last pull, skip\n const isNoOp =\n currentBodySha === remoteBodySha &&\n currentStatus === remoteItem.status &&\n typeof fm['last_synced_body_sha'] === 'string' &&\n fm['last_synced_body_sha'] === remoteBodySha &&\n lastPulledAt !== null;\n\n const now = nowFn();\n\n if (isNoOp) {\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'no-op',\n };\n await appendSyncLog(sprintRoot, entry);\n stdout(`pull: ${remoteId} no-op (no changes)\\n`);\n return;\n }\n\n // Apply the pull\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: remoteItem.status,\n last_pulled_by: identity.email,\n last_pulled_at: now,\n last_remote_update: remoteItem.updated_at,\n last_synced_status: remoteItem.status,\n last_synced_body_sha: remoteBodySha,\n };\n\n const newBody = remoteItem.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`pull: ${remoteId} applied to ${path.relative(projectRoot, localPath)}\\n`);\n\n // ── --comments: pull comment snapshot for this item ──────────────────────\n // Always pulls when flag is set (manual override; ignores active criteria).\n if (opts.comments) {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n\n // Rebuild updatedFm as the local item state post-pull for wiki-render\n const localItemForRender = { fm: { ...updatedFm, remote_id: remoteId } };\n await renderCommentsSection({\n projectRoot,\n remoteId,\n comments,\n localItems: [localItemForRender],\n });\n stdout(`pull: ${remoteId} comments fetched (${comments.length})\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveRemoteId(idOrRemoteId: string, projectRoot: string): Promise<string | null> {\n // If it looks like a remote ID pattern (e.g. LIN-NNNN, GH-NNNN, JIRA-123)\n if (/^[A-Z]+-\\d+/.test(idOrRemoteId)) {\n return idOrRemoteId;\n }\n // Try to find a local work item with matching ID, read its remote_id\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n try {\n const raw = await fsPromises.readFile(path.join(pendingSync, entry.name), 'utf8');\n const { fm } = parseFrontmatter(raw);\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId && typeof fm['remote_id'] === 'string') {\n return fm['remote_id'];\n }\n }\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function findLocalFile(remoteId: string, projectRoot: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n if (fm['remote_id'] === remoteId) return fullPath;\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * push.ts — STORY-010-07\n *\n * `cleargate push <file>` — push a local work item to the MCP server.\n * `cleargate push --revert <ID-or-remote_id>` — soft-revert a pushed item.\n *\n * Pre-push gate (client-side):\n * Reads local frontmatter. If approved !== true, exits 1 with a clear message\n * BEFORE any MCP call. Zero network traffic on refusal.\n *\n * Attribution write-back:\n * On success, writes pushed_by + pushed_at from MCP response back into the\n * local frontmatter atomically (.tmp + rename). Appends sync-log entry op='push'.\n *\n * Soft revert (--revert):\n * Calls cleargate_sync_status with new_status='archived-without-shipping'.\n * Does NOT delete the remote item. Does NOT clear local remote_id.\n * Guards against reverting status='done' items unless --force is passed.\n * Appends sync-log entry op='push-revert'.\n *\n * Token safety:\n * JWT tokens (eyJ…) are NEVER written to stdout, stderr, or sync-log.\n * redactDetail in appendSyncLog covers the detail field.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\n// ── Response shapes ─────────────────────────────────────────────────────────────\n\ninterface PushItemResult {\n version: number;\n updated_at: string;\n pushed_by: string;\n pushed_at: string;\n}\n\n// ── Options ─────────────────────────────────────────────────────────────────────\n\nexport interface PushOptions {\n /** --revert <ID-or-remote_id>: soft-revert a pushed item */\n revert?: string;\n /** --force: bypass \"done\" guard on revert */\n force?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (prevents token-from-env requirement) */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────────\n\nexport async function pushHandler(fileOrId: string, opts: PushOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client — resolved lazily and asynchronously via acquireAccessToken.\n // STORY-011-01: approved gate in handlePush runs BEFORE this, so no network\n // traffic happens on refusal (STORY-010-07 invariant preserved).\n async function resolveMcp(): Promise<McpClient> {\n if (opts.mcp) return opts.mcp;\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n throw new Error('unreachable');\n }\n // Acquire token\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n throw new Error('unreachable');\n }\n return createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Revert path ───────────────────────────────────────────────────────────────\n if (opts.revert !== undefined) {\n await handleRevert(opts.revert, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n force: opts.force ?? false,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n return;\n }\n\n // ── Push path ─────────────────────────────────────────────────────────────────\n await handlePush(fileOrId, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n}\n\n// ── Push implementation ───────────────────────────────────────────────────────\n\ninterface PushCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handlePush(filePath: string, ctx: PushCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve path (absolute or relative to projectRoot)\n const resolvedPath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(projectRoot, filePath);\n\n let rawContent: string;\n try {\n rawContent = await fsPromises.readFile(resolvedPath, 'utf8');\n } catch {\n stderr(`Error: cannot read file \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(rawContent));\n } catch (err) {\n stderr(`Error: cannot parse frontmatter in \"${resolvedPath}\": ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Client-side approved gate (BEFORE any MCP call) ─────────────────────────\n // STORY-010-07: if approved !== true, refuse and exit without network call.\n if (fm['approved'] !== true) {\n const itemId = getItemId(fm);\n stderr(\n `Error: push refused — ${itemId} has approved: false. ` +\n `Set approved: true in frontmatter after review.\\n`,\n );\n exit(1);\n return;\n }\n\n const itemId = getItemId(fm);\n const type = getItemType(fm);\n if (!type) {\n stderr(`Error: cannot determine item type from frontmatter in \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n // Derive title from body's first H1 if frontmatter lacks one.\n // ClearGate templates put the human-readable title in `# {ID}: {Name}`,\n // not in a `title:` frontmatter field. Admin UI reads payload.title for\n // item rows; without this, every row renders with an empty heading.\n const payloadForPush: Record<string, unknown> = { ...fm };\n if (typeof payloadForPush['title'] !== 'string' || payloadForPush['title'].length === 0) {\n const h1 = body.match(/^#\\s+(.+?)\\s*$/m)?.[1]?.trim();\n if (h1) payloadForPush['title'] = h1;\n }\n // Include the markdown body verbatim so the admin UI can render the full\n // work-item content (spec, Gherkin, implementation notes, DoD). Stored as\n // payload.body under the item's jsonb column — repo remains the canonical\n // source, MCP is a queryable mirror.\n payloadForPush['body'] = body;\n\n // MCP call\n const mcp = await resolveMcp();\n\n let result: PushItemResult;\n try {\n result = await mcp.call<PushItemResult>('push_item', {\n cleargate_id: itemId,\n type,\n payload: payloadForPush,\n ...(typeof fm['remote_id'] === 'string' ? { remote_id: fm['remote_id'] } : {}),\n });\n } catch (err) {\n stderr(`Error: push_item failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Attribution write-back (atomic) ──────────────────────────────────────────\n const updatedFm: Record<string, unknown> = {\n ...fm,\n pushed_by: result.pushed_by,\n pushed_at: result.pushed_at,\n ...(result.version !== undefined ? { push_version: result.version } : {}),\n };\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + body;\n await writeAtomic(resolvedPath, newContent);\n\n // Sync-log\n const now = nowFn();\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n // Note: pushed_by and pushed_at go in frontmatter, NOT in sync-log detail\n // to prevent any accidental token leakage via the detail field.\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push: ${itemId} → version ${result.version} (pushed_by: ${result.pushed_by})\\n`);\n}\n\n// ── Revert implementation ─────────────────────────────────────────────────────\n\ninterface RevertCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n force: boolean;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handleRevert(idOrRemoteId: string, ctx: RevertCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, force, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve to local file\n const resolved = await resolveLocalItem(idOrRemoteId, projectRoot);\n if (!resolved) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a local work item.\\n`);\n exit(1);\n return;\n }\n\n const { localPath, fm } = resolved;\n const itemId = getItemId(fm);\n const localStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n\n // Guard: refuse to revert \"done\" items without --force\n if (localStatus === 'done' && !force) {\n stderr(`Error: refusing to revert shipped item. Pass --force to override.\\n`);\n exit(1);\n return;\n }\n\n // Call sync_status with new_status='archived-without-shipping'\n const mcp = await resolveMcp();\n try {\n await mcp.call('sync_status', {\n cleargate_id: itemId,\n new_status: 'archived-without-shipping',\n });\n } catch (err) {\n stderr(`Error: sync_status revert failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // DO NOT clear local remote_id — item stays traceable\n // DO NOT overwrite local status — sync will pull the server state back\n\n // Sync-log\n const now = nowFn();\n const remoteId = typeof fm['remote_id'] === 'string' ? fm['remote_id'] : undefined;\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push-revert',\n target: itemId,\n ...(remoteId !== undefined ? { remote_id: remoteId } : {}),\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push --revert: ${itemId} → archived-without-shipping\\n`);\n void localPath; // referenced for clarity; not needed after initial read\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveLocalItem(\n idOrRemoteId: string,\n projectRoot: string,\n): Promise<{ localPath: string; fm: Record<string, unknown> } | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n\n // Match by remote_id\n if (fm['remote_id'] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n\n // Match by primary item ID (story_id, epic_id, etc.)\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n }\n } catch {\n // skip malformed\n }\n }\n }\n\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n\nfunction getItemType(fm: Record<string, unknown>): string | null {\n const typeMap: Record<string, string> = {\n story_id: 'story',\n epic_id: 'epic',\n proposal_id: 'proposal',\n cr_id: 'cr',\n bug_id: 'bug',\n };\n for (const [key, type] of Object.entries(typeMap)) {\n if (typeof fm[key] === 'string' && fm[key]) return type;\n }\n return null;\n}\n","/**\n * conflicts.ts — STORY-010-04 / updated STORY-011-01\n *\n * `cleargate conflicts` — read-only command that reads .cleargate/.conflicts.json\n * and prints unresolved items with one-line resolution hints.\n *\n * Exit 0 when unresolved: [], exit 1 otherwise.\n *\n * --refresh flag: force-invalidate the acquire cache and rotate the stored\n * refresh token even if the cached access token is still valid. This is the\n * only MCP call conflicts makes.\n *\n * No mutations (besides keychain rotation on --refresh). No top-level await\n * (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { ConflictsJson, ConflictEntry } from './sync.js';\nimport { acquireAccessToken } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\nexport interface ConflictsOptions {\n projectRoot?: string;\n /** --refresh: bypass single-flight cache and force a new /auth/refresh. */\n refresh?: boolean;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: override process.env lookup */\n env?: NodeJS.ProcessEnv;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n}\n\nconst RESOLUTION_HINTS: Record<string, string> = {\n 'local-delete-remote-edit': 'remote-delete: resurrect or delete remote?',\n 'remote-delete-local-edit': 'local-edit: push your changes or accept remote deletion?',\n 'refuse': 'manual resolution required — re-run sync after resolving',\n 'halt': 'unknown conflict shape — file a ClearGate bug',\n};\n\nfunction getHint(entry: ConflictEntry): string {\n return RESOLUTION_HINTS[entry.state] ?? RESOLUTION_HINTS[entry.resolution] ?? `resolve and re-run sync`;\n}\n\nexport async function conflictsHandler(opts: ConflictsOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n // ── --refresh: force-invalidate the cache and rotate the keychain token ─────\n if (opts.refresh) {\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — skip refresh; command proceeds without token rotation\n }\n }\n if (baseUrl && baseUrl.trim()) {\n try {\n await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n forceRefresh: true,\n env,\n });\n } catch {\n // Refresh errors are non-fatal for `conflicts` — proceed to print conflicts\n }\n }\n }\n\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n\n let data: ConflictsJson;\n try {\n const raw = await fsPromises.readFile(conflictsFile, 'utf8');\n data = JSON.parse(raw) as ConflictsJson;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n stdout('No conflicts file found. Run `cleargate sync` first.\\n');\n exit(0);\n return;\n }\n throw err;\n }\n\n const unresolved = data.unresolved ?? [];\n\n if (unresolved.length === 0) {\n stdout('No unresolved conflicts.\\n');\n exit(0);\n return;\n }\n\n stdout(`Unresolved conflicts (${unresolved.length}):\\n`);\n stdout(`Generated: ${data.generated_at} Sprint: ${data.sprint_id}\\n\\n`);\n\n for (const item of unresolved) {\n const hint = getHint(item);\n stdout(` ${item.item_id.padEnd(20)} ${item.state.padEnd(30)} ${hint}\\n`);\n }\n\n stdout('\\nRe-run `cleargate sync` after resolving conflicts.\\n');\n exit(1);\n}\n","/**\n * sync-log.ts (command) — STORY-010-04\n *\n * `cleargate sync-log` — filter/print wrapper over readSyncLog() from M1.\n *\n * Flags: --actor, --op, --target, --limit N (default 50). Newest-first guaranteed by lib.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { resolveActiveSprintDir, readSyncLog, type SyncLogOp, type SyncLogEntry } from '../lib/sync-log.js';\n\nexport interface SyncLogCommandOptions {\n actor?: string;\n op?: string;\n target?: string;\n limit?: number;\n projectRoot?: string;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n}\n\nexport async function syncLogHandler(opts: SyncLogCommandOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const limit = opts.limit ?? 50;\n\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // Validate --op value if provided\n const validOps = new Set<SyncLogOp>([\n 'push', 'pull', 'pull-intake', 'push-revert', 'sync-status', 'conflict-remote-wins', 'conflict-refused',\n ]);\n let opFilter: SyncLogOp | undefined;\n if (opts.op !== undefined) {\n if (!validOps.has(opts.op as SyncLogOp)) {\n stderr(`Warning: unknown op \"${opts.op}\". Valid ops: ${[...validOps].join(', ')}\\n`);\n } else {\n opFilter = opts.op as SyncLogOp;\n }\n }\n\n const entries = await readSyncLog(sprintRoot, {\n actor: opts.actor,\n op: opFilter,\n target: opts.target,\n });\n\n const limited = entries.slice(0, limit);\n\n if (limited.length === 0) {\n stdout('No sync-log entries match the given filters.\\n');\n return;\n }\n\n for (const entry of limited) {\n stdout(formatEntry(entry) + '\\n');\n }\n}\n\nfunction formatEntry(entry: SyncLogEntry): string {\n const parts: string[] = [\n entry.ts,\n entry.actor,\n entry.op.padEnd(20),\n entry.target.padEnd(24),\n entry.result,\n ];\n if (entry.remote_id) parts.push(`remote=${entry.remote_id}`);\n if (entry.detail) parts.push(`detail=${entry.detail}`);\n return parts.join(' ');\n}\n","/**\n * `cleargate admin login` — GitHub OAuth device flow for admin CLI login.\n *\n * Flow:\n * 1. POST <mcp-url>/admin-api/v1/auth/device/start → gets device_code + user_code + verification_uri.\n * 2. Prints the verification URL and user code for the operator to open in a browser.\n * 3. Polls POST /admin-api/v1/auth/device/poll every `interval` seconds.\n * 4. On success: writes ~/.cleargate/admin-auth.json { version: 1, token: <admin_jwt> } at chmod 600.\n * 5. On failure: prints a clear error message and exits with appropriate exit code.\n *\n * Exit codes (per STORY-005-06 spec):\n * 0 — success\n * 3 — network error (unreachable)\n * 4 — auth rejected (non-admin GitHub user — not_admin)\n * 5 — device-flow timeout or user denied (expired_token / access_denied)\n * 6 — other device-flow error\n * 99 — unhandled\n *\n * Secrets NEVER appear on stdout/stderr: neither the GitHub access token\n * (server-side only) nor the admin JWT.\n *\n * STORY-005-06.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { startDeviceFlow, DeviceFlowError } from '../auth/identity-flow.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface DevicePollPendingResponse {\n pending: true;\n retry_after?: number;\n}\n\nexport interface DevicePollSuccessResponse {\n pending: false;\n admin_token: string;\n expires_at: string;\n admin_user_id: string;\n}\n\nexport type DevicePollResponse = DevicePollPendingResponse | DevicePollSuccessResponse;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Options / seams\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AdminLoginOptions {\n mcpUrl?: string;\n env?: NodeJS.ProcessEnv;\n homedir?: () => string;\n fetch?: typeof globalThis.fetch;\n stdout?: (msg: string) => void;\n stderr?: (msg: string) => void;\n exit?: (code: number) => never;\n /** Override polling interval in milliseconds (used in tests to avoid real waits) */\n intervalOverrideMs?: number;\n /** Override admin-auth file path */\n authFilePath?: string;\n /** Override sleep implementation — injected in tests to capture interval values */\n sleepFn?: (ms: number) => Promise<void>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_MCP_URL = 'http://localhost:3000';\n\n/** Sleep helper — exported so tests can spy on it. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveMcpUrl(mcpUrlFlag?: string, env?: NodeJS.ProcessEnv): string {\n return (\n mcpUrlFlag ??\n (env ?? process.env)['CLEARGATE_MCP_URL'] ??\n DEFAULT_MCP_URL\n ).replace(/\\/$/, '');\n}\n\nfunction resolveAuthFilePath(opts: AdminLoginOptions): string {\n if (opts.authFilePath) return opts.authFilePath;\n const homedirFn = opts.homedir ?? os.homedir;\n return path.join(homedirFn(), '.cleargate', 'admin-auth.json');\n}\n\nfunction writeAdminAuth(filePath: string, token: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const payload = JSON.stringify({ version: 1, token }, null, 2);\n fs.writeFileSync(filePath, payload, { encoding: 'utf8', mode: 0o600 });\n // Explicit chmod in case the file already existed with wider permissions\n fs.chmodSync(filePath, 0o600);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main handler\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function adminLoginHandler(opts: AdminLoginOptions = {}): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((msg: string) => process.stdout.write(msg + '\\n'));\n const stderr = opts.stderr ?? ((msg: string) => process.stderr.write(msg + '\\n'));\n const exitFn = opts.exit ?? ((code: number): never => process.exit(code));\n const mcpBase = resolveMcpUrl(opts.mcpUrl, opts.env);\n\n // ── Step 1: Start device flow ──────────────────────────────────────────────\n let startData: DeviceStartResponse;\n try {\n const startRes = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n });\n\n if (!startRes.ok) {\n const body = (await startRes.json().catch(() => ({}))) as { error?: string };\n if (startRes.status === 503) {\n stderr('cleargate: error: device flow not configured on the server (CLEARGATE_GITHUB_CLI_CLIENT_ID not set).');\n return exitFn(6);\n }\n stderr(`cleargate: error: server error ${startRes.status}: ${body.error ?? 'unknown'}`);\n return exitFn(6);\n }\n\n startData = (await startRes.json()) as DeviceStartResponse;\n } catch (err) {\n stderr(`cleargate: error: cannot reach ${mcpBase} (${err instanceof Error ? err.message : String(err)})`);\n return exitFn(3);\n }\n\n // ── Step 2: Display instructions ───────────────────────────────────────────\n stdout(`Open the following URL in your browser and enter the code:`);\n stdout(` URL: ${startData.verification_uri}`);\n stdout(` Code: ${startData.user_code}`);\n stdout(` (Code expires in ${Math.floor(startData.expires_in / 60)} minutes)`);\n stdout('Waiting for authorization...');\n\n // ── Step 3: Poll via identity-flow.startDeviceFlow with captured success body ─\n // We wrap fetchPoll to capture the full DevicePollSuccessResponse (which includes\n // expires_at + admin_user_id needed for the success stdout message) since\n // startDeviceFlow only returns { accessToken }.\n let capturedSuccessBody: DevicePollSuccessResponse | null = null;\n\n const fetchPollCapture = async (deviceCode: string) => {\n const res = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/poll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n // Capture the success body before startDeviceFlow consumes it.\n // We intercept by wrapping json() to also store the body when pending=false.\n const originalJson = res.json.bind(res);\n return {\n status: res.status,\n json: async () => {\n const body = (await originalJson()) as Record<string, unknown>;\n if (body['pending'] === false) {\n capturedSuccessBody = body as unknown as DevicePollSuccessResponse;\n }\n return body;\n },\n };\n };\n\n try {\n await startDeviceFlow({\n deviceCode: startData.device_code,\n interval: startData.interval,\n expiresIn: startData.expires_in,\n fetchPoll: fetchPollCapture,\n // Only pass sleepFn if the caller explicitly injected one (test seam).\n // When sleepFn is omitted, startDeviceFlow uses its own defaultSleep.\n // This preserves the original bump-suppression logic:\n // shouldApplyBump = (sleepFn provided) || (intervalOverrideMs not set).\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n deadlineGraceMs: 10_000,\n });\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: error: access denied — you declined authorization in the browser.');\n return exitFn(5);\n case 'not_admin':\n stderr('cleargate: error: your GitHub account is not authorized as an admin user.');\n return exitFn(4);\n case 'expired_token':\n stderr('cleargate: error: device code expired — please run `cleargate admin login` again.');\n return exitFn(5);\n case 'timeout':\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n case 'unreachable':\n stderr(`cleargate: error: network error while polling`);\n return exitFn(3);\n default:\n stderr(`cleargate: error: unexpected server response`);\n return exitFn(6);\n }\n }\n stderr(`cleargate: error: unexpected error during device flow`);\n return exitFn(6);\n }\n\n if (!capturedSuccessBody) {\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n }\n\n // Extract fields with explicit typing to avoid TypeScript discriminated union narrowing\n const successBody = capturedSuccessBody as DevicePollSuccessResponse;\n\n // ── Step 4: Write admin-auth.json ──────────────────────────────────────────\n const authFilePath = resolveAuthFilePath(opts);\n try {\n writeAdminAuth(authFilePath, successBody.admin_token);\n } catch (err) {\n stderr(`cleargate: error: failed to write ${authFilePath}: ${err instanceof Error ? err.message : String(err)}`);\n return exitFn(99);\n }\n\n // ── Step 5: Success message (no secrets in output) ─────────────────────────\n stdout(`Logged in successfully. Token expires ${successBody.expires_at}.`);\n stdout(`Credentials saved to ${authFilePath} (chmod 600).`);\n}\n","/**\n * hotfix.ts — `cleargate hotfix new <slug>` command handler.\n *\n * STORY-022-06: Hotfix lane scaffolding.\n *\n * Creates a new HOTFIX-NNN_<slug>.md file in `.cleargate/delivery/pending-sync/`\n * from the bundled hotfix.md template, with ID auto-incremented via a scan of\n * existing HOTFIX-* files.\n *\n * Cap stub: blocks the 4th hotfix in a rolling 7-day window (pending-sync +\n * archive files modified within the last 7 days). Node fs APIs only — no\n * shell-outs (cross-OS per BUG-010 §4b).\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface HotfixCliOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override cwd for the repo root (test seam). */\n cwd?: string;\n /** Override the current ISO timestamp (test seam). */\n now?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nconst SLUG_RE = /^[a-z0-9-]+$/;\nconst HOTFIX_FILE_RE = /^HOTFIX-(\\d+)_.*\\.md$/;\n\n/**\n * Scan pending-sync/ for HOTFIX-NNN_*.md files; return the highest NNN found\n * (or 0 if none). Synchronous — no async needed for the tiny delivery dir.\n */\nfunction maxHotfixId(pendingDir: string): number {\n let max = 0;\n let entries: string[];\n try {\n entries = fs.readdirSync(pendingDir);\n } catch {\n return 0;\n }\n for (const entry of entries) {\n const m = HOTFIX_FILE_RE.exec(entry);\n if (m) {\n const n = parseInt(m[1]!, 10);\n if (n > max) max = n;\n }\n }\n return max;\n}\n\n/**\n * Count active hotfixes for the rolling-window cap.\n *\n * Counts:\n * - All HOTFIX-*.md files in pending-sync/ (regardless of mtime — they are\n * by definition active/in-flight).\n * - HOTFIX-*.md files in archive/ whose mtime is within the last 7 days\n * (recently merged/resolved within the window).\n */\nfunction countActiveHotfixes(repoRoot: string): number {\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(repoRoot, '.cleargate', 'delivery', 'archive');\n const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;\n\n let count = 0;\n\n // All in pending-sync\n let pendingEntries: string[] = [];\n try {\n pendingEntries = fs.readdirSync(pendingDir);\n } catch {\n // dir may not exist in test fixtures\n }\n for (const entry of pendingEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) count++;\n }\n\n // Recently archived (within 7-day window)\n let archiveEntries: string[] = [];\n try {\n archiveEntries = fs.readdirSync(archiveDir);\n } catch {\n // dir may not exist\n }\n for (const entry of archiveEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) {\n try {\n const stat = fs.statSync(path.join(archiveDir, entry));\n if (stat.mtimeMs >= sevenDaysAgo) count++;\n } catch {\n // Skip unreadable entries\n }\n }\n }\n\n return count;\n}\n\n/**\n * Resolve the path to `.cleargate/templates/hotfix.md` relative to the repo root.\n */\nfunction resolveTemplatePath(repoRoot: string): string {\n return path.join(repoRoot, '.cleargate', 'templates', 'hotfix.md');\n}\n\n// ─── hotfixNewHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate hotfix new <slug>`\n *\n * - Validates slug matches ^[a-z0-9-]+$.\n * - Checks rolling-window hotfix cap (≤3 per 7 days).\n * - Reads .cleargate/templates/hotfix.md and substitutes {ID}, {SLUG}, {ISO}.\n * - Writes to .cleargate/delivery/pending-sync/HOTFIX-<NNN>_<slug>.md\n * (slug with - replaced by _ in filename, matching STORY-NNN-NN_Name.md convention).\n *\n * Exits 1 on validation failure or cap exceeded; exits 0 on success.\n */\nexport function hotfixNewHandler(\n opts: { slug: string },\n cli?: HotfixCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const repoRoot = cli?.cwd ?? process.cwd();\n const now = cli?.now ?? new Date().toISOString();\n\n // ── Validate slug ────────────────────────────────────────────────────────\n if (!SLUG_RE.test(opts.slug)) {\n stderrFn(`[cleargate hotfix new] slug must match ^[a-z0-9-]+$ (got: \"${opts.slug}\")`);\n return exitFn(1);\n }\n\n // ── Cap check ────────────────────────────────────────────────────────────\n const activeCount = countActiveHotfixes(repoRoot);\n if (activeCount >= 3) {\n stderrFn(\n `Hotfix cap: ≤3 per rolling 7-day window. Currently ${activeCount} active. Bundle into a sprint or downgrade one to a CR.`,\n );\n return exitFn(1);\n }\n\n // ── Next ID ──────────────────────────────────────────────────────────────\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const maxId = maxHotfixId(pendingDir);\n const nextId = maxId + 1;\n const idStr = `HOTFIX-${String(nextId).padStart(3, '0')}`;\n\n // ── Read template ────────────────────────────────────────────────────────\n const templatePath = resolveTemplatePath(repoRoot);\n let templateContent: string;\n try {\n templateContent = fs.readFileSync(templatePath, 'utf8');\n } catch {\n stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);\n return exitFn(2);\n }\n\n // ── Substitute placeholders ──────────────────────────────────────────────\n const content = templateContent\n .replace(/\\{ID\\}/g, idStr)\n .replace(/\\{SLUG\\}/g, opts.slug)\n .replace(/\\{ISO\\}/g, now);\n\n // ── Write output file ────────────────────────────────────────────────────\n // Filename convention: HOTFIX-NNN_slug_with_underscores.md\n const fileSlug = opts.slug.replace(/-/g, '_');\n const fileName = `${idStr}_${fileSlug}.md`;\n const outPath = path.join(pendingDir, fileName);\n\n try {\n fs.mkdirSync(pendingDir, { recursive: true });\n fs.writeFileSync(outPath, content, 'utf8');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n stderrFn(`[cleargate hotfix new] write failed: ${msg}`);\n return exitFn(1);\n }\n\n stdoutFn(`[cleargate hotfix new] created: ${outPath}`);\n // Explicit exit 0 to satisfy the exit-seam contract used in tests.\n return exitFn(0);\n}\n"],"mappings":"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","names":["fs","path","fs","path","import_zod","Entry","os","path","body","decodeJwtPayload","pg","os","resolve","body","readline","hostname","readNextLine","resolve","fs","os","path","fs","path","fs","yaml","import_js_yaml","yaml","raw","fm","stampOpts","result","fs","path","import_node_url","import_node_child_process","fs","path","fs","path","fs","path","fs","path","state","fs","path","import_node_url","compile","state","resolveDefaultTemplateDir","fs","path","import_node_url","compile","state","resolveDefaultTemplateDir","fs","path","import_node_url","compile","state","resolveDefaultTemplateDir","isSet","compile","story","import_promises","path","state","readline","resolve","fs","path","os","import_node_child_process","hostname","fs","path","import_node_child_process","EXCLUDED_SUFFIXES","BUCKET_ORDER","BUCKET_LABELS","rename","buildParentRef","buildChildrenRefs","buildPageBody","compile","path","fs","path","fs","path","import_node_child_process","import_js_yaml","PREFIX_MAP","basename","yaml","fs","path","import_js_yaml","yaml","wiki","fs","path","fs","path","readline","resolve","fs","path","import_node_child_process","state","parseCachedGateResult","gate","fs","path","import_node_child_process","fs","path","import_js_yaml","fs","path","fs","import_js_yaml","yaml","yaml","gate","code","import_node_child_process","fs","path","import_node_child_process","import_js_yaml","TERMINAL_STATUSES","yaml","state","fs","path","import_node_child_process","defaultExit","resolveRunScript","state","path","import_node_child_process","defaultExit","resolveRunScript","fs","path","fs","path","basename","path","BLOCK_REGEX","state","resolve","import_node_child_process","resolve","writeAtomic","state","newSha","fs","fsp","path","import_node_child_process","writeAtomic","resolve","fsPromises","path","fs","fsPromises","path","classify","import_node_fs","os","path","resolve","fs","text","fsPromises","path","fsPromises","path","proposalId","slug","num","filename","targetPath","fs","fsPromises","path","fsPromises","path","fsPromises","path","writeAtomic","nowIso","classify","writeAtomic","fsPromises","path","entry","getItemId","writeAtomic","fsPromises","path","itemId","getItemId","writeAtomic","fsPromises","path","fs","path","os","fs","path","defaultExit","whoamiHandler","bootstrapRootHandler"]}
|
|
1
|
+
{"version":3,"sources":["../node_modules/tsup/assets/cjs_shims.js","../src/config.ts","../src/auth/keychain-store.ts","../src/auth/file-store.ts","../src/auth/factory.ts","../src/auth/acquire.ts","../src/commands/whoami.ts","../src/commands/bootstrap-root.ts","../src/cli.ts","../package.json","../src/commands/scaffold-lint.ts","../src/lib/scaffold-blocklist.ts","../src/commands/join.ts","../src/auth/identity-flow.ts","../src/commands/stamp.ts","../src/lib/codebase-version.ts","../src/lib/stamp-frontmatter.ts","../src/wiki/parse-frontmatter.ts","../src/lib/frontmatter-yaml.ts","../src/commands/init.ts","../src/init/copy-payload.ts","../src/init/merge-settings.ts","../src/init/inject-claude-md.ts","../src/commands/wiki-build.ts","../src/wiki/scan.ts","../src/wiki/derive-bucket.ts","../src/wiki/derive-repo.ts","../src/wiki/git-sha.ts","../src/wiki/page-schema.ts","../src/wiki/synthesis/active-sprint.ts","../src/wiki/synthesis/render.ts","../src/wiki/synthesis/open-gates.ts","../src/wiki/synthesis/product-state.ts","../src/wiki/synthesis/roadmap.ts","../src/lib/manifest.ts","../src/lib/sha256.ts","../src/lib/prompts.ts","../src/lib/identity.ts","../src/commands/wiki-ingest.ts","../src/commands/wiki-lint.ts","../src/wiki/load-wiki.ts","../src/wiki/lint-checks.ts","../src/lib/work-item-type.ts","../src/lib/wiki-config.ts","../src/commands/wiki-query.ts","../src/commands/wiki-audit-status.ts","../src/commands/doctor.ts","../src/lib/pricing.ts","../src/commands/gate.ts","../src/commands/execution-mode.ts","../src/lib/readiness-predicates.ts","../src/lib/frontmatter-cache.ts","../src/commands/gate-run.ts","../src/commands/sprint.ts","../src/commands/story.ts","../src/commands/state.ts","../src/commands/stamp-tokens.ts","../src/lib/ledger-reader.ts","../src/commands/upgrade.ts","../src/lib/claude-md-surgery.ts","../src/lib/settings-json-surgery.ts","../src/lib/merge-ui.ts","../src/lib/editor.ts","../src/commands/uninstall.ts","../src/commands/sync.ts","../src/lib/sync-log.ts","../src/lib/conflict-detector.ts","../src/lib/merge-helper.ts","../src/lib/mcp-client.ts","../src/lib/intake.ts","../src/lib/slug.ts","../src/lib/active-criteria.ts","../src/lib/comments-cache.ts","../src/lib/wiki-comments-render.ts","../src/commands/pull.ts","../src/commands/push.ts","../src/commands/conflicts.ts","../src/commands/sync-log.ts","../src/commands/admin-login.ts","../src/commands/hotfix.ts"],"sourcesContent":["// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { z } from 'zod';\n\nexport const ConfigSchema = z\n .object({\n mcpUrl: z.string().url().optional(),\n profile: z.string().min(1).default('default'),\n logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n })\n .strict();\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/** Partial raw config used for each layer before merge */\ntype RawConfig = Partial<{\n mcpUrl: string | undefined;\n profile: string | undefined;\n logLevel: string | undefined;\n}>;\n\nexport interface LoadConfigOptions {\n flags?: RawConfig;\n env?: NodeJS.ProcessEnv;\n configPath?: string;\n}\n\n/**\n * Synchronously loads and merges config from all layers:\n * flags > env > config file > zod defaults\n */\nexport function loadConfig(opts: LoadConfigOptions = {}): Config {\n const {\n flags = {},\n env = process.env,\n configPath,\n } = opts;\n\n // Resolve config file path\n const resolvedConfigPath =\n configPath ??\n (() => {\n const home = os.homedir();\n if (!home) return null;\n return path.join(home, '.cleargate', 'config.json');\n })();\n\n // Layer: file\n let fileLayer: RawConfig = {};\n if (resolvedConfigPath) {\n try {\n const raw = fs.readFileSync(resolvedConfigPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\n `Failed to parse config file at ${resolvedConfigPath}: invalid JSON`,\n );\n }\n // Validate file contents strictly (unknown keys will throw here)\n const fileResult = ConfigSchema.safeParse(parsed);\n if (!fileResult.success) {\n throw new Error(\n `Invalid config file at ${resolvedConfigPath}: ${fileResult.error.message}`,\n );\n }\n fileLayer = fileResult.data;\n } catch (err) {\n // Re-throw parse/validation errors; silently skip only ENOENT\n if (\n err instanceof Error &&\n 'code' in err &&\n (err as NodeJS.ErrnoException).code === 'ENOENT'\n ) {\n // file doesn't exist — skip silently\n } else {\n throw err;\n }\n }\n }\n\n // Layer: env\n const envLayer: RawConfig = {};\n if (env['CLEARGATE_MCP_URL']) {\n envLayer.mcpUrl = env['CLEARGATE_MCP_URL'];\n }\n if (env['CLEARGATE_PROFILE']) {\n envLayer.profile = env['CLEARGATE_PROFILE'];\n }\n if (env['CLEARGATE_LOG_LEVEL']) {\n envLayer.logLevel = env['CLEARGATE_LOG_LEVEL'];\n }\n\n // Merge: flags > env > file (start from {} so zod defaults fill in missing fields)\n const merged: Record<string, unknown> = {\n ...fileLayer,\n ...envLayer,\n ...(flags.mcpUrl !== undefined ? { mcpUrl: flags.mcpUrl } : {}),\n ...(flags.profile !== undefined ? { profile: flags.profile } : {}),\n ...(flags.logLevel !== undefined ? { logLevel: flags.logLevel } : {}),\n };\n\n // Remove undefined values so zod defaults apply properly\n for (const key of Object.keys(merged)) {\n if (merged[key] === undefined) {\n delete merged[key];\n }\n }\n\n const result = ConfigSchema.safeParse(merged);\n if (!result.success) {\n throw new Error(`Config validation failed: ${result.error.message}`);\n }\n\n return result.data;\n}\n\n/**\n * Asserts mcpUrl is present, throws a user-friendly error if not.\n */\nexport function requireMcpUrl(cfg: Config): string {\n if (cfg.mcpUrl === undefined) {\n throw new Error(\n 'mcpUrl not configured. Run `cleargate join <invite-url>` first.',\n );\n }\n return cfg.mcpUrl;\n}\n","import { Entry } from '@napi-rs/keyring';\nimport type { TokenStore } from './token-store.js';\n\nexport class KeychainTokenStore implements TokenStore {\n readonly backend = 'keychain' as const;\n\n constructor(private readonly service: string) {}\n\n async save(profile: string, token: string): Promise<void> {\n new Entry(this.service, profile).setPassword(token);\n }\n\n async load(profile: string): Promise<string | null> {\n try {\n const result = new Entry(this.service, profile).getPassword();\n // getPassword() returns string | null per @napi-rs/keyring@1.2.0 index.d.ts:124\n // Despite the docstring claiming it throws NoEntry, the return type wins.\n // Handle both: null return AND potential thrown NoEntry (platform-specific).\n return result ?? null;\n } catch {\n // NoEntry or other keychain error — treat as absent\n return null;\n }\n }\n\n async remove(profile: string): Promise<void> {\n try {\n new Entry(this.service, profile).deletePassword();\n } catch {\n // Entry didn't exist or other keychain error — idempotent, swallow\n }\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport type { TokenStore } from './token-store.js';\n\nconst ProfileEntrySchema = z.object({ refreshToken: z.string().min(1) }).strict();\n\nexport const AuthFileSchema = z\n .object({\n version: z.literal(1),\n profiles: z.record(z.string().min(1), ProfileEntrySchema),\n })\n .strict();\n\ntype AuthFile = z.infer<typeof AuthFileSchema>;\n\nconst EMPTY_AUTH_FILE: AuthFile = { version: 1, profiles: {} };\n\nexport class FileTokenStore implements TokenStore {\n readonly backend = 'file' as const;\n\n constructor(private readonly filePath: string) {}\n\n async save(profile: string, token: string): Promise<void> {\n const current = await this.readFile();\n const updated: AuthFile = {\n ...current,\n profiles: {\n ...current.profiles,\n [profile]: { refreshToken: token },\n },\n };\n await this.writeFile(updated);\n }\n\n async load(profile: string): Promise<string | null> {\n const data = await this.readFile();\n return data.profiles[profile]?.refreshToken ?? null;\n }\n\n async remove(profile: string): Promise<void> {\n let current: AuthFile;\n try {\n current = await this.readFile();\n } catch {\n // File doesn't exist or unreadable — no-op since there's nothing to remove\n return;\n }\n if (!(profile in current.profiles)) {\n return; // Profile doesn't exist — idempotent\n }\n const { [profile]: _removed, ...rest } = current.profiles;\n const updated: AuthFile = { ...current, profiles: rest };\n await this.writeFile(updated);\n }\n\n private async readFile(): Promise<AuthFile> {\n let raw: string;\n try {\n raw = await fs.readFile(this.filePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return EMPTY_AUTH_FILE;\n }\n throw err;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\n `Failed to parse auth file at ${this.filePath}: invalid JSON`,\n );\n }\n\n const result = AuthFileSchema.safeParse(parsed);\n if (!result.success) {\n // Check for version mismatch specifically\n const versionCheck = (parsed as Record<string, unknown>)?.['version'];\n if (versionCheck !== 1) {\n throw new Error(\n `Invalid auth file at ${this.filePath}: unsupported version ${String(versionCheck)}. Please upgrade \\`cleargate\\` to read this file.`,\n );\n }\n throw new Error(\n `Invalid auth file at ${this.filePath}: ${result.error.message}`,\n );\n }\n\n return result.data;\n }\n\n private async writeFile(data: AuthFile): Promise<void> {\n const dir = path.dirname(this.filePath);\n await fs.mkdir(dir, { recursive: true, mode: 0o700 });\n // Explicit chmod after mkdir — mkdir only sets mode on newly created dirs\n await fs.chmod(dir, 0o700).catch(() => {\n // If chmod fails on existing dir, that's acceptable — we don't want to\n // surprise users who have set custom modes on ~/.cleargate/\n });\n\n const json = JSON.stringify(data, null, 2);\n const tmpPath = path.join(dir, '.auth.json.tmp');\n\n // Atomic write: write to tmp then rename to avoid partial-write corruption\n await fs.writeFile(tmpPath, json, { mode: 0o600 });\n // Explicit chmod after writeFile — writeFile only sets mode on file creation\n await fs.chmod(tmpPath, 0o600);\n await fs.rename(tmpPath, this.filePath);\n // After rename, chmod the final path to ensure it stays 0600\n await fs.chmod(this.filePath, 0o600);\n }\n}\n","import * as os from 'node:os';\nimport * as path from 'node:path';\nimport { KeychainTokenStore } from './keychain-store.js';\nimport { FileTokenStore } from './file-store.js';\nimport type { TokenStore, TokenStoreFactoryOptions } from './token-store.js';\n\nconst DEFAULT_KEYCHAIN_SERVICE = 'cleargate';\n\nfunction resolveFilePath(opts: TokenStoreFactoryOptions): string {\n if (opts.filePath) return opts.filePath;\n const home = os.homedir();\n if (!home) {\n throw new Error(\n 'Cannot determine home directory. Set opts.filePath explicitly or ensure os.homedir() returns a non-empty string.',\n );\n }\n return path.join(home, '.cleargate', 'auth.json');\n}\n\nfunction defaultWarn(msg: string): void {\n process.stderr.write(msg + '\\n');\n}\n\n/**\n * Creates a TokenStore, selecting the keychain backend when available and\n * falling back to file storage with a stderr warning when the OS keychain\n * cannot be accessed.\n */\nexport async function createTokenStore(\n opts: TokenStoreFactoryOptions = {},\n): Promise<TokenStore> {\n const filePath = resolveFilePath(opts);\n const service = opts.keychainService ?? DEFAULT_KEYCHAIN_SERVICE;\n const warn = opts.warn ?? defaultWarn;\n\n // Short-circuit if backend is forced (test seam, skips probe)\n if (opts.forceBackend === 'file') {\n return new FileTokenStore(filePath);\n }\n if (opts.forceBackend === 'keychain') {\n return new KeychainTokenStore(service);\n }\n\n // Probe the keychain to determine availability\n try {\n const { Entry } = await import('@napi-rs/keyring');\n new Entry(service, '__cleargate_probe__').getPassword();\n // Probe succeeded (returned string | null cleanly) — use keychain\n return new KeychainTokenStore(service);\n } catch {\n // Constructor threw (native module load failed, libsecret missing on Linux)\n // OR getPassword() threw (dbus not running, prompt cancelled)\n // Either way, keychain is unavailable for this CLI invocation\n warn(\n `cleargate: OS keychain unavailable, falling back to file storage at ${filePath}. Run with --log-level=debug for details.`,\n );\n return new FileTokenStore(filePath);\n }\n}\n","/**\n * acquireAccessToken — resolve a short-lived MCP access-token JWT.\n *\n * Resolution order (first success wins):\n * 1. CLEARGATE_MCP_TOKEN env var — CI / dev short-circuit (assumed JWT, not verified locally).\n * 2. In-memory single-flight cache (keyed by `${profile}::${mcpUrl}`) — returns cached token\n * if still valid (expires 60s before access token's `exp` claim).\n * 3. Stored refresh token (keychain/file) + POST /auth/refresh → rotates refresh token, returns access token.\n *\n * Errors surface to caller with a clear message so command handlers can exit cleanly.\n *\n * Lives here (not in mcp-client.ts) because the refresh flow needs TokenStore + mcpUrl and\n * mcp-client.ts is kept thin (just: host, bearer, JSON-RPC).\n */\nimport { createTokenStore } from './factory.js';\nimport type { TokenStore } from './token-store.js';\n\n// ── Single-flight in-memory cache ─────────────────────────────────────────────\n// Process-local; naturally cleared when the Node CLI exits.\n// Key: `${profile}::${mcpUrl}` — R1 mitigation: two profiles in same process never collide.\n// Env-token path (CLEARGATE_MCP_TOKEN) bypasses this cache entirely.\n\nconst CACHE = new Map<string, { accessToken: string; expiresAtMs: number }>();\n\n/** Test seam: clear the acquire cache between tests. */\nexport function __resetAcquireCache(): void {\n CACHE.clear();\n}\n\n/** Decode a JWT payload without verifying the signature (CLI-side only). */\nfunction decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n const padded = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const json = Buffer.from(padded, 'base64').toString('utf8');\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nexport interface AcquireOptions {\n mcpUrl: string;\n profile: string;\n /** Force a fresh /auth/refresh even if the cache has a valid entry. */\n forceRefresh?: boolean;\n /** Test seam: overrides globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n /** Test seam: overrides createTokenStore */\n createStore?: () => Promise<TokenStore>;\n /** Test seam: overrides process.env lookup */\n env?: NodeJS.ProcessEnv;\n /** Test seam: overrides Date.now() for expiry calculations. */\n now?: () => number;\n}\n\nexport class AcquireError extends Error {\n constructor(\n message: string,\n public readonly code:\n | 'env_token'\n | 'no_stored_token'\n | 'invalid_token'\n | 'token_revoked'\n | 'transport'\n | 'unexpected_status'\n | 'bad_response',\n ) {\n super(message);\n this.name = 'AcquireError';\n }\n}\n\n/**\n * Returns a bearer string suitable for Authorization headers against /mcp and\n * /admin-api. Rotates the stored refresh token on success.\n */\nexport async function acquireAccessToken(opts: AcquireOptions): Promise<string> {\n const env = opts.env ?? process.env;\n const nowFn = opts.now ?? Date.now;\n\n // 1. Env short-circuit — CI / dev / manual paste. Assumed to be a valid JWT.\n // Env tokens are NOT cached — they have no known exp without decoding + the\n // env is set per-invocation in CI anyway.\n const envToken = env['CLEARGATE_MCP_TOKEN'];\n if (envToken && envToken.length > 0) {\n return envToken;\n }\n\n // 2. Single-flight cache check (skip when forceRefresh is set).\n const cacheKey = `${opts.profile}::${opts.mcpUrl}`;\n if (!opts.forceRefresh) {\n const cached = CACHE.get(cacheKey);\n if (cached && nowFn() < cached.expiresAtMs) {\n return cached.accessToken;\n }\n }\n\n // 3. Stored refresh token → POST /auth/refresh.\n const store = await (opts.createStore ?? createTokenStore)();\n const stored = await store.load(opts.profile);\n if (!stored) {\n throw new AcquireError(\n `No stored credentials for profile '${opts.profile}'. Run \\`cleargate join <invite-url>\\` first, or export CLEARGATE_MCP_TOKEN.`,\n 'no_stored_token',\n );\n }\n\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n let response: Response;\n try {\n response = await fetchFn(`${opts.mcpUrl}/auth/refresh`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ refresh_token: stored }),\n });\n } catch (err) {\n throw new AcquireError(\n `cannot reach ${opts.mcpUrl} (${err instanceof Error ? err.message : String(err)})`,\n 'transport',\n );\n }\n\n if (response.status === 401) {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n if (body.error === 'token_revoked') {\n throw new AcquireError(\n 'refresh token was revoked. Run `cleargate join <invite-url>` to re-authenticate.',\n 'token_revoked',\n );\n }\n throw new AcquireError(\n 'refresh token is invalid or expired. Run `cleargate join <invite-url>` to re-authenticate.',\n 'invalid_token',\n );\n }\n\n if (!response.ok) {\n throw new AcquireError(`unexpected status ${response.status} from /auth/refresh`, 'unexpected_status');\n }\n\n const body = (await response.json().catch(() => null)) as\n | { access_token?: unknown; refresh_token?: unknown }\n | null;\n if (\n !body ||\n typeof body.access_token !== 'string' ||\n typeof body.refresh_token !== 'string' ||\n body.access_token.length === 0 ||\n body.refresh_token.length === 0\n ) {\n throw new AcquireError('server returned unexpected /auth/refresh response shape', 'bad_response');\n }\n\n // Rotate — store the new refresh token so the next call uses a fresh jti.\n await store.save(opts.profile, body.refresh_token);\n\n const accessToken = body.access_token;\n\n // 4. Cache the new access token (expire 60s before the JWT exp claim).\n const payload = decodeJwtPayload(accessToken);\n const exp = payload?.exp;\n if (typeof exp === 'number' && Number.isFinite(exp)) {\n const expiresAtMs = (exp - 60) * 1000;\n CACHE.set(cacheKey, { accessToken, expiresAtMs });\n }\n // If exp is missing or non-numeric, do NOT cache — next call will re-refresh.\n\n return accessToken;\n}\n","/**\n * cleargate whoami — smoke-test command that proves the stored refresh token\n * can be exchanged for a short-lived MCP access token, decodes the JWT payload\n * (no verification — just introspection), and prints identity fields.\n *\n * This is the first command to exercise the acquireAccessToken flow end-to-end;\n * sync/pull/push will be wired in follow-up stories.\n */\nimport { Buffer } from 'node:buffer';\nimport { loadConfig, requireMcpUrl } from '../config.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\n\nexport interface WhoamiOptions {\n profile: string;\n mcpUrlFlag?: string;\n /** Test seam: replaces globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n}\n\nfunction decodeJwtPayload(jwt: string): Record<string, unknown> | null {\n const parts = jwt.split('.');\n if (parts.length !== 3) return null;\n try {\n const json = Buffer.from(parts[1]!, 'base64url').toString('utf8');\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\nexport async function whoamiHandler(opts: WhoamiOptions): Promise<void> {\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n const cfg = loadConfig({ flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag } });\n let mcpUrl: string;\n try {\n mcpUrl = requireMcpUrl(cfg);\n } catch (err) {\n stderr(`cleargate: ${err instanceof Error ? err.message : String(err)}\\n`);\n exit(5);\n return;\n }\n\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl,\n profile: opts.profile,\n fetch: opts.fetch,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`cleargate: ${err.message}\\n`);\n exit(err.code === 'transport' ? 2 : 5);\n return;\n }\n stderr(`cleargate: internal error: ${err instanceof Error ? err.message : String(err)}\\n`);\n exit(99);\n return;\n }\n\n const claims = decodeJwtPayload(accessToken);\n if (!claims) {\n stderr('cleargate: access token received but could not decode payload.\\n');\n exit(7);\n return;\n }\n\n stdout(\n [\n `mcp_url: ${mcpUrl}`,\n `profile: ${opts.profile}`,\n `member_id: ${claims.sub ?? '?'}`,\n `project_id: ${claims.project_id ?? '?'}`,\n `role: ${claims.role ?? '?'}`,\n `expires_at: ${typeof claims.exp === 'number' ? new Date(claims.exp * 1000).toISOString() : '?'}`,\n '',\n ].join('\\n'),\n );\n}\n","/**\n * `cleargate admin bootstrap-root <handle>` — seed the first root admin.\n *\n * Idempotent: running the same handle twice is safe. Provides a `--force` flag\n * to either promote an existing non-root user or insert a second root when one\n * already exists.\n *\n * Exit codes:\n * 0 — success (including idempotent no-op)\n * 1 — validation error (bad handle, second-root guard refused)\n * 2 — missing DATABASE_URL\n * 3 — connection / query failure\n *\n * DATABASE_URL password is scrubbed from all error output.\n *\n * STORY-011-03.\n */\n\nimport pg from 'pg';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Handle validation\n// ─────────────────────────────────────────────────────────────────────────────\n\n// GitHub handle grammar: starts with alphanumeric, ends with alphanumeric,\n// allows hyphens in the middle, max 39 chars total.\n// Single-char handles (no middle section) are allowed.\n// Trailing hyphens are rejected by requiring the last char to be alphanumeric\n// when the handle is longer than 1 char.\nconst HANDLE_RE = /^[A-Za-z0-9]([A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/;\n\nexport function isValidHandle(handle: string): boolean {\n return HANDLE_RE.test(handle);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Password scrubber\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst SCRUB = /(postgres(?:ql)?:\\/\\/[^:/@]+):[^@/]+@/gi;\n\nexport function scrubPassword(s: string): string {\n return s.replace(SCRUB, '$1:***@');\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Minimal pg client surface needed by this handler. */\nexport interface PgClientLike {\n // connect() returns Promise<void> or Promise<Client> depending on pg version.\n // We accept either by using Promise<unknown>.\n connect(): Promise<unknown>;\n query(text: string, values?: unknown[]): Promise<{ rows: Record<string, unknown>[] }>;\n end(): Promise<void>;\n}\n\nexport interface BootstrapRootOptions {\n handle: string;\n databaseUrl?: string;\n force?: boolean;\n env?: NodeJS.ProcessEnv;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam — factory receives the resolved connection string */\n pgClientFactory?: (url: string) => PgClientLike;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal result — avoids exception-based control flow bleeding into the\n// SQL error catch block (exit seam throws in tests; we must not catch those).\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface SqlResult {\n exitCode: number;\n kind: 'stdout' | 'stderr';\n message: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main handler\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function bootstrapRootHandler(opts: BootstrapRootOptions): Promise<void> {\n const env = opts.env ?? process.env;\n const stdoutFn = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn = opts.exit ?? ((code: number): never => process.exit(code));\n const force = opts.force ?? false;\n\n // ── 1. Handle validation ────────────────────────────────────────────────────\n const { handle } = opts;\n if (!isValidHandle(handle)) {\n stderrFn(`cleargate: error: '${handle}' is not a valid GitHub handle`);\n return exitFn(1);\n }\n\n // ── 2. Resolve DATABASE_URL ─────────────────────────────────────────────────\n const url = opts.databaseUrl ?? env['DATABASE_URL'];\n if (!url) {\n stderrFn('cleargate: error: DATABASE_URL is required (set env or pass --database-url)');\n return exitFn(2);\n }\n\n // ── 3. Connect ──────────────────────────────────────────────────────────────\n const clientFactory =\n opts.pgClientFactory ??\n ((connStr: string): PgClientLike => new pg.Client({ connectionString: connStr }));\n\n const client = clientFactory(url);\n\n try {\n await client.connect();\n } catch (err) {\n const raw = err instanceof Error ? `${err.message} ${err.stack ?? ''}` : String(err);\n stderrFn(`cleargate: error: cannot reach database (${scrubPassword(raw.trim())})`);\n return exitFn(3);\n }\n\n // ── 4. Run SQL (returns a SqlResult, never throws for logic branches) ────────\n // By returning a value rather than calling exitFn() inside runSql(), we ensure\n // that if the test exit-seam throws, the throw propagates out of the whole\n // handler rather than being caught here as a \"database error\".\n let result: SqlResult;\n let sqlError: unknown = undefined;\n\n try {\n result = await runSql(client, handle, force);\n } catch (err) {\n sqlError = err;\n result = { exitCode: 3, kind: 'stderr', message: '' }; // filled below\n }\n\n // ── 5. Close connection ─────────────────────────────────────────────────────\n try {\n await client.end();\n } catch {\n // ignore cleanup error\n }\n\n // ── 6. Handle SQL error ─────────────────────────────────────────────────────\n if (sqlError !== undefined) {\n const err = sqlError;\n const raw = err instanceof Error ? `${err.message} ${err.stack ?? ''}` : String(err);\n stderrFn(`cleargate: error: cannot reach database (${scrubPassword(raw.trim())})`);\n return exitFn(3);\n }\n\n // ── 7. Emit message and exit ────────────────────────────────────────────────\n if (result!.kind === 'stdout') {\n stdoutFn(result!.message);\n } else {\n stderrFn(result!.message);\n }\n return exitFn(result!.exitCode);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SQL logic — returns SqlResult, never calls exitFn\n// ─────────────────────────────────────────────────────────────────────────────\n\nasync function runSql(\n client: PgClientLike,\n handle: string,\n force: boolean,\n): Promise<SqlResult> {\n await client.query('BEGIN');\n\n // Step 1: does the handle already exist?\n const existing = await client.query(\n 'SELECT id, is_root FROM admin_users WHERE github_handle = $1',\n [handle],\n );\n\n if (existing.rows.length > 0) {\n const row = existing.rows[0]!;\n\n if (row['is_root'] === true) {\n // Branch A: already a root admin — idempotent no-op\n await client.query('COMMIT');\n return {\n exitCode: 0,\n kind: 'stdout',\n message: `Root admin '${handle}' already exists; no change.`,\n };\n }\n\n // Row exists but is_root=false\n if (force) {\n // Branch B: promote\n await client.query(\n 'UPDATE admin_users SET is_root = true WHERE github_handle = $1',\n [handle],\n );\n await client.query('COMMIT');\n return {\n exitCode: 0,\n kind: 'stdout',\n message: `Promoted '${handle}' to root admin.`,\n };\n }\n\n // Branch C: existing non-root, no --force\n await client.query('ROLLBACK');\n return {\n exitCode: 1,\n kind: 'stderr',\n message: `cleargate: error: '${handle}' exists but is not a root admin; pass --force to promote`,\n };\n }\n\n // No row for this handle — check if another root exists\n if (!force) {\n const rootCount = await client.query(\n 'SELECT count(*) AS cnt FROM admin_users WHERE is_root = true',\n );\n const cnt = Number((rootCount.rows[0] as { cnt: string })['cnt'] ?? 0);\n\n if (cnt >= 1) {\n // Branch D: refuse second root without --force\n await client.query('ROLLBACK');\n return {\n exitCode: 1,\n kind: 'stderr',\n message:\n 'cleargate: error: refusing to create a second root admin; pass --force to override',\n };\n }\n }\n\n // Branch E: insert (first root, or --force allows a second root)\n await client.query(\n `INSERT INTO admin_users (github_handle, is_root)\n VALUES ($1, true)\n ON CONFLICT (github_handle) DO UPDATE SET is_root = EXCLUDED.is_root`,\n [handle],\n );\n await client.query('COMMIT');\n return {\n exitCode: 0,\n kind: 'stdout',\n message: `Bootstrapped root admin '${handle}'.`,\n };\n}\n","// SPRINT-14 M5 dogfood smoke — STORY-099-01\nimport { Command } from 'commander';\nimport pkg from '../package.json' with { type: 'json' };\nimport { scaffoldLintHandler } from './commands/scaffold-lint.js';\nimport { joinHandler } from './commands/join.js';\nimport { stampHandler } from './commands/stamp.js';\nimport { initHandler } from './commands/init.js';\nimport { wikiBuildHandler } from './commands/wiki-build.js';\nimport { wikiIngestHandler } from './commands/wiki-ingest.js';\nimport { wikiLintHandler } from './commands/wiki-lint.js';\nimport { wikiQueryHandler } from './commands/wiki-query.js';\nimport { wikiAuditStatusHandler } from './commands/wiki-audit-status.js';\nimport { doctorHandler } from './commands/doctor.js';\nimport { gateCheckHandler, gateExplainHandler, gateQaHandler, gateArchHandler } from './commands/gate.js';\nimport { gateRunHandler } from './commands/gate-run.js';\nimport { sprintInitHandler, sprintCloseHandler, sprintArchiveHandler } from './commands/sprint.js';\nimport { storyStartHandler, storyCompleteHandler } from './commands/story.js';\nimport { stateUpdateHandler, stateValidateHandler } from './commands/state.js';\nimport { stampTokensHandler } from './commands/stamp-tokens.js';\nimport { upgradeHandler } from './commands/upgrade.js';\nimport { uninstallHandler } from './commands/uninstall.js';\nimport { syncHandler, syncCheckHandler } from './commands/sync.js';\nimport { pullHandler } from './commands/pull.js';\nimport { pushHandler } from './commands/push.js';\nimport { conflictsHandler } from './commands/conflicts.js';\nimport { syncLogHandler } from './commands/sync-log.js';\nimport { adminLoginHandler } from './commands/admin-login.js';\nimport { hotfixNewHandler } from './commands/hotfix.js';\n\nconst program = new Command();\n\nprogram\n .name('cleargate')\n .description('ClearGate CLI — connects AI agent teams to the ClearGate MCP server')\n .version(pkg.version, '-V, --version')\n .option('--profile <name>', 'configuration profile to use', 'default')\n .option('--mcp-url <url>', 'MCP server URL (overrides config file and env)')\n .showHelpAfterError('(use `cleargate --help`)');\n\nprogram\n .command('join <invite-url>')\n .description('join a ClearGate workspace using an invite URL')\n .option('--auth <provider>', 'identity provider: github | email')\n .option('--non-interactive', 'fail instead of prompting (CI mode)')\n .option('--code <code>', 'OTP code for non-interactive email auth')\n .action(async (inviteUrl: string, _opts: Record<string, unknown>, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n const cmdOpts = command.opts<{ auth?: string; nonInteractive?: boolean; code?: string }>();\n await joinHandler({\n inviteUrl,\n profile: globals.profile,\n mcpUrlFlag: globals.mcpUrl,\n // FLASHCARD #cli #commander #optional-key: only set keys when defined\n ...(cmdOpts.auth !== undefined ? { auth: cmdOpts.auth } : {}),\n ...(cmdOpts.nonInteractive === true ? { nonInteractive: true } : {}),\n ...(cmdOpts.code !== undefined ? { code: cmdOpts.code } : {}),\n });\n });\n\nprogram\n .command('init')\n .description('initialise a repo with ClearGate scaffold (CLAUDE.md block, hook config, agents, templates)')\n .option('--force', 'overwrite existing files that differ from the bundled payload')\n .option('--yes', 'non-interactive: accept all defaults without prompting')\n .option('--pin <ver>', 'CR-009: pin hook resolver to a specific cleargate CLI version (default: package version)')\n .action(async (opts: { force?: boolean; yes?: boolean; pin?: string }) => {\n await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false, pin: opts.pin });\n });\n\nprogram\n .command('whoami')\n .description('print the currently authenticated agent identity')\n .action(async () => {\n const { whoamiHandler } = await import('./commands/whoami.js');\n const parentOpts = program.opts<{ profile: string; mcpUrl?: string }>();\n await whoamiHandler({\n profile: parentOpts.profile,\n mcpUrlFlag: parentOpts.mcpUrl,\n });\n });\n\nprogram\n .command('stamp <file>')\n .description('stamp ClearGate metadata fields into a file\\'s frontmatter')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampHandler(file, { dryRun: opts.dryRun });\n });\n\nconst wiki = program\n .command('wiki')\n .description('query or update the workspace wiki');\n\nwiki\n .command('build')\n .description('full rebuild of .cleargate/wiki/ from raw delivery items')\n .action(async () => {\n await wikiBuildHandler();\n });\n\nwiki\n .command('ingest <file>')\n .description('ingest a single raw delivery file into the wiki')\n .action(async (file: string) => {\n await wikiIngestHandler({ rawPath: file });\n });\n\nwiki\n .command('lint')\n .description('check wiki pages for drift vs raw sources')\n .option('--suggest', 'advisory mode — exit 0, emit suggestions only')\n .helpOption('--help', [\n 'Usage: cleargate wiki lint [--suggest]',\n '',\n 'Enforcement mode (default): exits 1 on any finding.',\n 'Suggest mode (--suggest): exits 0, prefixes findings with [advisory],',\n ' and emits Karpathy cross-ref discovery candidates.',\n ].join('\\n'))\n .action(async (_opts: Record<string, unknown>, command: Command) => {\n const cmdOpts = command.opts<{ suggest?: boolean }>();\n await wikiLintHandler({\n mode: cmdOpts.suggest ? 'suggest' : 'enforce',\n });\n });\n\nwiki\n .command('query <terms...>')\n .description('search the wiki index for matching work items')\n .option('--persist', 'write result as a topic page under wiki/topics/')\n .addHelpText('after', [\n '',\n 'NOTE: CLI synthesis is grep-and-list. For NL synthesis with the',\n 'cleargate-wiki-query subagent, invoke from a Claude Code session.',\n 'This diverges from PROPOSAL-002 §2.2 intentionally for testability',\n 'and offline/scripted use.',\n ].join('\\n'))\n .action(async (terms: string[], opts: { persist?: boolean }) => {\n await wikiQueryHandler({\n query: terms.join(' '),\n persist: opts.persist ?? false,\n });\n });\n\nwiki\n .command('audit-status')\n .description('detect raw-item status/location drift; --fix applies safe corrections')\n .option('--fix', 'apply safe status corrections to frontmatter')\n .option('--yes', 'required together with --fix to confirm writes')\n .option('--quiet', 'suppress diff output')\n .action(async (opts: { fix?: boolean; yes?: boolean; quiet?: boolean }) => {\n await wikiAuditStatusHandler(opts);\n });\n\nconst gate = program\n .command('gate')\n .description('evaluate readiness gates for a ClearGate work-item file');\n\ngate\n .command('check <file>')\n .description('evaluate readiness criteria and write result to frontmatter')\n .option('-v, --verbose', 'show full expected-vs-actual detail per criterion')\n .option('--transition <name>', 'override auto-detected transition name')\n .action(async (file: string, opts: { verbose?: boolean; transition?: string }) => {\n await gateCheckHandler(file, { verbose: opts.verbose, transition: opts.transition });\n });\n\ngate\n .command('explain <file>')\n .description('render cached gate result in ≤50 agent tokens (read-only)')\n .action(async (file: string) => {\n await gateExplainHandler(file);\n });\n\ngate\n .command('qa <worktree> <branch>')\n .description('run QA pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateQaHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\ngate\n .command('arch <worktree> <branch>')\n .description('run Architect pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateArchHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\n// STORY-018-03: config-driven gates. Commander v12 does NOT treat `<name>` as a\n// catch-all fallback when sibling literal subcommands exist (QA'd on 2026-04-25\n// — it emits \"unknown command\" before a parameterized handler can fire). Since\n// the gate names are a closed set, enumerate them explicitly.\nfor (const gateName of ['precommit', 'test', 'typecheck', 'lint'] as const) {\n gate\n .command(gateName)\n .description(`run the configured ${gateName} gate command`)\n .option('--strict', 'exit non-zero if gate not configured')\n .action((opts: { strict?: boolean }) => {\n gateRunHandler(gateName, { strict: opts.strict === true ? true : undefined });\n });\n}\n\nprogram\n .command('scaffold-lint')\n .description('grep cleargate-planning/ for stack-specific strings; fail on leaks')\n .option('--fix-hint', 'emit placeholder suggestions per finding')\n .option('--versions', 'also flag semver-shaped strings')\n .option('--quiet', 'suppress per-finding output; exit code only')\n .action(async (opts: { fixHint?: boolean; versions?: boolean; quiet?: boolean }) => {\n await scaffoldLintHandler(opts);\n });\n\nconst sprint = program\n .command('sprint')\n .description('sprint lifecycle commands (v2 only — inert under v1)');\n\nsprint\n .command('init <sprint-id>')\n .description('initialise a new sprint — creates state.json and worktree skeleton')\n .requiredOption('--stories <csv>', 'comma-separated story IDs for this sprint')\n .action((sprintId: string, opts: { stories: string }) => {\n sprintInitHandler({ sprintId, stories: opts.stories });\n });\n\nsprint\n .command('close <sprint-id>')\n .description('close a sprint — validates all stories are terminal, runs prefill + suggest_improvements')\n .option('--assume-ack', 'skip the \"waiting for Reporter\" gate and flip state to Completed directly')\n .action((sprintId: string, opts: { assumeAck?: boolean }) => {\n const handlerOpts: { sprintId: string; assumeAck?: boolean } = { sprintId };\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n if (opts.assumeAck === true) {\n handlerOpts.assumeAck = true;\n }\n sprintCloseHandler(handlerOpts);\n });\n\nsprint\n .command('archive <sprint-id>')\n .description('archive a completed sprint — move pending-sync files, clear .active, merge + delete sprint branch')\n .option('--dry-run', 'print the archive plan without making any changes')\n .action(async (sprintId: string, opts: { dryRun?: boolean }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const handlerOpts: { sprintId: string; dryRun?: boolean } = { sprintId };\n if (opts.dryRun === true) {\n handlerOpts.dryRun = true;\n }\n await sprintArchiveHandler(handlerOpts);\n });\n\nconst story = program\n .command('story')\n .description('story lifecycle commands (v2 only — inert under v1)');\n\nstory\n .command('start <story-id>')\n .description('create a git worktree for a story on the sprint branch')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyStartHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nstory\n .command('complete <story-id>')\n .description('mark a story complete and clean up its worktree (stub — requires complete_story.mjs)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyCompleteHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nconst state = program\n .command('state')\n .description('state.json management commands (v2 only — inert under v1)');\n\nstate\n .command('update <story-id> <new-state>')\n .description('update a story\\'s state in state.json')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup (overrides .active sentinel)')\n .action((storyId: string, newState: string, opts: { sprint?: string }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const cliOpts: Parameters<typeof stateUpdateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateUpdateHandler({ storyId, newState }, cliOpts);\n });\n\nstate\n .command('validate <sprint-id>')\n .description('validate all story states in a sprint\\'s state.json')\n .option('--sprint <id>', 'override sprint ID for execution_mode lookup')\n .action((sprintId: string, opts: { sprint?: string }) => {\n const cliOpts: Parameters<typeof stateValidateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateValidateHandler({ sprintId: opts.sprint ?? sprintId }, cliOpts);\n });\n\nprogram\n .command('stamp-tokens <file>')\n .description('stamp draft_tokens from token-ledger into a work-item file (hook-invoked)')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampTokensHandler(file, { dryRun: opts.dryRun });\n });\n\nconst admin = program\n .command('admin')\n .description('administrative operations (login, create-project, invite, issue-token, revoke-token)');\n\nadmin\n .command('login')\n .description('log in as a ClearGate admin via GitHub OAuth device flow')\n .option('--mcp-url <url>', 'MCP server URL (overrides CLEARGATE_MCP_URL and config file)')\n .action(async (opts: { mcpUrl?: string }, command: Command) => {\n const globals = command.parent!.parent!.opts<{ mcpUrl?: string }>();\n await adminLoginHandler({\n mcpUrl: opts.mcpUrl ?? globals.mcpUrl,\n });\n });\n\nadmin\n .command('bootstrap-root <handle>')\n .description('seed the first root admin in admin_users (idempotent)')\n .option('--database-url <url>', 'Postgres connection string; falls back to DATABASE_URL env')\n .option('--force', 'override second-root guard / promote non-root user to root')\n .action(async (handle: string, opts: { databaseUrl?: string; force?: boolean }) => {\n const { bootstrapRootHandler } = await import('./commands/bootstrap-root.js');\n await bootstrapRootHandler({ handle, databaseUrl: opts.databaseUrl, force: opts.force ?? false });\n });\n\nprogram\n .command('doctor')\n .description('diagnose scaffold drift, hook health, blocked items, and token cost')\n .option('--check-scaffold', 'check scaffold files for drift against install snapshot')\n .option('--session-start-mode', 'hidden: enables daily throttle (used by session-start hook)', false)\n .option('--session-start', 'emit blocked pending-sync items summary (used by SessionStart hook)')\n .option('--pricing <file>', 'compute USD cost estimate from a work item\\'s draft_tokens')\n .option('--can-edit <file>', 'CR-008: exit 0 if editing file is allowed, exit 1 if planning required')\n .option('--cwd <dir>', 'working directory for the doctor check (default: process.cwd())')\n .option('-v, --verbose', 'show per-file drift detail')\n .addHelpText('after', [\n '',\n 'Modes (mutually exclusive):',\n ' --check-scaffold Compute drift for all tracked scaffold files.',\n ' Writes .cleargate/.drift-state.json.',\n ' --session-start List blocked pending-sync items (≤10, ≤100 tokens).',\n ' --pricing <file> Compute USD estimate from a work item\\'s draft_tokens.',\n ' --can-edit <file> Check if editing a file requires a planning work item.',\n ' (default) Print a minimal hook-config health report.',\n '',\n 'Exit codes:',\n ' 0 Clean — no blockers, no config errors.',\n ' 1 Blocked items or advisory issues — see stdout.',\n ' 2 ClearGate misconfigured or partially installed — see stdout for remediation.',\n ].join('\\n'))\n .action(async (opts: { checkScaffold?: boolean; sessionStartMode?: boolean; sessionStart?: boolean; pricing?: string; canEdit?: string; cwd?: string; verbose?: boolean }) => {\n await doctorHandler({\n checkScaffold: opts.checkScaffold,\n sessionStartMode: opts.sessionStartMode,\n sessionStart: opts.sessionStart,\n pricing: !!opts.pricing,\n pricingFile: opts.pricing,\n canEdit: !!opts.canEdit,\n canEditFile: opts.canEdit,\n verbose: opts.verbose,\n }, opts.cwd ? { cwd: opts.cwd } : undefined);\n });\n\nprogram\n .command('upgrade')\n .description('three-way merge scaffold files with upstream changes')\n .option('--dry-run', 'print plan without making any changes')\n .option('--yes', 'auto-accept \"take theirs\" for all merge-3way files (non-interactive)')\n .option('--only <tier>', 'restrict to a specific scaffold tier (protocol/template/agent/hook/skill/cli-config)')\n .addHelpText('after', [\n '',\n 'Overwrite policies:',\n ' always — silent overwrite with package content',\n ' never — silent skip',\n ' preserve — silent skip',\n ' merge-3way — interactive: [k]eep mine / [t]ake theirs / [e]dit in $EDITOR',\n '',\n '--yes auto-accepts [t]ake theirs for all merge-3way files.',\n ].join('\\n'))\n .action(async (opts: { dryRun?: boolean; yes?: boolean; only?: string }) => {\n await upgradeHandler({ dryRun: opts.dryRun, yes: opts.yes, only: opts.only });\n });\n\nprogram\n .command('uninstall')\n .description('remove ClearGate scaffold from a project (preservation-first)')\n .option('--dry-run', 'preview planned actions without making any changes (CI-safe)')\n .option('--preserve <tiers>', 'comma-separated tier ids to force-preserve (default: user-artifact)')\n .option('--remove <tiers>', 'comma-separated tier ids to force-remove; use \"all\" to remove everything including user artifacts (DANGEROUS)')\n .option('--yes', 'skip typed project-name confirmation (dangerous — use in scripts/CI)')\n .option('--path <dir>', 'target directory (must contain .cleargate/.install-manifest.json); defaults to cwd')\n .option('--force', 'bypass uncommitted-changes safety check (not applicable for non-git targets)')\n .addHelpText('after', [\n '',\n 'Preservation defaults:',\n ' user-artifact tier → kept (FLASHCARD.md, archive, pending-sync, sprint REPORT.md)',\n ' framework tiers → removed (protocol, template, agent, hook, skill, cli-config)',\n '',\n 'Always removed (no prompt): .claude/agents/*.md, ClearGate hooks,',\n ' .claude/skills/flashcard/, CLAUDE.md CLEARGATE block,',\n ' `cleargate` from package.json, .install-manifest.json, .drift-state.json.',\n '',\n 'Non-git targets: uncommitted-changes check is skipped silently.',\n ].join('\\n'))\n .action(async (opts: {\n dryRun?: boolean;\n preserve?: string;\n remove?: string;\n yes?: boolean;\n path?: string;\n force?: boolean;\n }) => {\n await uninstallHandler({\n dryRun: opts.dryRun,\n preserve: opts.preserve ? opts.preserve.split(',').map((s) => s.trim()) : undefined,\n remove: opts.remove ? opts.remove.split(',').map((s) => s.trim()) : undefined,\n yes: opts.yes,\n path: opts.path,\n force: opts.force,\n });\n });\n\nprogram\n .command('sync')\n .description('pull remote updates, resolve conflicts, push local changes')\n .option('--dry-run', 'print plan without making any changes or sync-log entries')\n .option('--check', 'read-only drift probe — prints JSON, no mutation, hook-safe')\n .action(async (opts: { dryRun?: boolean; check?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n if (opts.check) {\n await syncCheckHandler({ profile: globals.profile });\n return;\n }\n await syncHandler({ dryRun: opts.dryRun ?? false, profile: globals.profile });\n });\n\nprogram\n .command('pull <id-or-remote-id>')\n .description('pull a single item from the remote PM tool by local ID or remote_id')\n .option('--comments', 'also pull comments for this item (STORY-010-06; not yet implemented)')\n .action(async (idOrRemoteId: string, opts: { comments?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pullHandler(idOrRemoteId, { comments: opts.comments, profile: globals.profile });\n });\n\nprogram\n .command('push <file>')\n .description('push a local work item to the MCP server (requires approved: true in frontmatter)')\n .option('--revert <id-or-remote-id>', 'soft-revert a pushed item by setting status to archived-without-shipping')\n .option('--force', 'bypass the \"done\" status guard when reverting')\n .addHelpText('after', [\n '',\n 'Push mode:',\n ' Reads local frontmatter. Requires approved: true — exits 1 without network call otherwise.',\n ' On success: writes pushed_by + pushed_at from server back to local frontmatter.',\n ' Appends sync-log entry op=push.',\n '',\n 'Revert mode (--revert <id-or-remote-id>):',\n ' Calls cleargate_sync_status with status=archived-without-shipping.',\n ' Does NOT delete the remote item or remove local remote_id.',\n ' Refuses if local status=done unless --force is passed.',\n ' Appends sync-log entry op=push-revert.',\n ].join('\\n'))\n .action(async (file: string, opts: { revert?: string; force?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pushHandler(file, { revert: opts.revert, force: opts.force, profile: globals.profile });\n });\n\nprogram\n .command('conflicts')\n .description('list unresolved sync conflicts from .cleargate/.conflicts.json')\n .option('--refresh', 'force a new /auth/refresh even if the cached token is still valid')\n .action(async (opts: { refresh?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await conflictsHandler({ refresh: opts.refresh, profile: globals.profile });\n });\n\nprogram\n .command('sync-log')\n .description('filter and print sync-log entries')\n .option('--actor <email>', 'filter by actor email')\n .option('--op <op>', 'filter by operation (push|pull|pull-intake|...)')\n .option('--target <id>', 'filter by target work item ID')\n .option('--limit <n>', 'maximum number of entries to show (default 50)', '50')\n .action(async (opts: { actor?: string; op?: string; target?: string; limit?: string }) => {\n await syncLogHandler({\n actor: opts.actor,\n op: opts.op,\n target: opts.target,\n limit: opts.limit !== undefined ? parseInt(opts.limit, 10) : 50,\n });\n });\n\nconst hotfix = program\n .command('hotfix')\n .description('hotfix lane commands (off-sprint trivial fix scaffolding)');\n\n// FLASHCARD #cli #commander #subcommand-routing (2026-04-25): Commander v12\n// does NOT treat `<verb>` as a catch-all fallback when sibling literal\n// subcommands exist. Since `new` is the only verb for now, enumerate it\n// explicitly as a literal subcommand.\nhotfix\n .command('new <slug>')\n .description('scaffold a new HOTFIX-NNN_<slug>.md in pending-sync/')\n .action((slug: string) => {\n hotfixNewHandler({ slug });\n });\n\nvoid program.parseAsync(process.argv);\n","{\n \"name\": \"cleargate\",\n \"version\": \"0.6.2\",\n \"private\": false,\n \"type\": \"module\",\n \"description\": \"Planning framework for Claude Code agents — sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.\",\n \"license\": \"MIT\",\n \"bin\": {\n \"cleargate\": \"dist/cli.js\"\n },\n \"main\": \"./dist/cli.cjs\",\n \"module\": \"./dist/cli.js\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/cli.d.ts\",\n \"import\": \"./dist/cli.js\",\n \"require\": \"./dist/cli.cjs\"\n },\n \"./admin-api\": {\n \"types\": \"./dist/admin-api/index.d.ts\",\n \"import\": \"./dist/admin-api/index.js\",\n \"require\": \"./dist/admin-api/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"scripts\": {\n \"prebuild\": \"tsx scripts/build-manifest.ts && node scripts/copy-planning-payload.mjs\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"pretest\": \"npm run build\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@napi-rs/keyring\": \"^1.2.0\",\n \"commander\": \"^12\",\n \"diff\": \"^5.2.2\",\n \"js-yaml\": \"^4.1.0\",\n \"pg\": \"^8.12.0\",\n \"zod\": \"^4.3.0\"\n },\n \"devDependencies\": {\n \"@types/diff\": \"^5.2.3\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"@types/pg\": \"^8.11.10\",\n \"tsup\": \"^8\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.8.0\",\n \"vitest\": \"^2.1.0\"\n }\n}\n","/**\n * scaffold-lint.ts — `cleargate scaffold-lint` command handler.\n *\n * STORY-018-04: Scans cleargate-planning/ for stack-specific strings that\n * should not appear in the installable scaffold.\n *\n * Exit codes: 0 = clean, 1 = findings, 2 = config/parse error.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n DEFAULT_BLOCKLIST,\n getTermCategory,\n CATEGORY_PLACEHOLDERS,\n parseAllowlist,\n parseUserBlocklist,\n type AllowlistEntry,\n} from '../lib/scaffold-blocklist.js';\n\n// ─── Scan extensions ──────────────────────────────────────────────────────────\n\nconst SCAN_EXTENSIONS = new Set(['.md', '.sh', '.mjs', '.json']);\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ScaffoldLintOptions {\n fixHint?: boolean;\n versions?: boolean;\n quiet?: boolean;\n cwd?: string;\n planningDir?: string; // override for tests; defaults to path.join(cwd, 'cleargate-planning')\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n}\n\ninterface Finding {\n file: string; // relative to planningDir parent\n line: number;\n term: string;\n context: string; // matched line truncated to 80 chars\n}\n\n// ─── scaffoldLintHandler ──────────────────────────────────────────────────────\n\nexport function scaffoldLintHandler(opts: ScaffoldLintOptions): void {\n const stdoutFn = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n opts.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = opts.cwd ?? process.cwd();\n const planningDir = opts.planningDir ?? path.join(cwd, 'cleargate-planning');\n\n const result = runScaffoldLint(opts, cwd, planningDir, stdoutFn, stderrFn);\n\n if (result.exitCode === 0) {\n stdoutFn('scaffold-lint: clean');\n }\n\n if (result.exitCode !== 0) {\n exitFn(result.exitCode);\n }\n}\n\ninterface LintResult {\n exitCode: number;\n}\n\nfunction runScaffoldLint(\n opts: ScaffoldLintOptions,\n cwd: string,\n planningDir: string,\n stdoutFn: (s: string) => void,\n stderrFn: (s: string) => void,\n): LintResult {\n // ── 1. Load user blocklist (fail-fast on malformed) ─────────────────────────\n const userBlocklistPath = path.join(cwd, '.cleargate', 'scaffold-blocklist.txt');\n let userTerms: string[] = [];\n\n if (fs.existsSync(userBlocklistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(userBlocklistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: error reading ${userBlocklistPath}: ${String(err)}`);\n return { exitCode: 2 };\n }\n const parsed = parseUserBlocklist(raw, userBlocklistPath, stderrFn);\n if (parsed === null) {\n return { exitCode: 2 };\n }\n userTerms = parsed;\n }\n\n // ── 2. Load allowlist (skip + warn on malformed) ──────────────────────────\n const allowlistPath = path.join(cwd, '.cleargate', 'scaffold-allowlist.txt');\n let allowlistEntries: AllowlistEntry[] = [];\n\n if (fs.existsSync(allowlistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(allowlistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: warning: error reading ${allowlistPath}: ${String(err)}`);\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n allowlistEntries = parseAllowlist(raw!, allowlistPath, stderrFn);\n } catch {\n // parseAllowlist is synchronous and doesn't throw in normal usage\n }\n }\n\n // ── 3. Build combined blocklist ───────────────────────────────────────────\n const allTerms = [...DEFAULT_BLOCKLIST, ...userTerms];\n\n // ── 4. Walk cleargate-planning/ ───────────────────────────────────────────\n if (!fs.existsSync(planningDir)) {\n // No planning dir — nothing to scan, exit clean\n stdoutFn('scaffold-lint: clean');\n return { exitCode: 0 };\n }\n\n const files = walkDir(planningDir);\n\n // ── 5. Scan files ─────────────────────────────────────────────────────────\n const findings: Finding[] = [];\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!SCAN_EXTENSIONS.has(ext)) continue;\n\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf8');\n } catch {\n continue; // skip unreadable files\n }\n\n // Relative path from cwd for reporting\n const relPath = path.relative(cwd, filePath).replace(/\\\\/g, '/');\n\n const lines = content.split('\\n');\n for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {\n const lineNum = lineIdx + 1;\n const lineText = lines[lineIdx];\n\n for (const term of allTerms) {\n const re = new RegExp(escapeRegex(term), 'gi');\n if (!re.test(lineText)) continue;\n\n // Check allowlist\n if (isAllowlisted(relPath, term, allowlistEntries)) continue;\n\n findings.push({\n file: relPath,\n line: lineNum,\n term: term.toLowerCase(),\n context: lineText.slice(0, 80),\n });\n // Only record the FIRST matching term per line per occurrence\n // (break inner term loop to avoid duplicate lines for the same match)\n break;\n }\n }\n }\n\n // ── 6. Sort findings by file asc, line asc ───────────────────────────────\n findings.sort((a, b) => {\n if (a.file < b.file) return -1;\n if (a.file > b.file) return 1;\n return a.line - b.line;\n });\n\n // ── 7. Emit findings ──────────────────────────────────────────────────────\n if (findings.length === 0) {\n return { exitCode: 0 };\n }\n\n if (!opts.quiet) {\n for (const f of findings) {\n const line = `${f.file}:${f.line}: ${f.term} — example context: ${f.context}`;\n stderrFn(line);\n\n if (opts.fixHint) {\n const category = getTermCategory(f.term);\n const placeholder = category ? CATEGORY_PLACEHOLDERS.get(category) : undefined;\n if (placeholder) {\n stderrFn(` hint: replace with ${placeholder}`);\n } else {\n stderrFn(` hint: replace with <your-replacement>`);\n }\n }\n }\n }\n\n return { exitCode: 1 };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction walkDir(dir: string): string[] {\n const results: string[] = [];\n\n function recurse(current: string): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true }) as fs.Dirent[];\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n recurse(fullPath);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n }\n\n recurse(dir);\n return results;\n}\n\nfunction escapeRegex(term: string): string {\n return term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction isAllowlisted(\n relPath: string,\n term: string,\n entries: AllowlistEntry[],\n): boolean {\n const termLower = term.toLowerCase();\n\n for (const entry of entries) {\n if (entry.term !== termLower) continue;\n\n if (!entry.glob) {\n // Global suppression for this term\n return true;\n }\n\n // Glob is relative to repo root (cwd)\n // Use simple star matching via path.matchesGlob (Node 24)\n if (matchesGlob(relPath, entry.glob)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Minimal glob matcher using Node 24's path.matchesGlob when available,\n * with a fallback star-replace regex for older environments.\n */\nfunction matchesGlob(filePath: string, glob: string): boolean {\n // Node 24 built-in\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pathModule = path as any;\n if (typeof pathModule.matchesGlob === 'function') {\n return pathModule.matchesGlob(filePath, glob);\n }\n } catch {\n // fall through\n }\n\n // Fallback: simple star matching\n const regexStr = glob\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '§DSTAR§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§DSTAR§/g, '.*');\n\n return new RegExp(`^${regexStr}$`).test(filePath);\n}\n","/**\n * scaffold-blocklist.ts — Default blocklist for `cleargate scaffold-lint`.\n *\n * STORY-018-04: Stack-leak detection. These terms should not appear in the\n * installable scaffold under cleargate-planning/ — they indicate ClearGate's\n * own dogfooding vocabulary leaking into the template that downstream users\n * receive.\n *\n * Categories: ORMs, web frameworks, infra, DB engines, cache/queue, styling.\n */\n\n// ─── Blocklist categories ─────────────────────────────────────────────────────\n\nconst ORMS = ['drizzle', 'prisma', 'sequelize', 'typeorm'] as const;\n\nconst WEB_FRAMEWORKS = [\n 'fastify',\n 'express',\n 'hono',\n 'svelte',\n 'sveltekit',\n 'react',\n 'next.js',\n 'nextjs',\n 'nuxt',\n 'remix',\n 'vue',\n] as const;\n\nconst INFRA = ['coolify', 'vercel', 'netlify', 'heroku', 'render.com', 'fly.io'] as const;\n\nconst DB_ENGINES = ['postgres', 'postgresql', 'mysql', 'sqlite', 'mongodb', 'dynamodb'] as const;\n\nconst CACHE_QUEUE = ['redis', 'ioredis', 'memcached', 'rabbitmq', 'kafka'] as const;\n\nconst STYLING = ['daisyui', 'tailwind', 'bootstrap', 'mui'] as const;\n\n// ─── Category map (for --fix-hint placeholders) ───────────────────────────────\n\nexport type BlocklistCategory = 'orm' | 'framework' | 'infra' | 'db' | 'cache' | 'styling';\n\nconst TERM_CATEGORY_MAP: ReadonlyMap<string, BlocklistCategory> = new Map<string, BlocklistCategory>([\n ...ORMS.map((t): [string, BlocklistCategory] => [t, 'orm']),\n ...WEB_FRAMEWORKS.map((t): [string, BlocklistCategory] => [t, 'framework']),\n ...INFRA.map((t): [string, BlocklistCategory] => [t, 'infra']),\n ...DB_ENGINES.map((t): [string, BlocklistCategory] => [t, 'db']),\n ...CACHE_QUEUE.map((t): [string, BlocklistCategory] => [t, 'cache']),\n ...STYLING.map((t): [string, BlocklistCategory] => [t, 'styling']),\n]);\n\nexport const CATEGORY_PLACEHOLDERS: ReadonlyMap<BlocklistCategory, string> = new Map([\n ['orm', '<your-orm>'],\n ['framework', '<your-framework>'],\n ['infra', '<your-infra>'],\n ['db', '<your-db>'],\n ['cache', '<your-cache>'],\n ['styling', '<your-styling>'],\n]);\n\n// ─── Public exports ───────────────────────────────────────────────────────────\n\nexport const DEFAULT_BLOCKLIST: readonly string[] = [\n ...ORMS,\n ...WEB_FRAMEWORKS,\n ...INFRA,\n ...DB_ENGINES,\n ...CACHE_QUEUE,\n ...STYLING,\n];\n\n/**\n * Get the category for a blocklist term (default or user-supplied).\n * Returns undefined for user-supplied terms that don't map to a category.\n */\nexport function getTermCategory(term: string): BlocklistCategory | undefined {\n return TERM_CATEGORY_MAP.get(term.toLowerCase());\n}\n\n// ─── Allowlist parser ─────────────────────────────────────────────────────────\n\nexport interface AllowlistEntry {\n term: string;\n glob?: string;\n}\n\nexport interface ParsedAllowlist {\n entries: AllowlistEntry[];\n warnings: string[];\n}\n\n/**\n * Parse `.cleargate/scaffold-allowlist.txt` content.\n *\n * Format per line: `<term>[ <file-glob>]`\n * - Lines starting with `#` are comments.\n * - Blank lines are skipped.\n * - Term is case-insensitive substring.\n * - Glob is optional; when absent, suppresses ALL files.\n *\n * Returns parsed entries + any warnings (for malformed lines — per orchestrator\n * decision: skip + warn rather than fail; malformed-allowlist exit-2 is\n * handled by the blocklist file, not the allowlist).\n *\n * NOTE: Per M2.md \"malformed lines: fail fast\" applies to the *blocklist* file\n * format only. For allowlist, skip + warn to stderr per orchestrator Q2 decision.\n */\nexport function parseAllowlist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): AllowlistEntry[] {\n const entries: AllowlistEntry[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n // Skip blank lines and comments\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // Split on whitespace — first token is term, optional second is glob\n const parts = trimmed.split(/\\s+/);\n\n if (parts.length === 0 || parts[0] === '') {\n stderrFn(`scaffold-lint: warning: malformed line ${i + 1} in ${filePath}: ${raw}`);\n continue;\n }\n\n const entry: AllowlistEntry = { term: parts[0].toLowerCase() };\n if (parts.length >= 2) {\n entry.glob = parts[1];\n }\n entries.push(entry);\n }\n\n return entries;\n}\n\n/**\n * Parse `.cleargate/scaffold-blocklist.txt` (user-extensible).\n * Fail-fast on malformed lines (exit code 2).\n * Returns null if a parse error was encountered (caller should exit 2).\n */\nexport function parseUserBlocklist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): string[] | null {\n const terms: string[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // A valid term is a single non-whitespace token\n if (/\\s/.test(trimmed)) {\n stderrFn(`scaffold-lint: malformed line ${i + 1} in ${filePath}: ${raw}`);\n return null; // signal exit 2\n }\n\n terms.push(trimmed.toLowerCase());\n }\n\n return terms;\n}\n","/**\n * `cleargate join` — two-step identity-bound workspace invitation redemption.\n *\n * Flow:\n * 1. Parse invite URL/token.\n * 2. Pick provider (--auth flag, interactive picker, or error in non-interactive mode).\n * 3. POST /join/:token/challenge { provider } → get challengeId + clientHints.\n * 4. Drive provider flow:\n * - GitHub: client-side device-flow polling (via identity-flow.startDeviceFlow).\n * - Email: OTP prompt with 3-in-process retries (OD-3 resolution).\n * 5. POST /join/:token/complete { challenge_id, proof } → get refresh_token.\n * 6. Seat token via TokenStore.\n *\n * CR-006 EPIC-019.\n */\nimport * as os from 'node:os';\nimport { loadConfig } from '../config.js';\nimport { createTokenStore } from '../auth/factory.js';\nimport {\n pickProvider,\n startDeviceFlow,\n mapProviderError,\n DeviceFlowError,\n IdentityFlowError,\n type Provider,\n} from '../auth/identity-flow.js';\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\nconst UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// GitHub device-flow token endpoint (same as admin-login)\nconst GITHUB_DEVICE_FLOW_URL = 'https://github.com/login/oauth/access_token';\n\nexport interface JoinOptions {\n inviteUrl: string;\n profile: string;\n mcpUrlFlag?: string;\n /** Identity provider (--auth flag): 'github' | 'email'. */\n auth?: string;\n /** Non-interactive CI mode (--non-interactive flag). */\n nonInteractive?: boolean;\n /** Pre-seeded OTP code for non-interactive email auth (--code flag). */\n code?: string;\n /** Test seam: replaces globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n /** Test seam: replaces createTokenStore */\n createStore?: typeof createTokenStore;\n /** Test seam: replaces os.hostname() */\n hostname?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: replaces process.stdin for interactive prompts */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: whether stdin is a TTY (default: process.stdin.isTTY) */\n isTTY?: boolean;\n /** Test seam: sleepFn for device-flow polling (default: real setTimeout) */\n sleepFn?: (ms: number) => Promise<void>;\n /** Test seam: override device-flow poll interval in ms (0 = instant in tests) */\n intervalOverrideMs?: number;\n}\n\nexport async function joinHandler(opts: JoinOptions): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const hostname = opts.hostname ?? (() => os.hostname());\n const isTTY = opts.isTTY ?? (process.stdin.isTTY === true);\n\n // ── Parse inviteUrl → { token, baseUrl } ──────────────────────────────────\n let token: string;\n let baseUrl: string;\n\n try {\n if (UUID_V4_RE.test(opts.inviteUrl)) {\n // Bare UUID form — requires mcpUrl from config\n token = opts.inviteUrl;\n const cfg = loadConfig({\n flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },\n });\n if (!cfg.mcpUrl) {\n stderr(\n 'cleargate: bare invite token requires mcpUrl. Pass --mcp-url <url> or set CLEARGATE_MCP_URL.\\n',\n );\n exit(5);\n return;\n }\n baseUrl = cfg.mcpUrl;\n } else {\n // Full URL form: https://host/join/<uuid>\n const url = new URL(opts.inviteUrl);\n const m = url.pathname.match(/^\\/join\\/([0-9a-f-]{36})$/i);\n if (!m || !UUID_V4_RE.test(m[1]!)) {\n throw new Error('bad path');\n }\n token = m[1]!;\n baseUrl = url.origin;\n }\n } catch {\n stderr('cleargate: invalid invite URL or token format.\\n');\n exit(5);\n return;\n }\n\n // ── Non-interactive guards ────────────────────────────────────────────────\n if (opts.nonInteractive && !opts.auth) {\n stderr('cleargate: --auth required in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'email' && !opts.code) {\n stderr('cleargate: --code required for email provider in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'github') {\n stderr('cleargate: GitHub auth requires browser interaction; use `--auth email` for non-interactive flows\\n');\n exit(1);\n return;\n }\n\n // ── Pick provider ─────────────────────────────────────────────────────────\n let provider: Provider;\n try {\n provider = await pickProvider({\n flag: opts.auth,\n isTTY: !opts.nonInteractive && isTTY,\n available: ['github', 'email'],\n stdin: opts.stdin,\n stdout,\n });\n } catch (err) {\n if (err instanceof IdentityFlowError) {\n stderr(`${err.message}\\n`);\n exit(1);\n return;\n }\n throw err;\n }\n\n // ── POST /challenge ────────────────────────────────────────────────────────\n let challengeRes: Response;\n try {\n challengeRes = await fetchFn(`${baseUrl}/join/${token}/challenge`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ provider }),\n });\n } catch (err) {\n stderr(\n `cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`,\n );\n exit(2);\n return;\n }\n\n if (!challengeRes.ok) {\n const body = (await challengeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n challengeRes.status,\n body.error ?? '',\n parseRetryAfter(challengeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n let challengeBody: {\n challenge_id: string;\n provider: Provider;\n expires_in: number;\n client_hints: Record<string, unknown>;\n };\n try {\n challengeBody = (await challengeRes.json()) as typeof challengeBody;\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n\n const challengeId = challengeBody.challenge_id;\n const clientHints = challengeBody.client_hints;\n\n // ── Drive provider-specific completion + POST /complete ────────────────────\n let completeRawBody: unknown;\n\n if (provider === 'github') {\n // ── GitHub device-flow: poll GitHub client-side, then POST /complete ──────\n const deviceCode = clientHints['device_code'] as string;\n const userCode = clientHints['user_code'] as string;\n const verificationUri = clientHints['verification_uri'] as string;\n const expiresIn = typeof clientHints['expires_in'] === 'number' ? clientHints['expires_in'] as number : 900;\n const interval = typeof clientHints['interval'] === 'number' ? clientHints['interval'] as number : 5;\n\n stdout(`Open the following URL in your browser and enter the code:\\n`);\n stdout(` URL: ${verificationUri}\\n`);\n stdout(` Code: ${userCode}\\n`);\n stdout(` (Code expires in ${Math.floor(expiresIn / 60)} minutes)\\n`);\n stdout('Waiting for authorization...\\n');\n\n let accessToken: string;\n try {\n const result = await startDeviceFlow({\n deviceCode,\n interval,\n expiresIn,\n fetchPoll: async (dc) => {\n const res = await fetchFn(GITHUB_DEVICE_FLOW_URL, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: dc,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n });\n return {\n status: res.status,\n json: () => res.json() as Promise<unknown>,\n };\n },\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n });\n accessToken = result.accessToken;\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: access denied — you declined authorization in the browser.\\n');\n exit(5);\n return;\n case 'expired_token':\n stderr('cleargate: device code expired — please re-run `cleargate join <url>`.\\n');\n exit(5);\n return;\n case 'unreachable':\n stderr('cleargate: cannot reach GitHub. Check your connection and retry.\\n');\n exit(2);\n return;\n default:\n stderr(`cleargate: GitHub device flow error: ${err.code}\\n`);\n exit(6);\n return;\n }\n }\n stderr('cleargate: unexpected error during GitHub device flow\\n');\n exit(6);\n return;\n }\n\n // POST /complete with access_token proof (OD-2 fix: server member-mode accepts access_token)\n // FLASHCARD 2026-04-18 #cli #plaintext-redact: named field access only, never log accessToken\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { access_token: accessToken } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // ── Email magic-link: prompt for OTP with 3 retries (OD-3) ──────────────\n const sentTo = typeof clientHints['sent_to'] === 'string' ? clientHints['sent_to'] as string : '(unknown)';\n const maxRetries = 3;\n\n stdout(`We sent a 6-digit code to ${sentTo}.\\n`);\n\n // CI mode: use pre-seeded code directly\n if (opts.code !== undefined) {\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: opts.code } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // Interactive: prompt up to maxRetries times\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Queue-based line reading (pattern from identity-flow.ts)\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) return Promise.resolve(lineQueue.shift()!);\n if (rlClosed) return Promise.resolve('');\n return new Promise<string>((resolve) => { lineWaiters.push(resolve); });\n }\n\n let succeeded = false;\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n stdout('Enter code: ');\n const otpCode = (await readNextLine()).trim();\n\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: otpCode } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (completeRes.ok) {\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n succeeded = true;\n break;\n }\n\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const errorCode = body.error ?? '';\n\n // Terminal errors (don't retry)\n if (completeRes.status === 410 || errorCode === 'challenge_expired') {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n if (completeRes.status === 403 || (completeRes.status >= 400 && completeRes.status < 500 && errorCode !== 'provider_error')) {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n // Retryable (502 provider_error = wrong code)\n if (attempt < maxRetries) {\n stderr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n if (!succeeded) {\n stderr(`cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.\\n`);\n exit(12);\n return;\n }\n }\n }\n\n // ── Seat the refresh token ─────────────────────────────────────────────────\n const b = completeRawBody as {\n refresh_token?: unknown;\n project_name?: unknown;\n member_role?: unknown;\n };\n\n if (typeof b.refresh_token !== 'string' || typeof b.project_name !== 'string') {\n stderr('cleargate: server returned unexpected response shape.\\n');\n exit(7);\n return;\n }\n\n // Named field access — b.refresh_token is a bare string, never logged\n const refreshToken: string = b.refresh_token;\n const projectName: string = b.project_name;\n\n try {\n const store = await (opts.createStore ?? createTokenStore)();\n await store.save(opts.profile, refreshToken);\n\n // ── Success output ─────────────────────────────────────────────────────\n stdout(`joined project '${projectName}' as '${hostname()}'\\n`);\n stdout(`refresh token saved to ${store.backend}.\\n`);\n } catch (err) {\n stderr(\n `cleargate: internal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n exit(99);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const hdr = res.headers?.get?.('retry-after');\n if (!hdr) return undefined;\n const n = parseInt(hdr, 10);\n return isNaN(n) ? undefined : n;\n}\n","/**\n * Shared identity-flow helpers for `cleargate join` and `cleargate admin login`.\n *\n * Exposes:\n * - pickProvider — flag > required_provider pin > interactive picker > error\n * - promptPicker — numbered list via node:readline (no new dep)\n * - startDeviceFlow — GitHub device-flow poll loop (admin-login + member join)\n * - promptEmailOTP — 6-digit OTP prompt with up to 3 retries (OD-3)\n * - mapProviderError — HTTP-status + error-code → { message, exitCode, retryable }\n * - IdentityFlowError, DeviceFlowError typed error classes\n *\n * Test seams: functions that read from stdin or prompt accept\n * `{ stdin: NodeJS.ReadableStream, stdout: (s: string) => void }` as an\n * options object so tests can inject a pre-seeded Readable and capture output\n * without real TTY interaction.\n *\n * No new npm dependencies — uses only node:readline (built-in).\n *\n * CR-006 EPIC-019.\n */\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error classes\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class IdentityFlowError extends Error {\n constructor(\n public readonly code: string,\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'IdentityFlowError';\n }\n}\n\nexport class DeviceFlowError extends Error {\n constructor(\n public readonly code:\n | 'access_denied'\n | 'expired_token'\n | 'not_admin'\n | 'timeout'\n | 'unreachable'\n | 'server_error',\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'DeviceFlowError';\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type Provider = 'github' | 'email';\n\nexport interface PickProviderOptions {\n /** Explicit --auth flag value (wins over everything). */\n flag?: string;\n /** Is the process running in a real TTY? */\n isTTY?: boolean;\n /** Available providers (default: ['github', 'email']). */\n available?: Provider[];\n /** Stdin stream for interactive picker (test seam). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function for interactive picker (test seam). */\n stdout?: (s: string) => void;\n}\n\nexport interface StartDeviceFlowOptions {\n /** The device_code from the challenge client_hints. */\n deviceCode: string;\n /** Poll interval in seconds (from client_hints). */\n interval: number;\n /** How long until the device code expires in seconds. */\n expiresIn: number;\n /**\n * Function to POST to the poll endpoint.\n * Returns a Response-like object with status + json().\n */\n fetchPoll: (deviceCode: string) => Promise<{ status: number; json: () => Promise<unknown> }>;\n /**\n * Sleep implementation — injected in tests to avoid real waits.\n * Defaults to real setTimeout.\n */\n sleepFn?: (ms: number) => Promise<void>;\n /**\n * Override the base interval in milliseconds (used in tests — set to 0 to skip waits).\n * When provided, overrides `interval` from the server. Bump logic still applies if sleepFn is also provided.\n */\n intervalOverrideMs?: number;\n /**\n * Grace period beyond expiresIn before declaring timeout.\n * Default: 10_000ms (10 seconds for round-trip latency).\n */\n deadlineGraceMs?: number;\n}\n\nexport interface StartDeviceFlowResult {\n /** GitHub access_token returned by device-flow polling. */\n accessToken: string;\n}\n\nexport interface PromptEmailOTPOptions {\n /** Masked email shown to user, e.g. \"a***@example.com\". */\n sentTo: string;\n /** Pre-seeded code (CI / --code flag). If provided, skips prompt entirely. */\n code?: string;\n /** Number of in-process retries. Default: 3. */\n maxRetries?: number;\n /**\n * Function that performs one POST /complete attempt with the given OTP code.\n * Returns an object indicating success or a known error code.\n */\n attemptComplete: (code: string) => Promise<{ success: boolean; errorCode?: string }>;\n /** Stdin stream (test seam — default: process.stdin). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function (test seam). */\n stdout?: (s: string) => void;\n /** Stderr function (test seam). */\n stderr?: (s: string) => void;\n}\n\nexport interface MapProviderErrorResult {\n message: string;\n exitCode: number;\n retryable: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// pickProvider\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolves which provider to use for a join flow.\n *\n * Priority: flag > interactive picker > error (non-TTY)\n * Throws IdentityFlowError('provider_required') when non-interactive and no flag.\n * Throws IdentityFlowError('provider_unknown') when flag names an unrecognised provider.\n */\nexport async function pickProvider(opts: PickProviderOptions): Promise<Provider> {\n const available: Provider[] = opts.available ?? ['github', 'email'];\n\n if (opts.flag !== undefined) {\n const flagLower = opts.flag.toLowerCase() as Provider;\n if (!available.includes(flagLower)) {\n throw new IdentityFlowError(\n 'provider_unknown',\n `cleargate: unknown provider '${opts.flag}'. Available: ${available.join(', ')}`,\n );\n }\n return flagLower;\n }\n\n // No flag — need TTY for interactive picker\n if (!opts.isTTY) {\n throw new IdentityFlowError(\n 'provider_required',\n 'cleargate: --auth required in non-interactive mode',\n );\n }\n\n // Auto-select if only one provider\n if (available.length === 1) {\n return available[0]!;\n }\n\n return promptPicker(available, opts);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptPicker\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PROVIDER_LABELS: Record<Provider, string> = {\n github: 'GitHub OAuth',\n email: 'Email magic-link',\n};\n\n/**\n * Interactive numbered-list picker using node:readline.\n * Renders \"How would you like to verify your email?\" + numbered list.\n */\nexport async function promptPicker(\n options: Provider[],\n { stdin, stdout }: { stdin?: NodeJS.ReadableStream; stdout?: (s: string) => void } = {},\n): Promise<Provider> {\n const write = stdout ?? ((s: string) => process.stdout.write(s));\n\n write('How would you like to verify your email?\\n');\n options.forEach((p, i) => {\n write(` ${i + 1}. ${PROVIDER_LABELS[p]}\\n`);\n });\n write(`Choice [1-${options.length}]: `);\n\n const inputStream = (stdin as Readable | undefined) ?? process.stdin;\n\n return new Promise<Provider>((resolve, reject) => {\n let settled = false;\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n rl.once('line', (line) => {\n settled = true;\n rl.close();\n const idx = parseInt(line.trim(), 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n reject(\n new IdentityFlowError(\n 'invalid_choice',\n `cleargate: invalid choice '${line.trim()}'. Enter a number between 1 and ${options.length}.`,\n ),\n );\n return;\n }\n resolve(options[idx]!);\n });\n\n rl.once('error', (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n rl.once('close', () => {\n // Only reject if we haven't already resolved/rejected from the line event\n if (!settled) {\n settled = true;\n reject(new IdentityFlowError('provider_required', 'cleargate: no provider selected'));\n }\n });\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// startDeviceFlow\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Polls fetchPoll until the device is authorized or an error terminal state is reached.\n *\n * Used for both admin-login (polls /admin-api/v1/auth/device/poll) and\n * member-join GitHub flow (polls https://github.com/login/oauth/access_token).\n *\n * Returns { accessToken } on success.\n * Throws DeviceFlowError on terminal failure.\n */\nexport async function startDeviceFlow(opts: StartDeviceFlowOptions): Promise<StartDeviceFlowResult> {\n const sleepFn = opts.sleepFn ?? defaultSleep;\n let currentIntervalMs =\n opts.intervalOverrideMs !== undefined\n ? opts.intervalOverrideMs\n : Math.max(opts.interval, 5) * 1000;\n\n const expiresAtMs = Date.now() + opts.expiresIn * 1000;\n const deadline = expiresAtMs + (opts.deadlineGraceMs ?? 10_000);\n\n while (Date.now() < deadline) {\n await sleepFn(currentIntervalMs);\n\n let pollRes: { status: number; json: () => Promise<unknown> };\n try {\n pollRes = await opts.fetchPoll(opts.deviceCode);\n } catch {\n throw new DeviceFlowError('unreachable');\n }\n\n if (pollRes.status === 403) {\n let body: Record<string, unknown> = {};\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n // ignore parse failure\n }\n if (body['error'] === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n // not_admin\n throw new DeviceFlowError('not_admin');\n }\n\n if (pollRes.status === 410) {\n throw new DeviceFlowError('expired_token');\n }\n\n if (!pollRes.status || pollRes.status < 200 || pollRes.status >= 300) {\n if (pollRes.status >= 500 || pollRes.status < 100) {\n throw new DeviceFlowError('server_error');\n }\n }\n\n let body: Record<string, unknown>;\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n throw new DeviceFlowError('server_error');\n }\n\n // Handle GitHub's device-flow pending responses\n const errorField = body['error'];\n if (typeof errorField === 'string') {\n if (errorField === 'authorization_pending') {\n // keep polling\n continue;\n }\n if (errorField === 'slow_down') {\n // bump interval if retry_after is larger\n const retryAfter = body['interval'];\n if (typeof retryAfter === 'number') {\n const bumped = retryAfter * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n } else {\n // default slow_down bump: +5s\n currentIntervalMs += 5_000;\n }\n continue;\n }\n if (errorField === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n if (errorField === 'expired_token') {\n throw new DeviceFlowError('expired_token');\n }\n // unknown error — treat as server error\n throw new DeviceFlowError('server_error');\n }\n\n // Handle MCP server's pending response shape { pending: true, retry_after? }\n if (body['pending'] === true) {\n const shouldApplyBump =\n opts.sleepFn !== undefined || opts.intervalOverrideMs === undefined;\n if (shouldApplyBump && typeof body['retry_after'] === 'number') {\n const bumped = (body['retry_after'] as number) * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n }\n continue;\n }\n\n // Success — extract access_token (GitHub device-flow: access_token field)\n if (typeof body['access_token'] === 'string') {\n return { accessToken: body['access_token'] as string };\n }\n\n // MCP server success shape (admin_token for admin-login)\n if (typeof body['admin_token'] === 'string') {\n return { accessToken: body['admin_token'] as string };\n }\n\n throw new DeviceFlowError('server_error');\n }\n\n throw new DeviceFlowError('timeout');\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptEmailOTP\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prompts user for a 6-digit OTP code and attempts /complete up to maxRetries (default: 3) times.\n *\n * OD-3 resolution: 3 in-process retries for interactive UX; same challenge_id is reused each time.\n * Server MagicLinkProvider treats each /complete as independent — 502 on first attempt\n * does NOT burn the challenge (verified mcp/src/routes/join.ts:244-255).\n *\n * If `code` is provided (CI / --code flag), it is used directly without any prompt or retry.\n *\n * Returns the final code string on success.\n * Throws IdentityFlowError('otp_max_retries') after maxRetries failures.\n * Throws IdentityFlowError('otp_expired') on 410 challenge_expired.\n */\nexport async function promptEmailOTP(opts: PromptEmailOTPOptions): Promise<string> {\n const write = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const writeErr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const maxRetries = opts.maxRetries ?? 3;\n\n write(`We sent a 6-digit code to ${opts.sentTo}.\\n`);\n\n // CI mode: use provided code directly (no prompt, no retry)\n if (opts.code !== undefined) {\n const result = await opts.attemptComplete(opts.code);\n if (result.success) {\n return opts.code;\n }\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n throw new IdentityFlowError(\n 'otp_failed',\n `cleargate: code didn't match. Re-run \\`cleargate join <url>\\` to try again`,\n );\n }\n\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n // Create a single readline interface that persists across all retry attempts.\n // Each readLineFromRl() call reads exactly one line from the interface.\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Collect queued lines from the rl interface\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n // Drain any pending waiters with empty string (stream ended)\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) {\n return Promise.resolve(lineQueue.shift()!);\n }\n if (rlClosed) {\n return Promise.resolve('');\n }\n return new Promise<string>((resolve) => {\n lineWaiters.push(resolve);\n });\n }\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n write('Enter code: ');\n const code = (await readNextLine()).trim();\n\n const result = await opts.attemptComplete(code);\n\n if (result.success) {\n return code;\n }\n\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n\n if (attempt < maxRetries) {\n writeErr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n throw new IdentityFlowError(\n 'otp_max_retries',\n `cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.`,\n );\n}\n\n// readLine is no longer used (replaced by shared rl in promptEmailOTP)\n// Kept as a dead-code stub for any future one-shot callers.\n\n// ─────────────────────────────────────────────────────────────────────────────\n// mapProviderError\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Maps server HTTP status + error code pairs into user-readable messages and exit codes.\n *\n * Error table per M3 §2:\n * 400 invalid_request → exit 7\n * 400 provider_not_allowed → exit 9\n * 400 provider_unknown → exit 9\n * 400 identity_proof_required → exit 11 (stale CLI upgrade hint)\n * 403 email_mismatch → exit 10\n * 404 not_found → exit 4\n * 410 invite_expired → exit 3\n * 410 invite_already_consumed → exit 3\n * 410 challenge_expired → exit 3\n * 429 → exit 8\n * 502 provider_error → exit 12\n * >=500 (other) → exit 6\n */\nexport function mapProviderError(\n httpStatus: number,\n errorCode: string,\n retryAfterSeconds?: number,\n): MapProviderErrorResult {\n if (httpStatus === 400) {\n switch (errorCode) {\n case 'provider_not_allowed':\n return {\n message:\n 'cleargate: this invite requires a different provider — re-run with `--auth <pinned>`',\n exitCode: 9,\n retryable: true,\n };\n case 'provider_unknown':\n return {\n message:\n 'cleargate: server does not have that provider registered — contact the project admin',\n exitCode: 9,\n retryable: false,\n };\n case 'identity_proof_required':\n return {\n message: 'cleargate: this CLI is out of date — please upgrade and retry (`npm i -g cleargate@latest`)',\n exitCode: 11,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invalid request to server (please file a bug)',\n exitCode: 7,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 403 && errorCode === 'email_mismatch') {\n return {\n message:\n 'cleargate: verified email does not match the invitee — ask your admin to re-issue the invite',\n exitCode: 10,\n retryable: false,\n };\n }\n\n if (httpStatus === 404) {\n return {\n message: 'cleargate: invite not found',\n exitCode: 4,\n retryable: false,\n };\n }\n\n if (httpStatus === 410) {\n switch (errorCode) {\n case 'invite_expired':\n return {\n message: 'cleargate: invite expired. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'invite_already_consumed':\n return {\n message: 'cleargate: invite already consumed. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'challenge_expired':\n return {\n message: 'cleargate: code expired. Re-run `cleargate join <url>` to start over',\n exitCode: 3,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invite no longer valid. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 429) {\n const retryHint = retryAfterSeconds !== undefined ? `${retryAfterSeconds}` : '900';\n return {\n message: `cleargate: too many requests. Retry after ${retryHint}s`,\n exitCode: 8,\n retryable: true,\n };\n }\n\n if (httpStatus === 502 && errorCode === 'provider_error') {\n return {\n message: \"cleargate: code didn't match. Try again, or restart with `cleargate join <url>`\",\n exitCode: 12,\n retryable: true,\n };\n }\n\n if (httpStatus >= 500) {\n return {\n message: `cleargate: server error ${httpStatus}`,\n exitCode: 6,\n retryable: false,\n };\n }\n\n return {\n message: `cleargate: unexpected error ${httpStatus} ${errorCode}`,\n exitCode: 7,\n retryable: false,\n };\n}\n","/**\n * stamp.ts — `cleargate stamp <file>` command handler\n *\n * Wraps M1 helpers: getCodebaseVersion + stampFrontmatter.\n * Do NOT hand-roll YAML or shell git — use the M1 helpers.\n *\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #commander #optional-key: conditionally assign `now`/`version` opts.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { getCodebaseVersion, type CodebaseVersion } from '../lib/codebase-version.js';\nimport { stampFrontmatter, type StampOptions } from '../lib/stamp-frontmatter.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\n\nexport interface StampCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: inject a fixed CodebaseVersion instead of calling getCodebaseVersion. */\n getVersion?: () => CodebaseVersion;\n}\n\n/**\n * Build a unified-diff-style preview of the frontmatter changes.\n * Prints context lines (unchanged) with a leading space,\n * removed lines with `-`, added lines with `+`.\n */\nfunction buildDiffPreview(\n filePath: string,\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const lines: string[] = [`--- ${filePath}`, `+++ ${filePath} (after stamp)`];\n\n // Use insertion order of keys — include both before and after\n const seenKeys = new Set<string>();\n for (const key of [...Object.keys(before), ...Object.keys(after)]) {\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n const bVal = before[key];\n const aVal = after[key];\n if (bVal === aVal) {\n lines.push(` ${key}: ${String(bVal)}`);\n } else {\n if (key in before) {\n lines.push(`-${key}: ${String(bVal)}`);\n }\n if (key in after) {\n lines.push(`+${key}: ${String(aVal)}`);\n }\n }\n }\n return lines.join('\\n');\n}\n\nexport async function stampHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Verify file exists before calling helpers\n if (!fs.existsSync(absPath)) {\n process.stderr.write(`[cleargate stamp] error: file not found: ${absPath}\\n`);\n return exitFn(1);\n }\n\n const version = cli?.getVersion ? cli.getVersion() : getCodebaseVersion({ cwd });\n\n if (opts.dryRun) {\n // For dry-run: operate on a temp file copy so the real file is never written.\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-stamp-dry-'));\n try {\n const tmpFile = path.join(tmpDir, path.basename(absPath));\n fs.copyFileSync(absPath, tmpFile);\n\n // Read current frontmatter for the diff's before-side\n let before: Record<string, unknown> = {};\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n if (raw.trimStart().startsWith('---')) {\n ({ fm: before } = parseFrontmatter(raw));\n }\n } catch {\n // before stays empty if parse fails\n }\n\n // Build stamp options — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(tmpFile, stampOpts);\n\n const diff = buildDiffPreview(file, before, result.frontmatterAfter);\n stdoutFn(diff);\n if (result.reason === 'noop-archive' || result.reason === 'noop-unchanged') {\n stdoutFn(`[dry-run] no changes (${result.reason})`);\n }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n return;\n }\n\n // Real stamp — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(absPath, stampOpts);\n stdoutFn(`[stamped] ${file} (${result.reason})`);\n}\n","import { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface CodebaseVersion {\n sha: string | null;\n dirty: boolean;\n tag: string | null;\n package_version: string | null;\n version_string: string; // e.g. \"a3f2e91\", \"a3f2e91-dirty\", \"1.4.2\", \"unknown\"\n}\n\nexport type ExecFn = (cmd: string, args: string[]) => { stdout: string; code: number };\n\nexport interface CodebaseVersionOpts {\n cwd?: string;\n exec?: ExecFn;\n}\n\nfunction makeDefaultExec(cwd: string): ExecFn {\n return (cmd: string, args: string[]): { stdout: string; code: number } => {\n try {\n const stdout = execSync([cmd, ...args].join(' '), {\n cwd,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return { stdout: stdout.trim(), code: 0 };\n } catch (err: unknown) {\n const e = err as { stdout?: string | Buffer; status?: number };\n return {\n stdout: typeof e.stdout === 'string' ? e.stdout.trim() : '',\n code: typeof e.status === 'number' ? e.status : 1,\n };\n }\n };\n}\n\nfunction findPackageJson(startDir: string): string | null {\n let current = startDir;\n while (true) {\n const candidate = path.join(current, 'package.json');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n return null;\n }\n current = parent;\n }\n}\n\nexport function getCodebaseVersion(opts?: CodebaseVersionOpts): CodebaseVersion {\n const cwd = opts?.cwd ?? process.cwd();\n const execFn = opts?.exec ?? makeDefaultExec(cwd);\n\n // Try git SHA\n const shaResult = execFn('git', ['rev-parse', '--short', 'HEAD']);\n if (shaResult.code === 0 && shaResult.stdout.length > 0) {\n const sha = shaResult.stdout.trim();\n\n // Check dirty\n const statusResult = execFn('git', ['status', '--porcelain']);\n const dirty = statusResult.code === 0 && statusResult.stdout.trim().length > 0;\n\n const version_string = dirty ? `${sha}-dirty` : sha;\n\n return {\n sha,\n dirty,\n tag: null,\n package_version: null,\n version_string,\n };\n }\n\n // Fallback: find nearest package.json\n const pkgPath = findPackageJson(cwd);\n if (pkgPath !== null) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: string };\n const package_version = typeof pkg.version === 'string' ? pkg.version : null;\n if (package_version !== null) {\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version,\n version_string: package_version,\n };\n }\n } catch {\n // fall through to unknown\n }\n }\n\n // Unknown\n console.warn('[cleargate] codebase-version: could not determine version (no git, no package.json)');\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version: null,\n version_string: 'unknown',\n };\n}\n","import * as fs from 'fs/promises';\nimport type { CodebaseVersion } from './codebase-version.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface StampOptions {\n now?: () => Date;\n version?: CodebaseVersion;\n /** Default: /\\/\\.cleargate\\/delivery\\/archive\\// */\n archivePathMatcher?: (absPath: string) => boolean;\n}\n\nexport interface StampResult {\n changed: boolean;\n frontmatterBefore: Record<string, unknown>;\n frontmatterAfter: Record<string, unknown>;\n reason: 'created' | 'updated' | 'noop-archive' | 'noop-unchanged';\n}\n\n/** Write-template marker keys (epic/story/bug/CR/proposal). */\nconst WRITE_TEMPLATE_KEYS = new Set([\n 'story_id',\n 'epic_id',\n 'proposal_id',\n 'cr_id',\n 'bug_id',\n]);\n\nconst DEFAULT_ARCHIVE_MATCHER = (absPath: string): boolean =>\n /\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath);\n\nexport async function stampFrontmatter(absPath: string, opts?: StampOptions): Promise<StampResult> {\n const isArchive = (opts?.archivePathMatcher ?? DEFAULT_ARCHIVE_MATCHER)(absPath);\n if (isArchive) {\n // Read to get frontmatter for result shape, but do not write\n const raw = await fs.readFile(absPath, 'utf8');\n let fm: Record<string, unknown> = {};\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n // If parse fails, return empty frontmatter snapshot\n }\n return {\n changed: false,\n frontmatterBefore: fm,\n frontmatterAfter: fm,\n reason: 'noop-archive',\n };\n }\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n // Determine if the file has frontmatter at all\n const hasFrontmatter = raw.trimStart().startsWith('---');\n\n let fm: Record<string, unknown> = {};\n let body = raw;\n\n if (hasFrontmatter) {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n }\n\n const frontmatterBefore = { ...fm };\n\n const nowFn = opts?.now ?? (() => new Date());\n const now = nowFn();\n const nowIso = toIsoSecond(now);\n\n const version = opts?.version ?? { sha: null, dirty: false, tag: null, package_version: null, version_string: 'unknown' };\n const versionString = version.version_string;\n\n // Determine if this is a first stamp or re-stamp\n const hasCreatedAt = 'created_at' in fm && fm['created_at'] !== undefined && fm['created_at'] !== '' && fm['created_at'] !== null;\n\n // Determine if it's a write-template (needs server_pushed_at_version)\n const isWriteTemplate = WRITE_TEMPLATE_KEYS.has(Object.keys(fm).find((k) => WRITE_TEMPLATE_KEYS.has(k)) ?? '');\n\n // Build the new frontmatter:\n // 1. Preserve existing key order\n // 2. Append new keys in canonical order: created_at, updated_at, created_at_version, updated_at_version, server_pushed_at_version\n const newFm: Record<string, unknown> = {};\n\n // Copy all existing keys first (preserves order)\n for (const [k, v] of Object.entries(fm)) {\n newFm[k] = v;\n }\n\n if (!hasCreatedAt) {\n // First stamp: set all 4 fields\n // If the keys exist already (placeholder values), update them in-place order\n // If not, append at the end in canonical order\n newFm['created_at'] = nowIso;\n newFm['updated_at'] = nowIso;\n newFm['created_at_version'] = versionString;\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n } else {\n // Re-stamp: preserve created_at + created_at_version, advance updated_at + updated_at_version\n // created_at stays\n newFm['updated_at'] = nowIso;\n // created_at_version stays\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n }\n\n // Check noop-unchanged: if nothing changed, return early\n const unchanged =\n newFm['updated_at'] === fm['updated_at'] &&\n newFm['updated_at_version'] === fm['updated_at_version'] &&\n newFm['created_at'] === fm['created_at'] &&\n newFm['created_at_version'] === fm['created_at_version'];\n\n if (unchanged && hasCreatedAt) {\n return {\n changed: false,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: 'noop-unchanged',\n };\n }\n\n // Serialize and write\n const fmBlock = serializeFrontmatter(newFm);\n // Reconstruct: frontmatter block + newline + body\n // body from parseFrontmatter does NOT have a leading blank line (it strips one)\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n\n return {\n changed: true,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: hasCreatedAt ? 'updated' : 'created',\n };\n}\n","/**\n * YAML frontmatter parser backed by js-yaml with CORE_SCHEMA (YAML 1.2 core).\n *\n * Parses `---\\n<yaml>\\n---\\n<body>` into a typed frontmatter map + body string.\n * Preserves native types (null, boolean, number, string), nested maps, and\n * arrays. Uses CORE_SCHEMA so ISO-8601 timestamp strings are NOT coerced to\n * Date objects (YAML 1.1's quirk).\n *\n * Historical note: an earlier hand-rolled parser flattened indented nested\n * maps into top-level keys and stringified null/boolean scalars. See\n * BUG-001 and FLASHCARD entry `#yaml #frontmatter`.\n */\n\nimport yaml from 'js-yaml';\n\nexport function parseFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') {\n throw new Error('parseFrontmatter: input does not start with ---');\n }\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) {\n throw new Error('parseFrontmatter: missing closing ---');\n }\n\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n // strip one leading blank line if present\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n\n if (yamlText.trim() === '') {\n return { fm: {}, body };\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`parseFrontmatter: invalid YAML: ${(err as Error).message}`);\n }\n\n if (parsed === null || parsed === undefined) {\n return { fm: {}, body };\n }\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('parseFrontmatter: frontmatter is not a YAML mapping');\n }\n\n return { fm: parsed as Record<string, unknown>, body };\n}\n","/**\n * Frontmatter YAML serializer backed by js-yaml with CORE_SCHEMA.\n *\n * Emits a `---\\n<yaml>\\n---` block. Preserves key insertion order (JS\n * objects iterate non-numeric keys in insertion order by spec, and js-yaml\n * follows Object.keys), nested maps, arrays, and native scalar types.\n *\n * Partner of parse-frontmatter.ts — the two round-trip losslessly.\n */\n\nimport yaml from 'js-yaml';\n\n/**\n * Serialize a frontmatter record to a YAML block (including the --- delimiters).\n * Key order is preserved as supplied.\n */\nexport function serializeFrontmatter(fm: Record<string, unknown>): string {\n // Empty object → emit a two-delimiter block with no body\n if (Object.keys(fm).length === 0) {\n return '---\\n---';\n }\n\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n\n // js-yaml always ends with \\n; trim so we control the framing\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---`;\n}\n\n/**\n * Format a Date as ISO 8601 UTC with second precision: \"YYYY-MM-DDTHH:MM:SSZ\"\n */\nexport function toIsoSecond(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n","/**\n * init.ts — `cleargate init` command handler\n *\n * Steps:\n * 1. Validate cwd exists and is writable\n * 2. Resolve payloadDir (bundled cleargate-planning/ templates)\n * 3. copyPayload: copy scaffold files to target cwd\n * 4. mergeSettings: merge PostToolUse hook into .claude/settings.json\n * 5. injectClaudeMd: bounded-block inject into CLAUDE.md\n * 6. Bootstrap pass: if delivery/ has items, run wiki build\n * 7. Print Done\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawnSync } from 'node:child_process';\nimport { copyPayload } from '../init/copy-payload.js';\nimport { mergeSettings, type SettingsJson } from '../init/merge-settings.js';\nimport { injectClaudeMd, extractBlock } from '../init/inject-claude-md.js';\nimport { wikiBuildHandler, type WikiBuildOptions } from './wiki-build.js';\nimport { loadPackageManifest, type ManifestFile } from '../lib/manifest.js';\nimport { promptYesNo as defaultPromptYesNo, promptEmail as defaultPromptEmail } from '../lib/prompts.js';\nimport { resolveIdentity, readParticipant, writeParticipant, type ResolveIdentityOpts } from '../lib/identity.js';\n\n/**\n * The PostToolUse hook config to merge — updated in STORY-008-06 to use\n * stamp-and-gate.sh (replaces legacy SPRINT-04 inline wiki ingest command).\n * Uses ${CLAUDE_PROJECT_DIR} so the path is project-relative at runtime.\n * mergeSettings deduplicates by exact command string — safe to re-run.\n */\nconst HOOK_ADDITION: SettingsJson = {\n hooks: {\n PreToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-edit-gate.sh',\n },\n ],\n },\n ],\n PostToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/stamp-and-gate.sh',\n },\n ],\n },\n ],\n },\n};\n\nexport interface InitOptions {\n /** Target working directory (the repo being initialised). Default: process.cwd() */\n cwd?: string;\n /** Overwrite files that differ from payload. Default: false */\n force?: boolean;\n /** Accept all defaults non-interactively (same as stdin not a TTY). */\n yes?: boolean;\n /** Test seam: path to bundled cleargate-planning/ payload directory */\n payloadDir?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: runs wiki build (default: wikiBuildHandler) */\n runWikiBuild?: (opts: WikiBuildOptions) => Promise<void>;\n /**\n * Test seam: replaces the real Y/n prompt for restore flow.\n * STORY-009-03: injectable so integration tests don't block on stdin.\n */\n promptYesNo?: (question: string, defaultYes: boolean) => Promise<boolean>;\n /**\n * Test seam: replaces loadPackageManifest() call for snapshot step.\n * STORY-009-03: allows tests to supply a known ManifestFile without a real MANIFEST.json.\n */\n readInstallManifest?: () => ManifestFile;\n /**\n * Test seam: replaces the email prompt for participant identity flow.\n * STORY-010-01: injectable so tests don't block on stdin.\n */\n promptEmail?: (question: string, defaultValue: string) => Promise<string>;\n /**\n * Test seam: identity resolver options (gitEmail, hostname, username, env overrides).\n * STORY-010-01: used to inject a deterministic git email in tests.\n */\n identityOpts?: ResolveIdentityOpts;\n /**\n * Test seam: override process.stdin.isTTY for participant prompt decision.\n * STORY-010-01: in test environment stdin is not a TTY; inject true to force interactive path.\n */\n stdinIsTTY?: boolean;\n /**\n * CR-009: pin version to stamp into hook scripts. Overrides the default of\n * reading the version from `cleargate-cli/package.json`. Supports\n * `cleargate init --pin 0.6.0-beta` and test seam injection.\n */\n pin?: string;\n /**\n * Test seam: replaces child_process.spawnSync for the resolver probe (Step 7.6).\n * Injected in tests to avoid real npx invocations.\n */\n spawnSyncFn?: typeof spawnSync;\n}\n\n/** Shape of the .cleargate/.uninstalled marker written by STORY-009-07 `uninstall`. */\ninterface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n}\n\n/** Resolve default payloadDir from the installed package structure.\n *\n * tsup bundles all modules into dist/cli.js (single entry point).\n * As a result, import.meta.url inside ANY module resolves to dist/cli.js.\n * So dirname(import.meta.url) = dist/. One level up = package root.\n * See flashcard: #tsup #bundle #import-meta.\n */\nexport function resolveDefaultPayloadDir(): string {\n const thisFile = fileURLToPath(import.meta.url);\n // dist/cli.js → dirname = dist/ → one level up = package root\n const pkgRoot = path.resolve(path.dirname(thisFile), '..');\n return path.join(pkgRoot, 'templates', 'cleargate-planning');\n}\n\n/** Glob delivery dir for .md files, excluding .gitkeep */\nfunction countDeliveryItems(cwd: string): number {\n const pendingSync = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(cwd, '.cleargate', 'delivery', 'archive');\n let count = 0;\n for (const dir of [pendingSync, archive]) {\n if (!fs.existsSync(dir)) continue;\n const entries = fs.readdirSync(dir);\n for (const f of entries) {\n if (f.endsWith('.md') && f !== '.gitkeep') count++;\n }\n }\n return count;\n}\n\n/** Write file atomically: write to tmp, then rename. */\nfunction writeAtomic(filePath: string, content: string): void {\n const tmpPath = filePath + '.tmp.' + Date.now();\n fs.writeFileSync(tmpPath, content, 'utf8');\n fs.renameSync(tmpPath, filePath);\n}\n\n/**\n * CR-009: Read the version from a package.json file at `packageJsonPath`.\n * Returns null when the file is absent or malformed.\n */\nfunction readPackageVersion(packageJsonPath: string): string | null {\n try {\n const raw = fs.readFileSync(packageJsonPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: unknown };\n if (typeof pkg.version === 'string' && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nexport async function initHandler(opts: InitOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const force = opts.force ?? false;\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const runWikiBuild = opts.runWikiBuild ?? wikiBuildHandler;\n const promptYesNoFn = opts.promptYesNo ?? defaultPromptYesNo;\n const promptEmailFn = opts.promptEmail ?? defaultPromptEmail;\n const spawnSyncFn = opts.spawnSyncFn ?? spawnSync;\n\n // Step 1: Validate cwd\n if (!fs.existsSync(cwd)) {\n stderr(`[cleargate init] ERROR: target directory does not exist: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n // Check writable by attempting to create a tmp file\n const testWritePath = path.join(cwd, `.cleargate-init-write-test-${Date.now()}`);\n try {\n fs.writeFileSync(testWritePath, '');\n fs.unlinkSync(testWritePath);\n } catch {\n stderr(`[cleargate init] ERROR: target directory is not writable: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n stdout(`[cleargate init] Target: ${cwd}\\n`);\n\n // Step 2: Resolve payloadDir\n const payloadDir = opts.payloadDir ?? resolveDefaultPayloadDir();\n\n if (!fs.existsSync(payloadDir)) {\n stderr(`[cleargate init] ERROR: payload directory not found: ${payloadDir}\\n`);\n stderr(`[cleargate init] Run \\`npm run prebuild\\` to copy the payload first.\\n`);\n exit(1);\n return;\n }\n\n // Step 3: Copy scaffold payload\n // Step 3.5 (pre-copy): Detect .uninstalled marker and prompt restore.\n // Must run before any writes so the user sees the restore prompt first.\n const uninstalledMarkerPath = path.join(cwd, '.cleargate', '.uninstalled');\n let uninstalledMarker: UninstalledMarker | null = null;\n let userChoseRestore = false;\n\n if (fs.existsSync(uninstalledMarkerPath)) {\n try {\n const raw = fs.readFileSync(uninstalledMarkerPath, 'utf8');\n uninstalledMarker = JSON.parse(raw) as UninstalledMarker;\n } catch {\n stderr(`[cleargate init] WARNING: .uninstalled marker is malformed; ignoring it.\\n`);\n }\n\n if (uninstalledMarker !== null) {\n const { uninstalled_at, prior_version, preserved } = uninstalledMarker;\n const question =\n `[cleargate init] Detected previous ClearGate install` +\n ` (uninstalled ${uninstalled_at}, prior version ${prior_version}).` +\n ` Restore preserved items? [Y/n]`;\n userChoseRestore = await promptYesNoFn(question, true);\n\n if (userChoseRestore) {\n // Blind copy: just verify each preserved file still exists on disk\n // (it was preserved as intended). Log status; never touch content.\n for (const preservedPath of preserved) {\n const absPreserved = path.isAbsolute(preservedPath)\n ? preservedPath\n : path.join(cwd, preservedPath);\n if (fs.existsSync(absPreserved)) {\n stdout(`[cleargate init] [preserved] ${preservedPath}\\n`);\n } else {\n stdout(`[cleargate init] [warn] preserved path missing on disk: ${preservedPath}\\n`);\n }\n }\n } else {\n stdout(\n `[cleargate init] discarding preservation; preserved files untouched on disk\\n`,\n );\n }\n // Marker removal happens AFTER bootstrap (Step 6) completes — tracked below.\n }\n }\n\n // CR-009: Resolve pin version for hook script substitution.\n // Priority: explicit --pin flag → package.json next to payloadDir → package.json next to dist → fallback 'latest'\n let pinVersion: string | undefined;\n if (opts.pin) {\n pinVersion = opts.pin;\n } else {\n // payloadDir is `.../templates/cleargate-planning`; package.json is at `.../package.json`\n const payloadParent = path.resolve(payloadDir, '..', '..');\n pinVersion =\n readPackageVersion(path.join(payloadParent, 'package.json')) ??\n readPackageVersion(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json')) ??\n 'latest';\n }\n\n const copyReport = copyPayload(payloadDir, cwd, { force, pinVersion });\n for (const action of copyReport.actions) {\n const verb =\n action.action === 'created'\n ? 'Created'\n : action.action === 'overwritten'\n ? 'Overwritten'\n : 'Skipped (exists)';\n stdout(`[cleargate init] ${verb} ${action.relPath}\\n`);\n }\n\n // Step 4: Merge PostToolUse hook into .claude/settings.json\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n let existingSettings: SettingsJson | null = null;\n if (fs.existsSync(settingsPath)) {\n try {\n existingSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as SettingsJson;\n } catch {\n stderr(`[cleargate init] WARNING: could not parse ${settingsPath}; treating as empty.\\n`);\n }\n }\n\n const mergedSettings = mergeSettings(existingSettings, HOOK_ADDITION);\n fs.mkdirSync(path.dirname(settingsPath), { recursive: true });\n writeAtomic(settingsPath, JSON.stringify(mergedSettings, null, 2) + '\\n');\n stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook\\n`);\n\n // Step 5: Inject bounded block into CLAUDE.md\n const claudeMdPath = path.join(cwd, 'CLAUDE.md');\n const claudeMdSrcPath = path.join(payloadDir, 'CLAUDE.md');\n\n let claudeMdBlock: string;\n try {\n const claudeMdSrc = fs.readFileSync(claudeMdSrcPath, 'utf8');\n claudeMdBlock = extractBlock(claudeMdSrc);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not read CLAUDE.md block from payload: ${String(e)}\\n`);\n claudeMdBlock = '<!-- CLEARGATE:START -->\\n<!-- CLEARGATE:END -->';\n }\n\n const existingClaudeMd = fs.existsSync(claudeMdPath)\n ? fs.readFileSync(claudeMdPath, 'utf8')\n : null;\n\n const newClaudeMd = injectClaudeMd(existingClaudeMd, claudeMdBlock);\n writeAtomic(claudeMdPath, newClaudeMd);\n\n if (existingClaudeMd === null) {\n stdout(`[cleargate init] Created CLAUDE.md (with bounded block)\\n`);\n } else if (existingClaudeMd !== newClaudeMd) {\n stdout(`[cleargate init] Updated CLAUDE.md (bounded block injected/replaced)\\n`);\n } else {\n stdout(`[cleargate init] CLAUDE.md unchanged (block already up to date)\\n`);\n }\n\n // Step 6: Bootstrap pass\n const itemCount = countDeliveryItems(cwd);\n if (itemCount > 0) {\n stdout(`[cleargate init] Bootstrap: running wiki build (${itemCount} items found)...\\n`);\n await runWikiBuild({ cwd, now });\n stdout(`[cleargate init] Bootstrap: ran wiki build (${itemCount} items ingested)\\n`);\n } else {\n stdout(`[cleargate init] Bootstrap: no items to ingest, skipping build\\n`);\n }\n\n // Step 7: Write install snapshot atomically to .cleargate/.install-manifest.json.\n // Must be the FINAL step after all scaffold files are written (blueprint §1.2).\n const cleargateDir = path.join(cwd, '.cleargate');\n fs.mkdirSync(cleargateDir, { recursive: true });\n\n const snapshotPath = path.join(cleargateDir, '.install-manifest.json');\n try {\n const readManifest = opts.readInstallManifest ?? (() => loadPackageManifest({ packageRoot: payloadDir }));\n const pkgManifest = readManifest();\n const snapshot: ManifestFile = {\n ...pkgManifest,\n installed_at: now(),\n };\n writeAtomic(snapshotPath, JSON.stringify(snapshot, null, 2) + '\\n');\n stdout(`[cleargate init] Wrote install snapshot: .cleargate/.install-manifest.json\\n`);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not write install snapshot: ${String(e)}\\n`);\n }\n\n // Remove .uninstalled marker AFTER bootstrap + snapshot complete (whether user chose Y or N).\n // This prevents repeated prompting on subsequent init runs.\n if (uninstalledMarker !== null && fs.existsSync(uninstalledMarkerPath)) {\n try {\n fs.unlinkSync(uninstalledMarkerPath);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not remove .uninstalled marker: ${String(e)}\\n`);\n }\n }\n\n // Step 7.6 (CR-009): Resolver probe — run the three-branch resolver and print\n // a visible green/red status line. Converts \"invisible silent no-op at first\n // hook fire\" into \"loud failure at install time, when the user is watching\".\n {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n type ResolverBranch = { cmd: string; args: string[] } | null;\n\n // Mirror the bash resolver order: dist first (dogfood), then PATH, then npx.\n let branch: ResolverBranch = null;\n let branchLabel = '';\n\n if (fs.existsSync(distCliPath)) {\n branch = { cmd: 'node', args: [distCliPath, '--version'] };\n branchLabel = `local dist (${distCliPath})`;\n } else {\n // Try `cleargate --version` via PATH\n const whichResult = spawnSyncFn('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n branch = { cmd: 'cleargate', args: ['--version'] };\n branchLabel = 'PATH (global install)';\n } else {\n // Fall back to npx invocation\n branch = { cmd: 'npx', args: ['-y', `cleargate@${pinVersion}`, '--version'] };\n branchLabel = `npx cleargate@${pinVersion} (cold-start ~600ms first call)`;\n }\n }\n\n if (branch !== null) {\n const probeResult = spawnSyncFn(branch.cmd, branch.args, {\n encoding: 'utf8',\n timeout: 15000,\n });\n\n if (probeResult.status === 0) {\n stdout(`[cleargate init] \\u{1F7E2} cleargate CLI resolved via ${branchLabel}\\n`);\n } else {\n // Resolver chain exhausted — the hooks will no-op until cleargate is reachable.\n // Per BUG-015 (2026-04-27): convert from exit(1) to warn-not-block. The probe is a\n // best-effort signal; transient registry issues (CI race conditions, network blips)\n // shouldn't hard-fail init. Hooks will surface their own resolver-failure banners\n // at runtime if the issue persists. User can run `cleargate doctor` to investigate.\n stdout(\n `[cleargate init] \\u{1F7E1} cleargate CLI: not resolvable in this environment.\\n` +\n `[cleargate init] Attempted: ${branchLabel}\\n` +\n `[cleargate init] This is a warning, not a fatal error. Hooks will no-op until resolved.\\n` +\n `[cleargate init] Fix: npm i -g cleargate@${pinVersion} or npx cleargate@${pinVersion} doctor\\n`,\n );\n // Continue init. The resolver-status was emitted; hooks will surface their own\n // failure banners at runtime if needed.\n }\n }\n }\n\n // Step 7.5: Participant identity\n // Skip if .cleargate/.participant.json already exists (idempotent re-init).\n const existingParticipant = readParticipant(cwd);\n if (existingParticipant === null) {\n // Resolve git email as default (no env / host fallback during init — init needs a concrete prompt).\n const identityOpts = opts.identityOpts ?? {};\n\n // Resolve just the git rung: call resolveIdentity with env={} to skip env rung,\n // then check the source.\n const gitOnlyIdentity = resolveIdentity(cwd, {\n ...identityOpts,\n env: {}, // force skip env rung\n });\n const gitEmail =\n gitOnlyIdentity.source === 'git' ? gitOnlyIdentity.email : null;\n\n const stdinIsTTY = opts.stdinIsTTY ?? process.stdin.isTTY ?? false;\n const isNonInteractive = opts.yes === true || !stdinIsTTY;\n\n if (isNonInteractive) {\n // Non-interactive: use git email; if unavailable use host fallback via resolveIdentity\n const finalEmail =\n gitEmail ??\n resolveIdentity(cwd, identityOpts).email;\n\n await writeParticipant(cwd, finalEmail, 'inferred', now);\n stdout(`[cleargate init] Participant identity: ${finalEmail} (inferred)\\n`);\n } else {\n // Interactive: prompt for email.\n // BUG-007: the prior prompt reused the `[cleargate init]` info-log prefix\n // and was followed by a newline, so it visually merged with preceding log\n // lines and the cursor sat on a blank line below — users walked away\n // believing install had finished. Fix:\n // 1. Drop the `[cleargate init]` prefix on the prompt itself so it\n // reads as an interactive question, not a status message.\n // 2. Print a blank separator line above so the eye catches the change.\n // 3. Reject GitHub `users.noreply.github.com` git emails as a default —\n // they're unrouteable identities; users who blindly press Enter end\n // up with a participant identity that can't receive invites.\n const isNoreply = gitEmail !== null && /@users\\.noreply\\.github\\.com$/i.test(gitEmail);\n const defaultEmail = (gitEmail !== null && !isNoreply) ? gitEmail : 'user@localhost';\n stdout('\\n');\n const question = `Participant email (press Enter for default) [${defaultEmail}]:`;\n const answer = await promptEmailFn(question, defaultEmail);\n await writeParticipant(cwd, answer, 'prompted', now);\n stdout(`[cleargate init] Participant identity: ${answer} (prompted)\\n`);\n }\n }\n\n // Step 8: Done\n stdout(\n `[cleargate init] Done. Read CLAUDE.md and .cleargate/knowledge/cleargate-protocol.md to learn the protocol.\\n`,\n );\n\n void now; // suppress unused warning if not used after this\n}\n","/**\n * copy-payload.ts — recursively copy cleargate-planning/ payload to target cwd.\n *\n * Handles overwrite policy:\n * - by default: skip files that already exist with identical content\n * - force=true: overwrite all\n *\n * Does NOT prompt interactively; skip-or-overwrite is determined by `force`.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface CopyReport {\n created: number;\n skipped: number;\n overwritten: number;\n /** Per-file action lines for verbose printing */\n actions: Array<{ action: 'created' | 'skipped' | 'overwritten'; relPath: string }>;\n}\n\nexport interface CopyPayloadOptions {\n force: boolean;\n /**\n * CR-009: When set, substitute `__CLEARGATE_VERSION__` placeholder in hook scripts\n * with this version string. Applies to `.claude/hooks/stamp-and-gate.sh` and\n * `.claude/hooks/session-start.sh`. Use a sed-friendly format: `0.5.0`.\n */\n pinVersion?: string;\n}\n\n/** Hook files that carry the `__CLEARGATE_VERSION__` placeholder (CR-009). */\nconst PIN_PLACEHOLDER = '__CLEARGATE_VERSION__';\nconst HOOK_FILES_WITH_PIN = new Set([\n '.claude/hooks/stamp-and-gate.sh',\n '.claude/hooks/session-start.sh',\n]);\n\n/**\n * Files that copyPayload must NOT copy verbatim. The init pipeline owns these\n * via a downstream step that strips canonical-source preamble or otherwise\n * transforms content. Listing here ensures `listFilesRecursive` skips them.\n *\n * - CLAUDE.md (BUG-016): top-level CLAUDE.md in cleargate-planning/ carries a\n * meta-doc preamble describing the bounded-block contract. Step 5 of init\n * (`injectClaudeMd`) writes only `extractBlock(src)`. Copying the raw source\n * here would leave the preamble in the user repo because step 5 only\n * replaces the inner block region.\n */\nconst SKIP_FILES = new Set<string>(['CLAUDE.md']);\n\n/**\n * Recursively enumerate files under `dir`.\n * Returns paths relative to `dir`.\n */\nfunction listFilesRecursive(dir: string): string[] {\n const results: string[] = [];\n function walk(current: string, rel: string): void {\n const entries = fs.readdirSync(current, { withFileTypes: true });\n for (const entry of entries) {\n const entryRel = rel ? `${rel}/${entry.name}` : entry.name;\n const entryAbs = path.join(current, entry.name);\n if (entry.isDirectory()) {\n walk(entryAbs, entryRel);\n } else {\n results.push(entryRel);\n }\n }\n }\n walk(dir, '');\n return results;\n}\n\n/**\n * Copy all files from `payloadDir` to `targetCwd`, preserving directory structure.\n * Dotfiles and dot-directories (e.g. `.claude/`, `.cleargate/`) are preserved.\n */\nexport function copyPayload(\n payloadDir: string,\n targetCwd: string,\n opts: CopyPayloadOptions,\n): CopyReport {\n const report: CopyReport = { created: 0, skipped: 0, overwritten: 0, actions: [] };\n\n if (!fs.existsSync(payloadDir)) {\n throw new Error(`copyPayload: payloadDir does not exist: ${payloadDir}`);\n }\n\n const files = listFilesRecursive(payloadDir).filter((r) => !SKIP_FILES.has(r));\n\n for (const relPath of files) {\n const srcPath = path.join(payloadDir, relPath);\n const dstPath = path.join(targetCwd, relPath);\n\n // Ensure target directory exists\n fs.mkdirSync(path.dirname(dstPath), { recursive: true });\n\n let srcContent: Buffer | string = fs.readFileSync(srcPath);\n\n // CR-009: substitute __CLEARGATE_VERSION__ placeholder in hook scripts\n if (opts.pinVersion && HOOK_FILES_WITH_PIN.has(relPath)) {\n const text = srcContent.toString('utf8').replaceAll(PIN_PLACEHOLDER, opts.pinVersion);\n srcContent = text;\n }\n\n // Compare: convert srcContent to Buffer for comparison when it's a string\n const srcBuffer = typeof srcContent === 'string' ? Buffer.from(srcContent, 'utf8') : srcContent;\n\n if (fs.existsSync(dstPath)) {\n const dstContent = fs.readFileSync(dstPath);\n if (srcBuffer.equals(dstContent)) {\n // Identical — skip silently even with force (idempotent)\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n if (!opts.force) {\n // Different content, no force — skip\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n // Different + force — overwrite\n fs.writeFileSync(dstPath, srcBuffer);\n report.overwritten++;\n report.actions.push({ action: 'overwritten', relPath });\n } else {\n // New file\n fs.writeFileSync(dstPath, srcBuffer);\n report.created++;\n report.actions.push({ action: 'created', relPath });\n }\n }\n\n return report;\n}\n","/**\n * merge-settings.ts — JSON merge for .claude/settings.json\n *\n * Algorithm (from M4 blueprint):\n * - if existing is null: return addition\n * - otherwise deep-clone existing and merge addition.hooks into result.hooks\n * - For each event (e.g. PostToolUse):\n * - find matching entry by `matcher` field\n * - if absent: push entire new entry\n * - if present: de-dup merge inner hooks[] by exact `command` string match\n * - Preserves all other top-level keys (SubagentStop, permissions, etc.)\n */\n\nexport interface HookCommand {\n type: string;\n command: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n [key: string]: unknown;\n}\n\nexport interface HooksConfig {\n [event: string]: HookEntry[];\n}\n\nexport interface SettingsJson {\n hooks?: HooksConfig;\n [key: string]: unknown;\n}\n\n/**\n * Deep-clone a plain JSON-serializable object.\n */\nfunction deepClone<T>(obj: T): T {\n return JSON.parse(JSON.stringify(obj)) as T;\n}\n\n/**\n * Merge `addition` hook config into `existing` settings.\n * Returns a new merged object; does not mutate either argument.\n *\n * @param existing - parsed .claude/settings.json content, or null if file absent\n * @param addition - the hook config to merge in (must have `hooks` key)\n */\nexport function mergeSettings(\n existing: SettingsJson | null,\n addition: SettingsJson,\n): SettingsJson {\n if (existing === null) {\n return deepClone(addition);\n }\n\n const result: SettingsJson = deepClone(existing);\n\n // Ensure result.hooks exists\n if (!result.hooks) {\n result.hooks = {};\n }\n\n // Merge each event from addition\n for (const [eventName, eventArray] of Object.entries(addition.hooks ?? {})) {\n if (!result.hooks[eventName]) {\n result.hooks[eventName] = [];\n }\n\n for (const newEntry of eventArray) {\n const matchingIdx = result.hooks[eventName].findIndex(\n (e) => e.matcher === newEntry.matcher,\n );\n\n if (matchingIdx === -1) {\n // No matching entry — push entire new entry\n result.hooks[eventName].push(deepClone(newEntry));\n } else {\n // Matcher exists — merge inner hooks[] by de-dup on `command`\n const existingEntry = result.hooks[eventName][matchingIdx];\n const existingInner: HookCommand[] = Array.isArray(existingEntry.hooks)\n ? (existingEntry.hooks as HookCommand[])\n : [];\n\n for (const newInner of newEntry.hooks ?? []) {\n if (!existingInner.some((h) => h.command === newInner.command)) {\n existingInner.push(deepClone(newInner) as HookCommand);\n }\n }\n\n result.hooks[eventName][matchingIdx] = {\n ...existingEntry,\n hooks: existingInner,\n };\n }\n }\n }\n\n return result;\n}\n","/**\n * inject-claude-md.ts — bounded-block injection for CLAUDE.md\n *\n * Block format: <!-- CLEARGATE:START -->\\n<content>\\n<!-- CLEARGATE:END -->\n * Detection regex: /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/ (greedy, see below)\n *\n * Rules:\n * - If existing === null: create file with block as full content (+ trailing newline)\n * - If existing matches: replace the bounded block in place (idempotent)\n * - If existing no match: append block with 2 leading newlines (preserve user content above)\n */\n\n// Greedy match: from first <!-- CLEARGATE:START --> to LAST <!-- CLEARGATE:END -->.\n// Greedy is correct here because:\n// (a) cleargate-planning/CLAUDE.md body text mentions both markers inline as code references,\n// and non-greedy would stop at the inline END before the real one.\n// (b) We assume at most one cleargate block per file (idempotency requires it).\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/;\n\n/**\n * Extract the bounded block from a source file (e.g. cleargate-planning/CLAUDE.md).\n * Returns the text from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive.\n * Throws if the markers are not found.\n */\nexport function extractBlock(sourceContent: string): string {\n const match = BLOCK_REGEX.exec(sourceContent);\n if (!match) {\n throw new Error('inject-claude-md: CLEARGATE:START/END markers not found in source content');\n }\n return match[0];\n}\n\n/**\n * Inject or update the bounded block in an existing CLAUDE.md.\n *\n * @param existing - current content of CLAUDE.md, or null if file doesn't exist\n * @param block - the full block to inject, from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive\n * @returns - new file content (ready to write)\n */\nexport function injectClaudeMd(existing: string | null, block: string): string {\n if (existing === null) {\n // Create new file with block as full content\n return block + '\\n';\n }\n\n if (BLOCK_REGEX.test(existing)) {\n // Replace existing block in place\n return existing.replace(BLOCK_REGEX, block);\n }\n\n // Append block with 2 leading newlines\n return existing.trimEnd() + '\\n\\n' + block + '\\n';\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\n\nexport interface WikiBuildOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp for last_ingest field (defaults to new Date().toISOString()) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\n/** Terminal statuses — items with these statuses go to the Archive section. */\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n/**\n * Rollup threshold: if an active epic has >= ROLLUP_THRESHOLD active stories,\n * collapse them into a single summary line instead of individual rows.\n */\nconst ROLLUP_THRESHOLD = 3;\n\n/**\n * Active index bucket order: epics → sprints → proposals → crs → bugs → orphan stories.\n * Topics always skipped (written by query --persist only).\n */\nconst ACTIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\nexport async function wikiBuildHandler(opts: WikiBuildOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const templateDir = opts.templateDir;\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`wiki build: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n // Ensure wiki directory structure exists\n for (const bucket of BUCKET_ORDER) {\n fs.mkdirSync(path.join(wikiRoot, bucket), { recursive: true });\n }\n\n // Step 2: scan raw items\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Step 3: write per-item wiki pages\n const timestamp = now();\n let pagesWritten = 0;\n\n for (const item of items) {\n const sha = getGitSha(item.rawPath, gitRunner) ?? '';\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n\n const wikiPage: WikiPage = {\n type: item.type,\n id: item.id,\n parent,\n children,\n status: String(item.fm['status'] ?? ''),\n remote_id: String(item.fm['remote_id'] ?? ''),\n raw_path: item.rawPath,\n last_ingest: timestamp,\n last_ingest_commit: sha,\n repo: item.repo,\n };\n\n const body = buildPageBody(item, wikiPage);\n const content = serializePage(wikiPage, body);\n\n const pageDir = path.join(wikiRoot, item.bucket);\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(path.join(pageDir, `${item.id}.md`), content, 'utf8');\n pagesWritten++;\n }\n\n // Step 4: build index.md\n const indexContent = buildIndex(items);\n fs.writeFileSync(path.join(wikiRoot, 'index.md'), indexContent, 'utf8');\n\n // Step 5: build log.md\n const logContent = buildLog(items, timestamp);\n fs.writeFileSync(path.join(wikiRoot, 'log.md'), logContent, 'utf8');\n\n // Step 6: write synthesis pages\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n\n // Step 7: report\n stdout(`wiki build: OK (${pagesWritten} pages written)\\n`);\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: RawItem, page: WikiPage): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.').slice(0, 200);\n\n const blastParts: string[] = [];\n if (page.parent) blastParts.push(page.parent);\n for (const child of page.children) blastParts.push(child);\n\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction buildIndex(items: RawItem[]): string {\n const header = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n ];\n\n // Empty-delivery case\n if (items.length === 0) {\n return [\n ...header,\n '## Active',\n '',\n '_No active items._',\n '',\n '## Archive',\n '',\n '_No archived items._',\n '',\n ].join('\\n');\n }\n\n // 1. Partition items into active and archived\n const active: RawItem[] = [];\n const archived: RawItem[] = [];\n for (const item of items) {\n const status = String(item.fm['status'] ?? '');\n if (TERMINAL_STATUSES.has(status)) {\n archived.push(item);\n } else {\n active.push(item);\n }\n }\n\n // 2. Build storiesByEpic: active stories grouped by parent epic id.\n // Only include stories whose parent epic is itself active.\n const activeEpicIds = new Set(\n active.filter((i) => i.bucket === 'epics').map((i) => i.id),\n );\n const storiesByEpic = new Map<string, RawItem[]>();\n\n for (const item of active) {\n if (item.bucket !== 'stories') continue;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n // Strip [[ ]] wrappers if present\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n if (epicId && activeEpicIds.has(epicId)) {\n const list = storiesByEpic.get(epicId) ?? [];\n list.push(item);\n storiesByEpic.set(epicId, list);\n }\n }\n\n // Orphan active stories: no parent_epic_ref OR parent epic not active\n const orphanStories = active.filter((item) => {\n if (item.bucket !== 'stories') return false;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n return !epicId || !activeEpicIds.has(epicId);\n });\n\n // 3. Emit ## Active section\n const activeLines: string[] = ['## Active', ''];\n\n for (const bucket of ACTIVE_BUCKET_ORDER) {\n if (bucket === 'stories') {\n // Orphan stories only\n const sorted = orphanStories.slice().sort((a, b) => a.id.localeCompare(b.id));\n for (const item of sorted) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n } else if (bucket === 'epics') {\n const epicItems = active\n .filter((i) => i.bucket === 'epics')\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const epic of epicItems) {\n const status = String(epic.fm['status'] ?? '');\n activeLines.push(`- [[${epic.id}]] (${epic.type}) — ${status}`);\n\n // 4. Emit story rollup or individual rows under the epic\n const epicStories = (storiesByEpic.get(epic.id) ?? [])\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n if (epicStories.length >= ROLLUP_THRESHOLD) {\n // Build status breakdown: count per status, sort by count desc then status asc\n const counts = new Map<string, number>();\n for (const s of epicStories) {\n const st = String(s.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n const breakdown = [...counts.entries()]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n // Extract epic numeric prefix for rollup label (e.g. EPIC-014 → 014)\n const epicNum = epic.id.replace(/^EPIC-/, '');\n activeLines.push(` - STORY-${epicNum}-xx (${epicStories.length} stories) — ${breakdown}`);\n } else {\n for (const story of epicStories) {\n const st = String(story.fm['status'] ?? '');\n activeLines.push(` - [[${story.id}]] (${story.type}) — ${st}`);\n }\n }\n }\n } else {\n const bucketItems = active\n .filter((i) => i.bucket === bucket)\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const item of bucketItems) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n }\n }\n\n activeLines.push('');\n\n // 5. Emit ## Archive section — per-bucket summary only\n const archiveLines: string[] = ['## Archive', ''];\n // Bucket order for archive summary (excluding topics and stories — stories\n // don't get their own archive summary line per plan)\n const ARCHIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\n for (const bucket of ARCHIVE_BUCKET_ORDER) {\n const bucketArchived = archived.filter((i) => i.bucket === bucket);\n if (bucketArchived.length === 0) continue;\n\n // Count per status\n const counts = new Map<string, number>();\n for (const item of bucketArchived) {\n const st = String(item.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n // Sort by count desc then status asc, omit zero-count statuses\n const parts = [...counts.entries()]\n .filter(([, n]) => n > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n\n const label = BUCKET_LABELS[bucket] ?? bucket;\n archiveLines.push(`- ${label}: ${parts} · [expand](archive/${bucket}.md)`);\n }\n\n archiveLines.push('');\n\n return [...header, ...activeLines, ...archiveLines].join('\\n');\n}\n\nfunction buildLog(items: RawItem[], timestamp: string): string {\n if (items.length === 0) {\n return '# Wiki Event Log\\n\\n';\n }\n\n const entries = items.map((item) =>\n [\n `- timestamp: \"${timestamp}\"`,\n ` actor: \"cleargate wiki build\"`,\n ` action: \"create\"`,\n ` target: \"${item.id}\"`,\n ` path: \"${item.rawPath}\"`,\n ].join('\\n'),\n );\n\n return ['# Wiki Event Log', '', ...entries, ''].join('\\n');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { deriveBucket } from './derive-bucket.js';\nimport { deriveRepo } from './derive-repo.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface RawItem {\n /** Absolute path on disk */\n absPath: string;\n /** Path relative to repo root */\n rawPath: string;\n id: string;\n bucket: string;\n type: WikiPageType;\n repo: RepoTag;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/**\n * Scan pending-sync/ + archive/ under deliveryRoot for markdown work items.\n * Returns a sorted (by id) list of parsed raw items.\n */\nexport function scanRawItems(deliveryRoot: string, repoRoot: string): RawItem[] {\n const results: RawItem[] = [];\n\n for (const subdir of ['pending-sync', 'archive']) {\n const dir = path.join(deliveryRoot, subdir);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { recursive: true, encoding: 'utf8' }) as string[];\n for (const rel of entries) {\n if (!rel.endsWith('.md')) continue;\n if (rel.includes('~') || rel.startsWith('.')) continue;\n\n const absPath = path.join(dir, rel);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n // Compute rawPath relative to repo root\n const rawPath = path.relative(repoRoot, absPath).replace(/\\\\/g, '/');\n\n // Check exclusions\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) continue;\n\n // Derive bucket info from filename\n const filename = path.basename(absPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch {\n // Not a recognized work-item filename — skip silently\n continue;\n }\n\n // Derive repo from path\n let repo: RepoTag;\n try {\n repo = deriveRepo(rawPath);\n } catch {\n continue;\n }\n\n // Parse frontmatter\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n // Malformed frontmatter — skip\n continue;\n }\n\n results.push({\n absPath,\n rawPath,\n id: bucketInfo.id,\n bucket: bucketInfo.bucket,\n type: bucketInfo.type,\n repo,\n fm,\n body,\n });\n }\n }\n\n // Sort deterministically by id (alphanumeric ascending)\n results.sort((a, b) => a.id.localeCompare(b.id));\n return results;\n}\n","import type { WikiPageType } from './page-schema.js';\n\nexport interface BucketInfo {\n type: WikiPageType;\n id: string;\n bucket: string;\n}\n\nconst PREFIX_MAP: Array<{ prefix: string; type: WikiPageType; bucket: string }> = [\n { prefix: 'EPIC-', type: 'epic', bucket: 'epics' },\n { prefix: 'STORY-', type: 'story', bucket: 'stories' },\n { prefix: 'SPRINT-', type: 'sprint', bucket: 'sprints' },\n { prefix: 'PROPOSAL-', type: 'proposal', bucket: 'proposals' },\n { prefix: 'CR-', type: 'cr', bucket: 'crs' },\n { prefix: 'BUG-', type: 'bug', bucket: 'bugs' },\n];\n\n/**\n * Derive bucket, type, and id from a filename stem.\n * Filename `STORY-042-01_name.md` → `{ type: 'story', id: 'STORY-042-01', bucket: 'stories' }`.\n */\nexport function deriveBucket(filename: string): BucketInfo {\n // Strip path if any\n const base = filename.includes('/') ? filename.split('/').pop()! : filename;\n // Remove .md suffix\n const stem = base.endsWith('.md') ? base.slice(0, -3) : base;\n // id = everything before the first `_`\n const underscoreIdx = stem.indexOf('_');\n const id = underscoreIdx === -1 ? stem : stem.slice(0, underscoreIdx);\n\n for (const { prefix, type, bucket } of PREFIX_MAP) {\n if (id.startsWith(prefix)) {\n return { type, id, bucket };\n }\n }\n\n throw new Error(`deriveBucket: cannot determine bucket for filename: ${filename}`);\n}\n","import type { RepoTag } from './page-schema.js';\n\n/**\n * A1 helper: derive the `repo` tag from a raw file path prefix.\n * Mapping is per §10.4 field notes.\n */\nexport function deriveRepo(rawPath: string): RepoTag {\n if (rawPath.startsWith('cleargate-cli/')) return 'cli';\n if (rawPath.startsWith('mcp/')) return 'mcp';\n if (rawPath.startsWith('.cleargate/') || rawPath.startsWith('cleargate-planning/')) return 'planning';\n throw new Error(`cannot derive repo for path: ${rawPath}`);\n}\n","import { spawnSync } from 'node:child_process';\n\nexport type GitRunner = (cmd: string, args: string[]) => string;\n\n/**\n * A2 helper: return the git SHA of the last commit touching rawPath.\n * Returns null when the file is untracked (empty stdout, exit 0).\n * Accepts an optional `runner` test seam.\n */\nexport function getGitSha(rawPath: string, runner?: GitRunner): string | null {\n const run = runner ?? defaultRunner;\n const out = run('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n return out.length > 0 ? out : null;\n}\n\nfunction defaultRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n return result.stdout ?? '';\n}\n","/**\n * §10.4 Wiki Page Schema — exactly nine frontmatter fields.\n * Lint will flag any extra or missing fields.\n */\n\nexport type WikiPageType = 'epic' | 'story' | 'sprint' | 'proposal' | 'cr' | 'bug' | 'topic';\nexport type RepoTag = 'cli' | 'mcp' | 'planning';\n\n/** The nine-field frontmatter shape every wiki page must satisfy. */\nexport interface WikiPage {\n type: WikiPageType;\n id: string;\n parent: string; // \"[[EPIC-042]]\" or \"\" if none\n children: string[]; // [\"[[STORY-042-01]]\", ...]\n status: string;\n remote_id: string;\n raw_path: string;\n last_ingest: string; // ISO 8601 UTC\n last_ingest_commit: string; // git SHA or \"\"\n repo: RepoTag;\n}\n\n/** Serialise a WikiPage frontmatter + body into a markdown string. */\nexport function serializePage(page: WikiPage, body: string): string {\n const childrenYaml =\n page.children.length === 0\n ? '[]'\n : '\\n' + page.children.map((c) => ` - \"${c}\"`).join('\\n');\n\n const fm = [\n '---',\n `type: ${page.type}`,\n `id: \"${page.id}\"`,\n `parent: \"${page.parent}\"`,\n `children: ${childrenYaml}`,\n `status: \"${page.status}\"`,\n `remote_id: \"${page.remote_id}\"`,\n `raw_path: \"${page.raw_path}\"`,\n `last_ingest: \"${page.last_ingest}\"`,\n `last_ingest_commit: \"${page.last_ingest_commit}\"`,\n `repo: \"${page.repo}\"`,\n '---',\n ].join('\\n');\n\n return `${fm}\\n\\n${body}`;\n}\n\n/** Parse a serialised wiki page back into a WikiPage. Throws on schema violations. */\nexport function parsePage(raw: string): WikiPage {\n const { fm } = parseFmRaw(raw);\n\n const type = fm['type'] as WikiPageType;\n const id = String(fm['id'] ?? '');\n const parent = String(fm['parent'] ?? '');\n const rawChildren = fm['children'];\n const children: string[] = Array.isArray(rawChildren)\n ? (rawChildren as unknown[]).map(String)\n : [];\n const status = String(fm['status'] ?? '');\n const remote_id = String(fm['remote_id'] ?? '');\n const raw_path = String(fm['raw_path'] ?? '');\n const last_ingest = String(fm['last_ingest'] ?? '');\n const last_ingest_commit = String(fm['last_ingest_commit'] ?? '');\n const repo = fm['repo'] as RepoTag;\n\n return { type, id, parent, children, status, remote_id, raw_path, last_ingest, last_ingest_commit, repo };\n}\n\nfunction parseFmRaw(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') throw new Error('parsePage: missing opening ---');\n let close = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { close = i; break; }\n }\n if (close === -1) throw new Error('parsePage: missing closing ---');\n const fmLines = lines.slice(1, close);\n const body = lines.slice(close + 1).join('\\n').replace(/^\\n/, '');\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '[]') { fm[key] = []; continue; }\n if (val === '') { fm[key] = []; continue; }\n // inline list check\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1);\n fm[key] = inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return { fm, body };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the active-sprint synthesis page.\n * Loads template from templates/synthesis/active-sprint.md.\n * Partitions sprints by activated_at / completed_at frontmatter values.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'active-sprint.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n\n // Partition sprints:\n // - active: activated_at is set (non-null, non-empty) AND completed_at is not set\n // - completed: completed_at is set\n // - planned: neither activated_at nor completed_at is set\n const active = sprints.filter((s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n const completed = sprints.filter((s) => isSet(s.fm['completed_at']));\n const planned = sprints.filter((s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n\n const data: Record<string, unknown> = {\n active: active.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_active: active.length === 0 ? [{}] : [],\n planned: planned.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_planned: planned.length === 0 ? [{}] : [],\n completed: completed.slice(0, 3).map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_completed: completed.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // tsup bundles all modules into dist/cli.js.\n // When running the built bundle: import.meta.url = file://.../cleargate-cli/dist/cli.js\n // __dirname = cleargate-cli/dist/\n // ../templates/synthesis = cleargate-cli/templates/synthesis ✓ (source)\n // AND dist/templates/synthesis is also available (copied by onSuccess) ✓\n //\n // When vitest runs source (test seam): templateDir is always passed explicitly,\n // so this default is only used for the built/production case.\n //\n // Strategy: go one level up from the file containing this code (works for both\n // the dist/ bundle and npm-published dist/ layout), then into templates/synthesis.\n // For dist/cli.js: one up = package root → templates/synthesis. ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * Tiny Mustache-lite template renderer.\n * Supports:\n * {{var}} — variable substitution (missing → empty string)\n * {{#section}}...{{/section}} — array iteration; inner {{field}} resolves\n * against the current array element\n *\n * Anything more complex throws an Error.\n * No external dependencies.\n */\n\n/** Render a template string with the given data context. */\nexport function renderTemplate(template: string, data: Record<string, unknown>): string {\n // Validate: no nested sections or unsupported tags\n // We only support {{var}}, {{#section}}...{{/section}}\n const tagRe = /\\{\\{([^}]+)\\}\\}/g;\n const matches = [...template.matchAll(tagRe)].map((m) => m[1].trim());\n for (const tag of matches) {\n if (tag.startsWith('^') || tag.startsWith('>') || tag.startsWith('!') || tag.startsWith('=')) {\n throw new Error(`renderTemplate: unsupported tag type: {{${tag}}}`);\n }\n }\n\n return renderSection(template, data);\n}\n\nfunction renderSection(template: string, ctx: Record<string, unknown>): string {\n // Process {{#section}}...{{/section}} blocks first\n const sectionRe = /\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g;\n\n let result = template.replace(sectionRe, (_match, key: string, inner: string) => {\n const val = ctx[key];\n if (!Array.isArray(val)) {\n // Non-array truthy: render inner once with same ctx; falsy: skip\n if (!val) return '';\n return renderSection(inner, ctx);\n }\n if (val.length === 0) return '';\n return val\n .map((item: unknown) => {\n const itemCtx =\n item !== null && typeof item === 'object'\n ? (item as Record<string, unknown>)\n : { '.': item };\n return renderSection(inner, itemCtx);\n })\n .join('');\n });\n\n // Then substitute remaining {{var}} tokens\n result = result.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key: string) => {\n const val = ctx[key];\n if (val === undefined || val === null) return '';\n return String(val);\n });\n\n return result;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the open-gates (blocked items) synthesis page.\n * Loads template from templates/synthesis/open-gates.md.\n *\n * Three gate buckets (matching real corpus textual statuses):\n * Gate 1 — proposals with approved: false OR status: \"Draft\" / \"Approved\" (not yet shipped)\n * Gate 2 — stories with ambiguity starting with 🟡 or 🔴\n * Gate 3 — any item with status: \"Ready\" AND remote_id empty / null\n *\n * NOTE: The previous implementation filtered on status.includes('🔴') which matched\n * zero items in the real corpus (actual statuses are textual: Draft, Ready, Planned,\n * Active, Completed, Approved). This is the corpus-shape fix for STORY-002-09.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'open-gates.md'), 'utf8');\n\n // Gate 1: proposals pending approval\n const gate1 = state.filter((i) => {\n if (i.bucket !== 'proposals') return false;\n const status = String(i.fm['status'] ?? '');\n const approved = i.fm['approved'];\n // Proposals that are Draft or explicitly not approved\n return status === 'Draft' || approved === false || approved === 'false';\n });\n\n // Gate 2: stories with elevated ambiguity (🟡 Medium or 🔴 High)\n const gate2 = state.filter((i) => {\n if (i.bucket !== 'stories') return false;\n const ambiguity = String(i.fm['ambiguity'] ?? '');\n return ambiguity.startsWith('🟡') || ambiguity.startsWith('🔴');\n });\n\n // Gate 3: items that are Ready but not yet pushed (remote_id empty or null)\n const gate3 = state.filter((i) => {\n const status = String(i.fm['status'] ?? '');\n if (status !== 'Ready') return false;\n const remoteId = i.fm['remote_id'];\n return remoteId === null || remoteId === undefined || String(remoteId).trim() === '';\n });\n\n const data: Record<string, unknown> = {\n gate1: gate1.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate1: gate1.length === 0 ? [{}] : [],\n gate2: gate2.map((i) => ({ id: i.id, ambiguity: String(i.fm['ambiguity'] ?? '') })),\n no_gate2: gate2.length === 0 ? [{}] : [],\n gate3: gate3.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate3: gate3.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the product-state synthesis page.\n * Loads template from templates/synthesis/product-state.md.\n *\n * Shipped = items in archive/ subdir (rawPath contains '/archive/')\n * Active = status is Active, In Progress, or 🟢-prefixed\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'product-state.md'), 'utf8');\n\n function countBucket(bucket: string) {\n return state.filter((i) => i.bucket === bucket);\n }\n\n function isShipped(item: RawItem) {\n return item.rawPath.includes('/archive/');\n }\n\n function isActive(item: RawItem) {\n const status = String(item.fm['status'] ?? '');\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n }\n\n const buckets = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs'];\n\n function countFor(bucket: string, predicate: (i: RawItem) => boolean): number {\n return countBucket(bucket).filter(predicate).length;\n }\n\n const epics = countBucket('epics');\n const activeEpicsList = epics.filter(isActive);\n const shippedItems = state.filter(isShipped);\n\n const data: Record<string, unknown> = {\n // Totals\n total_epics: epics.length,\n total_stories: countBucket('stories').length,\n total_sprints: countBucket('sprints').length,\n total_proposals: countBucket('proposals').length,\n total_crs: countBucket('crs').length,\n total_bugs: countBucket('bugs').length,\n\n // Active counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`active_${b}`, countFor(b, isActive)])),\n\n // Shipped counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`shipped_${b}`, countFor(b, isShipped)])),\n\n // Active epics list\n active_epics_list: activeEpicsList.map((i) => ({\n id: i.id,\n status: String(i.fm['status'] ?? ''),\n })),\n no_active_epics: activeEpicsList.length === 0 ? [{}] : [],\n\n // Shipped items list\n shipped_items: shippedItems.map((i) => ({\n id: i.id,\n bucket: i.bucket,\n status: String(i.fm['status'] ?? ''),\n })),\n no_shipped: shippedItems.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the roadmap synthesis page.\n * Loads template from templates/synthesis/roadmap.md.\n *\n * Sprint buckets (by activated_at / completed_at):\n * in-flight: activated_at set AND completed_at not set\n * planned: neither set\n * shipped: completed_at set\n *\n * Epic buckets (by status):\n * active: Active / In Progress / 🟢-prefixed\n * planned: Ready / Planned / Draft\n * shipped: Completed / Approved\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'roadmap.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n const epics = state.filter((i) => i.bucket === 'epics');\n\n // Sprint partitions\n const inFlightSprints = sprints.filter(\n (s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const plannedSprints = sprints.filter(\n (s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const shippedSprints = sprints.filter((s) => isSet(s.fm['completed_at']));\n\n // Epic partitions\n const activeEpics = epics.filter((e) => isActiveStatus(String(e.fm['status'] ?? '')));\n const plannedEpics = epics.filter((e) => isPlannedStatus(String(e.fm['status'] ?? '')));\n const shippedEpics = epics.filter((e) => isShippedStatus(String(e.fm['status'] ?? '')));\n\n const data: Record<string, unknown> = {\n in_flight_sprints: inFlightSprints.map((s) => ({\n id: s.id,\n activated_at: String(s.fm['activated_at'] ?? ''),\n })),\n no_in_flight_sprints: inFlightSprints.length === 0 ? [{}] : [],\n\n planned_sprints: plannedSprints.map((s) => ({\n id: s.id,\n status: String(s.fm['status'] ?? ''),\n })),\n no_planned_sprints: plannedSprints.length === 0 ? [{}] : [],\n\n shipped_sprints: shippedSprints.map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_shipped_sprints: shippedSprints.length === 0 ? [{}] : [],\n\n active_epics: activeEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_active_epics: activeEpics.length === 0 ? [{}] : [],\n\n planned_epics: plannedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_planned_epics: plannedEpics.length === 0 ? [{}] : [],\n\n shipped_epics: shippedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_shipped_epics: shippedEpics.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction isActiveStatus(status: string): boolean {\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n}\n\nfunction isPlannedStatus(status: string): boolean {\n return status === 'Ready' || status === 'Planned' || status === 'Draft';\n}\n\nfunction isShippedStatus(status: string): boolean {\n return status === 'Completed' || status === 'Approved';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * manifest.ts — STORY-009-01\n *\n * Scaffold manifest loading, drift classification, and atomic drift-state writing.\n * Node built-ins only: fs/promises, path.\n *\n * MANIFEST.json does not exist until STORY-009-02 runs `npm run build`.\n * loadPackageManifest throws a clear error when the file is absent — not raw ENOENT.\n */\n\nimport { readFile, writeFile, rename, mkdir } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { hashNormalized } from './sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type Tier =\n | 'protocol'\n | 'template'\n | 'agent'\n | 'hook'\n | 'skill'\n | 'cli-config'\n | 'user-artifact'\n | 'derived';\n\nexport type DriftState =\n | 'clean'\n | 'user-modified'\n | 'upstream-changed'\n | 'both-changed'\n | 'untracked';\n\nexport interface ManifestEntry {\n path: string;\n sha256: string | null;\n tier: Tier;\n overwrite_policy: 'always' | 'merge-3way' | 'skip' | 'preserve' | 'pin-aware';\n preserve_on_uninstall: boolean;\n}\n\nexport interface ManifestFile {\n cleargate_version: string;\n generated_at: string;\n files: ManifestEntry[];\n /**\n * Present only in `.cleargate/.install-manifest.json` (the install snapshot).\n * Stamped by `cleargate init` as the FINAL step (STORY-009-03).\n * Not present in the package-shipped MANIFEST.json.\n */\n installed_at?: string;\n}\n\nexport interface DriftMapEntry {\n state: DriftState;\n entry: ManifestEntry;\n install_sha: string | null;\n current_sha: string | null;\n package_sha: string | null;\n}\n\nexport interface DriftMap {\n [filePath: string]: DriftMapEntry;\n}\n\n/**\n * The on-disk shape of `.cleargate/.drift-state.json`.\n * Wraps the DriftMap with a `last_refreshed` timestamp so the daily-throttle\n * logic in `cleargate doctor --check-scaffold` can skip re-computation.\n */\nexport interface DriftStateFile {\n last_refreshed: string;\n drift: DriftMap;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\nexport interface LoadPackageManifestOpts {\n /**\n * Override the root directory where MANIFEST.json is resolved.\n * Default: resolved via import.meta.url (1 level up from dist/ in prod,\n * or cleargate-planning/ in dev).\n *\n * This seam is mandatory per FLASHCARD #tsup #bundle #import-meta — the\n * bundle collapses import.meta.url to the bundle file so default resolution\n * must never be relied upon in tests.\n */\n packageRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveDefaultPackageRoot(): string {\n // In production (dist/cli.js), import.meta.url points to dist/cli.js.\n // MANIFEST.json is copied to dist/ by the build step — so 0 levels up.\n // In dev, import.meta.url is cleargate-cli/src/lib/manifest.ts — 3 levels up\n // to cleargate-cli/, then ../cleargate-planning/ for the fixture source.\n // Rather than guessing, we prefer the dist/ sibling first; fall back to the\n // source-tree dev path.\n const here = new URL('.', import.meta.url).pathname;\n\n // Try: same directory (dist/ scenario)\n const distCandidate = path.join(here, 'MANIFEST.json');\n if (existsSync(distCandidate)) {\n return here;\n }\n\n // Try: 1 level up (also dist/ scenario when emitted as dist/lib/manifest.js)\n const oneLevelUp = path.join(here, '..', 'MANIFEST.json');\n if (existsSync(oneLevelUp)) {\n return path.join(here, '..');\n }\n\n // Dev fallback: from src/lib walk up to repo root, then cleargate-planning/\n // src/lib → src → cleargate-cli → repo-root → cleargate-planning\n const devCandidate = path.join(here, '..', '..', '..', 'cleargate-planning', 'MANIFEST.json');\n if (existsSync(devCandidate)) {\n return path.join(here, '..', '..', '..', 'cleargate-planning');\n }\n\n // Cannot determine — caller will get a clear error from loadPackageManifest\n return here;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Load MANIFEST.json from the installed package root.\n *\n * Uses `opts.packageRoot` (required in tests) or default resolution.\n * Throws a descriptive error when the file is absent rather than a raw ENOENT.\n */\nexport function loadPackageManifest(opts?: LoadPackageManifestOpts): ManifestFile {\n const packageRoot = opts?.packageRoot ?? resolveDefaultPackageRoot();\n const manifestPath = path.join(packageRoot, 'MANIFEST.json');\n\n if (!existsSync(manifestPath)) {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n let raw: string;\n try {\n // Synchronous read — callers treat loadPackageManifest as synchronous (startup-time).\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n return JSON.parse(raw) as ManifestFile;\n}\n\n/**\n * Load the install-time snapshot from `<projectRoot>/.cleargate/.install-manifest.json`.\n * Returns null if the file does not exist (first install or pre-manifest era).\n */\nexport async function loadInstallSnapshot(projectRoot: string): Promise<ManifestFile | null> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n try {\n const raw = await readFile(snapshotPath, 'utf-8');\n return JSON.parse(raw) as ManifestFile;\n } catch {\n return null;\n }\n}\n\n/**\n * Compute the SHA256 of a tracked file in the current working tree.\n * Returns null when the file does not exist on disk.\n */\nexport async function computeCurrentSha(\n file: ManifestEntry,\n projectRoot: string\n): Promise<string | null> {\n const filePath = path.join(projectRoot, file.path);\n try {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Classify the drift state of a single tracked file.\n *\n * Decision table (PROP-006 §2.4):\n *\n * | Tier | Result |\n * |---------------|-------------|\n * | user-artifact | untracked |\n *\n * | pkgSha | installSha | currentSha | Result |\n * |--------|------------|------------|-------------------|\n * | any | any | null | untracked |\n * | A | A | A | clean |\n * | A | A | B (≠A) | user-modified |\n * | B (≠A) | A | A | upstream-changed |\n * | all differ pairwise | both-changed |\n */\nexport function classify(\n pkgSha: string | null,\n installSha: string | null,\n currentSha: string | null,\n tier: Tier\n): DriftState {\n // user-artifact short-circuit (EPIC-009 §6 Q8)\n if (tier === 'user-artifact') {\n return 'untracked';\n }\n\n // Missing current file\n if (currentSha === null) {\n return 'untracked';\n }\n\n const installEqualsPackage = installSha === pkgSha;\n const currentEqualsInstall = currentSha === installSha;\n\n if (installEqualsPackage && currentEqualsInstall) {\n // install == current == package\n return 'clean';\n }\n\n if (installEqualsPackage && !currentEqualsInstall) {\n // install == package, current != install => user modified\n return 'user-modified';\n }\n\n if (!installEqualsPackage && currentEqualsInstall) {\n // install == current, package != install => upstream changed\n return 'upstream-changed';\n }\n\n // All three differ pairwise\n return 'both-changed';\n}\n\n/**\n * Options for writeDriftState.\n */\nexport interface WriteDriftStateOpts {\n /**\n * ISO-8601 timestamp to record as `last_refreshed` in the output file.\n * When omitted, the current time is used (new Date().toISOString()).\n * This seam is mandatory for deterministic tests.\n */\n lastRefreshed?: string;\n}\n\n/**\n * Atomically write the drift-state map to `<projectRoot>/.cleargate/.drift-state.json`.\n *\n * The on-disk format is wrapped: `{ last_refreshed: string, drift: DriftMap }`.\n * This allows the daily-throttle logic in `cleargate doctor --check-scaffold`\n * to read the timestamp without re-computing all SHAs.\n *\n * Uses write-temp-then-rename (atomic on POSIX; best-effort on Windows).\n * Ensures parent directory exists before writing.\n *\n * STORY-009-04 extended the signature from `(projectRoot, DriftMap)` to\n * `(projectRoot, DriftMap, opts?)` — callers passing only two args continue to work.\n */\nexport async function writeDriftState(\n projectRoot: string,\n state: DriftMap,\n opts?: WriteDriftStateOpts\n): Promise<void> {\n const cleargatDir = path.join(projectRoot, '.cleargate');\n const finalPath = path.join(cleargatDir, '.drift-state.json');\n const tmpPath = `${finalPath}.tmp`;\n\n const lastRefreshed = opts?.lastRefreshed ?? new Date().toISOString();\n const fileContent: DriftStateFile = { last_refreshed: lastRefreshed, drift: state };\n\n await mkdir(cleargatDir, { recursive: true });\n await writeFile(tmpPath, JSON.stringify(fileContent, null, 2) + '\\n', 'utf-8');\n await rename(tmpPath, finalPath);\n}\n\n/**\n * Read the drift-state file written by `writeDriftState`.\n * Returns null when the file does not exist or is malformed.\n */\nexport async function readDriftState(projectRoot: string): Promise<DriftStateFile | null> {\n const driftPath = path.join(projectRoot, '.cleargate', '.drift-state.json');\n try {\n const raw = await readFile(driftPath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n // Accept both the new wrapped format {last_refreshed, drift} and the old flat format\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'last_refreshed' in parsed &&\n 'drift' in parsed\n ) {\n return parsed as DriftStateFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * sha256.ts — STORY-009-01\n *\n * Deterministic SHA256 hasher with cross-platform content normalization.\n * Node built-ins only: crypto, fs/promises, path.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Hash normalized content (string or Buffer) — 64 hex chars.\n *\n * Normalization steps applied in order:\n * 1. Convert Buffer to UTF-8 string.\n * 2. Strip leading BOM (U+FEFF).\n * 3. Normalize CRLF → LF.\n * 4. Enforce trailing newline (append `\\n` if missing).\n */\nexport function hashNormalized(content: string | Buffer): string {\n let text: string =\n Buffer.isBuffer(content) ? content.toString('utf-8') : content;\n\n // 1. Strip leading BOM\n if (text.startsWith('\\ufeff')) {\n text = text.slice(1);\n }\n\n // 2. CRLF → LF\n text = text.replace(/\\r\\n/g, '\\n');\n\n // 3. Enforce trailing newline\n if (!text.endsWith('\\n')) {\n text += '\\n';\n }\n\n return createHash('sha256').update(text, 'utf-8').digest('hex');\n}\n\n/**\n * Read a file, normalize its content, and return the SHA256 hex digest.\n */\nexport async function hashFile(filePath: string): Promise<string> {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n}\n\n/**\n * Return the first 8 hex characters of a full SHA256 digest for human-readable output.\n */\nexport function shortHash(full: string): string {\n return full.slice(0, 8);\n}\n","/**\n * prompts.ts — minimal readline-based prompt helpers\n *\n * STORY-009-03: created here (cited in story §3 as \"existing\"; verified absent).\n * STORY-010-01: added promptEmail for participant identity flow.\n * Used by init.ts restore flow and future commands that need interactive input.\n */\nimport * as readline from 'node:readline';\n\nexport interface PromptOptions {\n /** Override stdin for testing */\n stdin?: NodeJS.ReadableStream;\n /** Override stdout write for testing */\n stdout?: (s: string) => void;\n}\n\n/**\n * Prompt the user with a yes/no question.\n *\n * @param question The question text to display (without [Y/n] suffix — caller includes it)\n * @param defaultYes Whether Enter with no input means yes\n * @param opts Test seams for stdin/stdout\n * @returns true for yes, false for no\n */\nexport async function promptYesNo(\n question: string,\n defaultYes: boolean,\n opts?: PromptOptions,\n): Promise<boolean> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) keeps the cursor inline with the prompt so\n // the user's typed input is visible adjacent to the question — matches every\n // other CLI prompt convention. See BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<boolean>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim().toLowerCase();\n if (trimmed === '') {\n resolve(defaultYes);\n } else if (trimmed === 'y' || trimmed === 'yes') {\n resolve(true);\n } else {\n resolve(false);\n }\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — treat as default\n resolve(defaultYes);\n }\n });\n });\n}\n\n/**\n * Prompt the user for a text value with an optional default.\n *\n * @param question The question text to display\n * @param defaultValue The value used when the user presses Enter without typing\n * @param opts Test seams for stdin/stdout\n * @returns The entered string, or `defaultValue` if Enter is pressed with no input\n */\nexport async function promptEmail(\n question: string,\n defaultValue: string,\n opts?: PromptOptions,\n): Promise<string> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) — see promptYesNo above and BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<string>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim();\n resolve(trimmed === '' ? defaultValue : trimmed);\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — use default\n resolve(defaultValue);\n }\n });\n });\n}\n","/**\n * identity.ts — participant identity resolver.\n *\n * STORY-010-01: resolveIdentity precedence ladder + writeParticipant atomic write.\n *\n * Resolution order (highest-priority first):\n * 1. .cleargate/.participant.json\n * 2. CLEARGATE_USER env var\n * 3. git config user.email (via child_process.spawnSync — NOT simple-git; see flashcard #cli #simple-git #deps)\n * 4. \"{username}@{hostname}\" host fallback\n *\n * All external sources (env, git, host) are injectable as opts for test hermetics.\n * Do NOT reach into process.env / os.hostname() directly in the happy path.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as fsPromises from 'node:fs/promises';\nimport * as os from 'node:os';\nimport { spawnSync } from 'node:child_process';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type IdentitySource = 'participant-json' | 'env' | 'git' | 'host';\n\nexport interface Identity {\n email: string;\n source: IdentitySource;\n}\n\nexport interface ParticipantFile {\n email: string;\n set_at: string;\n source: 'prompted' | 'inferred';\n}\n\n// ── Read participant file ─────────────────────────────────────────────────────\n\n/**\n * Read .cleargate/.participant.json.\n * Returns null on ENOENT or malformed JSON — caller decides what to do.\n */\nexport function readParticipant(projectRoot: string): ParticipantFile | null {\n const filePath = path.join(projectRoot, '.cleargate', '.participant.json');\n try {\n const raw = fs.readFileSync(filePath, 'utf8');\n return JSON.parse(raw) as ParticipantFile;\n } catch {\n return null;\n }\n}\n\n// ── Write participant file ────────────────────────────────────────────────────\n\n/**\n * Write .cleargate/.participant.json atomically (tmp + rename).\n * Creates .cleargate/ directory if it does not exist.\n */\nexport async function writeParticipant(\n projectRoot: string,\n email: string,\n source: 'prompted' | 'inferred',\n now: () => string = () => new Date().toISOString(),\n): Promise<void> {\n const cleargateDir = path.join(projectRoot, '.cleargate');\n await fsPromises.mkdir(cleargateDir, { recursive: true });\n\n const filePath = path.join(cleargateDir, '.participant.json');\n const tmpPath = filePath + '.tmp.' + Date.now();\n\n const content: ParticipantFile = {\n email,\n set_at: now(),\n source,\n };\n\n await fsPromises.writeFile(tmpPath, JSON.stringify(content, null, 2) + '\\n', 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Resolve identity ──────────────────────────────────────────────────────────\n\nexport interface ResolveIdentityOpts {\n /** Override process.env for test hermetics */\n env?: NodeJS.ProcessEnv;\n /** Override git config user.email resolution. Return null when git unavailable. */\n gitEmail?: () => string | null;\n /** Override os.hostname() */\n hostname?: () => string;\n /** Override os.userInfo().username */\n username?: () => string;\n /** Override new Date().toISOString() (unused here; reserved for callers) */\n now?: () => string;\n}\n\n/**\n * Resolve caller identity via precedence ladder.\n * All external lookups go through opts test seams — never reach process.env\n * or os.hostname() directly so tests can assert precedence without env pollution.\n */\nexport function resolveIdentity(\n projectRoot: string,\n opts: ResolveIdentityOpts = {},\n): Identity {\n // 1. participant-json\n const participant = readParticipant(projectRoot);\n if (participant !== null && participant.email) {\n return { email: participant.email, source: 'participant-json' };\n }\n\n // 2. env\n const envValue = (opts.env ?? process.env)['CLEARGATE_USER'];\n if (envValue && envValue.trim()) {\n return { email: envValue.trim(), source: 'env' };\n }\n\n // 3. git\n const gitEmailFn =\n opts.gitEmail ??\n (() => {\n const result = spawnSync('git', ['config', 'user.email'], {\n encoding: 'utf8',\n timeout: 3000,\n });\n if (result.status === 0 && result.stdout) {\n const trimmed = result.stdout.trim();\n if (trimmed) return trimmed;\n }\n return null;\n });\n\n const gitEmail = gitEmailFn();\n if (gitEmail && gitEmail.trim()) {\n return { email: gitEmail.trim(), source: 'git' };\n }\n\n // 4. host fallback\n const hostnameFn = opts.hostname ?? (() => os.hostname());\n const usernameFn =\n opts.username ??\n (() => {\n try {\n return os.userInfo().username;\n } catch {\n return 'user';\n }\n });\n\n const hostname = hostnameFn();\n const username = usernameFn();\n return { email: `${username}@${hostname}`, source: 'host' };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { deriveBucket } from '../wiki/derive-bucket.js';\nimport { deriveRepo } from '../wiki/derive-repo.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, parsePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\n\nexport interface WikiIngestOptions {\n /** Absolute path to the raw delivery file to ingest */\n rawPath: string;\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: replaces fs.rename (for atomic index.md write test) */\n rename?: (src: string, dst: string) => void;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\nexport async function wikiIngestHandler(opts: WikiIngestOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const rename = opts.rename ?? fs.renameSync;\n const templateDir = opts.templateDir;\n\n const rawPath = opts.rawPath;\n\n // Resolve rawPath: if relative, resolve against cwd\n const absRawPath = path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n // Compute relative path from cwd (repo root)\n const relRawPath = path.relative(cwd, absRawPath).replace(/\\\\/g, '/');\n\n // Step 1: Validate path resolves under <cwd>/.cleargate/delivery/\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const deliveryRootNorm = deliveryRoot.replace(/\\\\/g, '/');\n const absDeliveryRoot = deliveryRoot;\n\n // Check: absRawPath must be under absDeliveryRoot\n const relToDelivery = path.relative(absDeliveryRoot, absRawPath);\n if (relToDelivery.startsWith('..') || path.isAbsolute(relToDelivery)) {\n stderr(`wiki ingest: ${rawPath} not under .cleargate/delivery/\\n`);\n exit(2);\n return;\n }\n\n void deliveryRootNorm; // suppress lint warning\n\n // Step 2: Exclusion check (defense-in-depth)\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => relRawPath.startsWith(excl));\n if (isExcluded) {\n stdout(`wiki ingest: ${rawPath} excluded (skip)\\n`);\n exit(0);\n return;\n }\n\n // Step 3: Derive bucket + id + type + repo\n const filename = path.basename(absRawPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch (e) {\n stderr(`wiki ingest: cannot determine bucket for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let repo: ReturnType<typeof deriveRepo>;\n try {\n repo = deriveRepo(relRawPath);\n } catch (e) {\n stderr(`wiki ingest: cannot derive repo for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n const { type, id, bucket } = bucketInfo;\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const pageDir = path.join(wikiRoot, bucket);\n const pagePath = path.join(pageDir, `${id}.md`);\n\n // Read and parse the raw file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absRawPath, 'utf8');\n } catch (e) {\n stderr(`wiki ingest: cannot read ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch (e) {\n stderr(`wiki ingest: malformed frontmatter in ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // Step 4: Idempotency guard (A2)\n const currentSha = getGitSha(absRawPath, gitRunner) ?? '';\n const pageExists = fs.existsSync(pagePath);\n\n if (pageExists && currentSha !== '') {\n let isNoOp = false;\n try {\n const existingPageContent = fs.readFileSync(pagePath, 'utf8');\n const existingPage = parsePage(existingPageContent);\n\n if (existingPage.last_ingest_commit === currentSha) {\n // Check if raw file content matches what git shows for that SHA\n const contentUnchanged = checkContentUnchanged(absRawPath, currentSha, relRawPath, gitRunner);\n if (contentUnchanged) {\n isNoOp = true;\n }\n }\n } catch {\n // If we can't parse the existing page, proceed with ingest\n }\n\n if (isNoOp) {\n stdout(`wiki ingest: ${id} unchanged (no-op)\\n`);\n exit(0);\n return;\n }\n }\n\n // Determine action\n const action = pageExists ? 'update' : 'create';\n\n // Step 5: Build new WikiPage and write it\n const parent = buildParentRef(fm);\n const children = buildChildrenRefs(fm);\n const timestamp = now();\n\n const wikiPage: WikiPage = {\n type,\n id,\n parent,\n children,\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: relRawPath,\n last_ingest: timestamp,\n last_ingest_commit: currentSha,\n repo,\n };\n\n const pageBody = buildPageBody({ id, fm, body });\n const pageContent = serializePage(wikiPage, pageBody);\n\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(pagePath, pageContent, 'utf8');\n\n // Step 6: Append one log entry to wiki/log.md\n appendLogEntry(wikiRoot, { timestamp, action, id, relRawPath });\n\n // Step 7: Update wiki/index.md (atomic write-temp-then-rename)\n updateIndex(wikiRoot, { id, type, status: wikiPage.status, relRawPath, rename });\n\n // Step 8: Recompile affected synthesis pages (all four — M3 over-recompiles)\n recompileSynthesis(wikiRoot, cwd, templateDir);\n\n // Step 9: Print result\n stdout(`wiki ingest: ${action} ${bucket}/${id}.md\\n`);\n}\n\nfunction checkContentUnchanged(\n absRawPath: string,\n sha: string,\n relRawPath: string,\n gitRunner?: GitRunner,\n): boolean {\n try {\n const run = gitRunner ?? defaultGitRunner;\n // git show <sha>:<relRawPath> returns file content at that commit\n const gitContent = run('git', ['show', `${sha}:${relRawPath}`]);\n // Non-zero exit is handled by runner returning empty string (defaultRunner returns stdout)\n // If empty string returned from show when sha is valid, treat as changed\n if (!gitContent && gitContent !== '') return false;\n const currentContent = fs.readFileSync(absRawPath, 'utf8');\n return gitContent === currentContent;\n } catch {\n return false;\n }\n}\n\nfunction defaultGitRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n if (result.status !== 0) return '\\0__NONZERO__'; // sentinel for non-zero exit\n return result.stdout ?? '';\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: { id: string; fm: Record<string, unknown>; body: string }): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(\n item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.',\n ).slice(0, 200);\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n const blastParts: string[] = [];\n if (parent) blastParts.push(parent);\n for (const child of children) blastParts.push(child);\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction appendLogEntry(\n wikiRoot: string,\n entry: { timestamp: string; action: string; id: string; relRawPath: string },\n): void {\n const logPath = path.join(wikiRoot, 'log.md');\n const logEntry = [\n `- timestamp: \"${entry.timestamp}\"`,\n ` actor: \"cleargate wiki ingest\"`,\n ` action: \"${entry.action}\"`,\n ` target: \"${entry.id}\"`,\n ` path: \"${entry.relRawPath}\"`,\n ].join('\\n');\n\n if (fs.existsSync(logPath)) {\n const existing = fs.readFileSync(logPath, 'utf8');\n // Append to existing log\n const newContent = existing.trimEnd() + '\\n' + logEntry + '\\n';\n fs.writeFileSync(logPath, newContent, 'utf8');\n } else {\n fs.mkdirSync(wikiRoot, { recursive: true });\n fs.writeFileSync(logPath, `# Wiki Event Log\\n\\n${logEntry}\\n`, 'utf8');\n }\n}\n\nfunction updateIndex(\n wikiRoot: string,\n opts: {\n id: string;\n type: string;\n status: string;\n relRawPath: string;\n rename: (src: string, dst: string) => void;\n },\n): void {\n const indexPath = path.join(wikiRoot, 'index.md');\n const tmpPath = `${indexPath}.tmp`;\n\n const newRow = `| [[${opts.id}]] | ${opts.type} | ${opts.status} | ${opts.relRawPath} |`;\n\n let content: string;\n if (fs.existsSync(indexPath)) {\n content = fs.readFileSync(indexPath, 'utf8');\n // Check if a row with this id already exists; if so, replace it\n const idPattern = `[[${opts.id}]]`;\n const lines = content.split('\\n');\n let replaced = false;\n const newLines = lines.map((line) => {\n if (line.includes(idPattern) && line.startsWith('|')) {\n replaced = true;\n return newRow;\n }\n return line;\n });\n\n if (replaced) {\n content = newLines.join('\\n');\n } else {\n // Insert into the correct bucket section\n content = insertIntoSection(content, opts.id, newRow);\n }\n } else {\n // Build a minimal index.md with the item\n content = buildMinimalIndex(opts.id, opts.type, opts.status, opts.relRawPath);\n }\n\n fs.writeFileSync(tmpPath, content, 'utf8');\n opts.rename(tmpPath, indexPath);\n}\n\nfunction insertIntoSection(content: string, id: string, newRow: string): string {\n // Determine which bucket section to insert into\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n const sectionHeader = `## ${label}`;\n\n const lines = content.split('\\n');\n let sectionStart = -1;\n let nextSectionStart = -1;\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === sectionHeader) {\n sectionStart = i;\n // Find next section\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j].startsWith('## ')) {\n nextSectionStart = j;\n break;\n }\n }\n break;\n }\n }\n\n if (sectionStart === -1) {\n // Section doesn't exist — append new section at end\n const sectionContent = [\n '',\n sectionHeader,\n '',\n newRow,\n '',\n ].join('\\n');\n return content.trimEnd() + sectionContent;\n }\n\n // Find insertion point: after any existing rows in this section, sorted by id\n const sectionEnd = nextSectionStart === -1 ? lines.length : nextSectionStart;\n const sectionLines = lines.slice(sectionStart + 1, sectionEnd);\n\n // Find existing row entries and insert in sorted order\n let insertAt = -1;\n for (let i = 0; i < sectionLines.length; i++) {\n const line = sectionLines[i];\n if (line.startsWith('|') && !line.startsWith('|---|')) {\n // Extract the id from the row: | [[ID]] | ...\n const match = /\\|\\s*\\[\\[([^\\]]+)\\]\\]/.exec(line);\n if (match) {\n const rowId = match[1];\n if (id.localeCompare(rowId) <= 0) {\n insertAt = sectionStart + 1 + i;\n break;\n }\n }\n }\n }\n\n if (insertAt === -1) {\n // Append before the next section or at end of section\n // Find last row in section\n let lastRowIdx = sectionStart + 1;\n for (let i = sectionStart + 1; i < sectionEnd; i++) {\n if (lines[i].startsWith('|')) {\n lastRowIdx = i + 1;\n }\n }\n lines.splice(lastRowIdx, 0, newRow);\n } else {\n lines.splice(insertAt, 0, newRow);\n }\n\n return lines.join('\\n');\n}\n\nfunction getBucketFromId(id: string): string {\n if (id.startsWith('EPIC-')) return 'epics';\n if (id.startsWith('STORY-')) return 'stories';\n if (id.startsWith('SPRINT-')) return 'sprints';\n if (id.startsWith('PROPOSAL-')) return 'proposals';\n if (id.startsWith('CR-')) return 'crs';\n if (id.startsWith('BUG-')) return 'bugs';\n return 'topics';\n}\n\nfunction buildMinimalIndex(id: string, type: string, status: string, relRawPath: string): string {\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n\n const lines: string[] = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n '| ID | Type | Status | Raw Path |',\n '|---|---|---|---|',\n ];\n\n for (const b of BUCKET_ORDER) {\n if (b === 'topics') continue;\n lines.push('', `## ${BUCKET_LABELS[b]}`, '');\n if (b === bucket) {\n lines.push(`| [[${id}]] | ${type} | ${status} | ${relRawPath} |`);\n } else {\n lines.push('_No items._');\n }\n }\n lines.push('');\n\n void label; // suppress\n return lines.join('\\n');\n}\n\nfunction recompileSynthesis(wikiRoot: string, cwd: string, templateDir?: string): void {\n // Recompile all four synthesis pages\n // Gather current state from wiki pages to pass to recipes\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n let items: RawItem[] = [];\n if (fs.existsSync(deliveryRoot)) {\n try {\n items = scanRawItems(deliveryRoot, cwd);\n } catch {\n // If scan fails, pass empty state — synthesis pages will reflect empty\n }\n }\n\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n}\n","/**\n * STORY-002-08: cleargate wiki lint\n *\n * Scans .cleargate/wiki/ pages against their raw source files.\n * Enforcement mode (default): exits non-zero on any drift finding.\n * Suggest mode (--suggest): exits 0, prefixes flags with [advisory].\n *\n * Output format matches cleargate-wiki-lint subagent def lines 220-227:\n * <category>: <primary-path> -> <secondary-path-or-detail> (<optional context>)\n * Summary line always last:\n * lint: <OK|FAIL> (N pages checked, M findings)\n */\n\nimport * as path from 'node:path';\nimport { loadWikiPages } from '../wiki/load-wiki.js';\nimport type { GitRunner } from '../wiki/git-sha.js';\nimport {\n checkOrphan,\n checkRepoMismatch,\n checkStaleCommit,\n checkMissingIngest,\n checkBrokenBacklinks,\n checkInvalidatedCitations,\n checkExcludedPathIngested,\n checkPaginationNeeded,\n checkGateFailure,\n checkGateStaleness,\n checkIndexBudget,\n discoverPlainTextMentions,\n type LintFinding,\n} from '../wiki/lint-checks.js';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\n\nexport interface WikiLintOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to git-sha calls */\n gitRunner?: GitRunner;\n /** Lint mode: 'enforce' (default, exits 1 on findings) or 'suggest' (always exits 0) */\n mode?: 'enforce' | 'suggest';\n}\n\nexport async function wikiLintHandler(opts: WikiLintOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n // stderr seam is wired but lint outputs everything to stdout per subagent def\n opts.stderr;\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const mode = opts.mode ?? 'enforce';\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const repoRoot = cwd;\n\n // Step 1: load all wiki pages (single discovery pass)\n let pages = loadWikiPages(wikiRoot);\n\n const findings: LintFinding[] = [];\n\n // Meta-check: pagination-needed (fires on bucket > 50 entries)\n const paginationFindings = checkPaginationNeeded(pages);\n findings.push(...paginationFindings);\n\n // Step 2: per-page checks (O(n))\n for (const page of pages) {\n // (a) orphan\n const orphan = checkOrphan(page, repoRoot);\n if (orphan) findings.push(orphan);\n\n // (b) repo-mismatch\n const repoMismatch = checkRepoMismatch(page, repoRoot);\n if (repoMismatch) findings.push(repoMismatch);\n\n // (c) stale-commit\n const staleCommit = checkStaleCommit(page, repoRoot, gitRunner);\n if (staleCommit) findings.push(staleCommit);\n\n // (d) missing-ingest\n const missingIngest = checkMissingIngest(page, repoRoot);\n if (missingIngest) findings.push(missingIngest);\n\n // (e) excluded-path-ingested\n const excludedPath = checkExcludedPathIngested(page, repoRoot);\n if (excludedPath) findings.push(excludedPath);\n\n // (f) gate-failure — enforcing for Epic/Story/CR/Bug\n const gateFail = checkGateFailure(page, repoRoot);\n if (gateFail) findings.push(gateFail);\n\n // (g) gate-stale — applies to ALL types\n const gateStale = checkGateStaleness(page, repoRoot);\n if (gateStale) findings.push(gateStale);\n }\n\n // Step 3: single index cross-check pass — broken backlinks\n const backlinkFindings = checkBrokenBacklinks(pages, repoRoot);\n findings.push(...backlinkFindings);\n\n // Step 4: topic-page invalidated-citation check\n const citationFindings = checkInvalidatedCitations(pages, repoRoot);\n findings.push(...citationFindings);\n\n // Step 4.5: index token-budget check (STORY-015-03)\n const wikiConfig = loadWikiConfig(cwd);\n const indexBudget = checkIndexBudget(repoRoot, wikiConfig.wiki.index_token_ceiling);\n\n // In suggest mode, always emit the usage line unconditionally (before advisory loop)\n if (mode === 'suggest' && indexBudget.tokens !== undefined) {\n const tokens = indexBudget.tokens;\n const ceiling = indexBudget.ceiling!;\n const pct = Math.round((tokens / ceiling) * 100);\n stdout(`index token usage: ${tokens} / ${ceiling} (${pct}%)\\n`);\n }\n\n // In enforce mode, push finding into findings array (will be emitted below)\n if (mode === 'enforce' && indexBudget.finding !== null) {\n findings.push(indexBudget.finding);\n }\n\n const pageCount = pages.length;\n const findingCount = findings.length;\n\n // Step 5: emit results\n if (mode === 'suggest') {\n // Advisory mode: prefix flags with [advisory] + do Karpathy discovery pass\n for (const finding of findings) {\n stdout(`[advisory] ${finding.line}\\n`);\n }\n\n // Karpathy discovery pass\n const suggestions = discoverPlainTextMentions(pages, repoRoot);\n for (const suggestion of suggestions) {\n stdout(`${suggestion}\\n`);\n }\n\n stdout(`lint: OK (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(0);\n return;\n }\n\n // Enforce mode\n for (const finding of findings) {\n stdout(`${finding.line}\\n`);\n }\n\n if (findingCount > 0) {\n stdout(`lint: FAIL (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(1);\n } else {\n stdout(`lint: OK (${pageCount} pages checked, 0 findings)\\n`);\n exit(0);\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport type { WikiPage } from './page-schema.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface LoadedWikiPage {\n absPath: string;\n page: WikiPage;\n body: string;\n}\n\nconst BUCKET_DIRS = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'];\n\n/**\n * Glob+Read every wiki page from wikiRoot once.\n * Shared by wiki-lint and wiki-query (STORY-002-07 and STORY-002-08).\n */\nexport function loadWikiPages(wikiRoot: string): LoadedWikiPage[] {\n const results: LoadedWikiPage[] = [];\n\n for (const bucket of BUCKET_DIRS) {\n const dir = path.join(wikiRoot, bucket);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { encoding: 'utf8' }) as string[];\n for (const filename of entries) {\n if (!filename.endsWith('.md')) continue;\n const absPath = path.join(dir, filename);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n continue;\n }\n\n const page: WikiPage = {\n type: (fm['type'] as WikiPageType) ?? 'epic',\n id: String(fm['id'] ?? ''),\n parent: String(fm['parent'] ?? ''),\n children: Array.isArray(fm['children'])\n ? (fm['children'] as unknown[]).map(String)\n : [],\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: String(fm['raw_path'] ?? ''),\n last_ingest: String(fm['last_ingest'] ?? ''),\n last_ingest_commit: String(fm['last_ingest_commit'] ?? ''),\n repo: (fm['repo'] as RepoTag) ?? 'planning',\n };\n\n results.push({ absPath, page, body });\n }\n }\n\n return results;\n}\n","/**\n * Pure-function lint check implementations.\n * One exported function per category so they can be unit-tested in isolation.\n * Categories must use these exact strings (subagent + CLI agree):\n * orphan, repo-mismatch, stale-commit, missing-ingest, broken-backlink,\n * invalidated-citation, excluded-path-ingested, pagination-needed, index-budget\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport type { GitRunner } from './git-sha.js';\nimport type { LoadedWikiPage } from './load-wiki.js';\nimport { deriveRepo } from './derive-repo.js';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { detectWorkItemTypeFromFm } from '../lib/work-item-type.js';\n\nexport interface LintFinding {\n category: string;\n line: string;\n}\n\n/** §10.3 excluded directories — wiki pages must not exist for raw files under these. */\nconst EXCLUDED_DIRS = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/** Maximum entries per bucket before pagination-needed fires. */\nconst MAX_BUCKET_ENTRIES = 50;\n\n/**\n * Check (a): Orphan — wiki page's raw_path doesn't exist on disk.\n * Skips pages whose raw_path is under an excluded directory (caught by check 7).\n */\nexport function checkOrphan(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n // Don't flag orphan for excluded paths — that's caught by excluded-path-ingested\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'orphan',\n line: `orphan: ${relPage} -> missing ${rawPath} (raw missing)`,\n };\n }\n return null;\n}\n\n/**\n * Check (b): repo-mismatch — stored repo field doesn't match raw_path prefix.\n */\nexport function checkRepoMismatch(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n let derivedRepo: string;\n try {\n derivedRepo = deriveRepo(rawPath);\n } catch {\n return null; // Can't derive — not our responsibility to flag here\n }\n\n if (page.page.repo !== derivedRepo) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'repo-mismatch',\n line: `repo-mismatch: ${relPage} declares repo:${page.page.repo} but raw_path implies repo:${derivedRepo}`,\n };\n }\n return null;\n}\n\n/**\n * Check (c): stale-commit — stored last_ingest_commit differs from current git HEAD SHA.\n * Empty stored SHA is tolerated (untracked file).\n */\nexport function checkStaleCommit(\n page: LoadedWikiPage,\n repoRoot: string,\n gitRunner?: GitRunner,\n): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const storedSha = page.page.last_ingest_commit;\n // If stored SHA is empty, file was untracked at ingest time — tolerate\n if (!storedSha) return null;\n\n // Run git log -1\n let currentSha: string;\n if (gitRunner) {\n currentSha = gitRunner('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n } else {\n const result = spawnSync('git', ['log', '-1', '--format=%H', '--', rawPath], {\n encoding: 'utf8',\n cwd: repoRoot,\n });\n currentSha = (result.stdout ?? '').trim();\n }\n\n if (!currentSha) return null; // untracked now — don't flag\n if (storedSha !== currentSha) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'stale-commit',\n line: `stale-commit: ${relPage} at ${storedSha}, current ${currentSha}`,\n };\n }\n return null;\n}\n\n/**\n * Check (d): missing-ingest — raw file mtime newer than wiki page mtime.\n */\nexport function checkMissingIngest(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null; // orphan check handles this\n\n const rawStat = fs.statSync(absRaw);\n const pageStat = fs.statSync(page.absPath);\n\n // Use > 2s gap to avoid HFS+ mtime resolution flakiness (per blueprint gotcha)\n const rawMtimeMs = rawStat.mtimeMs;\n const pageMtimeMs = pageStat.mtimeMs;\n\n if (rawMtimeMs - pageMtimeMs > 2000) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n const rawMtime = rawStat.mtime.toISOString();\n const pageMtime = pageStat.mtime.toISOString();\n return {\n category: 'missing-ingest',\n line: `missing-ingest: ${rawPath} newer than ${relPage} (raw mtime: ${rawMtime}, page mtime: ${pageMtime})`,\n };\n }\n return null;\n}\n\n/**\n * Check (backlink): broken backlink — child's parent exists but parent doesn't list the child.\n * O(n) linear scan over collected parent declarations.\n */\nexport function checkBrokenBacklinks(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n // Build a map of id -> page for O(1) lookup\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n for (const childPage of pages) {\n const parentRef = childPage.page.parent;\n if (!parentRef) continue;\n\n // Extract parent ID from \"[[PARENT-ID]]\" form\n const match = parentRef.match(/\\[\\[(.+?)\\]\\]/);\n if (!match) continue;\n const parentId = match[1];\n\n const parentPage = byId.get(parentId);\n if (!parentPage) {\n // Parent page missing — flag\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n continue;\n }\n\n // Check that parent's children list contains [[childId]]\n const childId = childPage.page.id;\n const childRef = `[[${childId}]]`;\n const parentHasChild = parentPage.page.children.some(\n (c) => c === childRef || c === childId,\n );\n\n if (!parentHasChild) {\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n }\n }\n\n return findings;\n}\n\n/**\n * Check: invalidated-citation — topic page cites a cancelled or missing item.\n */\nexport function checkInvalidatedCitations(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n const topicPages = pages.filter((p) => p.page.type === 'topic');\n\n for (const topicPage of topicPages) {\n // WikiPage doesn't have a cites field — re-parse raw frontmatter to get it.\n const relTopic = path.relative(wikiRoot, topicPage.absPath).replace(/\\\\/g, '/');\n\n let citesList: string[] = [];\n try {\n const raw = fs.readFileSync(topicPage.absPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n const rawCites = fm['cites'];\n if (Array.isArray(rawCites)) {\n citesList = (rawCites as unknown[]).map(String);\n }\n } catch {\n continue;\n }\n\n for (const cite of citesList) {\n const match = cite.match(/\\[\\[(.+?)\\]\\]/);\n const id = match ? match[1] : cite;\n\n const citedPage = byId.get(id);\n if (!citedPage) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (missing)`,\n });\n continue;\n }\n\n const status = citedPage.page.status;\n if (status === 'cancelled' || status.toLowerCase().includes('cancelled')) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (cancelled)`,\n });\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Check: excluded-path-ingested — wiki page exists for a raw file under an excluded directory.\n */\nexport function checkExcludedPathIngested(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'excluded-path-ingested',\n line: `excluded-path-ingested: ${relPage} (raw_path ${rawPath} is under an excluded directory)`,\n };\n }\n return null;\n}\n\n/**\n * Meta-check: pagination-needed — fires if any bucket has more than 50 entries.\n */\nexport function checkPaginationNeeded(pages: LoadedWikiPage[]): LintFinding[] {\n // Count by bucket (derived from absPath directory name)\n const bucketCounts = new Map<string, number>();\n for (const p of pages) {\n const bucket = path.basename(path.dirname(p.absPath));\n bucketCounts.set(bucket, (bucketCounts.get(bucket) ?? 0) + 1);\n }\n\n const findings: LintFinding[] = [];\n for (const [bucket, count] of bucketCounts) {\n if (count > MAX_BUCKET_ENTRIES) {\n findings.push({\n category: 'pagination-needed',\n line: `pagination-needed: ${bucket} (${count} entries, max ${MAX_BUCKET_ENTRIES} per bucket)`,\n });\n }\n }\n return findings;\n}\n\n/** Work-item types that trigger enforcing gate-failure lint (not advisory). */\nconst ENFORCING_TYPES = new Set(['epic', 'story', 'cr', 'bug']);\n\n/** Status values considered \"ready\" (🟢-candidate). */\nconst READY_STATUSES = new Set(['Ready', 'Active']);\n\n/**\n * Parse the cached_gate_result from a raw frontmatter record.\n * parseFrontmatter stores nested YAML objects as opaque strings starting with '{'.\n * This helper resolves either form into a plain object or null.\n */\nfunction parseCachedGateResult(\n raw: unknown,\n): { pass: unknown; failing_criteria: unknown; last_gate_check: unknown } | null {\n if (!raw || raw === null) return null;\n\n // Opaque string form — inline flow YAML written by writeCachedGate\n if (typeof raw === 'string') {\n if (!raw.startsWith('{')) return null;\n try {\n const parsed = yaml.load(raw);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n } catch {\n return null;\n }\n }\n\n // Already-parsed object form (future-proofing)\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n const p = raw as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n }\n\n return null;\n}\n\n/**\n * Check: gate-failure — 🟢-candidate Epic/Story/CR/Bug with cached_gate_result.pass === false.\n * Reads the raw work-item file (not the wiki page).\n * Proposal / Sprint / Initiative are advisory only → returns null (no enforcing block).\n */\nexport function checkGateFailure(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr || cgr.pass !== false) return null;\n\n // Check if the work-item type is enforcing\n const wiType = detectWorkItemTypeFromFm(rawFm);\n if (!wiType || !ENFORCING_TYPES.has(wiType)) return null;\n\n // Check if this is a 🟢-candidate (status Ready/Active or ambiguity 🟢 Low)\n const status = String(rawFm['status'] ?? '');\n const ambiguity = String(rawFm['ambiguity'] ?? '');\n const isReadyCandidate = READY_STATUSES.has(status) || ambiguity === '🟢 Low';\n if (!isReadyCandidate) return null;\n\n // Collect failing criteria IDs\n const failingCriteria = cgr.failing_criteria;\n const criteriaIds: string[] = [];\n if (Array.isArray(failingCriteria)) {\n for (const criterion of failingCriteria as unknown[]) {\n if (criterion && typeof criterion === 'object' && 'id' in (criterion as object)) {\n criteriaIds.push(String((criterion as Record<string, unknown>)['id']));\n } else if (typeof criterion === 'string') {\n criteriaIds.push(criterion);\n }\n }\n }\n\n const criteriaStr = criteriaIds.length > 0 ? criteriaIds.join(', ') : 'unknown';\n return {\n category: 'gate-failure',\n line: `gate-failure: ${rawPath} failed criteria: ${criteriaStr}`,\n };\n}\n\n/**\n * Check: gate-stale — cached_gate_result.last_gate_check < updated_at (ISO-8601 lexical compare).\n * Applies to ALL work-item types (including Proposal/Sprint/Initiative).\n * Reads the raw work-item file (not the wiki page).\n */\nexport function checkGateStaleness(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr) return null;\n\n const lastGateCheck = cgr.last_gate_check;\n if (!lastGateCheck || lastGateCheck === null) return null;\n\n const updatedAt = rawFm['updated_at'];\n if (!updatedAt) return null;\n\n const lastCheckStr = String(lastGateCheck);\n const updatedAtStr = String(updatedAt);\n\n // ISO-8601 lexical compare: if last_gate_check < updated_at → stale\n if (lastCheckStr < updatedAtStr) {\n return {\n category: 'gate-stale',\n line: `gate-stale: ${rawPath} last_gate_check=${lastCheckStr} < updated_at=${updatedAtStr}`,\n };\n }\n return null;\n}\n\n/**\n * Karpathy discovery pass: scan page bodies for plain-text ID mentions\n * (not wrapped in [[]]). Emit suggest lines.\n */\nexport function discoverPlainTextMentions(pages: LoadedWikiPage[], repoRoot: string): string[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, boolean>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, true);\n }\n\n const suggestions: string[] = [];\n const ID_PATTERN = /\\b((?:EPIC|STORY|SPRINT|PROPOSAL|CR|BUG)-[\\w-]+)\\b/g;\n const LINK_PATTERN = /\\[\\[[\\w-]+\\]\\]/g;\n\n for (const page of pages) {\n const relPage = path.relative(wikiRoot, page.absPath).replace(/\\\\/g, '/');\n // Find all [[...]] wrapped references to exclude\n const wrappedRefs = new Set<string>();\n for (const m of page.body.matchAll(LINK_PATTERN)) {\n const inner = m[0].slice(2, -2);\n wrappedRefs.add(inner);\n }\n\n // Find plain-text ID mentions\n for (const m of page.body.matchAll(ID_PATTERN)) {\n const mentionedId = m[1];\n if (!byId.has(mentionedId)) continue;\n if (wrappedRefs.has(mentionedId)) continue;\n if (mentionedId === page.page.id) continue; // self-reference\n suggestions.push(`suggest: ${relPage} mentions ${mentionedId} in plain text, consider [[${mentionedId}]] wrap`);\n }\n }\n\n return suggestions;\n}\n\n/**\n * Check: index-budget — wiki/index.md approximate token count exceeds configured ceiling.\n * Token heuristic: Math.round(bytes / 4). Returns null when index.md is absent.\n * This check is a structural check, not a per-page check, so it takes repoRoot directly.\n */\nexport interface IndexBudgetResult {\n /** Present when tokens > ceiling. Push this into findings array. */\n finding: LintFinding | null;\n /** Always populated when index.md exists; undefined when file absent. */\n tokens?: number;\n ceiling?: number;\n}\n\nexport function checkIndexBudget(repoRoot: string, indexTokenCeiling: number): IndexBudgetResult {\n const indexPath = path.join(repoRoot, '.cleargate', 'wiki', 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n return { finding: null };\n }\n\n const bytes = fs.statSync(indexPath).size;\n const tokens = Math.round(bytes / 4);\n const ceiling = indexTokenCeiling;\n\n if (tokens > ceiling) {\n return {\n finding: {\n category: 'index-budget',\n line: `index-budget: wiki/index.md exceeds token ceiling: ${tokens} > ${ceiling}. Shard or prune (see EPIC-015).`,\n },\n tokens,\n ceiling,\n };\n }\n\n return { finding: null, tokens, ceiling };\n}\n","/**\n * work-item-type.ts — Shared work-item type detection utility.\n *\n * STORY-008-03: extracted here for STORY-008-05 to import without duplication.\n * Maps frontmatter ID keys and filename patterns to canonical work-item types.\n */\n\nexport type WorkItemType = 'story' | 'epic' | 'proposal' | 'cr' | 'bug';\n\n/**\n * Frontmatter key → work-item type mapping.\n * Keys are checked in order; first match wins.\n */\nconst FM_KEY_MAP: Array<{ key: string; type: WorkItemType }> = [\n { key: 'story_id', type: 'story' },\n { key: 'epic_id', type: 'epic' },\n { key: 'proposal_id', type: 'proposal' },\n { key: 'cr_id', type: 'cr' },\n { key: 'bug_id', type: 'bug' },\n];\n\n/**\n * Filename / ID prefix → work-item type mapping.\n */\nconst PREFIX_MAP: Array<{ prefix: string; type: WorkItemType }> = [\n { prefix: 'STORY-', type: 'story' },\n { prefix: 'EPIC-', type: 'epic' },\n { prefix: 'PROPOSAL-', type: 'proposal' },\n { prefix: 'CR-', type: 'cr' },\n { prefix: 'BUG-', type: 'bug' },\n];\n\n/**\n * Detect the work-item type from a parsed frontmatter record.\n * Returns null if no recognized ID key is found.\n */\nexport function detectWorkItemTypeFromFm(\n fm: Record<string, unknown>,\n): WorkItemType | null {\n for (const { key, type } of FM_KEY_MAP) {\n if (fm[key] !== undefined && fm[key] !== null && fm[key] !== '') {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Detect the work-item type from an ID string or file path.\n * Matches against the uppercase prefix (STORY-, EPIC-, etc.).\n * Returns null if no prefix matches.\n */\nexport function detectWorkItemType(idOrPath: string): WorkItemType | null {\n const upper = idOrPath.toUpperCase();\n // Strip leading directory path components\n const basename = upper.split('/').pop() ?? upper;\n for (const { prefix, type } of PREFIX_MAP) {\n if (basename.includes(prefix)) {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Canonical transitions per work-item type.\n * Epic has 2; all others have 1.\n */\nexport const WORK_ITEM_TRANSITIONS: Record<WorkItemType, string[]> = {\n proposal: ['ready-for-decomposition'],\n epic: ['ready-for-decomposition', 'ready-for-coding'],\n story: ['ready-for-execution'],\n cr: ['ready-to-apply'],\n bug: ['ready-for-fix'],\n};\n","/**\n * STORY-015-03: Per-repo wiki configuration loader.\n * STORY-018-03: Extended to include `gates` map.\n *\n * Reads `.cleargate/config.yml` from the repo root.\n * Single responsibility: surface `wiki.index_token_ceiling` and `gates` map.\n * Missing file → defaults. Malformed YAML → throws with file path in message.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\n\nexport interface GatesConfig {\n precommit?: string;\n test?: string;\n typecheck?: string;\n lint?: string;\n}\n\nexport interface WikiConfig {\n wiki: {\n index_token_ceiling: number;\n };\n gates: GatesConfig;\n}\n\nconst DEFAULT_INDEX_TOKEN_CEILING = 8000;\n\n/**\n * Load per-repo wiki config from `<repoRoot>/.cleargate/config.yml`.\n * Returns defaults when file is absent.\n * Throws a descriptive error on malformed YAML.\n */\nexport function loadWikiConfig(repoRoot: string): WikiConfig {\n const configPath = path.join(repoRoot, '.cleargate', 'config.yml');\n\n if (!fs.existsSync(configPath)) {\n return { wiki: { index_token_ceiling: DEFAULT_INDEX_TOKEN_CEILING }, gates: {} };\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch (err) {\n throw new Error(`Failed to read ${configPath}: ${String(err)}`);\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`Malformed YAML in ${configPath}: ${String(err)}`);\n }\n\n const ceiling = extractCeiling(parsed);\n const gates = extractGates(parsed);\n\n return {\n wiki: {\n index_token_ceiling: ceiling,\n },\n gates,\n };\n}\n\nfunction extractCeiling(parsed: unknown): number {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const root = parsed as Record<string, unknown>;\n const wiki = root['wiki'];\n\n if (wiki === null || wiki === undefined || typeof wiki !== 'object' || Array.isArray(wiki)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const wikiObj = wiki as Record<string, unknown>;\n const ceiling = wikiObj['index_token_ceiling'];\n\n if (typeof ceiling === 'number' && Number.isFinite(ceiling) && ceiling > 0) {\n return ceiling;\n }\n\n return DEFAULT_INDEX_TOKEN_CEILING;\n}\n\nfunction extractGates(parsed: unknown): GatesConfig {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {};\n }\n\n const root = parsed as Record<string, unknown>;\n const gates = root['gates'];\n\n if (gates === null || gates === undefined || typeof gates !== 'object' || Array.isArray(gates)) {\n return {};\n }\n\n const gatesObj = gates as Record<string, unknown>;\n const result: GatesConfig = {};\n\n if (typeof gatesObj['precommit'] === 'string') result.precommit = gatesObj['precommit'];\n if (typeof gatesObj['test'] === 'string') result.test = gatesObj['test'];\n if (typeof gatesObj['typecheck'] === 'string') result.typecheck = gatesObj['typecheck'];\n if (typeof gatesObj['lint'] === 'string') result.lint = gatesObj['lint'];\n\n return result;\n}\n","/**\n * STORY-002-08: cleargate wiki query [--persist]\n *\n * Read-only by default: grep .cleargate/wiki/index.md for query terms,\n * return matching [[ID]] list with one-line excerpts to stdout. Exit 0.\n *\n * --persist: compute slug, write wiki/topics/<slug>.md with frontmatter\n * type: topic, id, created_by, created_at, cites. Append to wiki/index.md\n * ## Topics section.\n *\n * NOTE: CLI synthesis is grep-and-list. For NL synthesis with the\n * cleargate-wiki-query subagent, invoke from a Claude Code session.\n * This diverges from PROPOSAL-002 §2.2 intentionally for testability and\n * offline/scripted use.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface WikiQueryOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: frozen ISO timestamp (defaults to new Date().toISOString()) */\n now?: () => string;\n /** The query string */\n query: string;\n /** If true, write result as a topic page under wiki/topics/ */\n persist?: boolean;\n}\n\n/**\n * Compute a slug from a query string.\n * Lowercase, replace spaces and punctuation with hyphens,\n * strip consecutive hyphens, truncate to ≤40 chars.\n */\nexport function computeSlug(query: string): string {\n return query\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') // non-alphanumeric → hyphen\n .replace(/^-+|-+$/g, '') // strip leading/trailing hyphens\n .replace(/-{2,}/g, '-') // collapse consecutive hyphens\n .slice(0, 40)\n .replace(/-+$/, ''); // strip trailing hyphens after truncation\n}\n\n/**\n * Parse index.md and extract matching IDs for the given query terms.\n * Returns array of { id, line } matching lines.\n */\nfunction searchIndex(indexContent: string, query: string): Array<{ id: string; excerpt: string }> {\n const terms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 0);\n\n const results: Array<{ id: string; excerpt: string }> = [];\n const seenIds = new Set<string>();\n\n for (const line of indexContent.split('\\n')) {\n const lower = line.toLowerCase();\n const matchesAll = terms.every((term) => lower.includes(term));\n if (!matchesAll) continue;\n\n // Extract [[ID]] from line\n const match = line.match(/\\[\\[([^\\]]+)\\]\\]/);\n if (!match) continue;\n const id = match[1];\n if (seenIds.has(id)) continue;\n seenIds.add(id);\n\n results.push({ id, excerpt: line.trim() });\n }\n\n return results;\n}\n\nexport async function wikiQueryHandler(opts: WikiQueryOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const now = opts.now ?? (() => new Date().toISOString());\n const query = opts.query;\n const persist = opts.persist ?? false;\n\n void stderr; // suppress unused warning\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const indexPath = path.join(wikiRoot, 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n stdout(`wiki query: no index.md found at ${indexPath}\\n`);\n stdout(`Run \\`cleargate wiki build\\` first.\\n`);\n exit(1);\n return;\n }\n\n const indexContent = fs.readFileSync(indexPath, 'utf8');\n const matches = searchIndex(indexContent, query);\n\n if (matches.length === 0) {\n stdout(`wiki query: no matches for \"${query}\"\\n`);\n exit(0);\n return;\n }\n\n // Build output body\n const bodyLines: string[] = [\n `# Query: ${query}`,\n '',\n `Found ${matches.length} match(es):`,\n '',\n ];\n\n for (const { id, excerpt } of matches) {\n bodyLines.push(`- [[${id}]] — ${excerpt}`);\n }\n bodyLines.push('');\n\n const body = bodyLines.join('\\n');\n\n // Output to stdout (read-only mode: always output to stdout)\n stdout(body);\n\n if (!persist) {\n exit(0);\n return;\n }\n\n // Persist mode: write topic page\n const slug = computeSlug(query);\n const topicsDir = path.join(wikiRoot, 'topics');\n fs.mkdirSync(topicsDir, { recursive: true });\n\n const citesArray = matches.map(({ id }) => `\"[[${id}]]\"`);\n const createdAt = now();\n\n // Build topic page frontmatter\n const frontmatter = [\n '---',\n `type: topic`,\n `id: \"${slug}\"`,\n `created_by: \"cleargate-wiki-query\"`,\n `created_at: \"${createdAt}\"`,\n `cites: [${citesArray.join(', ')}]`,\n '---',\n ].join('\\n');\n\n const topicContent = `${frontmatter}\\n\\n${body}`;\n const topicPath = path.join(topicsDir, `${slug}.md`);\n\n // Overwrite if exists (slug collision → overwrite per subagent def line 136)\n fs.writeFileSync(topicPath, topicContent, 'utf8');\n\n // Update wiki/index.md Topics section\n updateIndexTopicsSection(indexPath, slug, query, createdAt);\n\n exit(0);\n}\n\n/**\n * Append one row to the ## Topics section of wiki/index.md.\n * Creates the section header if absent.\n */\nfunction updateIndexTopicsSection(\n indexPath: string,\n slug: string,\n query: string,\n createdAt: string,\n): void {\n let content = fs.readFileSync(indexPath, 'utf8');\n\n const row = `| ${slug} | ${query} | ${createdAt} |`;\n\n if (content.includes('## Topics')) {\n // Append after the last line of the Topics section (end of file or before next ##)\n // Find the Topics section and append the row at the end\n const topicsIdx = content.indexOf('## Topics');\n const afterTopics = content.slice(topicsIdx);\n\n // Find the next ## section or end of file\n const nextSectionMatch = afterTopics.slice('## Topics'.length).match(/\\n## /);\n if (nextSectionMatch && nextSectionMatch.index !== undefined) {\n const insertPos = topicsIdx + '## Topics'.length + nextSectionMatch.index;\n content = content.slice(0, insertPos) + `\\n${row}` + content.slice(insertPos);\n } else {\n // Topics is the last section — append at end\n content = content.trimEnd() + `\\n${row}\\n`;\n }\n } else {\n // Create Topics section at end of file\n content = content.trimEnd() + `\\n\\n## Topics\\n\\n${row}\\n`;\n }\n\n fs.writeFileSync(indexPath, content, 'utf8');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline';\nimport { scanRawItems } from '../wiki/scan.js';\n\nexport interface WikiAuditStatusOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp (not used for output; reserved for future use) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Apply safe status corrections to frontmatter */\n fix?: boolean;\n /** Required together with --fix to confirm writes in non-interactive mode */\n yes?: boolean;\n /** Suppress diff output */\n quiet?: boolean;\n /** Test seam: override isTTY detection */\n isTTY?: boolean;\n /** Test seam: override readline for confirmation prompt */\n promptReader?: () => Promise<string>;\n}\n\nconst TERMINAL = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\ninterface DriftItem {\n id: string;\n rawPath: string;\n absPath: string;\n bucket: string;\n currentStatus: string;\n rule: 'A' | 'B' | 'C';\n /** Suggested new status (undefined for Rule B — file move needed) */\n suggestedStatus?: string;\n /** Human-readable description for the report */\n description: string;\n}\n\nexport async function wikiAuditStatusHandler(opts: WikiAuditStatusOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => { process.stdout.write(s); });\n const stderr = opts.stderr ?? ((s: string) => { process.stderr.write(s); });\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`audit-status: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Build a lookup of epic id → child stories (with terminal status check)\n const storiesByEpic = new Map<string, typeof items>();\n for (const item of items) {\n if (item.bucket !== 'stories') continue;\n const epicRef = String(item.fm['parent_epic_ref'] ?? '').replace(/^\\[\\[|\\]\\]$/g, '');\n if (!epicRef) continue;\n if (!storiesByEpic.has(epicRef)) storiesByEpic.set(epicRef, []);\n storiesByEpic.get(epicRef)!.push(item);\n }\n\n const driftItems: DriftItem[] = [];\n\n for (const item of items) {\n const currentStatus = String(item.fm['status'] ?? '');\n const isTerminal = TERMINAL.has(currentStatus);\n const inArchive = item.rawPath.includes('/archive/');\n const inPendingSync = item.rawPath.includes('/pending-sync/');\n\n // Rule A: in archive/ but status is non-terminal\n if (inArchive && !isTerminal) {\n // Determine suggested fix: Completed if all child stories terminal (epics/sprints), else Abandoned\n let suggestedStatus = 'Abandoned';\n if (item.bucket === 'epics' || item.bucket === 'sprints') {\n const childStories = storiesByEpic.get(item.id) ?? [];\n if (childStories.length > 0 && childStories.every((s) => TERMINAL.has(String(s.fm['status'] ?? '')))) {\n suggestedStatus = 'Completed';\n }\n }\n\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'A',\n suggestedStatus,\n description: `Rule A — archived with non-terminal status '${currentStatus}'`,\n });\n }\n\n // Rule B: in pending-sync/ but status is terminal\n if (inPendingSync && isTerminal) {\n const archivePath = item.rawPath.replace('/pending-sync/', '/archive/');\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'B',\n // No suggestedStatus — Rule B requires file move, not status change\n description: `Rule B — pending-sync with terminal status '${currentStatus}'; run: git mv ${item.rawPath} ${archivePath.replace(/\\/[^/]+$/, '/')}`,\n });\n }\n\n // Rule C: sprint file, non-terminal status, all children of its epics are terminal\n if (item.bucket === 'sprints' && !isTerminal) {\n const epicRefs = item.fm['epics'];\n if (!epicRefs) continue; // No epics key → Rule C does not fire\n const epicsArr = Array.isArray(epicRefs) ? epicRefs : [epicRefs];\n if (epicsArr.length === 0) continue;\n\n let totalChildren = 0;\n let terminalChildren = 0;\n\n for (const epicRef of epicsArr) {\n const epicId = String(epicRef).replace(/^\\[\\[|\\]\\]$/g, '');\n const children = storiesByEpic.get(epicId) ?? [];\n totalChildren += children.length;\n terminalChildren += children.filter((s) => TERMINAL.has(String(s.fm['status'] ?? ''))).length;\n }\n\n if (totalChildren > 0 && totalChildren === terminalChildren) {\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'C',\n suggestedStatus: 'Completed',\n description: `Rule C — ${terminalChildren}/${totalChildren} child stories terminal; suggest Completed`,\n });\n }\n }\n }\n\n // Print report\n if (driftItems.length === 0) {\n stdout('audit-status: clean (0 drift)\\n');\n exit(0);\n return;\n }\n\n for (const d of driftItems) {\n if (d.rule === 'B') {\n // Rule B: emit git mv hint\n const archivePath = d.rawPath.replace('/pending-sync/', '/archive/');\n const archiveDir = archivePath.replace(/\\/[^/]+$/, '/');\n stdout(`${d.id}: ${d.description}\\n`);\n stdout(` git mv ${d.rawPath} ${archiveDir}\\n`);\n } else {\n stdout(`${d.id}: ${d.description}\\n`);\n }\n }\n\n if (!opts.fix) {\n exit(1);\n return;\n }\n\n // --fix mode\n const fixable = driftItems.filter((d) => d.rule !== 'B' && d.suggestedStatus !== undefined);\n const ruleB = driftItems.filter((d) => d.rule === 'B');\n\n if (ruleB.length > 0) {\n for (const d of ruleB) {\n stdout(` (skipping ${d.id}: Rule B requires manual file move, not a status change)\\n`);\n }\n }\n\n if (fixable.length === 0) {\n stdout('audit-status: no auto-fixable items (Rule B items require manual git mv)\\n');\n exit(0);\n return;\n }\n\n // Confirmation\n if (!opts.yes) {\n if (!isTTY) {\n stderr('audit-status: --fix requires --yes in non-interactive mode\\n');\n exit(2);\n return;\n }\n\n // TTY: prompt\n let answer: string;\n if (opts.promptReader) {\n answer = await opts.promptReader();\n } else {\n answer = await new Promise<string>((resolve) => {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`apply ${fixable.length} changes? [y/N] `, (ans) => {\n rl.close();\n resolve(ans);\n });\n });\n }\n\n if (!answer.trim().toLowerCase().startsWith('y')) {\n stdout('aborted\\n');\n exit(2);\n return;\n }\n }\n\n // Apply fixes via line-surgery regex (do NOT round-trip through parseFrontmatter)\n for (const d of fixable) {\n const rawText = fs.readFileSync(d.absPath, 'utf8');\n const updated = applyStatusFix(rawText, d.suggestedStatus!);\n\n if (!opts.quiet) {\n stdout(`--- ${d.rawPath}\\n`);\n stdout(`+++ ${d.rawPath}\\n`);\n stdout(`@@ status change @@\\n`);\n // Show the old and new status line\n const oldLine = rawText.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n const newLine = updated.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n stdout(`-${oldLine}\\n`);\n stdout(`+${newLine}\\n`);\n }\n\n fs.writeFileSync(d.absPath, updated, 'utf8');\n }\n\n stdout(`audit-status: applied ${fixable.length} fix(es)\\n`);\n exit(0);\n}\n\n/**\n * Replace the first `status:` line inside the first `---` YAML front-matter block.\n * Everything else is byte-identical (no round-trip through parseFrontmatter).\n */\nfunction applyStatusFix(rawText: string, newStatus: string): string {\n // Find the closing --- of the frontmatter\n const lines = rawText.split('\\n');\n if (lines[0] !== '---') return rawText; // no frontmatter — leave untouched\n\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return rawText; // malformed — leave untouched\n\n // Replace only the first `status:` line within the frontmatter block\n let replaced = false;\n for (let i = 1; i < closeIdx; i++) {\n if (!replaced && /^status:/.test(lines[i])) {\n lines[i] = `status: \"${newStatus}\"`;\n replaced = true;\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * doctor.ts — STORY-009-04 + STORY-008-06\n *\n * `cleargate doctor` base command + `--check-scaffold` / `--session-start` / `--pricing` modes.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { computeUsd, type DraftTokensInput } from '../lib/pricing.js';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n readDriftState,\n type DriftMap,\n type DriftMapEntry,\n type DriftState,\n} from '../lib/manifest.js';\nimport { shortHash } from '../lib/sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface DoctorCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override the package root for loadPackageManifest (test seam). */\n packageRoot?: string;\n}\n\n/**\n * STORY-014-01: Accumulator passed by reference through all mode handlers.\n * Top-level doctorHandler reads at the end and exits per §3.2 pseudocode:\n * configError → exit(2), blocker → exit(1), else → exit(0).\n */\nexport interface DoctorOutcome {\n configError: boolean;\n blocker: boolean;\n}\n\n/**\n * Flags for `cleargate doctor`.\n *\n * Reserved keys for 008-06 (M3): sessionStart, pricing.\n * All flags are mutually exclusive — selectMode throws when >1 is set.\n */\nexport interface DoctorFlags {\n checkScaffold?: boolean;\n /** Hidden flag: used by the M3 session-start hook; enables daily throttle. */\n sessionStartMode?: boolean;\n verbose?: boolean;\n /** --session-start: emit blocked pending-sync items summary */\n sessionStart?: boolean;\n /** --pricing: compute USD estimate for a work item */\n pricing?: boolean;\n /** File path passed to --pricing <file> */\n pricingFile?: string;\n /** CR-008: --can-edit <file>: exits 0 if allowed, 1 if would-block */\n canEdit?: boolean;\n /** CR-008: the file path argument for --can-edit */\n canEditFile?: string;\n}\n\nexport type DoctorMode = 'check-scaffold' | 'session-start' | 'pricing' | 'hook-health' | 'can-edit';\n\n// ─── Mode dispatcher ──────────────────────────────────────────────────────────\n\n/**\n * Determine which doctor mode to run based on flags.\n *\n * Throws when multiple mutually-exclusive mode flags are set.\n * Returns 'hook-health' when no mode flag is set (default).\n *\n * Exported so STORY-008-06 can add cases without re-editing the switch.\n */\nexport function selectMode(flags: DoctorFlags): DoctorMode {\n const modes: DoctorMode[] = [];\n if (flags.checkScaffold) modes.push('check-scaffold');\n if (flags.sessionStart) modes.push('session-start');\n if (flags.pricing) modes.push('pricing');\n if (flags.canEdit) modes.push('can-edit');\n\n if (modes.length > 1) {\n throw new Error(\n `cleargate doctor: mutually exclusive flags set: ${modes.join(', ')}. Use only one mode flag at a time.`\n );\n }\n\n if (modes.length === 1) {\n return modes[0]!;\n }\n\n return 'hook-health';\n}\n\n// ─── Hook-health default mode ─────────────────────────────────────────────────\n\nconst HOOK_LOG_24H_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Parse a single gate-check.log line.\n * Format: [ISO_TS] stamp=N gate=N ingest=N file=<path>\n * Returns null if the line does not match.\n */\nexport interface HookLogEntry {\n ts: string;\n stamp: number;\n gate: number;\n ingest: number;\n file: string;\n}\n\nexport function parseHookLogLine(line: string): HookLogEntry | null {\n // [2026-04-19T12:00:00Z] stamp=0 gate=1 ingest=0 file=/some/path\n const m = line.match(\n /^\\[([^\\]]+)\\]\\s+stamp=(\\d+)\\s+gate=(\\d+)\\s+ingest=(\\d+)\\s+file=(.+)$/\n );\n if (!m) return null;\n return {\n ts: m[1]!,\n stamp: parseInt(m[2]!, 10),\n gate: parseInt(m[3]!, 10),\n ingest: parseInt(m[4]!, 10),\n file: m[5]!.trim(),\n };\n}\n\nfunction runHookHealth(\n stdout: (s: string) => void,\n cwd: string,\n now?: Date,\n outcome?: DoctorOutcome\n): void {\n // STORY-014-01: config-error — missing .cleargate/ directory\n const cleargateDir = path.join(cwd, '.cleargate');\n if (!fs.existsSync(cleargateDir)) {\n stdout('cleargate misconfigured: no .cleargate/ found. Run: cleargate init');\n if (outcome) outcome.configError = true;\n return;\n }\n\n // STORY-014-01: config-error — missing cleargate-planning/MANIFEST.json\n const manifestPath = path.join(cwd, 'cleargate-planning', 'MANIFEST.json');\n if (!fs.existsSync(manifestPath)) {\n stdout(`cleargate misconfigured: cleargate-planning/MANIFEST.json not found. Run: cleargate init`);\n if (outcome) outcome.configError = true;\n // Do not return — continue with remaining checks\n }\n\n // Minimal hook-config report: check that .claude/settings.json has the\n // SubagentStop hook wired (if the .claude directory exists).\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n if (!fs.existsSync(settingsPath)) {\n stdout('[doctor] No .claude/settings.json found — hook config unavailable.');\n return;\n }\n\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as unknown;\n const hasHooks =\n typeof settings === 'object' &&\n settings !== null &&\n 'hooks' in settings;\n if (hasHooks) {\n stdout('[doctor] Hook config present in .claude/settings.json.');\n } else {\n stdout('[doctor] .claude/settings.json found but no hooks key — SubagentStop hook not wired.');\n }\n } catch {\n stdout('[doctor] .claude/settings.json is not valid JSON — cannot verify hook config.');\n }\n\n // Scan gate-check.log for recent failures\n const logPath = path.join(cwd, '.cleargate', 'hook-log', 'gate-check.log');\n if (!fs.existsSync(logPath)) {\n return;\n }\n\n let logContent: string;\n try {\n logContent = fs.readFileSync(logPath, 'utf-8');\n } catch {\n return;\n }\n\n const nowMs = (now ?? new Date()).getTime();\n const lines = logContent.split('\\n').filter((l) => l.trim().length > 0);\n\n for (const line of lines) {\n const entry = parseHookLogLine(line);\n if (!entry) continue;\n\n const entryMs = new Date(entry.ts).getTime();\n if (isNaN(entryMs)) continue;\n\n // Only consider entries within the last 24h\n if (nowMs - entryMs > HOOK_LOG_24H_MS) continue;\n\n // A failure means ANY step exit code is non-zero\n const isFailing = entry.stamp !== 0 || entry.gate !== 0 || entry.ingest !== 0;\n if (!isFailing) continue;\n\n stdout(\n `\\u26a0 hook failure at ${entry.ts}: stamp=${entry.stamp} gate=${entry.gate} ingest=${entry.ingest} file=${entry.file}`\n );\n }\n}\n\n// ─── Check-scaffold mode ──────────────────────────────────────────────────────\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Throttle decision: returns true when the cache is fresh enough to skip\n * re-computation.\n *\n * Fresh = `now - lastRefreshed < 24h`.\n * Throttle only applies when `sessionStartMode` is set (non-interactive invocation).\n * Exported for unit tests.\n */\nexport function shouldUseCache(\n lastRefreshed: string,\n now: Date,\n sessionStartMode: boolean\n): boolean {\n if (!sessionStartMode) {\n // Interactive mode always recomputes.\n return false;\n }\n const age = now.getTime() - new Date(lastRefreshed).getTime();\n return age < TWENTY_FOUR_HOURS_MS;\n}\n\n/**\n * Format a single non-clean file line for verbose output.\n * `<path> <state> (<installed>→<current> vs <package>)`\n * with 6-char short hashes.\n */\nexport function formatVerboseLine(\n filePath: string,\n entry: DriftMapEntry\n): string {\n const inst = entry.install_sha ? shortHash(entry.install_sha).slice(0, 6) : 'null';\n const curr = entry.current_sha ? shortHash(entry.current_sha).slice(0, 6) : 'null';\n const pkg = entry.package_sha ? shortHash(entry.package_sha).slice(0, 6) : 'null';\n return ` ${filePath} ${entry.state} (${inst}→${curr} vs ${pkg})`;\n}\n\ntype CountsByState = Record<Exclude<DriftState, 'untracked'>, number> & { untracked: number };\n\nfunction zeroCounts(): CountsByState {\n return {\n 'clean': 0,\n 'user-modified': 0,\n 'upstream-changed': 0,\n 'both-changed': 0,\n 'untracked': 0,\n };\n}\n\nasync function runCheckScaffold(\n flags: DoctorFlags,\n cli: DoctorCliOptions,\n cwd: string,\n now: Date,\n stdout: (s: string) => void,\n _stderr: (s: string) => void\n): Promise<void> {\n // 1. Check daily throttle when in session-start mode\n const sessionStartMode = flags.sessionStartMode ?? false;\n const existingState = await readDriftState(cwd);\n\n if (existingState && shouldUseCache(existingState.last_refreshed, now, sessionStartMode)) {\n // Reuse cached result — emit summary from cached data\n emitSummary(existingState.drift, flags.verbose ?? false, stdout);\n return;\n }\n\n // 2. Load manifests\n const pkgManifest = loadPackageManifest({ packageRoot: cli.packageRoot });\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // 3. Compute SHAs + classify\n const driftMap: DriftMap = {};\n\n await Promise.all(\n pkgManifest.files.map(async (entry) => {\n // Silently skip user-artifact tier (EPIC-009 §6 Q8)\n if (entry.tier === 'user-artifact') {\n return;\n }\n\n const currentSha = await computeCurrentSha(entry, cwd);\n const installSha =\n installSnapshot?.files.find((f) => f.path === entry.path)?.sha256 ?? null;\n const pkgSha = entry.sha256;\n const state = classify(pkgSha, installSha, currentSha, entry.tier);\n\n driftMap[entry.path] = {\n state,\n entry,\n install_sha: installSha,\n current_sha: currentSha,\n package_sha: pkgSha,\n };\n })\n );\n\n // 4. Write drift state atomically\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n // 5. Emit summary\n emitSummary(driftMap, flags.verbose ?? false, stdout);\n}\n\nfunction emitSummary(\n driftMap: DriftMap,\n verbose: boolean,\n stdout: (s: string) => void\n): void {\n const counts = zeroCounts();\n for (const entry of Object.values(driftMap)) {\n counts[entry.state]++;\n }\n\n stdout(\n `Scaffold drift: ${counts['user-modified']} user-modified, ` +\n `${counts['upstream-changed']} upstream-changed, ` +\n `${counts['both-changed']} both-changed, ` +\n `${counts['clean']} clean`\n );\n\n if (counts['upstream-changed'] > 0 || counts['both-changed'] > 0) {\n stdout('Run cleargate upgrade to review.');\n }\n\n if (verbose) {\n for (const [filePath, entry] of Object.entries(driftMap)) {\n if (entry.state !== 'clean' && entry.state !== 'untracked') {\n stdout(formatVerboseLine(filePath, entry));\n }\n }\n }\n}\n\n// ─── Session-start mode ───────────────────────────────────────────────────────\n\nconst SESSION_START_MAX_ITEMS = 10;\nconst SESSION_START_MAX_CHARS = 400;\n\ninterface BlockedItem {\n id: string;\n firstCriterionId: string;\n}\n\n/**\n * Coerce `cached_gate_result` into a typed shape.\n * Accepts both the current native-object form (parseFrontmatter via js-yaml)\n * and the legacy JSON-in-a-string form (pre-BUG-001 files).\n */\nfunction parseCachedGateResult(\n raw: unknown\n): { pass: boolean | null; failing_criteria: Array<{ id: string }> } | null {\n if (raw == null) return null;\n\n let parsed: { pass?: boolean | null; failing_criteria?: Array<{ id: string }> } | null = null;\n\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n parsed = raw as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } else if (typeof raw === 'string') {\n try {\n parsed = JSON.parse(raw) as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } catch {\n return null;\n }\n } else {\n return null;\n }\n\n return {\n pass: parsed.pass ?? null,\n failing_criteria: parsed.failing_criteria ?? [],\n };\n}\n\n/**\n * CR-009: Probe the three-branch resolver chain at runtime and emit one\n * `cleargate CLI: <branch> — <one-line>` status line to stdout.\n * This line is emitted ALWAYS (success or failure) so Claude sees which resolver\n * the hooks will use before any hook fires.\n */\nexport function emitResolverStatusLine(\n cwd: string,\n stdout: (s: string) => void\n): void {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n if (fs.existsSync(distCliPath)) {\n stdout(`cleargate CLI: local dist — ${distCliPath}`);\n return;\n }\n\n // Check PATH\n const whichResult = spawnSync('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n stdout('cleargate CLI: PATH (global install) — cleargate');\n return;\n }\n\n // Try to read the pinned version from the live hook script\n let pinVersion = 'unknown';\n const hookPath = path.join(cwd, '.claude', 'hooks', 'stamp-and-gate.sh');\n if (fs.existsSync(hookPath)) {\n try {\n const hookContent = fs.readFileSync(hookPath, 'utf-8');\n // Pattern: # cleargate-pin: 0.5.0\n const pinMatch = hookContent.match(/^#\\s*cleargate-pin:\\s*(\\S+)\\s*$/m);\n if (pinMatch?.[1]) {\n pinVersion = pinMatch[1];\n } else {\n // Fallback: look for npx -y \"@cleargate/cli@X.Y.Z\"\n const npxMatch = hookContent.match(/@cleargate\\/cli@([^\\s\"']+)/);\n if (npxMatch?.[1]) pinVersion = npxMatch[1];\n }\n } catch {\n // ignore\n }\n }\n\n if (pinVersion === 'unknown') {\n stdout('cleargate CLI: \\u{1F534} not resolvable — hooks will no-op. Fix: npm i -g cleargate or npx cleargate doctor');\n } else {\n stdout(`cleargate CLI: npx @cleargate/cli@${pinVersion} (cold-start ~600ms first call)`);\n }\n}\n\n/**\n * CR-008: planning-first reminder block text.\n * Emitted when pending-sync has zero approved stories AND no sprint-active sentinel.\n */\nexport const PLANNING_FIRST_REMINDER = `Triage first, draft second:\nBefore any Edit/Write that creates user-facing code, you must:\n (1) classify the request (Epic / Story / CR / Bug),\n (2) draft a work item under .cleargate/delivery/pending-sync/ from .cleargate/templates/,\n (3) halt at Gate 1 (Proposal approval) for human sign-off.\nBypass this only if the user has explicitly waived planning in this conversation.`;\n\nexport async function runSessionStart(\n cwd: string,\n stdout: (s: string) => void,\n outcome?: DoctorOutcome\n): Promise<void> {\n // CR-009: emit resolver-status line ALWAYS (before the blocked-items list).\n // STORY-014-01: if resolver is \"not resolvable\", set configError.\n const resolverLines: string[] = [];\n emitResolverStatusLine(cwd, (line) => {\n stdout(line);\n resolverLines.push(line);\n });\n // Check if resolver completely failed (🔴 not resolvable branch)\n if (outcome && resolverLines.some((l) => l.includes('\\u{1F534}'))) {\n outcome.configError = true;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // Directory doesn't exist or unreadable — nothing to report\n return;\n }\n\n const blocked: BlockedItem[] = [];\n let hasApprovedStory = false;\n\n for (const filePath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n // CR-008: track approved stories for planning-first gate\n if (fm['approved'] === true) {\n hasApprovedStory = true;\n }\n\n const gate = parseCachedGateResult(fm['cached_gate_result']);\n if (!gate || gate.pass !== false) continue;\n\n // Determine item ID from frontmatter\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id', 'sprint_id'];\n let itemId = '';\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim()) {\n itemId = val.trim();\n break;\n }\n }\n if (!itemId) {\n // Fallback: use filename stem\n itemId = path.basename(filePath, '.md');\n }\n\n const firstCriterionId =\n gate.failing_criteria.length > 0 ? (gate.failing_criteria[0]?.id ?? '') : '';\n\n blocked.push({ id: itemId, firstCriterionId });\n }\n\n // CR-008: check sprint-active sentinel\n const activesentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n const sprintActive = fs.existsSync(activesentinel);\n\n // CR-008: emit planning-first reminder when no approved stories AND no active sprint\n const shouldRemind = !hasApprovedStory && !sprintActive;\n if (shouldRemind) {\n stdout(PLANNING_FIRST_REMINDER);\n if (blocked.length > 0) {\n // Separator before blocked items\n stdout('');\n }\n }\n\n if (blocked.length === 0) {\n return;\n }\n\n // STORY-014-01: blocked items present → set blocker flag\n if (outcome) outcome.blocker = true;\n\n const overflow = blocked.length > SESSION_START_MAX_ITEMS\n ? blocked.length - SESSION_START_MAX_ITEMS\n : 0;\n const visible = blocked.slice(0, SESSION_START_MAX_ITEMS);\n\n const lines: string[] = [`${blocked.length} items blocked:`];\n for (const item of visible) {\n const line = item.firstCriterionId\n ? ` ${item.id}: ${item.firstCriterionId}`\n : ` ${item.id}`;\n lines.push(line);\n }\n if (overflow > 0) {\n lines.push(`…and ${overflow} more — run cleargate doctor for full list`);\n }\n\n let output = lines.join('\\n');\n\n // Cap at SESSION_START_MAX_CHARS (100-token proxy)\n if (output.length > SESSION_START_MAX_CHARS) {\n output = output.slice(0, SESSION_START_MAX_CHARS - 3) + '...';\n }\n\n stdout(output);\n}\n\n// ─── Pricing mode ─────────────────────────────────────────────────────────────\n\nexport async function runPricing(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n if (!filePath) {\n // STORY-014-01: missing <file> argument is a config/input error → exit(2)\n stderr('cleargate doctor --pricing: missing <file> argument');\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);\n\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf-8');\n } catch {\n // STORY-014-01: cannot read file is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot read file: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n if (!raw.trimStart().startsWith('---')) {\n // STORY-014-01: file has no frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: file has no frontmatter: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n // STORY-014-01: cannot parse frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot parse frontmatter in: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const draftTokensRaw = fm['draft_tokens'];\n if (!draftTokensRaw) {\n // STORY-014-01: draft_tokens unpopulated is a blocker (content exists, state incomplete) → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let draftTokens: DraftTokensInput & { model: string | null };\n if (typeof draftTokensRaw === 'object' && !Array.isArray(draftTokensRaw)) {\n draftTokens = draftTokensRaw as DraftTokensInput & { model: string | null };\n } else if (typeof draftTokensRaw === 'string') {\n try {\n draftTokens = JSON.parse(draftTokensRaw) as DraftTokensInput & { model: string | null };\n } catch {\n // STORY-014-01: unparseable draft_tokens is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n } else {\n // STORY-014-01: unexpected type is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n // Check if tokens are actually populated\n if (\n draftTokens.input === null &&\n draftTokens.output === null &&\n draftTokens.cache_read === null &&\n draftTokens.cache_creation === null\n ) {\n // STORY-014-01: all null values → blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n const { usd, unknownModel } = computeUsd(draftTokens);\n const model = draftTokens.model ?? 'unknown';\n\n if (unknownModel) {\n stderr(`cleargate doctor --pricing: unknown model '${model}' — no pricing data available`);\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n const fileName = path.basename(absPath);\n\n stdout(\n `${fileName}: ${model} — input:${input} output:${output} cache_read:${cacheRead} cache_creation:${cacheCreation} ≈ $${usd.toFixed(4)}`\n );\n}\n\n// ─── Can-edit mode (CR-008 Phase B) ──────────────────────────────────────────\n\n/**\n * CR-008: reasons why an edit would be blocked.\n */\nexport type CanEditBlockReason = 'no_approved_stories' | 'file_not_in_implementation_files';\n\n/**\n * CR-008: result of the can-edit check.\n */\nexport interface CanEditResult {\n allowed: boolean;\n reason?: CanEditBlockReason;\n}\n\n/**\n * CR-008: simple glob-style match.\n * Supports `*` (any characters except `/`) and `**` (any characters including `/`).\n */\nexport function globMatch(pattern: string, filePath: string): boolean {\n // Normalise separators\n const normalPattern = pattern.replace(/\\\\/g, '/');\n const normalFile = filePath.replace(/\\\\/g, '/');\n\n // Escape regex specials except * and ?\n const regexStr = normalPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '\u0000') // placeholder for **\n .replace(/\\*/g, '[^/]*')\n .replace(/\u0000/g, '.*');\n\n const re = new RegExp(`^${regexStr}$`);\n return re.test(normalFile);\n}\n\n/**\n * CR-008: check whether editing `filePath` is permitted.\n *\n * Logic:\n * 1. If sprint-active sentinel exists → always allowed.\n * 2. Read pending-sync/*.md; for each with approved: true:\n * a. If no implementation_files field → treat as \"any approved story → allow\".\n * b. If implementation_files present → glob-match filePath against each pattern.\n * 3. If zero approved stories → block with reason 'no_approved_stories'.\n * 4. If approved stories exist but filePath not covered → block with 'file_not_in_implementation_files'.\n */\nexport async function runCanEdit(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n // Sprint-active sentinel → always allow\n const activeSentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n if (fs.existsSync(activeSentinel)) {\n stdout('allowed: sprint active');\n return;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // No pending-sync dir → no approved stories → blocker → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let hasApprovedStory = false;\n let coveredByStory = false;\n\n for (const storyPath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(storyPath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n if (fm['approved'] !== true) continue;\n\n hasApprovedStory = true;\n\n const implFilesRaw = fm['implementation_files'];\n if (implFilesRaw === undefined || implFilesRaw === null) {\n // No implementation_files field → any approved story covers any file\n coveredByStory = true;\n break;\n }\n\n if (Array.isArray(implFilesRaw)) {\n for (const pattern of implFilesRaw) {\n if (typeof pattern !== 'string') continue;\n if (globMatch(pattern, filePath)) {\n coveredByStory = true;\n break;\n }\n }\n }\n\n if (coveredByStory) break;\n }\n\n if (!hasApprovedStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n if (!coveredByStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: file_not_in_implementation_files');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n stdout('allowed');\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function doctorHandler(\n flags: DoctorFlags,\n cli?: DoctorCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n\n // STORY-014-01: outcome accumulator — each mode pushes booleans here.\n // At the end, we exit per §3.2 pseudocode:\n // configError → exit(2), blocker → exit(1), else → exit(0).\n const outcome: DoctorOutcome = { configError: false, blocker: false };\n\n // Track whether exit() was already called by a mode (e.g. runPricing early error)\n // so we don't double-exit from the final outcome block.\n let exitedEarly = false;\n const wrappedExit = (code: number): never => {\n exitedEarly = true;\n return exit(code);\n };\n\n let mode: DoctorMode;\n try {\n mode = selectMode(flags);\n } catch (err) {\n // STORY-014-01: mutually exclusive flags is a config error → exit(2)\n stderr((err as Error).message);\n exit(2);\n return;\n }\n\n switch (mode) {\n case 'check-scaffold':\n await runCheckScaffold(flags, cli ?? {}, cwd, now, stdout, stderr);\n break;\n\n case 'hook-health':\n runHookHealth(stdout, cwd, now, outcome);\n break;\n\n case 'session-start':\n await runSessionStart(cwd, stdout, outcome);\n break;\n\n case 'pricing':\n await runPricing(flags.pricingFile ?? '', cwd, stdout, stderr, wrappedExit, outcome);\n break;\n\n case 'can-edit':\n await runCanEdit(flags.canEditFile ?? '', cwd, stdout, wrappedExit, outcome);\n break;\n\n default: {\n const exhaustiveCheck: never = mode;\n stderr(`cleargate doctor: unknown mode '${String(exhaustiveCheck)}'`);\n // STORY-014-01: unknown mode is a config error → exit(2)\n exit(2);\n return;\n }\n }\n\n // STORY-014-01: §3.2 pseudocode exit-code computation.\n // If a mode called exit() early (e.g. runPricing on bad input), don't double-exit.\n if (exitedEarly) return;\n\n if (outcome.configError) {\n exit(2);\n } else if (outcome.blocker) {\n exit(1);\n } else {\n exit(0);\n }\n}\n","/**\n * pricing.ts — STORY-008-06\n *\n * USD pricing table for Claude models (per 1M tokens).\n * Numbers from Anthropic public pricing as of 2026-04-19.\n * No network. No config file. Numbers live in source.\n */\n\nexport interface ModelPricing {\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n}\n\n/**\n * Pricing table: USD per 1,000,000 tokens.\n *\n * claude-opus-4-7: $15 input / $75 output / $1.50 cache_read / $18.75 cache_creation\n * claude-sonnet-4-5: $3 input / $15 output / $0.30 cache_read / $3.75 cache_creation\n * claude-haiku-4-5: $0.80 input / $4 output / $0.08 cache_read / $1 cache_creation\n */\nexport const PRICING_TABLE: Record<string, ModelPricing> = {\n 'claude-opus-4-7': {\n input: 15.0,\n output: 75.0,\n cache_read: 1.5,\n cache_creation: 18.75,\n },\n 'claude-sonnet-4-5': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-sonnet-4-6': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-haiku-4-5': {\n input: 0.8,\n output: 4.0,\n cache_read: 0.08,\n cache_creation: 1.0,\n },\n};\n\nexport interface DraftTokensInput {\n input: number | null;\n output: number | null;\n cache_read: number | null;\n cache_creation: number | null;\n model: string | null;\n}\n\nexport interface ComputeUsdResult {\n usd: number;\n unknownModel: boolean;\n}\n\n/**\n * Compute USD cost from draft_tokens and model.\n *\n * If modelOverride is provided, it takes precedence over draftTokens.model.\n * Unknown model → {usd: 0, unknownModel: true}.\n * All token counts default to 0 if null.\n */\nexport function computeUsd(\n draftTokens: DraftTokensInput,\n modelOverride?: string\n): ComputeUsdResult {\n const model = modelOverride ?? draftTokens.model ?? '';\n const pricing = PRICING_TABLE[model];\n\n if (!pricing) {\n return { usd: 0, unknownModel: true };\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n\n const usd =\n (input * pricing.input +\n output * pricing.output +\n cacheRead * pricing.cache_read +\n cacheCreation * pricing.cache_creation) /\n 1_000_000;\n\n return { usd, unknownModel: false };\n}\n","/**\n * gate.ts — `cleargate gate check|explain|qa|arch` command handlers.\n *\n * STORY-008-03: Wires readiness-predicates.evaluate() + frontmatter-cache into\n * two Commander subcommands (check + explain).\n *\n * STORY-013-08: Extends with gate qa|arch subcommands that shell out via\n * run_script.sh to pre_gate_runner.sh. Both are v1-inert.\n *\n * FLASHCARD #cli #commander #optional-key: opts.transition may be undefined — strip key.\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout`, `stderr` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * Output is agent-facing: only ❌ / ⚠ / ✅ emoji; no ANSI color codes.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { evaluate } from '../lib/readiness-predicates.js';\nimport type { ParsedDoc } from '../lib/readiness-predicates.js';\nimport { readCachedGate, writeCachedGate } from '../lib/frontmatter-cache.js';\nimport type { CachedGate } from '../lib/frontmatter-cache.js';\nimport {\n detectWorkItemTypeFromFm,\n WORK_ITEM_TRANSITIONS,\n} from '../lib/work-item-type.js';\nimport type { WorkItemType } from '../lib/work-item-type.js';\nimport { toIsoSecond } from '../lib/frontmatter-yaml.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to readiness-gates.md (test seam). */\n gatesDocPath?: string;\n /** Override path to wiki index (test seam). */\n wikiIndexPath?: string;\n}\n\n// ─── Internal gate-block shape ────────────────────────────────────────────────\n\ninterface GateCriterion {\n id: string;\n check: string;\n}\n\ninterface GateBlock {\n work_item_type: string;\n transition: string;\n severity: 'advisory' | 'enforcing';\n criteria: GateCriterion[];\n}\n\n// ─── Gate document loader ─────────────────────────────────────────────────────\n\n/**\n * Load and parse all fenced ```yaml blocks from readiness-gates.md.\n * Each block's yaml.load() returns an array — unwrap [0] per FLASHCARD.\n */\nfunction loadGateBlocks(gatesDocPath: string): GateBlock[] {\n const raw = fs.readFileSync(gatesDocPath, 'utf8');\n const blocks: GateBlock[] = [];\n\n // Match all fenced ```yaml ... ``` blocks\n const fenceRe = /^```yaml\\n([\\s\\S]*?)^```/gm;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(raw)) !== null) {\n const yamlContent = match[1]!;\n const parsed = yaml.load(yamlContent);\n // Per FLASHCARD: readiness-gates.md fenced yaml blocks are YAML lists; unwrap [0]\n const block = Array.isArray(parsed) ? parsed[0] : parsed;\n if (\n block &&\n typeof block === 'object' &&\n 'work_item_type' in block &&\n 'transition' in block &&\n 'severity' in block &&\n 'criteria' in block\n ) {\n blocks.push(block as GateBlock);\n }\n }\n return blocks;\n}\n\n/**\n * Find the gate block matching a work-item type + transition.\n */\nfunction findGate(\n blocks: GateBlock[],\n type: WorkItemType,\n transition: string,\n): GateBlock | null {\n return blocks.find(\n (b) => b.work_item_type === type && b.transition === transition,\n ) ?? null;\n}\n\n/**\n * Infer the default transition for a work-item type given the current cached gate state.\n * - If cached_gate_result is absent or failing → return first transition.\n * - If cached_gate_result.pass === true and there's a next transition → return next.\n * - Otherwise return first transition.\n */\nfunction inferTransition(\n type: WorkItemType,\n cachedGate: CachedGate | null,\n): string {\n const transitions = WORK_ITEM_TRANSITIONS[type];\n if (!cachedGate || !cachedGate.pass) {\n return transitions[0]!;\n }\n // Find next unpassed transition\n // We don't know which transition was last checked from cache alone;\n // for Epic: if cached pass=true, assume first is done → pick second.\n // For types with only one transition: always return that one.\n if (transitions.length === 1) {\n return transitions[0]!;\n }\n // Multi-transition (Epic): if cached gate passes, infer next\n return transitions[1]!;\n}\n\n// ─── gateCheckHandler ─────────────────────────────────────────────────────────\n\nexport async function gateCheckHandler(\n file: string,\n opts: { verbose?: boolean; transition?: string },\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n const nowFn = cli?.now ?? (() => new Date());\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Parse the document\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch (err) {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Detect work-item type from frontmatter\n const detectedType = detectWorkItemTypeFromFm(fm);\n if (!detectedType) {\n stderrFn(`[cleargate gate] error: unable to detect work-item type from frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Load gates document\n const projectRoot = cwd;\n const gatesDocPath = cli?.gatesDocPath\n ?? path.join(projectRoot, '.cleargate', 'knowledge', 'readiness-gates.md');\n\n if (!fs.existsSync(gatesDocPath)) {\n stderrFn(`[cleargate gate] error: readiness-gates.md not found at: ${gatesDocPath}`);\n return exitFn(1);\n }\n\n let gateBlocks: GateBlock[];\n try {\n gateBlocks = loadGateBlocks(gatesDocPath);\n } catch (err) {\n stderrFn(`[cleargate gate] error: failed to parse readiness-gates.md: ${String(err)}`);\n return exitFn(1);\n }\n\n // Read current cached gate for transition inference\n const cachedGate = await readCachedGate(absPath);\n\n // Determine transition\n const transition = opts.transition ?? inferTransition(detectedType, cachedGate);\n\n // Find the matching gate\n const gate = findGate(gateBlocks, detectedType, transition);\n if (!gate) {\n stderrFn(\n `[cleargate gate] error: no gate definition found for ${detectedType}.${transition}`,\n );\n return exitFn(1);\n }\n\n const wikiIndexPath = cli?.wikiIndexPath;\n const parsedDoc: ParsedDoc = { fm, body, absPath };\n const evalOpts = { projectRoot, ...(wikiIndexPath ? { wikiIndexPath } : {}) };\n\n // Evaluate each criterion\n const failingCriteria: { id: string; detail: string }[] = [];\n const allResults: Array<{ id: string; pass: boolean; detail: string }> = [];\n\n for (const criterion of gate.criteria) {\n let result: { pass: boolean; detail: string };\n try {\n result = evaluate(criterion.check, parsedDoc, evalOpts);\n } catch (err) {\n result = { pass: false, detail: `predicate error: ${String(err)}` };\n }\n allResults.push({ id: criterion.id, ...result });\n if (!result.pass) {\n failingCriteria.push({ id: criterion.id, detail: result.detail });\n }\n }\n\n const overallPass = failingCriteria.length === 0;\n const lastGateCheck = toIsoSecond(nowFn());\n\n // Write cached gate result\n const cacheResult: CachedGate = {\n pass: overallPass,\n failing_criteria: failingCriteria,\n last_gate_check: lastGateCheck,\n };\n await writeCachedGate(absPath, cacheResult, { now: nowFn });\n\n // Format and emit output\n const isAdvisory = gate.severity === 'advisory';\n const headerLine = `Gate: ${detectedType}.${transition} (${gate.severity})`;\n stdoutFn(headerLine);\n\n if (overallPass) {\n stdoutFn(`\\u2705 ${detectedType}.${transition} passed (${gate.criteria.length} criteria)`);\n } else {\n for (const r of allResults) {\n if (!r.pass) {\n if (isAdvisory) {\n stdoutFn(`\\u26A0 ${r.id}: ${r.detail} (advisory)`);\n } else {\n stdoutFn(`\\u274C ${r.id}: ${r.detail}`);\n }\n }\n if (opts.verbose) {\n // In verbose mode, emit full detail per criterion\n stdoutFn(` [${r.pass ? 'pass' : 'fail'}] ${r.id}: ${r.detail}`);\n }\n }\n }\n\n // Severity-based exit routing\n if (!overallPass && !isAdvisory) {\n return exitFn(1);\n }\n // advisory or pass → exit 0 (implicit return)\n}\n\n// ─── gateExplainHandler ───────────────────────────────────────────────────────\n\nexport async function gateExplainHandler(\n file: string,\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Read cached gate result — read-only, no evaluate calls\n const cached = await readCachedGate(absPath);\n\n if (!cached) {\n stdoutFn('no gate check cached; run: cleargate gate check <file>');\n return;\n }\n\n // Parse frontmatter to get type info (read-only — no writes)\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n const detectedType = detectWorkItemTypeFromFm(fm) ?? 'unknown';\n\n // Render ≤50-LLM-token summary\n const failingIds = cached.failing_criteria.map((c) => c.id).join(', ');\n const statusStr = cached.pass ? 'pass' : 'fail';\n const summary = failingIds\n ? `${detectedType}: ${statusStr} at ${cached.last_gate_check}; ${cached.failing_criteria.length} failing: ${failingIds}`\n : `${detectedType}: ${statusStr} at ${cached.last_gate_check}`;\n\n stdoutFn(summary);\n}\n\n// ─── v2 gate qa|arch handlers ─────────────────────────────────────────────────\n\n/**\n * Options for v2 gate subcommands (qa + arch).\n * Extends GateCliOptions with execution-mode seams.\n */\nexport interface GateV2CliOptions extends GateCliOptions, ExecutionModeOptions {\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Sprint ID for execution_mode discovery. */\n sprintId?: string;\n}\n\nfunction resolveRunScriptForGate(opts: GateV2CliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * `cleargate gate qa <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh qa <worktree> <branch>`\n */\nexport function gateQaHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'qa', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate qa] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n/**\n * `cleargate gate arch <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh arch <worktree> <branch>`\n */\nexport function gateArchHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'arch', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate arch] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * execution-mode.ts — reads `execution_mode` from a Sprint Plan frontmatter.\n *\n * STORY-013-08: provides a test seam via `sprintFilePath` override so tests\n * can inject synthetic SPRINT-99.md fixture without touching live state.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests — keep exitFn\n * only at handler top-level (not inside helper functions).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nconst V1_INERT_MESSAGE =\n 'v1 mode active — command inert. Set execution_mode: v2 in sprint frontmatter to enable.';\n\nexport type ExecutionMode = 'v1' | 'v2';\n\nexport interface ExecutionModeOptions {\n /** Absolute path to the sprint file. Overrides auto-discovery. */\n sprintFilePath?: string;\n /** Working directory for relative-path resolution. Defaults to process.cwd(). */\n cwd?: string;\n /**\n * When true and sprintId is absent or 'SPRINT-UNKNOWN', read\n * `.cleargate/sprint-runs/.active` for the sprint ID before falling through\n * to v1-inert. Callers can also use `resolveSprintIdFromSentinel` directly.\n */\n sentinelFallback?: boolean;\n}\n\n/**\n * Parse just the YAML frontmatter from a markdown file.\n * Returns the raw frontmatter block as a plain object.\n * On any parse failure, returns an empty object.\n */\nfunction parseFrontmatterSimple(raw: string): Record<string, unknown> {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(raw);\n if (!match) return {};\n const block = match[1]!;\n const result: Record<string, unknown> = {};\n for (const line of block.split('\\n')) {\n const kv = /^([^:]+):\\s*(.*)$/.exec(line.trim());\n if (!kv) continue;\n const key = kv[1]!.trim();\n const val = kv[2]!.trim().replace(/^[\"']|[\"']$/g, '');\n result[key] = val;\n }\n return result;\n}\n\n/**\n * Discover the sprint file for a given sprint ID.\n * Looks in `.cleargate/delivery/pending-sync/SPRINT-{id}_*.md`\n * and `.cleargate/delivery/archive/SPRINT-{id}_*.md`.\n *\n * Returns null if no matching file is found.\n */\nfunction discoverSprintFile(sprintId: string, cwd: string): string | null {\n const searchDirs = [\n path.join(cwd, '.cleargate', 'delivery', 'pending-sync'),\n path.join(cwd, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of searchDirs) {\n if (!fs.existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n const prefix = `${sprintId}_`;\n for (const entry of entries) {\n if (entry.startsWith(prefix) && entry.endsWith('.md')) {\n return path.join(dir, entry);\n }\n // Also allow exact match like SPRINT-99.md (test fixtures)\n if (entry === `${sprintId}.md`) {\n return path.join(dir, entry);\n }\n }\n }\n\n return null;\n}\n\n/**\n * Read the active sprint ID from `.cleargate/sprint-runs/.active`.\n * Returns null if the file does not exist or is empty after trim.\n *\n * This is the primary API for sentinel-based sprint discovery. Callers that\n * need a fallback chain use:\n * const sprintId = argSprintId ?? resolveSprintIdFromSentinel(cwd);\n * const mode = readSprintExecutionMode(sprintId ?? 'SPRINT-UNKNOWN', { cwd });\n */\nexport function resolveSprintIdFromSentinel(cwd?: string): string | null {\n const resolvedCwd = cwd ?? process.cwd();\n const sentinelPath = path.join(resolvedCwd, '.cleargate', 'sprint-runs', '.active');\n try {\n const content = fs.readFileSync(sentinelPath, 'utf8').trim();\n return content.length > 0 ? content : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read the `execution_mode` field from a sprint file.\n *\n * Resolution order:\n * 1. If `opts.sprintFilePath` is set, use that directly.\n * 2. Otherwise, discover the file by sprintId in `.cleargate/delivery/`.\n * 3. If `opts.sentinelFallback` is true and sprintId is absent or\n * 'SPRINT-UNKNOWN', read `.cleargate/sprint-runs/.active` and substitute.\n * 4. If no file found, return \"v1\" (safe default per §19.5).\n */\nexport function readSprintExecutionMode(\n sprintId: string,\n opts: ExecutionModeOptions = {},\n): ExecutionMode {\n const cwd = opts.cwd ?? process.cwd();\n\n // Sentinel fallback: when sprintId is absent or unknown, try .active\n let resolvedSprintId = sprintId;\n if (opts.sentinelFallback && (!resolvedSprintId || resolvedSprintId === 'SPRINT-UNKNOWN')) {\n const sentinelId = resolveSprintIdFromSentinel(cwd);\n if (sentinelId) {\n resolvedSprintId = sentinelId;\n }\n }\n\n let filePath: string | null = opts.sprintFilePath ?? null;\n if (!filePath) {\n filePath = discoverSprintFile(resolvedSprintId, cwd);\n }\n\n if (!filePath || !fs.existsSync(filePath)) {\n // Default to v1 — safe, no behavioral change (§19.5)\n return 'v1';\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf8');\n } catch {\n return 'v1';\n }\n\n const fm = parseFrontmatterSimple(raw);\n const mode = fm['execution_mode'];\n\n if (mode === 'v2') return 'v2';\n return 'v1';\n}\n\n/**\n * Print the v1-inert message and exit 0.\n * The caller is responsible for calling this when execution_mode is v1.\n */\nexport function printInertAndExit(\n stdoutFn: (s: string) => void,\n exitFn: (code: number) => never,\n): never {\n stdoutFn(V1_INERT_MESSAGE);\n return exitFn(0);\n}\n\nexport { V1_INERT_MESSAGE };\n","/**\n * STORY-008-02: Predicate evaluator for ClearGate readiness gates.\n * Supports exactly 6 closed-set predicate shapes. Any other shape throws.\n * Sandboxed: no shell-out, no network, read-only FS limited to projectRoot.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ParsedPredicate =\n | { kind: 'frontmatter'; ref: string; field: string; op: '==' | '!=' | '>=' | '<='; value: string | number | boolean }\n | { kind: 'body-contains'; needle: string; negated: boolean }\n | { kind: 'marker-absence'; marker: 'TBD' | 'TODO' | 'FIXME' }\n | { kind: 'section'; index: number; count: { op: '>=' | '==' | '>'; n: number }; itemType: 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item' }\n | { kind: 'file-exists'; path: string }\n | { kind: 'link-target-exists'; id: string }\n | { kind: 'status-of'; id: string; value: string };\n\nexport interface ParsedDoc {\n fm: Record<string, unknown>;\n body: string;\n absPath: string;\n}\n\nexport interface EvalOptions {\n projectRoot?: string;\n wikiIndexPath?: string;\n}\n\n// ─── Parser ───────────────────────────────────────────────────────────────────\n\n/**\n * Parse a predicate string into a typed ParsedPredicate.\n * Throws with \"unsupported predicate shape: <src>\" on any unrecognized input.\n * Target: ≤150 LoC, hand-rolled tokenizer + switch on first token.\n */\nexport function parsePredicate(src: string): ParsedPredicate {\n const s = src.trim();\n\n // 1. frontmatter(<ref>).<field> <op> <value>\n const fmMatch = s.match(\n /^frontmatter\\(([^)]*)\\)\\.(\\w+)\\s*(==|!=|>=|<=)\\s*(.+)$/\n );\n if (fmMatch) {\n const ref = fmMatch[1]!.trim();\n if (ref === '') throw new Error(`unsupported predicate shape: ${src}`);\n const field = fmMatch[2]!;\n const op = fmMatch[3] as '==' | '!=' | '>=' | '<=';\n const rawVal = fmMatch[4]!.trim();\n const value = parseValue(rawVal);\n return { kind: 'frontmatter', ref, field, op, value };\n }\n\n // 2a. body does not contain marker '<id>' — new marker-absence shape\n const markerNotMatch = s.match(/^body does not contain marker ['\"]([A-Z]+)['\"]$/);\n if (markerNotMatch) {\n const marker = markerNotMatch[1]!;\n if (marker !== 'TBD' && marker !== 'TODO' && marker !== 'FIXME') {\n throw new Error(`unsupported predicate shape: ${src}`);\n }\n return { kind: 'marker-absence', marker };\n }\n\n // 2b. body does not contain '<needle>'\n const bodyNotMatch = s.match(/^body does not contain ['\"](.+)['\"]$/);\n if (bodyNotMatch) {\n return { kind: 'body-contains', needle: bodyNotMatch[1]!, negated: true };\n }\n\n // 2c. body contains '<needle>'\n const bodyMatch = s.match(/^body contains ['\"](.+)['\"]$/);\n if (bodyMatch) {\n return { kind: 'body-contains', needle: bodyMatch[1]!, negated: false };\n }\n\n // 3. section(<N>) has <count> <item-type>\n const sectionMatch = s.match(\n /^section\\((\\d+)\\) has (≥|>=|==|>)(\\d+) (checked-checkbox|unchecked-checkbox|listed-item)$/\n );\n if (sectionMatch) {\n const index = parseInt(sectionMatch[1]!, 10);\n const opChar = sectionMatch[2]!;\n const n = parseInt(sectionMatch[3]!, 10);\n const itemType = sectionMatch[4] as 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item';\n let countOp: '>=' | '==' | '>';\n if (opChar === '≥' || opChar === '>=') countOp = '>=';\n else if (opChar === '>') countOp = '>';\n else countOp = '==';\n return { kind: 'section', index, count: { op: countOp, n }, itemType };\n }\n\n // 4. file-exists(<path>)\n const fileExistsMatch = s.match(/^file-exists\\((.+)\\)$/);\n if (fileExistsMatch) {\n const filePath = fileExistsMatch[1]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'file-exists', path: filePath };\n }\n\n // 5. link-target-exists([[ID]])\n const linkMatch = s.match(/^link-target-exists\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)$/);\n if (linkMatch) {\n return { kind: 'link-target-exists', id: linkMatch[1]! };\n }\n\n // 6. status-of([[ID]]) == <value>\n const statusMatch = s.match(/^status-of\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)\\s*==\\s*(.+)$/);\n if (statusMatch) {\n const id = statusMatch[1]!;\n const value = statusMatch[2]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'status-of', id, value };\n }\n\n throw new Error(`unsupported predicate shape: ${src}`);\n}\n\n/** Parse a YAML scalar value string to string | number | boolean. */\nfunction parseValue(raw: string): string | number | boolean {\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n if (raw === 'null') return 'null'; // treat null as string \"null\" for comparison\n const num = Number(raw);\n if (!isNaN(num) && raw !== '') return num;\n // Strip quotes\n return raw.replace(/^['\"]|['\"]$/g, '');\n}\n\n// ─── Evaluator ────────────────────────────────────────────────────────────────\n\n/**\n * Evaluate a predicate string against a document. Returns {pass, detail}.\n * Throws on malformed predicate (delegate to parsePredicate).\n */\nexport function evaluate(\n predicate: string,\n doc: ParsedDoc,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const parsed = parsePredicate(predicate);\n const projectRoot = opts?.projectRoot ?? process.cwd();\n\n switch (parsed.kind) {\n case 'frontmatter':\n return evalFrontmatter(parsed, doc, projectRoot);\n case 'body-contains':\n return evalBodyContains(parsed, doc);\n case 'marker-absence':\n return evalMarkerAbsence(parsed, doc);\n case 'section':\n return evalSection(parsed, doc);\n case 'file-exists':\n return evalFileExists(parsed, projectRoot);\n case 'link-target-exists':\n return evalLinkTargetExists(parsed, opts);\n case 'status-of':\n return evalStatusOf(parsed, opts, projectRoot);\n }\n}\n\n// ─── Frontmatter evaluator ────────────────────────────────────────────────────\n\nfunction evalFrontmatter(\n parsed: Extract<ParsedPredicate, { kind: 'frontmatter' }>,\n doc: ParsedDoc,\n projectRoot: string\n): { pass: boolean; detail: string } {\n let fm: Record<string, unknown>;\n\n if (parsed.ref === '.') {\n fm = doc.fm;\n } else {\n // ref is a frontmatter key whose value is a path to another document\n const refVal = doc.fm[parsed.ref];\n if (refVal === undefined || refVal === null) {\n return {\n pass: false,\n detail: `frontmatter key '${parsed.ref}' is missing or null in ${doc.absPath}`,\n };\n }\n\n // Sub-fix #1 (BUG-008): prose-vs-path heuristic.\n // If the value looks like prose (contains a space, em-dash, en-dash, colon, parens,\n // newline, or exceeds 200 chars), it is not a file path. In that case, pass the gate\n // only when the parent document declares an explicit proposal-gate waiver via any of:\n // - proposal_gate_waiver: <truthy> — explicit opt-in waiver field\n // - approved_by: <non-empty> AND approved_at: <non-empty> — existing approval fields\n // A plain path like \"PROPOSAL-999.md\" (no spaces, ≤200 chars, no special prose chars)\n // still falls through to the existing resolveLinkedPath logic — preserving the R-08\n // regression guarantee that broken file references still fail.\n const refStr = String(refVal);\n const looksLikeProse =\n refStr.length > 200 ||\n /[ —–:()\\n]/.test(refStr); // space, em-dash, en-dash, colon, parens, newline\n if (looksLikeProse) {\n // Signal 1: explicit proposal_gate_waiver field\n const waiver = doc.fm['proposal_gate_waiver'];\n const hasExplicitWaiver =\n waiver !== null && waiver !== undefined && waiver !== false &&\n String(waiver).trim() !== '' && String(waiver).trim() !== 'false';\n // Signal 2: approved_by + approved_at both set (existing approval fields)\n const approvedBy = doc.fm['approved_by'];\n const approvedAt = doc.fm['approved_at'];\n const hasApprovalFields =\n approvedBy !== null && approvedBy !== undefined && String(approvedBy).trim() !== '' &&\n approvedAt !== null && approvedAt !== undefined && String(approvedAt).trim() !== '';\n const hasWaiver = hasExplicitWaiver || hasApprovalFields;\n if (hasWaiver) {\n return {\n pass: true,\n detail: `context_source is prose; proposal-gate waiver per frontmatter approved_by/approved_at`,\n };\n }\n return {\n pass: false,\n detail: `context_source is prose but no proposal_gate_waiver (approved_by + approved_at) found in frontmatter`,\n };\n }\n\n // Resolve the path\n const linkedPath = resolveLinkedPath(String(refVal), doc.absPath, projectRoot);\n if (!linkedPath) {\n return {\n pass: false,\n detail: `linked file not found: ${refVal}`,\n };\n }\n fm = readFrontmatterFromFile(linkedPath);\n }\n\n const actual = fm[parsed.field];\n\n // Compare\n const pass = compareValues(actual, parsed.op, parsed.value);\n const detail = pass\n ? `frontmatter(${parsed.ref}).${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)} → actual: ${JSON.stringify(actual)}`\n : `expected ${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)}, got ${JSON.stringify(actual)}`;\n\n return { pass, detail };\n}\n\nfunction compareValues(\n actual: unknown,\n op: '==' | '!=' | '>=' | '<=',\n expected: string | number | boolean\n): boolean {\n // null check for != null\n if (expected === 'null') {\n const isNull = actual === null || actual === undefined || actual === '' || actual === 'null';\n return op === '==' ? isNull : !isNull;\n }\n // Normalize actual: strip quotes\n let a: unknown = actual;\n if (typeof a === 'string') {\n a = a.replace(/^[\"']|[\"']$/g, '');\n // Try to coerce to bool/number for comparison\n if (a === 'true') a = true;\n else if (a === 'false') a = false;\n else {\n const n = Number(a);\n if (!isNaN(n) && (a as string) !== '') a = n;\n }\n }\n\n switch (op) {\n case '==': return a === expected || String(a) === String(expected);\n case '!=': return a !== expected && String(a) !== String(expected);\n case '>=': return Number(a) >= Number(expected);\n case '<=': return Number(a) <= Number(expected);\n }\n}\n\n/** Resolve a path reference relative to the document or project root. */\nfunction resolveLinkedPath(\n ref: string,\n docAbsPath: string,\n projectRoot: string\n): string | null {\n // Try relative to doc first, then relative to projectRoot\n const candidates = [\n path.resolve(path.dirname(docAbsPath), ref),\n path.resolve(projectRoot, ref),\n ];\n for (const candidate of candidates) {\n // Sandbox check\n if (!candidate.startsWith(projectRoot)) continue;\n if (fs.existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** Read frontmatter from a file as a plain Record. Does not throw on body. */\nfunction readFrontmatterFromFile(absPath: string): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return {};\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return {};\n const fmLines = lines.slice(1, closeIdx);\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n if (line.trim() === '' || line.trim().startsWith('#')) continue;\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '' || val === '[]') { fm[key] = []; continue; }\n if (val.startsWith('{')) { fm[key] = val; continue; }\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1).trim();\n fm[key] = inner === '' ? [] : inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return fm;\n } catch {\n return {};\n }\n}\n\n// ─── Body-contains evaluator ──────────────────────────────────────────────────\n\nfunction evalBodyContains(\n parsed: Extract<ParsedPredicate, { kind: 'body-contains' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const body = doc.body;\n const needle = parsed.needle;\n\n // Count occurrences and find section context\n let count = 0;\n let pos = 0;\n const sections: number[] = []; // 1-indexed section numbers for each occurrence\n const bodySections = body.split(/^## /m);\n\n // Simple occurrence count\n while ((pos = body.indexOf(needle, pos)) !== -1) {\n count++;\n // Find which section this occurrence is in\n const before = body.slice(0, pos);\n const sectionCount = (before.match(/^## /gm) || []).length;\n sections.push(sectionCount + 1); // 1-indexed\n pos += needle.length;\n }\n\n const present = count > 0;\n void bodySections; // suppress unused warning\n\n if (parsed.negated) {\n // \"body does not contain\" → pass when absent\n if (present) {\n const sectionList = [...new Set(sections)].map((s) => `§${s}`).join(', ');\n return {\n pass: false,\n detail: `${count} occurrence${count === 1 ? '' : 's'} at ${sectionList}`,\n };\n }\n return { pass: true, detail: `'${needle}' not found in body` };\n } else {\n // \"body contains\" → pass when present\n if (present) {\n return { pass: true, detail: `'${needle}' found ${count} time${count === 1 ? '' : 's'}` };\n }\n return { pass: false, detail: `'${needle}' not found in body` };\n }\n}\n\n// ─── Marker-absence evaluator ─────────────────────────────────────────────────\n\n/**\n * Sub-fix #2 (BUG-008): Evaluates \"body does not contain marker '<id>'\".\n * A marker is counted only when it appears in a syntactic role:\n * 1. Followed immediately by a colon: TBD: ...\n * 2. Wrapped in parens: (TBD) or square brackets: [TBD]\n * 3. The entire trimmed line equals the marker: bare TBD on its own line\n * 4. Preceded by a code-comment prefix: // TBD or # TBD\n *\n * NOT counted:\n * - TBD as part of another word (TBDs, TBDish, TBD's)\n * - TBD inside quotes in prose (\"TBD resolution\")\n * - Template self-reference lines: \"- [x] 0 \"TBDs\" exist\" / \"- [ ] 0 \"TBDs\" exist\"\n */\nfunction evalMarkerAbsence(\n parsed: Extract<ParsedPredicate, { kind: 'marker-absence' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const { marker } = parsed;\n const lines = doc.body.split('\\n');\n\n // Template self-reference lines to exclude (BUG-008 spec: \"- [x] 0 \"TBDs\" exist\")\n const templateSelfRefRe = /^\\s*-\\s*\\[[x ]\\]\\s*0\\s*\"TBDs?\"\\s*exist/i;\n\n // Regex: marker in a syntactic role.\n // Matches: (MARKER) | [MARKER] | MARKER: | // MARKER | # MARKER | bare MARKER line\n // The (?<!\\w) and (?!\\w) prevent matching inside longer words.\n const markerRe = new RegExp(\n `(?:^|(?<=\\\\())${marker}(?=:)|` + // MARKER: (colon follows)\n `\\\\(${marker}\\\\)|` + // (MARKER) parens\n `\\\\[${marker}\\\\]|` + // [MARKER] square brackets\n `(?<=//\\\\s*)${marker}(?!\\\\w)|` + // // MARKER (comment)\n `(?<=#\\\\s*)${marker}(?!\\\\w)`, // # MARKER (comment)\n 'g'\n );\n\n // Also check bare-line: entire trimmed line is just the marker\n const bareLineRe = new RegExp(`^${marker}$`);\n\n const violations: number[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n\n // Skip template self-reference boilerplate\n if (templateSelfRefRe.test(line)) continue;\n\n const trimmed = line.trim();\n\n // Check bare line\n if (bareLineRe.test(trimmed)) {\n violations.push(i + 1);\n continue;\n }\n\n // Check syntactic marker roles via regex\n markerRe.lastIndex = 0;\n if (markerRe.test(line)) {\n violations.push(i + 1);\n }\n }\n\n if (violations.length > 0) {\n return {\n pass: false,\n detail: `${violations.length} marker occurrence${violations.length === 1 ? '' : 's'} of '${marker}' at line${violations.length === 1 ? '' : 's'} ${violations.join(', ')}`,\n };\n }\n return { pass: true, detail: `no '${marker}' markers found in body` };\n}\n\n// ─── Section evaluator ────────────────────────────────────────────────────────\n\nfunction evalSection(\n parsed: Extract<ParsedPredicate, { kind: 'section' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n // Split body on ## headings (1-indexed).\n // Use lookahead so each part starts with \"## \" (or is preamble if body doesn't start with ##).\n const body = doc.body;\n\n const rawParts = body.split(/^(?=## )/m);\n // If body starts with \"## \", rawParts[0] = \"## Section 1\\n...\", rawParts[1] = \"## Section 2\\n...\", etc.\n // Section N is rawParts[N-1] (0-based array, 1-indexed sections).\n // If body has preamble before first ##, rawParts[0] = preamble (section 0), rawParts[1] = section 1, etc.\n\n // Detect if there is a preamble (content before first ##)\n const hasPreamble = rawParts.length > 0 && !rawParts[0]!.startsWith('## ');\n // Section N → rawParts index: with preamble, index = N; without, index = N - 1\n const arrayIndex = hasPreamble ? parsed.index : parsed.index - 1;\n const sectionContent = rawParts[arrayIndex];\n const totalSections = hasPreamble ? rawParts.length - 1 : rawParts.length;\n\n if (!sectionContent) {\n return {\n pass: false,\n detail: `section ${parsed.index} not found (body has ${totalSections} sections)`,\n };\n }\n\n let actualCount: number;\n switch (parsed.itemType) {\n case 'checked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[x\\]/gim) || []).length;\n break;\n case 'unchecked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[ \\]/gim) || []).length;\n break;\n case 'listed-item':\n actualCount = (sectionContent.match(/^\\s*- /gm) || []).length;\n break;\n }\n\n const pass = applyCountOp(actualCount, parsed.count.op, parsed.count.n);\n const opStr = parsed.count.op === '>=' ? '≥' : parsed.count.op;\n const detail = pass\n ? `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`\n : `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`;\n\n return { pass, detail };\n}\n\nfunction applyCountOp(actual: number, op: '>=' | '==' | '>', n: number): boolean {\n switch (op) {\n case '>=': return actual >= n;\n case '==': return actual === n;\n case '>': return actual > n;\n }\n}\n\n// ─── File-exists evaluator ────────────────────────────────────────────────────\n\nfunction evalFileExists(\n parsed: Extract<ParsedPredicate, { kind: 'file-exists' }>,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const resolved = path.resolve(projectRoot, parsed.path);\n\n // Sandbox check: must be inside projectRoot\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n return {\n pass: false,\n detail: `path '${parsed.path}' resolves outside project root (sandbox violation)`,\n };\n }\n\n const exists = fs.existsSync(resolved);\n return {\n pass: exists,\n detail: exists ? `${parsed.path} exists` : `${parsed.path} not found`,\n };\n}\n\n// ─── Link-target-exists evaluator ────────────────────────────────────────────\n\nfunction evalLinkTargetExists(\n parsed: Extract<ParsedPredicate, { kind: 'link-target-exists' }>,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const projectRoot = opts?.projectRoot ?? process.cwd();\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n const found = indexContent.includes(`[[${parsed.id}]]`);\n return {\n pass: found,\n detail: found\n ? `[[${parsed.id}]] found in wiki index`\n : `[[${parsed.id}]] not found in wiki index`,\n };\n}\n\n// ─── Status-of evaluator ─────────────────────────────────────────────────────\n\nfunction evalStatusOf(\n parsed: Extract<ParsedPredicate, { kind: 'status-of' }>,\n opts: EvalOptions | undefined,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n // Find the raw path for this ID in the wiki index\n // Wiki index format: | [[STORY-003-13]] | story | Draft | .cleargate/delivery/... |\n const rowMatch = indexContent.match(\n new RegExp(`\\\\[\\\\[${parsed.id}\\\\]\\\\]\\\\s*\\\\|[^|]+\\\\|[^|]+\\\\|\\\\s*([^|\\\\n]+)`)\n );\n if (!rowMatch) {\n return { pass: false, detail: `[[${parsed.id}]] not found in wiki index` };\n }\n\n const rawPath = rowMatch[1]!.trim();\n const fullPath = path.resolve(projectRoot, rawPath);\n\n // Sandbox check\n if (!fullPath.startsWith(projectRoot)) {\n return { pass: false, detail: `wiki path for ${parsed.id} resolves outside project root` };\n }\n\n const linkedFm = readFrontmatterFromFile(fullPath);\n const status = linkedFm['status'];\n if (status === undefined) {\n return { pass: false, detail: `[[${parsed.id}]] has no status field` };\n }\n\n const pass = String(status).replace(/^[\"']|[\"']$/g, '') === parsed.value;\n return {\n pass,\n detail: pass\n ? `status-of([[${parsed.id}]]) == ${parsed.value}`\n : `status-of([[${parsed.id}]]) is '${status}', expected '${parsed.value}'`,\n };\n}\n","/**\n * STORY-008-02: Idempotent cached_gate_result frontmatter writer.\n * Reuses the shared frontmatter serializer from frontmatter-yaml.ts.\n *\n * writeCachedGate is byte-identical on re-run with identical inputs (same now + same result).\n *\n * Post-BUG-001: cached_gate_result is stored as a native YAML mapping\n * (parseFrontmatter returns it as an object). Legacy flow-style strings are\n * still accepted on read for backwards-compat with old files.\n */\n\nimport * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface CachedGate {\n pass: boolean;\n failing_criteria: { id: string; detail: string }[];\n last_gate_check: string;\n}\n\n/**\n * Read the cached_gate_result from a file's frontmatter.\n * Returns null if the key is absent or the file has no valid frontmatter.\n */\nexport async function readCachedGate(absPath: string): Promise<CachedGate | null> {\n let raw: string;\n try {\n raw = await fs.readFile(absPath, 'utf8');\n } catch {\n return null;\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n return null;\n }\n\n return coerceCachedGate(fm['cached_gate_result']);\n}\n\n/**\n * Write (or update) cached_gate_result in a file's frontmatter.\n * Idempotent: if the result deep-equals the existing cached value AND the same\n * now() is supplied, the file bytes are left untouched.\n *\n * @param absPath Absolute path to the markdown file.\n * @param result The gate result to cache.\n * @param opts Optional: inject `now` for test determinism.\n */\nexport async function writeCachedGate(\n absPath: string,\n result: CachedGate,\n opts?: { now?: () => Date }\n): Promise<void> {\n const nowFn = opts?.now ?? (() => new Date());\n const lastGateCheck = result.last_gate_check || toIsoSecond(nowFn());\n\n const newResult: CachedGate = {\n pass: result.pass,\n failing_criteria: result.failing_criteria,\n last_gate_check: lastGateCheck,\n };\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n throw new Error(`writeCachedGate: failed to parse frontmatter in ${absPath}`);\n }\n\n // Idempotency check: compare existing cached_gate_result\n const existing = coerceCachedGate(fm['cached_gate_result']);\n if (existing && JSON.stringify(existing) === JSON.stringify(newResult)) {\n return;\n }\n\n // Build new frontmatter: preserve all existing keys, inject/update cached_gate_result\n const newFm: Record<string, unknown> = {};\n let inserted = false;\n for (const [k, v] of Object.entries(fm)) {\n if (k === 'cached_gate_result') {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n inserted = true;\n } else {\n newFm[k] = v;\n }\n }\n if (!inserted) {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n }\n\n const fmBlock = serializeFrontmatter(newFm);\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Coerce a frontmatter value (native object or legacy flow-style string) into\n * a CachedGate. Returns null if absent or unrecognizable.\n */\nfunction coerceCachedGate(val: unknown): CachedGate | null {\n if (val === undefined || val === null) return null;\n\n // Native object (current format)\n if (typeof val === 'object' && !Array.isArray(val)) {\n const c = val as Record<string, unknown>;\n return {\n pass: Boolean(c['pass']),\n failing_criteria: Array.isArray(c['failing_criteria'])\n ? (c['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(c['last_gate_check'] ?? ''),\n };\n }\n\n // Legacy flow-style string \"{pass: true, ...}\" — parse via js-yaml\n if (typeof val === 'string' && val.startsWith('{')) {\n try {\n const parsed = yaml.load(val, { schema: yaml.CORE_SCHEMA });\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return {\n pass: Boolean(p['pass']),\n failing_criteria: Array.isArray(p['failing_criteria'])\n ? (p['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(p['last_gate_check'] ?? ''),\n };\n } catch {\n return null;\n }\n }\n\n return null;\n}\n","/**\n * gate-run.ts — `cleargate gate <name>` command handler.\n *\n * STORY-018-03: Config-driven gates. Runs a shell command configured in\n * `.cleargate/config.yml` under `gates.<name>`. Known gate names:\n * precommit, test, typecheck, lint.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n * FLASHCARD #cli #commander #optional-key: opts.strict undefined must be\n * checked with === true, not truthy check.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\nimport type { WikiConfig } from '../lib/wiki-config.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateRunCliOptions {\n cwd?: string;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n spawnFn?: typeof spawnSync;\n configLoader?: (repoRoot: string) => WikiConfig;\n}\n\nconst KNOWN_GATES = ['precommit', 'test', 'typecheck', 'lint'] as const;\ntype KnownGate = typeof KNOWN_GATES[number];\n\n// ─── gateRunHandler ───────────────────────────────────────────────────────────\n\n/**\n * Handle `cleargate gate <name>`.\n *\n * - Validates name is in KNOWN_GATES (exit 2 if not).\n * - Loads config via configLoader (defaults to loadWikiConfig from cwd).\n * - If gate not configured: friendly message + exit 0 (default) or exit 1 (--strict).\n * - If configured: spawnSync with shell:true, propagate exit code.\n */\nexport function gateRunHandler(\n name: string,\n opts: { strict?: boolean },\n cli?: GateRunCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n const configLoaderFn = cli?.configLoader ?? loadWikiConfig;\n\n // Validate gate name\n if (!(KNOWN_GATES as readonly string[]).includes(name)) {\n stderrFn(\n `unknown gate name '${name}' — must be one of: precommit, test, typecheck, lint`,\n );\n return exitFn(2);\n }\n\n // Load config\n const config = configLoaderFn(cwd);\n const cmd = config.gates[name as KnownGate];\n\n if (cmd == null) {\n const msg = `gate \"${name}\" not configured — add gates.${name} to .cleargate/config.yml (see cleargate-planning/.cleargate/config.example.yml)`;\n if (opts.strict === true) {\n stderrFn(msg);\n return exitFn(1);\n } else {\n stdoutFn(msg);\n return exitFn(0);\n }\n }\n\n // Run the configured command\n const result = spawnFn(cmd, { shell: true, stdio: 'inherit', cwd });\n\n if (result.error) {\n stderrFn(`[cleargate gate ${name}] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n return exitFn(result.status ?? 0);\n}\n","/**\n * sprint.ts — `cleargate sprint init|close|archive` command handlers.\n *\n * STORY-013-08: CLI wrappers for sprint lifecycle scripts.\n * STORY-014-08: sprintArchiveHandler — final sprint close-out.\n * STORY-015-04: stampSprintClose + rollback on wiki build/lint failure.\n *\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh\n * or orchestrate filesystem + git operations directly.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly —\n * always route through `run_script.sh`.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport { wikiBuildHandler } from './wiki-build.js';\nimport { wikiLintHandler } from './wiki-lint.js';\n\n// Terminal statuses — re-declared locally to avoid cross-module runtime import.\n// Keep in sync with TERMINAL_STATUSES in wiki-build.ts.\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface SprintCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Test seam: override wiki build invocation. Defaults to wikiBuildHandler. */\n wikiBuildFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n /** Test seam: override wiki lint invocation. Defaults to wikiLintHandler. */\n wikiLintFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n}\n\n// ─── Shared run_script.sh resolution ─────────────────────────────────────────\n\nfunction resolveRunScript(opts: SprintCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\n// ─── sprintInitHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint init <sprint-id> --stories <csv>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh init_sprint.mjs <sprint-id> --stories <csv>`\n */\nexport function sprintInitHandler(\n opts: { sprintId: string; stories: string },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['init_sprint.mjs', opts.sprintId, '--stories', opts.stories];\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint init] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── sprintCloseHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint close <sprint-id> [--assume-ack]`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh close_sprint.mjs <sprint-id> [--assume-ack]`\n */\nexport function sprintCloseHandler(\n opts: { sprintId: string; assumeAck?: boolean },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['close_sprint.mjs', opts.sprintId];\n // FLASHCARD #cli #commander #optional-key: omit the key when undefined; only\n // append --assume-ack when the flag was explicitly set (opts.assumeAck === true).\n if (opts.assumeAck === true) {\n args.push('--assume-ack');\n }\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint close] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── sprintArchiveHandler ─────────────────────────────────────────────────────\n\n/**\n * Parse just the frontmatter from a markdown file using js-yaml CORE_SCHEMA.\n * Returns { fm, body } where body is the content after the closing `---`.\n * FLASHCARD #cli #frontmatter #parse: body may start with blank line; we strip\n * one leading blank to match parseFrontmatter.ts convention.\n */\nfunction parseFileFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return { fm: {}, body: raw };\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return { fm: {}, body: raw };\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n if (yamlText.trim() === '') return { fm: {}, body };\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch {\n return { fm: {}, body };\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return { fm: {}, body };\n return { fm: parsed as Record<string, unknown>, body };\n}\n\n/**\n * Serialize frontmatter + body back to a markdown string.\n * Always adds blank separator between `---` and body.\n */\nfunction serializeFileContent(fm: Record<string, unknown>, body: string): string {\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---\\n\\n${body}`;\n}\n\n/**\n * Atomic write via tmp + rename (same pattern as story.ts / close_sprint.mjs).\n */\nfunction atomicWriteStr(filePath: string, content: string): void {\n const tmp = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmp, content, 'utf8');\n fs.renameSync(tmp, filePath);\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranchForArchive(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Stamp a file's frontmatter with status + completed_at.\n * Returns the stamped content string (does NOT write to disk).\n */\nfunction stampFile(raw: string, status: string, completedAt: string): string {\n const { fm, body } = parseFileFrontmatter(raw);\n fm['status'] = status;\n fm['completed_at'] = completedAt;\n return serializeFileContent(fm, body);\n}\n\n/**\n * STORY-015-04: Stamp the sprint file's frontmatter with status=\"Completed\"\n * (if not already terminal) and completed_at (if absent). Writes atomically.\n *\n * Returns:\n * previousContent — original file bytes for rollback\n * stampedContent — the content written to disk\n * didChange — false if file was already terminal + completed_at set\n */\nexport function stampSprintClose(\n sprintPath: string,\n now: () => string,\n): { previousContent: string; stampedContent: string; didChange: boolean } {\n const previousContent = fs.readFileSync(sprintPath, 'utf8');\n const { fm, body } = parseFileFrontmatter(previousContent);\n\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const alreadyTerminal = TERMINAL_STATUSES.has(currentStatus);\n const hasCompletedAt = typeof fm['completed_at'] === 'string' && fm['completed_at'].length > 0;\n\n if (alreadyTerminal && hasCompletedAt) {\n return { previousContent, stampedContent: previousContent, didChange: false };\n }\n\n if (!alreadyTerminal) {\n fm['status'] = 'Completed';\n }\n if (!hasCompletedAt) {\n fm['completed_at'] = now();\n }\n\n const stampedContent = serializeFileContent(fm, body);\n atomicWriteStr(sprintPath, stampedContent);\n return { previousContent, stampedContent, didChange: true };\n}\n\n/**\n * STORY-015-04: Atomically restore a sprint file from a previously-snapshotted\n * string (rollback after wiki build/lint failure).\n */\nexport function restoreSprintFile(sprintPath: string, original: string): void {\n atomicWriteStr(sprintPath, original);\n}\n\n/** Return state.json story keys that belong to a given epic. */\nfunction storyKeysForEpic(\n stateStories: Record<string, unknown>,\n epicId: string,\n): string[] {\n const epicNum = epicId.replace('EPIC-', '');\n return Object.keys(stateStories).filter((k) => k.startsWith(`STORY-${epicNum}-`));\n}\n\n/**\n * `cleargate sprint archive <sprint-id> [--dry-run]`\n *\n * v1: print inert message, exit 0.\n * v2:\n * 1. Read state.json — refuse if sprint_status !== 'Completed'.\n * 2. Resolve file set from sprint frontmatter + state.json + orphan scan.\n * 3. --dry-run: print plan; no writes.\n * 4. Live: stamp sprint file, wiki build+lint (if wiki initialised), then\n * move files, clear .active, git checkout main, merge, branch -d.\n */\nexport async function sprintArchiveHandler(\n opts: { sprintId: string; dryRun?: boolean },\n cli?: SprintCliOptions,\n): Promise<void> {\n try {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Wiki build/lint seam wrappers.\n // wikiBuildHandler on success returns (no exit call); on failure calls exit(1) which throws.\n // wikiLintHandler on success calls exit(0) which throws; on findings calls exit(1) which throws.\n const wikiBuildFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiBuildFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-build-exit:${code}`); };\n try {\n await wikiBuildHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-build-exit:0')) return;\n throw err;\n }\n });\n const wikiLintFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiLintFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-lint-exit:${code}`); };\n try {\n await wikiLintHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-lint-exit:0')) return;\n throw err;\n }\n });\n\n // Step 1: v1-inert check\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // Step 2: Read state.json — refuse if not Completed\n const stateFile = path.join(cwd, '.cleargate', 'sprint-runs', opts.sprintId, 'state.json');\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate sprint archive] state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n sprint_status?: string;\n stories?: Record<string, unknown>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate sprint archive] failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n if (state.sprint_status !== 'Completed') {\n stderrFn(\n `sprint not closed — run \\`cleargate sprint close ${opts.sprintId} --assume-ack\\` first`,\n );\n return exitFn(1);\n }\n\n const stateStories: Record<string, unknown> = state.stories ?? {};\n\n // Step 3: Resolve file set\n const pendingDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(cwd, '.cleargate', 'delivery', 'archive');\n\n // Sprint file\n let sprintFile: string | null = null;\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${opts.sprintId}_`) || entry === `${opts.sprintId}.md`) && entry.endsWith('.md')) {\n sprintFile = path.join(pendingDir, entry);\n break;\n }\n }\n\n // Sprint frontmatter → epics list\n let epicIds: string[] = [];\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { fm } = parseFileFrontmatter(fs.readFileSync(sprintFile, 'utf8'));\n const epics = fm['epics'];\n if (Array.isArray(epics)) {\n epicIds = epics.map(String);\n }\n }\n\n // Plan entries: { src, destName, status }\n interface FilePlan {\n src: string;\n destName: string;\n status: string;\n }\n const plan: FilePlan[] = [];\n\n if (sprintFile) {\n plan.push({\n src: sprintFile,\n destName: path.basename(sprintFile),\n status: 'Completed',\n });\n }\n\n for (const epicId of epicIds) {\n // Epic file\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${epicId}_`) || entry === `${epicId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Approved',\n });\n break;\n }\n }\n\n // Story files (authoritative: state.json keys for this epic)\n const storyKeys = storyKeysForEpic(stateStories, epicId);\n for (const storyId of storyKeys) {\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${storyId}_`) || entry === `${storyId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Done',\n });\n break;\n }\n }\n }\n }\n\n // Orphan scan: any STORY-*.md in pending-sync with parent_epic_ref matching\n // one of our epics but NOT in state.json keys.\n const storyIdsInState = new Set(Object.keys(stateStories));\n const planSrcs = new Set(plan.map((p) => p.src));\n const orphans: string[] = [];\n for (const entry of fs.readdirSync(pendingDir)) {\n if (!entry.startsWith('STORY-') || !entry.endsWith('.md')) continue;\n const candidate = path.join(pendingDir, entry);\n if (planSrcs.has(candidate)) continue;\n let raw: string;\n try {\n raw = fs.readFileSync(candidate, 'utf8');\n } catch { continue; }\n const { fm } = parseFileFrontmatter(raw);\n const parentRef = String(fm['parent_epic_ref'] ?? '');\n if (epicIds.includes(parentRef)) {\n // Derive story ID from filename (STORY-014-01_Something.md → STORY-014-01)\n const storyId = entry.replace(/\\.md$/, '').replace(/_.*$/, '');\n if (!storyIdsInState.has(storyId)) {\n orphans.push(candidate);\n stderrFn(`WARN: orphan story ${entry} matches epic ${parentRef} but is not in state.json — archiving anyway`);\n plan.push({ src: candidate, destName: entry, status: 'Done' });\n }\n }\n }\n\n // Step 4: --dry-run: print plan + exit 0\n const completedAt = new Date().toISOString();\n const sprintBranch = deriveSprintBranchForArchive(opts.sprintId);\n const activePath = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n\n if (opts.dryRun) {\n stdoutFn(`[dry-run] Sprint archive plan for ${opts.sprintId}:`);\n stdoutFn(` Sprint branch: ${sprintBranch}`);\n stdoutFn(` Files to archive (${plan.length}):`);\n for (const entry of plan) {\n stdoutFn(\n ` ${path.basename(entry.src)} → archive/${entry.destName} [stamp: status=${entry.status}, completed_at=<now>]`,\n );\n }\n if (orphans.length > 0) {\n stdoutFn(` Orphan files (${orphans.length}): ${orphans.map((o) => path.basename(o)).join(', ')}`);\n }\n stdoutFn(` .active → \"\" (truncate)`);\n stdoutFn(` git checkout main`);\n stdoutFn(` git merge --no-ff -m \"merge: ${sprintBranch} → main\" ${sprintBranch}`);\n stdoutFn(` git branch -d ${sprintBranch}`);\n return exitFn(0);\n }\n\n // Step 5a: Stamp the sprint file's frontmatter (status + completed_at)\n let sprintFileSnapshot: string | null = null;\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const wikiInitialised = fs.existsSync(wikiRoot);\n\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { previousContent } = stampSprintClose(sprintFile, () => completedAt);\n sprintFileSnapshot = previousContent;\n\n // Step 5b: wiki build + lint — only if wiki has been initialised.\n // Skip gracefully when .cleargate/wiki/ doesn't exist yet (wiki not built).\n if (wikiInitialised) {\n for (const [stepName, stepFn] of [\n ['wiki build', () => wikiBuildFn(cwd, stdoutFn)] as const,\n ['wiki lint', () => wikiLintFn(cwd, stdoutFn)] as const,\n ]) {\n try {\n await stepFn();\n } catch (err) {\n // Rollback sprint file frontmatter\n atomicWriteStr(sprintFile!, sprintFileSnapshot!);\n stderrFn(\n `[cleargate sprint archive] post-stamp ${stepName} failed — sprint frontmatter reverted`,\n );\n if (err instanceof Error) stderrFn(err.message);\n return exitFn(1);\n }\n }\n }\n }\n\n // Step 5c: Live run — stamp + move each file\n for (const entry of plan) {\n if (!fs.existsSync(entry.src)) {\n stderrFn(`[cleargate sprint archive] source not found: ${entry.src} — skipping`);\n continue;\n }\n const raw = fs.readFileSync(entry.src, 'utf8');\n const stamped = stampFile(raw, entry.status, completedAt);\n const dest = path.join(archiveDir, entry.destName);\n atomicWriteStr(entry.src, stamped);\n fs.renameSync(entry.src, dest);\n stdoutFn(`archived: ${entry.destName}`);\n }\n\n // Step 6: Truncate .active\n try {\n atomicWriteStr(activePath, '');\n } catch {\n // .active may not exist — not fatal\n }\n\n // Step 7: git checkout main\n const step7 = spawnFn('git', ['checkout', 'main'], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step7.error || (step7.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git checkout main failed`);\n if (step7.stderr) stderrFn(String(step7.stderr));\n return exitFn(step7.status ?? 1);\n }\n\n // Step 8: git merge --no-ff -m \"merge: sprint/<branch> → main\" sprint/<branch>\n const mergeMsg = `merge: ${sprintBranch} → main`;\n const step8 = spawnFn(\n 'git',\n ['merge', '--no-ff', '-m', mergeMsg, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step8.error || (step8.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git merge failed — run \\`git merge --abort\\` if needed`);\n if (step8.stderr) stderrFn(String(step8.stderr));\n return exitFn(step8.status ?? 1);\n }\n const mergeSha = String(step8.stdout ?? '').trim().split('\\n')[0] ?? '';\n\n // Step 9: git branch -d sprint/<branch>\n const step9 = spawnFn('git', ['branch', '-d', sprintBranch], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step9.error || (step9.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git branch -d ${sprintBranch} failed`);\n if (step9.stderr) stderrFn(String(step9.stderr));\n return exitFn(step9.status ?? 1);\n }\n\n stdoutFn(\n `archive complete: ${plan.length} files moved, branch ${sprintBranch} deleted` +\n (mergeSha ? `, merge SHA: ${mergeSha}` : ''),\n );\n return exitFn(0);\n } catch (e) {\n // The exitFn seam throws `Error(\"exit:<n>\")` as a synchronous control-flow\n // shortcut so the handler bails without running further steps. Under an\n // async handler that escapes as an unhandled rejection even when tests\n // `getCode()` confirms the expected exit. Swallow the sentinel here; any\n // other error re-throws.\n if (e instanceof Error && /^exit:\\d+$/.test(e.message)) return;\n throw e;\n }\n}\n","/**\n * story.ts — `cleargate story start|complete` command handlers.\n *\n * STORY-014-07: atomic orchestration of worktree + branch + merge + state.\n *\n * Both handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they orchestrate the full\n * worktree/merge sequence via `spawnSync` + a second-pass `state.json` write.\n *\n * `story start <story-id>`:\n * 1. `git worktree add <cwd>/.worktrees/<ID> -b story/<ID> <sprintBranch>`\n * 2. `bash run_script.sh update_state.mjs <ID> Bouncing`\n * 3. Re-read `state.json`, set `stories[<ID>].worktree` field, atomic write.\n *\n * `story complete <story-id>`:\n * 1. `git rev-list --count <sprintBranch>..story/<ID>` — if 0 → abort.\n * 2. `git -C <cwd> checkout <sprintBranch>`\n * 3. `git merge story/<ID> --no-ff -m \"merge: story/<ID> → <sprintBranch>\"`\n * — on conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n * 4. `git worktree remove .worktrees/<ID>`\n * 5. `git branch -d story/<ID>`\n * 6. `bash run_script.sh update_state.mjs <ID> Done`\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StoryCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID override for execution_mode discovery.\n * If absent, the handler reads `.cleargate/sprint-runs/.active`.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StoryCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranch(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Atomic write: tmp + rename. Inlined per plan (do not import from .mjs).\n * Caller passes the raw JSON-stringified text (or any string content).\n */\nfunction atomicWriteString(filePath: string, text: string): void {\n const tmpFile = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmpFile, text, 'utf8');\n fs.renameSync(tmpFile, filePath);\n}\n\n/**\n * Path to the sprint's state.json. The file may not exist yet under v2\n * when the sprint was just initialized — callers must guard.\n */\nfunction stateJsonPath(cwd: string, sprintId: string): string {\n return path.join(cwd, '.cleargate', 'sprint-runs', sprintId, 'state.json');\n}\n\n// ─── storyStartHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate story start <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 3-step spawn sequence + second-pass state.json mutation.\n */\nexport function storyStartHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve sprint ID: explicit override, else sentinel-fallback via .active.\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 3-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const worktreePath = path.join(cwd, '.worktrees', opts.storyId);\n const storyBranch = `story/${opts.storyId}`;\n\n // Step 1: git worktree add <path> -b story/<ID> <sprintBranch>\n // FLASHCARD (plan): flag order matters on macOS git — PATH before -b.\n const step1 = spawnFn(\n 'git',\n ['worktree', 'add', worktreePath, '-b', storyBranch, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n\n // Step 2: run_script.sh update_state.mjs <ID> Bouncing\n const runScript = resolveRunScript(cli ?? { cwd });\n const step2 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Bouncing'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: re-read state.json (fresh bytes AFTER update_state.mjs), set\n // stories[<ID>].worktree = \".worktrees/<ID>\", atomic write.\n const stateFile = stateJsonPath(cwd, sprintId);\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate story start] step 3: state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n stories?: Record<string, { worktree?: string | null } & Record<string, unknown>>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n state.stories = state.stories ?? {};\n const existing = state.stories[opts.storyId] ?? {\n state: 'Bouncing',\n qa_bounces: 0,\n arch_bounces: 0,\n worktree: null,\n updated_at: new Date().toISOString(),\n notes: '',\n };\n existing.worktree = `.worktrees/${opts.storyId}`;\n state.stories[opts.storyId] = existing;\n\n try {\n atomicWriteString(stateFile, JSON.stringify(state, null, 2) + '\\n');\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: atomic write failed: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n stdoutFn(`worktree created at .worktrees/${opts.storyId} on branch ${storyBranch}`);\n return exitFn(0);\n}\n\n// ─── storyCompleteHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate story complete <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 6-step orchestration (pre-flight, checkout, merge, worktree remove,\n * branch -d, update_state.mjs Done).\n */\nexport function storyCompleteHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 6-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const storyBranch = `story/${opts.storyId}`;\n const worktreeRel = path.join('.worktrees', opts.storyId);\n\n // Step 1: pre-flight rev-list — refuse if 0 commits on story branch.\n const step1 = spawnFn(\n 'git',\n ['rev-list', '--count', `${sprintBranch}..${storyBranch}`],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n const count = parseInt(String(step1.stdout ?? '0').trim(), 10);\n if (!Number.isFinite(count) || count <= 0) {\n stderrFn('no commits on story branch — nothing to merge');\n return exitFn(1);\n }\n\n // Step 2: checkout sprint branch.\n const step2 = spawnFn(\n 'git',\n ['-C', cwd, 'checkout', sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story complete] step 2 (checkout) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 2 (checkout ${sprintBranch}) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: merge story/<ID> --no-ff -m \"...\"\n // On conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n const mergeMsg = `merge: ${storyBranch} → ${sprintBranch}`;\n const step3 = spawnFn(\n 'git',\n ['merge', storyBranch, '--no-ff', '-m', mergeMsg],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step3.error) {\n stderrFn(`[cleargate story complete] step 3 (merge) error: ${step3.error.message}`);\n return exitFn(1);\n }\n if ((step3.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] merge conflict — run \\`git merge --abort\\` then re-run after resolution`);\n if (step3.stderr) stderrFn(String(step3.stderr));\n return exitFn(1);\n }\n\n // Step 4: git worktree remove .worktrees/<ID>\n const step4 = spawnFn(\n 'git',\n ['worktree', 'remove', worktreeRel],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step4.error) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove) error: ${step4.error.message}`);\n return exitFn(1);\n }\n if ((step4.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove ${worktreeRel}) failed with exit ${step4.status}`);\n if (step4.stderr) stderrFn(String(step4.stderr));\n return exitFn(step4.status ?? 1);\n }\n\n // Step 5: git branch -d story/<ID>\n const step5 = spawnFn(\n 'git',\n ['branch', '-d', storyBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step5.error) {\n stderrFn(`[cleargate story complete] step 5 (branch -d) error: ${step5.error.message}`);\n return exitFn(1);\n }\n if ((step5.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 5 (branch -d ${storyBranch}) failed with exit ${step5.status}`);\n if (step5.stderr) stderrFn(String(step5.stderr));\n return exitFn(step5.status ?? 1);\n }\n\n // Step 6: run_script.sh update_state.mjs <ID> Done\n const runScript = resolveRunScript(cli ?? { cwd });\n const step6 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Done'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step6.error) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) error: ${step6.error.message}`);\n return exitFn(1);\n }\n if ((step6.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) failed with exit ${step6.status}`);\n if (step6.stderr) stderrFn(String(step6.stderr));\n return exitFn(step6.status ?? 1);\n }\n\n stdoutFn(`merged ${storyBranch} → ${sprintBranch}; worktree + branch removed; state = Done`);\n return exitFn(0);\n}\n","/**\n * state.ts — `cleargate state update|validate` command handlers.\n *\n * STORY-013-08: CLI wrappers for state management scripts.\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StateCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID for execution_mode discovery. Required for state update/validate.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StateCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n// ─── stateUpdateHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate state update <story-id> <new-state>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh update_state.mjs <story-id> <new-state>`\n */\nexport function stateUpdateHandler(\n opts: { storyId: string; newState: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n // Sprint ID resolution order:\n // 1. Explicit --sprint <id> from CLI (cli?.sprintId)\n // 2. .cleargate/sprint-runs/.active sentinel\n // 3. Fall through to SPRINT-UNKNOWN (v1-inert)\n const cwd = cli?.cwd;\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, opts.newState],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state update] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── stateValidateHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate state validate <sprint-id>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh validate_state.mjs <sprint-id>`\n */\nexport function stateValidateHandler(\n opts: { sprintId: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'validate_state.mjs', opts.sprintId],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state validate] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * stamp-tokens.ts — STORY-008-05\n *\n * `cleargate stamp-tokens <file>` CLI command.\n * Reads token-ledger rows for a work item, aggregates per-session totals, and\n * stamps `draft_tokens:` into the file's YAML frontmatter.\n *\n * Hook-invoked. Idempotent within a session (last_stamp check), accumulative\n * across sessions (sessions[] array).\n *\n * No top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from '../lib/frontmatter-yaml.js';\nimport { readLedgerForWorkItem } from '../lib/ledger-reader.js';\nimport { detectWorkItemType } from '../lib/work-item-type.js';\nimport type { SessionBucket } from '../lib/ledger-reader.js';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport interface StampTokensCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n sprintRunsRoot?: string;\n}\n\nexport interface DraftTokensSession {\n session: string;\n model: string;\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n ts: string;\n}\n\nexport interface DraftTokens {\n input: number | null;\n output: number | null;\n cache_creation: number | null;\n cache_read: number | null;\n model: string | null;\n last_stamp: string;\n sessions: DraftTokensSession[];\n}\n\nexport async function stampTokensHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampTokensCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn =\n cli?.exit ??\n ((code: number) => {\n process.exit(code);\n });\n const nowFn = cli?.now ?? (() => new Date());\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Archive freeze: if path contains /.cleargate/delivery/archive/, noop\n if (/\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath)) {\n stdoutFn(`[frozen] ${absPath}`);\n exitFn(0);\n return;\n }\n\n // Read the file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absPath, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot read file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Parse frontmatter\n let fm: Record<string, unknown> = {};\n let body = '';\n\n const hasFrontmatter = rawContent.trimStart().startsWith('---');\n if (hasFrontmatter) {\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot parse frontmatter in: ${absPath}`);\n exitFn(1);\n return;\n }\n } else {\n body = rawContent;\n }\n\n // Extract work_item_id from frontmatter ID key or filename fallback\n const workItemId = extractWorkItemId(fm, absPath);\n if (!workItemId) {\n stdoutFn(`[stamp-tokens] error: cannot determine work_item_id from frontmatter or filename: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Read existing draft_tokens from frontmatter (parsed as native nested object\n // since parseFrontmatter now returns typed values)\n const existingDraftTokens = coerceDraftTokens(fm['draft_tokens']);\n const existingLastStamp = existingDraftTokens?.last_stamp ?? null;\n\n // Read ledger\n const buckets = readLedgerForWorkItem(workItemId, { sprintRunsRoot: cli?.sprintRunsRoot });\n\n // Idempotency check: if all ledger rows are older than last_stamp, and we\n // already have sessions, no-op.\n if (existingLastStamp && buckets.length > 0) {\n const allRowsOlderThanLastStamp = buckets.every((bucket) =>\n bucket.rows.every((row) => row.ts < existingLastStamp),\n );\n if (allRowsOlderThanLastStamp && existingDraftTokens !== null) {\n // No new rows since last stamp — no-op\n exitFn(0);\n return;\n }\n }\n\n const nowIso = toIsoSecond(nowFn());\n\n let newFm: Record<string, unknown>;\n let stampError: string | undefined;\n\n if (buckets.length === 0) {\n // Missing ledger: write all-null draft_tokens + stamp_error\n stampError = `no ledger rows for work_item_id ${workItemId}`;\n const nullTokens: DraftTokens = {\n input: null,\n output: null,\n cache_creation: null,\n cache_read: null,\n model: null,\n last_stamp: nowIso,\n sessions: [],\n };\n newFm = buildNewFrontmatter(fm, nullTokens, stampError);\n } else {\n // Aggregate across all sessions\n const tokens = aggregateBuckets(buckets, nowIso);\n newFm = buildNewFrontmatter(fm, tokens, undefined);\n // Remove stale stamp_error if ledger now has rows\n delete newFm['stamp_error'];\n }\n\n const serialized = buildSerializedContent(newFm, body);\n\n if (opts.dryRun) {\n // Print planned diff without writing\n stdoutFn(`[dry-run] stamp-tokens would write draft_tokens for ${workItemId}:`);\n const draftTokensVal = newFm['draft_tokens'];\n stdoutFn(` draft_tokens: ${JSON.stringify(draftTokensVal)}`);\n if (stampError) {\n stdoutFn(` stamp_error: \"${stampError}\"`);\n }\n exitFn(0);\n return;\n }\n\n // Write the file\n try {\n fs.writeFileSync(absPath, serialized, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot write file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n stdoutFn(`[stamped] ${absPath} (${workItemId})`);\n exitFn(0);\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Extract the work_item_id from frontmatter ID fields or filename regex fallback.\n */\nfunction extractWorkItemId(fm: Record<string, unknown>, absPath: string): string | null {\n // Try frontmatter ID keys in order\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id'];\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim() !== '') {\n return val.trim();\n }\n }\n\n // Fallback: extract from filename\n const basename = path.basename(absPath);\n const match = basename.match(/^(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/i);\n if (match) {\n return match[0].toUpperCase();\n }\n\n // Also try detectWorkItemType for any prefix match\n const typeFromPath = detectWorkItemType(absPath);\n if (typeFromPath) {\n // Try to find the ID segment in the basename\n const idMatch = basename.match(/((?:STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(?:-\\d+)?)/i);\n if (idMatch) {\n return idMatch[1].toUpperCase();\n }\n }\n\n return null;\n}\n\n/**\n * Coerce the existing draft_tokens value from frontmatter into a DraftTokens shape.\n * Handles two on-disk shapes:\n * 1. Native nested YAML map (current format, parsed by js-yaml as an object)\n * 2. Legacy JSON-in-a-string, from pre-fix files written before BUG-001\n */\nfunction coerceDraftTokens(val: unknown): DraftTokens | null {\n if (val == null) return null;\n\n if (typeof val === 'object' && !Array.isArray(val)) {\n const o = val as Record<string, unknown>;\n return {\n input: typeof o['input'] === 'number' ? o['input'] : null,\n output: typeof o['output'] === 'number' ? o['output'] : null,\n cache_creation: typeof o['cache_creation'] === 'number' ? o['cache_creation'] : null,\n cache_read: typeof o['cache_read'] === 'number' ? o['cache_read'] : null,\n model: typeof o['model'] === 'string' ? o['model'] : null,\n last_stamp: typeof o['last_stamp'] === 'string' ? o['last_stamp'] : '',\n sessions: Array.isArray(o['sessions']) ? (o['sessions'] as DraftTokensSession[]) : [],\n };\n }\n\n if (typeof val === 'string') {\n try {\n const parsed = JSON.parse(val) as DraftTokens;\n return parsed;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Aggregate ledger buckets into a DraftTokens object.\n * model: comma-joined sorted unique models across all buckets' rows.\n * sessions[]: one entry per bucket, using bucket totals.\n */\nexport function aggregateBuckets(buckets: SessionBucket[], nowIso: string): DraftTokens {\n let totalInput = 0;\n let totalOutput = 0;\n let totalCacheCreation = 0;\n let totalCacheRead = 0;\n\n const uniqueModels = new Set<string>();\n const sessions: DraftTokensSession[] = [];\n\n for (const bucket of buckets) {\n totalInput += bucket.totals.input;\n totalOutput += bucket.totals.output;\n totalCacheCreation += bucket.totals.cache_creation;\n totalCacheRead += bucket.totals.cache_read;\n\n // Collect unique models from rows and derive session model\n const sessionModels = new Set<string>();\n for (const row of bucket.rows) {\n if (row.model) {\n uniqueModels.add(row.model);\n sessionModels.add(row.model);\n }\n }\n\n // Session model: comma-joined unique models for this session\n const sessionModel = Array.from(sessionModels).sort().join(', ');\n\n sessions.push({\n session: bucket.session_id,\n model: sessionModel,\n input: bucket.totals.input,\n output: bucket.totals.output,\n cache_read: bucket.totals.cache_read,\n cache_creation: bucket.totals.cache_creation,\n ts: bucket.rows[0]?.ts ?? '',\n });\n }\n\n const model = Array.from(uniqueModels).sort().join(', ') || null;\n\n return {\n input: totalInput,\n output: totalOutput,\n cache_creation: totalCacheCreation,\n cache_read: totalCacheRead,\n model,\n last_stamp: nowIso,\n sessions,\n };\n}\n\n/**\n * Build the new frontmatter record with draft_tokens as a native nested\n * object. serializeFrontmatter (js-yaml) emits it as a block-style YAML map.\n */\nfunction buildNewFrontmatter(\n existingFm: Record<string, unknown>,\n tokens: DraftTokens,\n stampError: string | undefined,\n): Record<string, unknown> {\n const newFm: Record<string, unknown> = {};\n\n // Preserve all existing keys except draft_tokens and stamp_error (we'll re-add)\n for (const [k, v] of Object.entries(existingFm)) {\n if (k !== 'draft_tokens' && k !== 'stamp_error') {\n newFm[k] = v;\n }\n }\n\n if (stampError) {\n newFm['stamp_error'] = stampError;\n }\n\n // Store as a plain object; serializer emits block-style YAML\n newFm['draft_tokens'] = tokens as unknown as Record<string, unknown>;\n\n return newFm;\n}\n\n/**\n * Build the final file content: frontmatter block + body.\n */\nfunction buildSerializedContent(fm: Record<string, unknown>, body: string): string {\n const fmBlock = serializeFrontmatter(fm);\n if (body.length > 0) {\n return `${fmBlock}\\n\\n${body}`;\n }\n return `${fmBlock}\\n`;\n}\n\n","/**\n * ledger-reader.ts — STORY-008-04\n *\n * Read-only library for scanning token-ledger.jsonl files across all sprint-run\n * directories and grouping rows by session for per-work-item cost attribution.\n *\n * Node built-ins only. No runtime deps.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface LedgerRow {\n ts: string;\n sprint_id: string;\n agent_type: string;\n /** Populated only for STORY-* items (backward compat; empty string for non-story items) */\n story_id: string;\n /** Always populated when detection succeeded. Equals story_id for STORY items. */\n work_item_id: string;\n session_id: string;\n transcript: string;\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n model: string;\n turns: number;\n}\n\nexport interface SessionBucket {\n session_id: string;\n rows: LedgerRow[];\n totals: {\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n turns: number;\n };\n}\n\nexport interface ReadLedgerOptions {\n /** ISO timestamp string; rows with ts < since are excluded */\n since?: string;\n /**\n * Root directory for sprint-runs/. Defaults to\n * <repo_root>/.cleargate/sprint-runs where repo_root is resolved by\n * walking up from cwd to find .cleargate/.\n * Override in tests to avoid touching the real repo.\n */\n sprintRunsRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Walk up from cwd until we find a directory containing `.cleargate/sprint-runs/`.\n * Returns the sprint-runs path or null if not found.\n */\nfunction findSprintRunsRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const candidate = path.join(dir, '.cleargate', 'sprint-runs');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction normalizeRow(raw: Record<string, unknown>): LedgerRow {\n // work_item_id may be absent in pre-STORY-008-04 rows; default from story_id.\n const story_id = typeof raw['story_id'] === 'string' ? raw['story_id'] : '';\n const work_item_id =\n typeof raw['work_item_id'] === 'string' && raw['work_item_id'] !== ''\n ? raw['work_item_id']\n : story_id;\n\n return {\n ts: typeof raw['ts'] === 'string' ? raw['ts'] : '',\n sprint_id: typeof raw['sprint_id'] === 'string' ? raw['sprint_id'] : '',\n agent_type: typeof raw['agent_type'] === 'string' ? raw['agent_type'] : 'unknown',\n story_id,\n work_item_id,\n session_id: typeof raw['session_id'] === 'string' ? raw['session_id'] : '',\n transcript: typeof raw['transcript'] === 'string' ? raw['transcript'] : '',\n input: typeof raw['input'] === 'number' ? raw['input'] : 0,\n output: typeof raw['output'] === 'number' ? raw['output'] : 0,\n cache_creation: typeof raw['cache_creation'] === 'number' ? raw['cache_creation'] : 0,\n cache_read: typeof raw['cache_read'] === 'number' ? raw['cache_read'] : 0,\n model: typeof raw['model'] === 'string' ? raw['model'] : '',\n turns: typeof raw['turns'] === 'number' ? raw['turns'] : 0,\n };\n}\n\nfunction rowMatchesWorkItem(row: LedgerRow, workItemId: string): boolean {\n return row.work_item_id === workItemId || row.story_id === workItemId;\n}\n\nfunction buildBucket(session_id: string, rows: LedgerRow[]): SessionBucket {\n const totals = rows.reduce(\n (acc, r) => ({\n input: acc.input + r.input,\n output: acc.output + r.output,\n cache_creation: acc.cache_creation + r.cache_creation,\n cache_read: acc.cache_read + r.cache_read,\n turns: acc.turns + r.turns,\n }),\n { input: 0, output: 0, cache_creation: 0, cache_read: 0, turns: 0 }\n );\n return { session_id, rows, totals };\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Scan all sprint-runs/<sprint>/token-ledger.jsonl files and return rows\n * matching the given workItemId, grouped by session_id.\n *\n * Rows are compared by work_item_id first, then story_id (for backward compat\n * with pre-STORY-008-04 rows that lack work_item_id).\n *\n * Pre-fix rows (missing work_item_id) default work_item_id from story_id.\n *\n * @param workItemId e.g. \"STORY-008-04\", \"EPIC-008\", \"PROPOSAL-042\"\n * @param opts optional filters and path overrides\n * @returns array of SessionBucket, one per unique session_id, sorted\n * by the ts of the earliest row in each bucket\n */\nexport function readLedgerForWorkItem(\n workItemId: string,\n opts: ReadLedgerOptions = {}\n): SessionBucket[] {\n // Resolve sprint-runs root\n let sprintRunsRoot: string;\n if (opts.sprintRunsRoot) {\n sprintRunsRoot = opts.sprintRunsRoot;\n } else {\n const found = findSprintRunsRoot(process.cwd());\n if (!found) {\n return [];\n }\n sprintRunsRoot = found;\n }\n\n if (!fs.existsSync(sprintRunsRoot)) {\n return [];\n }\n\n // Collect all token-ledger.jsonl files across all sprint dirs\n let ledgerFiles: string[];\n try {\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n ledgerFiles = entries\n .filter((e) => e.isDirectory())\n .map((e) => path.join(sprintRunsRoot, e.name, 'token-ledger.jsonl'))\n .filter((f) => fs.existsSync(f));\n } catch {\n return [];\n }\n\n // Parse matching rows from each file\n const matchingRows: LedgerRow[] = [];\n\n for (const ledgerFile of ledgerFiles) {\n let content: string;\n try {\n content = fs.readFileSync(ledgerFile, 'utf-8');\n } catch {\n continue;\n }\n\n const lines = content.split('\\n').filter((l) => l.trim() !== '');\n for (const line of lines) {\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(line) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n const row = normalizeRow(raw);\n\n // Apply since filter\n if (opts.since && row.ts < opts.since) {\n continue;\n }\n\n if (rowMatchesWorkItem(row, workItemId)) {\n matchingRows.push(row);\n }\n }\n }\n\n // Group by session_id\n const sessionMap = new Map<string, LedgerRow[]>();\n for (const row of matchingRows) {\n const key = row.session_id || '(unknown-session)';\n const existing = sessionMap.get(key);\n if (existing) {\n existing.push(row);\n } else {\n sessionMap.set(key, [row]);\n }\n }\n\n // Build buckets and sort by earliest row ts within each bucket\n const buckets = Array.from(sessionMap.entries()).map(([session_id, rows]) => {\n rows.sort((a, b) => a.ts.localeCompare(b.ts));\n return buildBucket(session_id, rows);\n });\n\n // Sort buckets by earliest row ts\n buckets.sort((a, b) => {\n const aTs = a.rows[0]?.ts ?? '';\n const bTs = b.rows[0]?.ts ?? '';\n return aTs.localeCompare(bTs);\n });\n\n return buckets;\n}\n\n","/**\n * upgrade.ts — STORY-009-05\n *\n * `cleargate upgrade [--dry-run] [--yes] [--only <tier>]`\n *\n * Three-way merge driver: walks each tracked scaffold file, classifies its\n * drift state, routes by overwrite_policy, and applies the user-chosen merge\n * action. Snapshot is updated atomically after every successfully handled file\n * so the command is resumable (re-run continues from the last clean state).\n *\n * Special-case handling:\n * - CLAUDE.md tier (cli-config): uses claude-md-surgery to merge only the\n * CLEARGATE:START…CLEARGATE:END bounded block, leaving user prose intact.\n * - settings.json tier (cli-config): uses settings-json-surgery to merge\n * only ClearGate-owned hooks, leaving user hooks intact.\n *\n * CRITICAL: all file mutations are atomic (write .tmp → fs.rename).\n * INCREMENTAL: each file is independent. Failure on file N does not roll back\n * file N-1. Re-running re-classifies against the updated snapshot.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n * Inject `promptMergeChoice` + `openInEditor` via `UpgradeCliOptions` for tests\n * (FLASHCARD #cli #determinism #test-seam).\n */\n\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n type ManifestFile,\n type ManifestEntry,\n type DriftMap,\n type Tier,\n} from '../lib/manifest.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { readBlock, writeBlock } from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\nimport {\n promptMergeChoice as defaultPromptMergeChoice,\n type MergeChoice,\n} from '../lib/merge-ui.js';\nimport {\n openInEditor as defaultOpenInEditor,\n containsConflictMarkers,\n} from '../lib/editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface UpgradeCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: override the package root for loadPackageManifest. */\n packageRoot?: string;\n /** Test seam: inject a custom promptMergeChoice (avoids interactive stdin). */\n promptMergeChoice?: typeof defaultPromptMergeChoice;\n /** Test seam: inject a custom openInEditor (avoids forking real editors). */\n openInEditor?: typeof defaultOpenInEditor;\n /** Test seam: inject a custom stdin for promptMergeChoice. */\n stdin?: NodeJS.ReadableStream;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/** Write file atomically: write to .tmp, then rename. Never leaves partial writes. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = filePath + '.tmp.' + Date.now();\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/**\n * Update a single file's sha256 in the install snapshot atomically.\n * Reads the snapshot file, patches the matching entry, and re-writes atomically.\n */\nasync function updateSnapshotEntry(\n projectRoot: string,\n filePath: string,\n newSha: string | null\n): Promise<void> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n let snapshot: ManifestFile;\n\n try {\n const raw = await fsp.readFile(snapshotPath, 'utf-8');\n snapshot = JSON.parse(raw) as ManifestFile;\n } catch {\n // If no snapshot exists yet, cannot update — silently skip.\n return;\n }\n\n const updated: ManifestFile = {\n ...snapshot,\n files: snapshot.files.map((entry) =>\n entry.path === filePath ? { ...entry, sha256: newSha } : entry\n ),\n };\n\n await writeAtomic(snapshotPath, JSON.stringify(updated, null, 2) + '\\n');\n}\n\n/**\n * Determine if this is a CLAUDE.md file (needs block surgery).\n */\nfunction isClaudeMd(filePath: string): boolean {\n return path.basename(filePath) === 'CLAUDE.md';\n}\n\n/**\n * Determine if this is a settings.json file (needs hook surgery).\n */\nfunction isSettingsJson(filePath: string): boolean {\n return path.basename(filePath) === 'settings.json' && filePath.includes('.claude');\n}\n\n/**\n * Merge-3way handler: always-policy — overwrite file with package content.\n * Used for `always` overwrite_policy files.\n */\nasync function applyAlwaysOverwrite(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n stdout: (s: string) => void\n): Promise<void> {\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n try {\n const pkgContent = await fsp.readFile(sourcePath, 'utf-8');\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, pkgContent);\n await updateSnapshotEntry(projectRoot, entry.path, entry.sha256);\n stdout(`[always] overwritten: ${entry.path}`);\n } catch (err) {\n stdout(`[always] error overwriting ${entry.path}: ${(err as Error).message}`);\n }\n}\n\n/**\n * Merge-3way handler for `merge-3way` overwrite_policy files.\n * Supports k/t/e choices with conflict-marker semantics for 'e'.\n * Returns the post-merge sha or null if skipped/failed.\n */\nasync function applyMerge3Way(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n installSha: string | null,\n currentSha: string | null,\n flags: { yes?: boolean; dryRun?: boolean },\n opts: {\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n promptMergeChoiceFn: typeof defaultPromptMergeChoice;\n openInEditorFn: typeof defaultOpenInEditor;\n stdin?: NodeJS.ReadableStream;\n }\n): Promise<{ updated: boolean; newSha: string | null }> {\n const { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin } = opts;\n\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n // Read current (ours) and package (theirs) content\n let ours = '';\n let theirs = '';\n\n try {\n ours = await fsp.readFile(targetPath, 'utf-8');\n } catch {\n // File missing on disk — treat as empty\n ours = '';\n }\n\n try {\n theirs = await fsp.readFile(sourcePath, 'utf-8');\n } catch {\n // Package file missing — skip\n stdout(`[merge] skip: package file not found for ${entry.path}`);\n return { updated: false, newSha: null };\n }\n\n // Compute drift state for display\n const state = classify(entry.sha256, installSha, currentSha, entry.tier);\n\n let choice: MergeChoice;\n\n if (flags.yes) {\n // --yes: auto-take-theirs\n choice = 't';\n stdout(`[yes] taking theirs: ${entry.path} state=${state}`);\n } else {\n // Interactive prompt\n choice = await promptMergeChoiceFn({\n path: entry.path,\n state,\n ours,\n theirs,\n stdin,\n stdout,\n });\n }\n\n if (choice === 'k') {\n // Keep mine: file unchanged, snapshot records current_sha as installed_sha\n stdout(`[keep] ${entry.path}`);\n await updateSnapshotEntry(projectRoot, entry.path, currentSha);\n return { updated: true, newSha: currentSha };\n }\n\n if (choice === 't') {\n // Take theirs: apply CLAUDE.md or settings.json surgery if needed, else raw overwrite\n let mergedContent = theirs;\n\n if (isClaudeMd(entry.path)) {\n // Merge only the bounded block; preserve user prose\n try {\n const ourBlock = readBlock(ours);\n const theirBlock = readBlock(theirs);\n if (ourBlock !== null && theirBlock !== null) {\n mergedContent = writeBlock(ours, theirBlock);\n } else if (theirBlock !== null) {\n // No block in ours — full overwrite\n mergedContent = theirs;\n }\n // If no block in theirs, skip (no ClearGate content to take)\n } catch {\n // Surgery failed — fall back to full overwrite\n mergedContent = theirs;\n }\n } else if (isSettingsJson(entry.path)) {\n // Merge only ClearGate-owned hooks; preserve user hooks\n try {\n const ourSettings = JSON.parse(ours) as ClaudeSettings;\n const theirSettings = JSON.parse(theirs) as ClaudeSettings;\n // Remove ClearGate hooks from ours, then add ClearGate hooks from theirs\n const withoutOurCg = removeClearGateHooks(ourSettings);\n // Build merged: user hooks from ours + ClearGate hooks from theirs\n const cgHooks = theirSettings.hooks ?? {};\n const merged: ClaudeSettings = { ...withoutOurCg };\n if (Object.keys(cgHooks).length > 0) {\n merged.hooks = { ...(withoutOurCg.hooks ?? {}), ...cgHooks };\n }\n mergedContent = JSON.stringify(merged, null, 2) + '\\n';\n } catch {\n // Surgery failed — full overwrite\n mergedContent = theirs;\n }\n }\n\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, mergedContent);\n const newSha = hashNormalized(mergedContent);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[take] ${entry.path}`);\n return { updated: true, newSha };\n }\n\n // choice === 'e': write conflict-marker file + open editor\n const mergeFilePath = targetPath + '.cleargate-merge';\n const conflictContent =\n `<<<<<<< ours (installed)\\n${ours}=======\\n${theirs}>>>>>>> theirs (upstream)\\n`;\n\n await fsp.mkdir(path.dirname(mergeFilePath), { recursive: true });\n await writeAtomic(mergeFilePath, conflictContent);\n\n try {\n const result = await openInEditorFn(mergeFilePath);\n if (result.exitCode !== 0) {\n stderr(`[edit] editor exited with code ${result.exitCode}; markers may remain in ${mergeFilePath}`);\n }\n } catch (err) {\n stderr(`[edit] could not open editor: ${(err as Error).message}`);\n stderr(`[edit] resolve markers manually in: ${mergeFilePath}`);\n return { updated: false, newSha: null };\n }\n\n // Read post-edit content\n let edited = '';\n try {\n edited = await fsp.readFile(mergeFilePath, 'utf-8');\n } catch {\n stderr(`[edit] could not read ${mergeFilePath} after editor exit`);\n return { updated: false, newSha: null };\n }\n\n if (containsConflictMarkers(edited)) {\n stderr(`[edit] unresolved conflict markers remain in ${mergeFilePath}`);\n stderr(`[edit] file NOT updated. Resolve manually and re-run upgrade.`);\n // Leave .cleargate-merge in place for manual resolution\n return { updated: false, newSha: null };\n }\n\n // Markers resolved — overwrite target file, remove merge file\n await writeAtomic(targetPath, edited);\n try {\n await fsp.unlink(mergeFilePath);\n } catch {\n // Non-fatal if cleanup fails\n }\n\n const newSha = hashNormalized(edited);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[edit] resolved: ${entry.path}`);\n return { updated: true, newSha };\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function upgradeHandler(\n flags: { dryRun?: boolean; yes?: boolean; only?: string },\n cli?: UpgradeCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n const promptMergeChoiceFn = cli?.promptMergeChoice ?? defaultPromptMergeChoice;\n const openInEditorFn = cli?.openInEditor ?? defaultOpenInEditor;\n const stdin = cli?.stdin;\n\n // ─── 1. Load manifests + compute drift ──────────────────────────────────────\n\n let pkgManifest: ManifestFile;\n try {\n pkgManifest = loadPackageManifest({ packageRoot: cli?.packageRoot });\n } catch (err) {\n stderr(`[upgrade] ${(err as Error).message}`);\n exit(1);\n return;\n }\n\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // Build a lookup from the snapshot\n const snapshotByPath = new Map<string, string | null>();\n for (const entry of installSnapshot?.files ?? []) {\n snapshotByPath.set(entry.path, entry.sha256);\n }\n\n // ─── 2. Apply --only <tier> filter ──────────────────────────────────────────\n\n const onlyTier: Tier | undefined = flags.only as Tier | undefined;\n const filteredFiles = onlyTier\n ? pkgManifest.files.filter((e) => e.tier === onlyTier)\n : pkgManifest.files;\n\n // ─── 3. Classify all files ──────────────────────────────────────────────────\n\n interface FileWork {\n entry: ManifestEntry;\n currentSha: string | null;\n installSha: string | null;\n action: 'overwrite' | 'skip' | 'merge-3way';\n }\n\n const workItems: FileWork[] = [];\n\n await Promise.all(\n filteredFiles.map(async (entry) => {\n if (entry.tier === 'user-artifact') {\n // Always skip user-artifact tier (never tracked)\n return;\n }\n\n const currentSha = await computeCurrentSha(entry, cwd);\n const installSha = snapshotByPath.get(entry.path) ?? null;\n\n let action: FileWork['action'];\n switch (entry.overwrite_policy) {\n case 'always':\n action = 'overwrite';\n break;\n case 'skip':\n case 'preserve':\n action = 'skip';\n break;\n case 'merge-3way':\n default:\n action = 'merge-3way';\n break;\n }\n\n workItems.push({ entry, currentSha, installSha, action });\n })\n );\n\n // Sort for deterministic output\n workItems.sort((a, b) => a.entry.path.localeCompare(b.entry.path));\n\n // ─── 4. --dry-run: print plan and exit ──────────────────────────────────────\n\n if (flags.dryRun) {\n let count = 0;\n for (const item of workItems) {\n const state = classify(item.entry.sha256, item.installSha, item.currentSha, item.entry.tier);\n stdout(`[dry-run] ${item.entry.path} action=${item.action} state=${state}`);\n count++;\n }\n stdout(`[dry-run] ${count} files planned. No changes made.`);\n return;\n }\n\n // ─── 5. Execute per file ─────────────────────────────────────────────────────\n\n // Determine package root for reading source files.\n // cli.packageRoot is the test seam (always injected in tests).\n // In production, use the same default resolution as loadPackageManifest.\n // Since we already loaded the manifest above (which resolves the path internally),\n // we simply use cli.packageRoot when provided, otherwise fall back to cwd\n // (in production the actual resolution is inside loadPackageManifest).\n const packageRoot = cli?.packageRoot ?? cwd;\n\n const driftMap: DriftMap = {};\n\n for (const item of workItems) {\n const { entry, currentSha, installSha, action } = item;\n\n switch (action) {\n case 'skip': {\n // never / preserve — no prompt, no write\n stdout(`[skip] ${entry.path} policy=${entry.overwrite_policy}`);\n break;\n }\n\n case 'overwrite': {\n // always — overwrite silently\n await applyAlwaysOverwrite(entry, cwd, packageRoot, stdout);\n break;\n }\n\n case 'merge-3way': {\n // Interactive 3-way merge (unless --yes)\n await applyMerge3Way(\n entry,\n cwd,\n packageRoot,\n installSha,\n currentSha,\n { yes: flags.yes, dryRun: false },\n { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin }\n );\n break;\n }\n }\n\n // Re-compute current sha after potential mutation (for drift map)\n const postSha = await computeCurrentSha(entry, cwd);\n driftMap[entry.path] = {\n state: classify(entry.sha256, installSha, postSha, entry.tier),\n entry,\n install_sha: installSha,\n current_sha: postSha,\n package_sha: entry.sha256,\n };\n }\n\n // ─── 6. Refresh .drift-state.json ────────────────────────────────────────────\n\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n stdout('[upgrade] complete.');\n}\n","export const CLEARGATE_START = '<!-- CLEARGATE:START -->';\nexport const CLEARGATE_END = '<!-- CLEARGATE:END -->';\n\n// IMPORTANT: regex MUST be GREEDY ([\\s\\S]* not [\\s\\S]*?)\n// The block body itself may reference both markers in prose (FLASHCARD 2026-04-19 #init #inject-claude-md #regex).\n// Non-greedy would stop at the first inline END marker in prose, cutting off the real block.\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->([\\s\\S]*)<!-- CLEARGATE:END -->/;\n\n/**\n * Returns the content between CLEARGATE:START and CLEARGATE:END markers.\n * Returns null if either marker is missing.\n */\nexport function readBlock(content: string): string | null {\n const match = BLOCK_REGEX.exec(content);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Replaces the content between CLEARGATE:START and CLEARGATE:END markers,\n * preserving the markers themselves and all surrounding content.\n * Throws if markers are missing.\n */\nexport function writeBlock(content: string, newBlockBody: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, `${CLEARGATE_START}${newBlockBody}${CLEARGATE_END}`);\n}\n\n/**\n * Removes both markers AND the content between them, leaving surrounding content intact.\n * Throws if markers are missing.\n */\nexport function removeBlock(content: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, '');\n}\n","export interface HookCommand {\n type: 'command';\n command: string;\n if?: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n}\n\nexport interface ClaudeSettings {\n hooks?: {\n PostToolUse?: HookEntry[];\n SessionStart?: HookEntry[];\n SubagentStop?: HookEntry[];\n [k: string]: HookEntry[] | undefined;\n };\n [k: string]: unknown;\n}\n\n/**\n * Returns true if the given command string belongs to ClearGate.\n * Matches:\n * - .claude/hooks/token-ledger.sh\n * - .claude/hooks/stamp-and-gate.sh\n * - .claude/hooks/session-start.sh\n * - .claude/hooks/wiki-ingest.sh (legacy)\n * - .claude/hooks/cleargate-*.sh (catch-all for future hooks)\n * - inline commands containing 'wiki ingest' (legacy SPRINT-04 PostToolUse inline)\n */\nfunction isClearGateCommand(command: string): boolean {\n if (command.includes('wiki ingest')) return true;\n return /\\/\\.claude\\/hooks\\/(token-ledger|stamp-and-gate|session-start|wiki-ingest|cleargate-[^/]*)\\.sh/.test(command);\n}\n\n/**\n * Removes ClearGate-owned hook entries from a ClaudeSettings object.\n * - Removes individual inner `hooks[]` sub-entries whose `command` matches ClearGate patterns.\n * - If the parent HookEntry's `hooks[]` array becomes empty, removes that HookEntry.\n * - If an event-category array (e.g. hooks.PostToolUse) becomes empty, removes the key.\n * - Preserves all other hook entries and all other top-level keys.\n */\nexport function removeClearGateHooks(settings: ClaudeSettings): ClaudeSettings {\n if (!settings.hooks) return { ...settings };\n\n const newHooks: NonNullable<ClaudeSettings['hooks']> = {};\n\n for (const [eventName, entries] of Object.entries(settings.hooks)) {\n if (!entries) continue;\n\n const filteredEntries: HookEntry[] = [];\n\n for (const entry of entries) {\n if (!entry.hooks || entry.hooks.length === 0) {\n // No inner hooks — keep as-is (not a ClearGate entry)\n filteredEntries.push(entry);\n continue;\n }\n\n const remainingInnerHooks = entry.hooks.filter(\n (h) => !isClearGateCommand(h.command)\n );\n\n if (remainingInnerHooks.length === 0) {\n // All inner hooks were ClearGate — drop this HookEntry entirely\n continue;\n }\n\n if (remainingInnerHooks.length === entry.hooks.length) {\n // No change — keep original entry reference\n filteredEntries.push(entry);\n } else {\n // Some removed — keep entry with remaining inner hooks\n filteredEntries.push({ ...entry, hooks: remainingInnerHooks });\n }\n }\n\n if (filteredEntries.length > 0) {\n newHooks[eventName] = filteredEntries;\n }\n // If filteredEntries is empty, skip the key entirely\n }\n\n const result: ClaudeSettings = { ...settings };\n\n if (Object.keys(newHooks).length > 0) {\n result.hooks = newHooks;\n } else {\n delete result.hooks;\n }\n\n return result;\n}\n\n/**\n * Returns true if settings contains any ClearGate-owned hook entries.\n */\nexport function hasClearGateHooks(settings: ClaudeSettings): boolean {\n if (!settings.hooks) return false;\n\n for (const entries of Object.values(settings.hooks)) {\n if (!entries) continue;\n for (const entry of entries) {\n if (!entry.hooks) continue;\n for (const h of entry.hooks) {\n if (isClearGateCommand(h.command)) return true;\n }\n }\n }\n\n return false;\n}\n","/**\n * merge-ui.ts — STORY-009-05\n *\n * Diff renderer + 3-choice interactive prompt for `cleargate upgrade`.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { createPatch } from 'diff';\nimport type { DriftState } from './manifest.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeChoice = 'k' | 't' | 'e';\n\n// ─── Diff renderer ────────────────────────────────────────────────────────────\n\n/**\n * Render an inline unified diff of `ours` vs `theirs` for the given `filePath`.\n * Uses the `diff` npm package's `createPatch`.\n */\nexport function renderInlineDiff(ours: string, theirs: string, filePath: string): string {\n return createPatch(filePath, ours, theirs, 'installed', 'upstream');\n}\n\n// ─── Prompt ───────────────────────────────────────────────────────────────────\n\n/**\n * Print file info + inline diff, then prompt the user for a merge choice.\n * Returns one of: 'k' (keep mine), 't' (take theirs), 'e' (edit in $EDITOR).\n *\n * Test seam: pass `stdin` to drive from a buffer instead of process.stdin.\n * Test seam: pass `stdout` to capture output instead of process.stdout.write.\n */\nexport async function promptMergeChoice(opts: {\n path: string;\n state: DriftState;\n ours: string;\n theirs: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n}): Promise<MergeChoice> {\n const { path: filePath, state, ours, theirs } = opts;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stdin = opts.stdin ?? process.stdin;\n\n // Print file header\n stdout(`\\n[merge] ${filePath} state=${state}\\n`);\n\n // Print diff\n const patch = renderInlineDiff(ours, theirs, filePath);\n stdout(patch + '\\n');\n\n // Prompt\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR: ');\n\n return new Promise<MergeChoice>((resolve, reject) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n const answer = buf.slice(0, newline).trim().toLowerCase();\n stdin.removeListener('data', onData);\n stdin.removeListener('error', onError);\n if (answer === 'k' || answer === 't' || answer === 'e') {\n resolve(answer as MergeChoice);\n } else {\n // Default to 'k' (keep mine) for unrecognised input\n stdout(`Unknown choice '${answer}'; defaulting to [k]eep mine.\\n`);\n resolve('k');\n }\n }\n };\n\n const onError = (err: Error) => {\n stdin.removeListener('data', onData);\n reject(err);\n };\n\n stdin.on('data', onData);\n stdin.once('error', onError);\n });\n}\n","/**\n * editor.ts — STORY-009-05\n *\n * Safely spawn $EDITOR for the `cleargate upgrade` edit-in-editor flow.\n * Uses child_process.spawn with stdio: 'inherit' — does NOT use execSync\n * (would block the event loop and prevent test injection).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { spawn } from 'node:child_process';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Spawn $EDITOR for `filePath`, waiting for the editor process to exit.\n *\n * Returns the editor's exit code.\n * If `opts.editor` is provided, uses it instead of $EDITOR.\n * If neither is set, returns `{ exitCode: -1 }` with an error (handled by caller).\n *\n * Uses `stdio: 'inherit'` so the terminal is fully connected to the editor.\n * Do NOT use execSync — it blocks the event loop and breaks tests.\n */\nexport async function openInEditor(\n filePath: string,\n opts?: { editor?: string; env?: NodeJS.ProcessEnv }\n): Promise<{ exitCode: number }> {\n const env = opts?.env ?? process.env;\n const editor = opts?.editor ?? env['EDITOR'] ?? env['VISUAL'];\n\n if (!editor) {\n throw new Error('$EDITOR not set; cannot [e]dit option. Set the EDITOR environment variable.');\n }\n\n return new Promise<{ exitCode: number }>((resolve, reject) => {\n const child = spawn(editor, [filePath], {\n stdio: 'inherit',\n env: { ...env },\n });\n\n child.on('error', (err) => {\n reject(new Error(`Failed to start editor '${editor}': ${err.message}`));\n });\n\n child.on('close', (code) => {\n resolve({ exitCode: code ?? 0 });\n });\n });\n}\n\n/**\n * Return true if `content` contains unresolved conflict markers.\n *\n * Checks for any of:\n * <<<<<<< ours\n * =======\n * >>>>>>> theirs\n */\nexport function containsConflictMarkers(content: string): boolean {\n return (\n content.includes('<<<<<<< ours') ||\n content.includes('>>>>>>> theirs') ||\n // Also catch generic git-style markers in case user merged manually\n /^<<<<<<< /m.test(content) ||\n /^>>>>>>> /m.test(content)\n );\n}\n","/**\n * uninstall.ts — STORY-009-07\n *\n * `cleargate uninstall [--dry-run] [--preserve <tiers>] [--remove <tiers>] [--yes] [--path <dir>] [--force]`\n *\n * Most-destructive command in the CLI. Preservation-first: user artifacts\n * (FLASHCARD, archive, pending-sync, sprint retrospectives) are kept by default.\n * Framework files (knowledge, templates, wiki, hook-log, agents, hooks, skills)\n * are removed. CLAUDE.md and settings.json are surgically stripped — the rest\n * of those files is left intact.\n *\n * Safety rails:\n * - Typed confirmation: user must type the project name (or pass --yes).\n * - Uncommitted-changes check: git status --porcelain on manifest-tracked files.\n * - --dry-run: preview only, zero disk writes.\n * - Single-target: does NOT recurse into nested .cleargate/ directories.\n * - Idempotency: if .uninstalled marker exists and .install-manifest.json is absent → \"already uninstalled\".\n *\n * All test-facing behaviours are injectable via UninstallOptions seams.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport {\n loadInstallSnapshot,\n type ManifestFile,\n type ManifestEntry,\n type Tier,\n} from '../lib/manifest.js';\nimport {\n removeBlock,\n CLEARGATE_START,\n CLEARGATE_END,\n} from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Tiers that belong to \"user-artifact\" bucket (preserved by default). */\nconst USER_ARTIFACT_TIERS: Tier[] = ['user-artifact'];\n\n/** Framework tiers that are removed by default. */\nconst FRAMEWORK_TIERS: Tier[] = ['protocol', 'template', 'agent', 'hook', 'skill', 'cli-config', 'derived'];\n\nexport interface UninstallOptions {\n cwd?: string;\n path?: string; // resolved target dir (defaults to cwd)\n dryRun?: boolean;\n yes?: boolean; // skip typed confirmation\n force?: boolean; // override uncommitted-changes refusal\n preserve?: string[]; // tier ids to force-preserve (comma-split at CLI level)\n remove?: string[]; // tier ids to force-remove (comma-split at CLI level, 'all' = all tiers)\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: prompt for typed project-name confirmation. */\n promptName?: () => Promise<string>;\n /** Test seam: prompt yes/no for interactive category decisions. */\n promptYesNo?: (q: string) => Promise<boolean>;\n /** Test seam: injectable clock. */\n now?: () => Date;\n /** Test seam: git status --porcelain runner. Returns { stdout, code }. */\n git?: (args: string[]) => { stdout: string; code: number };\n}\n\nexport interface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n removed: string[];\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse a tier list string (comma-separated) into a Set.\n * 'all' expands to all known tiers.\n */\nfunction parseTierList(raw: string[]): Set<Tier> {\n const result = new Set<Tier>();\n for (const item of raw) {\n for (const t of item.split(',')) {\n const tier = t.trim();\n if (tier === 'all') {\n for (const f of FRAMEWORK_TIERS) result.add(f);\n for (const u of USER_ARTIFACT_TIERS) result.add(u);\n } else {\n result.add(tier as Tier);\n }\n }\n }\n return result;\n}\n\n/**\n * Determine if an entry should be preserved (true) or removed (false).\n *\n * Rules (highest priority first):\n * 1. --remove tier override → remove\n * 2. --preserve tier override → preserve\n * 3. user-artifact tier → preserve (default)\n * 4. Framework tiers (protocol/template/agent/hook/skill/cli-config/derived) → remove (default)\n */\nexport function shouldPreserve(\n entry: ManifestEntry,\n preserveSet: Set<Tier>,\n removeSet: Set<Tier>\n): boolean {\n if (removeSet.has(entry.tier)) return false;\n if (preserveSet.has(entry.tier)) return true;\n if (USER_ARTIFACT_TIERS.includes(entry.tier)) return true;\n return false;\n}\n\n/**\n * Read the project name from package.json (name field), or fall back to\n * the basename of the target directory.\n */\nfunction resolveProjectName(target: string): string {\n const pkgPath = path.join(target, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf-8');\n const parsed = JSON.parse(raw) as { name?: string };\n if (parsed.name && typeof parsed.name === 'string') {\n return parsed.name;\n }\n } catch {\n // Fall through to basename\n }\n }\n return path.basename(target);\n}\n\n/**\n * Run git status --porcelain in the target directory, filtered to manifest-tracked paths.\n * Returns the list of uncommitted files that overlap with the manifest.\n *\n * Non-git targets return an empty array (skip silently per orchestrator decision).\n */\nfunction detectUncommittedChanges(\n target: string,\n manifestPaths: string[],\n gitRunner?: UninstallOptions['git']\n): string[] {\n const run = gitRunner ?? ((args: string[]) => {\n try {\n const out = execSync(['git', ...args].join(' '), {\n cwd: target,\n stdio: ['pipe', 'pipe', 'pipe'],\n encoding: 'utf-8',\n });\n return { stdout: out, code: 0 };\n } catch (e: unknown) {\n const err = e as { stdout?: string; status?: number };\n return { stdout: err.stdout ?? '', code: err.status ?? 1 };\n }\n });\n\n // First check if this is a git repo at all\n const isGit = run(['-C', target, 'rev-parse', '--is-inside-work-tree']);\n if (isGit.code !== 0) {\n // Not a git repo — skip silently (orchestrator decision)\n return [];\n }\n\n const result = run(['-C', target, 'status', '--porcelain']);\n if (result.code !== 0) {\n return [];\n }\n\n // Parse porcelain output — each line: XY path\n const changedFiles = result.stdout\n .split('\\n')\n .filter(Boolean)\n .map((line) => line.slice(3).trim());\n\n // Only flag files that are listed in the install manifest\n const manifestSet = new Set(manifestPaths);\n return changedFiles.filter((f) => manifestSet.has(f));\n}\n\n/**\n * Remove @cleargate/cli from package.json dependencies/devDependencies.\n * Writes back if modified. Returns true if modified.\n */\nasync function removeFromPackageJson(target: string, dryRun: boolean): Promise<boolean> {\n const pkgPath = path.join(target, 'package.json');\n if (!fs.existsSync(pkgPath)) return false;\n\n let raw: string;\n try {\n raw = await fsp.readFile(pkgPath, 'utf-8');\n } catch {\n return false;\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n let modified = false;\n\n for (const key of ['dependencies', 'devDependencies'] as const) {\n const deps = parsed[key];\n if (deps && typeof deps === 'object' && '@cleargate/cli' in (deps as Record<string, unknown>)) {\n const updated = { ...(deps as Record<string, unknown>) };\n delete updated['@cleargate/cli'];\n parsed[key] = updated;\n modified = true;\n }\n }\n\n if (modified && !dryRun) {\n await fsp.writeFile(pkgPath, JSON.stringify(parsed, null, 2) + '\\n', 'utf-8');\n }\n\n return modified;\n}\n\n/** Atomically write a file (tmp → rename). */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/** Remove a file, silently ignoring missing files. */\nasync function removeFile(filePath: string): Promise<void> {\n try {\n await fsp.unlink(filePath);\n } catch {\n // Ignore ENOENT and other errors\n }\n}\n\n/** Remove a directory tree, silently ignoring missing directories. */\nasync function removeDir(dirPath: string): Promise<void> {\n try {\n fs.rmSync(dirPath, { recursive: true, force: true });\n } catch {\n // Ignore\n }\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function uninstallHandler(opts: UninstallOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = opts.exit ?? ((code: number) => process.exit(code) as never);\n const now = opts.now ?? (() => new Date());\n const dryRun = opts.dryRun ?? false;\n const yes = opts.yes ?? false;\n const force = opts.force ?? false;\n\n // Parse tier overrides\n const preserveSet = parseTierList(opts.preserve ?? []);\n const removeSet = parseTierList(opts.remove ?? []);\n const removeAll = (opts.remove ?? []).some((r) => r === 'all');\n if (removeAll) {\n for (const t of FRAMEWORK_TIERS) removeSet.add(t);\n for (const u of USER_ARTIFACT_TIERS) removeSet.add(u);\n }\n\n // ─── 1. Resolve target ────────────────────────────────────────────────────────\n\n const target = opts.path ? path.resolve(opts.path) : cwd;\n const cleargateDir = path.join(target, '.cleargate');\n const manifestPath = path.join(cleargateDir, '.install-manifest.json');\n const uninstalledPath = path.join(cleargateDir, '.uninstalled');\n\n // ─── 2. Missing manifest → no install detected ────────────────────────────────\n\n if (!fs.existsSync(manifestPath)) {\n // Check idempotency: if .uninstalled exists without manifest → already done\n if (fs.existsSync(uninstalledPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 3. Idempotency short-circuit ────────────────────────────────────────────\n\n if (fs.existsSync(uninstalledPath) && !fs.existsSync(manifestPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n\n // ─── 4. Load install manifest ────────────────────────────────────────────────\n\n const snapshot: ManifestFile | null = await loadInstallSnapshot(target);\n if (!snapshot) {\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 5. Uncommitted-changes check ────────────────────────────────────────────\n\n if (!force) {\n const manifestPaths = snapshot.files.map((e) => e.path);\n const uncommitted = detectUncommittedChanges(target, manifestPaths, opts.git);\n if (uncommitted.length > 0) {\n stderr(\n `Uncommitted changes to tracked files: ${uncommitted.slice(0, 5).join(', ')}${uncommitted.length > 5 ? ` and ${uncommitted.length - 5} more` : ''}. Commit, stash, or pass --force.`\n );\n exit(1);\n return;\n }\n }\n\n // ─── 6. CLAUDE.md marker check ───────────────────────────────────────────────\n\n const claudeMdPath = path.join(target, 'CLAUDE.md');\n let claudeMdContent: string | null = null;\n\n if (fs.existsSync(claudeMdPath)) {\n claudeMdContent = fs.readFileSync(claudeMdPath, 'utf-8');\n if (!claudeMdContent.includes(CLEARGATE_START)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n exit(1);\n return;\n }\n if (!claudeMdContent.includes(CLEARGATE_END)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n exit(1);\n return;\n }\n }\n\n // ─── 7. Classify entries ──────────────────────────────────────────────────────\n\n const toRemove: ManifestEntry[] = [];\n const toPreserve: ManifestEntry[] = [];\n const toSkip: ManifestEntry[] = []; // missing from disk\n\n for (const entry of snapshot.files) {\n const filePath = path.join(target, entry.path);\n if (!fs.existsSync(filePath)) {\n toSkip.push(entry);\n continue;\n }\n if (shouldPreserve(entry, preserveSet, removeSet)) {\n toPreserve.push(entry);\n } else {\n toRemove.push(entry);\n }\n }\n\n // ─── 8. Preview summary ───────────────────────────────────────────────────────\n\n stdout(`Will remove ${toRemove.length} files, keep ${toPreserve.length} files, update CLAUDE.md to strip CLEARGATE block, remove @cleargate/cli from package.json.`);\n\n if (dryRun) {\n stdout('');\n stdout('[dry-run] Planned removals:');\n for (const e of toRemove) {\n stdout(` [remove] ${e.path}`);\n }\n stdout('');\n stdout('[dry-run] Planned preservations:');\n for (const e of toPreserve) {\n stdout(` [keep] ${e.path}`);\n }\n if (toSkip.length > 0) {\n stdout('');\n stdout('[dry-run] Untracked (already missing on disk):');\n for (const e of toSkip) {\n stdout(` [skip] ${e.path}`);\n }\n }\n stdout('');\n stdout('[dry-run] No files changed.');\n exit(0);\n return;\n }\n\n // ─── 9. Typed confirmation (skipped with --yes) ───────────────────────────────\n\n if (!yes) {\n const projectName = resolveProjectName(target);\n\n const promptNameFn = opts.promptName ?? (() => {\n // Real interactive prompt — readline\n return new Promise<string>((resolve) => {\n process.stdout.write(`Type the project name \"${projectName}\" to confirm uninstall: `);\n let buf = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.once('data', (chunk: Buffer | string) => {\n buf = chunk.toString().trim();\n resolve(buf);\n });\n });\n });\n\n const typed = await promptNameFn();\n if (typed !== projectName) {\n stdout('name mismatch — aborting');\n exit(1);\n return;\n }\n }\n\n // ─── 10. Execute removal ──────────────────────────────────────────────────────\n\n const removedPaths: string[] = [];\n const preservedPaths: string[] = [];\n\n // Remove classified entries\n for (const entry of toRemove) {\n const filePath = path.join(target, entry.path);\n await removeFile(filePath);\n removedPaths.push(entry.path);\n }\n\n // Collect preserved paths\n for (const entry of toPreserve) {\n preservedPaths.push(entry.path);\n }\n\n // Surgery: CLAUDE.md — strip only the CLEARGATE block\n if (claudeMdContent !== null) {\n try {\n const stripped = removeBlock(claudeMdContent);\n await writeAtomic(claudeMdPath, stripped);\n removedPaths.push('CLAUDE.md (CLEARGATE block)');\n } catch (err) {\n stderr(`Warning: could not strip CLAUDE.md block: ${(err as Error).message}`);\n }\n }\n\n // Surgery: settings.json — remove ClearGate hooks only\n const settingsPath = path.join(target, '.claude', 'settings.json');\n if (fs.existsSync(settingsPath)) {\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as ClaudeSettings;\n const cleaned = removeClearGateHooks(settings);\n await writeAtomic(settingsPath, JSON.stringify(cleaned, null, 2) + '\\n');\n removedPaths.push('.claude/settings.json (ClearGate hooks)');\n } catch (err) {\n stderr(`Warning: could not update settings.json: ${(err as Error).message}`);\n }\n }\n\n // Remove @cleargate/cli from package.json\n const pkgModified = await removeFromPackageJson(target, false);\n if (pkgModified) {\n removedPaths.push('package.json (@cleargate/cli dep)');\n stdout('Removed @cleargate/cli from package.json. Run `npm install` to update package-lock.json.');\n }\n\n // Remove the install manifest itself (and drift-state)\n await removeFile(manifestPath);\n await removeFile(path.join(cleargateDir, '.drift-state.json'));\n\n // ─── 11. Write .uninstalled marker ───────────────────────────────────────────\n\n const marker: UninstalledMarker = {\n uninstalled_at: now().toISOString(),\n prior_version: snapshot.cleargate_version,\n preserved: preservedPaths,\n removed: removedPaths,\n };\n\n // Ensure .cleargate/ dir still exists (may have had files removed from it)\n await fsp.mkdir(cleargateDir, { recursive: true });\n await writeAtomic(uninstalledPath, JSON.stringify(marker, null, 2) + '\\n');\n\n // ─── 12. Empty .cleargate/ cleanup ───────────────────────────────────────────\n\n // Per story Gherkin scenario 11: when --remove all removes all user-artifact\n // items too (nothing preserved inside .cleargate/), the directory itself is\n // removed (including the .uninstalled marker we just wrote).\n // In the default case (preservations exist), we leave .cleargate/ with the\n // preserved files + .uninstalled marker.\n if (removeAll) {\n const hasPreservedInsideCleargate = preservedPaths.some((p) =>\n p.startsWith('.cleargate/')\n );\n if (!hasPreservedInsideCleargate) {\n // Remove the entire .cleargate/ directory (including the marker we wrote)\n await removeDir(cleargateDir);\n }\n }\n\n // ─── 13. Restore hint ────────────────────────────────────────────────────────\n\n if (preservedPaths.length > 0) {\n stdout(`Preserved ${preservedPaths.length} items. Run cleargate init in this directory to restore.`);\n }\n}\n","/**\n * sync.ts — STORY-010-04\n *\n * `cleargate sync` — canonical 6-step driver:\n * 1. Identity + sprint resolve\n * 2. List remote updates + pull each item\n * (3. STORY-010-05 insertion point: runIntakeBranch)\n * 4. Classify conflicts per local work items\n * 5. Resolve (merge prompt / silent merge / remote-wins / refuse)\n * 6. Apply + log\n *\n * R2 invariant: ALL pulls complete before ANY push begins.\n *\n * --dry-run: steps 1–4 only; zero fs writes; zero sync-log entries.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { classify } from '../lib/conflict-detector.js';\nimport type { SinceLastSync, LocalSnapshot, RemoteSnapshot } from '../lib/conflict-detector.js';\nimport { promptThreeWayMerge } from '../lib/merge-helper.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteUpdateRef } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { runIntakeBranch } from '../lib/intake.js';\nimport type { IntakeResult } from '../lib/intake.js';\nimport { resolveActiveItems } from '../lib/active-criteria.js';\nimport type { LocalWorkItemRef } from '../lib/active-criteria.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\nimport type { RemoteComment } from '../lib/mcp-client.js';\n\n// ── Public Options ─────────────────────────────────────────────────────────────\n\nexport interface SyncOptions {\n dryRun?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (bypasses acquireAccessToken) */\n mcp?: McpClient;\n /** Test seam: override process.stdin for merge prompt */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── SyncCheckOptions ──────────────────────────────────────────────────────────\n\nexport interface SyncCheckOptions {\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n/**\n * syncCheckHandler — `cleargate sync --check`\n *\n * Hook-safe read-only drift probe. Exits 0 on ALL failure paths.\n * Emits a single JSON line to stdout: {\"updates\":<N>,\"since\":\"<iso>\"} on success\n * or {\"updates\":0,\"error\":\"<msg>\"} on failure.\n *\n * Never writes sync-log entries, never touches .conflicts.json, never pushes.\n * Updates .cleargate/.sync-marker.json with last_check timestamp (even on error,\n * to throttle repeat attempts).\n *\n * FLASHCARD: #hook-safe #sync-check #exit-code\n * syncHandler exits 2 on missing token/URL/adapter. This handler MUST NOT.\n */\nexport async function syncCheckHandler(opts: SyncCheckOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n const markerPath = path.join(projectRoot, '.cleargate', '.sync-marker.json');\n\n // Helper: write marker atomically, even on error (throttle repeat calls)\n const updateMarker = async (nowIso: string): Promise<void> => {\n try {\n const content = JSON.stringify({ last_check: nowIso });\n await fsPromises.mkdir(path.dirname(markerPath), { recursive: true });\n const tmpPath = `${markerPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, markerPath);\n } catch {\n // Ignore marker write failures — do not surface to caller\n }\n };\n\n // Helper: emit error JSON and exit 0\n const emitError = async (msg: string, nowIso: string): Promise<void> => {\n stdout(JSON.stringify({ updates: 0, error: msg.slice(0, 200) }) + '\\n');\n await updateMarker(nowIso);\n };\n\n const nowIso = nowFn();\n\n // Read last_check from marker; fall back to epoch\n let since = '1970-01-01T00:00:00.000Z';\n try {\n const raw = await fsPromises.readFile(markerPath, 'utf8');\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (typeof parsed['last_check'] === 'string') {\n since = parsed['last_check'];\n }\n } catch {\n // No marker file or parse error — use epoch\n }\n\n // Resolve MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n // Acquire token — hook-safe: any AcquireError → emitError, exit 0\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Pre-flight: adapter info (but don't exit 2 — emit error JSON instead)\n try {\n const adapterInfo = await mcp.adapterInfo();\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n } catch {\n // If adapterInfo fails, proceed — older MCP may not have this tool\n }\n\n // Call list_remote_updates\n let refs: RemoteUpdateRef[];\n try {\n refs = await mcp.call<RemoteUpdateRef[]>('cleargate_list_remote_updates', { since });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n await emitError(msg, nowIso);\n return;\n }\n\n // Success path\n stdout(JSON.stringify({ updates: refs.length, since }) + '\\n');\n await updateMarker(nowIso);\n}\n\n// ── ConflictJson shape ────────────────────────────────────────────────────────\n\nexport interface ConflictEntry {\n item_id: string;\n remote_id: string;\n state: string;\n resolution: string;\n reason: string;\n local_path: string;\n}\n\nexport interface ConflictsJson {\n generated_at: string;\n sprint_id: string;\n unresolved: ConflictEntry[];\n}\n\n// ── Main handler ──────────────────────────────────────────────────────────────\n\nexport async function syncHandler(opts: SyncOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const dryRun = opts.dryRun ?? false;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // ── Step 1: Identity + sprint resolve ───────────────────────────────────────\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintRoot);\n\n // ── MCP client setup ────────────────────────────────────────────────────────\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n if (err.code === 'token_revoked') {\n stderr(`Error: ${err.message}\\n`);\n } else if (err.code === 'no_stored_token' || err.code === 'invalid_token') {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${err.message}\\n`);\n }\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Pre-flight: adapter-info probe ──────────────────────────────────────────\n let adapterInfo: { configured: boolean; name: string };\n try {\n adapterInfo = await mcp.adapterInfo();\n } catch {\n // Tool may not exist on older MCP versions — warn and proceed\n adapterInfo = { configured: true, name: 'unknown' };\n }\n\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n stderr(\n 'Error: ClearGate MCP has no PM adapter configured (LINEAR_API_KEY missing server-side). ' +\n 'Sync cannot proceed.\\n',\n );\n exit(2);\n return;\n }\n\n // ── Step 2: List remote updates + pull ──────────────────────────────────────\n // Read last_remote_sync from wiki meta or use epoch\n const wikiMetaPath = path.join(projectRoot, '.cleargate', 'wiki', 'meta.json');\n let lastRemoteSync = '1970-01-01T00:00:00.000Z';\n try {\n const metaRaw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n const meta = JSON.parse(metaRaw) as Record<string, unknown>;\n if (typeof meta['last_remote_sync'] === 'string') {\n lastRemoteSync = meta['last_remote_sync'];\n }\n } catch {\n // No meta file — start from epoch\n }\n\n const remoteRefs = await mcp.call<RemoteUpdateRef[]>(\n 'cleargate_list_remote_updates',\n { since: lastRemoteSync },\n );\n\n // Pull each item — all awaited BEFORE any push (R2 invariant)\n const pulled: RemoteItem[] = [];\n for (const ref of remoteRefs) {\n const item = await mcp.call<RemoteItem | null>(\n 'cleargate_pull_item',\n { remote_id: ref.remote_id },\n );\n if (item !== null) {\n pulled.push(item);\n }\n }\n\n // ── Step 3: Stakeholder proposal intake (STORY-010-05) ──────────────────────\n const labelFilter = (opts.env ?? process.env)['CLEARGATE_PROPOSAL_LABEL'] ?? 'cleargate:proposal';\n let intakeResult: IntakeResult = { created: 0, items: [] };\n try {\n intakeResult = await runIntakeBranch({\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter,\n now: nowFn,\n });\n } catch (err) {\n // Non-fatal: intake errors do not abort the main sync loop\n stderr(`warn: intake branch failed: ${String(err)}\\n`);\n }\n\n // Emit R10 warning if present\n if (intakeResult.warning) {\n stderr(`${intakeResult.warning}\\n`);\n }\n\n // ── Step 3b: Pull comments for active items (STORY-010-06) ─────────────────\n // Load all local items first (needed for active-criteria + wiki render).\n const localItems = await scanLocalItems(projectRoot);\n\n if (!dryRun) {\n const localRefs: LocalWorkItemRef[] = localItems.map(({ fm }) => ({\n primaryId: getItemId(fm),\n remoteId: typeof fm['remote_id'] === 'string' && fm['remote_id'] ? fm['remote_id'] : undefined,\n lastRemoteUpdate: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : undefined,\n }));\n\n const activeSet = await resolveActiveItems(projectRoot, localRefs, nowFn);\n\n for (const remoteId of activeSet) {\n try {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n await renderCommentsSection({ projectRoot, remoteId, comments, localItems });\n } catch (err: unknown) {\n // R4 mitigation: per-item try/catch — do NOT break the outer loop\n const errMsg = err instanceof Error ? err.message : String(err);\n if (/MCP HTTP 429/.test(errMsg)) {\n // Find primary ID for logging\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'skipped-rate-limit',\n detail: '429',\n });\n } else {\n // Other errors: log as error-transport, continue\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'error-transport',\n detail: errMsg.slice(0, 200),\n });\n }\n }\n }\n }\n\n // ── Step 4: Classify local work items ───────────────────────────────────────\n // localItems already loaded above for comment-pull; reuse here.\n const conflictsJson: ConflictEntry[] = [];\n\n // Maps for tracking what to apply\n type QueuedPull = { item: RemoteItem; localPath: string; fm: Record<string, unknown>; body: string };\n type QueuedPush = { localPath: string; fm: Record<string, unknown>; body: string; itemId: string };\n\n const pullQueue: QueuedPull[] = [];\n const pushQueue: QueuedPush[] = [];\n\n const pulledByRemoteId = new Map<string, RemoteItem>();\n for (const item of pulled) {\n pulledByRemoteId.set(item.remote_id, item);\n }\n\n let dryRunPulls = 0;\n let dryRunPushes = 0;\n let dryRunConflicts = 0;\n\n for (const { localPath, fm, body } of localItems) {\n const remoteIdVal = fm['remote_id'];\n if (typeof remoteIdVal !== 'string' || !remoteIdVal) continue;\n\n const remoteItem = pulledByRemoteId.get(remoteIdVal);\n if (!remoteItem) continue;\n\n const lastBodySha = typeof fm['last_synced_body_sha'] === 'string' ? fm['last_synced_body_sha'] : null;\n const localBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n\n const localSnap: LocalSnapshot = {\n updated_at: typeof fm['updated_at'] === 'string' ? fm['updated_at'] : '1970-01-01T00:00:00Z',\n body_sha: localBodySha,\n status: typeof fm['status'] === 'string' ? fm['status'] : '',\n deleted: false,\n };\n\n const remoteSnap: RemoteSnapshot = {\n updated_at: remoteItem.updated_at,\n body_sha: remoteBodySha,\n status: remoteItem.status,\n deleted: false,\n };\n\n const since: SinceLastSync = {\n last_pushed_at: typeof fm['pushed_at'] === 'string' ? fm['pushed_at'] : null,\n last_pulled_at: typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null,\n last_remote_update: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : null,\n last_body_sha: lastBodySha,\n last_synced_status: typeof fm['last_synced_status'] === 'string' ? fm['last_synced_status'] : null,\n };\n\n const classification = classify(localSnap, remoteSnap, since);\n\n if (dryRun) {\n if (classification.resolution === 'pull') dryRunPulls++;\n else if (classification.resolution === 'push') dryRunPushes++;\n else if (classification.resolution === 'refuse' || classification.resolution === 'halt') dryRunConflicts++;\n continue;\n }\n\n // ── Step 5: Resolve ──────────────────────────────────────────────────────\n switch (classification.resolution) {\n case 'pull':\n case 'merge-silent':\n case 'remote-wins':\n pullQueue.push({ item: remoteItem, localPath, fm, body });\n break;\n\n case 'push':\n if (fm['approved'] === true) {\n pushQueue.push({ localPath, fm, body, itemId: getItemId(fm) });\n }\n break;\n\n case 'merge': {\n // Three-way merge prompt\n const mergeResult = await promptThreeWayMerge({\n local: body,\n remote: remoteItem.body ?? '',\n base: '',\n itemId: getItemId(fm),\n stdin: opts.stdin ?? process.stdin,\n stdout,\n });\n if (mergeResult.resolution === 'aborted') {\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n } else {\n // Apply merge result\n pullQueue.push({\n item: { ...remoteItem, body: mergeResult.body },\n localPath,\n fm,\n body,\n });\n }\n break;\n }\n\n case 'refuse':\n case 'halt':\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n // Do NOT abort — continue processing remaining items (AC §2.1)\n break;\n\n default:\n break;\n }\n }\n\n // ── dry-run summary and early exit ──────────────────────────────────────────\n if (dryRun) {\n stdout(\n `Would pull: ${dryRunPulls}, push: ${dryRunPushes}, ` +\n `intake: ${intakeResult.created}, conflicts: ${dryRunConflicts}\\n`,\n );\n return;\n }\n\n // ── Step 6: Apply + log ──────────────────────────────────────────────────────\n // R2: ALL pulls applied before pushes\n for (const { item, localPath, fm } of pullQueue) {\n await applyPull(item, localPath, fm, identity.email, nowFn);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n for (const { localPath, fm, body, itemId } of pushQueue) {\n // Push item to MCP\n await mcp.call('push_item', {\n cleargate_id: itemId,\n type: typeof fm['story_id'] === 'string' ? 'story'\n : typeof fm['epic_id'] === 'string' ? 'epic'\n : typeof fm['proposal_id'] === 'string' ? 'proposal'\n : 'story',\n payload: fm,\n });\n\n // Stamp last_synced_status + last_synced_body_sha so classify() sees a clean\n // baseline on the next sync run (mirrors the pull-apply path at applyPull:393-394).\n const pushedFm: Record<string, unknown> = {\n ...fm,\n last_synced_status: fm['status'],\n last_synced_body_sha: hashNormalized(body),\n };\n const newContent = serializeFrontmatter(pushedFm) + '\\n\\n' + body;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Log conflicts\n for (const c of conflictsJson) {\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'conflict-refused',\n target: c.item_id,\n remote_id: c.remote_id,\n result: 'halted',\n detail: c.reason,\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Atomic-write .conflicts.json\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n const conflictsContent: ConflictsJson = {\n generated_at: nowFn(),\n sprint_id: sprintId,\n unresolved: conflictsJson,\n };\n await writeAtomic(conflictsFile, JSON.stringify(conflictsContent, null, 2) + '\\n');\n\n // Update wiki meta last_remote_sync\n try {\n await fsPromises.mkdir(path.dirname(wikiMetaPath), { recursive: true });\n let meta: Record<string, unknown> = {};\n try {\n const raw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n meta = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // New meta\n }\n meta['last_remote_sync'] = nowFn();\n await writeAtomic(wikiMetaPath, JSON.stringify(meta, null, 2) + '\\n');\n } catch {\n // Non-fatal\n }\n\n const totalPulls = pullQueue.length;\n const totalPushes = pushQueue.length;\n const totalConflicts = conflictsJson.length;\n stdout(`sync: pulled ${totalPulls}, pushed ${totalPushes}, conflicts ${totalConflicts}\\n`);\n\n // Print intake summary (orchestrator stdout format)\n if (intakeResult.created > 0) {\n const plural = intakeResult.created === 1 ? 'proposal' : 'proposals';\n const inlineList = intakeResult.items\n .map((item) => `${item.proposalId} (${item.remoteId} '${item.title}')`)\n .join(', ');\n stdout(`📥 ${intakeResult.created} new stakeholder ${plural} pulled: ${inlineList}\\n`);\n stdout(` — review at .cleargate/delivery/pending-sync/\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Apply a remote item to the local file: update frontmatter + body. */\nasync function applyPull(\n item: RemoteItem,\n localPath: string,\n fm: Record<string, unknown>,\n actorEmail: string,\n nowFn: () => string,\n): Promise<void> {\n const now = nowFn();\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: item.status,\n last_pulled_by: actorEmail,\n last_pulled_at: now,\n last_remote_update: item.updated_at,\n last_synced_status: item.status,\n last_synced_body_sha: hashNormalized(item.body ?? ''),\n };\n\n const newBody = item.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n}\n\n/** Atomic write: write to .tmp file then rename. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\ninterface LocalWorkItem {\n localPath: string;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Scan .cleargate/delivery/pending-sync/ for tracked work items with remote_id. */\nasync function scanLocalItems(projectRoot: string): Promise<LocalWorkItem[]> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const results: LocalWorkItem[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm, body } = parseFrontmatter(raw);\n if (typeof fm['remote_id'] === 'string' && fm['remote_id']) {\n results.push({ localPath: fullPath, fm, body });\n }\n } catch {\n // Skip malformed files\n }\n }\n\n return results;\n}\n\n/** Extract primary item ID from frontmatter. */\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * sync-log.ts — append-only JSONL sync audit log.\n *\n * STORY-010-01: defines types + appendSyncLog + readSyncLog + resolveActiveSprintDir.\n *\n * Atomicity guarantee for appendSyncLog:\n * Uses fs.promises.appendFile with default flag 'a' (O_APPEND).\n * POSIX guarantees that concurrent O_APPEND writes ≤ PIPE_BUF (4 KB) are atomic\n * — a sync-log line is < 500 bytes so lines from concurrent writers never interleave.\n * We deliberately use appendFile (not read-then-write) to preserve this guarantee.\n *\n * Token redaction:\n * detail fields containing JWT tokens (eyJ…) are redacted before writing.\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\n/** R-014 rule ID constant (defined here for STORY-010-08 to wire into lint pipeline). */\nexport const R014 = 'sync-attribution-missing' as const;\n\nexport type SyncLogOp =\n | 'push'\n | 'pull'\n | 'pull-intake' // STORY-010-05: stakeholder proposal intake\n | 'pull-comments' // STORY-010-06: comment snapshot pull\n | 'push-revert'\n | 'sync-status'\n | 'conflict-remote-wins'\n | 'conflict-refused';\n\nexport type SyncLogResult =\n | 'ok'\n | 'no-op'\n | 'error-not-found'\n | 'error-transport'\n | 'skipped-rate-limit'\n | 'halted';\n\nexport interface SyncLogEntry {\n ts: string;\n actor: string;\n op: SyncLogOp;\n target: string;\n remote_id?: string;\n result: SyncLogResult;\n detail?: string;\n}\n\n// ── Active-sprint resolution ─────────────────────────────────────────────────\n\n/**\n * Resolve the active sprint directory under .cleargate/sprint-runs/.\n *\n * Strategy: scan sprint-runs/* (excluding _off-sprint), pick the entry with the\n * newest mtime. If none exist, create and return _off-sprint/.\n *\n * Open Decision (flagged to orchestrator): story §1.2 says \"read INDEX.md for\n * active sprint\", but INDEX.md has no machine-readable status:active field —\n * only execution_order integers inside sprint frontmatter. Newest-mtime dir is\n * the reliable signal available today.\n */\nexport function resolveActiveSprintDir(\n projectRoot: string,\n _opts?: { now?: () => string },\n): string {\n const sprintRunsRoot = path.join(projectRoot, '.cleargate', 'sprint-runs');\n const offSprint = path.join(sprintRunsRoot, '_off-sprint');\n\n if (!fs.existsSync(sprintRunsRoot)) {\n fs.mkdirSync(sprintRunsRoot, { recursive: true });\n fs.mkdirSync(offSprint, { recursive: true });\n return offSprint;\n }\n\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n const sprintDirs = entries\n .filter((e) => e.isDirectory() && e.name !== '_off-sprint')\n .map((e) => {\n const fullPath = path.join(sprintRunsRoot, e.name);\n const stat = fs.statSync(fullPath);\n return { name: e.name, fullPath, mtimeMs: stat.mtimeMs };\n })\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n\n if (sprintDirs.length === 0) {\n if (!fs.existsSync(offSprint)) {\n fs.mkdirSync(offSprint, { recursive: true });\n }\n return offSprint;\n }\n\n return sprintDirs[0].fullPath;\n}\n\n// ── JWT redaction ─────────────────────────────────────────────────────────────\n\nfunction redactDetail(detail: string | undefined): string | undefined {\n if (detail === undefined) return undefined;\n return detail.replace(/eyJ[A-Za-z0-9._-]+/g, '[REDACTED]');\n}\n\n// ── Append ────────────────────────────────────────────────────────────────────\n\n/**\n * Append one JSONL entry to <sprintRoot>/sync-log.jsonl.\n * Creates the file and parent directory if absent.\n * Uses O_APPEND for POSIX atomicity — never read-modify-write.\n */\nexport async function appendSyncLog(\n sprintRoot: string,\n entry: SyncLogEntry,\n): Promise<void> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n // Ensure directory exists\n await fsPromises.mkdir(sprintRoot, { recursive: true });\n\n const safeEntry: SyncLogEntry = {\n ...entry,\n detail: redactDetail(entry.detail),\n };\n\n const line = JSON.stringify(safeEntry) + '\\n';\n\n // appendFile with flag 'a' → O_APPEND; POSIX guarantees line-atomicity for <PIPE_BUF writes\n await fsPromises.appendFile(logPath, line, { encoding: 'utf8' });\n}\n\n// ── Read ──────────────────────────────────────────────────────────────────────\n\n/**\n * Read and optionally filter sync-log entries, returning newest-first.\n * Skips malformed lines silently — partial writes never crash the reader.\n */\nexport async function readSyncLog(\n sprintRoot: string,\n filters?: { actor?: string; op?: SyncLogOp; target?: string },\n): Promise<SyncLogEntry[]> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(logPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw err;\n }\n\n const entries: SyncLogEntry[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n try {\n const parsed = JSON.parse(trimmed) as SyncLogEntry;\n entries.push(parsed);\n } catch {\n // malformed line — skip silently\n }\n }\n\n // Apply filters\n let result = entries;\n if (filters?.actor !== undefined) {\n result = result.filter((e) => e.actor === filters.actor);\n }\n if (filters?.op !== undefined) {\n result = result.filter((e) => e.op === filters.op);\n }\n if (filters?.target !== undefined) {\n result = result.filter((e) => e.target === filters.target);\n }\n\n // Newest first (entries are appended oldest-first)\n return result.reverse();\n}\n","/**\n * conflict-detector.ts — STORY-010-03\n *\n * Pure classifier for local-vs-remote sync conflicts.\n * Implements the 8-state PROP-007 §2.3 matrix + explicit 9th \"unknown\" fallthrough.\n *\n * No I/O. No imports from node:fs, node:child_process, node:os.\n * No imports from commands/ or bin/.\n */\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type ConflictState =\n | 'no-change'\n | 'local-only' // local content edit, no remote change\n | 'remote-only' // remote status/metadata change, local untouched\n | 'content-content' // both bodies diverged since last sync\n | 'content-status' // local body + remote status\n | 'status-status' // both statuses diverged\n | 'local-delete-remote-edit'\n | 'remote-delete-local-edit'\n | 'unknown'; // R3: explicit fallthrough — never silently wrong\n\nexport type Resolution =\n | 'push'\n | 'pull'\n | 'merge' // three-way merge prompt\n | 'merge-silent' // content+status: no prompt, apply both sides\n | 'remote-wins' // status+status: silent remote takes, log conflict-remote-wins\n | 'refuse' // halt, surface to human\n | 'halt'; // unknown state — halt sync with explicit message\n\nexport interface LocalSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface RemoteSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface SinceLastSync {\n last_pushed_at: string | null;\n last_pulled_at: string | null;\n last_remote_update: string | null; // ISO-8601 string or null; opaque per M1 lock\n last_body_sha: string | null; // sha at last successful sync (merge-base)\n last_synced_status: string | null; // status recorded at last successful sync (rule 6)\n}\n\nexport interface Classification {\n state: ConflictState;\n resolution: Resolution;\n reason: string; // human-readable; used for sync-log detail + halt messages\n}\n\n// ─── Classifier ───────────────────────────────────────────────────────────────\n\n/**\n * classify — pure function; maps a (local, remote, since) triple to a Classification.\n *\n * Decision table follows PROP-007 §2.3 + M2 plan \"8-state + 9th fallthrough\" exactly.\n * States are evaluated in priority order; first match wins.\n */\nexport function classify(\n local: LocalSnapshot,\n remote: RemoteSnapshot,\n since: SinceLastSync,\n): Classification {\n const baseSha = since.last_body_sha ?? '';\n const lastPulled = since.last_pulled_at ?? '0';\n const lastPushed = since.last_pushed_at ?? '0';\n\n // Rule 7 — local-delete-remote-edit (check deletes first — highest priority)\n if (local.deleted && remote.updated_at > lastPulled) {\n return {\n state: 'local-delete-remote-edit',\n resolution: 'refuse',\n reason: 'local deletion conflicts with remote edit',\n };\n }\n\n // Rule 8 — remote-delete-local-edit\n if (remote.deleted && local.updated_at > lastPushed) {\n return {\n state: 'remote-delete-local-edit',\n resolution: 'refuse',\n reason: 'remote deletion conflicts with local edit',\n };\n }\n\n // Rule 1 — no-change: bodies and status are identical at sync base; nothing deleted\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status === remote.status &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'no-change',\n resolution: 'pull',\n reason: 'no change since last sync',\n };\n }\n\n // Rule 4 — content-content: both bodies diverged\n if (\n local.body_sha !== baseSha &&\n remote.body_sha !== baseSha &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'content-content',\n resolution: 'merge',\n reason: 'both bodies diverged — three-way merge required',\n };\n }\n\n // Rule 5 — content-status: local body changed + remote status changed, bodies otherwise aligned\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status !== local.status\n ) {\n return {\n state: 'content-status',\n resolution: 'merge-silent',\n reason: 'local body edit + remote status change — merged without prompt',\n };\n }\n\n // Rule 2 — local-only: local content edited, remote unchanged, same status\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status === local.status &&\n !remote.deleted\n ) {\n return {\n state: 'local-only',\n resolution: 'push',\n reason: 'local content edit only',\n };\n }\n\n // Rule 6 — status-status: both sides changed status since last sync\n // Requires since.last_synced_status to distinguish from remote-only.\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status !== remote.status &&\n since.last_synced_status !== null &&\n local.status !== since.last_synced_status &&\n remote.status !== since.last_synced_status\n ) {\n return {\n state: 'status-status',\n resolution: 'remote-wins',\n reason: 'status diverged on both sides; remote authoritative',\n };\n }\n\n // Rule 3 — remote-only: local body unchanged, remote status or metadata changed\n if (\n local.body_sha === baseSha &&\n (remote.status !== local.status || remote.updated_at > lastPulled)\n ) {\n return {\n state: 'remote-only',\n resolution: 'pull',\n reason: 'remote status/metadata change only',\n };\n }\n\n // Rule 9 — unknown fallthrough (R3): explicitly refuse to guess\n return {\n state: 'unknown',\n resolution: 'halt',\n reason:\n 'conflict shape not recognized — please resolve manually and file a ClearGate bug with this sync-log entry',\n };\n}\n","/**\n * merge-helper.ts — STORY-010-03\n *\n * Three-way merge prompt for `cleargate sync` content-content conflicts.\n * Reuses EPIC-009 primitives: renderInlineDiff (merge-ui.ts), openInEditor +\n * containsConflictMarkers (editor.ts).\n *\n * Adds the fourth [a]bort branch on top of merge-ui's k/t/e set.\n * Never writes to the caller's file — returns MergeResult; caller applies.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { renderInlineDiff } from './merge-ui.js';\nimport { openInEditor, containsConflictMarkers } from './editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeResolution = 'keep' | 'take' | 'edited' | 'aborted';\n\nexport interface MergeResult {\n resolution: MergeResolution;\n body: string; // the chosen/edited body\n}\n\nexport interface PromptThreeWayMergeOpts {\n local: string;\n remote: string;\n base: string; // merge-base body; used to construct git-merge-markers on [e]dit\n itemId: string; // for diff header + temp-file name\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n editor?: string; // override $EDITOR for tests\n now?: () => string; // temp-file name determinism seam\n}\n\n// ─── Four-choice prompt (extends merge-ui's k/t/e with [a]bort) ──────────────\n\ntype FourChoice = 'k' | 't' | 'e' | 'a';\n\n/**\n * promptFourChoice — renders the four-option prompt and reads one line from stdin.\n * Ctrl-C (stream 'close' without data) and 'a' both resolve to 'a' (abort).\n */\nfunction promptFourChoice(opts: {\n stdin: NodeJS.ReadableStream;\n stdout: (s: string) => void;\n}): Promise<FourChoice> {\n const { stdin, stdout } = opts;\n\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR / [a]bort: ');\n\n return new Promise<FourChoice>((resolve) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n cleanup();\n const answer = buf.slice(0, newline).trim().toLowerCase();\n if (answer === 'k' || answer === 't' || answer === 'e' || answer === 'a') {\n resolve(answer as FourChoice);\n } else {\n stdout(`Unknown choice '${answer}'; treating as [a]bort.\\n`);\n resolve('a');\n }\n }\n };\n\n // Ctrl-C or stdin close without data → abort\n const onClose = () => {\n cleanup();\n resolve('a');\n };\n\n const onEnd = () => {\n cleanup();\n resolve('a');\n };\n\n const onError = () => {\n cleanup();\n resolve('a');\n };\n\n function cleanup() {\n stdin.removeListener('data', onData);\n stdin.removeListener('close', onClose);\n stdin.removeListener('end', onEnd);\n stdin.removeListener('error', onError);\n }\n\n stdin.on('data', onData);\n stdin.once('close', onClose);\n stdin.once('end', onEnd);\n stdin.once('error', onError);\n });\n}\n\n// ─── Main export ──────────────────────────────────────────────────────────────\n\n/**\n * promptThreeWayMerge — interactive three-way merge UX.\n *\n * Flow:\n * 1. Renders unified diff of local vs remote.\n * 2. Prompts [k]eep / [t]ake / [e]dit / [a]bort.\n * 3. On [e]dit: writes a temp file with git-merge-marker content, spawns $EDITOR,\n * re-reads on close, re-prompts if conflict markers remain.\n * Temp file is always unlinked (finally block).\n * 4. On [a]bort or Ctrl-C: returns { resolution: 'aborted', body: local }.\n * 5. Never writes to caller's file.\n */\nexport async function promptThreeWayMerge(opts: PromptThreeWayMergeOpts): Promise<MergeResult> {\n const {\n local,\n remote,\n itemId,\n stdin = process.stdin,\n editor,\n } = opts;\n\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const now = opts.now ?? (() => Date.now().toString());\n\n // 1. Render diff\n const patch = renderInlineDiff(local, remote, itemId);\n stdout(`\\n[merge] ${itemId}\\n`);\n stdout(patch + '\\n');\n\n // 2–3. Prompt loop (re-prompt if editor leaves unresolved markers)\n for (;;) {\n const choice = await promptFourChoice({ stdin, stdout });\n\n switch (choice) {\n case 'k':\n return { resolution: 'keep', body: local };\n\n case 't':\n return { resolution: 'take', body: remote };\n\n case 'a':\n return { resolution: 'aborted', body: local };\n\n case 'e': {\n const tmpFile = path.join(os.tmpdir(), `cleargate-merge-${itemId}-${now()}.md`);\n const markerContent = `<<<<<<< local\\n${local}\\n=======\\n${remote}\\n>>>>>>> remote\\n`;\n\n try {\n await fs.writeFile(tmpFile, markerContent, 'utf-8');\n\n await openInEditor(tmpFile, { editor: editor ?? process.env['EDITOR'] ?? 'vi' });\n\n const edited = await fs.readFile(tmpFile, 'utf-8');\n\n if (containsConflictMarkers(edited)) {\n stdout('File still contains conflict markers — please resolve all conflicts.\\n');\n // re-prompt\n continue;\n }\n\n return { resolution: 'edited', body: edited };\n } finally {\n // Always clean up temp file even on error\n await fs.unlink(tmpFile).catch(() => {/* already gone — ignore */});\n }\n }\n }\n }\n}\n","/**\n * mcp-client.ts — STORY-010-04 / updated STORY-011-01\n *\n * Minimal JSON-RPC-over-HTTP client for ClearGate's MCP server.\n *\n * Token acquisition: callers obtain a token via acquireAccessToken() from\n * cleargate-cli/src/auth/acquire.ts and pass it via McpClientOptions.token.\n * This file does NOT read CLEARGATE_MCP_TOKEN or call acquireAccessToken.\n *\n * MCP endpoint: passed directly as McpClientOptions.baseUrl by the caller.\n *\n * Flashcard: no pre-existing MCP client in cleargate-cli — built from scratch here.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\n// ── Wire types (re-exported for consumers) ───────────────────────────────────\n\nexport interface RemoteUpdateRef {\n remote_id: string;\n updated_at: string;\n}\n\nexport interface RemoteItem {\n remote_id: string;\n title: string;\n body: string | null;\n status: string;\n assignees: string[];\n labels: string[];\n updated_at: string;\n source_tool: string;\n raw: unknown;\n}\n\nexport interface RemoteComment {\n id: string;\n author_email: string | null;\n author_name: string;\n body: string;\n created_at: string;\n remote_id: string;\n}\n\nexport interface AdapterInfo {\n configured: boolean;\n name: 'linear' | 'jira' | 'github-projects' | 'no-adapter-configured';\n}\n\n// ── McpClient interface ───────────────────────────────────────────────────────\n\nexport interface McpClient {\n call<T>(tool: string, args: Record<string, unknown>): Promise<T>;\n adapterInfo(): Promise<AdapterInfo>;\n}\n\nexport interface McpClientOptions {\n baseUrl: string;\n token: string;\n /** Test seam: override globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n}\n\n// ── JSON-RPC envelope ─────────────────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n method: 'tools/call';\n params: {\n name: string;\n arguments: Record<string, unknown>;\n };\n id: number;\n}\n\ninterface JsonRpcResponse<T> {\n jsonrpc: '2.0';\n id: number;\n result?: {\n content?: Array<{ type: 'text'; text: string }>;\n structuredContent?: T;\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\nlet _reqId = 1;\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * createMcpClient — build a JSON-RPC client for the ClearGate MCP server.\n *\n * Sends POST to ${baseUrl}/mcp with Authorization: Bearer <token>.\n * Parses the StreamableHTTP response (may be plain JSON or SSE stream).\n */\nexport function createMcpClient(opts: McpClientOptions): McpClient {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n async function call<T>(tool: string, args: Record<string, unknown>): Promise<T> {\n const body: JsonRpcRequest = {\n jsonrpc: '2.0',\n method: 'tools/call',\n params: { name: tool, arguments: args },\n id: _reqId++,\n };\n\n let response: Response;\n try {\n response = await fetchFn(`${opts.baseUrl}/mcp`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json, text/event-stream',\n 'Authorization': `Bearer ${opts.token}`,\n },\n body: JSON.stringify(body),\n });\n } catch (err) {\n throw new Error(`MCP transport error calling ${tool}: ${String(err)}`);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`MCP HTTP ${response.status} calling ${tool}: ${text.slice(0, 256)}`);\n }\n\n const text = await response.text();\n\n // Handle SSE stream: extract last JSON from event-stream data lines\n let jsonText = text;\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/event-stream')) {\n const dataLines = text\n .split('\\n')\n .filter((l) => l.startsWith('data: '))\n .map((l) => l.slice('data: '.length).trim())\n .filter((l) => l !== '' && l !== '[DONE]');\n if (dataLines.length === 0) {\n throw new Error(`MCP SSE response for ${tool} contained no data lines`);\n }\n jsonText = dataLines[dataLines.length - 1];\n }\n\n let parsed: JsonRpcResponse<T>;\n try {\n parsed = JSON.parse(jsonText) as JsonRpcResponse<T>;\n } catch {\n throw new Error(`MCP response for ${tool} is not valid JSON: ${jsonText.slice(0, 256)}`);\n }\n\n if (parsed.error) {\n throw new Error(`MCP tool ${tool} returned error ${parsed.error.code}: ${parsed.error.message}`);\n }\n\n // structuredContent preferred; fall back to parsing text content\n if (parsed.result?.structuredContent !== undefined) {\n return parsed.result.structuredContent as T;\n }\n\n const textContent = parsed.result?.content?.find((c) => c.type === 'text')?.text;\n if (textContent !== undefined) {\n try {\n return JSON.parse(textContent) as T;\n } catch {\n throw new Error(`MCP tool ${tool} text content is not valid JSON: ${textContent.slice(0, 256)}`);\n }\n }\n\n throw new Error(`MCP tool ${tool} returned no content`);\n }\n\n async function adapterInfo(): Promise<AdapterInfo> {\n return call<AdapterInfo>('cleargate_adapter_info', {});\n }\n\n return { call, adapterInfo };\n}\n\n","/**\n * intake.ts — STORY-010-05\n *\n * Stakeholder proposal intake branch for `cleargate sync`.\n *\n * `runIntakeBranch` is called from `syncHandler` at step 3 (after pull, before\n * conflict classification). It:\n * 1. Calls `cleargate_detect_new_items({ label })` to get remote proposals.\n * 2. Deduplicates against pending-sync + archive by `remote_id` frontmatter.\n * 3. For each new item: allocates a `PROP-NNN` ID, slugifies the title,\n * writes a draft proposal file from the template, and appends a sync-log\n * entry with op:'pull-intake'.\n * 4. Emits an R10 warning to stderr if zero items matched AND this appears to\n * be the first intake run for this workspace (no prior `source: remote-authored`\n * files detected).\n * 5. Returns a summary for the end-of-sync stdout print.\n *\n * Respects dryRun: zero fs writes, zero sync-log entries, returns the plan.\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { slugify, nextProposalId, findByRemoteId } from './slug.js';\nimport { appendSyncLog, type SyncLogEntry } from './sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from './frontmatter-yaml.js';\nimport type { McpClient, RemoteItem } from './mcp-client.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface IntakeItem {\n proposalId: string;\n remoteId: string;\n title: string;\n path: string;\n}\n\nexport interface IntakeResult {\n created: number;\n items: IntakeItem[];\n warning?: string;\n}\n\nexport interface IntakeBranchOptions {\n mcp: McpClient;\n identity: { email: string };\n sprintRoot: string;\n projectRoot: string;\n dryRun: boolean;\n labelFilter?: string;\n /** Test seam: override now() */\n now?: () => string;\n}\n\n// ── runIntakeBranch ───────────────────────────────────────────────────────────\n\nexport async function runIntakeBranch(opts: IntakeBranchOptions): Promise<IntakeResult> {\n const {\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter = 'cleargate:proposal',\n now = () => new Date().toISOString(),\n } = opts;\n\n const pendingSyncDir = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n\n // Detect new remote items with the cleargate:proposal label\n let remoteItems: RemoteItem[] = [];\n try {\n remoteItems = await mcp.call<RemoteItem[]>(\n 'cleargate_detect_new_items',\n { label: labelFilter },\n );\n if (!Array.isArray(remoteItems)) {\n remoteItems = [];\n }\n } catch {\n // Non-fatal: if the tool doesn't exist yet, treat as zero items\n remoteItems = [];\n }\n\n // R10: zero-label warning — fires when zero items returned AND this is the\n // first intake run (no existing `source: remote-authored` files found)\n let warning: string | undefined;\n if (remoteItems.length === 0) {\n const hasExistingIntake = await hasAnyRemoteAuthored(projectRoot);\n if (!hasExistingIntake) {\n warning =\n `warn: no Linear issues match label '${labelFilter}' — ` +\n `confirm the label exists in your workspace. See EPIC-010 R10.`;\n }\n }\n\n const createdItems: IntakeItem[] = [];\n\n for (const item of remoteItems) {\n // Idempotency: skip if a local counterpart already exists (in either dir)\n const existingPath = await findByRemoteId(projectRoot, item.remote_id);\n if (existingPath !== null) {\n continue;\n }\n\n if (dryRun) {\n // In dry-run mode: plan the intake without writing anything\n const proposalId = await nextProposalId(projectRoot);\n const slug = slugify(item.title ?? 'untitled');\n const num = proposalId.replace('PROP-', '');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n continue;\n }\n\n // Compute the next proposal ID and filename\n // Note: we recompute after each write so IDs are consistent even when multiple\n // items are being created in the same run\n const proposalId = await nextProposalId(projectRoot);\n const num = proposalId.replace('PROP-', '');\n const slug = slugify(item.title ?? 'untitled');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n const nowTs = now();\n\n // Build frontmatter from template + our sync fields\n const fm: Record<string, unknown> = {\n proposal_id: proposalId,\n remote_id: item.remote_id,\n status: 'Draft',\n approved: false,\n source: 'remote-authored',\n last_pulled_by: identity.email,\n last_pulled_at: nowTs,\n last_remote_update: item.updated_at,\n created_at: nowTs,\n updated_at: nowTs,\n pushed_by: null,\n pushed_at: null,\n last_synced_status: null,\n last_synced_body_sha: null,\n };\n\n // Build body from the proposal template + pre-fill §1 body from remote item\n const body = buildProposalBody(item, projectRoot);\n\n // Atomic write: .tmp + rename\n await fsPromises.mkdir(pendingSyncDir, { recursive: true });\n const content = serializeFrontmatter(fm) + '\\n\\n' + body;\n const tmpPath = `${targetPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, targetPath);\n\n // Sync-log entry\n const logEntry: SyncLogEntry = {\n ts: nowTs,\n actor: identity.email,\n op: 'pull-intake',\n target: proposalId,\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, logEntry);\n\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n }\n\n return {\n created: createdItems.length,\n items: createdItems,\n warning,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Build the body for an intake proposal file.\n * Seeds §1 \"Initiative & Context\" with the remote item body;\n * leaves other sections as template placeholders.\n */\nfunction buildProposalBody(item: RemoteItem, _projectRoot: string): string {\n const title = item.title ?? '(untitled)';\n const remoteBody = item.body ?? '';\n\n return `# ${title}\n\n## 1. Initiative & Context\n\n### 1.1 Objective\n\n${remoteBody || '(pre-filled from Linear issue body)'}\n\n### 1.2 The \"Why\"\n\n{Reason 1}\n{Reason 2}\n\n## 2. Technical Architecture & Constraints\n\n### 2.1 Dependencies\n\n{List required external APIs, packages, or systems}\n\n### 2.2 System Constraints\n\n| Constraint | Details |\n|---|---|\n| Architectural Rules | {e.g., Must use purely functional components} |\n| Security | {e.g., Data must be encrypted at rest.} |\n\n## 3. Scope Impact (Touched Files & Data)\n\n### 3.1 Known Files\n\npath/to/existing/file.ext - {Explanation of expected change}\n\n### 3.2 Expected New Entities\n\npath/to/new/file.ext - {Explanation of purpose}\n\n## Approval Gate\n\n(Vibe Coder: Review this proposal. If the architecture and context are correct, change approved: false to approved: true in the YAML frontmatter. Only then is the AI authorized to proceed with Epic/Story decomposition.)\n`;\n}\n\n/**\n * Check if any `source: remote-authored` file already exists in pending-sync or archive.\n * Used to gate the R10 zero-label warning.\n */\nasync function hasAnyRemoteAuthored(projectRoot: string): Promise<boolean> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n // Quick scan without full parse — look for source: remote-authored in frontmatter\n const fmEnd = raw.indexOf('\\n---', 4);\n if (fmEnd === -1) continue;\n const fmBlock = raw.slice(0, fmEnd);\n if (/source:\\s*['\"]?remote-authored['\"]?/.test(fmBlock)) {\n return true;\n }\n } catch {\n // Skip\n }\n }\n }\n\n return false;\n}\n\n// Re-export parseFrontmatter for consumers that read template files\nexport { parseFrontmatter };\n","/**\n * slug.ts — STORY-010-05\n *\n * Filename slug helper + proposal ID scanner for stakeholder intake.\n *\n * `slugify(title, max)` — deterministic, locale-free slug from a title string.\n * `nextProposalId(projectRoot)` — scans pending-sync + archive for max PROP-NNN\n * and returns the next sequential ID as a zero-padded string.\n * `findByRemoteId(projectRoot, remoteId)` — dedup helper; returns local path on\n * hit, null on miss. Scans frontmatter block only (first --- ... --- delimiters).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── slugify ──────────────────────────────────────────────────────────────────\n\n/**\n * Convert a title string into a URL-safe slug.\n *\n * Algorithm:\n * 1. NFKD-normalize → strip combining marks (é→e, ü→u, etc.)\n * 2. Lowercase.\n * 3. Replace runs of [^a-z0-9]+ with a single dash.\n * 4. Trim leading/trailing dashes.\n * 5. Truncate to `max` characters; re-trim trailing dash.\n * 6. If empty/all-dashes after step 5 → return \"untitled\".\n */\nexport function slugify(title: string, max: number = 40): string {\n // Step 1: NFKD normalize + strip combining marks (Unicode category M)\n const normalized = title.normalize('NFKD').replace(/\\p{M}/gu, '');\n // Step 2: lowercase\n const lowered = normalized.toLowerCase();\n // Step 3: replace non-alphanumeric runs with dash\n const dashed = lowered.replace(/[^a-z0-9]+/g, '-');\n // Step 4: trim leading/trailing dashes\n const trimmed = dashed.replace(/^-+|-+$/g, '');\n // Step 5: truncate to max; re-trim trailing dash\n const truncated = trimmed.slice(0, max).replace(/-+$/, '');\n // Step 6: fallback for empty result\n if (!truncated) {\n return 'untitled';\n }\n return truncated;\n}\n\n// ── nextProposalId ────────────────────────────────────────────────────────────\n\n/** Pattern matching `proposal_id: \"PROP-NNN\"` or `proposal_id: PROP-NNN` */\nconst PROPOSAL_ID_RE = /^proposal_id:\\s*\"?PROP-(\\d+)\"?/m;\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for `.md` files whose frontmatter contains `proposal_id: \"PROP-NNN\"`.\n * Returns the next ID as `\"PROP-<max+1, zero-padded to 3>\"`.\n *\n * Gap-tolerant: returns max+1, NOT the first gap.\n * Empty dirs or no proposals found → `\"PROP-001\"`.\n */\nexport async function nextProposalId(projectRoot: string): Promise<string> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n let maxN = 0;\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n // Read only the frontmatter block (first --- ... ---)\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fmEnd = extractFrontmatterBlock(raw);\n if (!fmEnd) continue;\n const match = PROPOSAL_ID_RE.exec(fmEnd);\n if (!match) continue;\n const n = parseInt(match[1]!, 10);\n if (n > maxN) maxN = n;\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n const next = maxN + 1;\n return `PROP-${String(next).padStart(3, '0')}`;\n}\n\n// ── findByRemoteId ────────────────────────────────────────────────────────────\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for a `.md` file whose frontmatter contains `remote_id: \"<remoteId>\"`.\n *\n * Returns the absolute path of the first match, or `null` if not found.\n * Reads only the frontmatter block (first --- ... ---) for efficiency.\n */\nexport async function findByRemoteId(\n projectRoot: string,\n remoteId: string,\n): Promise<string | null> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n // Build a regex matching `remote_id: \"LIN-NNN\"` or `remote_id: LIN-NNN`\n const escaped = remoteId.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`^remote_id:\\\\s*\"?${escaped}\"?\\\\s*$`, 'm');\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fm = extractFrontmatterBlock(raw);\n if (!fm) continue;\n if (re.test(fm)) return fullPath;\n } catch {\n // Skip\n }\n }\n }\n\n return null;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Extract the YAML content between the first `---` delimiters.\n * Returns the raw YAML text (without the delimiters), or null if not found.\n */\nfunction extractFrontmatterBlock(raw: string): string | null {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return null;\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return null;\n return lines.slice(1, closeIdx).join('\\n');\n}\n","/**\n * active-criteria.ts — STORY-010-06\n *\n * Resolves which work items are \"active\" for the purpose of comment-pull.\n * Active = (item is referenced in the current sprint) OR (item's last_remote_update is within 30 days).\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but NO `stories:` list.\n * Body-regex scan is the only reliable signal available today.\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveActiveSprintDir } from './sync-log.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface LocalWorkItemRef {\n /** Primary cleargate ID (e.g. STORY-010-06, EPIC-010). */\n primaryId: string;\n /** Remote ID in the PM tool (e.g. LIN-1042). May be undefined if not yet pushed. */\n remoteId: string | undefined;\n /** ISO string from `last_remote_update` frontmatter field. */\n lastRemoteUpdate: string | undefined;\n}\n\n// ── Active set resolver ───────────────────────────────────────────────────────\n\n/**\n * Resolve which items (by remote_id) are \"active\" and should have comments fetched.\n *\n * @param projectRoot - absolute path to the project root\n * @param localItems - all local work items with their IDs\n * @param nowFn - injectable clock (for 30-day window)\n * @returns - Set of remote_id values that are active\n */\nexport async function resolveActiveItems(\n projectRoot: string,\n localItems: LocalWorkItemRef[],\n nowFn: () => string = () => new Date().toISOString(),\n): Promise<Set<string>> {\n const active = new Set<string>();\n const now = Date.parse(nowFn());\n const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;\n\n // ── Branch 1: items referenced in the current sprint ─────────────────────\n const inSprintIds = await resolveInSprintIds(projectRoot);\n\n // ── Branch 2: items updated in last 30 days + union of sprint ─────────────\n for (const item of localItems) {\n if (!item.remoteId) continue;\n\n // In-sprint check\n if (inSprintIds.has(item.primaryId)) {\n active.add(item.remoteId);\n continue;\n }\n\n // 30-day window check\n if (item.lastRemoteUpdate) {\n const itemMs = Date.parse(item.lastRemoteUpdate);\n if (!isNaN(itemMs) && (now - itemMs) <= thirtyDaysMs) {\n active.add(item.remoteId);\n }\n }\n }\n\n return active;\n}\n\n// ── Internal: resolve sprint item references ──────────────────────────────────\n\n/**\n * Read the active sprint file and extract all work-item ID references from its body.\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but no `stories:` array.\n * We scan the full body text for IDs matching:\n * (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n *\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n */\nasync function resolveInSprintIds(projectRoot: string): Promise<Set<string>> {\n const ids = new Set<string>();\n\n try {\n const sprintDir = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintDir);\n\n if (sprintId === '_off-sprint') return ids;\n\n // Try pending-sync first, then archive\n const sprintFile = await findSprintFile(projectRoot, sprintId);\n if (!sprintFile) return ids;\n\n const content = await fsPromises.readFile(sprintFile, 'utf8');\n\n // Scan body for work-item ID patterns\n // (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n const pattern = /(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/g;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(content)) !== null) {\n ids.add(match[0]);\n }\n } catch {\n // Non-fatal: if we can't resolve sprint, return empty set\n }\n\n return ids;\n}\n\nasync function findSprintFile(projectRoot: string, sprintId: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isFile() && entry.name.startsWith(sprintId) && entry.name.endsWith('.md')) {\n return path.join(dir, entry.name);\n }\n }\n } catch {\n // Directory not found — try next\n }\n }\n\n return null;\n}\n","/**\n * comments-cache.ts — STORY-010-06\n *\n * Atomic read/write for comment cache files at\n * .cleargate/.comments-cache/<remote_id>.json\n *\n * Contents = raw RemoteComment[] array from cleargate_pull_comments.\n * Write is atomic via .tmp + rename (mirrors sync.ts:writeAtomic).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Cache directory ───────────────────────────────────────────────────────────\n\nfunction cacheDir(projectRoot: string): string {\n return path.join(projectRoot, '.cleargate', '.comments-cache');\n}\n\nfunction cachePath(projectRoot: string, remoteId: string): string {\n return path.join(cacheDir(projectRoot), `${remoteId}.json`);\n}\n\n// ── Write ──────────────────────────────────────────────────────────────────────\n\n/**\n * Atomically write a RemoteComment[] array to the cache for `remoteId`.\n * Creates the cache directory if absent.\n */\nexport async function writeCommentCache(\n projectRoot: string,\n remoteId: string,\n comments: RemoteComment[],\n): Promise<void> {\n const dir = cacheDir(projectRoot);\n await fsPromises.mkdir(dir, { recursive: true });\n\n const filePath = cachePath(projectRoot, remoteId);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n const content = JSON.stringify(comments, null, 2) + '\\n';\n\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Read ───────────────────────────────────────────────────────────────────────\n\n/**\n * Read cached comments for `remoteId`.\n * Returns null if the cache file is absent or contains malformed JSON.\n */\nexport async function readCommentCache(\n projectRoot: string,\n remoteId: string,\n): Promise<RemoteComment[] | null> {\n const filePath = cachePath(projectRoot, remoteId);\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(filePath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n\n try {\n return JSON.parse(raw) as RemoteComment[];\n } catch {\n return null;\n }\n}\n","/**\n * wiki-comments-render.ts — STORY-010-06\n *\n * Renders (inserts / replaces / removes) the \"## Remote comments\" section\n * on an existing wiki page at .cleargate/wiki/<bucket>/<primaryId>.md.\n *\n * DELIBERATELY separate from commands/wiki-ingest.ts which owns full-page\n * rebuild from raw. Section overlay is a different concern and would fight\n * wiki-ingest's SHA-idempotency guard.\n *\n * Delimiter matching uses literal-string indexOf, NOT regex.\n * FLASHCARD #regex #inject-claude-md: fuzzy whitespace regex breaks when the\n * block body itself references both markers in prose — use indexOf exclusively.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Delimiters (literal strings — never change to regex) ───────────────────────\n\nconst START = '<!-- cleargate:comments:start -->';\nconst END = '<!-- cleargate:comments:end -->';\n\n// ── Bucket resolution ─────────────────────────────────────────────────────────\n\n/**\n * Map a frontmatter record to the wiki bucket directory name.\n * Returns null if the item type cannot be determined.\n */\nexport function resolveBucket(fm: Record<string, unknown>): string | null {\n if (typeof fm['story_id'] === 'string' && fm['story_id']) return 'stories';\n if (typeof fm['epic_id'] === 'string' && fm['epic_id']) return 'epics';\n if (typeof fm['proposal_id'] === 'string' && fm['proposal_id']) return 'proposals';\n if (typeof fm['cr_id'] === 'string' && fm['cr_id']) return 'crs';\n if (typeof fm['bug_id'] === 'string' && fm['bug_id']) return 'bugs';\n return null;\n}\n\n/**\n * Extract the primary item ID (e.g. STORY-010-06) from frontmatter.\n */\nexport function getPrimaryId(fm: Record<string, unknown>): string | null {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return null;\n}\n\n// ── Section builder ───────────────────────────────────────────────────────────\n\n/**\n * Build the full delimited comment section string.\n * Sorts comments by created_at ascending.\n */\nexport function buildCommentSection(comments: RemoteComment[]): string {\n const sorted = [...comments].sort((a, b) => {\n return a.created_at < b.created_at ? -1 : a.created_at > b.created_at ? 1 : 0;\n });\n\n const entries = sorted.map((c) => {\n const author = c.author_email\n ? `${c.author_name} (${c.author_email})`\n : c.author_name;\n\n // Multi-line body: prefix every line with \"> \"\n const bodyLines = c.body.split('\\n').map((line) => `> ${line}`).join('\\n');\n\n return `### ${author} · ${c.created_at}\\n${bodyLines}`;\n });\n\n return (\n `${START}\\n` +\n `## Remote comments\\n` +\n `\\n` +\n `_Read-only snapshot. Comments live in the PM tool — reply there, not here._\\n` +\n `\\n` +\n entries.join('\\n\\n') +\n `\\n${END}`\n );\n}\n\n// ── Main export ───────────────────────────────────────────────────────────────\n\nexport interface RenderCommentsSectionOpts {\n /** Absolute path to project root */\n projectRoot: string;\n /** Remote ID (e.g. LIN-1042) */\n remoteId: string;\n /** Comment array from cleargate_pull_comments */\n comments: RemoteComment[];\n /** Local work items to resolve wiki path from */\n localItems: Array<{ fm: Record<string, unknown> }>;\n}\n\n/**\n * Insert / replace / remove the ## Remote comments section on the wiki page\n * corresponding to the given remote_id.\n *\n * - If the wiki page does not exist: no-op (wiki-ingest may not have run yet).\n * - Byte-idempotent: running twice with identical input produces identical output.\n * - Atomic write via .tmp + rename.\n */\nexport async function renderCommentsSection(\n opts: RenderCommentsSectionOpts,\n): Promise<void> {\n const { projectRoot, remoteId, comments, localItems } = opts;\n\n // Find the local item whose remote_id matches\n const localItem = localItems.find(\n (item) => item.fm['remote_id'] === remoteId,\n );\n if (!localItem) return;\n\n const bucket = resolveBucket(localItem.fm);\n const primaryId = getPrimaryId(localItem.fm);\n if (!bucket || !primaryId) return;\n\n const wikiPath = path.join(\n projectRoot,\n '.cleargate',\n 'wiki',\n bucket,\n `${primaryId}.md`,\n );\n\n // Read existing wiki page\n let existing: string;\n try {\n existing = await fsPromises.readFile(wikiPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return; // wiki page not yet built\n throw err;\n }\n\n const startIdx = existing.indexOf(START);\n const endIdx = existing.indexOf(END);\n\n let updated: string;\n\n if (startIdx === -1 && comments.length === 0) {\n // No section, no comments — no-op\n return;\n } else if (startIdx === -1 && comments.length > 0) {\n // Insert new section at end of file\n const section = buildCommentSection(comments);\n const base = existing.endsWith('\\n\\n')\n ? existing.slice(0, -1) // trim one trailing newline\n : existing.endsWith('\\n')\n ? existing\n : existing + '\\n';\n updated = base + '\\n' + section + '\\n';\n } else if (startIdx !== -1 && comments.length > 0) {\n // Replace existing section\n const section = buildCommentSection(comments);\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n\\n' + section + '\\n' + (after ? '\\n' + after : '');\n } else {\n // startIdx !== -1 && comments.length === 0: remove section\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n' + (after ? after : '');\n }\n\n // Atomic write\n await writeAtomic(wikiPath, updated);\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n","/**\n * pull.ts — STORY-010-04\n *\n * `cleargate pull <ID-or-remote_id>` — targeted single-item pull.\n *\n * --comments flag: reserved for STORY-010-06; emits a warn-level message and\n * proceeds without comment pull. Does NOT error. Does NOT accept silently.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteComment } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\n\nexport interface PullOptions {\n comments?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\nexport async function pullHandler(idOrRemoteId: string, opts: PullOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Resolve the remote_id to pull\n // If idOrRemoteId looks like a remote ID (e.g. LIN-1042) use directly;\n // otherwise try to find local file with matching story_id / epic_id / etc.\n const remoteId = await resolveRemoteId(idOrRemoteId, projectRoot);\n if (!remoteId) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a remote_id. Check that the item has been pushed first.\\n`);\n exit(1);\n return;\n }\n\n // Pull from MCP\n const remoteItem = await mcp.call<RemoteItem | null>('cleargate_pull_item', { remote_id: remoteId });\n if (!remoteItem) {\n stderr(`Error: item ${remoteId} not found on MCP server.\\n`);\n exit(1);\n return;\n }\n\n // Find the local file\n const localPath = await findLocalFile(remoteId, projectRoot);\n\n if (!localPath) {\n stderr(`Error: no local file found with remote_id \"${remoteId}\".\\n`);\n exit(1);\n return;\n }\n\n // Read current state and check idempotency\n const rawContent = await fsPromises.readFile(localPath, 'utf8');\n const { fm, body } = parseFrontmatter(rawContent);\n\n const currentBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const lastPulledAt = typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null;\n\n // Idempotency check: if nothing changed since last pull, skip\n const isNoOp =\n currentBodySha === remoteBodySha &&\n currentStatus === remoteItem.status &&\n typeof fm['last_synced_body_sha'] === 'string' &&\n fm['last_synced_body_sha'] === remoteBodySha &&\n lastPulledAt !== null;\n\n const now = nowFn();\n\n if (isNoOp) {\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'no-op',\n };\n await appendSyncLog(sprintRoot, entry);\n stdout(`pull: ${remoteId} no-op (no changes)\\n`);\n return;\n }\n\n // Apply the pull\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: remoteItem.status,\n last_pulled_by: identity.email,\n last_pulled_at: now,\n last_remote_update: remoteItem.updated_at,\n last_synced_status: remoteItem.status,\n last_synced_body_sha: remoteBodySha,\n };\n\n const newBody = remoteItem.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`pull: ${remoteId} applied to ${path.relative(projectRoot, localPath)}\\n`);\n\n // ── --comments: pull comment snapshot for this item ──────────────────────\n // Always pulls when flag is set (manual override; ignores active criteria).\n if (opts.comments) {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n\n // Rebuild updatedFm as the local item state post-pull for wiki-render\n const localItemForRender = { fm: { ...updatedFm, remote_id: remoteId } };\n await renderCommentsSection({\n projectRoot,\n remoteId,\n comments,\n localItems: [localItemForRender],\n });\n stdout(`pull: ${remoteId} comments fetched (${comments.length})\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveRemoteId(idOrRemoteId: string, projectRoot: string): Promise<string | null> {\n // If it looks like a remote ID pattern (e.g. LIN-NNNN, GH-NNNN, JIRA-123)\n if (/^[A-Z]+-\\d+/.test(idOrRemoteId)) {\n return idOrRemoteId;\n }\n // Try to find a local work item with matching ID, read its remote_id\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n try {\n const raw = await fsPromises.readFile(path.join(pendingSync, entry.name), 'utf8');\n const { fm } = parseFrontmatter(raw);\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId && typeof fm['remote_id'] === 'string') {\n return fm['remote_id'];\n }\n }\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function findLocalFile(remoteId: string, projectRoot: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n if (fm['remote_id'] === remoteId) return fullPath;\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * push.ts — STORY-010-07\n *\n * `cleargate push <file>` — push a local work item to the MCP server.\n * `cleargate push --revert <ID-or-remote_id>` — soft-revert a pushed item.\n *\n * Pre-push gate (client-side):\n * Reads local frontmatter. If approved !== true, exits 1 with a clear message\n * BEFORE any MCP call. Zero network traffic on refusal.\n *\n * Attribution write-back:\n * On success, writes pushed_by + pushed_at from MCP response back into the\n * local frontmatter atomically (.tmp + rename). Appends sync-log entry op='push'.\n *\n * Soft revert (--revert):\n * Calls cleargate_sync_status with new_status='archived-without-shipping'.\n * Does NOT delete the remote item. Does NOT clear local remote_id.\n * Guards against reverting status='done' items unless --force is passed.\n * Appends sync-log entry op='push-revert'.\n *\n * Token safety:\n * JWT tokens (eyJ…) are NEVER written to stdout, stderr, or sync-log.\n * redactDetail in appendSyncLog covers the detail field.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\n// ── Response shapes ─────────────────────────────────────────────────────────────\n\ninterface PushItemResult {\n version: number;\n updated_at: string;\n pushed_by: string;\n pushed_at: string;\n}\n\n// ── Options ─────────────────────────────────────────────────────────────────────\n\nexport interface PushOptions {\n /** --revert <ID-or-remote_id>: soft-revert a pushed item */\n revert?: string;\n /** --force: bypass \"done\" guard on revert */\n force?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (prevents token-from-env requirement) */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────────\n\nexport async function pushHandler(fileOrId: string, opts: PushOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client — resolved lazily and asynchronously via acquireAccessToken.\n // STORY-011-01: approved gate in handlePush runs BEFORE this, so no network\n // traffic happens on refusal (STORY-010-07 invariant preserved).\n async function resolveMcp(): Promise<McpClient> {\n if (opts.mcp) return opts.mcp;\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n throw new Error('unreachable');\n }\n // Acquire token\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n throw new Error('unreachable');\n }\n return createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Revert path ───────────────────────────────────────────────────────────────\n if (opts.revert !== undefined) {\n await handleRevert(opts.revert, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n force: opts.force ?? false,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n return;\n }\n\n // ── Push path ─────────────────────────────────────────────────────────────────\n await handlePush(fileOrId, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n}\n\n// ── Push implementation ───────────────────────────────────────────────────────\n\ninterface PushCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handlePush(filePath: string, ctx: PushCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve path (absolute or relative to projectRoot)\n const resolvedPath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(projectRoot, filePath);\n\n let rawContent: string;\n try {\n rawContent = await fsPromises.readFile(resolvedPath, 'utf8');\n } catch {\n stderr(`Error: cannot read file \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(rawContent));\n } catch (err) {\n stderr(`Error: cannot parse frontmatter in \"${resolvedPath}\": ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Client-side approved gate (BEFORE any MCP call) ─────────────────────────\n // STORY-010-07: if approved !== true, refuse and exit without network call.\n if (fm['approved'] !== true) {\n const itemId = getItemId(fm);\n stderr(\n `Error: push refused — ${itemId} has approved: false. ` +\n `Set approved: true in frontmatter after review.\\n`,\n );\n exit(1);\n return;\n }\n\n const itemId = getItemId(fm);\n const type = getItemType(fm);\n if (!type) {\n stderr(`Error: cannot determine item type from frontmatter in \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n // Derive title from body's first H1 if frontmatter lacks one.\n // ClearGate templates put the human-readable title in `# {ID}: {Name}`,\n // not in a `title:` frontmatter field. Admin UI reads payload.title for\n // item rows; without this, every row renders with an empty heading.\n const payloadForPush: Record<string, unknown> = { ...fm };\n if (typeof payloadForPush['title'] !== 'string' || payloadForPush['title'].length === 0) {\n const h1 = body.match(/^#\\s+(.+?)\\s*$/m)?.[1]?.trim();\n if (h1) payloadForPush['title'] = h1;\n }\n // Include the markdown body verbatim so the admin UI can render the full\n // work-item content (spec, Gherkin, implementation notes, DoD). Stored as\n // payload.body under the item's jsonb column — repo remains the canonical\n // source, MCP is a queryable mirror.\n payloadForPush['body'] = body;\n\n // MCP call\n const mcp = await resolveMcp();\n\n let result: PushItemResult;\n try {\n result = await mcp.call<PushItemResult>('push_item', {\n cleargate_id: itemId,\n type,\n payload: payloadForPush,\n ...(typeof fm['remote_id'] === 'string' ? { remote_id: fm['remote_id'] } : {}),\n });\n } catch (err) {\n stderr(`Error: push_item failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Attribution write-back (atomic) ──────────────────────────────────────────\n const updatedFm: Record<string, unknown> = {\n ...fm,\n pushed_by: result.pushed_by,\n pushed_at: result.pushed_at,\n ...(result.version !== undefined ? { push_version: result.version } : {}),\n };\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + body;\n await writeAtomic(resolvedPath, newContent);\n\n // Sync-log\n const now = nowFn();\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n // Note: pushed_by and pushed_at go in frontmatter, NOT in sync-log detail\n // to prevent any accidental token leakage via the detail field.\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push: ${itemId} → version ${result.version} (pushed_by: ${result.pushed_by})\\n`);\n}\n\n// ── Revert implementation ─────────────────────────────────────────────────────\n\ninterface RevertCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n force: boolean;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handleRevert(idOrRemoteId: string, ctx: RevertCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, force, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve to local file\n const resolved = await resolveLocalItem(idOrRemoteId, projectRoot);\n if (!resolved) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a local work item.\\n`);\n exit(1);\n return;\n }\n\n const { localPath, fm } = resolved;\n const itemId = getItemId(fm);\n const localStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n\n // Guard: refuse to revert \"done\" items without --force\n if (localStatus === 'done' && !force) {\n stderr(`Error: refusing to revert shipped item. Pass --force to override.\\n`);\n exit(1);\n return;\n }\n\n // Call sync_status with new_status='archived-without-shipping'\n const mcp = await resolveMcp();\n try {\n await mcp.call('sync_status', {\n cleargate_id: itemId,\n new_status: 'archived-without-shipping',\n });\n } catch (err) {\n stderr(`Error: sync_status revert failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // DO NOT clear local remote_id — item stays traceable\n // DO NOT overwrite local status — sync will pull the server state back\n\n // Sync-log\n const now = nowFn();\n const remoteId = typeof fm['remote_id'] === 'string' ? fm['remote_id'] : undefined;\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push-revert',\n target: itemId,\n ...(remoteId !== undefined ? { remote_id: remoteId } : {}),\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push --revert: ${itemId} → archived-without-shipping\\n`);\n void localPath; // referenced for clarity; not needed after initial read\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveLocalItem(\n idOrRemoteId: string,\n projectRoot: string,\n): Promise<{ localPath: string; fm: Record<string, unknown> } | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n\n // Match by remote_id\n if (fm['remote_id'] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n\n // Match by primary item ID (story_id, epic_id, etc.)\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n }\n } catch {\n // skip malformed\n }\n }\n }\n\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n\nfunction getItemType(fm: Record<string, unknown>): string | null {\n const typeMap: Record<string, string> = {\n story_id: 'story',\n epic_id: 'epic',\n proposal_id: 'proposal',\n cr_id: 'cr',\n bug_id: 'bug',\n };\n for (const [key, type] of Object.entries(typeMap)) {\n if (typeof fm[key] === 'string' && fm[key]) return type;\n }\n return null;\n}\n","/**\n * conflicts.ts — STORY-010-04 / updated STORY-011-01\n *\n * `cleargate conflicts` — read-only command that reads .cleargate/.conflicts.json\n * and prints unresolved items with one-line resolution hints.\n *\n * Exit 0 when unresolved: [], exit 1 otherwise.\n *\n * --refresh flag: force-invalidate the acquire cache and rotate the stored\n * refresh token even if the cached access token is still valid. This is the\n * only MCP call conflicts makes.\n *\n * No mutations (besides keychain rotation on --refresh). No top-level await\n * (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { ConflictsJson, ConflictEntry } from './sync.js';\nimport { acquireAccessToken } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\nexport interface ConflictsOptions {\n projectRoot?: string;\n /** --refresh: bypass single-flight cache and force a new /auth/refresh. */\n refresh?: boolean;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: override process.env lookup */\n env?: NodeJS.ProcessEnv;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n}\n\nconst RESOLUTION_HINTS: Record<string, string> = {\n 'local-delete-remote-edit': 'remote-delete: resurrect or delete remote?',\n 'remote-delete-local-edit': 'local-edit: push your changes or accept remote deletion?',\n 'refuse': 'manual resolution required — re-run sync after resolving',\n 'halt': 'unknown conflict shape — file a ClearGate bug',\n};\n\nfunction getHint(entry: ConflictEntry): string {\n return RESOLUTION_HINTS[entry.state] ?? RESOLUTION_HINTS[entry.resolution] ?? `resolve and re-run sync`;\n}\n\nexport async function conflictsHandler(opts: ConflictsOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n // ── --refresh: force-invalidate the cache and rotate the keychain token ─────\n if (opts.refresh) {\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — skip refresh; command proceeds without token rotation\n }\n }\n if (baseUrl && baseUrl.trim()) {\n try {\n await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n forceRefresh: true,\n env,\n });\n } catch {\n // Refresh errors are non-fatal for `conflicts` — proceed to print conflicts\n }\n }\n }\n\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n\n let data: ConflictsJson;\n try {\n const raw = await fsPromises.readFile(conflictsFile, 'utf8');\n data = JSON.parse(raw) as ConflictsJson;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n stdout('No conflicts file found. Run `cleargate sync` first.\\n');\n exit(0);\n return;\n }\n throw err;\n }\n\n const unresolved = data.unresolved ?? [];\n\n if (unresolved.length === 0) {\n stdout('No unresolved conflicts.\\n');\n exit(0);\n return;\n }\n\n stdout(`Unresolved conflicts (${unresolved.length}):\\n`);\n stdout(`Generated: ${data.generated_at} Sprint: ${data.sprint_id}\\n\\n`);\n\n for (const item of unresolved) {\n const hint = getHint(item);\n stdout(` ${item.item_id.padEnd(20)} ${item.state.padEnd(30)} ${hint}\\n`);\n }\n\n stdout('\\nRe-run `cleargate sync` after resolving conflicts.\\n');\n exit(1);\n}\n","/**\n * sync-log.ts (command) — STORY-010-04\n *\n * `cleargate sync-log` — filter/print wrapper over readSyncLog() from M1.\n *\n * Flags: --actor, --op, --target, --limit N (default 50). Newest-first guaranteed by lib.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { resolveActiveSprintDir, readSyncLog, type SyncLogOp, type SyncLogEntry } from '../lib/sync-log.js';\n\nexport interface SyncLogCommandOptions {\n actor?: string;\n op?: string;\n target?: string;\n limit?: number;\n projectRoot?: string;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n}\n\nexport async function syncLogHandler(opts: SyncLogCommandOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const limit = opts.limit ?? 50;\n\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // Validate --op value if provided\n const validOps = new Set<SyncLogOp>([\n 'push', 'pull', 'pull-intake', 'push-revert', 'sync-status', 'conflict-remote-wins', 'conflict-refused',\n ]);\n let opFilter: SyncLogOp | undefined;\n if (opts.op !== undefined) {\n if (!validOps.has(opts.op as SyncLogOp)) {\n stderr(`Warning: unknown op \"${opts.op}\". Valid ops: ${[...validOps].join(', ')}\\n`);\n } else {\n opFilter = opts.op as SyncLogOp;\n }\n }\n\n const entries = await readSyncLog(sprintRoot, {\n actor: opts.actor,\n op: opFilter,\n target: opts.target,\n });\n\n const limited = entries.slice(0, limit);\n\n if (limited.length === 0) {\n stdout('No sync-log entries match the given filters.\\n');\n return;\n }\n\n for (const entry of limited) {\n stdout(formatEntry(entry) + '\\n');\n }\n}\n\nfunction formatEntry(entry: SyncLogEntry): string {\n const parts: string[] = [\n entry.ts,\n entry.actor,\n entry.op.padEnd(20),\n entry.target.padEnd(24),\n entry.result,\n ];\n if (entry.remote_id) parts.push(`remote=${entry.remote_id}`);\n if (entry.detail) parts.push(`detail=${entry.detail}`);\n return parts.join(' ');\n}\n","/**\n * `cleargate admin login` — GitHub OAuth device flow for admin CLI login.\n *\n * Flow:\n * 1. POST <mcp-url>/admin-api/v1/auth/device/start → gets device_code + user_code + verification_uri.\n * 2. Prints the verification URL and user code for the operator to open in a browser.\n * 3. Polls POST /admin-api/v1/auth/device/poll every `interval` seconds.\n * 4. On success: writes ~/.cleargate/admin-auth.json { version: 1, token: <admin_jwt> } at chmod 600.\n * 5. On failure: prints a clear error message and exits with appropriate exit code.\n *\n * Exit codes (per STORY-005-06 spec):\n * 0 — success\n * 3 — network error (unreachable)\n * 4 — auth rejected (non-admin GitHub user — not_admin)\n * 5 — device-flow timeout or user denied (expired_token / access_denied)\n * 6 — other device-flow error\n * 99 — unhandled\n *\n * Secrets NEVER appear on stdout/stderr: neither the GitHub access token\n * (server-side only) nor the admin JWT.\n *\n * STORY-005-06.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { startDeviceFlow, DeviceFlowError } from '../auth/identity-flow.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface DevicePollPendingResponse {\n pending: true;\n retry_after?: number;\n}\n\nexport interface DevicePollSuccessResponse {\n pending: false;\n admin_token: string;\n expires_at: string;\n admin_user_id: string;\n}\n\nexport type DevicePollResponse = DevicePollPendingResponse | DevicePollSuccessResponse;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Options / seams\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AdminLoginOptions {\n mcpUrl?: string;\n env?: NodeJS.ProcessEnv;\n homedir?: () => string;\n fetch?: typeof globalThis.fetch;\n stdout?: (msg: string) => void;\n stderr?: (msg: string) => void;\n exit?: (code: number) => never;\n /** Override polling interval in milliseconds (used in tests to avoid real waits) */\n intervalOverrideMs?: number;\n /** Override admin-auth file path */\n authFilePath?: string;\n /** Override sleep implementation — injected in tests to capture interval values */\n sleepFn?: (ms: number) => Promise<void>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_MCP_URL = 'http://localhost:3000';\n\n/** Sleep helper — exported so tests can spy on it. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveMcpUrl(mcpUrlFlag?: string, env?: NodeJS.ProcessEnv): string {\n return (\n mcpUrlFlag ??\n (env ?? process.env)['CLEARGATE_MCP_URL'] ??\n DEFAULT_MCP_URL\n ).replace(/\\/$/, '');\n}\n\nfunction resolveAuthFilePath(opts: AdminLoginOptions): string {\n if (opts.authFilePath) return opts.authFilePath;\n const homedirFn = opts.homedir ?? os.homedir;\n return path.join(homedirFn(), '.cleargate', 'admin-auth.json');\n}\n\nfunction writeAdminAuth(filePath: string, token: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const payload = JSON.stringify({ version: 1, token }, null, 2);\n fs.writeFileSync(filePath, payload, { encoding: 'utf8', mode: 0o600 });\n // Explicit chmod in case the file already existed with wider permissions\n fs.chmodSync(filePath, 0o600);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main handler\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function adminLoginHandler(opts: AdminLoginOptions = {}): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((msg: string) => process.stdout.write(msg + '\\n'));\n const stderr = opts.stderr ?? ((msg: string) => process.stderr.write(msg + '\\n'));\n const exitFn = opts.exit ?? ((code: number): never => process.exit(code));\n const mcpBase = resolveMcpUrl(opts.mcpUrl, opts.env);\n\n // ── Step 1: Start device flow ──────────────────────────────────────────────\n let startData: DeviceStartResponse;\n try {\n const startRes = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n });\n\n if (!startRes.ok) {\n const body = (await startRes.json().catch(() => ({}))) as { error?: string };\n if (startRes.status === 503) {\n stderr('cleargate: error: device flow not configured on the server (CLEARGATE_GITHUB_CLI_CLIENT_ID not set).');\n return exitFn(6);\n }\n stderr(`cleargate: error: server error ${startRes.status}: ${body.error ?? 'unknown'}`);\n return exitFn(6);\n }\n\n startData = (await startRes.json()) as DeviceStartResponse;\n } catch (err) {\n stderr(`cleargate: error: cannot reach ${mcpBase} (${err instanceof Error ? err.message : String(err)})`);\n return exitFn(3);\n }\n\n // ── Step 2: Display instructions ───────────────────────────────────────────\n stdout(`Open the following URL in your browser and enter the code:`);\n stdout(` URL: ${startData.verification_uri}`);\n stdout(` Code: ${startData.user_code}`);\n stdout(` (Code expires in ${Math.floor(startData.expires_in / 60)} minutes)`);\n stdout('Waiting for authorization...');\n\n // ── Step 3: Poll via identity-flow.startDeviceFlow with captured success body ─\n // We wrap fetchPoll to capture the full DevicePollSuccessResponse (which includes\n // expires_at + admin_user_id needed for the success stdout message) since\n // startDeviceFlow only returns { accessToken }.\n let capturedSuccessBody: DevicePollSuccessResponse | null = null;\n\n const fetchPollCapture = async (deviceCode: string) => {\n const res = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/poll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n // Capture the success body before startDeviceFlow consumes it.\n // We intercept by wrapping json() to also store the body when pending=false.\n const originalJson = res.json.bind(res);\n return {\n status: res.status,\n json: async () => {\n const body = (await originalJson()) as Record<string, unknown>;\n if (body['pending'] === false) {\n capturedSuccessBody = body as unknown as DevicePollSuccessResponse;\n }\n return body;\n },\n };\n };\n\n try {\n await startDeviceFlow({\n deviceCode: startData.device_code,\n interval: startData.interval,\n expiresIn: startData.expires_in,\n fetchPoll: fetchPollCapture,\n // Only pass sleepFn if the caller explicitly injected one (test seam).\n // When sleepFn is omitted, startDeviceFlow uses its own defaultSleep.\n // This preserves the original bump-suppression logic:\n // shouldApplyBump = (sleepFn provided) || (intervalOverrideMs not set).\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n deadlineGraceMs: 10_000,\n });\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: error: access denied — you declined authorization in the browser.');\n return exitFn(5);\n case 'not_admin':\n stderr('cleargate: error: your GitHub account is not authorized as an admin user.');\n return exitFn(4);\n case 'expired_token':\n stderr('cleargate: error: device code expired — please run `cleargate admin login` again.');\n return exitFn(5);\n case 'timeout':\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n case 'unreachable':\n stderr(`cleargate: error: network error while polling`);\n return exitFn(3);\n default:\n stderr(`cleargate: error: unexpected server response`);\n return exitFn(6);\n }\n }\n stderr(`cleargate: error: unexpected error during device flow`);\n return exitFn(6);\n }\n\n if (!capturedSuccessBody) {\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n }\n\n // Extract fields with explicit typing to avoid TypeScript discriminated union narrowing\n const successBody = capturedSuccessBody as DevicePollSuccessResponse;\n\n // ── Step 4: Write admin-auth.json ──────────────────────────────────────────\n const authFilePath = resolveAuthFilePath(opts);\n try {\n writeAdminAuth(authFilePath, successBody.admin_token);\n } catch (err) {\n stderr(`cleargate: error: failed to write ${authFilePath}: ${err instanceof Error ? err.message : String(err)}`);\n return exitFn(99);\n }\n\n // ── Step 5: Success message (no secrets in output) ─────────────────────────\n stdout(`Logged in successfully. Token expires ${successBody.expires_at}.`);\n stdout(`Credentials saved to ${authFilePath} (chmod 600).`);\n}\n","/**\n * hotfix.ts — `cleargate hotfix new <slug>` command handler.\n *\n * STORY-022-06: Hotfix lane scaffolding.\n *\n * Creates a new HOTFIX-NNN_<slug>.md file in `.cleargate/delivery/pending-sync/`\n * from the bundled hotfix.md template, with ID auto-incremented via a scan of\n * existing HOTFIX-* files.\n *\n * Cap stub: blocks the 4th hotfix in a rolling 7-day window (pending-sync +\n * archive files modified within the last 7 days). Node fs APIs only — no\n * shell-outs (cross-OS per BUG-010 §4b).\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface HotfixCliOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override cwd for the repo root (test seam). */\n cwd?: string;\n /** Override the current ISO timestamp (test seam). */\n now?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nconst SLUG_RE = /^[a-z0-9-]+$/;\nconst HOTFIX_FILE_RE = /^HOTFIX-(\\d+)_.*\\.md$/;\n\n/**\n * Scan pending-sync/ for HOTFIX-NNN_*.md files; return the highest NNN found\n * (or 0 if none). Synchronous — no async needed for the tiny delivery dir.\n */\nfunction maxHotfixId(pendingDir: string): number {\n let max = 0;\n let entries: string[];\n try {\n entries = fs.readdirSync(pendingDir);\n } catch {\n return 0;\n }\n for (const entry of entries) {\n const m = HOTFIX_FILE_RE.exec(entry);\n if (m) {\n const n = parseInt(m[1]!, 10);\n if (n > max) max = n;\n }\n }\n return max;\n}\n\n/**\n * Count active hotfixes for the rolling-window cap.\n *\n * Counts:\n * - All HOTFIX-*.md files in pending-sync/ (regardless of mtime — they are\n * by definition active/in-flight).\n * - HOTFIX-*.md files in archive/ whose mtime is within the last 7 days\n * (recently merged/resolved within the window).\n */\nfunction countActiveHotfixes(repoRoot: string): number {\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(repoRoot, '.cleargate', 'delivery', 'archive');\n const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;\n\n let count = 0;\n\n // All in pending-sync\n let pendingEntries: string[] = [];\n try {\n pendingEntries = fs.readdirSync(pendingDir);\n } catch {\n // dir may not exist in test fixtures\n }\n for (const entry of pendingEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) count++;\n }\n\n // Recently archived (within 7-day window)\n let archiveEntries: string[] = [];\n try {\n archiveEntries = fs.readdirSync(archiveDir);\n } catch {\n // dir may not exist\n }\n for (const entry of archiveEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) {\n try {\n const stat = fs.statSync(path.join(archiveDir, entry));\n if (stat.mtimeMs >= sevenDaysAgo) count++;\n } catch {\n // Skip unreadable entries\n }\n }\n }\n\n return count;\n}\n\n/**\n * Resolve the path to `.cleargate/templates/hotfix.md` relative to the repo root.\n */\nfunction resolveTemplatePath(repoRoot: string): string {\n return path.join(repoRoot, '.cleargate', 'templates', 'hotfix.md');\n}\n\n// ─── hotfixNewHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate hotfix new <slug>`\n *\n * - Validates slug matches ^[a-z0-9-]+$.\n * - Checks rolling-window hotfix cap (≤3 per 7 days).\n * - Reads .cleargate/templates/hotfix.md and substitutes {ID}, {SLUG}, {ISO}.\n * - Writes to .cleargate/delivery/pending-sync/HOTFIX-<NNN>_<slug>.md\n * (slug with - replaced by _ in filename, matching STORY-NNN-NN_Name.md convention).\n *\n * Exits 1 on validation failure or cap exceeded; exits 0 on success.\n */\nexport function hotfixNewHandler(\n opts: { slug: string },\n cli?: HotfixCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const repoRoot = cli?.cwd ?? process.cwd();\n const now = cli?.now ?? new Date().toISOString();\n\n // ── Validate slug ────────────────────────────────────────────────────────\n if (!SLUG_RE.test(opts.slug)) {\n stderrFn(`[cleargate hotfix new] slug must match ^[a-z0-9-]+$ (got: \"${opts.slug}\")`);\n return exitFn(1);\n }\n\n // ── Cap check ────────────────────────────────────────────────────────────\n const activeCount = countActiveHotfixes(repoRoot);\n if (activeCount >= 3) {\n stderrFn(\n `Hotfix cap: ≤3 per rolling 7-day window. Currently ${activeCount} active. Bundle into a sprint or downgrade one to a CR.`,\n );\n return exitFn(1);\n }\n\n // ── Next ID ──────────────────────────────────────────────────────────────\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const maxId = maxHotfixId(pendingDir);\n const nextId = maxId + 1;\n const idStr = `HOTFIX-${String(nextId).padStart(3, '0')}`;\n\n // ── Read template ────────────────────────────────────────────────────────\n const templatePath = resolveTemplatePath(repoRoot);\n let templateContent: string;\n try {\n templateContent = fs.readFileSync(templatePath, 'utf8');\n } catch {\n stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);\n return exitFn(2);\n }\n\n // ── Substitute placeholders ──────────────────────────────────────────────\n const content = templateContent\n .replace(/\\{ID\\}/g, idStr)\n .replace(/\\{SLUG\\}/g, opts.slug)\n .replace(/\\{ISO\\}/g, now);\n\n // ── Write output file ────────────────────────────────────────────────────\n // Filename convention: HOTFIX-NNN_slug_with_underscores.md\n const fileSlug = opts.slug.replace(/-/g, '_');\n const fileName = `${idStr}_${fileSlug}.md`;\n const outPath = path.join(pendingDir, fileName);\n\n try {\n fs.mkdirSync(pendingDir, { recursive: true });\n fs.writeFileSync(outPath, content, 'utf8');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n stderrFn(`[cleargate hotfix new] write failed: ${msg}`);\n return exitFn(1);\n }\n\n stdoutFn(`[cleargate hotfix new] created: ${outPath}`);\n // Explicit exit 0 to satisfy the exit-seam contract used in tests.\n return exitFn(0);\n}\n"],"mappings":"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","names":["fs","path","fs","path","import_zod","Entry","os","path","body","decodeJwtPayload","pg","os","resolve","body","readline","hostname","readNextLine","resolve","fs","os","path","fs","path","fs","yaml","import_js_yaml","yaml","raw","fm","stampOpts","result","fs","path","import_node_url","import_node_child_process","fs","path","fs","path","fs","path","fs","path","state","fs","path","import_node_url","compile","state","resolveDefaultTemplateDir","fs","path","import_node_url","compile","state","resolveDefaultTemplateDir","fs","path","import_node_url","compile","state","resolveDefaultTemplateDir","isSet","compile","story","import_promises","path","state","readline","resolve","fs","path","os","import_node_child_process","hostname","fs","path","import_node_child_process","EXCLUDED_SUFFIXES","BUCKET_ORDER","BUCKET_LABELS","rename","buildParentRef","buildChildrenRefs","buildPageBody","compile","path","fs","path","fs","path","import_node_child_process","import_js_yaml","PREFIX_MAP","basename","yaml","fs","path","import_js_yaml","yaml","wiki","fs","path","fs","path","readline","resolve","fs","path","import_node_child_process","state","parseCachedGateResult","gate","fs","path","import_node_child_process","fs","path","import_js_yaml","fs","path","fs","import_js_yaml","yaml","yaml","gate","code","import_node_child_process","fs","path","import_node_child_process","import_js_yaml","TERMINAL_STATUSES","yaml","state","fs","path","import_node_child_process","defaultExit","resolveRunScript","state","path","import_node_child_process","defaultExit","resolveRunScript","fs","path","fs","path","basename","path","BLOCK_REGEX","state","resolve","import_node_child_process","resolve","writeAtomic","state","newSha","fs","fsp","path","import_node_child_process","writeAtomic","resolve","fsPromises","path","fs","fsPromises","path","classify","import_node_fs","os","path","resolve","fs","text","fsPromises","path","fsPromises","path","proposalId","slug","num","filename","targetPath","fs","fsPromises","path","fsPromises","path","fsPromises","path","writeAtomic","nowIso","classify","writeAtomic","fsPromises","path","entry","getItemId","writeAtomic","fsPromises","path","itemId","getItemId","writeAtomic","fsPromises","path","fs","path","os","fs","path","defaultExit","whoamiHandler","bootstrapRootHandler"]}
|