lee-spec-kit 0.7.11 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.en.md +32 -54
  2. package/README.md +32 -54
  3. package/dist/bootstrap-G37N6RGB.js +5 -0
  4. package/dist/bootstrap-G37N6RGB.js.map +1 -0
  5. package/dist/chunk-7V7RMGEU.js +11 -0
  6. package/dist/chunk-7V7RMGEU.js.map +1 -0
  7. package/dist/chunk-GR7JQBWF.js +26 -0
  8. package/dist/chunk-GR7JQBWF.js.map +1 -0
  9. package/dist/chunk-LYFRLOFQ.js +275 -0
  10. package/dist/chunk-LYFRLOFQ.js.map +1 -0
  11. package/dist/hooks-4S33YUIB.js +1082 -0
  12. package/dist/hooks-4S33YUIB.js.map +1 -0
  13. package/dist/index.js +4689 -15581
  14. package/dist/index.js.map +1 -1
  15. package/package.json +2 -2
  16. package/templates/en/common/README.md +12 -10
  17. package/templates/en/common/agents/agents.md +35 -89
  18. package/templates/en/common/agents/skills/create-feature.md +32 -57
  19. package/templates/en/common/agents/skills/create-issue.md +9 -10
  20. package/templates/en/common/agents/skills/create-pr.md +6 -11
  21. package/templates/en/common/agents/skills/execute-task.md +35 -96
  22. package/templates/en/common/features/README.md +1 -1
  23. package/templates/en/common/features/feature-base/tasks.md +5 -5
  24. package/templates/ko/common/README.md +12 -10
  25. package/templates/ko/common/agents/agents.md +34 -87
  26. package/templates/ko/common/agents/skills/create-feature.md +32 -58
  27. package/templates/ko/common/agents/skills/create-issue.md +9 -10
  28. package/templates/ko/common/agents/skills/create-pr.md +6 -11
  29. package/templates/ko/common/agents/skills/execute-task.md +35 -102
  30. package/templates/ko/common/features/README.md +1 -1
  31. package/templates/ko/common/features/feature-base/tasks.md +5 -5
package/README.en.md CHANGED
@@ -7,7 +7,7 @@
7
7
  </div>
8
8
 
9
9
  <p align="center">
10
- <strong>Orchestration harness CLI for AI agent-driven development</strong>
10
+ <strong>Document-centered harness engineering toolkit for AI agent development</strong>
11
11
  </p>
12
12
 
13
13
  <p align="center">
@@ -18,8 +18,8 @@
18
18
 
19
19
  <p align="center">
20
20
  <a href="#quick-start">Quick Start</a> •
21
- <a href="#what-this-cli-does">Why</a> •
22
- <a href="#commands-the-agent-usually-runs">Commands</a> •
21
+ <a href="#why-it-exists">Why</a> •
22
+ <a href="#main-commands">Commands</a> •
23
23
  <a href="#docs">Docs</a>
24
24
  </p>
25
25
 
@@ -36,82 +36,60 @@
36
36
 
37
37
  ## Quick Start
38
38
 
39
- In most cases, the human asks in natural language and the main agent runs this flow.
39
+ `lee-spec-kit` creates PRD, idea, and feature docs, then helps agents work from those documents.
40
40
 
41
41
  ```bash
42
42
  npx lee-spec-kit init
43
+ npx lee-spec-kit integrations codex-hooks
43
44
  npx lee-spec-kit idea improve-auth-flow
44
45
  npx lee-spec-kit feature user-auth
45
- npx lee-spec-kit context
46
- npx lee-spec-kit flow
47
46
  ```
48
47
 
48
+ After that, the human can keep using normal natural-language requests.
49
+
49
50
  ## Why It Exists
50
51
 
51
52
  This CLI was built to keep documents and actual execution flow together when working with an AI agent.
52
53
 
53
- It is not just a tool that creates a docs folder. The main agent can use it to understand which feature is active, what the next action is, and where user approval is required.
54
+ It is not just a tool that creates a docs folder. It is closer to a harness that helps the agent handle the active feature, the next action, and the points where user approval is required under the same set of rules.
54
55
 
55
- The project structure follows `PRD → idea → feature`. PRD is where top-level requirements are written under `docs/prd/`, idea is for candidate approaches or experiments, and feature is the stage where actual work is managed through `spec.md`, `plan.md`, and `tasks.md`.
56
+ The project structure follows an SDD (spec-driven development) flow: `PRD → idea → feature`. PRD is where top-level requirements are written under `docs/prd/`, idea is for candidate approaches or experiments, and feature is the stage where actual work is managed through `spec.md`, `plan.md`, `tasks.md`, and `decisions.md`.
56
57
 
