@jspg-ai/coding-bb 0.0.1
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.md +41 -0
- package/cbb/dev-standards/rules/cbb-ai-behavior.md +104 -0
- package/cbb/dev-standards/rules/cbb-coding-rule.md +50 -0
- package/cbb/dev-standards/rules/cbb-priority.md +56 -0
- package/cbb/dev-standards/skills/architecture-specs/SKILL.md +129 -0
- package/cbb/dev-standards/skills/coding-specs/SKILL.md +376 -0
- package/cbb/dev-standards/skills/coding-specs/references/concurrency.md +53 -0
- package/cbb/dev-standards/skills/coding-specs/references/config-center.md +16 -0
- package/cbb/dev-standards/skills/coding-specs/references/distributed.md +42 -0
- package/cbb/dev-standards/skills/coding-specs/references/es-coding.md +60 -0
- package/cbb/dev-standards/skills/coding-specs/references/scheduled-task.md +25 -0
- package/cbb/dev-standards/skills/coding-specs/references/security.md +11 -0
- package/cbb/dev-standards/skills/coding-specs/references/unit-testing.md +43 -0
- package/cbb/dev-standards/skills/es-design-specs/SKILL.md +104 -0
- package/cbb/dev-standards/skills/mysql-design-specs/SKILL.md +89 -0
- package/cbb/lib/install/claude-code.js +20 -0
- package/cbb/lib/install/cleanup.js +64 -0
- package/cbb/lib/install/init.js +1875 -0
- package/cbb/lib/install/qoder.js +20 -0
- package/cbb/lib/install/workspaces.js +232 -0
- package/cbb/lib/openspec/index.js +554 -0
- package/cbb/lib/superpowers/index.js +265 -0
- package/cbb/lib/utils/check-update.js +147 -0
- package/cbb/lib/utils/checkbox.js +383 -0
- package/cbb/lib/utils/gitignore.js +69 -0
- package/cbb/lib/utils/output.js +64 -0
- package/cbb/lib/utils/settings.js +119 -0
- package/cbb/lib/utils/version.js +135 -0
- package/cbb/lib/wiki/api-client.js +358 -0
- package/cbb/lib/wiki/cli.js +427 -0
- package/cbb/lib/wiki/convert.js +464 -0
- package/cbb/lib/wiki/index.js +218 -0
- package/cbb/lib/wiki/mermaid-guard.js +103 -0
- package/cbb/lib/wiki/split.js +131 -0
- package/cbb/tools/cbb-decompile-jar/SKILL.md +220 -0
- package/cbb/tools/cbb-decompile-jar/scripts/decompile.py +863 -0
- package/cbb/tools/cbb-design-to-wiki/SKILL.md +180 -0
- package/cbb/tools/cbb-mvn-guide/SKILL.md +281 -0
- package/cbb/tools/cbb-mvn-guide/scripts/mvn_jdk_manager.py +378 -0
- package/cbb/tools/cbb-mvn-guide/scripts/mvn_pom_jdk_reader.py +197 -0
- package/cbb/tools/cbb-plantuml-authoring/SKILL.md +448 -0
- package/cbb/tools/cbb-plantuml-authoring/references/drawing-templates.md +479 -0
- package/cbb/tools/cbb-wiki-ops/SKILL.md +134 -0
- package/cbb/tools/cbb-wiki-ops/scripts/check-auth.js +44 -0
- package/cbb/worktrees/_shared/scripts/find-target-worktree.js +96 -0
- package/cbb/worktrees/_shared/scripts/find-workspace-root.js +67 -0
- package/cbb/worktrees/_shared/scripts/push-core.js +136 -0
- package/cbb/worktrees/_shared/scripts/silence-popup.js +23 -0
- package/cbb/worktrees/commands/close.md +64 -0
- package/cbb/worktrees/commands/extend.md +65 -0
- package/cbb/worktrees/commands/init.md +55 -0
- package/cbb/worktrees/commands/push.md +42 -0
- package/cbb/worktrees/skills/openspec-close-worktree/SKILL.md +449 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/check-env.js +76 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/check-unarchived.js +48 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/delete-branches.js +136 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/discover-apps.js +76 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/find-target-worktree.js +96 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/find-workspace-root.js +67 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/remove-worktrees.js +124 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/safety-check.js +174 -0
- package/cbb/worktrees/skills/openspec-close-worktree/scripts/silence-popup.js +23 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/SKILL.md +390 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-env.js +95 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/check-repos.js +98 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/create-branches-and-worktrees.js +135 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/find-target-worktree.js +96 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/find-workspace-root.js +67 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/install-ai.js +150 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/list-available-apps.js +88 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/push-core.js +136 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/silence-popup.js +23 -0
- package/cbb/worktrees/skills/openspec-extend-worktree/scripts/sync-repos.js +93 -0
- package/cbb/worktrees/skills/openspec-init-worktree/SKILL.md +539 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/auto-open.js +136 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/check-env-deep.js +87 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/check-env.js +90 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/check-repos.js +171 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/create-branches.js +103 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/create-worktrees.js +154 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/find-workspace-root.js +67 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/generate-app-options.js +93 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/install-ai.js +177 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/parse-config.js +67 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/push-branches.js +101 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/push-core.js +136 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/silence-popup.js +23 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/sync-repos.js +95 -0
- package/cbb/worktrees/skills/openspec-init-worktree/scripts/update-gitignore.js +134 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/SKILL.md +307 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/commit-worktrees.js +269 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/find-target-worktree.js +96 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/find-workspace-root.js +67 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/push-core.js +136 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/push-worktrees.js +159 -0
- package/cbb/worktrees/skills/openspec-push-worktrees/scripts/silence-popup.js +23 -0
- package/config/cbb.yaml +6 -0
- package/config/config.sample.json +16 -0
- package/config/openspec/config.yaml +28 -0
- package/config/openspec/schemas/spec-driven/schema.yaml +247 -0
- package/config/openspec/schemas/spec-driven/templates/design.md +475 -0
- package/config/openspec/schemas/spec-driven/templates/proposal.md +58 -0
- package/config/openspec/schemas/spec-driven/templates/spec.md +8 -0
- package/config/openspec/schemas/spec-driven/templates/tasks.md +9 -0
- package/config/workspaces.json +15 -0
- package/openspec/.version +6 -0
- package/openspec/commands/apply.md +182 -0
- package/openspec/commands/archive.md +223 -0
- package/openspec/commands/bulk-archive.md +334 -0
- package/openspec/commands/continue.md +112 -0
- package/openspec/commands/explore.md +206 -0
- package/openspec/commands/ff.md +111 -0
- package/openspec/commands/new.md +70 -0
- package/openspec/commands/onboard.md +555 -0
- package/openspec/commands/propose.md +157 -0
- package/openspec/commands/sync.md +256 -0
- package/openspec/commands/update.md +85 -0
- package/openspec/commands/verify.md +169 -0
- package/openspec/skills/openspec-apply-change/SKILL.md +187 -0
- package/openspec/skills/openspec-archive-change/SKILL.md +181 -0
- package/openspec/skills/openspec-bulk-archive-change/SKILL.md +338 -0
- package/openspec/skills/openspec-continue-change/SKILL.md +117 -0
- package/openspec/skills/openspec-explore/SKILL.md +342 -0
- package/openspec/skills/openspec-ff-change/SKILL.md +116 -0
- package/openspec/skills/openspec-new-change/SKILL.md +76 -0
- package/openspec/skills/openspec-onboard/SKILL.md +560 -0
- package/openspec/skills/openspec-propose/SKILL.md +162 -0
- package/openspec/skills/openspec-sync-specs/SKILL.md +261 -0
- package/openspec/skills/openspec-update-change/SKILL.md +90 -0
- package/openspec/skills/openspec-verify-change/SKILL.md +174 -0
- package/package.json +46 -0
- package/superpowers/.version +6 -0
- package/superpowers/skills/brainstorming/SKILL.md +250 -0
- package/superpowers/skills/brainstorming/scripts/frame-template.html +213 -0
- package/superpowers/skills/brainstorming/scripts/helper.js +167 -0
- package/superpowers/skills/brainstorming/scripts/server.cjs +723 -0
- package/superpowers/skills/brainstorming/scripts/start-server.sh +209 -0
- package/superpowers/skills/brainstorming/scripts/stop-server.sh +120 -0
- package/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/superpowers/skills/brainstorming/visual-companion.md +299 -0
- package/superpowers/skills/dispatching-parallel-agents/SKILL.md +167 -0
- package/superpowers/skills/executing-plans/SKILL.md +64 -0
- package/superpowers/skills/finishing-a-development-branch/SKILL.md +225 -0
- package/superpowers/skills/receiving-code-review/SKILL.md +205 -0
- package/superpowers/skills/requesting-code-review/SKILL.md +95 -0
- package/superpowers/skills/requesting-code-review/code-reviewer.md +181 -0
- package/superpowers/skills/subagent-driven-development/SKILL.md +568 -0
- package/superpowers/skills/subagent-driven-development/implementer-prompt.md +154 -0
- package/superpowers/skills/subagent-driven-development/re-review-prompt.md +115 -0
- package/superpowers/skills/subagent-driven-development/scripts/review-package +46 -0
- package/superpowers/skills/subagent-driven-development/scripts/sdd-workspace +40 -0
- package/superpowers/skills/subagent-driven-development/scripts/task-brief +41 -0
- package/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +207 -0
- package/superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/superpowers/skills/systematic-debugging/SKILL.md +283 -0
- package/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/superpowers/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/superpowers/skills/systematic-debugging/find-polluter.sh +72 -0
- package/superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/superpowers/skills/systematic-debugging/test-academic.md +14 -0
- package/superpowers/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/superpowers/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/superpowers/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/superpowers/skills/test-driven-development/SKILL.md +320 -0
- package/superpowers/skills/test-driven-development/writing-good-tests.md +198 -0
- package/superpowers/skills/using-git-worktrees/SKILL.md +167 -0
- package/superpowers/skills/using-superpowers/SKILL.md +63 -0
- package/superpowers/skills/using-superpowers/references/antigravity-tools.md +23 -0
- package/superpowers/skills/using-superpowers/references/codex-tools.md +108 -0
- package/superpowers/skills/using-superpowers/references/gemini-tools.md +63 -0
- package/superpowers/skills/using-superpowers/references/hermes-tools.md +56 -0
- package/superpowers/skills/using-superpowers/references/pi-tools.md +16 -0
- package/superpowers/skills/verification-before-completion/SKILL.md +120 -0
- package/superpowers/skills/writing-plans/SKILL.md +171 -0
- package/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/superpowers/skills/writing-skills/SKILL.md +679 -0
- package/superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/superpowers/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/superpowers/skills/writing-skills/persuasion-principles.md +187 -0
- package/superpowers/skills/writing-skills/render-graphs.js +169 -0
- package/superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require('./silence-popup');
|
|
2
|
+
/**
|
|
3
|
+
* openspec-shared 工作空间根目录定位
|
|
4
|
+
*
|
|
5
|
+
* 唯一真源:cbb/worktrees/_shared/scripts/find-workspace-root.js
|
|
6
|
+
* 由 scripts/sync-shared.js 展开到各 skill 的 scripts/ 下(init / extend / close / push)。
|
|
7
|
+
*
|
|
8
|
+
* 优先:git worktree list 第一行(主仓库根目录,不受 worktree 内 CONFIG.json 副本干扰)
|
|
9
|
+
* 兜底:当前目录含 CONFIG.json
|
|
10
|
+
*
|
|
11
|
+
* 退出码:
|
|
12
|
+
* 0 - 定位成功,输出 WS_ROOT 绝对路径
|
|
13
|
+
* 1 - 未找到工作空间根目录
|
|
14
|
+
*
|
|
15
|
+
* 输出格式:
|
|
16
|
+
* 第一行:人类可读报告
|
|
17
|
+
* 最后一行:纯路径(便于 AI agent 解析后用于 cd)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { execSync } = require('child_process');
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
function tryExec(cmd) {
|
|
25
|
+
try {
|
|
26
|
+
return { ok: true, out: execSync(cmd, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] }).trim() };
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return { ok: false };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let wsRoot = '';
|
|
33
|
+
|
|
34
|
+
// 优先:git worktree list 第一行
|
|
35
|
+
const worktreeList = tryExec('git worktree list');
|
|
36
|
+
if (worktreeList.ok) {
|
|
37
|
+
const firstLine = worktreeList.out.split('\n')[0];
|
|
38
|
+
if (firstLine) {
|
|
39
|
+
// 格式:<path> <sha> [<branch>]
|
|
40
|
+
const candidate = firstLine.split(/\s+/)[0];
|
|
41
|
+
if (candidate && fs.existsSync(candidate)) {
|
|
42
|
+
wsRoot = candidate;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 兜底:当前目录含 CONFIG.json
|
|
48
|
+
if (!wsRoot) {
|
|
49
|
+
const cwd = process.cwd();
|
|
50
|
+
if (fs.existsSync(path.join(cwd, 'CONFIG.json'))) {
|
|
51
|
+
wsRoot = cwd;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 校验
|
|
56
|
+
if (!wsRoot || !fs.existsSync(path.join(wsRoot, 'CONFIG.json'))) {
|
|
57
|
+
console.error('❌ 未找到工作空间根目录');
|
|
58
|
+
console.error(' 原因:git worktree list 失败且当前目录无 CONFIG.json');
|
|
59
|
+
console.error(' 请在业务工作空间根目录(由 cbb setup 初始化、包含 CONFIG.json)下执行本技能');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.log('✅ 工作空间根目录:' + wsRoot);
|
|
64
|
+
console.log('---');
|
|
65
|
+
// 最后一行输出纯路径,AI agent 可直接 cd 使用
|
|
66
|
+
console.log(wsRoot);
|
|
67
|
+
process.exit(0);
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require('./silence-popup');
|
|
2
|
+
/**
|
|
3
|
+
* openspec-shared 推送核心模块
|
|
4
|
+
*
|
|
5
|
+
* 唯一真源:cbb/worktrees/_shared/scripts/push-core.js
|
|
6
|
+
* 由 scripts/sync-shared.js 展开到各 skill 的 scripts/ 下(init-worktree / extend-worktree / push-worktrees)。
|
|
7
|
+
*
|
|
8
|
+
* 公开 API:
|
|
9
|
+
* - tryExec(cmd, cwd, execImpl) 包装 execSync,统一返回 {ok, out|err}
|
|
10
|
+
* - escapeBranch(name) 分支名 shell 转义(防命令注入)
|
|
11
|
+
* - classifyError(stderr) 将 stderr 归类为 8 种错误码之一
|
|
12
|
+
* - pushSingle(repoPath, repoName, branch) 推送单个仓库并建立 upstream
|
|
13
|
+
* - collectRemoteMetadata(repoPath, branch, remote) 采集远程 commit 元信息
|
|
14
|
+
*
|
|
15
|
+
* 错误码契约(classifyError 输出):
|
|
16
|
+
* - ssh-auth-failed Permission denied (publickey)
|
|
17
|
+
* - https-auth-failed Authentication failed
|
|
18
|
+
* - network-unreachable Could not resolve host / hung up / connection refused
|
|
19
|
+
* - branch-protected protected branch / deny updating
|
|
20
|
+
* - remote-exists already exists
|
|
21
|
+
* - no-remote (pushSingle 内部判定)
|
|
22
|
+
* - non-fast-forward non-fast-forward
|
|
23
|
+
* - unknown 兜底
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const { execSync: realExecSync } = require('child_process');
|
|
27
|
+
|
|
28
|
+
const PUSH_TIMEOUT_MS = 60000;
|
|
29
|
+
|
|
30
|
+
function tryExec(cmd, cwd, execImpl) {
|
|
31
|
+
const execSync = (execImpl || realExecSync);
|
|
32
|
+
try {
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
out: execSync(cmd, { cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: PUSH_TIMEOUT_MS }).toString().trim(),
|
|
36
|
+
};
|
|
37
|
+
} catch (e) {
|
|
38
|
+
const stderr = (e.stderr ? e.stderr.toString() : e.message || String(e)).trim();
|
|
39
|
+
return { ok: false, err: stderr };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function escapeBranch(name) {
|
|
44
|
+
return '"' + String(name).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function classifyError(stderr) {
|
|
48
|
+
const s = (stderr || '').toLowerCase();
|
|
49
|
+
if (s.includes('permission denied (publickey)')) return 'ssh-auth-failed';
|
|
50
|
+
if (s.includes('authentication failed')) return 'https-auth-failed';
|
|
51
|
+
if (s.includes('could not resolve host') || s.includes('hung up') || s.includes('connection refused')) {
|
|
52
|
+
return 'network-unreachable';
|
|
53
|
+
}
|
|
54
|
+
if (s.includes('protected branch') || s.includes('deny updating')) return 'branch-protected';
|
|
55
|
+
if (s.includes('already exists')) return 'remote-exists';
|
|
56
|
+
if (s.includes('non-fast-forward')) return 'non-fast-forward';
|
|
57
|
+
return 'unknown';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 采集远程分支的元信息(hash/author/date/message + 本地 hash)
|
|
62
|
+
* 任意子命令失败时返回 null(不抛错)
|
|
63
|
+
* @param {string} repoPath
|
|
64
|
+
* @param {string} branch
|
|
65
|
+
* @param {string} remote
|
|
66
|
+
* @param {{ execSyncImpl?: Function }} [opts]
|
|
67
|
+
* @returns {{ remote: {hash, author, date, message}, local: {hash} } | null}
|
|
68
|
+
*/
|
|
69
|
+
function collectRemoteMetadata(repoPath, branch, remote, opts) {
|
|
70
|
+
const execImpl = opts && opts.execSyncImpl;
|
|
71
|
+
try {
|
|
72
|
+
const remoteHash = tryExec('git rev-parse ' + escapeBranch(remote + '/' + branch), repoPath, execImpl);
|
|
73
|
+
if (!remoteHash.ok || !remoteHash.out) return null;
|
|
74
|
+
|
|
75
|
+
const remoteLog = tryExec('git log -1 --format="%an <%ae> | %ad | %s" ' + escapeBranch(remote + '/' + branch), repoPath, execImpl);
|
|
76
|
+
const localHash = tryExec('git rev-parse ' + escapeBranch(branch), repoPath, execImpl);
|
|
77
|
+
if (!remoteLog.ok || !localHash.ok || !localHash.out) return null;
|
|
78
|
+
|
|
79
|
+
// 解析 "author <email> | date | message"
|
|
80
|
+
const parts = remoteLog.out.split(' | ');
|
|
81
|
+
const author = parts[0] || '';
|
|
82
|
+
const date = parts[1] || '';
|
|
83
|
+
const message = parts.slice(2).join(' | ') || '';
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
remote: {
|
|
87
|
+
hash: remoteHash.out,
|
|
88
|
+
author,
|
|
89
|
+
date,
|
|
90
|
+
message,
|
|
91
|
+
},
|
|
92
|
+
local: {
|
|
93
|
+
hash: localHash.out,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
} catch (e) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 推送单个仓库的分支并建立 upstream
|
|
103
|
+
* @param {string} repoPath
|
|
104
|
+
* @param {string} repoName
|
|
105
|
+
* @param {string} branch
|
|
106
|
+
* @param {{ execSyncImpl?: Function }} [opts]
|
|
107
|
+
* @returns {{ name: string, status: string, upstream?: string, error?: string, message?: string, metadata?: object }}
|
|
108
|
+
*/
|
|
109
|
+
function pushSingle(repoPath, repoName, branch, opts) {
|
|
110
|
+
const execImpl = opts && opts.execSyncImpl;
|
|
111
|
+
const remote = tryExec('git remote', repoPath, execImpl);
|
|
112
|
+
if (!remote.ok || !remote.out) {
|
|
113
|
+
return { name: repoName, status: 'failed', error: 'no-remote', message: '无远程配置' };
|
|
114
|
+
}
|
|
115
|
+
const r = remote.out.split(/\r?\n/)[0];
|
|
116
|
+
const branchEsc = escapeBranch(branch);
|
|
117
|
+
const remoteEsc = '"' + String(r).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
|
118
|
+
const pushResult = tryExec('git push -u ' + remoteEsc + ' ' + branchEsc, repoPath, execImpl);
|
|
119
|
+
if (pushResult.ok) {
|
|
120
|
+
return { name: repoName, status: 'pushed', upstream: r + '/' + branch };
|
|
121
|
+
}
|
|
122
|
+
const errCode = classifyError(pushResult.err);
|
|
123
|
+
if (errCode === 'remote-exists') {
|
|
124
|
+
const metadata = collectRemoteMetadata(repoPath, branch, r, opts);
|
|
125
|
+
return {
|
|
126
|
+
name: repoName,
|
|
127
|
+
status: 'remote-exists',
|
|
128
|
+
error: errCode,
|
|
129
|
+
message: '远程分支已存在,需用户决策',
|
|
130
|
+
metadata,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return { name: repoName, status: 'failed', error: errCode, message: pushResult.err.split(/\r?\n/)[0] };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = { tryExec, escapeBranch, classifyError, pushSingle, collectRemoteMetadata };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require('./silence-popup');
|
|
2
|
+
/**
|
|
3
|
+
* openspec-push-worktrees 推送所有 worktree 仓库
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* node push-worktrees.js <ws_root> <branch>
|
|
7
|
+
* node push-worktrees.js <wt_path> # wt_path 必须是 <ws_root>/.worktrees/worktree-<branch> 形态
|
|
8
|
+
*
|
|
9
|
+
* 行为:
|
|
10
|
+
* 1. 扫描 <ws_root>/.worktrees/worktree-<branch>/ 下所有 git 仓库(workspace + 各 app 子 worktree)
|
|
11
|
+
* 2. 对每个仓库调 push-core.pushSingle(已建立 upstream 时直接 push -u;否则建立 upstream)
|
|
12
|
+
* 3. 逐个报告状态,输出末行 PUSH_RESULT
|
|
13
|
+
*
|
|
14
|
+
* 退出码:
|
|
15
|
+
* 0 - 全部推送成功
|
|
16
|
+
* 1 - 参数错误(ws_root/branch 无法识别)
|
|
17
|
+
* 2 - 部分或全部推送失败(AI agent 必须阻塞流程)
|
|
18
|
+
*
|
|
19
|
+
* 与 init-worktree/scripts/push-branches.js 的区别:
|
|
20
|
+
* - push-branches.js 推送 workspace 主目录 + .code/<app>/(创建 worktree 时使用)
|
|
21
|
+
* - push-worktrees.js 推送 .worktrees/worktree-<branch>/ 下所有 worktree(开发完成时使用)
|
|
22
|
+
*
|
|
23
|
+
* 错误码契约复用 push-core.classifyError:
|
|
24
|
+
* - pushed / remote-exists / failed(ssh-auth-failed / https-auth-failed / network-unreachable
|
|
25
|
+
* / branch-protected / no-remote / non-fast-forward / unknown)
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const { pushSingle } = require('./push-core');
|
|
29
|
+
const fs = require('fs');
|
|
30
|
+
const path = require('path');
|
|
31
|
+
|
|
32
|
+
function resolveInputs(argv) {
|
|
33
|
+
// argv[2] 单独:wt_path 形式
|
|
34
|
+
// argv[2] + argv[3]:ws_root + branch 形式
|
|
35
|
+
if (argv.length === 3) {
|
|
36
|
+
const wtPath = path.resolve(argv[2]);
|
|
37
|
+
// 形如 <ws_root>/.worktrees/worktree-<branch>
|
|
38
|
+
const m = wtPath.match(/^(.+)[\\\/]\.worktrees[\\\/]worktree-(.+)$/);
|
|
39
|
+
if (!m) {
|
|
40
|
+
return { error: 'argv[2] 必须是 <ws_root>/.worktrees/worktree-<branch> 形态,或同时传 ws_root + branch' };
|
|
41
|
+
}
|
|
42
|
+
return { wsRoot: m[1], branch: m[2] };
|
|
43
|
+
}
|
|
44
|
+
if (argv.length >= 4) {
|
|
45
|
+
return { wsRoot: path.resolve(argv[2]), branch: argv[3] };
|
|
46
|
+
}
|
|
47
|
+
return { error: '用法:node push-worktrees.js <ws_root> <branch> | node push-worktrees.js <wt_path>' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isGitRepo(p) {
|
|
51
|
+
try {
|
|
52
|
+
// worktree 顶层是 .git 文件(gitdir 指向真实 .git),主目录是 .git 目录
|
|
53
|
+
return fs.existsSync(path.join(p, '.git'));
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 扫描 worktree 下的所有 git 仓库
|
|
61
|
+
* - workspace worktree:<ws_root>/.worktrees/worktree-<branch>/ 本身
|
|
62
|
+
* - app worktree:<ws_root>/.worktrees/worktree-<branch>/<app_name>/(嵌套子目录)
|
|
63
|
+
*/
|
|
64
|
+
function scanWorktreeRepos(wsRoot, branch) {
|
|
65
|
+
const wtRoot = path.join(wsRoot, '.worktrees', 'worktree-' + branch);
|
|
66
|
+
const repos = [];
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(wtRoot)) {
|
|
69
|
+
return { wtRoot, repos, error: 'worktree 目录不存在:' + wtRoot };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// workspace worktree(自身是 git worktree)
|
|
73
|
+
if (isGitRepo(wtRoot)) {
|
|
74
|
+
repos.push({ name: 'workspace', wt: wtRoot, repo: wsRoot });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// app worktree(嵌套子目录)
|
|
78
|
+
const entries = fs.readdirSync(wtRoot, { withFileTypes: true });
|
|
79
|
+
for (const e of entries) {
|
|
80
|
+
if (!e.isDirectory()) continue;
|
|
81
|
+
// 跳过 . 开头的隐藏目录(如 .va、.claude、.qoder)
|
|
82
|
+
if (e.name.startsWith('.')) continue;
|
|
83
|
+
const appWt = path.join(wtRoot, e.name);
|
|
84
|
+
if (!isGitRepo(appWt)) continue;
|
|
85
|
+
repos.push({ name: e.name, wt: appWt, repo: path.join(wsRoot, '.code', e.name) });
|
|
86
|
+
}
|
|
87
|
+
return { wtRoot, repos, error: null };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 编排:扫描 + 逐个 push
|
|
92
|
+
* @param {{ wsRoot: string, branch: string, execSyncImpl?: Function }} opts
|
|
93
|
+
* @returns {{ wtRoot: string, results: Array }}
|
|
94
|
+
*/
|
|
95
|
+
function executePush(opts) {
|
|
96
|
+
const { wtRoot, repos, error } = scanWorktreeRepos(opts.wsRoot, opts.branch);
|
|
97
|
+
if (error) {
|
|
98
|
+
return { wtRoot, results: [], error };
|
|
99
|
+
}
|
|
100
|
+
const results = [];
|
|
101
|
+
for (const r of repos) {
|
|
102
|
+
// push 在 worktree 目录下执行(worktree 自带 .git 引用,git push 走 worktree 检出状态)
|
|
103
|
+
const pushOpts = { execSyncImpl: opts.execSyncImpl };
|
|
104
|
+
const res = pushSingle(r.wt, r.name, opts.branch, pushOpts);
|
|
105
|
+
results.push(res);
|
|
106
|
+
}
|
|
107
|
+
return { wtRoot, results, error: null };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 导出供单测
|
|
111
|
+
module.exports = { resolveInputs, scanWorktreeRepos, executePush };
|
|
112
|
+
|
|
113
|
+
function main() {
|
|
114
|
+
const inputs = resolveInputs(process.argv);
|
|
115
|
+
if (inputs.error) {
|
|
116
|
+
console.error('❌ ' + inputs.error);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log('=== 推送所有 worktree 仓库 ===');
|
|
121
|
+
console.log('工作空间根:' + inputs.wsRoot);
|
|
122
|
+
console.log('需求名(分支):' + inputs.branch);
|
|
123
|
+
|
|
124
|
+
const { wtRoot, results, error } = executePush({ wsRoot: inputs.wsRoot, branch: inputs.branch });
|
|
125
|
+
|
|
126
|
+
if (error) {
|
|
127
|
+
console.error('❌ ' + error);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (results.length === 0) {
|
|
132
|
+
console.log('⚠️ 未在 worktree 下发现任何 git 仓库');
|
|
133
|
+
console.log('---');
|
|
134
|
+
console.log('PUSH_RESULT', JSON.stringify([]));
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 人类可读报告
|
|
139
|
+
for (const r of results) {
|
|
140
|
+
if (r.status === 'pushed') {
|
|
141
|
+
console.log('✅ ' + r.name + ': 已推送 ' + r.upstream);
|
|
142
|
+
} else if (r.status === 'remote-exists') {
|
|
143
|
+
console.log('⚠️ ' + r.name + ': 远程分支已存在(需用户决策)');
|
|
144
|
+
} else {
|
|
145
|
+
console.error('❌ ' + r.name + ': 推送失败 - ' + (r.message || r.error));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const hasFailure = results.some(r => r.status === 'failed' || r.status === 'remote-exists');
|
|
150
|
+
|
|
151
|
+
console.log('---');
|
|
152
|
+
console.log('PUSH_RESULT', JSON.stringify(results));
|
|
153
|
+
process.exit(hasFailure ? 2 : 0);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// CLI 入口
|
|
157
|
+
if (require.main === module) {
|
|
158
|
+
main();
|
|
159
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// silence-popup.js (shared)
|
|
2
|
+
// Silences Windows 0xc0000142 (STATUS_DLL_INIT_FAILED) popups from child git processes.
|
|
3
|
+
// Cross-platform: only effective on Windows; no-op on macOS/Linux.
|
|
4
|
+
// koffi is resolved from cbb global install (no local node_modules needed).
|
|
5
|
+
//
|
|
6
|
+
// 唯一真源:cbb/worktrees/_shared/scripts/silence-popup.js
|
|
7
|
+
// 由 scripts/sync-shared.js 展开到各 skill 的 scripts/ 下(init / extend / close / push)。
|
|
8
|
+
// skill 内同目录引用:脚本第一行 `require('./silence-popup')`。
|
|
9
|
+
if (process.platform === 'win32') {
|
|
10
|
+
try {
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
const globalRoot = execSync('npm root -g', { stdio: 'pipe', timeout: 5000 }).toString().trim();
|
|
14
|
+
const koffiPath = path.join(globalRoot, '@jspg-ai', 'coding-bb', 'node_modules', 'koffi');
|
|
15
|
+
const koffi = require(koffiPath);
|
|
16
|
+
const kernel32 = koffi.load('kernel32.dll');
|
|
17
|
+
const SetErrorMode = kernel32.func('uint __stdcall SetErrorMode(uint uMode)');
|
|
18
|
+
// SEM_FAILCRITICALERRORS=0x0001 | SEM_NOGPFAULTERRORBOX=0x0002 | SEM_NOOPENFILEERRORBOX=0x8000
|
|
19
|
+
SetErrorMode(0x0001 | 0x0002 | 0x8000);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// koffi unavailable or SetErrorMode failed: silently continue (worst case: popups may reappear)
|
|
22
|
+
}
|
|
23
|
+
}
|
package/config/cbb.yaml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "AI 开发工作空间配置样例(请勿修改本文件,复制为工作空间根目录的 CONFIG.json 后编辑使用)。apps 数组中每个对象需包含:name(应用名,同时作为 .code/ 子目录名)、repo(Git 仓库地址,支持 SSH 或 HTTPS)、side(仓库类型,'front' 表示前端工程,'back' 表示后端工程)。",
|
|
3
|
+
"apps":
|
|
4
|
+
[
|
|
5
|
+
{
|
|
6
|
+
"name": "ep-user-auth-app",
|
|
7
|
+
"repo": "git@code.example.com:team/ep-user-auth-app.git",
|
|
8
|
+
"side": "back"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "ep-user-web",
|
|
12
|
+
"repo": "git@code.example.com:team/ep-user-web.git",
|
|
13
|
+
"side": "front"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
schema: spec-driven
|
|
2
|
+
|
|
3
|
+
context: |
|
|
4
|
+
# 工作目录约束
|
|
5
|
+
不要 cd 进子仓库目录。读取代码用绝对路径,所有命令在当前目录执行。
|
|
6
|
+
|
|
7
|
+
语言:中文(简体)
|
|
8
|
+
所有文档必须使用中文简体编写,但技术术语保持英文,如 API、REST、RPC、MQ 等。
|
|
9
|
+
|
|
10
|
+
# Java 技术栈
|
|
11
|
+
|
|
12
|
+
## 核心框架
|
|
13
|
+
- Java 1.8 或 Java 21
|
|
14
|
+
- Spring Boot 2.0.4.RELEASE
|
|
15
|
+
- Ironman 1.3.19
|
|
16
|
+
|
|
17
|
+
## 中间件
|
|
18
|
+
- 数据库:Zebra 分库分表 (Ironman-SQL)
|
|
19
|
+
- RPC:Pigeon (Ironman-RPC)
|
|
20
|
+
- 消息队列:RocketMQ (Ironman-MQ)
|
|
21
|
+
- 缓存:Redis (Ironman-Redis)
|
|
22
|
+
- 配置中心:Lion (Ironman-Config)
|
|
23
|
+
|
|
24
|
+
## 文档写作原则
|
|
25
|
+
- 每个章节先给结论或概览,再展开细节
|
|
26
|
+
- 用业务语言描述目标和规则,用技术语言描述实现方式
|
|
27
|
+
- 图优于表,表优于列表,列表优于段落
|
|
28
|
+
- 只写不显而易见的内容,标准实践不写
|