sillyspec 3.27.5 → 3.27.6
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/.worktrees/8d2d0392/.claude/skills/sillyspec-archive/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-auto/SKILL.md +93 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-commit/SKILL.md +67 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-continue/SKILL.md +47 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-execute/SKILL.md +214 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-explore/SKILL.md +102 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-export/SKILL.md +57 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-init/SKILL.md +177 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-plan/SKILL.md +156 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-propose/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-quick/SKILL.md +116 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-resume/SKILL.md +70 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-scan/SKILL.md +79 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-state/SKILL.md +64 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-status/SKILL.md +54 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-verify/SKILL.md +87 -0
- package/.worktrees/8d2d0392/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
- package/.worktrees/8d2d0392/.claude/skills/verify-per-user/SKILL.md +6 -0
- package/.worktrees/8d2d0392/.gitattributes +2 -0
- package/.worktrees/8d2d0392/.husky/pre-push +20 -0
- package/.worktrees/8d2d0392/README.md +203 -0
- package/.worktrees/8d2d0392/SKILL.md +91 -0
- package/.worktrees/8d2d0392/architecture-analysis.md +448 -0
- package/.worktrees/8d2d0392/bin/sillyspec.js +2 -0
- package/.worktrees/8d2d0392/eval/README.md +86 -0
- package/.worktrees/8d2d0392/eval/eval.config.json +28 -0
- package/.worktrees/8d2d0392/eval/lib/adapters/cli.mjs +63 -0
- package/.worktrees/8d2d0392/eval/lib/adapters/dryrun.mjs +27 -0
- package/.worktrees/8d2d0392/eval/lib/config.mjs +82 -0
- package/.worktrees/8d2d0392/eval/lib/db.mjs +76 -0
- package/.worktrees/8d2d0392/eval/lib/report.mjs +120 -0
- package/.worktrees/8d2d0392/eval/lib/runner.mjs +157 -0
- package/.worktrees/8d2d0392/eval/lib/tasks.mjs +73 -0
- package/.worktrees/8d2d0392/eval/lib/util.mjs +107 -0
- package/.worktrees/8d2d0392/eval/lib/workdir.mjs +30 -0
- package/.worktrees/8d2d0392/eval/run.mjs +83 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/001-slugify/verify.mjs +28 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/002-each-day/verify.mjs +31 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/data/notes.txt +5 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/files/package.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/task.json +6 -0
- package/.worktrees/8d2d0392/eval/tasks/003-word-report/verify.mjs +38 -0
- package/.worktrees/8d2d0392/logo.jpg +0 -0
- package/.worktrees/8d2d0392/package-lock.json +977 -0
- package/.worktrees/8d2d0392/package.json +41 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/favicon.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/index.html +16 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/logo.jpg +0 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/.worktrees/8d2d0392/packages/dashboard/dist/prototype-overview.html +256 -0
- package/.worktrees/8d2d0392/packages/dashboard/package.json +25 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/executor.js +95 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/index.js +679 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/parser.js +526 -0
- package/.worktrees/8d2d0392/packages/dashboard/server/watcher.js +357 -0
- package/.worktrees/8d2d0392/src/agent-session-log.js +803 -0
- package/.worktrees/8d2d0392/src/change-list.js +290 -0
- package/.worktrees/8d2d0392/src/change-risk-profile.js +339 -0
- package/.worktrees/8d2d0392/src/check-primitives.js +65 -0
- package/.worktrees/8d2d0392/src/classify-change.js +109 -0
- package/.worktrees/8d2d0392/src/commit-suggest.js +202 -0
- package/.worktrees/8d2d0392/src/config-cat.js +88 -0
- package/.worktrees/8d2d0392/src/config-schema.js +328 -0
- package/.worktrees/8d2d0392/src/constants.js +97 -0
- package/.worktrees/8d2d0392/src/contract-matrix.js +533 -0
- package/.worktrees/8d2d0392/src/datetime.js +20 -0
- package/.worktrees/8d2d0392/src/db-engine.js +55 -0
- package/.worktrees/8d2d0392/src/db.js +381 -0
- package/.worktrees/8d2d0392/src/decision-distill.js +517 -0
- package/.worktrees/8d2d0392/src/dispatch/backends/local-agent.js +155 -0
- package/.worktrees/8d2d0392/src/dispatch/backends/sillyhub-mcp.js +245 -0
- package/.worktrees/8d2d0392/src/dispatch/probe.js +246 -0
- package/.worktrees/8d2d0392/src/dispatch/strategy.js +121 -0
- package/.worktrees/8d2d0392/src/docs-check.js +825 -0
- package/.worktrees/8d2d0392/src/docs-debt.js +393 -0
- package/.worktrees/8d2d0392/src/docs-gate.js +107 -0
- package/.worktrees/8d2d0392/src/doctor-diagnostics.js +1099 -0
- package/.worktrees/8d2d0392/src/endpoint-extractor.js +409 -0
- package/.worktrees/8d2d0392/src/foreign-declared.js +78 -0
- package/.worktrees/8d2d0392/src/fs-atomic.js +73 -0
- package/.worktrees/8d2d0392/src/git-helper.js +150 -0
- package/.worktrees/8d2d0392/src/hooks/claude-pre-tool-use.cjs +130 -0
- package/.worktrees/8d2d0392/src/hooks/worktree-guard.js +892 -0
- package/.worktrees/8d2d0392/src/index.js +2854 -0
- package/.worktrees/8d2d0392/src/init.js +626 -0
- package/.worktrees/8d2d0392/src/knowledge-match.js +231 -0
- package/.worktrees/8d2d0392/src/local-detect.js +128 -0
- package/.worktrees/8d2d0392/src/local-register.js +104 -0
- package/.worktrees/8d2d0392/src/machine-interface.js +543 -0
- package/.worktrees/8d2d0392/src/migrate.js +127 -0
- package/.worktrees/8d2d0392/src/module-impact.js +166 -0
- package/.worktrees/8d2d0392/src/module-resolve.js +222 -0
- package/.worktrees/8d2d0392/src/modules.js +580 -0
- package/.worktrees/8d2d0392/src/plan-adopt-waves.js +126 -0
- package/.worktrees/8d2d0392/src/progress/change-registry.js +336 -0
- package/.worktrees/8d2d0392/src/progress/consistency-doctor.js +355 -0
- package/.worktrees/8d2d0392/src/progress/shared.js +36 -0
- package/.worktrees/8d2d0392/src/progress/stage-machine.js +594 -0
- package/.worktrees/8d2d0392/src/progress/step-store.js +265 -0
- package/.worktrees/8d2d0392/src/progress.js +1304 -0
- package/.worktrees/8d2d0392/src/quick-recommend.js +115 -0
- package/.worktrees/8d2d0392/src/quicklog.js +883 -0
- package/.worktrees/8d2d0392/src/review-tier.js +77 -0
- package/.worktrees/8d2d0392/src/run/command.js +1641 -0
- package/.worktrees/8d2d0392/src/run/complete-handlers.js +1552 -0
- package/.worktrees/8d2d0392/src/run/complete.js +1259 -0
- package/.worktrees/8d2d0392/src/run/concurrent-detect.js +156 -0
- package/.worktrees/8d2d0392/src/run/gates.js +1094 -0
- package/.worktrees/8d2d0392/src/run/multi-repo-context.js +208 -0
- package/.worktrees/8d2d0392/src/run/next.js +128 -0
- package/.worktrees/8d2d0392/src/run/prompt.js +843 -0
- package/.worktrees/8d2d0392/src/run/quick-audit.js +186 -0
- package/.worktrees/8d2d0392/src/run/scan-profile.js +269 -0
- package/.worktrees/8d2d0392/src/run/shared.js +1562 -0
- package/.worktrees/8d2d0392/src/run/stage.js +587 -0
- package/.worktrees/8d2d0392/src/run.js +23 -0
- package/.worktrees/8d2d0392/src/scan-diff.js +388 -0
- package/.worktrees/8d2d0392/src/scan-postcheck.js +524 -0
- package/.worktrees/8d2d0392/src/scan-staleness.js +95 -0
- package/.worktrees/8d2d0392/src/setup.js +414 -0
- package/.worktrees/8d2d0392/src/sillyhub-mcp/client.js +597 -0
- package/.worktrees/8d2d0392/src/sillyhub-mcp/config.js +62 -0
- package/.worktrees/8d2d0392/src/spec-dir-typo.js +50 -0
- package/.worktrees/8d2d0392/src/spec-sync.js +358 -0
- package/.worktrees/8d2d0392/src/stage-contract-engine.js +171 -0
- package/.worktrees/8d2d0392/src/stage-contract-spec.js +551 -0
- package/.worktrees/8d2d0392/src/stage-contract.js +948 -0
- package/.worktrees/8d2d0392/src/stage-review.js +597 -0
- package/.worktrees/8d2d0392/src/stages/archive.js +199 -0
- package/.worktrees/8d2d0392/src/stages/brainstorm-auto.js +285 -0
- package/.worktrees/8d2d0392/src/stages/brainstorm.js +624 -0
- package/.worktrees/8d2d0392/src/stages/cmd-existence.js +145 -0
- package/.worktrees/8d2d0392/src/stages/doctor.js +540 -0
- package/.worktrees/8d2d0392/src/stages/execute.js +1277 -0
- package/.worktrees/8d2d0392/src/stages/explore.js +43 -0
- package/.worktrees/8d2d0392/src/stages/index.js +29 -0
- package/.worktrees/8d2d0392/src/stages/knowledge.js +514 -0
- package/.worktrees/8d2d0392/src/stages/plan-postcheck.js +1398 -0
- package/.worktrees/8d2d0392/src/stages/plan.js +651 -0
- package/.worktrees/8d2d0392/src/stages/quick.js +167 -0
- package/.worktrees/8d2d0392/src/stages/scan.js +608 -0
- package/.worktrees/8d2d0392/src/stages/status.js +64 -0
- package/.worktrees/8d2d0392/src/stages/verify.js +246 -0
- package/.worktrees/8d2d0392/src/sync.js +1470 -0
- package/.worktrees/8d2d0392/src/task-review.js +1498 -0
- package/.worktrees/8d2d0392/src/taskcard-placeholders.js +21 -0
- package/.worktrees/8d2d0392/src/taskcard.js +217 -0
- package/.worktrees/8d2d0392/src/verify-postcheck.js +1718 -0
- package/.worktrees/8d2d0392/src/verify-probes.js +327 -0
- package/.worktrees/8d2d0392/src/version.js +19 -0
- package/.worktrees/8d2d0392/src/workflow.js +600 -0
- package/.worktrees/8d2d0392/src/workspace.js +125 -0
- package/.worktrees/8d2d0392/src/worktree-apply.js +1611 -0
- package/.worktrees/8d2d0392/src/worktree-deps.js +576 -0
- package/.worktrees/8d2d0392/src/worktree.js +1838 -0
- package/.worktrees/8d2d0392/templates/claude-instruction.md +23 -0
- package/.worktrees/8d2d0392/templates/prompts/taskcard-rules.md +29 -0
- package/.worktrees/8d2d0392/templates/prompts/testcase-design.md +12 -0
- package/.worktrees/8d2d0392/templates/prompts/verify-probes.md +32 -0
- package/.worktrees/8d2d0392/templates/workflows/archive-impact.yaml +79 -0
- package/.worktrees/8d2d0392/templates/workflows/scan-docs.yaml +132 -0
- package/architecture-analysis.md +448 -0
- package/package.json +1 -1
- package/src/contract-matrix.js +1 -1
- package/src/endpoint-extractor.js +41 -52
- package/src/foreign-declared.js +78 -1
- package/src/index.js +9 -7
- package/src/run/command.js +2 -1
- package/src/run/gates.js +17 -0
- package/src/run/stage.js +8 -5
- package/src/stage-contract-spec.js +1 -1
- package/src/stages/brainstorm-auto.js +43 -1
- package/src/stages/brainstorm.js +2 -0
- package/src/stages/plan.js +8 -4
- package/src/stages/verify.js +1 -1
- package/src/taskcard.js +33 -0
- package/src/verify-probes.js +27 -6
- package/src/worktree-apply.js +451 -286
- package/src/worktree-deps.js +93 -1
- package/src/worktree.js +84 -19
- package/templates/prompts/taskcard-rules.md +1 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* concurrent-detect — 多 agent 并发写预检的纯函数检测核心(design §7)。
|
|
3
|
+
*
|
|
4
|
+
* 单次 git status --porcelain 扫描,复用 isQuickMetadata 的「关联 vs 他者」分类口径,
|
|
5
|
+
* 产出两类并发信号:
|
|
6
|
+
* - foreignFiles: 脏文件里非 metadata、不在 ownFiles 的真实业务文件(他者未提交改动)
|
|
7
|
+
* - otherActiveChanges: 脏文件落在 .sillyspec/changes/<他者变更>/ 下去重成的变更名集合
|
|
8
|
+
*
|
|
9
|
+
* 非阻塞 advisory:检测结果仅在调用点本地消费(→ formatConcurrentWarning → console.warn),
|
|
10
|
+
* 不改 audit status / gate 通过性 / isQuickMetadata 语义(design §2/§9)。
|
|
11
|
+
*
|
|
12
|
+
* 纯函数无副作用:不写盘、不 console(console 留给调用点 task-02/03)。
|
|
13
|
+
*/
|
|
14
|
+
import { safeGit, parsePorcelainPath, isQuickMetadata } from './shared.js'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 内联解析 .sillyspec/changes/<dir>/ 路径取 <dir>。
|
|
18
|
+
* 与 shared.js isQuickMetadata 内部 regex(^\.sillyspec\/changes\/([^/]+)(\/|$))同源——
|
|
19
|
+
* D-008 deferred:暂不抽 shared.js 公共 helper,改其一改其二。
|
|
20
|
+
* @param {string} file 已归一化(正斜杠)的 git 路径
|
|
21
|
+
* @returns {string|null} 变更目录名,不在 changes/ 下返回 null
|
|
22
|
+
*/
|
|
23
|
+
function extractChangeDir(file) {
|
|
24
|
+
const m = file.match(/^\.sillyspec\/changes\/([^/]+)(\/|$)/)
|
|
25
|
+
return m ? m[1] : null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** git 路径反斜杠归一(跨平台 Windows 兼容)。 */
|
|
29
|
+
function normalizeGitPath(p) {
|
|
30
|
+
return String(p).replace(/\\/g, '/')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 并发预检 ownFiles 锚点(2026-08-18 误归属修复)。
|
|
35
|
+
*
|
|
36
|
+
* 未声明会话(allowedFiles 空)维持旧口径 changedFiles ∪ baselineFiles——无声明即无归属信息,
|
|
37
|
+
* 收窄锚点会把本会话每次未声明的自身改动全误报他者。声明会话锚点 = baselineFiles ∪ allowedFiles
|
|
38
|
+
* (声明即归属):窗口 diff(changedFiles)在多 agent 并发仓库可能含他者污染(ql-20260818-003
|
|
39
|
+
* 实证:并行会话窗口内改的文件被算进本会话 changedFiles 再被 ownFiles 自吞),超出声明集的
|
|
40
|
+
* 窗口新脏文件才作为 foreign 信号报出(= 他者或漏声明,歧义由 advisory 文案兜住)。
|
|
41
|
+
*
|
|
42
|
+
* 纯函数:不查盘不调 git,调用点(complete-handlers task-02 钩子)传入三个列表。
|
|
43
|
+
* @param {{ changedFiles?: string[], baselineFiles?: string[], allowedFiles?: string[] }} opts
|
|
44
|
+
* @returns {string[]} ownFiles 锚点集合(去重)
|
|
45
|
+
*/
|
|
46
|
+
export function resolveConcurrentAnchor({ changedFiles = [], baselineFiles = [], allowedFiles = [] } = {}) {
|
|
47
|
+
const arr = (x) => (Array.isArray(x) ? x : [])
|
|
48
|
+
const declared = arr(allowedFiles).map(normalizeGitPath).filter(Boolean)
|
|
49
|
+
if (declared.length === 0) {
|
|
50
|
+
return [...arr(changedFiles).map(normalizeGitPath), ...arr(baselineFiles).map(normalizeGitPath)]
|
|
51
|
+
}
|
|
52
|
+
return [...new Set([...arr(baselineFiles).map(normalizeGitPath), ...declared])]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 检测工作树里的并发他者改动(非阻塞 advisory 用)。
|
|
57
|
+
*
|
|
58
|
+
* @param {string} cwd 主仓库根
|
|
59
|
+
* @param {{ changeName: string, linkedChanges?: string[], ownFiles?: string[], specDir?: string }} opts
|
|
60
|
+
* - changeName: 当前变更名(排除自身变更目录)
|
|
61
|
+
* - linkedChanges: 关联变更(透传给 isQuickMetadata 的关联归类,同时从 otherActiveChanges 排除)
|
|
62
|
+
* - ownFiles: 本 --done 负责的文件(从 foreignFiles 排除,避免把自己当他者)
|
|
63
|
+
* - specDir: 规范目录(保留参数,当前实现用 .sillyspec/ 前缀 regex 定位,暂未使用)
|
|
64
|
+
* @returns {{ hasForeign: boolean, foreignFiles: string[], otherActiveChanges: string[], gitError: string|null }}
|
|
65
|
+
* - foreignFiles: 脏文件里非 metadata、不在 ownFiles 的真实业务文件
|
|
66
|
+
* - otherActiveChanges: 脏文件落在 .sillyspec/changes/<他者变更>/ 下,去重成的变更名集合
|
|
67
|
+
* - gitError: git status 读失败时填错误串,hasForeign=false(FR-04 fail-open,不抛异常)
|
|
68
|
+
*/
|
|
69
|
+
export function detectConcurrentChanges(cwd, { changeName, linkedChanges = [], ownFiles = [], specDir } = {}) {
|
|
70
|
+
// safeGit 必传 trim:false(D-004):porcelain 首行前导空格是状态码一部分,trim 会削掉致
|
|
71
|
+
// parsePorcelainPath 丢首字符(坑见 shared.js auditQuickCompletion :448 注释)。
|
|
72
|
+
const statusResult = safeGit(cwd, ['status', '--porcelain'], { trim: false })
|
|
73
|
+
|
|
74
|
+
// FR-04 fail-open:git status 读不到不崩、不阻断、不误报(advisory 漏报可接受,最坏漏报而非误阻)。
|
|
75
|
+
if (statusResult.error) {
|
|
76
|
+
return { hasForeign: false, foreignFiles: [], otherActiveChanges: [], gitError: statusResult.error }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const gitStatus = statusResult.value || ''
|
|
80
|
+
// 不整段 trim:同 auditQuickCompletion :455 注释,会削首行前导空格致首文件路径丢首字符。
|
|
81
|
+
const entries = gitStatus.split('\n').filter(Boolean)
|
|
82
|
+
|
|
83
|
+
// 本变更 + 关联变更目录集合:rule1 据此把「自己/关联」从他者变更中排除。
|
|
84
|
+
const ownChangeSet = new Set(
|
|
85
|
+
(Array.isArray(linkedChanges) ? linkedChanges : [])
|
|
86
|
+
.map(normalizeGitPath)
|
|
87
|
+
.filter(Boolean)
|
|
88
|
+
)
|
|
89
|
+
if (changeName) ownChangeSet.add(normalizeGitPath(changeName))
|
|
90
|
+
|
|
91
|
+
// ownFiles 归一化集合:rule3 据此把本会话产出从他者业务文件中排除。
|
|
92
|
+
const ownFileSet = new Set(
|
|
93
|
+
(Array.isArray(ownFiles) ? ownFiles : [])
|
|
94
|
+
.map(normalizeGitPath)
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
const foreignFiles = []
|
|
99
|
+
const otherActiveSet = new Set()
|
|
100
|
+
|
|
101
|
+
for (const line of entries) {
|
|
102
|
+
// parsePorcelainPath:去引号 / 处理 rename / 归一化反斜杠(跨平台)。
|
|
103
|
+
const file = parsePorcelainPath(line)
|
|
104
|
+
if (!file) continue
|
|
105
|
+
|
|
106
|
+
// rule1:落 .sillyspec/changes/<dir>/ 且 <dir> 非本变更非关联 → otherActiveChanges 去重。
|
|
107
|
+
// 'archive' 排除(坑 archive-dir-as-other-change):归档移动产生 .sillyspec/changes/archive/...
|
|
108
|
+
// 脏路径,extractChangeDir 返回 'archive',不排除会把归档动作误报成「他者活跃变更: archive」。
|
|
109
|
+
const dir = extractChangeDir(file)
|
|
110
|
+
if (dir && dir !== 'archive' && !ownChangeSet.has(normalizeGitPath(dir))) {
|
|
111
|
+
otherActiveSet.add(dir)
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// rule2:isQuickMetadata 为 true(quick 元数据 / 关联变更目录 / quicklog/.runtime/modules 等)→ 跳过。
|
|
116
|
+
// 注意:当前变更自己的 changes/<changeName>/ 也走这里——changeName 通常不在 linkedChanges,
|
|
117
|
+
// isQuickMetadata 视为元数据放行,与本变更自身目录不该当他者的语义一致。
|
|
118
|
+
if (isQuickMetadata(file, linkedChanges)) continue
|
|
119
|
+
|
|
120
|
+
// rule3:真实业务文件 → 不在 ownFiles 则归入他者 foreignFiles。
|
|
121
|
+
if (!ownFileSet.has(file)) {
|
|
122
|
+
foreignFiles.push(file)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const otherActiveChanges = [...otherActiveSet]
|
|
127
|
+
const hasForeign = foreignFiles.length > 0 || otherActiveChanges.length > 0
|
|
128
|
+
return { hasForeign, foreignFiles, otherActiveChanges, gitError: null }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 把检测结果格式化为多行 ⚠️ 警告串。
|
|
133
|
+
* @param {{ hasForeign: boolean, foreignFiles?: string[], otherActiveChanges?: string[], gitError?: string|null }|null} detected
|
|
134
|
+
* @returns {string|null} 无他者并发(hasForeign 为 false / null 输入)返回 null,调用点据此跳过 console.warn
|
|
135
|
+
*/
|
|
136
|
+
export function formatConcurrentWarning(detected) {
|
|
137
|
+
if (!detected || !detected.hasForeign) return null
|
|
138
|
+
const foreignFiles = Array.isArray(detected.foreignFiles) ? detected.foreignFiles : []
|
|
139
|
+
const otherActiveChanges = Array.isArray(detected.otherActiveChanges) ? detected.otherActiveChanges : []
|
|
140
|
+
|
|
141
|
+
const lines = ['⚠️ 检测到工作树存在并发他者改动(非阻断 advisory,请人工确认):']
|
|
142
|
+
|
|
143
|
+
if (foreignFiles.length > 0) {
|
|
144
|
+
lines.push('他者业务文件(git-dirty,可能非本变更产出):')
|
|
145
|
+
for (const f of foreignFiles) lines.push(` - ${f}`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (otherActiveChanges.length > 0) {
|
|
149
|
+
// D-005:文案用「脏变更目录」+ git-dirty 标注,勿用「活跃」防与 DB active 状态混淆。
|
|
150
|
+
lines.push('他者脏变更目录(git-dirty,非 DB active):')
|
|
151
|
+
for (const c of otherActiveChanges) lines.push(` - ${c}`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
lines.push('提交请用显式 pathspec 隔离本变更文件,勿 git add . 扫入他者工作。')
|
|
155
|
+
return lines.join('\n')
|
|
156
|
+
}
|