57
- The overall approach is influenced by [spec-kit](https://github.com/github/spec-kit) and [OpenSpec](https://github.com/Fission-AI/OpenSpec). The difference is that this project is less about inventing a new standard and more about keeping my own document flow and execution flow in one CLI.
58
+ The overall approach is influenced by [spec-kit](https://github.com/github/spec-kit) and [OpenSpec](https://github.com/Fission-AI/OpenSpec).
58
59
 
59
- ## What This CLI Does
60
+ ## Humans Usually Ask Like This
60
61
 
61
- `lee-spec-kit` is less a power-user console and more an opinionated harness that helps the main agent read project state and choose the next action.
62
+ - "Organize ideas from these requirements."
63
+ - "Promote this idea into a feature and move it forward."
64
+ - "Draft the issue from the current feature docs."
65
+ - "Continue the next feature according to the rules."
66
+ - "Check the docs and code together before we finish."
62
67
 
63
- - Humans usually ask in natural language.
64
- - The human-facing surface stays small: `init`, `idea`, `feature`, `context`, `flow`.
65
- - In practice, the main agent runs `detect`, `context`, and `flow` first.
66
- - Deeper operational commands still exist, but they are no longer front-loaded in the default help output.
68
+ ## Main Commands
67
69
 
68
- ## How It Works
70
+ - `init`
71
+ - `idea`
72
+ - `feature`
73
+ - `docs`
74
+ - `detect`
75
+ - `github`
76
+ - `integrations codex-hooks`
77
+ - `integrations codex`
78
+ - `commit-audit --json`
79
+ - `workflow-audit --json`
69
80
 
70
- 1. Use `init` to attach docs/workflow scaffolding.
71
- 2. Define top-level requirements in `docs/prd/`.
72
- 3. Create work with `idea` or `feature`.
73
- 4. Let the main agent read `detect` and `context`.
74
- 5. Humans step in for approvals, exceptions, and direction changes.
75
- 6. Use `context` for the current action and `flow` as the default workflow runner.
81
+ Supported modes:
76
82
 
77
- ## Humans Usually Ask Like This
78
-
79
- - "Read this doc and help me start the project structure."
80
- - "Organize ideas from these requirements."
81
- - "Promote this idea into a feature and move it forward."
82
- - "What is the next action right now?"
83
- - "Check the overall project state."
84
-
85
- ## Commands
86
-
87
- - The core agent-facing commands are the three commands below.
88
- - `detect`: detect whether the workspace uses lee-spec-kit
89
- - `context`: read the current feature state and next actions
90
- - `flow`: run the default workflow auto-loop and pause at selection/approval/manual/resume boundaries
91
- - The public human-facing commands are the five commands below.
92
- - `init`: initialize docs/workflow scaffolding
93
- - `idea`: create a pre-feature idea document
94
- - `feature`: create a concrete execution unit
95
- - `context`: show the current state and next action
96
- - `flow`: run the default workflow auto-loop and pause at selection/approval/manual/resume boundaries
97
-
98
- ## Agent Kickoff Prompt
99
-
100
- ```text
101
- Start procedure:
102
- 1) Run npx lee-spec-kit detect --json
103
- 2) If isLeeSpecKitProject === true, run npx lee-spec-kit context --json-compact
104
- 3) Use context as the read-only state probe, and use flow as the default execution/resume entrypoint
105
- 4) If approvalRequest.required=true, briefly restate the current stage from matchedFeature.currentSubstate* when available, then show approvalRequest.userFacingLines exactly as provided and wait for user approval
106
- 5) Do not execute before approval; for command execution, default to npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute
107
- 6) If isLeeSpecKitProject === false, skip lee-spec-kit-specific flow and continue with normal workflow
108
- ```
83
+ - `embedded`: keep `docs/` inside the project repository.
84
+ - `standalone`: keep the docs repo and project repo separate under a shared workspace root.
109
85
 
110
86
  ## Docs
111
87
 
112
88
  - [Public CLI Reference](./docs/reference/public-cli.md)
113
89
  - [Agent CLI Reference](./docs/reference/agent-cli.md)
114
90
  - [Internal CLI Reference](./docs/reference/internal-cli.md)
91
+ - [Codex Hooks Integration](./docs/reference/codex-hooks.md)
92
+ - [Migration Guide](./docs/reference/migration-codex-hooks.md)
115
93
  - [Reference Index](./docs/reference/README.md)
116
94
 
117
95
  ## License
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  </div>
8
8
 
9
9
  <p align="center">
10
- <strong>AI 에이전트 기반 개발을 위한 오케스트레이션 하네스 CLI</strong>
10
+ <strong>AI 에이전트 개발을 위한 문서 중심 하네스 엔지니어링 툴킷</strong>
11
11
  </p>
12
12
 
13
13
  <p align="center">
@@ -18,8 +18,8 @@
18
18
 
19
19
  <p align="center">
20
20
  <a href="#quick-start">Quick Start</a> •
21
- <a href="#이-cli가-하는-일">Why</a> •
22
- <a href="#에이전트가-주로-실행하는-명령">Commands</a> •
21
+ <a href="#왜-만들었나">Why</a> •
22
+ <a href="#주요-명령">Commands</a> •
23
23
  <a href="#docs">Docs</a>
24
24
  </p>
25
25
 
@@ -36,82 +36,60 @@
36
36
 
37
37
  ## Quick Start
38
38
 
39
- 대부분의 경우 사용자는 자연어로 요청하고, 메인 에이전트가 아래 흐름을 실행합니다.
39
+ `lee-spec-kit`은 PRD, idea, feature 문서를 만들고, 에이전트가 문서를 기준으로 작업하도록 돕는 도구입니다.
40
40
 
41
41
  ```bash
42
42
  npx lee-spec-kit init
43
+ npx lee-spec-kit integrations codex-hooks
43
44
  npx lee-spec-kit idea improve-auth-flow
44
45
  npx lee-spec-kit feature user-auth
45
- npx lee-spec-kit context
46
- npx lee-spec-kit flow
47
46
  ```
48
47
 
48
+ 그 다음부터는 자연어로 요청하면 됩니다.
49
+
49
50
  ## 왜 만들었나
50
51
 
51
52
  이 CLI는 AI 에이전트와 함께 프로젝트를 진행할 때, 문서와 실제 작업 흐름이 따로 놀지 않게 하려고 만들었습니다.
52
53
 
53
- 단순히 문서 폴더만 만드는 것이 아니라, 메인 에이전트가 지금 어떤 feature를 보고 있는지, 다음에 무엇을 해야 하는지, 어디서 사용자 확인이 필요한지를 같이 다룰 있게 하는 쪽에 더 가깝습니다.
54
+ 단순히 문서 폴더만 만드는 것이 아니라, 에이전트가 지금 어떤 feature를 보고 있는지, 다음에 무엇을 해야 하는지, 어디서 사용자 확인이 필요한지를 같은 규칙 안에서 다루도록 만드는 쪽에 더 가깝습니다.
54
55
 
55
- 작업 구조는 `PRD → idea → feature` 흐름을 따릅니다. PRD는 `docs/prd/`에서 상위 요구사항을 정리하는 공간이고, idea는 후보나 실험을 적어두는 단계이며, feature는 실제로 실행할 단위를 `spec.md`, `plan.md`, `tasks.md`로 내려 관리하는 단계입니다.
56
+ 작업 구조는 SDD(spec-driven development) 기반의 `PRD → idea → feature` 흐름을 따릅니다. PRD는 `docs/prd/`에서 상위 요구사항을 정리하는 공간이고, idea는 후보나 실험을 적어두는 단계이며, feature는 실제로 실행할 단위를 `spec.md`, `plan.md`, `tasks.md`, `decisions.md`로 내려 관리하는 단계입니다.
56
57
 
57
- 구조적으로는 [spec-kit](https://github.com/github/spec-kit)과 [OpenSpec](https://github.com/Fission-AI/OpenSpec)의 접근을 참고했습니다. 다만 이 프로젝트는 새 표준을 만들려는 쪽보다는, 내가 실제로 쓰는 작업 흐름을 더 잘 굴리기 위해 문서 구조와 실행 흐름을 한 CLI에 묶는 쪽에 가깝습니다.
58
+ 구조적으로는 [spec-kit](https://github.com/github/spec-kit)과 [OpenSpec](https://github.com/Fission-AI/OpenSpec)의 접근을 참고했습니다.
58
59
 
59
- ## CLI가 하는
60
+ ## 사람은 보통 이렇게 요청합니다
60
61
 
61
- `lee-spec-kit`는 사람이 세부 운영 명령을 외우는 도구라기보다, 메인 에이전트가 현재 상태를 읽고 다음 액션을 고르도록 돕는 개인용 하네스에 가깝습니다.
62
+ - "이 요구사항 기준으로 idea 정리해줘."
63
+ - "이 idea를 feature로 올려서 진행해줘."
64
+ - "현재 feature 기준으로 issue 초안 만들어줘."
65
+ - "규칙에 따라 다음 feature 진행해줘."
66
+ - "작업 끝났으니 문서랑 같이 점검해줘."
62
67
 
63
- - 사람은 보통 자연어로 요청합니다.
64
- - 사람용으로는 `init`, `idea`, `feature`, `context`, `flow` 정도의 작은 표면을 제공합니다.
65
- - 실제 에이전트 실행은 `detect`, `context`, `flow` 3개 명령을 기준으로 움직입니다.
66
- - 더 깊은 운영 명령은 여전히 지원되지만 기본 help에는 전면 노출하지 않습니다.
68
+ ## 주요 명령
67
69
 
68
- ## 어떻게 동작하나
70
+ - `init`: docs/workflow 구조 초기화
71
+ - `idea`: 구현 전 idea 문서 생성
72
+ - `feature`: 실제 작업 단위 생성
73
+ - `docs`: 내장 agent policy 문서 조회
74
+ - `detect`: 현재 워크스페이스가 lee-spec-kit 프로젝트인지 감지
75
+ - `github`: issue/pr 본문 생성 및 검증
76
+ - `integrations codex-hooks`: 현재 workspace용 Codex hooks 스캐폴드 생성/제거
77
+ - `integrations codex`: 전역 Codex hooks flag 설치/제거
78
+ - `commit-audit --json`: hooks용 commit-time docs path validator
79
+ - `workflow-audit --json`: hooks용 docs sync validator
69
80
 
70
- 1. `init`으로 docs/workflow 구조를 붙입니다.
71
- 2. `docs/prd/`에서 상위 요구사항을 정리합니다.
72
- 3. `idea`로 후보/실험을 정리하거나, 바로 `feature`로 실행 단위를 만듭니다.
73
- 4. 메인 에이전트가 `detect`와 `context`를 읽고 진행합니다.
74
- 5. 사람은 승인, 예외 처리, 방향 수정 시점에 개입합니다.
75
- 6. 현재 다음 액션과 승인 대기 상태는 `context`, 기본 workflow 실행/재개는 `flow`로 진행합니다.
81
+ 지원 모드:
76
82
 
77
- ## 사람은 보통 이렇게 요청합니다
78
-
79
- - "이 문서 읽고 프로젝트 구조 시작하려고 해."
80
- - "이 요구사항 기준으로 idea 정리해줘."
81
- - "이 idea를 feature로 올려서 진행해줘."
82
- - "지금 다음 액션이 뭐야?"
83
- - "전체 상태 한번 점검해줘."
84
-
85
- ## 에이전트가 주로 실행하는 명령
86
-
87
- - 실제 에이전트 실행 기준 명령은 아래 3개입니다.
88
- - `detect`: 현재 워크스페이스가 lee-spec-kit 프로젝트인지 감지합니다.
89
- - `context`: 현재 feature 상태와 다음 액션을 읽습니다.
90
- - `flow`: 기본 workflow auto-run을 진행하고 선택/승인/수동/재개 경계에서 멈춥니다.
91
- - 사람용 public 명령은 아래 다섯 개입니다.
92
- - `init`: docs/workflow 구조를 초기화합니다.
93
- - `idea`: 구현 전 아이디어 문서를 생성합니다.
94
- - `feature`: 실제 작업 단위를 생성합니다.
95
- - `context`: 현재 feature 상태와 다음 액션을 읽습니다.
96
- - `flow`: 기본 workflow auto-run을 진행하고 선택/승인/수동/재개 경계에서 멈춥니다.
97
-
98
- ## 에이전트 킥오프 프롬프트
99
-
100
- ```text
101
- 작업 시작 절차:
102
- 1) npx lee-spec-kit detect --json
103
- 2) isLeeSpecKitProject === true 이면 npx lee-spec-kit context --json-compact 실행
104
- 3) 상태 확인은 context를 read-only probe로 사용하고, 실제 실행/재개는 flow를 기본 엔트리포인트로 사용
105
- 4) approvalRequest.required=true 이면 matchedFeature.currentSubstate* 기반 현재 단계 한 줄 요약을 먼저 짧게 말하고 approvalRequest.userFacingLines를 그대로 사용자에게 제시한 뒤 승인 대기
106
- 5) 승인 전에는 실행하지 말고, 명령 실행은 기본적으로 npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute 사용
107
- 6) isLeeSpecKitProject === false 이면 lee-spec-kit 전용 절차를 건너뛰고 일반 워크플로우로 진행
108
- ```
83
+ - `embedded`: 프로젝트 안에 `docs/`를 함께 둡니다.
84
+ - `standalone`: workspace root 아래에서 docs repo와 project repo를 따로 관리합니다.
109
85
 
110
86
  ## Docs
111
87
 
112
88
  - [Public CLI Reference](./docs/reference/public-cli.md)
113
89
  - [Agent CLI Reference](./docs/reference/agent-cli.md)
114
90
  - [Internal CLI Reference](./docs/reference/internal-cli.md)
91
+ - [Codex Hooks Integration](./docs/reference/codex-hooks.md)
92
+ - [Migration Guide](./docs/reference/migration-codex-hooks.md)
115
93
  - [Reference Index](./docs/reference/README.md)
116
94
 
117
95
  ## License
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ export { LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN, LEE_SPEC_KIT_CODEX_BOOTSTRAP_END, getCodexConfigPath, getCodexHome, hasLeeSpecKitCodexBootstrap, removeLeeSpecKitCodexBootstrap, upsertLeeSpecKitCodexBootstrap } from './chunk-LYFRLOFQ.js';
3
+ import './chunk-7V7RMGEU.js';
4
+ //# sourceMappingURL=bootstrap-G37N6RGB.js.map
5
+ //# sourceMappingURL=bootstrap-G37N6RGB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"bootstrap-G37N6RGB.js"}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ var getFilename = () => fileURLToPath(import.meta.url);
6
+ var getDirname = () => path.dirname(getFilename());
7
+ var __dirname$1 = /* @__PURE__ */ getDirname();
8
+
9
+ export { __dirname$1 as __dirname };
10
+ //# sourceMappingURL=chunk-7V7RMGEU.js.map
11
+ //# sourceMappingURL=chunk-7V7RMGEU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js"],"names":["__dirname"],"mappings":";;;;AAIA,IAAM,WAAA,GAAc,MAAM,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA;AACvD,IAAM,UAAA,GAAa,MAAM,IAAA,CAAK,OAAA,CAAQ,aAAa,CAAA;AAE5C,IAAMA,8BAA4B,UAAA","file":"chunk-7V7RMGEU.js","sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n"]}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from 'child_process';
3
+
4
+ function runGitOrThrow(args, cwd, options = {}) {
5
+ const encoding = options.encoding ?? "utf-8";
6
+ const stdio = options.stdio ?? "ignore";
7
+ const out = execFileSync("git", args, {
8
+ cwd,
9
+ encoding,
10
+ stdio
11
+ });
12
+ if (typeof out === "string") return out.trim();
13
+ if (Buffer.isBuffer(out)) return out.toString(encoding).trim();
14
+ return "";
15
+ }
16
+ function runGitCapture(args, cwd) {
17
+ try {
18
+ return runGitOrThrow(args, cwd, { stdio: ["ignore", "pipe", "pipe"] });
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ }
23
+
24
+ export { runGitCapture, runGitOrThrow };
25
+ //# sourceMappingURL=chunk-GR7JQBWF.js.map
26
+ //# sourceMappingURL=chunk-GR7JQBWF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/git-run.ts"],"names":[],"mappings":";;;AAGO,SAAS,aAAA,CACd,IAAA,EACA,GAAA,EACA,OAAA,GAOI,EAAC,EACG;AACR,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,OAAA;AACrC,EAAA,MAAM,KAAA,GAAQ,QAAQ,KAAA,IAAS,QAAA;AAC/B,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,KAAA,EAAO,IAAA,EAAM;AAAA,IACpC,GAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,EAAU,OAAO,IAAI,IAAA,EAAK;AAC7C,EAAA,IAAI,MAAA,CAAO,SAAS,GAAG,CAAA,SAAU,GAAA,CAAI,QAAA,CAAS,QAAQ,CAAA,CAAE,IAAA,EAAK;AAC7D,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,aAAA,CAAc,MAAgB,GAAA,EAAiC;AAC7E,EAAA,IAAI;AACF,IAAA,OAAO,aAAA,CAAc,IAAA,EAAM,GAAA,EAAK,EAAE,KAAA,EAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,MAAM,CAAA,EAAG,CAAA;AAAA,EACvE,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF","file":"chunk-GR7JQBWF.js","sourcesContent":["/* eslint-disable no-undef */\nimport { execFileSync } from 'child_process';\n\nexport function runGitOrThrow(\n args: string[],\n cwd: string,\n options: {\n encoding?: BufferEncoding;\n stdio?:\n | 'pipe'\n | 'ignore'\n | ['ignore', 'pipe', 'pipe']\n | ['ignore', 'pipe', 'ignore'];\n } = {}\n): string {\n const encoding = options.encoding ?? 'utf-8';\n const stdio = options.stdio ?? 'ignore';\n const out = execFileSync('git', args, {\n cwd,\n encoding,\n stdio,\n }) as string | Buffer | null;\n\n if (typeof out === 'string') return out.trim();\n if (Buffer.isBuffer(out)) return out.toString(encoding).trim();\n return '';\n}\n\nexport function runGitCapture(args: string[], cwd: string): string | undefined {\n try {\n return runGitOrThrow(args, cwd, { stdio: ['ignore', 'pipe', 'pipe'] });\n } catch {\n return undefined;\n }\n}\n"]}
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs-extra';
3
+ import os from 'os';
4
+ import path from 'path';
5
+
6
+ var LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN = "# lee-spec-kit:codex-bootstrap:begin";
7
+ var LEE_SPEC_KIT_CODEX_BOOTSTRAP_END = "# lee-spec-kit:codex-bootstrap:end";
8
+ var REQUIRED_HOOKS_FLAG_LINE = "codex_hooks = true";
9
+ function renderManagedSegment() {
10
+ return [
11
+ LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN,
12
+ REQUIRED_HOOKS_FLAG_LINE,
13
+ LEE_SPEC_KIT_CODEX_BOOTSTRAP_END
14
+ ].join("\n");
15
+ }
16
+ function renderManagedBlock() {
17
+ return `${renderManagedSegment()}
18
+
19
+ `;
20
+ }
21
+ function sanitizeTomlScanContent(content) {
22
+ let result = "";
23
+ let index = 0;
24
+ let state = "normal";
25
+ while (index < content.length) {
26
+ const nextThree = content.slice(index, index + 3);
27
+ const char = content[index] || "";
28
+ if (state === "normal") {
29
+ if (nextThree === '"""') {
30
+ result += " ";
31
+ index += 3;
32
+ state = "multibasic";
33
+ continue;
34
+ }
35
+ if (nextThree === "'''") {
36
+ result += " ";
37
+ index += 3;
38
+ state = "multiliteral";
39
+ continue;
40
+ }
41
+ if (char === '"') {
42
+ result += " ";
43
+ index += 1;
44
+ state = "basic";
45
+ continue;
46
+ }
47
+ if (char === "'") {
48
+ result += " ";
49
+ index += 1;
50
+ state = "literal";
51
+ continue;
52
+ }
53
+ result += char;
54
+ index += 1;
55
+ continue;
56
+ }
57
+ if (state === "basic") {
58
+ if (char === "\\") {
59
+ result += " ";
60
+ index += 2;
61
+ continue;
62
+ }
63
+ result += char === "\n" ? "\n" : " ";
64
+ index += 1;
65
+ if (char === '"') state = "normal";
66
+ continue;
67
+ }
68
+ if (state === "literal") {
69
+ result += char === "\n" ? "\n" : " ";
70
+ index += 1;
71
+ if (char === "'") state = "normal";
72
+ continue;
73
+ }
74
+ if (state === "multibasic") {
75
+ if (nextThree === '"""') {
76
+ result += " ";
77
+ index += 3;
78
+ state = "normal";
79
+ continue;
80
+ }
81
+ result += char === "\n" ? "\n" : " ";
82
+ index += 1;
83
+ continue;
84
+ }
85
+ if (nextThree === "'''") {
86
+ result += " ";
87
+ index += 3;
88
+ state = "normal";
89
+ continue;
90
+ }
91
+ result += char === "\n" ? "\n" : " ";
92
+ index += 1;
93
+ }
94
+ return result;
95
+ }
96
+ function stripManagedBlock(content) {
97
+ const beginIndex = content.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);
98
+ const endIndex = content.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);
99
+ if (beginIndex === -1 || endIndex === -1 || beginIndex > endIndex) {
100
+ return content;
101
+ }
102
+ const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;
103
+ return `${content.slice(0, beginIndex)}${content.slice(replaceEnd)}`;
104
+ }
105
+ function findFirstTableHeaderIndex(content) {
106
+ const sanitized = sanitizeTomlScanContent(content);
107
+ const match = sanitized.match(/^\s*\[[^\]]+\](?:\s*#.*)?$/m);
108
+ return match?.index ?? -1;
109
+ }
110
+ function insertManagedBlockAtTopLevel(content, block) {
111
+ const normalizedBlock = block.trimEnd();
112
+ const firstTableIndex = findFirstTableHeaderIndex(content);
113
+ if (firstTableIndex === -1) {
114
+ let next2 = content;
115
+ if (next2.length > 0 && !next2.endsWith("\n")) next2 += "\n";
116
+ if (next2.trim().length > 0 && !next2.endsWith("\n\n")) next2 += "\n";
117
+ next2 += `${normalizedBlock}
118
+ `;
119
+ return next2;
120
+ }
121
+ const before = content.slice(0, firstTableIndex).trimEnd();
122
+ const after = content.slice(firstTableIndex).replace(/^\n+/, "");
123
+ let next = "";
124
+ if (before.length > 0) {
125
+ next += before;
126
+ if (!next.endsWith("\n\n")) next += next.endsWith("\n") ? "\n" : "\n\n";
127
+ }
128
+ next += `${normalizedBlock}
129
+
130
+ ${after}`;
131
+ return next;
132
+ }
133
+ function getCodexHome() {
134
+ const explicit = String(process.env.CODEX_HOME || "").trim();
135
+ if (explicit) return explicit;
136
+ return path.join(os.homedir(), ".codex");
137
+ }
138
+ function getCodexConfigPath() {
139
+ return path.join(getCodexHome(), "config.toml");
140
+ }
141
+ function contentIncludesRequiredBootstrap(content) {
142
+ return hasEnabledTopLevelCodexHooksKey(content) || hasEnabledFeaturesTableCodexHooksKey(content) || hasEnabledFeaturesInlineTableCodexHooksKey(content);
143
+ }
144
+ function hasConflictingTopLevelKey(content, key) {
145
+ const sanitized = sanitizeTomlScanContent(content);
146
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
147
+ const keyPattern = new RegExp(`^\\s*${escaped}\\s*=`, "m");
148
+ return keyPattern.test(sanitized);
149
+ }
150
+ function hasEnabledTopLevelCodexHooksKey(content) {
151
+ return /^\s*codex_hooks\s*=\s*true\b/m.test(
152
+ sanitizeTomlScanContent(content)
153
+ );
154
+ }
155
+ function hasConflictingFeaturesTableKey(content, key) {
156
+ const lines = sanitizeTomlScanContent(content).split("\n");
157
+ let inFeaturesTable = false;
158
+ for (const rawLine of lines) {
159
+ const line = rawLine.trim();
160
+ if (!line || line.startsWith("#")) continue;
161
+ const tableMatch = line.match(/^\[([^\]]+)\](?:\s*#.*)?$/);
162
+ if (tableMatch) {
163
+ inFeaturesTable = tableMatch[1]?.trim() === "features";
164
+ continue;
165
+ }
166
+ if (!inFeaturesTable) continue;
167
+ if (new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=`).test(line)) {
168
+ return true;
169
+ }
170
+ }
171
+ return false;
172
+ }
173
+ function hasEnabledFeaturesTableCodexHooksKey(content) {
174
+ const lines = sanitizeTomlScanContent(content).split("\n");
175
+ let inFeaturesTable = false;
176
+ for (const rawLine of lines) {
177
+ const line = rawLine.trim();
178
+ if (!line || line.startsWith("#")) continue;
179
+ const tableMatch = line.match(/^\[([^\]]+)\](?:\s*#.*)?$/);
180
+ if (tableMatch) {
181
+ inFeaturesTable = tableMatch[1]?.trim() === "features";
182
+ continue;
183
+ }
184
+ if (!inFeaturesTable) continue;
185
+ if (/^codex_hooks\s*=\s*true\b/.test(line)) {
186
+ return true;
187
+ }
188
+ }
189
+ return false;
190
+ }
191
+ function hasConflictingFeaturesInlineTableKey(content, key) {
192
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
193
+ const lines = sanitizeTomlScanContent(content).split("\n");
194
+ for (const rawLine of lines) {
195
+ const line = rawLine.trim();
196
+ if (!line || line.startsWith("#")) continue;
197
+ if (!/^features\s*=\s*\{/.test(line)) continue;
198
+ if (new RegExp(`\\b${escapedKey}\\s*=`).test(line)) {
199
+ return true;
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+ function hasEnabledFeaturesInlineTableCodexHooksKey(content) {
205
+ const lines = sanitizeTomlScanContent(content).split("\n");
206
+ for (const rawLine of lines) {
207
+ const line = rawLine.trim();
208
+ if (!line || line.startsWith("#")) continue;
209
+ if (!/^features\s*=\s*\{/.test(line)) continue;
210
+ if (/\bcodex_hooks\s*=\s*true\b/.test(line)) {
211
+ return true;
212
+ }
213
+ }
214
+ return false;
215
+ }
216
+ async function hasLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
217
+ if (!await fs.pathExists(filePath)) return false;
218
+ const content = await fs.readFile(filePath, "utf-8");
219
+ return content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN) && content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END) || contentIncludesRequiredBootstrap(content);
220
+ }
221
+ async function upsertLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
222
+ const block = renderManagedBlock();
223
+ const segment = renderManagedSegment();
224
+ await fs.ensureDir(path.dirname(filePath));
225
+ const exists = await fs.pathExists(filePath);
226
+ if (!exists) {
227
+ await fs.writeFile(filePath, block, "utf-8");
228
+ return { changed: true, action: "created", filePath };
229
+ }
230
+ const current = await fs.readFile(filePath, "utf-8");
231
+ const externalContent = stripManagedBlock(current);
232
+ const beginIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);
233
+ const endIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);
234
+ if (hasConflictingTopLevelKey(externalContent, "codex_hooks") || hasConflictingTopLevelKey(externalContent, "features.codex_hooks") || hasConflictingFeaturesTableKey(externalContent, "codex_hooks") || hasConflictingFeaturesInlineTableKey(externalContent, "codex_hooks")) {
235
+ throw new Error(
236
+ `Codex config already defines codex_hooks outside lee-spec-kit managed block: ${filePath}`
237
+ );
238
+ }
239
+ if (beginIndex !== -1 && endIndex !== -1 && beginIndex <= endIndex) {
240
+ const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;
241
+ const next2 = `${current.slice(0, beginIndex)}${segment}${current.slice(replaceEnd)}`;
242
+ if (next2 === current) {
243
+ return { changed: false, action: "noop", filePath };
244
+ }
245
+ await fs.writeFile(filePath, next2, "utf-8");
246
+ return { changed: true, action: "updated", filePath };
247
+ }
248
+ if (contentIncludesRequiredBootstrap(current)) {
249
+ return { changed: false, action: "noop", filePath };
250
+ }
251
+ const next = insertManagedBlockAtTopLevel(current, block);
252
+ await fs.writeFile(filePath, next, "utf-8");
253
+ return { changed: true, action: "appended", filePath };
254
+ }
255
+ async function removeLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
256
+ if (!await fs.pathExists(filePath)) {
257
+ return { changed: false, filePath };
258
+ }
259
+ const current = await fs.readFile(filePath, "utf-8");
260
+ const beginIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);
261
+ const endIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);
262
+ if (beginIndex === -1 || endIndex === -1 || beginIndex > endIndex) {
263
+ return { changed: false, filePath };
264
+ }
265
+ const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;
266
+ let next = `${current.slice(0, beginIndex)}${current.slice(replaceEnd)}`;
267
+ next = next.replace(/\n{3,}/g, "\n\n").trimEnd();
268
+ if (next.length > 0) next += "\n";
269
+ await fs.writeFile(filePath, next, "utf-8");
270
+ return { changed: true, filePath };
271
+ }
272
+
273
+ export { LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN, LEE_SPEC_KIT_CODEX_BOOTSTRAP_END, getCodexConfigPath, getCodexHome, hasLeeSpecKitCodexBootstrap, removeLeeSpecKitCodexBootstrap, upsertLeeSpecKitCodexBootstrap };
274
+ //# sourceMappingURL=chunk-LYFRLOFQ.js.map
275
+ //# sourceMappingURL=chunk-LYFRLOFQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations/codex/bootstrap.ts"],"names":["next"],"mappings":";;;;;AAIO,IAAM,kCAAA,GACX;AACK,IAAM,gCAAA,GACX;AAEF,IAAM,wBAAA,GAA2B,oBAAA;AAEjC,SAAS,oBAAA,GAA+B;AACtC,EAAA,OAAO;AAAA,IACL,kCAAA;AAAA,IACA,wBAAA;AAAA,IACA;AAAA,GACF,CAAE,KAAK,IAAI,CAAA;AACb;AAEA,SAAS,kBAAA,GAA6B;AACpC,EAAA,OAAO,CAAA,EAAG,sBAAsB;;AAAA,CAAA;AAClC;AAEA,SAAS,wBAAwB,OAAA,EAAyB;AACxD,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,KAAA,GAAwE,QAAA;AAE5E,EAAA,OAAO,KAAA,GAAQ,QAAQ,MAAA,EAAQ;AAC7B,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAC,CAAA;AAChD,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAK,CAAA,IAAK,EAAA;AAE/B,IAAA,IAAI,UAAU,QAAA,EAAU;AACtB,MAAA,IAAI,cAAc,KAAA,EAAO;AACvB,QAAA,MAAA,IAAU,KAAA;AACV,QAAA,KAAA,IAAS,CAAA;AACT,QAAA,KAAA,GAAQ,YAAA;AACR,QAAA;AAAA,MACF;AACA,MAAA,IAAI,cAAc,KAAA,EAAO;AACvB,QAAA,MAAA,IAAU,KAAA;AACV,QAAA,KAAA,IAAS,CAAA;AACT,QAAA,KAAA,GAAQ,cAAA;AACR,QAAA;AAAA,MACF;AACA,MAAA,IAAI,SAAS,GAAA,EAAK;AAChB,QAAA,MAAA,IAAU,GAAA;AACV,QAAA,KAAA,IAAS,CAAA;AACT,QAAA,KAAA,GAAQ,OAAA;AACR,QAAA;AAAA,MACF;AACA,MAAA,IAAI,SAAS,GAAA,EAAK;AAChB,QAAA,MAAA,IAAU,GAAA;AACV,QAAA,KAAA,IAAS,CAAA;AACT,QAAA,KAAA,GAAQ,SAAA;AACR,QAAA;AAAA,MACF;AACA,MAAA,MAAA,IAAU,IAAA;AACV,MAAA,KAAA,IAAS,CAAA;AACT,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAU,OAAA,EAAS;AACrB,MAAA,IAAI,SAAS,IAAA,EAAM;AACjB,QAAA,MAAA,IAAU,IAAA;AACV,QAAA,KAAA,IAAS,CAAA;AACT,QAAA;AAAA,MACF;AACA,MAAA,MAAA,IAAU,IAAA,KAAS,OAAO,IAAA,GAAO,GAAA;AACjC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,IAAI,IAAA,KAAS,KAAK,KAAA,GAAQ,QAAA;AAC1B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAU,SAAA,EAAW;AACvB,MAAA,MAAA,IAAU,IAAA,KAAS,OAAO,IAAA,GAAO,GAAA;AACjC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,IAAI,IAAA,KAAS,KAAK,KAAA,GAAQ,QAAA;AAC1B,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,UAAU,YAAA,EAAc;AAC1B,MAAA,IAAI,cAAc,KAAA,EAAO;AACvB,QAAA,MAAA,IAAU,KAAA;AACV,QAAA,KAAA,IAAS,CAAA;AACT,QAAA,KAAA,GAAQ,QAAA;AACR,QAAA;AAAA,MACF;AACA,MAAA,MAAA,IAAU,IAAA,KAAS,OAAO,IAAA,GAAO,GAAA;AACjC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,cAAc,KAAA,EAAO;AACvB,MAAA,MAAA,IAAU,KAAA;AACV,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,KAAA,GAAQ,QAAA;AACR,MAAA;AAAA,IACF;AACA,IAAA,MAAA,IAAU,IAAA,KAAS,OAAO,IAAA,GAAO,GAAA;AACjC,IAAA,KAAA,IAAS,CAAA;AAAA,EACX;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,kBAAkB,OAAA,EAAyB;AAClD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,kCAAkC,CAAA;AACrE,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,gCAAgC,CAAA;AACjE,EAAA,IAAI,UAAA,KAAe,EAAA,IAAM,QAAA,KAAa,EAAA,IAAM,aAAa,QAAA,EAAU;AACjE,IAAA,OAAO,OAAA;AAAA,EACT;AACA,EAAA,MAAM,UAAA,GAAa,WAAW,gCAAA,CAAiC,MAAA;AAC/D,EAAA,OAAO,CAAA,EAAG,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,UAAU,CAAC,CAAA,EAAG,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAC,CAAA,CAAA;AACpE;AAEA,SAAS,0BAA0B,OAAA,EAAyB;AAC1D,EAAA,MAAM,SAAA,GAAY,wBAAwB,OAAO,CAAA;AACjD,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,KAAA,CAAM,6BAA6B,CAAA;AAC3D,EAAA,OAAO,OAAO,KAAA,IAAS,EAAA;AACzB;AAEA,SAAS,4BAAA,CAA6B,SAAiB,KAAA,EAAuB;AAC5E,EAAA,MAAM,eAAA,GAAkB,MAAM,OAAA,EAAQ;AACtC,EAAA,MAAM,eAAA,GAAkB,0BAA0B,OAAO,CAAA;AAEzD,EAAA,IAAI,oBAAoB,EAAA,EAAI;AAC1B,IAAA,IAAIA,KAAAA,GAAO,OAAA;AACX,IAAA,IAAIA,KAAAA,CAAK,SAAS,CAAA,IAAK,CAACA,MAAK,QAAA,CAAS,IAAI,CAAA,EAAGA,KAAAA,IAAQ,IAAA;AACrD,IAAA,IAAIA,KAAAA,CAAK,IAAA,EAAK,CAAE,MAAA,GAAS,CAAA,IAAK,CAACA,KAAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAGA,KAAAA,IAAQ,IAAA;AAC9D,IAAAA,KAAAA,IAAQ,GAAG,eAAe;AAAA,CAAA;AAC1B,IAAA,OAAOA,KAAAA;AAAA,EACT;AAEA,EAAA,MAAM,SAAS,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,eAAe,EAAE,OAAA,EAAQ;AACzD,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA,CAAM,eAAe,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAE/D,EAAA,IAAI,IAAA,GAAO,EAAA;AACX,EAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,IAAA,IAAA,IAAQ,MAAA;AACR,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,UAAW,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,GAAI,IAAA,GAAO,MAAA;AAAA,EACnE;AACA,EAAA,IAAA,IAAQ,GAAG,eAAe;;AAAA,EAAO,KAAK,CAAA,CAAA;AACtC,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,YAAA,GAAuB;AACrC,EAAA,MAAM,WAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,UAAA,IAAc,EAAE,EAAE,IAAA,EAAK;AAC3D,EAAA,IAAI,UAAU,OAAO,QAAA;AACrB,EAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAA,CAAG,OAAA,IAAW,QAAQ,CAAA;AACzC;AAEO,SAAS,kBAAA,GAA6B;AAC3C,EAAA,OAAO,IAAA,CAAK,IAAA,CAAK,YAAA,EAAa,EAAG,aAAa,CAAA;AAChD;AAEA,SAAS,iCAAiC,OAAA,EAA0B;AAClE,EAAA,OACE,gCAAgC,OAAO,CAAA,IACvC,qCAAqC,OAAO,CAAA,IAC5C,2CAA2C,OAAO,CAAA;AAEtD;AAEA,SAAS,yBAAA,CAA0B,SAAiB,GAAA,EAAsB;AACxE,EAAA,MAAM,SAAA,GAAY,wBAAwB,OAAO,CAAA;AACjD,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAA;AACzD,EAAA,MAAM,aAAa,IAAI,MAAA,CAAO,CAAA,KAAA,EAAQ,OAAO,SAAS,GAAG,CAAA;AACzD,EAAA,OAAO,UAAA,CAAW,KAAK,SAAS,CAAA;AAClC;AAEA,SAAS,gCAAgC,OAAA,EAA0B;AACjE,EAAA,OAAO,+BAAA,CAAgC,IAAA;AAAA,IACrC,wBAAwB,OAAO;AAAA,GACjC;AACF;AAEA,SAAS,8BAAA,CAA+B,SAAiB,GAAA,EAAsB;AAC7E,EAAA,MAAM,KAAA,GAAQ,uBAAA,CAAwB,OAAO,CAAA,CAAE,MAAM,IAAI,CAAA;AACzD,EAAA,IAAI,eAAA,GAAkB,KAAA;AAEtB,EAAA,KAAA,MAAW,WAAW,KAAA,EAAO;AAC3B,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,EAAK;AAC1B,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AAEnC,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,KAAA,CAAM,2BAA2B,CAAA;AACzD,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,eAAA,GAAkB,UAAA,CAAW,CAAC,CAAA,EAAG,IAAA,EAAK,KAAM,UAAA;AAC5C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,eAAA,EAAiB;AACtB,IAAA,IAAI,IAAI,MAAA,CAAO,CAAA,CAAA,EAAI,GAAA,CAAI,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAC,CAAA,KAAA,CAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,EAAG;AAChF,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,qCAAqC,OAAA,EAA0B;AACtE,EAAA,MAAM,KAAA,GAAQ,uBAAA,CAAwB,OAAO,CAAA,CAAE,MAAM,IAAI,CAAA;AACzD,EAAA,IAAI,eAAA,GAAkB,KAAA;AAEtB,EAAA,KAAA,MAAW,WAAW,KAAA,EAAO;AAC3B,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,EAAK;AAC1B,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AAEnC,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,KAAA,CAAM,2BAA2B,CAAA;AACzD,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,eAAA,GAAkB,UAAA,CAAW,CAAC,CAAA,EAAG,IAAA,EAAK,KAAM,UAAA;AAC5C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,eAAA,EAAiB;AACtB,IAAA,IAAI,2BAAA,CAA4B,IAAA,CAAK,IAAI,CAAA,EAAG;AAC1C,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,oCAAA,CAAqC,SAAiB,GAAA,EAAsB;AACnF,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAA;AAC5D,EAAA,MAAM,KAAA,GAAQ,uBAAA,CAAwB,OAAO,CAAA,CAAE,MAAM,IAAI,CAAA;AAEzD,EAAA,KAAA,MAAW,WAAW,KAAA,EAAO;AAC3B,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,EAAK;AAC1B,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACnC,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAA,CAAK,IAAI,CAAA,EAAG;AACtC,IAAA,IAAI,IAAI,OAAO,CAAA,GAAA,EAAM,UAAU,OAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,EAAG;AAClD,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,2CAA2C,OAAA,EAA0B;AAC5E,EAAA,MAAM,KAAA,GAAQ,uBAAA,CAAwB,OAAO,CAAA,CAAE,MAAM,IAAI,CAAA;AAEzD,EAAA,KAAA,MAAW,WAAW,KAAA,EAAO;AAC3B,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,EAAK;AAC1B,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACnC,IAAA,IAAI,CAAC,oBAAA,CAAqB,IAAA,CAAK,IAAI,CAAA,EAAG;AACtC,IAAA,IAAI,4BAAA,CAA6B,IAAA,CAAK,IAAI,CAAA,EAAG;AAC3C,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAEA,eAAsB,2BAAA,CACpB,QAAA,GAAW,kBAAA,EAAmB,EACZ;AAClB,EAAA,IAAI,CAAE,MAAM,EAAA,CAAG,UAAA,CAAW,QAAQ,GAAI,OAAO,KAAA;AAC7C,EAAA,MAAM,OAAA,GAAU,MAAM,EAAA,CAAG,QAAA,CAAS,UAAU,OAAO,CAAA;AACnD,EAAA,OACG,OAAA,CAAQ,SAAS,kCAAkC,CAAA,IAClD,QAAQ,QAAA,CAAS,gCAAgC,CAAA,IACnD,gCAAA,CAAiC,OAAO,CAAA;AAE5C;AAEA,eAAsB,8BAAA,CACpB,QAAA,GAAW,kBAAA,EAAmB,EAK7B;AACD,EAAA,MAAM,QAAQ,kBAAA,EAAmB;AACjC,EAAA,MAAM,UAAU,oBAAA,EAAqB;AACrC,EAAA,MAAM,EAAA,CAAG,SAAA,CAAU,IAAA,CAAK,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAEzC,EAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,UAAA,CAAW,QAAQ,CAAA;AAC3C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,EAAA,CAAG,SAAA,CAAU,QAAA,EAAU,KAAA,EAAO,OAAO,CAAA;AAC3C,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,MAAA,EAAQ,WAAW,QAAA,EAAS;AAAA,EACtD;AAEA,EAAA,MAAM,OAAA,GAAU,MAAM,EAAA,CAAG,QAAA,CAAS,UAAU,OAAO,CAAA;AACnD,EAAA,MAAM,eAAA,GAAkB,kBAAkB,OAAO,CAAA;AACjD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,kCAAkC,CAAA;AACrE,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,gCAAgC,CAAA;AAEjE,EAAA,IACE,yBAAA,CAA0B,eAAA,EAAiB,aAAa,CAAA,IACxD,0BAA0B,eAAA,EAAiB,sBAAsB,CAAA,IACjE,8BAAA,CAA+B,iBAAiB,aAAa,CAAA,IAC7D,oCAAA,CAAqC,eAAA,EAAiB,aAAa,CAAA,EACnE;AACA,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,gFAAgF,QAAQ,CAAA;AAAA,KAC1F;AAAA,EACF;AAEA,EAAA,IAAI,UAAA,KAAe,EAAA,IAAM,QAAA,KAAa,EAAA,IAAM,cAAc,QAAA,EAAU;AAClE,IAAA,MAAM,UAAA,GAAa,WAAW,gCAAA,CAAiC,MAAA;AAC/D,IAAA,MAAMA,KAAAA,GAAO,CAAA,EAAG,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,UAAU,CAAC,CAAA,EAAG,OAAO,CAAA,EAAG,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAC,CAAA,CAAA;AAClF,IAAA,IAAIA,UAAS,OAAA,EAAS;AACpB,MAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,QAAQ,QAAA,EAAS;AAAA,IACpD;AACA,IAAA,MAAM,EAAA,CAAG,SAAA,CAAU,QAAA,EAAUA,KAAAA,EAAM,OAAO,CAAA;AAC1C,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,MAAA,EAAQ,WAAW,QAAA,EAAS;AAAA,EACtD;AAEA,EAAA,IAAI,gCAAA,CAAiC,OAAO,CAAA,EAAG;AAC7C,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,QAAQ,QAAA,EAAS;AAAA,EACpD;AAEA,EAAA,MAAM,IAAA,GAAO,4BAAA,CAA6B,OAAA,EAAS,KAAK,CAAA;AAExD,EAAA,MAAM,EAAA,CAAG,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM,OAAO,CAAA;AAC1C,EAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,MAAA,EAAQ,YAAY,QAAA,EAAS;AACvD;AAEA,eAAsB,8BAAA,CACpB,QAAA,GAAW,kBAAA,EAAmB,EACmB;AACjD,EAAA,IAAI,CAAE,MAAM,EAAA,CAAG,UAAA,CAAW,QAAQ,CAAA,EAAI;AACpC,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,QAAA,EAAS;AAAA,EACpC;AAEA,EAAA,MAAM,OAAA,GAAU,MAAM,EAAA,CAAG,QAAA,CAAS,UAAU,OAAO,CAAA;AACnD,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,kCAAkC,CAAA;AACrE,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,gCAAgC,CAAA;AACjE,EAAA,IAAI,UAAA,KAAe,EAAA,IAAM,QAAA,KAAa,EAAA,IAAM,aAAa,QAAA,EAAU;AACjE,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,QAAA,EAAS;AAAA,EACpC;AAEA,EAAA,MAAM,UAAA,GAAa,WAAW,gCAAA,CAAiC,MAAA;AAC/D,EAAA,IAAI,IAAA,GAAO,CAAA,EAAG,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,UAAU,CAAC,CAAA,EAAG,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAC,CAAA,CAAA;AACtE,EAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,MAAM,EAAE,OAAA,EAAQ;AAC/C,EAAA,IAAI,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG,IAAA,IAAQ,IAAA;AAC7B,EAAA,MAAM,EAAA,CAAG,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM,OAAO,CAAA;AAC1C,EAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,QAAA,EAAS;AACnC","file":"chunk-LYFRLOFQ.js","sourcesContent":["import fs from 'fs-extra';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport const LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN =\n '# lee-spec-kit:codex-bootstrap:begin';\nexport const LEE_SPEC_KIT_CODEX_BOOTSTRAP_END =\n '# lee-spec-kit:codex-bootstrap:end';\n\nconst REQUIRED_HOOKS_FLAG_LINE = 'codex_hooks = true';\n\nfunction renderManagedSegment(): string {\n return [\n LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN,\n REQUIRED_HOOKS_FLAG_LINE,\n LEE_SPEC_KIT_CODEX_BOOTSTRAP_END,\n ].join('\\n');\n}\n\nfunction renderManagedBlock(): string {\n return `${renderManagedSegment()}\\n\\n`;\n}\n\nfunction sanitizeTomlScanContent(content: string): string {\n let result = '';\n let index = 0;\n let state: 'normal' | 'basic' | 'literal' | 'multibasic' | 'multiliteral' = 'normal';\n\n while (index < content.length) {\n const nextThree = content.slice(index, index + 3);\n const char = content[index] || '';\n\n if (state === 'normal') {\n if (nextThree === '\"\"\"') {\n result += ' ';\n index += 3;\n state = 'multibasic';\n continue;\n }\n if (nextThree === \"'''\") {\n result += ' ';\n index += 3;\n state = 'multiliteral';\n continue;\n }\n if (char === '\"') {\n result += ' ';\n index += 1;\n state = 'basic';\n continue;\n }\n if (char === \"'\") {\n result += ' ';\n index += 1;\n state = 'literal';\n continue;\n }\n result += char;\n index += 1;\n continue;\n }\n\n if (state === 'basic') {\n if (char === '\\\\') {\n result += ' ';\n index += 2;\n continue;\n }\n result += char === '\\n' ? '\\n' : ' ';\n index += 1;\n if (char === '\"') state = 'normal';\n continue;\n }\n\n if (state === 'literal') {\n result += char === '\\n' ? '\\n' : ' ';\n index += 1;\n if (char === \"'\") state = 'normal';\n continue;\n }\n\n if (state === 'multibasic') {\n if (nextThree === '\"\"\"') {\n result += ' ';\n index += 3;\n state = 'normal';\n continue;\n }\n result += char === '\\n' ? '\\n' : ' ';\n index += 1;\n continue;\n }\n\n if (nextThree === \"'''\") {\n result += ' ';\n index += 3;\n state = 'normal';\n continue;\n }\n result += char === '\\n' ? '\\n' : ' ';\n index += 1;\n }\n\n return result;\n}\n\nfunction stripManagedBlock(content: string): string {\n const beginIndex = content.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);\n const endIndex = content.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);\n if (beginIndex === -1 || endIndex === -1 || beginIndex > endIndex) {\n return content;\n }\n const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;\n return `${content.slice(0, beginIndex)}${content.slice(replaceEnd)}`;\n}\n\nfunction findFirstTableHeaderIndex(content: string): number {\n const sanitized = sanitizeTomlScanContent(content);\n const match = sanitized.match(/^\\s*\\[[^\\]]+\\](?:\\s*#.*)?$/m);\n return match?.index ?? -1;\n}\n\nfunction insertManagedBlockAtTopLevel(content: string, block: string): string {\n const normalizedBlock = block.trimEnd();\n const firstTableIndex = findFirstTableHeaderIndex(content);\n\n if (firstTableIndex === -1) {\n let next = content;\n if (next.length > 0 && !next.endsWith('\\n')) next += '\\n';\n if (next.trim().length > 0 && !next.endsWith('\\n\\n')) next += '\\n';\n next += `${normalizedBlock}\\n`;\n return next;\n }\n\n const before = content.slice(0, firstTableIndex).trimEnd();\n const after = content.slice(firstTableIndex).replace(/^\\n+/, '');\n\n let next = '';\n if (before.length > 0) {\n next += before;\n if (!next.endsWith('\\n\\n')) next += next.endsWith('\\n') ? '\\n' : '\\n\\n';\n }\n next += `${normalizedBlock}\\n\\n${after}`;\n return next;\n}\n\nexport function getCodexHome(): string {\n const explicit = String(process.env.CODEX_HOME || '').trim();\n if (explicit) return explicit;\n return path.join(os.homedir(), '.codex');\n}\n\nexport function getCodexConfigPath(): string {\n return path.join(getCodexHome(), 'config.toml');\n}\n\nfunction contentIncludesRequiredBootstrap(content: string): boolean {\n return (\n hasEnabledTopLevelCodexHooksKey(content) ||\n hasEnabledFeaturesTableCodexHooksKey(content) ||\n hasEnabledFeaturesInlineTableCodexHooksKey(content)\n );\n}\n\nfunction hasConflictingTopLevelKey(content: string, key: string): boolean {\n const sanitized = sanitizeTomlScanContent(content);\n const escaped = key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const keyPattern = new RegExp(`^\\\\s*${escaped}\\\\s*=`, 'm');\n return keyPattern.test(sanitized);\n}\n\nfunction hasEnabledTopLevelCodexHooksKey(content: string): boolean {\n return /^\\s*codex_hooks\\s*=\\s*true\\b/m.test(\n sanitizeTomlScanContent(content)\n );\n}\n\nfunction hasConflictingFeaturesTableKey(content: string, key: string): boolean {\n const lines = sanitizeTomlScanContent(content).split('\\n');\n let inFeaturesTable = false;\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (!line || line.startsWith('#')) continue;\n\n const tableMatch = line.match(/^\\[([^\\]]+)\\](?:\\s*#.*)?$/);\n if (tableMatch) {\n inFeaturesTable = tableMatch[1]?.trim() === 'features';\n continue;\n }\n\n if (!inFeaturesTable) continue;\n if (new RegExp(`^${key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s*=`).test(line)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction hasEnabledFeaturesTableCodexHooksKey(content: string): boolean {\n const lines = sanitizeTomlScanContent(content).split('\\n');\n let inFeaturesTable = false;\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (!line || line.startsWith('#')) continue;\n\n const tableMatch = line.match(/^\\[([^\\]]+)\\](?:\\s*#.*)?$/);\n if (tableMatch) {\n inFeaturesTable = tableMatch[1]?.trim() === 'features';\n continue;\n }\n\n if (!inFeaturesTable) continue;\n if (/^codex_hooks\\s*=\\s*true\\b/.test(line)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction hasConflictingFeaturesInlineTableKey(content: string, key: string): boolean {\n const escapedKey = key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const lines = sanitizeTomlScanContent(content).split('\\n');\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (!line || line.startsWith('#')) continue;\n if (!/^features\\s*=\\s*\\{/.test(line)) continue;\n if (new RegExp(`\\\\b${escapedKey}\\\\s*=`).test(line)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction hasEnabledFeaturesInlineTableCodexHooksKey(content: string): boolean {\n const lines = sanitizeTomlScanContent(content).split('\\n');\n\n for (const rawLine of lines) {\n const line = rawLine.trim();\n if (!line || line.startsWith('#')) continue;\n if (!/^features\\s*=\\s*\\{/.test(line)) continue;\n if (/\\bcodex_hooks\\s*=\\s*true\\b/.test(line)) {\n return true;\n }\n }\n\n return false;\n}\n\nexport async function hasLeeSpecKitCodexBootstrap(\n filePath = getCodexConfigPath()\n): Promise<boolean> {\n if (!(await fs.pathExists(filePath))) return false;\n const content = await fs.readFile(filePath, 'utf-8');\n return (\n (content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN) &&\n content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END)) ||\n contentIncludesRequiredBootstrap(content)\n );\n}\n\nexport async function upsertLeeSpecKitCodexBootstrap(\n filePath = getCodexConfigPath()\n): Promise<{\n changed: boolean;\n action: 'created' | 'appended' | 'updated' | 'noop';\n filePath: string;\n}> {\n const block = renderManagedBlock();\n const segment = renderManagedSegment();\n await fs.ensureDir(path.dirname(filePath));\n\n const exists = await fs.pathExists(filePath);\n if (!exists) {\n await fs.writeFile(filePath, block, 'utf-8');\n return { changed: true, action: 'created', filePath };\n }\n\n const current = await fs.readFile(filePath, 'utf-8');\n const externalContent = stripManagedBlock(current);\n const beginIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);\n const endIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);\n\n if (\n hasConflictingTopLevelKey(externalContent, 'codex_hooks') ||\n hasConflictingTopLevelKey(externalContent, 'features.codex_hooks') ||\n hasConflictingFeaturesTableKey(externalContent, 'codex_hooks') ||\n hasConflictingFeaturesInlineTableKey(externalContent, 'codex_hooks')\n ) {\n throw new Error(\n `Codex config already defines codex_hooks outside lee-spec-kit managed block: ${filePath}`\n );\n }\n\n if (beginIndex !== -1 && endIndex !== -1 && beginIndex <= endIndex) {\n const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;\n const next = `${current.slice(0, beginIndex)}${segment}${current.slice(replaceEnd)}`;\n if (next === current) {\n return { changed: false, action: 'noop', filePath };\n }\n await fs.writeFile(filePath, next, 'utf-8');\n return { changed: true, action: 'updated', filePath };\n }\n\n if (contentIncludesRequiredBootstrap(current)) {\n return { changed: false, action: 'noop', filePath };\n }\n\n const next = insertManagedBlockAtTopLevel(current, block);\n\n await fs.writeFile(filePath, next, 'utf-8');\n return { changed: true, action: 'appended', filePath };\n}\n\nexport async function removeLeeSpecKitCodexBootstrap(\n filePath = getCodexConfigPath()\n): Promise<{ changed: boolean; filePath: string }> {\n if (!(await fs.pathExists(filePath))) {\n return { changed: false, filePath };\n }\n\n const current = await fs.readFile(filePath, 'utf-8');\n const beginIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN);\n const endIndex = current.indexOf(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END);\n if (beginIndex === -1 || endIndex === -1 || beginIndex > endIndex) {\n return { changed: false, filePath };\n }\n\n const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;\n let next = `${current.slice(0, beginIndex)}${current.slice(replaceEnd)}`;\n next = next.replace(/\\n{3,}/g, '\\n\\n').trimEnd();\n if (next.length > 0) next += '\\n';\n await fs.writeFile(filePath, next, 'utf-8');\n return { changed: true, filePath };\n}\n"]}