lee-spec-kit 0.8.7 → 0.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/dist/{bootstrap-G37N6RGB.js → bootstrap-Q77MTW3Q.js} +3 -3
- package/dist/{bootstrap-G37N6RGB.js.map → bootstrap-Q77MTW3Q.js.map} +1 -1
- package/dist/{chunk-LYFRLOFQ.js → chunk-3AFCPGGS.js} +62 -54
- package/dist/chunk-3AFCPGGS.js.map +1 -0
- package/dist/{hooks-UUAAVDS3.js → hooks-373Z6JG2.js} +51 -23
- package/dist/hooks-373Z6JG2.js.map +1 -0
- package/dist/index.js +35 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-LYFRLOFQ.js.map +0 -1
- package/dist/hooks-UUAAVDS3.js.map +0 -1
package/README.en.md
CHANGED
|
@@ -75,8 +75,8 @@ The overall approach is influenced by [spec-kit](https://github.com/github/spec-
|
|
|
75
75
|
- `docs`
|
|
76
76
|
- `detect`
|
|
77
77
|
- `github`
|
|
78
|
-
- `integrations codex-hooks
|
|
79
|
-
- `integrations codex`
|
|
78
|
+
- `integrations codex-hooks`: install/remove hooks in the workspace and configured project roots
|
|
79
|
+
- `integrations codex`: install/remove the optional global `[features].hooks` setting
|
|
80
80
|
- `commit-audit --json`
|
|
81
81
|
- `workflow-audit --json`
|
|
82
82
|
|
package/README.md
CHANGED
|
@@ -75,8 +75,8 @@ npx lee-spec-kit feature user-auth
|
|
|
75
75
|
- `docs`: 내장 agent policy 문서 조회
|
|
76
76
|
- `detect`: 현재 워크스페이스가 lee-spec-kit 프로젝트인지 감지
|
|
77
77
|
- `github`: issue/pr 본문 생성 및 검증
|
|
78
|
-
- `integrations codex-hooks`: 현재 workspace용 Codex hooks
|
|
79
|
-
- `integrations codex`: 전역
|
|
78
|
+
- `integrations codex-hooks`: 현재 workspace와 configured project root용 Codex hooks 생성/제거
|
|
79
|
+
- `integrations codex`: 선택적 전역 `[features].hooks` 설정 설치/제거
|
|
80
80
|
- `commit-audit --json`: hooks용 commit-time docs path validator
|
|
81
81
|
- `workflow-audit --json`: hooks용 docs sync validator
|
|
82
82
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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-
|
|
2
|
+
export { LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN, LEE_SPEC_KIT_CODEX_BOOTSTRAP_END, getCodexConfigPath, getCodexHome, hasLeeSpecKitCodexBootstrap, removeLeeSpecKitCodexBootstrap, upsertLeeSpecKitCodexBootstrap } from './chunk-3AFCPGGS.js';
|
|
3
3
|
import './chunk-7V7RMGEU.js';
|
|
4
|
-
//# sourceMappingURL=bootstrap-
|
|
5
|
-
//# sourceMappingURL=bootstrap-
|
|
4
|
+
//# sourceMappingURL=bootstrap-Q77MTW3Q.js.map
|
|
5
|
+
//# sourceMappingURL=bootstrap-Q77MTW3Q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"bootstrap-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"bootstrap-Q77MTW3Q.js"}
|
|
@@ -5,7 +5,7 @@ import path from 'path';
|
|
|
5
5
|
|
|
6
6
|
var LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN = "# lee-spec-kit:codex-bootstrap:begin";
|
|
7
7
|
var LEE_SPEC_KIT_CODEX_BOOTSTRAP_END = "# lee-spec-kit:codex-bootstrap:end";
|
|
8
|
-
var REQUIRED_HOOKS_FLAG_LINE = "
|
|
8
|
+
var REQUIRED_HOOKS_FLAG_LINE = "hooks = true";
|
|
9
9
|
function renderManagedSegment() {
|
|
10
10
|
return [
|
|
11
11
|
LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN,
|
|
@@ -13,11 +13,6 @@ function renderManagedSegment() {
|
|
|
13
13
|
LEE_SPEC_KIT_CODEX_BOOTSTRAP_END
|
|
14
14
|
].join("\n");
|
|
15
15
|
}
|
|
16
|
-
function renderManagedBlock() {
|
|
17
|
-
return `${renderManagedSegment()}
|
|
18
|
-
|
|
19
|
-
`;
|
|
20
|
-
}
|
|
21
16
|
function sanitizeTomlScanContent(content) {
|
|
22
17
|
let result = "";
|
|
23
18
|
let index = 0;
|
|
@@ -102,33 +97,22 @@ function stripManagedBlock(content) {
|
|
|
102
97
|
const replaceEnd = endIndex + LEE_SPEC_KIT_CODEX_BOOTSTRAP_END.length;
|
|
103
98
|
return `${content.slice(0, beginIndex)}${content.slice(replaceEnd)}`;
|
|
104
99
|
}
|
|
105
|
-
function
|
|
100
|
+
function findFeaturesTableHeaderEnd(content) {
|
|
106
101
|
const sanitized = sanitizeTomlScanContent(content);
|
|
107
|
-
const match =
|
|
108
|
-
return match?.index
|
|
102
|
+
const match = /^\s*\[features\](?:\s*#.*)?(?:\r?\n|$)/m.exec(sanitized);
|
|
103
|
+
return match?.index === void 0 ? -1 : match.index + match[0].length;
|
|
109
104
|
}
|
|
110
|
-
function
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
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";
|
|
105
|
+
function insertManagedFeaturesBlock(content) {
|
|
106
|
+
const segment = renderManagedSegment();
|
|
107
|
+
const featuresHeaderEnd = findFeaturesTableHeaderEnd(content);
|
|
108
|
+
if (featuresHeaderEnd !== -1) {
|
|
109
|
+
return `${content.slice(0, featuresHeaderEnd)}${segment}
|
|
110
|
+
${content.slice(featuresHeaderEnd)}`;
|
|
127
111
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
${
|
|
131
|
-
|
|
112
|
+
const prefix = content.trimEnd();
|
|
113
|
+
return `${prefix}${prefix ? "\n\n" : ""}[features]
|
|
114
|
+
${segment}
|
|
115
|
+
`;
|
|
132
116
|
}
|
|
133
117
|
function getCodexHome() {
|
|
134
118
|
const explicit = String(process.env.CODEX_HOME || "").trim();
|
|
@@ -139,7 +123,7 @@ function getCodexConfigPath() {
|
|
|
139
123
|
return path.join(getCodexHome(), "config.toml");
|
|
140
124
|
}
|
|
141
125
|
function contentIncludesRequiredBootstrap(content) {
|
|
142
|
-
return hasEnabledTopLevelCodexHooksKey(content) || hasEnabledFeaturesTableCodexHooksKey(content) || hasEnabledFeaturesInlineTableCodexHooksKey(content);
|
|
126
|
+
return hasEnabledTopLevelFeaturesHooksKey(content) || hasEnabledFeaturesTableHooksKey(content) || hasEnabledFeaturesInlineTableHooksKey(content) || hasEnabledTopLevelCodexHooksKey(content) || hasEnabledFeaturesTableCodexHooksKey(content) || hasEnabledFeaturesInlineTableCodexHooksKey(content);
|
|
143
127
|
}
|
|
144
128
|
function hasConflictingTopLevelKey(content, key) {
|
|
145
129
|
const sanitized = sanitizeTomlScanContent(content);
|
|
@@ -148,7 +132,10 @@ function hasConflictingTopLevelKey(content, key) {
|
|
|
148
132
|
return keyPattern.test(sanitized);
|
|
149
133
|
}
|
|
150
134
|
function hasEnabledTopLevelCodexHooksKey(content) {
|
|
151
|
-
return /^\s*codex_hooks\s*=\s*true\b/m.test(
|
|
135
|
+
return /^\s*codex_hooks\s*=\s*true\b/m.test(sanitizeTomlScanContent(content));
|
|
136
|
+
}
|
|
137
|
+
function hasEnabledTopLevelFeaturesHooksKey(content) {
|
|
138
|
+
return /^\s*features\.hooks\s*=\s*true\b/m.test(
|
|
152
139
|
sanitizeTomlScanContent(content)
|
|
153
140
|
);
|
|
154
141
|
}
|
|
@@ -164,7 +151,9 @@ function hasConflictingFeaturesTableKey(content, key) {
|
|
|
164
151
|
continue;
|
|
165
152
|
}
|
|
166
153
|
if (!inFeaturesTable) continue;
|
|
167
|
-
if (new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=`).test(
|
|
154
|
+
if (new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=`).test(
|
|
155
|
+
line
|
|
156
|
+
)) {
|
|
168
157
|
return true;
|
|
169
158
|
}
|
|
170
159
|
}
|
|
@@ -188,6 +177,23 @@ function hasEnabledFeaturesTableCodexHooksKey(content) {
|
|
|
188
177
|
}
|
|
189
178
|
return false;
|
|
190
179
|
}
|
|
180
|
+
function hasEnabledFeaturesTableHooksKey(content) {
|
|
181
|
+
const lines = sanitizeTomlScanContent(content).split("\n");
|
|
182
|
+
let inFeaturesTable = false;
|
|
183
|
+
for (const rawLine of lines) {
|
|
184
|
+
const line = rawLine.trim();
|
|
185
|
+
if (!line || line.startsWith("#")) continue;
|
|
186
|
+
const tableMatch = line.match(/^\[([^\]]+)\](?:\s*#.*)?$/);
|
|
187
|
+
if (tableMatch) {
|
|
188
|
+
inFeaturesTable = tableMatch[1]?.trim() === "features";
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (inFeaturesTable && /^hooks\s*=\s*true\b/.test(line)) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
191
197
|
function hasConflictingFeaturesInlineTableKey(content, key) {
|
|
192
198
|
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
193
199
|
const lines = sanitizeTomlScanContent(content).split("\n");
|
|
@@ -213,44 +219,46 @@ function hasEnabledFeaturesInlineTableCodexHooksKey(content) {
|
|
|
213
219
|
}
|
|
214
220
|
return false;
|
|
215
221
|
}
|
|
222
|
+
function hasEnabledFeaturesInlineTableHooksKey(content) {
|
|
223
|
+
const lines = sanitizeTomlScanContent(content).split("\n");
|
|
224
|
+
return lines.some((rawLine) => {
|
|
225
|
+
const line = rawLine.trim();
|
|
226
|
+
return !!line && !line.startsWith("#") && /^features\s*=\s*\{/.test(line) && /\bhooks\s*=\s*true\b/.test(line);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
216
229
|
async function hasLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
|
|
217
230
|
if (!await fs.pathExists(filePath)) return false;
|
|
218
231
|
const content = await fs.readFile(filePath, "utf-8");
|
|
219
232
|
return content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN) && content.includes(LEE_SPEC_KIT_CODEX_BOOTSTRAP_END) || contentIncludesRequiredBootstrap(content);
|
|
220
233
|
}
|
|
221
234
|
async function upsertLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
|
|
222
|
-
const block = renderManagedBlock();
|
|
223
|
-
const segment = renderManagedSegment();
|
|
224
235
|
await fs.ensureDir(path.dirname(filePath));
|
|
225
236
|
const exists = await fs.pathExists(filePath);
|
|
226
237
|
if (!exists) {
|
|
227
|
-
await fs.writeFile(filePath,
|
|
238
|
+
await fs.writeFile(filePath, insertManagedFeaturesBlock(""), "utf-8");
|
|
228
239
|
return { changed: true, action: "created", filePath };
|
|
229
240
|
}
|
|
230
241
|
const current = await fs.readFile(filePath, "utf-8");
|
|
231
242
|
const externalContent = stripManagedBlock(current);
|
|
232
|
-
|
|
233
|
-
|
|
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
|
+
if (contentIncludesRequiredBootstrap(externalContent)) {
|
|
244
|
+
if (externalContent === current) {
|
|
243
245
|
return { changed: false, action: "noop", filePath };
|
|
244
246
|
}
|
|
245
|
-
await fs.writeFile(filePath,
|
|
247
|
+
await fs.writeFile(filePath, externalContent.trimEnd() + "\n", "utf-8");
|
|
246
248
|
return { changed: true, action: "updated", filePath };
|
|
247
249
|
}
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
+
if (hasConflictingTopLevelKey(externalContent, "features.hooks") || hasConflictingFeaturesTableKey(externalContent, "hooks") || hasConflictingFeaturesInlineTableKey(externalContent, "hooks") || hasConflictingTopLevelKey(externalContent, "codex_hooks") || hasConflictingTopLevelKey(externalContent, "features.codex_hooks") || hasConflictingFeaturesTableKey(externalContent, "codex_hooks") || hasConflictingFeaturesInlineTableKey(externalContent, "codex_hooks")) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Codex config already defines hooks outside lee-spec-kit managed block: ${filePath}`
|
|
253
|
+
);
|
|
250
254
|
}
|
|
251
|
-
const next =
|
|
255
|
+
const next = insertManagedFeaturesBlock(externalContent);
|
|
252
256
|
await fs.writeFile(filePath, next, "utf-8");
|
|
253
|
-
return {
|
|
257
|
+
return {
|
|
258
|
+
changed: true,
|
|
259
|
+
action: externalContent === current ? "appended" : "updated",
|
|
260
|
+
filePath
|
|
261
|
+
};
|
|
254
262
|
}
|
|
255
263
|
async function removeLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
|
|
256
264
|
if (!await fs.pathExists(filePath)) {
|
|
@@ -271,5 +279,5 @@ async function removeLeeSpecKitCodexBootstrap(filePath = getCodexConfigPath()) {
|
|
|
271
279
|
}
|
|
272
280
|
|
|
273
281
|
export { LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN, LEE_SPEC_KIT_CODEX_BOOTSTRAP_END, getCodexConfigPath, getCodexHome, hasLeeSpecKitCodexBootstrap, removeLeeSpecKitCodexBootstrap, upsertLeeSpecKitCodexBootstrap };
|
|
274
|
-
//# sourceMappingURL=chunk-
|
|
275
|
-
//# sourceMappingURL=chunk-
|
|
282
|
+
//# sourceMappingURL=chunk-3AFCPGGS.js.map
|
|
283
|
+
//# sourceMappingURL=chunk-3AFCPGGS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/codex/bootstrap.ts"],"names":[],"mappings":"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","file":"chunk-3AFCPGGS.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 = '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 sanitizeTomlScanContent(content: string): string {\n let result = '';\n let index = 0;\n let state: 'normal' | 'basic' | 'literal' | 'multibasic' | 'multiliteral' =\n '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 findFeaturesTableHeaderEnd(content: string): number {\n const sanitized = sanitizeTomlScanContent(content);\n const match = /^\\s*\\[features\\](?:\\s*#.*)?(?:\\r?\\n|$)/m.exec(sanitized);\n return match?.index === undefined ? -1 : match.index + match[0].length;\n}\n\nfunction insertManagedFeaturesBlock(content: string): string {\n const segment = renderManagedSegment();\n const featuresHeaderEnd = findFeaturesTableHeaderEnd(content);\n if (featuresHeaderEnd !== -1) {\n return `${content.slice(0, featuresHeaderEnd)}${segment}\\n${content.slice(featuresHeaderEnd)}`;\n }\n\n const prefix = content.trimEnd();\n return `${prefix}${prefix ? '\\n\\n' : ''}[features]\\n${segment}\\n`;\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 hasEnabledTopLevelFeaturesHooksKey(content) ||\n hasEnabledFeaturesTableHooksKey(content) ||\n hasEnabledFeaturesInlineTableHooksKey(content) ||\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(sanitizeTomlScanContent(content));\n}\n\nfunction hasEnabledTopLevelFeaturesHooksKey(content: string): boolean {\n return /^\\s*features\\.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 (\n new RegExp(`^${key.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s*=`).test(\n line\n )\n ) {\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 hasEnabledFeaturesTableHooksKey(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 && /^hooks\\s*=\\s*true\\b/.test(line)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction hasConflictingFeaturesInlineTableKey(\n content: string,\n key: string\n): 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\nfunction hasEnabledFeaturesInlineTableHooksKey(content: string): boolean {\n const lines = sanitizeTomlScanContent(content).split('\\n');\n return lines.some((rawLine) => {\n const line = rawLine.trim();\n return (\n !!line &&\n !line.startsWith('#') &&\n /^features\\s*=\\s*\\{/.test(line) &&\n /\\bhooks\\s*=\\s*true\\b/.test(line)\n );\n });\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 await fs.ensureDir(path.dirname(filePath));\n\n const exists = await fs.pathExists(filePath);\n if (!exists) {\n await fs.writeFile(filePath, insertManagedFeaturesBlock(''), '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\n if (contentIncludesRequiredBootstrap(externalContent)) {\n if (externalContent === current) {\n return { changed: false, action: 'noop', filePath };\n }\n await fs.writeFile(filePath, externalContent.trimEnd() + '\\n', 'utf-8');\n return { changed: true, action: 'updated', filePath };\n }\n\n if (\n hasConflictingTopLevelKey(externalContent, 'features.hooks') ||\n hasConflictingFeaturesTableKey(externalContent, 'hooks') ||\n hasConflictingFeaturesInlineTableKey(externalContent, 'hooks') ||\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 hooks outside lee-spec-kit managed block: ${filePath}`\n );\n }\n\n const next = insertManagedFeaturesBlock(externalContent);\n\n await fs.writeFile(filePath, next, 'utf-8');\n return {\n changed: true,\n action: externalContent === current ? 'appended' : 'updated',\n filePath,\n };\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"]}
|
|
@@ -12,12 +12,14 @@ var MANAGED_HOOK_FILENAMES = [
|
|
|
12
12
|
"pre_tool_use_policy.mjs",
|
|
13
13
|
"stop_workflow_audit.mjs"
|
|
14
14
|
];
|
|
15
|
-
function getHookScriptContent(fileName) {
|
|
15
|
+
function getHookScriptContent(fileName, repoRoot, workflowRoot) {
|
|
16
16
|
switch (fileName) {
|
|
17
17
|
case "_lee_spec_kit_hook_utils.mjs":
|
|
18
18
|
return `#!/usr/bin/env node
|
|
19
19
|
import fs from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
20
21
|
import { spawnSync } from 'node:child_process';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
21
23
|
|
|
22
24
|
export function readHookInput() {
|
|
23
25
|
try {
|
|
@@ -38,6 +40,18 @@ export function readHookInput() {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const CLI_ENTRYPOINT = ${JSON.stringify(getInstalledCliEntrypoint())};
|
|
43
|
+
const HOOK_REPO_ROOT = path.resolve(
|
|
44
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
45
|
+
'..',
|
|
46
|
+
'..'
|
|
47
|
+
);
|
|
48
|
+
const WORKFLOW_ROOT_RELATIVE = ${JSON.stringify(
|
|
49
|
+
path.relative(path.resolve(repoRoot), path.resolve(workflowRoot))
|
|
50
|
+
)};
|
|
51
|
+
|
|
52
|
+
export function getWorkflowCwd() {
|
|
53
|
+
return path.resolve(HOOK_REPO_ROOT, WORKFLOW_ROOT_RELATIVE);
|
|
54
|
+
}
|
|
41
55
|
|
|
42
56
|
export function runLeeSpecKit(args, cwd = process.cwd()) {
|
|
43
57
|
return spawnSync(process.execPath, [CLI_ENTRYPOINT, ...args], {
|
|
@@ -115,7 +129,7 @@ export function printBlock(reason) {
|
|
|
115
129
|
`;
|
|
116
130
|
case "session_start_lee_spec_kit.mjs":
|
|
117
131
|
return `#!/usr/bin/env node
|
|
118
|
-
import { printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
132
|
+
import { getWorkflowCwd, printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
119
133
|
|
|
120
134
|
// Equivalent CLI probe: npx lee-spec-kit detect --json
|
|
121
135
|
const inputResult = readHookInput();
|
|
@@ -123,7 +137,7 @@ if (!inputResult.ok) {
|
|
|
123
137
|
process.exit(0);
|
|
124
138
|
}
|
|
125
139
|
const input = inputResult.value;
|
|
126
|
-
const cwd =
|
|
140
|
+
const cwd = getWorkflowCwd();
|
|
127
141
|
const detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);
|
|
128
142
|
const detected = detectedResult.ok ? detectedResult.data : null;
|
|
129
143
|
|
|
@@ -167,14 +181,14 @@ if (detected?.status === 'ok' && detected?.isLeeSpecKitProject === true) {
|
|
|
167
181
|
`;
|
|
168
182
|
case "user_prompt_submit_lee_spec_kit.mjs":
|
|
169
183
|
return `#!/usr/bin/env node
|
|
170
|
-
import { printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
184
|
+
import { getWorkflowCwd, printAdditionalContext, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
171
185
|
|
|
172
186
|
const inputResult = readHookInput();
|
|
173
187
|
if (!inputResult.ok) {
|
|
174
188
|
process.exit(0);
|
|
175
189
|
}
|
|
176
190
|
const input = inputResult.value;
|
|
177
|
-
const cwd =
|
|
191
|
+
const cwd = getWorkflowCwd();
|
|
178
192
|
const detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);
|
|
179
193
|
const detected = detectedResult.ok ? detectedResult.data : null;
|
|
180
194
|
|
|
@@ -213,7 +227,7 @@ if (detected?.status === 'ok' && detected?.isLeeSpecKitProject === true) {
|
|
|
213
227
|
`;
|
|
214
228
|
case "pre_tool_use_policy.mjs":
|
|
215
229
|
return `#!/usr/bin/env node
|
|
216
|
-
import { printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
230
|
+
import { getWorkflowCwd, printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
217
231
|
import fs from 'node:fs';
|
|
218
232
|
import path from 'node:path';
|
|
219
233
|
|
|
@@ -232,6 +246,7 @@ if (!inputResult.ok) {
|
|
|
232
246
|
}
|
|
233
247
|
const input = inputResult.value;
|
|
234
248
|
const cwd = typeof input.cwd === 'string' && input.cwd ? input.cwd : process.cwd();
|
|
249
|
+
const workflowCwd = getWorkflowCwd();
|
|
235
250
|
const command = String(input?.tool_input?.command || '').trim();
|
|
236
251
|
|
|
237
252
|
function tokenizeShellCommand(value) {
|
|
@@ -873,7 +888,7 @@ if (hasUnsupportedGitTarget || hasGitTargetEnvOverride) {
|
|
|
873
888
|
process.exit(0);
|
|
874
889
|
}
|
|
875
890
|
|
|
876
|
-
const detectedResult = runLeeSpecKitJson(['detect', '--json'],
|
|
891
|
+
const detectedResult = runLeeSpecKitJson(['detect', '--json'], workflowCwd);
|
|
877
892
|
if (!detectedResult.ok) {
|
|
878
893
|
printBlock('lee-spec-kit detection failed inside the Codex hook. Fix the local CLI or hook setup before continuing.');
|
|
879
894
|
process.exit(0);
|
|
@@ -921,7 +936,7 @@ if (stageBoundAction || isPotentialMergeCleanupCommand || hasUnsupportedShellWra
|
|
|
921
936
|
const stageArgs = commandFeatureRef
|
|
922
937
|
? ['workflow-stage', commandFeatureRef, '--json']
|
|
923
938
|
: ['workflow-stage', '--json'];
|
|
924
|
-
const stageResult = runLeeSpecKitJson(stageArgs,
|
|
939
|
+
const stageResult = runLeeSpecKitJson(stageArgs, workflowCwd);
|
|
925
940
|
if (!stageResult.ok) {
|
|
926
941
|
printBlock('lee-spec-kit workflow-stage failed inside the Codex hook. Resolve the workflow stage before running this stage-bound command.');
|
|
927
942
|
process.exit(0);
|
|
@@ -972,7 +987,7 @@ if (isGitCommit) {
|
|
|
972
987
|
if (commitMessage) {
|
|
973
988
|
commitAuditArgs.push('--message', commitMessage);
|
|
974
989
|
}
|
|
975
|
-
const commitAuditResult = runLeeSpecKitJson(commitAuditArgs,
|
|
990
|
+
const commitAuditResult = runLeeSpecKitJson(commitAuditArgs, workflowCwd);
|
|
976
991
|
if (!commitAuditResult.ok) {
|
|
977
992
|
printBlock('lee-spec-kit commit-audit failed inside the Codex hook. Resolve the docs guardrail failure before committing.');
|
|
978
993
|
process.exit(0);
|
|
@@ -992,7 +1007,7 @@ if (isGitCommit) {
|
|
|
992
1007
|
}
|
|
993
1008
|
}
|
|
994
1009
|
|
|
995
|
-
const auditResult = runLeeSpecKitJson(['workflow-audit', '--json'],
|
|
1010
|
+
const auditResult = runLeeSpecKitJson(['workflow-audit', '--json'], workflowCwd);
|
|
996
1011
|
if (!auditResult.ok) {
|
|
997
1012
|
printBlock('lee-spec-kit workflow-audit failed inside the Codex hook. Resolve the docs sync guardrail failure before continuing.');
|
|
998
1013
|
process.exit(0);
|
|
@@ -1008,7 +1023,7 @@ if (!(audit?.status === 'ok' || audit?.status === 'skipped')) {
|
|
|
1008
1023
|
`;
|
|
1009
1024
|
case "stop_workflow_audit.mjs":
|
|
1010
1025
|
return `#!/usr/bin/env node
|
|
1011
|
-
import { printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
1026
|
+
import { getWorkflowCwd, printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
1012
1027
|
|
|
1013
1028
|
// Equivalent CLI probe: npx lee-spec-kit workflow-audit --json
|
|
1014
1029
|
const inputResult = readHookInput();
|
|
@@ -1022,7 +1037,7 @@ if (input?.stop_hook_active === true) {
|
|
|
1022
1037
|
process.exit(0);
|
|
1023
1038
|
}
|
|
1024
1039
|
|
|
1025
|
-
const cwd =
|
|
1040
|
+
const cwd = getWorkflowCwd();
|
|
1026
1041
|
const detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);
|
|
1027
1042
|
if (!detectedResult.ok) {
|
|
1028
1043
|
printBlock('lee-spec-kit detection failed inside the stop hook. Resolve the local CLI or hook setup before stopping.');
|
|
@@ -1065,7 +1080,9 @@ function escapeRegExp(value) {
|
|
|
1065
1080
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1066
1081
|
}
|
|
1067
1082
|
function getPortableHookCommandSuffix(fileName) {
|
|
1068
|
-
const relativeHookPath = normalizePathSlashes(
|
|
1083
|
+
const relativeHookPath = normalizePathSlashes(
|
|
1084
|
+
getManagedHookRelativePath(fileName)
|
|
1085
|
+
);
|
|
1069
1086
|
const loaderSource = [
|
|
1070
1087
|
"(async () => {",
|
|
1071
1088
|
" const fs = require('node:fs');",
|
|
@@ -1095,7 +1112,9 @@ function getPortableHookCommandSuffix(fileName) {
|
|
|
1095
1112
|
function isManagedCommand(command) {
|
|
1096
1113
|
const normalized = normalizePathSlashes(command).trim();
|
|
1097
1114
|
return MANAGED_HOOK_FILENAMES.some((fileName) => {
|
|
1098
|
-
const currentCommand = normalizePathSlashes(
|
|
1115
|
+
const currentCommand = normalizePathSlashes(
|
|
1116
|
+
toPortableHookCommand(fileName)
|
|
1117
|
+
).trim();
|
|
1099
1118
|
const portableSuffix = normalizePathSlashes(
|
|
1100
1119
|
getPortableHookCommandSuffix(fileName)
|
|
1101
1120
|
).trim();
|
|
@@ -1118,7 +1137,7 @@ function getManagedHooksConfig() {
|
|
|
1118
1137
|
return {
|
|
1119
1138
|
SessionStart: [
|
|
1120
1139
|
{
|
|
1121
|
-
matcher: "startup|resume",
|
|
1140
|
+
matcher: "startup|resume|clear|compact",
|
|
1122
1141
|
hooks: [
|
|
1123
1142
|
{
|
|
1124
1143
|
type: "command",
|
|
@@ -1203,7 +1222,12 @@ function removeManagedGroups(current) {
|
|
|
1203
1222
|
const nextHooks = {
|
|
1204
1223
|
...current.hooks && typeof current.hooks === "object" ? current.hooks : {}
|
|
1205
1224
|
};
|
|
1206
|
-
for (const eventName of [
|
|
1225
|
+
for (const eventName of [
|
|
1226
|
+
"SessionStart",
|
|
1227
|
+
"UserPromptSubmit",
|
|
1228
|
+
"PreToolUse",
|
|
1229
|
+
"Stop"
|
|
1230
|
+
]) {
|
|
1207
1231
|
const pruned = pruneManagedGroups(
|
|
1208
1232
|
Array.isArray(nextHooks[eventName]) ? nextHooks[eventName] : void 0
|
|
1209
1233
|
);
|
|
@@ -1230,16 +1254,20 @@ function getRepoHooksDir(repoRoot = process.cwd()) {
|
|
|
1230
1254
|
function getRepoHooksConfigPath(repoRoot = process.cwd()) {
|
|
1231
1255
|
return path.join(getRepoCodexDir(repoRoot), "hooks.json");
|
|
1232
1256
|
}
|
|
1233
|
-
async function upsertLeeSpecKitCodexHooks(repoRoot = process.cwd()) {
|
|
1257
|
+
async function upsertLeeSpecKitCodexHooks(repoRoot = process.cwd(), workflowRoot = repoRoot) {
|
|
1234
1258
|
const hooksDir = getRepoHooksDir(repoRoot);
|
|
1235
1259
|
const hooksJsonPath = getRepoHooksConfigPath(repoRoot);
|
|
1236
1260
|
await fs.ensureDir(hooksDir);
|
|
1237
1261
|
for (const fileName of MANAGED_HOOK_FILENAMES) {
|
|
1238
1262
|
const targetPath = path.join(hooksDir, fileName);
|
|
1239
|
-
await fs.writeFile(
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1263
|
+
await fs.writeFile(
|
|
1264
|
+
targetPath,
|
|
1265
|
+
getHookScriptContent(fileName, repoRoot, workflowRoot),
|
|
1266
|
+
{
|
|
1267
|
+
encoding: "utf-8",
|
|
1268
|
+
mode: 493
|
|
1269
|
+
}
|
|
1270
|
+
);
|
|
1243
1271
|
}
|
|
1244
1272
|
const managedHooks = getManagedHooksConfig();
|
|
1245
1273
|
const exists = await fs.pathExists(hooksJsonPath);
|
|
@@ -1286,5 +1314,5 @@ async function removeLeeSpecKitCodexHooks(repoRoot = process.cwd()) {
|
|
|
1286
1314
|
}
|
|
1287
1315
|
|
|
1288
1316
|
export { getRepoCodexDir, getRepoHooksConfigPath, getRepoHooksDir, removeLeeSpecKitCodexHooks, resolveCodexHooksRepoRoot, upsertLeeSpecKitCodexHooks };
|
|
1289
|
-
//# sourceMappingURL=hooks-
|
|
1290
|
-
//# sourceMappingURL=hooks-
|
|
1317
|
+
//# sourceMappingURL=hooks-373Z6JG2.js.map
|
|
1318
|
+
//# sourceMappingURL=hooks-373Z6JG2.js.map
|