speccore 5.71.0 → 5.74.0
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/.agents/skills/spec-ask/SKILL.md +10 -0
- package/.agents/skills/speccore-router/SKILL.md +10 -0
- package/README.md +30 -8
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +8 -1
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/analyze.d.ts +3 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +120 -21
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/ask.d.ts +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +12 -5
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/change.d.ts +5 -0
- package/dist/commands/change.d.ts.map +1 -1
- package/dist/commands/change.js +588 -22
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/code-index.d.ts +18 -0
- package/dist/commands/code-index.d.ts.map +1 -0
- package/dist/commands/code-index.js +126 -0
- package/dist/commands/code-index.js.map +1 -0
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +10 -40
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +133 -17
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doc2spec.js +4 -4
- package/dist/commands/doc2spec.js.map +1 -1
- package/dist/commands/execute.d.ts +2 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +243 -51
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/help.js +17 -7
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +8 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/create.js +38 -6
- package/dist/commands/iteration/create.js.map +1 -1
- package/dist/commands/iteration/split.js +70 -12
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/lifecycle.d.ts.map +1 -1
- package/dist/commands/lifecycle.js +47 -23
- package/dist/commands/lifecycle.js.map +1 -1
- package/dist/commands/plan.d.ts +20 -0
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +54 -39
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/retro.d.ts +9 -0
- package/dist/commands/retro.d.ts.map +1 -1
- package/dist/commands/retro.js +61 -15
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/status-panel.js +33 -6
- package/dist/commands/status-panel.js.map +1 -1
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +25 -1
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/verify.d.ts +22 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +158 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/welcome.d.ts.map +1 -1
- package/dist/commands/welcome.js +5 -9
- package/dist/commands/welcome.js.map +1 -1
- package/dist/core/ai-context-generator.js +2 -2
- package/dist/core/ai-context-generator.js.map +1 -1
- package/dist/core/analyze-engine.d.ts +19 -0
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +225 -13
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +100 -10
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/capabilities.js +3 -3
- package/dist/core/code-index-markdown.d.ts +7 -0
- package/dist/core/code-index-markdown.d.ts.map +1 -0
- package/dist/core/code-index-markdown.js +160 -0
- package/dist/core/code-index-markdown.js.map +1 -0
- package/dist/core/code-scanner.d.ts +58 -3
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +406 -24
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/git-integration.d.ts +10 -0
- package/dist/core/git-integration.d.ts.map +1 -1
- package/dist/core/git-integration.js +99 -2
- package/dist/core/git-integration.js.map +1 -1
- package/dist/core/inbox.d.ts +120 -0
- package/dist/core/inbox.d.ts.map +1 -0
- package/dist/core/inbox.js +377 -0
- package/dist/core/inbox.js.map +1 -0
- package/dist/core/intent-cache.d.ts.map +1 -1
- package/dist/core/intent-cache.js +8 -2
- package/dist/core/intent-cache.js.map +1 -1
- package/dist/core/intent-recognition.d.ts.map +1 -1
- package/dist/core/intent-recognition.js +4 -3
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/next-steps.js +1 -1
- package/dist/core/next-steps.js.map +1 -1
- package/dist/core/plan-html.js +3 -3
- package/dist/core/prompt-builder.d.ts +7 -0
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +52 -9
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/resolver.d.ts +52 -0
- package/dist/core/resolver.d.ts.map +1 -0
- package/dist/core/resolver.js +257 -0
- package/dist/core/resolver.js.map +1 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +46 -14
- package/dist/core/state.js.map +1 -1
- package/dist/core/verify-engine.d.ts +64 -0
- package/dist/core/verify-engine.d.ts.map +1 -0
- package/dist/core/verify-engine.js +560 -0
- package/dist/core/verify-engine.js.map +1 -0
- package/package.json +1 -1
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Commit 消息自动生成
|
|
8
8
|
* - PR 描述自动生成
|
|
9
9
|
* - Git Hook 安装
|
|
10
|
+
* - 保护分支拦截
|
|
10
11
|
*/
|
|
11
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
13
|
exports.createTaskBranch = createTaskBranch;
|
|
@@ -14,6 +15,8 @@ exports.detectDefaultBranch = detectDefaultBranch;
|
|
|
14
15
|
exports.getCurrentTaskMapping = getCurrentTaskMapping;
|
|
15
16
|
exports.generateCommitMessage = generateCommitMessage;
|
|
16
17
|
exports.generatePRDescription = generatePRDescription;
|
|
18
|
+
exports.getProtectedBranches = getProtectedBranches;
|
|
19
|
+
exports.isProtectedBranch = isProtectedBranch;
|
|
17
20
|
exports.installGitHooks = installGitHooks;
|
|
18
21
|
exports.cleanStaleCache = cleanStaleCache;
|
|
19
22
|
const fs_extra_1 = require("fs-extra");
|
|
@@ -147,6 +150,48 @@ function generatePRDescription(taskId, taskName) {
|
|
|
147
150
|
];
|
|
148
151
|
return lines.join('\n');
|
|
149
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* 从 CONSTITUTION.md 读取保护分支列表
|
|
155
|
+
*/
|
|
156
|
+
function getProtectedBranches() {
|
|
157
|
+
try {
|
|
158
|
+
const constitutionPath = (0, path_1.join)('.speccore', 'CONSTITUTION.md');
|
|
159
|
+
if (!(0, fs_extra_1.existsSync)(constitutionPath))
|
|
160
|
+
return [];
|
|
161
|
+
const content = (0, fs_extra_1.readFileSync)(constitutionPath, 'utf-8');
|
|
162
|
+
const match = content.match(/保护分支[::]\s*(.+)/);
|
|
163
|
+
if (!match)
|
|
164
|
+
return [];
|
|
165
|
+
return match[1]
|
|
166
|
+
.split(',')
|
|
167
|
+
.map((s) => s.trim())
|
|
168
|
+
.filter(Boolean);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return [];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 检查分支是否为保护分支
|
|
176
|
+
* 支持精确匹配和通配符(如 release/*)
|
|
177
|
+
*/
|
|
178
|
+
function isProtectedBranch(branchName) {
|
|
179
|
+
const protected_ = getProtectedBranches();
|
|
180
|
+
for (const pattern of protected_) {
|
|
181
|
+
if (pattern.includes('*')) {
|
|
182
|
+
// 通配符匹配: release/* → release/1.0.0
|
|
183
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
|
184
|
+
if (regex.test(branchName))
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// 精确匹配
|
|
189
|
+
if (branchName === pattern)
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
150
195
|
/**
|
|
151
196
|
* 安装 Git Hooks
|
|
152
197
|
*/
|
|
@@ -156,16 +201,68 @@ function installGitHooks() {
|
|
|
156
201
|
throw new Error('Not a Git repository');
|
|
157
202
|
}
|
|
158
203
|
const hooksDir = (0, path_1.join)(gitDir, 'hooks');
|
|
159
|
-
// pre-commit:
|
|
204
|
+
// pre-commit: 保护分支检查 + spec annotations 检查
|
|
160
205
|
const preCommitContent = `#!/bin/sh
|
|
161
206
|
# SpecCore pre-commit hook
|
|
207
|
+
|
|
208
|
+
# ── 1. 保护分支检查 ──
|
|
209
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)
|
|
210
|
+
if [ -n "$CURRENT_BRANCH" ] && [ -f ".speccore/CONSTITUTION.md" ]; then
|
|
211
|
+
PROTECTED=$(grep '保护分支' .speccore/CONSTITUTION.md | sed 's/.*[::]\\s*//' | tr ',' '\\n' | sed 's/^[ \\t]*//')
|
|
212
|
+
for PATTERN in $PROTECTED; do
|
|
213
|
+
# 精确匹配
|
|
214
|
+
if [ "$CURRENT_BRANCH" = "$PATTERN" ]; then
|
|
215
|
+
echo "❌ SpecCore: 保护分支 '$CURRENT_BRANCH' 禁止直接 commit"
|
|
216
|
+
echo " 请切换到 feature/ 分支或使用 PR 合并"
|
|
217
|
+
exit 1
|
|
218
|
+
fi
|
|
219
|
+
# 通配符匹配 (release/* → release/xxx)
|
|
220
|
+
case "$PATTERN" in
|
|
221
|
+
*"*"*)
|
|
222
|
+
REGEX=$(echo "$PATTERN" | sed 's/\\*/.*/')
|
|
223
|
+
if echo "$CURRENT_BRANCH" | grep -qE "^\${REGEX}$"; then
|
|
224
|
+
echo "❌ SpecCore: 保护分支 '$CURRENT_BRANCH' 匹配保护规则 '$PATTERN',禁止直接 commit"
|
|
225
|
+
echo " 请切换到 feature/ 分支或使用 PR 合并"
|
|
226
|
+
exit 1
|
|
227
|
+
fi
|
|
228
|
+
;;
|
|
229
|
+
esac
|
|
230
|
+
done
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# ── 2. Spec annotations 检查 ──
|
|
162
234
|
echo "🔍 SpecCore: Checking @spec annotations..."
|
|
163
235
|
git diff --cached --name-only | grep -q "@spec" && echo "✅ Spec annotations found" || true
|
|
164
236
|
`;
|
|
165
237
|
(0, fs_extra_1.writeFileSync)((0, path_1.join)(hooksDir, 'pre-commit'), preCommitContent, { mode: 0o755 });
|
|
166
|
-
// pre-push:
|
|
238
|
+
// pre-push: 保护分支检查 + validate
|
|
167
239
|
const prePushContent = `#!/bin/sh
|
|
168
240
|
# SpecCore pre-push hook
|
|
241
|
+
|
|
242
|
+
# ── 1. 保护分支检查 ──
|
|
243
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)
|
|
244
|
+
if [ -n "$CURRENT_BRANCH" ] && [ -f ".speccore/CONSTITUTION.md" ]; then
|
|
245
|
+
PROTECTED=$(grep '保护分支' .speccore/CONSTITUTION.md | sed 's/.*[::]\\s*//' | tr ',' '\\n' | sed 's/^[ \\t]*//')
|
|
246
|
+
for PATTERN in $PROTECTED; do
|
|
247
|
+
if [ "$CURRENT_BRANCH" = "$PATTERN" ]; then
|
|
248
|
+
echo "❌ SpecCore: 保护分支 '$CURRENT_BRANCH' 禁止 push"
|
|
249
|
+
echo " 请通过 PR 合并代码"
|
|
250
|
+
exit 1
|
|
251
|
+
fi
|
|
252
|
+
case "$PATTERN" in
|
|
253
|
+
*"*"*)
|
|
254
|
+
REGEX=$(echo "$PATTERN" | sed 's/\\*/.*/')
|
|
255
|
+
if echo "$CURRENT_BRANCH" | grep -qE "^\${REGEX}$"; then
|
|
256
|
+
echo "❌ SpecCore: 保护分支 '$CURRENT_BRANCH' 匹配保护规则 '$PATTERN',禁止 push"
|
|
257
|
+
echo " 请通过 PR 合并代码"
|
|
258
|
+
exit 1
|
|
259
|
+
fi
|
|
260
|
+
;;
|
|
261
|
+
esac
|
|
262
|
+
done
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
# ── 2. Spec 验证 ──
|
|
169
266
|
echo "🔍 SpecCore: Validating specs..."
|
|
170
267
|
speccore validate --warn-only
|
|
171
268
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-integration.js","sourceRoot":"","sources":["../../src/core/git-integration.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"git-integration.js","sourceRoot":"","sources":["../../src/core/git-integration.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAmBH,4CA6BC;AAGD,kDAsCC;AAKD,sDAQC;AAKD,sDAcC;AAKD,sDAmBC;AAKD,oDAcC;AAMD,8CAaC;AAKD,0CA6EC;AA4BD,0CAaC;AAhTD,uCAAmF;AACnF,+BAA4B;AAC5B,iDAAyC;AAEzC,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AAUvD;;GAEG;AACH,SAAgB,gBAAgB,CAAC,MAAc,EAAE,QAAgB,EAAE,UAAmB,EAAE,SAAkB;IACxG,IAAI,CAAC;QACH,iCAAiC;QACjC,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;QACvE,MAAM,UAAU,GAAG,WAAW,MAAM,IAAI,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAErE,wDAAwD;QACxD,IAAI,aAAa,GAAG,UAAU,CAAC;QAC/B,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,aAAa,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,IAAA,wBAAQ,EAAC,iBAAiB,aAAa,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,IAAA,wBAAQ,EAAC,oBAAoB,UAAU,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAE/D,OAAO;QACP,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QAC1F,WAAW,CAAC,OAAO,CAAC,CAAC;QAErB,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,cAAc;QACd,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC;YAAE,OAAO,WAAW,MAAM,EAAE,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,oDAAoD;AACpD,SAAgB,mBAAmB,CAAC,SAAkB;IACpD,wCAAwC;IACxC,IAAI,SAAS,EAAE,CAAC;QACd,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,IAAA,WAAI,EAAC,aAAa,SAAS,EAAE,EAAE,cAAc,EAAE,kBAAkB,CAAC,CAAC;YACzF,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC5C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;gBACnE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBAChD,IAAI,KAAK;oBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;IAED,wBAAwB;IACxB,IAAI,CAAC;QACH,MAAM,gBAAgB,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAC9D,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;YACtE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAChD,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,qBAAqB;IACrB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,wBAAQ,EAAC,oCAAoC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACrF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACvD,IAAI,SAAS;YAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,UAAU;IACV,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAA,wBAAQ,EAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/D,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,MAAM,CAAC;QAC3E,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,OAAO,QAAQ,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,qBAAqB;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,wBAAQ,EAAC,2BAA2B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAClG,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,qBAAqB,CAAC,MAAc,EAAE,QAAgB,EAAE,KAAgB;IACtF,MAAM,KAAK,GAAG,CAAC,QAAQ,MAAM,MAAM,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC;IAClC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAgB,qBAAqB,CAAC,MAAc,EAAE,QAAgB;IACpE,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG;QACZ,SAAS;QACT,KAAK,MAAM,IAAI,QAAQ,EAAE;QACzB,EAAE;QACF,SAAS;QACT,GAAG,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,EAAE;QACF,SAAS;QACT,mBAAmB;QACnB,mBAAmB;QACnB,EAAE;QACF,OAAO;QACP,6BAA6B;QAC7B,EAAE;QACF,YAAY,MAAM,EAAE;KACrB,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB;IAClC,IAAI,CAAC;QACH,MAAM,gBAAgB,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAC9D,IAAI,CAAC,IAAA,qBAAU,EAAC,gBAAgB,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAA,uBAAY,EAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC,CAAC,CAAC;aACZ,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,UAAkB;IAClD,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;QACjC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,mCAAmC;YACnC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YACnE,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,OAAO;YACP,IAAI,UAAU,KAAK,OAAO;gBAAE,OAAO,IAAI,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAgB,eAAe;IAC7B,MAAM,MAAM,GAAG,IAAA,WAAI,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAA,qBAAU,EAAC,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEvC,2CAA2C;IAC3C,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+B1B,CAAC;IACA,IAAA,wBAAa,EAAC,IAAA,WAAI,EAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/E,8BAA8B;IAC9B,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BxB,CAAC;IACA,IAAA,wBAAa,EAAC,IAAA,WAAI,EAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAE3E,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED,kBAAkB;AAElB,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,IAAI,IAAA,qBAAU,EAAC,gBAAgB,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,uBAAY,EAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,WAAW,CAAC,OAAmB;IACtC,IAAA,yBAAc,EAAC,gBAAgB,CAAC,CAAC;IACjC,IAAA,wBAAa,EAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,wBAAQ,EAAC,2BAA2B,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3F,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,6BAA6B;AAC7B,SAAgB,eAAe;IAC7B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,qCAAqC,CAAC;QACxD,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;QACvD,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;YAClB,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export interface InboxFileEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
size: number;
|
|
5
|
+
mtime: string;
|
|
6
|
+
type: 'text' | 'excel' | 'image' | 'other';
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
export interface InboxScanResult {
|
|
10
|
+
newFiles: InboxFileEntry[];
|
|
11
|
+
modifiedFiles: InboxFileEntry[];
|
|
12
|
+
skippedFiles: string[];
|
|
13
|
+
allFiles: InboxFileEntry[];
|
|
14
|
+
}
|
|
15
|
+
export interface ManifestEntry {
|
|
16
|
+
processedAt: string;
|
|
17
|
+
mtime: string;
|
|
18
|
+
linkedTo: string[];
|
|
19
|
+
action: 'change' | 'new' | 'unknown';
|
|
20
|
+
}
|
|
21
|
+
export interface InboxManifest {
|
|
22
|
+
files: Record<string, ManifestEntry>;
|
|
23
|
+
lastScan: string;
|
|
24
|
+
}
|
|
25
|
+
/** 单个任务的影响分析结果 */
|
|
26
|
+
export interface TaskImpact {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
status: string;
|
|
30
|
+
level: 'direct' | 'indirect' | 'none';
|
|
31
|
+
reason: string;
|
|
32
|
+
affectedFiles: string[];
|
|
33
|
+
needReExecute: boolean;
|
|
34
|
+
needRegression: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** 结构化影响分析报告 */
|
|
37
|
+
export interface ImpactReport {
|
|
38
|
+
directTasks: TaskImpact[];
|
|
39
|
+
indirectTasks: TaskImpact[];
|
|
40
|
+
unaffectedTasks: TaskImpact[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 获取 inbox 目录绝对路径
|
|
44
|
+
*/
|
|
45
|
+
export declare function getInboxDir(): string;
|
|
46
|
+
/**
|
|
47
|
+
* 确保 inbox 目录存在
|
|
48
|
+
*/
|
|
49
|
+
export declare function ensureInboxDir(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* 读取 manifest
|
|
52
|
+
*/
|
|
53
|
+
export declare function readManifest(): Promise<InboxManifest>;
|
|
54
|
+
/**
|
|
55
|
+
* 写入 manifest
|
|
56
|
+
*/
|
|
57
|
+
export declare function writeManifest(manifest: InboxManifest): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* 扫描 inbox 目录,区分新文件/已修改/已处理
|
|
60
|
+
*/
|
|
61
|
+
export declare function scanInbox(options?: {
|
|
62
|
+
reprocess?: boolean;
|
|
63
|
+
}): Promise<InboxScanResult>;
|
|
64
|
+
/**
|
|
65
|
+
* 更新 manifest:标记文件已处理
|
|
66
|
+
*/
|
|
67
|
+
export declare function markProcessed(files: InboxFileEntry[], action: 'change' | 'new', linkedTo?: string[]): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* 清理 manifest 中已不存在的文件记录
|
|
70
|
+
*/
|
|
71
|
+
export declare function cleanManifest(): Promise<number>;
|
|
72
|
+
/**
|
|
73
|
+
* 格式化 inbox 扫描结果为终端输出
|
|
74
|
+
*/
|
|
75
|
+
export declare function logInboxScan(result: InboxScanResult): void;
|
|
76
|
+
/**
|
|
77
|
+
* 构建澄清 Prompt:将用户描述 + 文件内容 + 任务详细上下文组合为结构化 Prompt
|
|
78
|
+
* taskDetails 包含每个任务的 REQ 摘要、TECH 摘要、状态、依赖关系
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildClarifyPrompt(desc: string, files: InboxFileEntry[], taskDetails: {
|
|
81
|
+
id: string;
|
|
82
|
+
name: string;
|
|
83
|
+
reqSummary: string;
|
|
84
|
+
techSummary: string;
|
|
85
|
+
status: string;
|
|
86
|
+
dependencies: string[];
|
|
87
|
+
}[]): string;
|
|
88
|
+
/** 解析澄清响应的返回类型 */
|
|
89
|
+
export interface ClarifyResult {
|
|
90
|
+
intent: 'change' | 'new';
|
|
91
|
+
structuredDesc: string;
|
|
92
|
+
keyPoints: string[];
|
|
93
|
+
acceptanceCriteria: string[];
|
|
94
|
+
impactReport: {
|
|
95
|
+
directTasks: {
|
|
96
|
+
id: string;
|
|
97
|
+
reason: string;
|
|
98
|
+
affectedFiles: string[];
|
|
99
|
+
}[];
|
|
100
|
+
indirectTasks: {
|
|
101
|
+
id: string;
|
|
102
|
+
reason: string;
|
|
103
|
+
}[];
|
|
104
|
+
unaffectedTasks: string[];
|
|
105
|
+
};
|
|
106
|
+
suggestedActions: string[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 解析澄清响应(AI 返回的 JSON)
|
|
110
|
+
*/
|
|
111
|
+
export declare function parseClarifyResponse(response: string): ClarifyResult | null;
|
|
112
|
+
/**
|
|
113
|
+
* 格式化澄清结果为终端展示(含结构化影响范围)
|
|
114
|
+
*/
|
|
115
|
+
export declare function logClarifyResult(result: ClarifyResult): void;
|
|
116
|
+
/**
|
|
117
|
+
* 格式化影响分析报告为终端展示(本地分析结果)
|
|
118
|
+
*/
|
|
119
|
+
export declare function logImpactReport(report: ImpactReport): void;
|
|
120
|
+
//# sourceMappingURL=inbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../../src/core/inbox.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,aAAa,EAAE,cAAc,EAAE,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kBAAkB;AAClB,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,gBAAgB;AAChB,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,eAAe,EAAE,UAAU,EAAE,CAAC;CAC/B;AASD;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAEpD;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,aAAa,CAAC,CAW3D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1E;AAiDD;;GAEG;AACH,wBAAsB,SAAS,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAqD/F;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,cAAc,EAAE,EACvB,MAAM,EAAE,QAAQ,GAAG,KAAK,EACxB,QAAQ,GAAE,MAAM,EAAO,GACtB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAcrD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CA6B1D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,cAAc,EAAE,EACvB,WAAW,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,EAAE,GAC3H,MAAM,CA0DR;AAED,kBAAkB;AAClB,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,QAAQ,GAAG,KAAK,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,YAAY,EAAE;QACZ,WAAW,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,aAAa,EAAE,MAAM,EAAE,CAAA;SAAE,EAAE,CAAC;QACvE,aAAa,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAChD,eAAe,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;IACF,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAQ3E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAuD5D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAiC1D"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getInboxDir = getInboxDir;
|
|
4
|
+
exports.ensureInboxDir = ensureInboxDir;
|
|
5
|
+
exports.readManifest = readManifest;
|
|
6
|
+
exports.writeManifest = writeManifest;
|
|
7
|
+
exports.scanInbox = scanInbox;
|
|
8
|
+
exports.markProcessed = markProcessed;
|
|
9
|
+
exports.cleanManifest = cleanManifest;
|
|
10
|
+
exports.logInboxScan = logInboxScan;
|
|
11
|
+
exports.buildClarifyPrompt = buildClarifyPrompt;
|
|
12
|
+
exports.parseClarifyResponse = parseClarifyResponse;
|
|
13
|
+
exports.logClarifyResult = logClarifyResult;
|
|
14
|
+
exports.logImpactReport = logImpactReport;
|
|
15
|
+
/**
|
|
16
|
+
* inbox — 需求收件箱
|
|
17
|
+
* 管理 .speccore/inbox/ 目录:扫描新文件、追踪处理状态、读取文件内容
|
|
18
|
+
*/
|
|
19
|
+
const path_1 = require("path");
|
|
20
|
+
const fs_extra_1 = require("fs-extra");
|
|
21
|
+
const logger_1 = require("../utils/logger");
|
|
22
|
+
// ── 常量 ──
|
|
23
|
+
const INBOX_DIR = '.speccore/inbox';
|
|
24
|
+
const MANIFEST_FILE = '.manifest.json';
|
|
25
|
+
// ── 核心函数 ──
|
|
26
|
+
/**
|
|
27
|
+
* 获取 inbox 目录绝对路径
|
|
28
|
+
*/
|
|
29
|
+
function getInboxDir() {
|
|
30
|
+
return (0, path_1.join)(process.cwd(), INBOX_DIR);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 确保 inbox 目录存在
|
|
34
|
+
*/
|
|
35
|
+
async function ensureInboxDir() {
|
|
36
|
+
await (0, fs_extra_1.ensureDir)(getInboxDir());
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 读取 manifest
|
|
40
|
+
*/
|
|
41
|
+
async function readManifest() {
|
|
42
|
+
const manifestPath = (0, path_1.join)(getInboxDir(), MANIFEST_FILE);
|
|
43
|
+
if (await (0, fs_extra_1.pathExists)(manifestPath)) {
|
|
44
|
+
try {
|
|
45
|
+
const content = await (0, fs_extra_1.readFile)(manifestPath, 'utf-8');
|
|
46
|
+
return JSON.parse(content);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { files: {}, lastScan: '' };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { files: {}, lastScan: '' };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 写入 manifest
|
|
56
|
+
*/
|
|
57
|
+
async function writeManifest(manifest) {
|
|
58
|
+
const manifestPath = (0, path_1.join)(getInboxDir(), MANIFEST_FILE);
|
|
59
|
+
manifest.lastScan = new Date().toISOString();
|
|
60
|
+
await (0, fs_extra_1.writeFile)(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 检测文件类型
|
|
64
|
+
*/
|
|
65
|
+
function detectFileType(name) {
|
|
66
|
+
const ext = name.split('.').pop()?.toLowerCase() || '';
|
|
67
|
+
if (['md', 'txt', 'markdown', 'json', 'yaml', 'yml', 'csv'].includes(ext))
|
|
68
|
+
return 'text';
|
|
69
|
+
if (['xlsx', 'xls'].includes(ext))
|
|
70
|
+
return 'excel';
|
|
71
|
+
if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'].includes(ext))
|
|
72
|
+
return 'image';
|
|
73
|
+
return 'other';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 读取单个文件内容
|
|
77
|
+
*/
|
|
78
|
+
async function readInboxFile(filePath, fileType) {
|
|
79
|
+
switch (fileType) {
|
|
80
|
+
case 'text':
|
|
81
|
+
return (0, fs_extra_1.readFile)(filePath, 'utf-8');
|
|
82
|
+
case 'excel':
|
|
83
|
+
try {
|
|
84
|
+
const XLSX = require('xlsx');
|
|
85
|
+
const wb = XLSX.readFile(filePath);
|
|
86
|
+
const sheets = [];
|
|
87
|
+
for (const name of wb.SheetNames) {
|
|
88
|
+
const ws = wb.Sheets[name];
|
|
89
|
+
const csv = XLSX.utils.sheet_to_csv(ws);
|
|
90
|
+
sheets.push(`## Sheet: ${name}\n${csv}`);
|
|
91
|
+
}
|
|
92
|
+
return sheets.join('\n\n');
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
return `[Excel 解析失败: ${filePath}]`;
|
|
96
|
+
}
|
|
97
|
+
case 'image':
|
|
98
|
+
// 图片无法直接读取文本,返回路径引用
|
|
99
|
+
return `[图片文件: ${filePath}]`;
|
|
100
|
+
case 'other':
|
|
101
|
+
try {
|
|
102
|
+
return await (0, fs_extra_1.readFile)(filePath, 'utf-8');
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return `[无法读取: ${filePath}]`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 扫描 inbox 目录,区分新文件/已修改/已处理
|
|
111
|
+
*/
|
|
112
|
+
async function scanInbox(options = {}) {
|
|
113
|
+
const inboxDir = getInboxDir();
|
|
114
|
+
if (!await (0, fs_extra_1.pathExists)(inboxDir)) {
|
|
115
|
+
return { newFiles: [], modifiedFiles: [], skippedFiles: [], allFiles: [] };
|
|
116
|
+
}
|
|
117
|
+
const manifest = await readManifest();
|
|
118
|
+
const entries = await (0, fs_extra_1.readdir)(inboxDir, { withFileTypes: true });
|
|
119
|
+
const result = { newFiles: [], modifiedFiles: [], skippedFiles: [], allFiles: [] };
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.isFile() || entry.name.startsWith('.'))
|
|
122
|
+
continue;
|
|
123
|
+
const filePath = (0, path_1.join)(inboxDir, entry.name);
|
|
124
|
+
const fileType = detectFileType(entry.name);
|
|
125
|
+
const fileStat = await (0, fs_extra_1.stat)(filePath);
|
|
126
|
+
const mtime = fileStat.mtime.toISOString();
|
|
127
|
+
const fileEntry = {
|
|
128
|
+
name: entry.name,
|
|
129
|
+
path: filePath,
|
|
130
|
+
size: fileStat.size,
|
|
131
|
+
mtime,
|
|
132
|
+
type: fileType,
|
|
133
|
+
content: '',
|
|
134
|
+
};
|
|
135
|
+
// 检查 manifest 状态
|
|
136
|
+
const manifestEntry = manifest.files[entry.name];
|
|
137
|
+
if (options.reprocess || !manifestEntry) {
|
|
138
|
+
// 新文件 或 强制重新处理
|
|
139
|
+
fileEntry.content = await readInboxFile(filePath, fileType);
|
|
140
|
+
if (manifestEntry) {
|
|
141
|
+
result.modifiedFiles.push(fileEntry);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
result.newFiles.push(fileEntry);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else if (manifestEntry.mtime !== mtime) {
|
|
148
|
+
// 文件已修改
|
|
149
|
+
fileEntry.content = await readInboxFile(filePath, fileType);
|
|
150
|
+
result.modifiedFiles.push(fileEntry);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// 已处理且未修改 → 跳过
|
|
154
|
+
result.skippedFiles.push(entry.name);
|
|
155
|
+
}
|
|
156
|
+
result.allFiles.push(fileEntry);
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 更新 manifest:标记文件已处理
|
|
162
|
+
*/
|
|
163
|
+
async function markProcessed(files, action, linkedTo = []) {
|
|
164
|
+
const manifest = await readManifest();
|
|
165
|
+
for (const file of files) {
|
|
166
|
+
manifest.files[file.name] = {
|
|
167
|
+
processedAt: new Date().toISOString(),
|
|
168
|
+
mtime: file.mtime,
|
|
169
|
+
linkedTo,
|
|
170
|
+
action,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
await writeManifest(manifest);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 清理 manifest 中已不存在的文件记录
|
|
177
|
+
*/
|
|
178
|
+
async function cleanManifest() {
|
|
179
|
+
const manifest = await readManifest();
|
|
180
|
+
const inboxDir = getInboxDir();
|
|
181
|
+
let cleaned = 0;
|
|
182
|
+
for (const name of Object.keys(manifest.files)) {
|
|
183
|
+
if (!await (0, fs_extra_1.pathExists)((0, path_1.join)(inboxDir, name))) {
|
|
184
|
+
delete manifest.files[name];
|
|
185
|
+
cleaned++;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (cleaned > 0)
|
|
189
|
+
await writeManifest(manifest);
|
|
190
|
+
return cleaned;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 格式化 inbox 扫描结果为终端输出
|
|
194
|
+
*/
|
|
195
|
+
function logInboxScan(result) {
|
|
196
|
+
const total = result.newFiles.length + result.modifiedFiles.length + result.skippedFiles.length;
|
|
197
|
+
if (total === 0) {
|
|
198
|
+
logger_1.logger.info('📎 Inbox: 目录为空');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
logger_1.logger.info('📎 Inbox 扫描:');
|
|
202
|
+
for (const f of result.newFiles) {
|
|
203
|
+
const size = f.size > 1024 ? `${(f.size / 1024).toFixed(1)}KB` : `${f.size}B`;
|
|
204
|
+
logger_1.logger.info(` 🆕 ${f.name} (新文件, ${size})`);
|
|
205
|
+
}
|
|
206
|
+
for (const f of result.modifiedFiles) {
|
|
207
|
+
const size = f.size > 1024 ? `${(f.size / 1024).toFixed(1)}KB` : `${f.size}B`;
|
|
208
|
+
logger_1.logger.info(` 🔄 ${f.name} (已修改, ${size})`);
|
|
209
|
+
}
|
|
210
|
+
for (const name of result.skippedFiles) {
|
|
211
|
+
logger_1.logger.info(` ✅ ${name} (已处理 → 跳过)`);
|
|
212
|
+
}
|
|
213
|
+
const actionable = result.newFiles.length + result.modifiedFiles.length;
|
|
214
|
+
if (actionable === 0) {
|
|
215
|
+
logger_1.logger.info('');
|
|
216
|
+
logger_1.logger.info(' 所有文件已处理。请添加新文件或使用 --file 指定');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 构建澄清 Prompt:将用户描述 + 文件内容 + 任务详细上下文组合为结构化 Prompt
|
|
221
|
+
* taskDetails 包含每个任务的 REQ 摘要、TECH 摘要、状态、依赖关系
|
|
222
|
+
*/
|
|
223
|
+
function buildClarifyPrompt(desc, files, taskDetails) {
|
|
224
|
+
const sections = [];
|
|
225
|
+
sections.push('# 需求澄清与影响分析');
|
|
226
|
+
sections.push('');
|
|
227
|
+
sections.push('## 用户输入');
|
|
228
|
+
sections.push(`原始描述: ${desc}`);
|
|
229
|
+
sections.push('');
|
|
230
|
+
// 附件内容
|
|
231
|
+
if (files.length > 0) {
|
|
232
|
+
sections.push('## 附件内容');
|
|
233
|
+
for (const f of files) {
|
|
234
|
+
if (f.type === 'image') {
|
|
235
|
+
sections.push(`### ${f.name}`);
|
|
236
|
+
sections.push(`[图片: ${f.path}]`);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
sections.push(`### ${f.name}`);
|
|
240
|
+
sections.push(f.content.slice(0, 5000));
|
|
241
|
+
if (f.content.length > 5000) {
|
|
242
|
+
sections.push(`... (内容截断,共 ${f.content.length} 字符)`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
sections.push('');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// 现有任务详细上下文
|
|
249
|
+
if (taskDetails.length > 0) {
|
|
250
|
+
sections.push('## 现有任务详情');
|
|
251
|
+
for (const t of taskDetails) {
|
|
252
|
+
sections.push(`### ${t.id}: ${t.name}`);
|
|
253
|
+
sections.push(`- 状态: ${t.status}`);
|
|
254
|
+
if (t.reqSummary)
|
|
255
|
+
sections.push(`- 需求摘要: ${t.reqSummary}`);
|
|
256
|
+
if (t.techSummary)
|
|
257
|
+
sections.push(`- 技术方案摘要: ${t.techSummary}`);
|
|
258
|
+
if (t.dependencies.length > 0)
|
|
259
|
+
sections.push(`- 依赖: ${t.dependencies.join(', ')}`);
|
|
260
|
+
sections.push('');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// 输出要求
|
|
264
|
+
sections.push('## 请分析并输出 JSON:');
|
|
265
|
+
sections.push('```json');
|
|
266
|
+
sections.push('{');
|
|
267
|
+
sections.push(' "intent": "change | new",');
|
|
268
|
+
sections.push(' "structuredDesc": "结构化需求描述",');
|
|
269
|
+
sections.push(' "keyPoints": ["要点1", "要点2"],');
|
|
270
|
+
sections.push(' "acceptanceCriteria": ["验收标准1", "验收标准2"],');
|
|
271
|
+
sections.push(' "impactReport": {');
|
|
272
|
+
sections.push(' "directTasks": [{ "id": "Task-003", "reason": "密码规则在该任务中定义", "affectedFiles": ["REQ.md", "TECH.md"] }],');
|
|
273
|
+
sections.push(' "indirectTasks": [{ "id": "Task-005", "reason": "依赖 Task-003 的密码校验逻辑" }],');
|
|
274
|
+
sections.push(' "unaffectedTasks": ["Task-001", "Task-002"]');
|
|
275
|
+
sections.push(' },');
|
|
276
|
+
sections.push(' "suggestedActions": ["修改 Task-003 密码字段定义", "回归测试 Task-005 登录流程"]');
|
|
277
|
+
sections.push('}');
|
|
278
|
+
sections.push('```');
|
|
279
|
+
return sections.join('\n');
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* 解析澄清响应(AI 返回的 JSON)
|
|
283
|
+
*/
|
|
284
|
+
function parseClarifyResponse(response) {
|
|
285
|
+
try {
|
|
286
|
+
const jsonMatch = response.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
287
|
+
const jsonStr = jsonMatch ? jsonMatch[1].trim() : response.trim();
|
|
288
|
+
return JSON.parse(jsonStr);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* 格式化澄清结果为终端展示(含结构化影响范围)
|
|
296
|
+
*/
|
|
297
|
+
function logClarifyResult(result) {
|
|
298
|
+
logger_1.logger.info('');
|
|
299
|
+
logger_1.logger.info('📋 需求澄清结果:');
|
|
300
|
+
logger_1.logger.info('┌─────────────────────────────────────┐');
|
|
301
|
+
logger_1.logger.info(`│ 意图: ${result.intent === 'new' ? '🆕 新增(全新功能)' : '🔄 变更(修改已有需求)'}`);
|
|
302
|
+
logger_1.logger.info(`│ 结构化描述: ${result.structuredDesc}`);
|
|
303
|
+
if (result.keyPoints.length > 0) {
|
|
304
|
+
logger_1.logger.info('│ 功能要点:');
|
|
305
|
+
for (const p of result.keyPoints) {
|
|
306
|
+
logger_1.logger.info(`│ • ${p}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (result.acceptanceCriteria.length > 0) {
|
|
310
|
+
logger_1.logger.info('│ 验收标准:');
|
|
311
|
+
for (const c of result.acceptanceCriteria) {
|
|
312
|
+
logger_1.logger.info(`│ ✓ ${c}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
logger_1.logger.info('│');
|
|
316
|
+
// 结构化影响范围
|
|
317
|
+
if (result.impactReport) {
|
|
318
|
+
const { directTasks, indirectTasks, unaffectedTasks } = result.impactReport;
|
|
319
|
+
if (directTasks.length > 0) {
|
|
320
|
+
logger_1.logger.info('│ 🔴 直接影响(需修改 Spec + 重新执行):');
|
|
321
|
+
for (const t of directTasks) {
|
|
322
|
+
const files = t.affectedFiles?.length ? ` [${t.affectedFiles.join(', ')}]` : '';
|
|
323
|
+
logger_1.logger.info(`│ ${t.id}${files} — ${t.reason}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (indirectTasks.length > 0) {
|
|
327
|
+
logger_1.logger.info('│ 🟡 间接影响(需回归验证):');
|
|
328
|
+
for (const t of indirectTasks) {
|
|
329
|
+
logger_1.logger.info(`│ ${t.id} — ${t.reason}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (unaffectedTasks.length > 0) {
|
|
333
|
+
logger_1.logger.info(`│ 🟢 无影响: ${unaffectedTasks.join(', ')}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (result.suggestedActions?.length > 0) {
|
|
337
|
+
logger_1.logger.info('│ 建议操作:');
|
|
338
|
+
for (const a of result.suggestedActions) {
|
|
339
|
+
logger_1.logger.info(`│ → ${a}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
logger_1.logger.info('└─────────────────────────────────────┘');
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 格式化影响分析报告为终端展示(本地分析结果)
|
|
346
|
+
*/
|
|
347
|
+
function logImpactReport(report) {
|
|
348
|
+
logger_1.logger.info('');
|
|
349
|
+
logger_1.logger.info('📋 影响范围分析:');
|
|
350
|
+
logger_1.logger.info('┌─────────────────────────────────────┐');
|
|
351
|
+
if (report.directTasks.length > 0) {
|
|
352
|
+
logger_1.logger.info('│ 🔴 直接影响(需修改 Spec + 重新执行):');
|
|
353
|
+
for (const t of report.directTasks) {
|
|
354
|
+
const statusTag = t.status === 'done' ? ' [done→needs-rework]' : ` [${t.status}]`;
|
|
355
|
+
logger_1.logger.info(`│ ${t.id} ${t.name}${statusTag}`);
|
|
356
|
+
logger_1.logger.info(`│ ${t.reason}`);
|
|
357
|
+
if (t.affectedFiles.length > 0) {
|
|
358
|
+
logger_1.logger.info(`│ 受影响文件: ${t.affectedFiles.join(', ')}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (report.indirectTasks.length > 0) {
|
|
363
|
+
logger_1.logger.info('│');
|
|
364
|
+
logger_1.logger.info('│ 🟡 间接影响(需回归验证):');
|
|
365
|
+
for (const t of report.indirectTasks) {
|
|
366
|
+
const statusTag = t.status === 'done' ? ' [需回归测试]' : ` [${t.status}]`;
|
|
367
|
+
logger_1.logger.info(`│ ${t.id} ${t.name}${statusTag}`);
|
|
368
|
+
logger_1.logger.info(`│ ${t.reason}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (report.unaffectedTasks.length > 0) {
|
|
372
|
+
logger_1.logger.info('│');
|
|
373
|
+
logger_1.logger.info(`│ 🟢 无影响: ${report.unaffectedTasks.map(t => t.id).join(', ')}`);
|
|
374
|
+
}
|
|
375
|
+
logger_1.logger.info('└─────────────────────────────────────┘');
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=inbox.js.map
|