lee-spec-kit 0.8.6 → 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/chunk-7V7RMGEU.js +0 -0
- package/dist/chunk-GR7JQBWF.js +0 -0
- package/dist/{hooks-43P4YKHY.js → hooks-373Z6JG2.js} +158 -30
- package/dist/hooks-373Z6JG2.js.map +1 -0
- package/dist/index.js +135 -72
- package/dist/index.js.map +1 -1
- package/package.json +15 -13
- package/templates/en/common/agents/agents.md +1 -0
- package/templates/ko/common/agents/agents.md +1 -0
- package/dist/chunk-LYFRLOFQ.js.map +0 -1
- package/dist/hooks-43P4YKHY.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"]}
|
package/dist/chunk-7V7RMGEU.js
CHANGED
|
File without changes
|
package/dist/chunk-GR7JQBWF.js
CHANGED
|
File without changes
|
|
@@ -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) {
|
|
@@ -660,6 +675,88 @@ function normalizeCommandText(value) {
|
|
|
660
675
|
return String(value || '').replace(/[ \\t\\r\\n]+/g, ' ').trim();
|
|
661
676
|
}
|
|
662
677
|
|
|
678
|
+
function extractLeeSpecKitFeatureRef(value) {
|
|
679
|
+
const tokens = tokenizeShellCommand(value);
|
|
680
|
+
const cliIndex = tokens.findIndex((token) => /(?:^|[\\\\/])lee-spec-kit(?:\\.cmd|\\.exe)?$/i.test(token));
|
|
681
|
+
if (cliIndex === -1) return null;
|
|
682
|
+
for (let index = cliIndex + 1; index < tokens.length; index += 1) {
|
|
683
|
+
const token = tokens[index];
|
|
684
|
+
if (!token || token === 'npx' || token === '--yes' || token === '-y') continue;
|
|
685
|
+
if (token === 'github' && tokens[index + 1] === 'issue') return tokens[index + 2] || null;
|
|
686
|
+
if (token === 'github' && tokens[index + 1] === 'pr') return tokens[index + 2] || null;
|
|
687
|
+
if (token === 'workflow-stage') {
|
|
688
|
+
const candidate = tokens[index + 1] || null;
|
|
689
|
+
return candidate && !candidate.startsWith('-') ? candidate : null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function extractBranchCreateTarget(value) {
|
|
696
|
+
const tokens = tokenizeShellCommand(unwrapShellCommand(value));
|
|
697
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
698
|
+
const token = tokens[index] || '';
|
|
699
|
+
if (token === '-b' || token === '-c' || token === '--create') {
|
|
700
|
+
const candidate = tokens[index + 1] || '';
|
|
701
|
+
if (candidate && !candidate.startsWith('-')) return candidate;
|
|
702
|
+
}
|
|
703
|
+
if (token.startsWith('-b') && token.length > 2) return token.slice(2);
|
|
704
|
+
if (token.startsWith('-c') && token.length > 2) return token.slice(2);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const worktreeAddIndex = tokens.findIndex((token, index) => {
|
|
708
|
+
return token === 'add' && index > 0 && tokens.slice(0, index).some((item) => normalizeExecutableToken(item) === 'git' || item === 'worktree');
|
|
709
|
+
});
|
|
710
|
+
if (worktreeAddIndex !== -1) {
|
|
711
|
+
const nonOptionArgs = [];
|
|
712
|
+
for (let index = worktreeAddIndex + 1; index < tokens.length; index += 1) {
|
|
713
|
+
const token = tokens[index];
|
|
714
|
+
if (!token || token.startsWith('-')) continue;
|
|
715
|
+
nonOptionArgs.push(token);
|
|
716
|
+
}
|
|
717
|
+
return nonOptionArgs.length >= 2 ? nonOptionArgs[1] : null;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const branchNameMatch = String(value).match(/\\bfeat\\/[A-Za-z0-9][A-Za-z0-9._/-]*/);
|
|
721
|
+
return branchNameMatch?.[0] || null;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function escapeRegExp(value) {
|
|
725
|
+
return String(value || '').replace(/[\\^$.*+?()[\\]{}|]/g, '\\\\$&');
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function readFeatureRefFromTasksBranch(docsDir, branchName) {
|
|
729
|
+
if (!docsDir || !branchName) return null;
|
|
730
|
+
const featuresRoot = path.join(docsDir, 'features');
|
|
731
|
+
const stack = [featuresRoot];
|
|
732
|
+
while (stack.length > 0) {
|
|
733
|
+
const current = stack.pop();
|
|
734
|
+
let entries = [];
|
|
735
|
+
try {
|
|
736
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
737
|
+
} catch {
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
for (const entry of entries) {
|
|
741
|
+
const entryPath = path.join(current, entry.name);
|
|
742
|
+
if (entry.isDirectory()) {
|
|
743
|
+
stack.push(entryPath);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (entry.name !== 'tasks.md') continue;
|
|
747
|
+
try {
|
|
748
|
+
const content = fs.readFileSync(entryPath, 'utf8');
|
|
749
|
+
if (new RegExp('^-\\\\s+\\\\*\\\\*(?:Branch|\uBE0C\uB79C\uCE58)\\\\*\\\\*:\\\\s+\`?' + escapeRegExp(branchName) + '\`?\\\\s*$', 'm').test(content)) {
|
|
750
|
+
return path.basename(path.dirname(entryPath));
|
|
751
|
+
}
|
|
752
|
+
} catch {
|
|
753
|
+
// Ignore unreadable feature docs and keep scanning.
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
|
|
663
760
|
function hasUnsupportedGitTargetOptions(value) {
|
|
664
761
|
const unwrappedValue = unwrapShellCommand(value);
|
|
665
762
|
const tokens = tokenizeShellCommand(unwrappedValue);
|
|
@@ -786,17 +883,12 @@ if (isAlwaysBlockedGhOperation) {
|
|
|
786
883
|
process.exit(0);
|
|
787
884
|
}
|
|
788
885
|
|
|
789
|
-
if (hasUnsupportedShellWrappedDangerousCommand) {
|
|
790
|
-
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.');
|
|
791
|
-
process.exit(0);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
886
|
if (hasUnsupportedGitTarget || hasGitTargetEnvOverride) {
|
|
795
887
|
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.');
|
|
796
888
|
process.exit(0);
|
|
797
889
|
}
|
|
798
890
|
|
|
799
|
-
const detectedResult = runLeeSpecKitJson(['detect', '--json'],
|
|
891
|
+
const detectedResult = runLeeSpecKitJson(['detect', '--json'], workflowCwd);
|
|
800
892
|
if (!detectedResult.ok) {
|
|
801
893
|
printBlock('lee-spec-kit detection failed inside the Codex hook. Fix the local CLI or hook setup before continuing.');
|
|
802
894
|
process.exit(0);
|
|
@@ -833,8 +925,18 @@ const isPotentialMergeCleanupCommand =
|
|
|
833
925
|
command.includes('branch -D') ||
|
|
834
926
|
command.includes('push origin --delete')
|
|
835
927
|
);
|
|
836
|
-
|
|
837
|
-
|
|
928
|
+
const commandFeatureRef =
|
|
929
|
+
extractLeeSpecKitFeatureRef(command) ||
|
|
930
|
+
readFeatureRefFromTasksBranch(docsDir, extractBranchCreateTarget(command));
|
|
931
|
+
if (hasUnsupportedShellWrappedDangerousCommand && !commandFeatureRef) {
|
|
932
|
+
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.');
|
|
933
|
+
process.exit(0);
|
|
934
|
+
}
|
|
935
|
+
if (stageBoundAction || isPotentialMergeCleanupCommand || hasUnsupportedShellWrappedDangerousCommand) {
|
|
936
|
+
const stageArgs = commandFeatureRef
|
|
937
|
+
? ['workflow-stage', commandFeatureRef, '--json']
|
|
938
|
+
: ['workflow-stage', '--json'];
|
|
939
|
+
const stageResult = runLeeSpecKitJson(stageArgs, workflowCwd);
|
|
838
940
|
if (!stageResult.ok) {
|
|
839
941
|
printBlock('lee-spec-kit workflow-stage failed inside the Codex hook. Resolve the workflow stage before running this stage-bound command.');
|
|
840
942
|
process.exit(0);
|
|
@@ -844,7 +946,17 @@ if (stageBoundAction || isPotentialMergeCleanupCommand) {
|
|
|
844
946
|
printBlock('Resolve feature selection and workflow stage before running this stage-bound command.');
|
|
845
947
|
process.exit(0);
|
|
846
948
|
}
|
|
847
|
-
|
|
949
|
+
const isExactNextActionCommand =
|
|
950
|
+
normalizeCommandText(stage?.nextAction?.command) === normalizeCommandText(command);
|
|
951
|
+
if (hasUnsupportedShellWrappedDangerousCommand && !isExactNextActionCommand) {
|
|
952
|
+
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.');
|
|
953
|
+
process.exit(0);
|
|
954
|
+
}
|
|
955
|
+
if (
|
|
956
|
+
stageBoundAction &&
|
|
957
|
+
stage?.nextAction?.category !== stageBoundAction &&
|
|
958
|
+
!isExactNextActionCommand
|
|
959
|
+
) {
|
|
848
960
|
printBlock(
|
|
849
961
|
\`Current workflow stage is \${stage?.stage || 'unknown'} and only \${stage?.nextAction?.category || 'the current nextAction'} is allowed next. Do not jump ahead to \${stageBoundAction}.\`
|
|
850
962
|
);
|
|
@@ -855,10 +967,13 @@ if (stageBoundAction || isPotentialMergeCleanupCommand) {
|
|
|
855
967
|
const isExactMergeCleanupCommand =
|
|
856
968
|
stage?.nextAction?.category === 'merge_cleanup' &&
|
|
857
969
|
normalizeCommandText(stage?.nextAction?.command) === normalizeCommandText(command);
|
|
970
|
+
const isExactNextActionCommand =
|
|
971
|
+
normalizeCommandText(stage?.nextAction?.command) === normalizeCommandText(command);
|
|
858
972
|
|
|
859
973
|
if (
|
|
860
974
|
path.resolve(gitCommandCwd) !== path.resolve(cwd) &&
|
|
861
975
|
!isGitCommit &&
|
|
976
|
+
!isExactNextActionCommand &&
|
|
862
977
|
!isExactMergeCleanupCommand &&
|
|
863
978
|
!(stageBoundAction === 'branch_create' && (isGitCreateBranch || isGitWorktreeAdd))
|
|
864
979
|
) {
|
|
@@ -872,7 +987,7 @@ if (isGitCommit) {
|
|
|
872
987
|
if (commitMessage) {
|
|
873
988
|
commitAuditArgs.push('--message', commitMessage);
|
|
874
989
|
}
|
|
875
|
-
const commitAuditResult = runLeeSpecKitJson(commitAuditArgs,
|
|
990
|
+
const commitAuditResult = runLeeSpecKitJson(commitAuditArgs, workflowCwd);
|
|
876
991
|
if (!commitAuditResult.ok) {
|
|
877
992
|
printBlock('lee-spec-kit commit-audit failed inside the Codex hook. Resolve the docs guardrail failure before committing.');
|
|
878
993
|
process.exit(0);
|
|
@@ -892,7 +1007,7 @@ if (isGitCommit) {
|
|
|
892
1007
|
}
|
|
893
1008
|
}
|
|
894
1009
|
|
|
895
|
-
const auditResult = runLeeSpecKitJson(['workflow-audit', '--json'],
|
|
1010
|
+
const auditResult = runLeeSpecKitJson(['workflow-audit', '--json'], workflowCwd);
|
|
896
1011
|
if (!auditResult.ok) {
|
|
897
1012
|
printBlock('lee-spec-kit workflow-audit failed inside the Codex hook. Resolve the docs sync guardrail failure before continuing.');
|
|
898
1013
|
process.exit(0);
|
|
@@ -908,7 +1023,7 @@ if (!(audit?.status === 'ok' || audit?.status === 'skipped')) {
|
|
|
908
1023
|
`;
|
|
909
1024
|
case "stop_workflow_audit.mjs":
|
|
910
1025
|
return `#!/usr/bin/env node
|
|
911
|
-
import { printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
1026
|
+
import { getWorkflowCwd, printBlock, readHookInput, runLeeSpecKitJson } from './_lee_spec_kit_hook_utils.mjs';
|
|
912
1027
|
|
|
913
1028
|
// Equivalent CLI probe: npx lee-spec-kit workflow-audit --json
|
|
914
1029
|
const inputResult = readHookInput();
|
|
@@ -922,7 +1037,7 @@ if (input?.stop_hook_active === true) {
|
|
|
922
1037
|
process.exit(0);
|
|
923
1038
|
}
|
|
924
1039
|
|
|
925
|
-
const cwd =
|
|
1040
|
+
const cwd = getWorkflowCwd();
|
|
926
1041
|
const detectedResult = runLeeSpecKitJson(['detect', '--json'], cwd);
|
|
927
1042
|
if (!detectedResult.ok) {
|
|
928
1043
|
printBlock('lee-spec-kit detection failed inside the stop hook. Resolve the local CLI or hook setup before stopping.');
|
|
@@ -965,7 +1080,9 @@ function escapeRegExp(value) {
|
|
|
965
1080
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
966
1081
|
}
|
|
967
1082
|
function getPortableHookCommandSuffix(fileName) {
|
|
968
|
-
const relativeHookPath = normalizePathSlashes(
|
|
1083
|
+
const relativeHookPath = normalizePathSlashes(
|
|
1084
|
+
getManagedHookRelativePath(fileName)
|
|
1085
|
+
);
|
|
969
1086
|
const loaderSource = [
|
|
970
1087
|
"(async () => {",
|
|
971
1088
|
" const fs = require('node:fs');",
|
|
@@ -995,7 +1112,9 @@ function getPortableHookCommandSuffix(fileName) {
|
|
|
995
1112
|
function isManagedCommand(command) {
|
|
996
1113
|
const normalized = normalizePathSlashes(command).trim();
|
|
997
1114
|
return MANAGED_HOOK_FILENAMES.some((fileName) => {
|
|
998
|
-
const currentCommand = normalizePathSlashes(
|
|
1115
|
+
const currentCommand = normalizePathSlashes(
|
|
1116
|
+
toPortableHookCommand(fileName)
|
|
1117
|
+
).trim();
|
|
999
1118
|
const portableSuffix = normalizePathSlashes(
|
|
1000
1119
|
getPortableHookCommandSuffix(fileName)
|
|
1001
1120
|
).trim();
|
|
@@ -1018,7 +1137,7 @@ function getManagedHooksConfig() {
|
|
|
1018
1137
|
return {
|
|
1019
1138
|
SessionStart: [
|
|
1020
1139
|
{
|
|
1021
|
-
matcher: "startup|resume",
|
|
1140
|
+
matcher: "startup|resume|clear|compact",
|
|
1022
1141
|
hooks: [
|
|
1023
1142
|
{
|
|
1024
1143
|
type: "command",
|
|
@@ -1103,7 +1222,12 @@ function removeManagedGroups(current) {
|
|
|
1103
1222
|
const nextHooks = {
|
|
1104
1223
|
...current.hooks && typeof current.hooks === "object" ? current.hooks : {}
|
|
1105
1224
|
};
|
|
1106
|
-
for (const eventName of [
|
|
1225
|
+
for (const eventName of [
|
|
1226
|
+
"SessionStart",
|
|
1227
|
+
"UserPromptSubmit",
|
|
1228
|
+
"PreToolUse",
|
|
1229
|
+
"Stop"
|
|
1230
|
+
]) {
|
|
1107
1231
|
const pruned = pruneManagedGroups(
|
|
1108
1232
|
Array.isArray(nextHooks[eventName]) ? nextHooks[eventName] : void 0
|
|
1109
1233
|
);
|
|
@@ -1130,16 +1254,20 @@ function getRepoHooksDir(repoRoot = process.cwd()) {
|
|
|
1130
1254
|
function getRepoHooksConfigPath(repoRoot = process.cwd()) {
|
|
1131
1255
|
return path.join(getRepoCodexDir(repoRoot), "hooks.json");
|
|
1132
1256
|
}
|
|
1133
|
-
async function upsertLeeSpecKitCodexHooks(repoRoot = process.cwd()) {
|
|
1257
|
+
async function upsertLeeSpecKitCodexHooks(repoRoot = process.cwd(), workflowRoot = repoRoot) {
|
|
1134
1258
|
const hooksDir = getRepoHooksDir(repoRoot);
|
|
1135
1259
|
const hooksJsonPath = getRepoHooksConfigPath(repoRoot);
|
|
1136
1260
|
await fs.ensureDir(hooksDir);
|
|
1137
1261
|
for (const fileName of MANAGED_HOOK_FILENAMES) {
|
|
1138
1262
|
const targetPath = path.join(hooksDir, fileName);
|
|
1139
|
-
await fs.writeFile(
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1263
|
+
await fs.writeFile(
|
|
1264
|
+
targetPath,
|
|
1265
|
+
getHookScriptContent(fileName, repoRoot, workflowRoot),
|
|
1266
|
+
{
|
|
1267
|
+
encoding: "utf-8",
|
|
1268
|
+
mode: 493
|
|
1269
|
+
}
|
|
1270
|
+
);
|
|
1143
1271
|
}
|
|
1144
1272
|
const managedHooks = getManagedHooksConfig();
|
|
1145
1273
|
const exists = await fs.pathExists(hooksJsonPath);
|
|
@@ -1186,5 +1314,5 @@ async function removeLeeSpecKitCodexHooks(repoRoot = process.cwd()) {
|
|
|
1186
1314
|
}
|
|
1187
1315
|
|
|
1188
1316
|
export { getRepoCodexDir, getRepoHooksConfigPath, getRepoHooksDir, removeLeeSpecKitCodexHooks, resolveCodexHooksRepoRoot, upsertLeeSpecKitCodexHooks };
|
|
1189
|
-
//# sourceMappingURL=hooks-
|
|
1190
|
-
//# sourceMappingURL=hooks-
|
|
1317
|
+
//# sourceMappingURL=hooks-373Z6JG2.js.map
|
|
1318
|
+
//# sourceMappingURL=hooks-373Z6JG2.js.map
|