lee-spec-kit 0.9.10 → 0.9.13
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/LICENSE +5 -0
- package/README.en.md +6 -2
- package/README.md +9 -3
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/dist/{hooks-GUQ2II2R.js → hooks-P7CYYJYH.js} +28 -5
- package/dist/hooks-P7CYYJYH.js.map +1 -0
- package/dist/index.js +3350 -1012
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/resources/openwiki-skills/lee-spec-kit-technical-writing/.lee-spec-kit-skill.json +6 -0
- package/resources/openwiki-skills/lee-spec-kit-technical-writing/LICENSE.md +9 -0
- package/resources/openwiki-skills/lee-spec-kit-technical-writing/SKILL.md +62 -0
- package/resources/openwiki-skills/lee-spec-kit-technical-writing/references/document-patterns.md +54 -0
- package/resources/openwiki-skills/lee-spec-kit-technical-writing/references/information-architecture.md +53 -0
- package/resources/openwiki-skills/lee-spec-kit-technical-writing/references/korean-style.md +71 -0
- package/templates/en/common/README.md +9 -3
- package/templates/en/common/agents/agents.md +5 -4
- package/templates/en/common/agents/custom.md +3 -2
- package/templates/en/common/agents/skills/create-pr.md +2 -0
- package/templates/en/common/agents/skills/execute-task.md +2 -0
- package/templates/en/common/features/README.md +2 -2
- package/templates/en/common/features/feature-base/decisions.md +1 -0
- package/templates/en/common/features/feature-base/plan.md +24 -3
- package/templates/ko/common/README.md +9 -3
- package/templates/ko/common/agents/agents.md +5 -4
- package/templates/ko/common/agents/custom.md +3 -2
- package/templates/ko/common/agents/skills/create-pr.md +2 -0
- package/templates/ko/common/agents/skills/execute-task.md +2 -0
- package/templates/ko/common/features/README.md +2 -2
- package/templates/ko/common/features/feature-base/decisions.md +1 -0
- package/templates/ko/common/features/feature-base/plan.md +24 -3
- package/dist/hooks-GUQ2II2R.js.map +0 -1
|
@@ -51,8 +51,11 @@ src/
|
|
|
51
51
|
|
|
52
52
|
## Curated Documentation Impact
|
|
53
53
|
|
|
54
|
+
발견한 문서 불일치는 `decisions.md`에만 남기고 종료하지 않습니다. 현재 사실의 명백한 오류가 승인 범위 안에 있으면 `UPDATE`/`ADD`와 task `Docs`로 연결합니다. 제품 의도 확인이나 범위 확장이 필요하면 충돌한 문서 경로·근거, 확인할 질문, 보류 이유와 실제 후속 task/Feature/issue 참조를 기록합니다. 없는 번호나 승인을 만들지 않습니다. 추적 항목 생성에 승인이 필요하면 사용자 확인 전 해결된 것으로 기록하지 않습니다. `NONE`의 근거에는 알려진 불일치가 없거나, 남은 불일치가 해당 후속 항목으로 추적되고 있음을 설명합니다. 코드나 OpenWiki에 맞추기 위해 미구현 PRD 요구를 삭제하지 않습니다.
|
|
55
|
+
|
|
54
56
|
> 모든 결정이 `NONE`이어도 영향 판정을 완료합니다. `NONE`은 사람이 관리하는 상위 문서를 검토했지만 변경할 필요가 없다는 뜻입니다. 생성형 OpenWiki 동기화는 별도로 판정합니다.
|
|
55
57
|
|
|
58
|
+
- **Schema**: 2
|
|
56
59
|
- **Assessment**: Pending
|
|
57
60
|
- 값: Pending | Complete
|
|
58
61
|
- **Product requirements**: -
|
|
@@ -70,6 +73,24 @@ src/
|
|
|
70
73
|
|
|
71
74
|
---
|
|
72
75
|
|
|
76
|
+
## Additional Curated Impacts
|
|
77
|
+
|
|
78
|
+
> constitution/custom, 디자인 시스템, API·데이터, 보안, 배포, 관측성처럼 조건부로 존재하는 상위 문서를 판정합니다. 해당 영향이 없으면 `Decision: NONE`을 명시하고 표는 비워 둡니다.
|
|
79
|
+
|
|
80
|
+
- **Assessment**: Pending
|
|
81
|
+
- **Decision**: -
|
|
82
|
+
- 값: NONE | DECLARED
|
|
83
|
+
|
|
84
|
+
| Kind | Decision | Target | Reason |
|
|
85
|
+
| ---- | -------- | ------ | ------ |
|
|
86
|
+
| - | - | - | - |
|
|
87
|
+
|
|
88
|
+
허용 Kind: `engineering-agent-policy`, `design-system-ux`, `api-data-contract`, `security-privacy`, `release-deployment`, `observability`, `other-curated`
|
|
89
|
+
|
|
90
|
+
`DECLARED` 행의 Decision은 `UPDATE` 또는 `ADD`이고, Target은 `docs:<path>` 또는 `project:<path>`여야 합니다. 모든 Target은 task `Docs` 목록에 연결합니다.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
73
94
|
## Verification Contract
|
|
74
95
|
|
|
75
96
|
### 변경 분류
|
|
@@ -87,9 +108,9 @@ src/
|
|
|
87
108
|
|
|
88
109
|
### 테스트 결정
|
|
89
110
|
|
|
90
|
-
| 계약 / 요구사항 | 결정
|
|
91
|
-
| --------------- |
|
|
92
|
-
| (AC/FR 참조) | NONE \| UPDATE \| ADD | 단위 \| 통합 \| E2E \| 비테스트 | (방지할 실패)
|
|
111
|
+
| 계약 / 요구사항 | 결정 | 테스트 수준 | 보호할 현실적인 회귀 | 독립적인 Oracle |
|
|
112
|
+
| --------------- | --------------------- | ------------------------------- | -------------------- | -------------------------- |
|
|
113
|
+
| (AC/FR 참조) | NONE \| UPDATE \| ADD | 단위 \| 통합 \| E2E \| 비테스트 | (방지할 실패) | (스펙/출시 동작/외부 기준) |
|
|
93
114
|
|
|
94
115
|
### 의도적으로 제외하는 테스트
|
|
95
116
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/integrations/codex/hooks.ts"],"names":[],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","file":"hooks-GUQ2II2R.js","sourcesContent":["import fs from 'fs-extra';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { runGitCapture } from '../../utils/git-run.js';\n\nconst MANAGED_HOOK_FILENAMES = [\n '_lee_spec_kit_hook_utils.mjs',\n 'session_start_lee_spec_kit.mjs',\n 'subagent_start_lee_spec_kit.mjs',\n 'user_prompt_submit_lee_spec_kit.mjs',\n 'pre_tool_use_policy.mjs',\n 'stop_workflow_audit.mjs',\n] as const;\n\ntype ManagedHookFileName = (typeof MANAGED_HOOK_FILENAMES)[number];\ntype HookEventName =\n | 'SessionStart'\n | 'SubagentStart'\n | 'UserPromptSubmit'\n | 'PreToolUse'\n | 'Stop';\n\ninterface HookHandler {\n type: 'command';\n command: string;\n statusMessage?: string;\n timeout?: number;\n}\n\ninterface HookMatcherGroup {\n matcher?: string;\n hooks: HookHandler[];\n}\n\ninterface HooksConfigFile {\n hooks?: Partial<Record<HookEventName, HookMatcherGroup[]>> &\n Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction getHookScriptContent(\n fileName: ManagedHookFileName,\n repoRoot: string,\n workflowRoot: string\n): string {\n switch (fileName) {\n case '_lee_spec_kit_hook_utils.mjs':\n return `#!/usr/bin/env node\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\n\nexport function readHookInput() {\n try {\n const raw = fs.readFileSync(0, 'utf8').trim();\n if (!raw) return { ok: true, value: {} };\n return {\n ok: true,\n value: JSON.parse(raw),\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Invalid Codex hook payload';\n return {\n ok: false,\n error: message,\n };\n }\n}\n\nconst CLI_ENTRYPOINT = ${JSON.stringify(getInstalledCliEntrypoint())};\nconst HOOK_REPO_ROOT = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n '..',\n '..'\n);\nconst WORKFLOW_ROOT_RELATIVE = ${JSON.stringify(\n path.relative(path.resolve(repoRoot), path.resolve(workflowRoot))\n )};\n\nexport function getWorkflowCwd() {\n return path.resolve(HOOK_REPO_ROOT, WORKFLOW_ROOT_RELATIVE);\n}\n\nexport function runLeeSpecKit(args, cwd = process.cwd()) {\n return spawnSync(process.execPath, [CLI_ENTRYPOINT, ...args], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n}\n\nexport function runLeeSpecKitJson(args, cwd = process.cwd()) {\n const result = runLeeSpecKit(args, cwd);\n const stdout = String(result.stdout || '').trim();\n const stderr = String(result.stderr || '').trim();\n\n if (result.error) {\n return {\n ok: false,\n error: result.error.message || String(result.error),\n status: result.status ?? 1,\n };\n }\n\n if (result.status !== 0) {\n return {\n ok: false,\n error: stderr || stdout || \\`lee-spec-kit \\${args.join(' ')} failed\\`,\n status: result.status ?? 1,\n };\n }\n\n if (!stdout) {\n return {\n ok: false,\n error: \\`lee-spec-kit \\${args.join(' ')} returned empty JSON output\\`,\n status: result.status ?? 0,\n };\n }\n\n try {\n return {\n ok: true,\n data: JSON.parse(stdout),\n status: result.status ?? 0,\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Invalid JSON output from lee-spec-kit';\n return {\n ok: false,\n error: \\`\\${message}: \\${stdout.slice(0, 200)}\\`,\n status: result.status ?? 0,\n };\n }\n}\n\nexport function printAdditionalContext(hookEventName, additionalContext) {\n process.stdout.write(\n JSON.stringify({\n hookSpecificOutput: {\n hookEventName,\n additionalContext,\n },\n })\n );\n}\n\nexport function printBlock(reason) {\n process.stdout.write(\n JSON.stringify({\n decision: 'block',\n reason,\n })\n );\n}\n`;\n case 'session_start_lee_spec_kit.mjs':\n return `#!/usr/bin/env node\nimport { getWorkflowCwd, printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';\n\n// Equivalent CLI probe: npx lee-spec-kit detect --json\nconst inputResult = readHookInput();\nif (!inputResult.ok) {\n process.exit(0);\n}\nconst input = inputResult.value;\nconst cwd = getWorkflowCwd();\nconst detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);\nconst detected = detectedResult.ok ? detectedResult.data : null;\n\nif (detected?.status === 'ok' && detected?.isLeeSpecKitProject === true) {\n const docsDir = detected.docsDir || '(unknown docs dir)';\n const stageResult = runLeeSpecKitJson(['workflow-stage', '--json'], cwd);\n const lines = [\n 'lee-spec-kit project detected.',\n 'Use lee-spec-kit docs and workflow policy only when explicitly detected.',\n 'Prefer Codex native execution with workspace-scoped AGENTS.md plus official hooks for the default runtime path.',\n 'If the user gives a generic request such as continuing the next feature according to the rules, interpret it through this workflow automatically.',\n 'infer the workflow automatically even for generic rule-following requests.',\n \\`Docs dir: \\${docsDir}\\`,\n 'Primary agents start by reading npx lee-spec-kit docs get agents --json and the active feature docs.',\n 'Delegated subagents skip the primary-agent bootstrap and follow the exact delegationContext and workerContract returned by workflow-stage.',\n 'Primary agents run npx lee-spec-kit workflow-stage --json before the next stage and only follow its nextAction.',\n 'Keep docs as the SSOT and treat workflow-audit as the end-of-turn sync guard.',\n ];\n if (stageResult.ok && stageResult.data?.status === 'ok') {\n lines.push(\n \\`Current workflow stage: \\${stageResult.data.stage}\\`,\n \\`Next allowed action: \\${stageResult.data.nextAction?.category || 'none'}\\`,\n \\`Approval required: \\${stageResult.data.approvalRequired ? 'yes' : 'no'}\\`,\n \\`Implementation allowed: \\${stageResult.data.implementationAllowed ? 'yes' : 'no'}\\`\n );\n if (stageResult.data.primaryActionLabel && Array.isArray(stageResult.data.actionOptions)) {\n lines.push(\n \\`Primary reply label: \\${stageResult.data.primaryActionLabel}\\`,\n ...stageResult.data.actionOptions.map(\n (option) => \\`Option \\${option.label} -> reply \\${option.reply}: \\${option.summary}\\`\n )\n );\n }\n } else if (stageResult.ok && stageResult.data?.status === 'error') {\n lines.push(\n \\`Workflow stage is unresolved: \\${stageResult.data.reasonCode}\\`,\n 'Resolve feature selection or create/select the target feature before continuing.'\n );\n }\n printAdditionalContext('SessionStart', lines.join('\\\\n'));\n}\n`;\n case 'subagent_start_lee_spec_kit.mjs':\n return `#!/usr/bin/env node\nimport { getWorkflowCwd, printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';\n\nconst inputResult = readHookInput();\nif (!inputResult.ok) {\n process.exit(0);\n}\nconst input = inputResult.value;\nconst cwd = getWorkflowCwd();\nconst detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);\nconst detected = detectedResult.ok ? detectedResult.data : null;\n\nif (detected?.status === 'ok' && detected?.isLeeSpecKitProject === true) {\n const agentType =\n typeof input.agent_type === 'string' && input.agent_type.trim()\n ? input.agent_type.trim().replace(/[\\u0000-\\u001f\\u007f]/g, ' ')\n : 'unknown';\n const lines = [\n 'lee-spec-kit delegated subagent detected.',\n \\`Codex agent type/profile: \\${agentType}\\`,\n 'Skip the primary-agent startup bootstrap: do not run detect, docs get agents, load every requiredDocs entry, or run workflow-stage.',\n 'Follow the exact delegationContext and workerContract supplied by the parent agent. Read the requiredDocuments in that contract and use referenceDocuments only under their stated conditions.',\n 'Do not load built-in requiredDocs, unrelated Features, or additional Feature documents outside the delegationContext. Ask the parent agent if the contract is missing or insufficient before expanding scope.',\n 'The agent type/profile is informative; delegationContext, the delegation prompt, and workerContract define the exact implementation or review role and its permissions.',\n 'Continue to follow applicable repository safety instructions and the explicit delegation boundaries.',\n ];\n printAdditionalContext('SubagentStart', lines.join('\\\\n'));\n}\n`;\n case 'user_prompt_submit_lee_spec_kit.mjs':\n return `#!/usr/bin/env node\nimport { getWorkflowCwd, printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';\n\nconst inputResult = readHookInput();\nif (!inputResult.ok) {\n process.exit(0);\n}\nconst input = inputResult.value;\nconst cwd = getWorkflowCwd();\nconst detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);\nconst detected = detectedResult.ok ? detectedResult.data : null;\n\nif (detected?.status === 'ok' && detected?.isLeeSpecKitProject === true) {\n const stageResult = runLeeSpecKitJson(['workflow-stage', '--json'], cwd);\n const lines = [\n 'This prompt is inside a lee-spec-kit workspace.',\n 'Interpret generic rule-following requests through the lee-spec-kit docs workflow automatically.',\n 'Primary agents prefer docs get plus feature-local docs as the primary context source.',\n 'Delegated subagents follow the exact delegationContext and workerContract instead of running the primary-agent bootstrap.',\n 'Primary agents use workflow-stage --json to determine the next allowed stage before implementation.',\n ];\n if (stageResult.ok && stageResult.data?.status === 'ok') {\n lines.push(\n \\`Current workflow stage: \\${stageResult.data.stage}\\`,\n \\`Next allowed action: \\${stageResult.data.nextAction?.category || 'none'}\\`,\n \\`Approval required: \\${stageResult.data.approvalRequired ? 'yes' : 'no'}\\`,\n \\`Implementation allowed: \\${stageResult.data.implementationAllowed ? 'yes' : 'no'}\\`,\n 'Do not jump ahead of the reported nextAction.'\n );\n if (stageResult.data.primaryActionLabel && Array.isArray(stageResult.data.actionOptions)) {\n lines.push(\n 'If labeled action options are present, keep the option labels but ask the user to reply with the exact reply token shown for that option.',\n ...stageResult.data.actionOptions.map(\n (option) => \\`Option \\${option.label} -> reply \\${option.reply}: \\${option.summary}\\`\n )\n );\n }\n } else if (stageResult.ok && stageResult.data?.status === 'error') {\n lines.push(\n \\`Workflow stage is unresolved: \\${stageResult.data.reasonCode}\\`,\n 'Resolve feature selection before attempting implementation.'\n );\n }\n printAdditionalContext('UserPromptSubmit', lines.join('\\\\n'));\n}\n`;\n case 'pre_tool_use_policy.mjs':\n return `#!/usr/bin/env node\nimport { getWorkflowCwd, printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nfunction normalizeResolvedPath(value) {\n try {\n return fs.realpathSync.native(value);\n } catch {\n return path.resolve(value);\n }\n}\n\nconst inputResult = readHookInput();\nif (!inputResult.ok) {\n printBlock('Codex hook input was malformed. Resolve the local hook setup before continuing.');\n process.exit(0);\n}\nconst input = inputResult.value;\nconst cwd = typeof input.cwd === 'string' && input.cwd ? input.cwd : process.cwd();\nconst workflowCwd = getWorkflowCwd();\nconst command = String(input?.tool_input?.command || '').trim();\n\nfunction tokenizeShellCommand(value) {\n const matches = value.match(/\"(?:\\\\\\\\.|[^\"])*\"|'(?:\\\\\\\\.|[^'])*'|\\\\S+/g) || [];\n return matches.map((token) => {\n if (\n (token.startsWith('\"') && token.endsWith('\"')) ||\n (token.startsWith(\"'\") && token.endsWith(\"'\"))\n ) {\n return token.slice(1, -1);\n }\n return token;\n });\n}\n\nfunction normalizeExecutableToken(token) {\n const base = token.split(/[\\\\\\\\/]/).pop() || token;\n return base.replace(/\\\\.(?:bat|cmd|exe)$/i, '').toLowerCase();\n}\n\nfunction stripEnvWrapper(tokens) {\n let index = 1;\n while (index < tokens.length) {\n const token = tokens[index];\n if (!token) {\n index += 1;\n continue;\n }\n if (token === '--') {\n return tokens.slice(index + 1);\n }\n if (token.startsWith('-')) {\n index += 1;\n continue;\n }\n if (/^[A-Za-z_][A-Za-z0-9_]*=.*/.test(token)) {\n index += 1;\n continue;\n }\n return tokens.slice(index);\n }\n\n return tokens;\n}\n\nfunction stripSudoWrapper(tokens) {\n let index = 1;\n while (index < tokens.length) {\n const token = tokens[index];\n if (!token) {\n index += 1;\n continue;\n }\n if (token === '--') {\n return tokens.slice(index + 1);\n }\n if (token === '-u' || token === '-g' || token === '-h' || token === '-p') {\n index += 2;\n continue;\n }\n if (token.startsWith('-')) {\n index += 1;\n continue;\n }\n return tokens.slice(index);\n }\n\n return tokens;\n}\n\nconst KNOWN_SHELL_EXECUTABLES = new Set([\n 'ash',\n 'bash',\n 'cmd',\n 'dash',\n 'fish',\n 'ksh',\n 'powershell',\n 'pwsh',\n 'sh',\n 'zsh',\n]);\nconst DIRECT_GIT_OR_GH_EXECUTABLES = new Set(['git', 'gh']);\n\nfunction isShellCommandFlag(token) {\n const lower = token.toLowerCase();\n if (lower === '-c' || lower === '/c' || lower === '-command') {\n return true;\n }\n if (token === lower && /^-[a-z]*c[a-z]*$/.test(token)) {\n return true;\n }\n return false;\n}\n\nfunction isExecutablePayloadFlag(token) {\n const lower = token.toLowerCase();\n if (isShellCommandFlag(token)) {\n return true;\n }\n return lower === '-e' || lower === '-r' || lower === '--eval' || lower === '--execute';\n}\n\nfunction findShellCommandFlagIndex(tokens) {\n return tokens.findIndex((token, index) => index > 0 && isShellCommandFlag(token));\n}\n\nfunction findExecutablePayloadFlagIndex(tokens) {\n return tokens.findIndex((token, index) => index > 0 && isExecutablePayloadFlag(token));\n}\n\nfunction containsDangerousGitOrGhPayload(value) {\n return (\n /\\\\bgit(?:\\\\.cmd|\\\\.exe)?\\\\b[\\\\s\\\\S]{0,80}\\\\b(?:commit|push|checkout|switch|restore|clean|rebase|merge|cherry-pick|revert|stash|reset|branch|tag)\\\\b/i.test(\n value\n ) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\b[\\\\s\\\\S]{0,80}\\\\b(?:issue|pr|repo|release)\\\\b/i.test(\n value\n )\n );\n}\n\nfunction containsProcessExecutionPayload(value) {\n return (\n /\\\\bchild_process\\\\b/i.test(value) ||\n /\\\\bspawn(?:Sync)?\\\\s*\\\\(/i.test(value) ||\n /\\\\bexec(?:Sync|FileSync|File)?\\\\s*\\\\(/i.test(value) ||\n /\\\\bfork\\\\s*\\\\(/i.test(value) ||\n /\\\\bsubprocess\\\\b/i.test(value) ||\n /\\\\bos\\\\.system\\\\s*\\\\(/i.test(value) ||\n /\\\\bsystem\\\\s*\\\\(/i.test(value) ||\n /\\\\bpopen\\\\s*\\\\(/i.test(value) ||\n /\\\\bcreateprocess\\\\b/i.test(value) ||\n /\\\\bstart-process\\\\b/i.test(value)\n );\n}\n\nconst KNOWN_EXECUTABLE_WRAPPERS = new Set([\n 'bun',\n 'deno',\n 'node',\n 'nodejs',\n 'perl',\n 'php',\n 'python',\n 'python2',\n 'python3',\n 'ruby',\n]);\n\nconst KNOWN_WRAPPER_LAUNCHERS = new Set(['uv', 'uvx']);\n\nconst EXECUTABLE_WRAPPER_OPTIONS_WITH_VALUE = new Set([\n '--experimental-loader',\n '--import',\n '--loader',\n '--require',\n '-m',\n '-r',\n]);\n\nconst UNSUPPORTED_WRAPPER_PAYLOAD = '__LEE_SPEC_KIT_UNSUPPORTED_WRAPPER_PAYLOAD__';\n\nfunction readWrapperScriptPayload(executable, tokens, rawValue, baseCwd) {\n if (KNOWN_WRAPPER_LAUNCHERS.has(executable)) {\n return UNSUPPORTED_WRAPPER_PAYLOAD;\n }\n\n if (!KNOWN_EXECUTABLE_WRAPPERS.has(executable)) {\n const flagIndex = findExecutablePayloadFlagIndex(tokens);\n return flagIndex === -1 || flagIndex + 1 >= tokens.length\n ? null\n : UNSUPPORTED_WRAPPER_PAYLOAD;\n }\n\n if (rawValue.includes('<<')) {\n return UNSUPPORTED_WRAPPER_PAYLOAD;\n }\n\n const flagIndex = findExecutablePayloadFlagIndex(tokens);\n if (flagIndex !== -1 && flagIndex + 1 < tokens.length) {\n return UNSUPPORTED_WRAPPER_PAYLOAD;\n }\n return resolveScriptToken(tokens) ? UNSUPPORTED_WRAPPER_PAYLOAD : null;\n}\n\nfunction resolveScriptToken(tokens) {\n for (let index = 1; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (!token) continue;\n if (token === '--') {\n return tokens[index + 1] || null;\n }\n if (token === '-') {\n return token;\n }\n if (token.startsWith('-')) {\n if (\n EXECUTABLE_WRAPPER_OPTIONS_WITH_VALUE.has(token.toLowerCase()) &&\n index + 1 < tokens.length\n ) {\n index += 1;\n }\n continue;\n }\n return token;\n }\n return null;\n}\n\nfunction resolvesToExistingFile(token, baseCwd) {\n if (!token || token.startsWith('-')) {\n return false;\n }\n\n const resolvedPath = path.resolve(baseCwd, token);\n try {\n return fs.statSync(resolvedPath).isFile();\n } catch {\n return false;\n }\n}\n\nfunction unwrapShellCommand(value) {\n let currentValue = value;\n\n for (let depth = 0; depth < 6; depth += 1) {\n const tokens = tokenizeShellCommand(currentValue);\n const executable = normalizeExecutableToken(tokens[0] || '');\n\n if (executable === 'sudo') {\n const stripped = stripSudoWrapper(tokens);\n currentValue = stripped.join(' ');\n continue;\n }\n\n if (executable === 'command' || executable === 'nohup') {\n if (tokens.length <= 1) return currentValue;\n currentValue = tokens.slice(1).join(' ');\n continue;\n }\n\n if (executable === 'env') {\n const stripped = stripEnvWrapper(tokens);\n currentValue = stripped.join(' ');\n continue;\n }\n\n if (!KNOWN_SHELL_EXECUTABLES.has(executable)) {\n return currentValue;\n }\n\n const flagIndex = findShellCommandFlagIndex(tokens);\n if (flagIndex === -1 || flagIndex + 1 >= tokens.length) {\n return currentValue;\n }\n\n currentValue = tokens.slice(flagIndex + 1).join(' ');\n }\n\n return currentValue;\n}\n\nfunction hasUnsupportedDangerousShellWrapper(value, baseCwd) {\n let currentValue = value;\n\n for (let depth = 0; depth < 6; depth += 1) {\n const tokens = tokenizeShellCommand(currentValue);\n const executable = normalizeExecutableToken(tokens[0] || '');\n\n if (executable === 'sudo') {\n currentValue = stripSudoWrapper(tokens).join(' ');\n continue;\n }\n\n if (executable === 'command' || executable === 'nohup') {\n if (tokens.length <= 1) return false;\n currentValue = tokens.slice(1).join(' ');\n continue;\n }\n\n if (executable === 'env') {\n currentValue = stripEnvWrapper(tokens).join(' ');\n continue;\n }\n\n if (DIRECT_GIT_OR_GH_EXECUTABLES.has(executable)) {\n return false;\n }\n\n const flagIndex = findExecutablePayloadFlagIndex(tokens);\n if (!KNOWN_SHELL_EXECUTABLES.has(executable)) {\n const payload = readWrapperScriptPayload(\n executable,\n tokens,\n currentValue,\n baseCwd\n );\n if (payload === UNSUPPORTED_WRAPPER_PAYLOAD) {\n return true;\n }\n if (!payload) {\n return false;\n }\n return containsDangerousGitOrGhPayload(payload) || containsProcessExecutionPayload(payload);\n }\n\n if (flagIndex === -1 || flagIndex + 1 >= tokens.length) {\n if (currentValue.includes('<<') || resolveScriptToken(tokens)) {\n return true;\n }\n return false;\n }\n\n const payload = tokens.slice(flagIndex + 1).join(' ');\n const payloadTokens = tokenizeShellCommand(payload);\n if (resolvesToExistingFile(payloadTokens[0] || '', baseCwd)) {\n return true;\n }\n currentValue = payload;\n }\n\n return false;\n}\n\nconst GIT_OPTIONS_WITH_VALUE = new Set([\n '-C',\n '-c',\n '--exec-path',\n '--git-dir',\n '--namespace',\n '--super-prefix',\n '--work-tree',\n '--config-env',\n]);\n\nfunction getGitSubcommand(value) {\n const unwrappedValue = unwrapShellCommand(value);\n const tokens = tokenizeShellCommand(unwrappedValue);\n const gitIndex = tokens.findIndex(\n (token) => normalizeExecutableToken(token) === 'git'\n );\n if (gitIndex === -1) return null;\n\n for (let index = gitIndex + 1; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (!token) continue;\n if (token === '--') {\n return tokens[index + 1] || null;\n }\n if (!token.startsWith('-')) {\n return token;\n }\n if (GIT_OPTIONS_WITH_VALUE.has(token) && index + 1 < tokens.length) {\n index += 1;\n }\n }\n\n return null;\n}\n\nfunction getGitCommandCwd(value, baseCwd) {\n const unwrappedValue = unwrapShellCommand(value);\n const tokens = tokenizeShellCommand(unwrappedValue);\n const gitIndex = tokens.findIndex(\n (token) => normalizeExecutableToken(token) === 'git'\n );\n if (gitIndex === -1) return baseCwd;\n\n let currentCwd = baseCwd;\n for (let index = gitIndex + 1; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (!token) continue;\n if (token === '--') break;\n if (!token.startsWith('-')) break;\n if (token === '-C' && index + 1 < tokens.length) {\n currentCwd = path.resolve(currentCwd, tokens[index + 1]);\n index += 1;\n continue;\n }\n if (GIT_OPTIONS_WITH_VALUE.has(token) && index + 1 < tokens.length) {\n index += 1;\n }\n }\n\n return currentCwd;\n}\n\nfunction getGitCommitMessage(value) {\n const unwrappedValue = unwrapShellCommand(value);\n const tokens = tokenizeShellCommand(unwrappedValue);\n const gitIndex = tokens.findIndex(\n (token) => normalizeExecutableToken(token) === 'git'\n );\n if (gitIndex === -1) return null;\n\n let sawCommit = false;\n for (let index = gitIndex + 1; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (!token) continue;\n if (token === '--') break;\n\n if (!sawCommit) {\n if (token === 'commit') {\n sawCommit = true;\n continue;\n }\n if (token.startsWith('-')) {\n if (GIT_OPTIONS_WITH_VALUE.has(token) && index + 1 < tokens.length) {\n index += 1;\n }\n continue;\n }\n break;\n }\n\n if (token === '-m' || token === '--message') {\n return index + 1 < tokens.length ? tokens[index + 1] : null;\n }\n }\n\n return null;\n}\n\nfunction normalizeCommandText(value) {\n return String(value || '').replace(/[ \\\\t\\\\r\\\\n]+/g, ' ').trim();\n}\n\nfunction extractLeeSpecKitFeatureRef(value) {\n const tokens = tokenizeShellCommand(value);\n const cliIndex = tokens.findIndex((token) => /(?:^|[\\\\\\\\/])lee-spec-kit(?:\\\\.cmd|\\\\.exe)?$/i.test(token));\n if (cliIndex === -1) return null;\n for (let index = cliIndex + 1; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (!token || token === 'npx' || token === '--yes' || token === '-y') continue;\n if (token === 'github' && tokens[index + 1] === 'issue') return tokens[index + 2] || null;\n if (token === 'github' && tokens[index + 1] === 'pr') return tokens[index + 2] || null;\n if (token === 'workflow-stage') {\n const candidate = tokens[index + 1] || null;\n return candidate && !candidate.startsWith('-') ? candidate : null;\n }\n }\n return null;\n}\n\nfunction extractBranchCreateTarget(value) {\n const tokens = tokenizeShellCommand(unwrapShellCommand(value));\n for (let index = 0; index < tokens.length; index += 1) {\n const token = tokens[index] || '';\n if (token === '-b' || token === '-c' || token === '--create') {\n const candidate = tokens[index + 1] || '';\n if (candidate && !candidate.startsWith('-')) return candidate;\n }\n if (token.startsWith('-b') && token.length > 2) return token.slice(2);\n if (token.startsWith('-c') && token.length > 2) return token.slice(2);\n }\n\n const worktreeAddIndex = tokens.findIndex((token, index) => {\n return token === 'add' && index > 0 && tokens.slice(0, index).some((item) => normalizeExecutableToken(item) === 'git' || item === 'worktree');\n });\n if (worktreeAddIndex !== -1) {\n const nonOptionArgs = [];\n for (let index = worktreeAddIndex + 1; index < tokens.length; index += 1) {\n const token = tokens[index];\n if (!token || token.startsWith('-')) continue;\n nonOptionArgs.push(token);\n }\n return nonOptionArgs.length >= 2 ? nonOptionArgs[1] : null;\n }\n\n const branchNameMatch = String(value).match(/\\\\bfeat\\\\/[A-Za-z0-9][A-Za-z0-9._/-]*/);\n return branchNameMatch?.[0] || null;\n}\n\nfunction escapeRegExp(value) {\n return String(value || '').replace(/[\\\\^$.*+?()[\\\\]{}|]/g, '\\\\\\\\$&');\n}\n\nfunction readFeatureRefFromTasksBranch(docsDir, branchName) {\n if (!docsDir || !branchName) return null;\n const featuresRoot = path.join(docsDir, 'features');\n const stack = [featuresRoot];\n while (stack.length > 0) {\n const current = stack.pop();\n let entries = [];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n const entryPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n stack.push(entryPath);\n continue;\n }\n if (entry.name !== 'tasks.md') continue;\n try {\n const content = fs.readFileSync(entryPath, 'utf8');\n if (new RegExp('^-\\\\\\\\s+\\\\\\\\*\\\\\\\\*(?:Branch|브랜치)\\\\\\\\*\\\\\\\\*:\\\\\\\\s+\\`?' + escapeRegExp(branchName) + '\\`?\\\\\\\\s*$', 'm').test(content)) {\n return path.basename(path.dirname(entryPath));\n }\n } catch {\n // Ignore unreadable feature docs and keep scanning.\n }\n }\n }\n return null;\n}\n\nfunction hasUnsupportedGitTargetOptions(value) {\n const unwrappedValue = unwrapShellCommand(value);\n const tokens = tokenizeShellCommand(unwrappedValue);\n return tokens.some((token) => {\n const normalized = String(token || '').toLowerCase();\n return (\n normalized === '--git-dir' ||\n normalized.startsWith('--git-dir=') ||\n normalized === '--work-tree' ||\n normalized.startsWith('--work-tree=')\n );\n });\n}\n\nfunction hasGitTargetEnvOverrides(value) {\n const tokens = tokenizeShellCommand(value);\n return tokens.some((token) => {\n const normalized = String(token || '').trim().toUpperCase();\n return (\n normalized.startsWith('GIT_DIR=') ||\n normalized.startsWith('GIT_WORK_TREE=')\n );\n });\n}\n\nconst normalizedCommand = unwrapShellCommand(command);\nconst hasUnsupportedShellWrappedDangerousCommand =\n hasUnsupportedDangerousShellWrapper(command, cwd);\nconst gitSubcommand = getGitSubcommand(command);\nconst gitCommandCwd = getGitCommandCwd(command, cwd);\nconst hasUnsupportedGitTarget = hasUnsupportedGitTargetOptions(command);\nconst hasGitTargetEnvOverride = hasGitTargetEnvOverrides(command);\nconst isGitCommit = gitSubcommand === 'commit';\nconst isGitPush = gitSubcommand === 'push';\nconst isGitCheckout = gitSubcommand === 'checkout';\nconst isGitSwitch = gitSubcommand === 'switch';\nconst isGitWorktree = gitSubcommand === 'worktree';\nconst isGitRestore = gitSubcommand === 'restore';\nconst isGitClean = gitSubcommand === 'clean';\nconst isGitRebase = gitSubcommand === 'rebase';\nconst isGitMerge = gitSubcommand === 'merge';\nconst isGitCherryPick = gitSubcommand === 'cherry-pick';\nconst isGitRevert = gitSubcommand === 'revert';\nconst isGitStash = gitSubcommand === 'stash';\nconst isGitBranchDelete =\n gitSubcommand === 'branch' &&\n /(^|\\\\s)(?:-D|-d|--delete)(\\\\s|$)/.test(normalizedCommand);\nconst isGitTagDelete =\n gitSubcommand === 'tag' &&\n /(^|\\\\s)-d(\\\\s|$)/.test(normalizedCommand);\nconst isGitResetHard =\n gitSubcommand === 'reset' && /(^|\\\\s)--hard(\\\\s|$)/.test(normalizedCommand);\nconst isAlwaysBlockedGhCommand =\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+repo\\\\s+delete\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+release\\\\s+delete\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+api\\\\b[\\\\s\\\\S]{0,160}(?:--method=DELETE|(?:-X|--method)\\\\s+DELETE)\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+api\\\\b[\\\\s\\\\S]{0,120}\\\\bgraphql\\\\b/i.test(command);\nconst isAlwaysBlockedGhOperation =\n isAlwaysBlockedGhCommand;\nconst isDangerousGhCommand =\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+issue\\\\s+(?:create|delete|edit|close|reopen)\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+pr\\\\s+(?:create|merge|close|reopen|review|ready)\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+repo\\\\s+(?:delete|archive|rename|edit)\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+release\\\\s+(?:create|delete|edit)\\\\b/i.test(command) ||\n /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+api\\\\b[\\\\s\\\\S]{0,160}(?:--method=(?:DELETE|PATCH|POST|PUT)|(?:-X|--method)\\\\s+(?:DELETE|PATCH|POST|PUT))\\\\b/i.test(command);\nconst isGitCreateBranch =\n (isGitCheckout && /(^|\\\\s)-b(\\\\s|$)/.test(normalizedCommand)) ||\n (isGitSwitch && /(^|\\\\s)(?:-c|--create)(\\\\s|$)/.test(normalizedCommand));\nconst isGitWorktreeAdd =\n isGitWorktree && /(^|\\\\s)add(\\\\s|$)/.test(normalizedCommand);\nconst isLeeSpecKitIssueCreate =\n /\\\\blee-spec-kit\\\\b[\\\\s\\\\S]{0,120}\\\\bgithub\\\\s+issue\\\\b[\\\\s\\\\S]{0,160}\\\\b--create\\\\b/i.test(command);\nconst isLeeSpecKitPrCreate =\n /\\\\blee-spec-kit\\\\b[\\\\s\\\\S]{0,120}\\\\bgithub\\\\s+pr\\\\b[\\\\s\\\\S]{0,160}\\\\b--create\\\\b/i.test(command);\nconst isLeeSpecKitPrMerge =\n /\\\\blee-spec-kit\\\\b[\\\\s\\\\S]{0,120}\\\\bgithub\\\\s+pr\\\\b[\\\\s\\\\S]{0,160}\\\\b--merge\\\\b/i.test(command);\nconst isPotentialOpenWikiCommand = /\\\\bopenwiki\\\\b/i.test(command);\nconst isGhIssueCreate =\n isDangerousGhCommand && /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+issue\\\\s+create\\\\b/i.test(command);\nconst isGhPrCreate =\n isDangerousGhCommand && /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+pr\\\\s+create\\\\b/i.test(command);\nconst isGhPrMerge =\n isDangerousGhCommand && /\\\\bgh(?:\\\\.cmd|\\\\.exe)?\\\\s+pr\\\\s+merge\\\\b/i.test(command);\nlet stageBoundAction = null;\nif (isGitCreateBranch) {\n stageBoundAction = 'branch_create';\n} else if (isGitWorktreeAdd) {\n stageBoundAction = 'branch_create';\n} else if (isGhIssueCreate || isLeeSpecKitIssueCreate) {\n stageBoundAction = 'issue_create';\n} else if (isGhPrCreate || isLeeSpecKitPrCreate) {\n stageBoundAction = 'pr_create';\n} else if (isGhPrMerge || isLeeSpecKitPrMerge) {\n stageBoundAction = 'pr_merge';\n}\nconst isDangerousCommandWithoutOpenWiki =\n isAlwaysBlockedGhOperation ||\n hasUnsupportedShellWrappedDangerousCommand ||\n isGitCommit ||\n isGitPush ||\n isGitCheckout ||\n isGitSwitch ||\n isGitWorktree ||\n isGitRestore ||\n isGitClean ||\n isGitRebase ||\n isGitMerge ||\n isGitCherryPick ||\n isGitRevert ||\n isGitStash ||\n isGitBranchDelete ||\n isGitTagDelete ||\n isGitResetHard ||\n isDangerousGhCommand ||\n isLeeSpecKitIssueCreate ||\n isLeeSpecKitPrCreate ||\n isLeeSpecKitPrMerge;\nconst isDangerousCommand =\n isDangerousCommandWithoutOpenWiki || isPotentialOpenWikiCommand;\n\nif (!command || !isDangerousCommand) {\n process.exit(0);\n}\n\nif (isAlwaysBlockedGhOperation) {\n printBlock('Destructive GitHub CLI commands such as repo or release deletion are not supported by lee-spec-kit hooks. Re-run them manually after explicit review.');\n process.exit(0);\n}\n\nif (hasUnsupportedGitTarget || hasGitTargetEnvOverride) {\n printBlock('Git commands using --git-dir, --work-tree, GIT_DIR, or GIT_WORK_TREE are not supported by lee-spec-kit hooks. Re-run the command from the target repo root instead.');\n process.exit(0);\n}\n\nconst detectedResult = runLeeSpecKitJson(['detect', '--json'], workflowCwd);\nif (!detectedResult.ok) {\n printBlock('lee-spec-kit detection failed inside the Codex hook. Fix the local CLI or hook setup before continuing.');\n process.exit(0);\n}\nconst detected = detectedResult.data;\nif (!(detected?.status === 'ok' && detected?.isLeeSpecKitProject === true)) {\n process.exit(0);\n}\n\nfunction classifyOpenWikiInvocation(value, depth = 0) {\n if (depth > 4) return 'blocked';\n const source = String(value || '');\n if (!/\\\\bopenwiki\\\\b/iu.test(source)) return 'none';\n if (\n /\\\\$\\\\(|\\\\x60|\\\\beval\\\\b|\\\\bxargs\\\\b|\\\\bfind\\\\b[\\\\s\\\\S]{0,80}-exec\\\\b|\\\\bpowershell\\\\b|\\\\bcmd(?:\\\\.exe)?\\\\s+\\\\/c\\\\b/iu.test(source) ||\n /(?:^|\\\\s)[A-Za-z_][A-Za-z0-9_]*\\\\s*=\\\\s*openwiki\\\\b/iu.test(source)\n ) {\n return 'blocked';\n }\n const isOpenWikiPackage = (token) => {\n const normalized = String(token || '')\n .replace(/^['\"]|['\"]$/gu, '')\n .split('/')\n .pop();\n return /^openwiki(?:@[^\\\\s]+)?$/iu.test(normalized || '');\n };\n const isSafeArgs = (args) => {\n const normalized = args\n .map((token) => String(token || '').replace(/^['\"]|['\"]$/gu, ''))\n .filter(Boolean);\n if (normalized.length === 1 && /^(?:--help|-h|--version)$/u.test(normalized[0])) {\n return true;\n }\n if (normalized[0] === 'help') return true;\n return normalized[0] === 'auth';\n };\n const segments = source.split(/&&|\\\\|\\\\||[;|]/u);\n let found = false;\n for (const segment of segments) {\n const tokens = segment.trim().split(/\\\\s+/u).filter(Boolean);\n let index = 0;\n while (index < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/u.test(tokens[index])) {\n index += 1;\n }\n while (tokens[index] === 'env' || tokens[index] === 'command') {\n index += 1;\n while (index < tokens.length && (/^[A-Za-z_][A-Za-z0-9_]*=/u.test(tokens[index]) || tokens[index].startsWith('-'))) {\n index += 1;\n }\n }\n const readExecutable = () => (tokens[index] || '')\n .replace(/^['\"]|['\"]$/gu, '')\n .split('/')\n .pop()\n .replace(/\\\\.(?:cmd|exe)$/iu, '');\n let executable = readExecutable();\n while (executable === 'sudo') {\n index += 1;\n while (index < tokens.length && tokens[index].startsWith('-')) {\n const option = tokens[index];\n index += 1;\n if (/^(?:-u|-g|-h|-p|-C|--user|--group|--host|--prompt|--chdir)$/u.test(option)) {\n index += 1;\n }\n }\n executable = readExecutable();\n }\n if (executable === 'corepack') {\n index += 1;\n while (index < tokens.length && tokens[index].startsWith('-')) index += 1;\n executable = readExecutable();\n }\n if (executable === 'openwiki') {\n found = true;\n if (!isSafeArgs(tokens.slice(index + 1))) return 'blocked';\n continue;\n }\n if (executable === 'npx' || executable === 'bunx') {\n index += 1;\n while (index < tokens.length && tokens[index].startsWith('-')) {\n const option = tokens[index];\n index += 1;\n if (/^(?:--package|-p)$/u.test(option)) {\n if (!isOpenWikiPackage(tokens[index])) return 'blocked';\n index += 1;\n }\n }\n if (isOpenWikiPackage(tokens[index])) {\n found = true;\n if (!isSafeArgs(tokens.slice(index + 1))) return 'blocked';\n continue;\n }\n }\n if (executable === 'npm' || executable === 'pnpm' || executable === 'yarn') {\n index += 1;\n while (index < tokens.length && tokens[index].startsWith('-')) index += 1;\n const runner = (tokens[index] || '').replace(/^['\"]|['\"]$/gu, '');\n if (runner === 'exec' || runner === 'x' || runner === 'dlx') {\n index += 1;\n while (index < tokens.length && tokens[index].startsWith('-')) index += 1;\n if (isOpenWikiPackage(tokens[index])) {\n found = true;\n if (!isSafeArgs(tokens.slice(index + 1))) return 'blocked';\n continue;\n }\n }\n if (runner === 'run') {\n index += 1;\n while (index < tokens.length && tokens[index] === '--') index += 1;\n if (isOpenWikiPackage(tokens[index])) return 'blocked';\n }\n }\n if (executable === 'bash' || executable === 'sh' || executable === 'zsh') {\n const commandIndex = tokens.findIndex(\n (token, tokenIndex) => tokenIndex > index && token === '-c'\n );\n if (\n commandIndex >= 0 &&\n classifyOpenWikiInvocation(tokens.slice(commandIndex + 1).join(' '), depth + 1) !== 'none'\n ) {\n return 'blocked';\n }\n }\n if (/^(?:node|deno|tsx|ts-node)$/u.test(executable) && /openwiki/iu.test(segment)) {\n return 'blocked';\n }\n }\n if (\n /\\\\b(?:bash|sh|zsh)(?:\\\\.exe)?\\\\b[\\\\s\\\\S]{0,80}\\\\s-c\\\\s+[\"'][^\"']*\\\\bopenwiki(?:\\\\.cmd|\\\\.exe)?\\\\b/iu.test(String(value || ''))\n ) {\n return 'blocked';\n }\n return found ? 'safe' : 'none';\n}\n\nif (detected?.experimentalOpenwiki === true) {\n const openWikiPolicy = classifyOpenWikiInvocation(command);\n if (openWikiPolicy === 'blocked') {\n printBlock('OpenWiki repository generation must run through the exact lee-spec-kit knowledge sync nextAction. Only simple --help and auth commands are allowed directly.');\n process.exit(0);\n }\n if (openWikiPolicy === 'safe' && !isDangerousCommandWithoutOpenWiki) {\n process.exit(0);\n }\n}\n\nif (isPotentialOpenWikiCommand && !isDangerousCommandWithoutOpenWiki) {\n process.exit(0);\n}\n\nconst docsDir = typeof detected?.docsDir === 'string' ? detected.docsDir : '';\nconst gitTargetIsDocsRepo =\n !!docsDir &&\n normalizeResolvedPath(gitCommandCwd) === normalizeResolvedPath(docsDir);\n\nif (\n gitTargetIsDocsRepo &&\n (isGitCheckout ||\n isGitSwitch ||\n isGitCreateBranch ||\n isGitWorktreeAdd ||\n gitSubcommand === 'branch')\n) {\n printBlock('Standalone docs repos stay on their docs branch and must not be switched into feature branches or worktrees.');\n process.exit(0);\n}\n\nlet stage = null;\nconst isPotentialMergeCleanupCommand =\n !stageBoundAction &&\n !isGitCommit &&\n path.resolve(gitCommandCwd) !== path.resolve(cwd) &&\n (\n command.includes('worktree remove') ||\n command.includes('branch -D') ||\n command.includes('push origin --delete')\n );\nconst commandFeatureRef =\n extractLeeSpecKitFeatureRef(command) ||\n readFeatureRefFromTasksBranch(docsDir, extractBranchCreateTarget(command));\nif (hasUnsupportedShellWrappedDangerousCommand && !commandFeatureRef) {\n printBlock('lee-spec-kit hooks do not support this shell wrapper for git or gh commands. Re-run the command from a supported shell or the target repo root instead.');\n process.exit(0);\n}\nif (isGitCommit) {\n const commitStageArgs = commandFeatureRef\n ? ['workflow-stage', commandFeatureRef, '--json']\n : ['workflow-stage', '--json'];\n const commitStageResult = runLeeSpecKitJson(commitStageArgs, workflowCwd);\n const commitStage = commitStageResult.ok ? commitStageResult.data : null;\n if (\n commitStage?.status === 'ok' &&\n commitStage?.nextAction?.category === 'task_execute'\n ) {\n printBlock('Commits are not allowed while task_execute is active. The task implementation worker must return code and verification results without committing; the main agent must synchronize docs and task state before the task_commit checkpoint.');\n process.exit(0);\n }\n}\nif (stageBoundAction || isPotentialMergeCleanupCommand || hasUnsupportedShellWrappedDangerousCommand) {\n const stageArgs = commandFeatureRef\n ? ['workflow-stage', commandFeatureRef, '--json']\n : ['workflow-stage', '--json'];\n const stageResult = runLeeSpecKitJson(stageArgs, workflowCwd);\n if (!stageResult.ok) {\n printBlock('lee-spec-kit workflow-stage failed inside the Codex hook. Resolve the workflow stage before running this stage-bound command.');\n process.exit(0);\n }\n stage = stageResult.data;\n if (stage?.status !== 'ok') {\n printBlock('Resolve feature selection and workflow stage before running this stage-bound command.');\n process.exit(0);\n }\n const isExactNextActionCommand =\n normalizeCommandText(stage?.nextAction?.command) === normalizeCommandText(command);\n if (hasUnsupportedShellWrappedDangerousCommand && !isExactNextActionCommand) {\n printBlock('lee-spec-kit hooks do not support this shell wrapper for git or gh commands. Re-run the command from a supported shell or the target repo root instead.');\n process.exit(0);\n }\n if (\n stageBoundAction &&\n stage?.nextAction?.category !== stageBoundAction &&\n !isExactNextActionCommand\n ) {\n printBlock(\n \\`Current workflow stage is \\${stage?.stage || 'unknown'} and only \\${stage?.nextAction?.category || 'the current nextAction'} is allowed next. Do not jump ahead to \\${stageBoundAction}.\\`\n );\n process.exit(0);\n }\n}\n\nconst isExactMergeCleanupCommand =\n stage?.nextAction?.category === 'merge_cleanup' &&\n normalizeCommandText(stage?.nextAction?.command) === normalizeCommandText(command);\nconst isExactNextActionCommand =\n normalizeCommandText(stage?.nextAction?.command) === normalizeCommandText(command);\n\nif (\n path.resolve(gitCommandCwd) !== path.resolve(cwd) &&\n !isGitCommit &&\n !isExactNextActionCommand &&\n !isExactMergeCleanupCommand &&\n !(stageBoundAction === 'branch_create' && (isGitCreateBranch || isGitWorktreeAdd))\n) {\n printBlock('Git commands targeting another repo via -C are only supported for git commit. Re-run the command from the target repo root instead.');\n process.exit(0);\n}\n\nif (isGitCommit) {\n const commitAuditArgs = ['commit-audit', '--json', '--git-root', gitCommandCwd];\n const commitMessage = getGitCommitMessage(command);\n if (commitMessage) {\n commitAuditArgs.push('--message', commitMessage);\n }\n const commitAuditResult = runLeeSpecKitJson(commitAuditArgs, workflowCwd);\n if (!commitAuditResult.ok) {\n printBlock('lee-spec-kit commit-audit failed inside the Codex hook. Resolve the docs guardrail failure before committing.');\n process.exit(0);\n }\n const commitAudit = commitAuditResult.data;\n if (commitAudit?.status === 'blocked') {\n if (commitAudit?.reasonCode === 'UNSUPPORTED_GIT_TARGET') {\n printBlock('Git commit targets outside the current lee-spec-kit project topology are not supported. Re-run the command from the active workspace or target repo root instead.');\n process.exit(0);\n }\n printBlock('Normalize or allowlist non-canonical docs paths before committing.');\n process.exit(0);\n }\n if (!(commitAudit?.status === 'ok' || commitAudit?.status === 'skipped')) {\n printBlock('lee-spec-kit commit-audit returned a non-ok status inside the Codex hook. Resolve the docs guardrail failure before committing.');\n process.exit(0);\n }\n}\n\nconst auditResult = runLeeSpecKitJson(['workflow-audit', '--json'], workflowCwd);\nif (!auditResult.ok) {\n printBlock('lee-spec-kit workflow-audit failed inside the Codex hook. Resolve the docs sync guardrail failure before continuing.');\n process.exit(0);\n}\nconst audit = auditResult.data;\nif (audit?.status === 'needs_sync') {\n printBlock('Sync the active feature docs before running remote or destructive commands.');\n process.exit(0);\n}\nif (!(audit?.status === 'ok' || audit?.status === 'skipped')) {\n printBlock('lee-spec-kit workflow-audit returned a non-ok status inside the Codex hook. Resolve the docs sync guardrail failure before continuing.');\n}\n`;\n case 'stop_workflow_audit.mjs':\n return `#!/usr/bin/env node\nimport { getWorkflowCwd, printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';\n\n// Equivalent CLI probe: npx lee-spec-kit workflow-audit --json\nconst inputResult = readHookInput();\nif (!inputResult.ok) {\n printBlock('Codex stop hook input was malformed. Resolve the local hook setup before stopping.');\n process.exit(0);\n}\nconst input = inputResult.value;\nif (input?.stop_hook_active === true) {\n process.stdout.write(JSON.stringify({ continue: true }));\n process.exit(0);\n}\n\nconst cwd = getWorkflowCwd();\nconst detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);\nif (!detectedResult.ok) {\n printBlock('lee-spec-kit detection failed inside the stop hook. Resolve the local CLI or hook setup before stopping.');\n process.exit(0);\n}\nconst detected = detectedResult.data;\nif (!(detected?.status === 'ok' && detected?.isLeeSpecKitProject === true)) {\n process.stdout.write(JSON.stringify({ continue: true }));\n process.exit(0);\n}\n\nconst auditResult = runLeeSpecKitJson(['workflow-audit', '--json'], cwd);\nif (!auditResult.ok) {\n printBlock('lee-spec-kit workflow-audit failed inside the stop hook. Resolve the docs sync guardrail failure before stopping.');\n process.exit(0);\n}\nconst audit = auditResult.data;\nif (audit?.status === 'needs_sync') {\n printBlock('Run one more pass and sync the active feature docs before stopping.');\n process.exit(0);\n}\nif (!(audit?.status === 'ok' || audit?.status === 'skipped')) {\n printBlock('lee-spec-kit workflow-audit returned a non-ok status inside the stop hook. Resolve the docs sync guardrail failure before stopping.');\n process.exit(0);\n}\n\nif (detected?.experimentalOpenwiki === true) {\n const stageResult = runLeeSpecKitJson(['workflow-stage', '--json'], cwd);\n const category = stageResult.ok ? stageResult.data?.nextAction?.category : null;\n if (category === 'knowledge_setup' || category === 'knowledge_sync' || category === 'knowledge_commit') {\n printBlock('Complete the required OpenWiki Knowledge nextAction before stopping. Partial or stale generated Knowledge cannot pass the workflow.');\n process.exit(0);\n }\n}\n\nprocess.stdout.write(JSON.stringify({ continue: true }));\n`;\n default:\n return '';\n }\n}\n\nfunction getManagedHookRelativePath(fileName: ManagedHookFileName): string {\n return path.posix.join('.codex', 'hooks', fileName);\n}\n\nfunction normalizePathSlashes(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction getPortableHookCommandSuffix(fileName: ManagedHookFileName): string {\n const relativeHookPath = normalizePathSlashes(\n getManagedHookRelativePath(fileName)\n );\n const loaderSource = [\n '(async () => {',\n \" const fs = require('node:fs');\",\n \" const path = require('node:path');\",\n \" const { pathToFileURL } = require('node:url');\",\n ` const relativeHookPath = ${JSON.stringify(relativeHookPath)};`,\n ' let dir = process.cwd();',\n ' while (true) {',\n ' const candidate = path.join(dir, relativeHookPath);',\n ' if (fs.existsSync(candidate)) {',\n ' await import(pathToFileURL(candidate).href);',\n ' return;',\n ' }',\n ' const parent = path.dirname(dir);',\n ' if (parent === dir) {',\n \" throw new Error('lee-spec-kit hook script not found: ' + relativeHookPath);\",\n ' }',\n ' dir = parent;',\n ' }',\n '})().catch((error) => {',\n ' console.error(error && error.stack ? error.stack : String(error));',\n ' process.exit(1);',\n '});',\n ].join(' ');\n return ` -e ${JSON.stringify(loaderSource)}`;\n}\n\nfunction isManagedCommand(command: string): boolean {\n const normalized = normalizePathSlashes(command).trim();\n return MANAGED_HOOK_FILENAMES.some((fileName) => {\n const currentCommand = normalizePathSlashes(\n toPortableHookCommand(fileName)\n ).trim();\n const portableSuffix = normalizePathSlashes(\n getPortableHookCommandSuffix(fileName)\n ).trim();\n if (normalized === currentCommand || normalized.endsWith(portableSuffix)) {\n return true;\n }\n\n const relativeHookPath = escapeRegExp(getManagedHookRelativePath(fileName));\n const legacyAbsolutePattern = new RegExp(\n `^node\\\\s+[\"']?.*${relativeHookPath}[\"']?$`\n );\n return legacyAbsolutePattern.test(normalized);\n });\n}\n\nfunction toPortableHookCommand(fileName: ManagedHookFileName): string {\n const nodeCommand = JSON.stringify(process.execPath);\n return `${nodeCommand}${getPortableHookCommandSuffix(fileName)}`;\n}\n\nfunction getManagedHooksConfig(): Record<HookEventName, HookMatcherGroup[]> {\n const commandFor = (fileName: ManagedHookFileName) =>\n toPortableHookCommand(fileName);\n\n return {\n SessionStart: [\n {\n matcher: 'startup|resume|clear|compact',\n hooks: [\n {\n type: 'command',\n command: commandFor('session_start_lee_spec_kit.mjs'),\n statusMessage: 'Loading lee-spec-kit workflow context',\n },\n ],\n },\n ],\n SubagentStart: [\n {\n hooks: [\n {\n type: 'command',\n command: commandFor('subagent_start_lee_spec_kit.mjs'),\n statusMessage: 'Loading scoped lee-spec-kit subagent context',\n },\n ],\n },\n ],\n UserPromptSubmit: [\n {\n hooks: [\n {\n type: 'command',\n command: commandFor('user_prompt_submit_lee_spec_kit.mjs'),\n },\n ],\n },\n ],\n PreToolUse: [\n {\n matcher: 'Bash',\n hooks: [\n {\n type: 'command',\n command: commandFor('pre_tool_use_policy.mjs'),\n statusMessage: 'Checking lee-spec-kit workflow guardrails',\n },\n ],\n },\n ],\n Stop: [\n {\n hooks: [\n {\n type: 'command',\n command: commandFor('stop_workflow_audit.mjs'),\n timeout: 30,\n },\n ],\n },\n ],\n };\n}\n\nfunction getInstalledCliEntrypoint(): string {\n return path.join(path.dirname(fileURLToPath(import.meta.url)), 'index.js');\n}\n\nfunction pruneManagedGroups(\n groups: HookMatcherGroup[] | undefined\n): HookMatcherGroup[] {\n if (!Array.isArray(groups)) return [];\n return groups\n .map((group) => {\n if (!group || typeof group !== 'object' || !Array.isArray(group.hooks)) {\n return group;\n }\n\n const hooks = group.hooks.filter(\n (hook) =>\n !(\n hook &&\n typeof hook === 'object' &&\n typeof hook.command === 'string' &&\n isManagedCommand(hook.command)\n )\n );\n\n if (hooks.length === 0) {\n return null;\n }\n\n return {\n ...group,\n hooks,\n };\n })\n .filter((group): group is HookMatcherGroup => !!group);\n}\n\nfunction mergeManagedGroups(\n current: HooksConfigFile,\n managedHooks: Record<HookEventName, HookMatcherGroup[]>\n): HooksConfigFile {\n const nextHooks: Record<string, unknown> = {\n ...(current.hooks && typeof current.hooks === 'object'\n ? current.hooks\n : {}),\n };\n\n for (const eventName of Object.keys(managedHooks) as HookEventName[]) {\n const existing = pruneManagedGroups(\n Array.isArray(nextHooks[eventName])\n ? (nextHooks[eventName] as HookMatcherGroup[])\n : undefined\n );\n nextHooks[eventName] = [...existing, ...managedHooks[eventName]];\n }\n\n return {\n ...current,\n hooks: nextHooks,\n };\n}\n\nfunction removeManagedGroups(current: HooksConfigFile): HooksConfigFile {\n const nextHooks: Record<string, unknown> = {\n ...(current.hooks && typeof current.hooks === 'object'\n ? current.hooks\n : {}),\n };\n\n for (const eventName of [\n 'SessionStart',\n 'SubagentStart',\n 'UserPromptSubmit',\n 'PreToolUse',\n 'Stop',\n ]) {\n const pruned = pruneManagedGroups(\n Array.isArray(nextHooks[eventName])\n ? (nextHooks[eventName] as HookMatcherGroup[])\n : undefined\n );\n if (pruned.length > 0) {\n nextHooks[eventName] = pruned;\n } else {\n delete nextHooks[eventName];\n }\n }\n\n return {\n ...current,\n hooks: nextHooks,\n };\n}\n\nexport function getRepoCodexDir(repoRoot = process.cwd()): string {\n return path.join(repoRoot, '.codex');\n}\n\nexport function resolveCodexHooksRepoRoot(cwd = process.cwd()): string {\n return runGitCapture(['rev-parse', '--show-toplevel'], cwd) || cwd;\n}\n\nexport function getRepoHooksDir(repoRoot = process.cwd()): string {\n return path.join(getRepoCodexDir(repoRoot), 'hooks');\n}\n\nexport function getRepoHooksConfigPath(repoRoot = process.cwd()): string {\n return path.join(getRepoCodexDir(repoRoot), 'hooks.json');\n}\n\nexport async function upsertLeeSpecKitCodexHooks(\n repoRoot = process.cwd(),\n workflowRoot = repoRoot\n): Promise<{\n changed: boolean;\n action: 'created' | 'updated' | 'noop';\n hooksJsonPath: string;\n}> {\n const hooksDir = getRepoHooksDir(repoRoot);\n const hooksJsonPath = getRepoHooksConfigPath(repoRoot);\n await fs.ensureDir(hooksDir);\n\n for (const fileName of MANAGED_HOOK_FILENAMES) {\n const targetPath = path.join(hooksDir, fileName);\n await fs.writeFile(\n targetPath,\n getHookScriptContent(fileName, repoRoot, workflowRoot),\n {\n encoding: 'utf-8',\n mode: 0o755,\n }\n );\n }\n\n const managedHooks = getManagedHooksConfig();\n const exists = await fs.pathExists(hooksJsonPath);\n const current = exists\n ? ((await fs.readJson(hooksJsonPath)) as HooksConfigFile)\n : ({ hooks: {} } as HooksConfigFile);\n const next = mergeManagedGroups(current, managedHooks);\n const nextJson = `${JSON.stringify(next, null, 2)}\\n`;\n const currentJson = exists ? `${JSON.stringify(current, null, 2)}\\n` : null;\n\n if (currentJson === nextJson) {\n return { changed: false, action: 'noop', hooksJsonPath };\n }\n\n await fs.writeFile(hooksJsonPath, nextJson, 'utf-8');\n return {\n changed: true,\n action: exists ? 'updated' : 'created',\n hooksJsonPath,\n };\n}\n\nexport async function removeLeeSpecKitCodexHooks(\n repoRoot = process.cwd()\n): Promise<{ changed: boolean; hooksJsonPath: string }> {\n const hooksDir = getRepoHooksDir(repoRoot);\n const hooksJsonPath = getRepoHooksConfigPath(repoRoot);\n let changed = false;\n\n if (await fs.pathExists(hooksJsonPath)) {\n const current = (await fs.readJson(hooksJsonPath)) as HooksConfigFile;\n const next = removeManagedGroups(current);\n const currentJson = `${JSON.stringify(current, null, 2)}\\n`;\n const nextJson = `${JSON.stringify(next, null, 2)}\\n`;\n if (currentJson !== nextJson) {\n await fs.writeFile(hooksJsonPath, nextJson, 'utf-8');\n changed = true;\n }\n }\n\n for (const fileName of MANAGED_HOOK_FILENAMES) {\n const targetPath = path.join(hooksDir, fileName);\n if (await fs.pathExists(targetPath)) {\n await fs.remove(targetPath);\n changed = true;\n }\n }\n\n return { changed, hooksJsonPath };\n}\n"]}
|