sillyspec 3.23.6 → 3.23.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/.claude/skills/sillyspec-brainstorm/SKILL.md +86 -86
- package/.claude/skills/sillyspec-execute/SKILL.md +1 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +74 -74
- package/.claude/skills/sillyspec-propose/SKILL.md +61 -61
- package/package.json +1 -1
- package/src/db.js +1 -1
- package/src/progress.js +1 -3
- package/src/review-tier.js +65 -0
- package/src/run.js +84 -1
- package/src/stage-review.js +300 -0
- package/src/stages/brainstorm.js +5 -0
- package/src/stages/execute.js +4 -3
- package/src/stages/plan.js +53 -47
- package/src/stages/propose.js +6 -1
- package/src/worktree.js +81 -34
- package/.npm-publish-userconfig.tmp +0 -1
package/src/worktree.js
CHANGED
|
@@ -69,6 +69,79 @@ function gitQuiet(cwd, args) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* 检测 worktree base(主仓库 HEAD)与 origin 默认分支的同步状态(只读:不 ff、不改 HEAD)。
|
|
74
|
+
* 替代旧的 fetch + merge --ff-only:旧逻辑分叉时 ff 失败被静默吞(syncStatus=failed),
|
|
75
|
+
* 成功时 baseHash 锚点又不更新(ff 引入的 main 内容污染 apply patch)。现改为只检测+报告,
|
|
76
|
+
* 不阻断 create,对齐 origin 的动作留给用户/agent。
|
|
77
|
+
*
|
|
78
|
+
* @param {string} cwd 主仓库目录
|
|
79
|
+
* @param {string} baseHash worktree base commit(= 主仓库 HEAD)
|
|
80
|
+
* @returns {{ status: 'up-to-date'|'behind'|'diverged'|'ahead'|'unknown', defaultBranch: string|null, behind: number, ahead: number }}
|
|
81
|
+
*/
|
|
82
|
+
export function computeBaseSync(cwd, baseHash) {
|
|
83
|
+
const diag = { status: 'unknown', defaultBranch: null, behind: 0, ahead: 0 };
|
|
84
|
+
// 推断默认分支:优先 origin/HEAD 指向的真实分支名(修复旧 339-342 的运算符优先级 bug
|
|
85
|
+
// ——旧代码在 origin/HEAD 存在时恒返回 'main',丢弃 symbolic-ref 的真实结果)。
|
|
86
|
+
const headRef = gitQuiet(cwd, 'symbolic-ref refs/remotes/origin/HEAD --short');
|
|
87
|
+
let defaultBranch = headRef ? headRef.replace('origin/', '') : null;
|
|
88
|
+
if (!defaultBranch) {
|
|
89
|
+
// fallback:origin/HEAD 未设置时,探测常见默认分支是否存在
|
|
90
|
+
for (const cand of ['main', 'master']) {
|
|
91
|
+
if (gitQuiet(cwd, `rev-parse --verify --quiet refs/remotes/origin/${cand}`)) {
|
|
92
|
+
defaultBranch = cand;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (!defaultBranch) return diag; // 无 origin 或推不出默认分支 → unknown
|
|
98
|
+
diag.defaultBranch = defaultBranch;
|
|
99
|
+
|
|
100
|
+
// best-effort fetch(只更新 remote-tracking,不改工作区/HEAD/分支;失败静默降级用缓存)
|
|
101
|
+
gitQuiet(cwd, 'fetch origin --quiet');
|
|
102
|
+
const remoteHead = gitQuiet(cwd, `rev-parse --verify --quiet refs/remotes/origin/${defaultBranch}`);
|
|
103
|
+
if (!baseHash || !remoteHead) return diag;
|
|
104
|
+
if (baseHash === remoteHead) {
|
|
105
|
+
diag.status = 'up-to-date';
|
|
106
|
+
return diag;
|
|
107
|
+
}
|
|
108
|
+
const behind = Number(gitQuiet(cwd, `rev-list --count ${baseHash}..${remoteHead}`) || 0);
|
|
109
|
+
const ahead = Number(gitQuiet(cwd, `rev-list --count ${remoteHead}..${baseHash}`) || 0);
|
|
110
|
+
diag.behind = behind;
|
|
111
|
+
diag.ahead = ahead;
|
|
112
|
+
diag.status = (behind > 0 && ahead > 0) ? 'diverged' : (behind > 0 ? 'behind' : 'ahead');
|
|
113
|
+
return diag;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 把 base 同步检测结果打印到 stdout(醒目但不阻断 create)。
|
|
118
|
+
* up-to-date 静默不刷屏;其余给风险等级 + 对齐命令,由用户/agent 决定是否处理。
|
|
119
|
+
*/
|
|
120
|
+
function printSyncReport(diag, baseHash, changeName) {
|
|
121
|
+
const short = (baseHash || '').slice(0, 8);
|
|
122
|
+
if (diag.status === 'up-to-date') return;
|
|
123
|
+
if (diag.status === 'unknown') {
|
|
124
|
+
console.log(`ℹ️ base 同步检测:无 origin 远端或未设置默认分支,跳过(base ${short})`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const b = diag.defaultBranch;
|
|
128
|
+
const ch = changeName || '<change>';
|
|
129
|
+
if (diag.status === 'behind') {
|
|
130
|
+
console.log(`⚠️ base 同步检测:落后 origin/${b} ${diag.behind} 个 commit(base ${short})`);
|
|
131
|
+
console.log(` execute 期间若主仓库对齐 main,apply 回来可能冲突。建议先对齐:`);
|
|
132
|
+
console.log(` git merge --ff-only origin/${b}`);
|
|
133
|
+
console.log(` sillyspec worktree cleanup ${ch} && sillyspec run execute --change ${ch}`);
|
|
134
|
+
console.log(` 或继续,apply 时用 --merge 降级(会引入合并提交)。`);
|
|
135
|
+
} else if (diag.status === 'diverged') {
|
|
136
|
+
console.log(`⚠️ base 同步检测:与 origin/${b} 分叉(领先 ${diag.ahead} / 落后 ${diag.behind},base ${short})`);
|
|
137
|
+
console.log(` apply 回 main 大概率冲突。建议先在主仓库对齐(rebase 或 merge origin/${b}),`);
|
|
138
|
+
console.log(` 再 sillyspec worktree cleanup ${ch} + 重跑 execute。`);
|
|
139
|
+
} else {
|
|
140
|
+
// ahead:本地有未 push 的提交,无 apply 冲突风险
|
|
141
|
+
console.log(`ℹ️ base 同步检测:领先 origin/${b} ${diag.ahead} 个 commit(本地有未 push 的提交,无冲突风险)`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
72
145
|
function parseJSON(raw) {
|
|
73
146
|
try { return JSON.parse(raw); } catch { return null; }
|
|
74
147
|
}
|
|
@@ -328,37 +401,12 @@ export class WorktreeManager {
|
|
|
328
401
|
writeFileSync(join(worktreePath, META_FILE), JSON.stringify(placeholderMeta, null, 2) + '\n');
|
|
329
402
|
} catch {}
|
|
330
403
|
|
|
331
|
-
// 5.5
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// 尝试 merge origin/main(或 origin/master)到 worktree 分支
|
|
339
|
-
const defaultBranch = gitQuiet(this.cwd, 'symbolic-ref refs/remotes/origin/HEAD --short')?.replace('origin/', '')
|
|
340
|
-
|| gitQuiet(this.cwd, 'rev-parse --abbrev-ref origin/main') ? 'main'
|
|
341
|
-
: gitQuiet(this.cwd, 'rev-parse --abbrev-ref origin/master') ? 'master'
|
|
342
|
-
: null;
|
|
343
|
-
|
|
344
|
-
if (defaultBranch) {
|
|
345
|
-
// 检查 worktree 是否落后于远程
|
|
346
|
-
const localHead = gitQuiet(worktreePath, 'rev-parse HEAD');
|
|
347
|
-
const remoteHead = gitQuiet(worktreePath, `rev-parse origin/${defaultBranch}`);
|
|
348
|
-
|
|
349
|
-
if (localHead && remoteHead && localHead !== remoteHead) {
|
|
350
|
-
// 检查是否有共同祖先(避免完全不相关的分支强行 merge)
|
|
351
|
-
const mergeBase = gitQuiet(worktreePath, `merge-base ${localHead} origin/${defaultBranch}`);
|
|
352
|
-
if (mergeBase) {
|
|
353
|
-
git(worktreePath, `merge origin/${defaultBranch} --ff-only`);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
} catch (e) {
|
|
358
|
-
syncStatus = 'failed';
|
|
359
|
-
syncError = e.message || String(e);
|
|
360
|
-
console.warn(`⚠️ worktree 远程同步失败:${syncError}`);
|
|
361
|
-
}
|
|
404
|
+
// 5.5 base 同步检测(只读:不 ff、不改 HEAD;best-effort fetch 失败降级用缓存)。
|
|
405
|
+
// 旧逻辑 fetch + merge --ff-only:分叉时 ff 失败被静默吞(syncStatus=failed),成功时
|
|
406
|
+
// baseHash 锚点又不更新(ff 引入的 main 内容污染 apply patch)。现改为只检测 + 报告,
|
|
407
|
+
// 不阻断 create,对齐 origin 的动作留给用户/agent。
|
|
408
|
+
const syncDiagnostic = computeBaseSync(this.cwd, baseHash);
|
|
409
|
+
printSyncReport(syncDiagnostic, baseHash, name);
|
|
362
410
|
|
|
363
411
|
// 5.6 Dirty baseline overlay:将主工作区未提交变更同步到 worktree
|
|
364
412
|
const baselineResult = this._overlayBaseline(this.cwd, worktreePath);
|
|
@@ -394,8 +442,7 @@ export class WorktreeManager {
|
|
|
394
442
|
baselineFiles,
|
|
395
443
|
baselineCommit,
|
|
396
444
|
baselineHash,
|
|
397
|
-
|
|
398
|
-
...(syncError ? { syncError } : {}),
|
|
445
|
+
syncDiagnostic,
|
|
399
446
|
depsStatus: deps.depsStatus,
|
|
400
447
|
depsMethod: deps.depsMethod || null,
|
|
401
448
|
depsSource: deps.depsSource || null,
|
|
@@ -407,7 +454,7 @@ export class WorktreeManager {
|
|
|
407
454
|
const metaPath = join(worktreePath, META_FILE);
|
|
408
455
|
writeFileSync(metaPath, JSON.stringify(meta, null, 2) + '\n');
|
|
409
456
|
|
|
410
|
-
return { branch, worktreePath, baseHash, mode: meta.mode };
|
|
457
|
+
return { branch, worktreePath, baseHash, mode: meta.mode, syncDiagnostic };
|
|
411
458
|
}
|
|
412
459
|
|
|
413
460
|
/**
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|