speccore 8.3.250 → 8.3.252
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-once/SKILL.md +3 -2
- package/.agents/skills/speccore-router/SKILL.md +3 -3
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +31 -0
- package/dist/commands/analyze.js.map +1 -1
- package/dist/utils/ai-trace.d.ts +3 -0
- package/dist/utils/ai-trace.d.ts.map +1 -0
- package/dist/utils/ai-trace.js +49 -0
- package/dist/utils/ai-trace.js.map +1 -0
- package/package.json +1 -1
|
@@ -25,7 +25,8 @@ disable-model-invocation: false
|
|
|
25
25
|
先识别用户给了什么,按入口执行预处理,再统一做范围确认:
|
|
26
26
|
|
|
27
27
|
**入口 A:已切分好的功能单元**(用户指定了单元名或说「全部」)
|
|
28
|
-
-
|
|
28
|
+
- 无需预处理,直接进入下方「范围确认」
|
|
29
|
+
- 用户只给单元名(如「features/ 下的 v3-service-01-门禁授权同步」)时:先 `speccore status` 探测活跃迭代(.speccore/local/context.json 的 currentIteration);探不到或单元不在该迭代,向用户确认迭代名,不要猜
|
|
29
30
|
|
|
30
31
|
**入口 B:一个文档地址**(.md / .docx / .pdf,多个文档可批量)
|
|
31
32
|
- `.md` → 执行 `speccore unitize -f "<路径>"`(多文档可 `--files "a.md=端1,b.md"`,端名须与 CONSTITUTION.md 一致)
|
|
@@ -44,7 +45,7 @@ disable-model-invocation: false
|
|
|
44
45
|
5. 目录/任务命名由 CLI 确定性生成(单元目录 {文档slug}-{序号}-{标题}、Task-{编号}-{slug}、子任务 Task-{父任务}-{端名}),不要自己改
|
|
45
46
|
|
|
46
47
|
### Step 1 澄清与分析
|
|
47
|
-
1. 执行 `speccore analyze --prompt -I <迭代> --with-code`(--with-code 使 Prompt 携带各端工程路径表与源码上下文;指定范围用 `--unit M-01,M-03
|
|
48
|
+
1. 执行 `speccore analyze --prompt -I <迭代> --with-code`(--with-code 使 Prompt 携带各端工程路径表与源码上下文;指定范围用 `--unit`,直接写切分出的目录名即可——如 `--unit "v3-service-01-门禁授权同步"` 或 `--unit M-01,M-03` 均可(v8.3.250+ 目录名与 M-ID 双匹配);不要用 --feature——那是确定性局部分析,不出 Prompt)
|
|
48
49
|
2. stdout 出现 `[SPECCORE_PROMPT]` → 将 Prompt 内容作为你的工作指令执行(不要改写、不要略过);多单元时按 Prompt 内的「逐个单元」指示处理,每个单元用 `analyze --unit <id> --apply '<内容>'` 提交
|
|
49
50
|
3. 分析中发现需求模糊点 → **向用户提问澄清**,不要自行脑补
|
|
50
51
|
4. 分析完成后按 Prompt 中的写入格式执行 `speccore analyze --apply '{json}' -I <迭代>` 落盘
|
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
或明确要求通过 speccore 命令行工具执行操作,且意图涉及以下概念时使用:
|
|
6
6
|
迭代管理、需求文档导入(doc2spec)、需求切分(unitize)、Spec 分析生成(analyze --prompt)、
|
|
7
7
|
任务拆分(taskify)、执行计划(plan --prompt)、代码执行(execute --prompt)、
|
|
8
|
-
任务归档(done)、PR 提交(pr)、状态查看(status/dashboard/doctor
|
|
8
|
+
任务归档(done)、PR 提交(pr)、状态查看(status/dashboard/doctor)、智能级联/端到端流水线(run)。
|
|
9
9
|
将用户意图映射为 speccore CLI 命令,通过 execute_command 执行。
|
|
10
10
|
核心原则:必须直接执行,决不输出命令文本。唯一下例外:"只看不跑"。
|
|
11
11
|
以下情况明确不处理:解释代码逻辑、审查代码片段、讨论需求合理性、
|
|
@@ -312,8 +312,8 @@ allowed-tools: ["Bash", "Read", "Write", "Edit"]
|
|
|
312
312
|
### 智能级联 / 端到端流水线
|
|
313
313
|
| 用户说法 | 提取参数 | CLI 命令 |
|
|
314
314
|
| :--- | :--- | :--- |
|
|
315
|
-
| 智能级联/全流程推进 | - | `speccore
|
|
316
|
-
| 推进项目/继续开发 | - | `speccore
|
|
315
|
+
| 智能级联/全流程推进 | - | `speccore run` |
|
|
316
|
+
| 推进项目/继续开发 | - | `speccore run --auto` |
|
|
317
317
|
| 全自动推进/跑流水线 | - | `speccore run` |
|
|
318
318
|
| 端到端/一键开发/自动开发 | demand? | `speccore run "{demand}"` |
|
|
319
319
|
| {一句话需求描述}(如"做积分功能") | demand | `speccore run "{demand}"` |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/analyze.js
CHANGED
|
@@ -83,6 +83,7 @@ const unit_context_assembler_1 = require("../core/unit-context-assembler");
|
|
|
83
83
|
const dispatch_loop_1 = require("../core/dispatch-loop");
|
|
84
84
|
const pipeline_engine_1 = require("../core/pipeline-engine");
|
|
85
85
|
const change_detection_1 = require("../core/change-detection");
|
|
86
|
+
const ai_trace_1 = require("../utils/ai-trace");
|
|
86
87
|
const streaming_analyzer_1 = require("../core/streaming-analyzer");
|
|
87
88
|
const requirement_clarifier_1 = require("../core/requirement-clarifier");
|
|
88
89
|
const agents_1 = require("../core/agents");
|
|
@@ -242,6 +243,14 @@ async function applyDocContent(content, iter) {
|
|
|
242
243
|
const iterDir = await (0, context_1.getIterationDir)(iter);
|
|
243
244
|
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
244
245
|
let docs = {};
|
|
246
|
+
// v8.3.252+: 飞行记录仪——解析前原样留档(格式错误也留痕),
|
|
247
|
+
// 区分「AI 没给」与「给了但被解析丢弃」
|
|
248
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'apply-in', {
|
|
249
|
+
rawLength: content.length,
|
|
250
|
+
hasJson: trimmed.startsWith('{'),
|
|
251
|
+
hasDocMarkers: /\[DOC:[^\]]+\]/.test(trimmed),
|
|
252
|
+
rawPreview: trimmed.slice(0, 500),
|
|
253
|
+
});
|
|
245
254
|
// 格式 1:JSON 对象
|
|
246
255
|
if (trimmed.startsWith('{')) {
|
|
247
256
|
try {
|
|
@@ -258,6 +267,11 @@ async function applyDocContent(content, iter) {
|
|
|
258
267
|
for (const [name, c] of blocks)
|
|
259
268
|
docs[name] = c;
|
|
260
269
|
}
|
|
270
|
+
if (Object.keys(docs).length === 0) {
|
|
271
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'apply-fail', { reason: 'no-parsable-content', rawInput: content });
|
|
272
|
+
logger_1.logger.warn('⚠️ 已收到 AI 回传内容,但 JSON 与 [DOC:] 两种格式均未解析出文档,请检查回传格式');
|
|
273
|
+
return 0;
|
|
274
|
+
}
|
|
261
275
|
let applied = 0;
|
|
262
276
|
// v8.3.237+: 与 --apply 主通道一致——有功能模块的项目才拦截(内容类归 {feature}/overview/,
|
|
263
277
|
// 全局 overview/REQUIREMENT.md 由 split 聚合),无功能模块的老项目放行
|
|
@@ -273,6 +287,11 @@ async function applyDocContent(content, iter) {
|
|
|
273
287
|
await (0, fs_extra_1.writeFile)(fp, docContent);
|
|
274
288
|
applied++;
|
|
275
289
|
}
|
|
290
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'apply-done', {
|
|
291
|
+
applied,
|
|
292
|
+
files: Object.keys(docs),
|
|
293
|
+
totalChars: Object.values(docs).reduce((s, c) => s + (typeof c === 'string' ? c.length : 0), 0),
|
|
294
|
+
});
|
|
276
295
|
return applied;
|
|
277
296
|
}
|
|
278
297
|
/**
|
|
@@ -784,6 +803,7 @@ async function analyzeCommand(options) {
|
|
|
784
803
|
const targetUnit = targetUnits[0];
|
|
785
804
|
if (options.prompt) {
|
|
786
805
|
const prompt = await buildUnitAnalysisPrompt(iterDir, iter, targetUnit, units);
|
|
806
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'prompt-out', { mode: 'unit', unit: targetUnit.id, promptLength: prompt.length, prompt });
|
|
787
807
|
process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
|
|
788
808
|
process.exitCode = 10;
|
|
789
809
|
return;
|
|
@@ -791,14 +811,22 @@ async function analyzeCommand(options) {
|
|
|
791
811
|
if (options.apply) {
|
|
792
812
|
// v8.3.249+: 此前为“空实现”——只打印「已接收」但 [DOC:xxx] 内容从不落盘,
|
|
793
813
|
// AI 认真提交后产物丢失,taskify 拿不到分析结果。现在真正解析写入。
|
|
814
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'apply-in', {
|
|
815
|
+
unit: targetUnit.id,
|
|
816
|
+
rawLength: options.apply.length,
|
|
817
|
+
hasDocMarkers: /\[DOC:[^\]]+\]/.test(options.apply),
|
|
818
|
+
rawPreview: options.apply.trim().slice(0, 500),
|
|
819
|
+
});
|
|
794
820
|
const docBlocks = parseDocMarkers(options.apply);
|
|
795
821
|
if (docBlocks.size === 0) {
|
|
822
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'apply-fail', { unit: targetUnit.id, reason: 'no-doc-markers', rawInput: options.apply });
|
|
796
823
|
logger_1.logger.warn('⚠️ 未检测到 [DOC:xxx] 标记,请按 prompt 格式输出');
|
|
797
824
|
return;
|
|
798
825
|
}
|
|
799
826
|
const specDir = getUnitSpecDir(iterDir, targetUnit);
|
|
800
827
|
await (0, fs_extra_1.ensureDir)(specDir);
|
|
801
828
|
let written = 0;
|
|
829
|
+
const writtenFiles = [];
|
|
802
830
|
for (const [docName, content] of docBlocks) {
|
|
803
831
|
const safeName = (0, path_1.basename)(docName);
|
|
804
832
|
if (!/^[A-Za-z_][A-Za-z0-9_-]*\.(md|yaml)$/.test(safeName))
|
|
@@ -809,7 +837,9 @@ async function analyzeCommand(options) {
|
|
|
809
837
|
}
|
|
810
838
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(specDir, safeName), content.trim());
|
|
811
839
|
written++;
|
|
840
|
+
writtenFiles.push(safeName);
|
|
812
841
|
}
|
|
842
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'apply-done', { unit: targetUnit.id, applied: written, files: writtenFiles });
|
|
813
843
|
logger_1.logger.success(`✅ 功能单元 ${targetUnit.id} 分析结果已写入 ${written} 个文档`);
|
|
814
844
|
logger_1.logger.info(` 📂 020-specs/${sanitizeUnitDirName(targetUnit.name)}/overview/`);
|
|
815
845
|
return;
|
|
@@ -2738,6 +2768,7 @@ ${singlePrompt}`);
|
|
|
2738
2768
|
catch { /* ignore */ }
|
|
2739
2769
|
}
|
|
2740
2770
|
const prompt = await buildMultiDocPrompt('analyze', { iteration: iter, task: options.task, type: options.type, scope: options.scope, withCode: options.withCode, platform: options.platform, phase: options.phase, autoMode: options.auto }, options, needsClarify ? { needsClarify, clarifyTargets } : undefined);
|
|
2771
|
+
await (0, ai_trace_1.recordAiTrace)(iter, 'prompt-out', { mode: 'analyze-main', scope: options.scope, promptLength: prompt.length, prompt });
|
|
2741
2772
|
process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
|
|
2742
2773
|
// v7.2.0+: 全局分析完成后输出下一步引导
|
|
2743
2774
|
if (options.scope === 'global') {
|