@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,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qoder 适配器
|
|
3
|
+
*
|
|
4
|
+
* 将编码规范分发到 .qoder/rules/ 目录
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
name: 'qoder',
|
|
12
|
+
displayName: 'Qoder',
|
|
13
|
+
rulesDir: '.qoder/rules',
|
|
14
|
+
skillsDir: '.qoder/skills',
|
|
15
|
+
commandsDir: '.qoder/commands/opsx',
|
|
16
|
+
// 用户级 settings,写入全局生效,不污染项目目录
|
|
17
|
+
settingsFile: path.join(os.homedir(), '.qoder', 'settings.json'),
|
|
18
|
+
settingsFileIsUserLevel: true,
|
|
19
|
+
hookEvents: ['UserPromptSubmit'],
|
|
20
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 业务空间配置加载与匹配工具。
|
|
5
|
+
*
|
|
6
|
+
* 数据源:包内随发的 config/workspaces.json(含 _comment 字段会被忽略)。
|
|
7
|
+
* 提供:
|
|
8
|
+
* - 加载 + 校验
|
|
9
|
+
* - 归一化 remote URL(兼容 SSH / HTTPS 两种写法)
|
|
10
|
+
* - 检测当前目录是否已拉取指定 workspace 的仓库
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { execSync } = require('child_process');
|
|
16
|
+
|
|
17
|
+
const DEFAULT_CONFIG = 'config/workspaces.json';
|
|
18
|
+
const SAMPLE_CONFIG = 'config/config.sample.json';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 归一化 git remote URL:提取 "namespace/repo" 形式(去掉 .git 后缀)。
|
|
22
|
+
* 支持 SSH / HTTPS / HTTP / 已经是 path 的形式。
|
|
23
|
+
* 仅接受非空字符串,其它输入(null / undefined / 非字符串 / 空串)一律返回 '',
|
|
24
|
+
* 由调用方决定如何处理。
|
|
25
|
+
*/
|
|
26
|
+
function normalizeRepoUrl(url) {
|
|
27
|
+
if (typeof url !== 'string') return '';
|
|
28
|
+
const u = url.trim();
|
|
29
|
+
if (!u) return '';
|
|
30
|
+
const stripped = u.replace(/\.git$/i, '');
|
|
31
|
+
// SSH: git@host:path/to/repo
|
|
32
|
+
const ssh = stripped.match(/^git@[^:\s]+:(.+)$/);
|
|
33
|
+
if (ssh) return ssh[1];
|
|
34
|
+
// HTTPS/HTTP: scheme://host/path/to/repo
|
|
35
|
+
const http = stripped.match(/^https?:\/\/[^/]+\/(.+)$/i);
|
|
36
|
+
if (http) return http[1];
|
|
37
|
+
// 已是 path 形式(兼容裸 "group/repo")
|
|
38
|
+
return stripped.replace(/^\/+/, '');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 判断 git remote URL 是否匹配配置的 repo。
|
|
43
|
+
* 采用归一化后严格相等比较(同一仓库的 SSH/HTTPS 写法归一后相等)。
|
|
44
|
+
*/
|
|
45
|
+
function matchesConfiguredRepo(remoteUrl, configRepo) {
|
|
46
|
+
if (!remoteUrl || !configRepo) return false;
|
|
47
|
+
return normalizeRepoUrl(remoteUrl) === normalizeRepoUrl(configRepo);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 加载并校验业务空间配置。
|
|
52
|
+
* @param {string} [configPath] 相对于包根目录的配置文件路径,默认 config/workspaces.json
|
|
53
|
+
* @returns {{name: string, repo: string, businessLine: string}[]}
|
|
54
|
+
* @throws 配置缺失 / 格式错误时抛出,错误信息面向最终用户
|
|
55
|
+
*/
|
|
56
|
+
function loadWorkspaces(configPath = DEFAULT_CONFIG) {
|
|
57
|
+
const absPath = path.isAbsolute(configPath)
|
|
58
|
+
? configPath
|
|
59
|
+
: path.resolve(__dirname, '..', '..', '..', configPath);
|
|
60
|
+
|
|
61
|
+
if (!fs.existsSync(absPath)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`未找到业务空间配置文件:${configPath}\n` +
|
|
64
|
+
`请在项目维护方提供的本包内维护该文件后再试。`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let raw;
|
|
69
|
+
try {
|
|
70
|
+
raw = JSON.parse(fs.readFileSync(absPath, 'utf-8'));
|
|
71
|
+
} catch (e) {
|
|
72
|
+
throw new Error(`业务空间配置 ${configPath} 不是合法 JSON:${e.message}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!raw || typeof raw !== 'object' || !Array.isArray(raw.workspaces)) {
|
|
76
|
+
throw new Error(`业务空间配置 ${configPath} 缺少 workspaces 数组`);
|
|
77
|
+
}
|
|
78
|
+
if (raw.workspaces.length === 0) {
|
|
79
|
+
throw new Error(`业务空间配置 ${configPath} 的 workspaces 数组为空,请至少配置一项`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return raw.workspaces.map((w, i) => {
|
|
83
|
+
const itemLabel = `第 ${i + 1} 项`;
|
|
84
|
+
if (!w || typeof w !== 'object') {
|
|
85
|
+
throw new Error(`业务空间配置 ${itemLabel} 不是对象`);
|
|
86
|
+
}
|
|
87
|
+
if (typeof w.name !== 'string' || !w.name.trim()) {
|
|
88
|
+
throw new Error(`业务空间配置 ${itemLabel} 缺少 name(业务空间名)`);
|
|
89
|
+
}
|
|
90
|
+
if (typeof w.repo !== 'string' || !w.repo.trim()) {
|
|
91
|
+
throw new Error(`业务空间 "${w.name}" 缺少 repo(GitLab 仓库地址)`);
|
|
92
|
+
}
|
|
93
|
+
if (typeof w.businessLine !== 'string' || !w.businessLine.trim()) {
|
|
94
|
+
throw new Error(`业务空间 "${w.name}" 缺少 businessLine(业务线,如:直采、增值、KA)`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
name: w.name.trim(),
|
|
98
|
+
repo: w.repo.trim(),
|
|
99
|
+
businessLine: w.businessLine.trim(),
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 校验业务工作空间 CONFIG.json 的格式是否合法。
|
|
106
|
+
*
|
|
107
|
+
* 校验项:
|
|
108
|
+
* - 文件存在且为合法 JSON
|
|
109
|
+
* - 含 apps 数组且非空
|
|
110
|
+
* - 每个 app 对象必须含 name / repo / side 三个字段
|
|
111
|
+
* - name、repo 为非空字符串;side 必须为 'front' 或 'back'
|
|
112
|
+
*
|
|
113
|
+
* @param {string} configPath CONFIG.json 的绝对路径
|
|
114
|
+
* @returns {{ valid: boolean, message?: string, config?: object }}
|
|
115
|
+
*/
|
|
116
|
+
function validateWorkspaceConfig(configPath) {
|
|
117
|
+
if (!fs.existsSync(configPath)) {
|
|
118
|
+
return { valid: false, message: `CONFIG.json 不存在:${configPath}` };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let raw;
|
|
122
|
+
try {
|
|
123
|
+
raw = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
124
|
+
} catch (e) {
|
|
125
|
+
return { valid: false, message: `CONFIG.json 不是合法 JSON:${e.message}` };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!raw || typeof raw !== 'object') {
|
|
129
|
+
return { valid: false, message: 'CONFIG.json 内容应为 JSON 对象' };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!Array.isArray(raw.apps)) {
|
|
133
|
+
return { valid: false, message: 'CONFIG.json 缺少 apps 数组' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (raw.apps.length === 0) {
|
|
137
|
+
return { valid: false, message: 'CONFIG.json 中 apps 数组为空,请至少配置一个关联应用' };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const VALID_SIDES = ['front', 'back'];
|
|
141
|
+
for (let i = 0; i < raw.apps.length; i++) {
|
|
142
|
+
const app = raw.apps[i];
|
|
143
|
+
const label = `第 ${i + 1} 项`;
|
|
144
|
+
|
|
145
|
+
if (!app || typeof app !== 'object') {
|
|
146
|
+
return { valid: false, message: `CONFIG.json ${label}不是对象` };
|
|
147
|
+
}
|
|
148
|
+
if (typeof app.name !== 'string' || !app.name.trim()) {
|
|
149
|
+
return { valid: false, message: `CONFIG.json ${label}缺少 name(应用名)` };
|
|
150
|
+
}
|
|
151
|
+
if (typeof app.repo !== 'string' || !app.repo.trim()) {
|
|
152
|
+
return { valid: false, message: `CONFIG.json "${app.name}" 缺少 repo(Git 仓库地址)` };
|
|
153
|
+
}
|
|
154
|
+
if (typeof app.side !== 'string' || !app.side.trim()) {
|
|
155
|
+
return { valid: false, message: `CONFIG.json "${app.name}" 缺少 side(仓库类型,可选值:front / back)` };
|
|
156
|
+
}
|
|
157
|
+
if (!VALID_SIDES.includes(app.side.trim())) {
|
|
158
|
+
return { valid: false, message: `CONFIG.json "${app.name}" 的 side 字段值无效:"${app.side}"(可选值:front / back)` };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return { valid: true, config: raw };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 在指定目录执行 `git remote -v`,解析出 [{ name, url }] 列表。
|
|
167
|
+
* 任何异常静默返回空数组,便于上层用 state 机判定。
|
|
168
|
+
*/
|
|
169
|
+
function readGitRemotes(cwd) {
|
|
170
|
+
try {
|
|
171
|
+
const out = execSync('git remote -v', {
|
|
172
|
+
cwd,
|
|
173
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
174
|
+
encoding: 'utf-8',
|
|
175
|
+
timeout: 10000,
|
|
176
|
+
});
|
|
177
|
+
const remotes = [];
|
|
178
|
+
out.split(/\r?\n/).forEach(line => {
|
|
179
|
+
// 仅保留 fetch 行,避免 push/fetch 各占一行导致重复
|
|
180
|
+
const m = line.match(/^(\S+)\s+(\S+)\s+\(fetch\)$/);
|
|
181
|
+
if (m) remotes.push({ name: m[1], url: m[2] });
|
|
182
|
+
});
|
|
183
|
+
return remotes;
|
|
184
|
+
} catch (_) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* 检测目标目录相对于 configRepo 的"是否已拉取"状态。
|
|
191
|
+
* @returns {{ state: 'not-git' | 'matched' | 'mismatched', remotes: {name:string,url:string}[] }}
|
|
192
|
+
* - not-git:目录不是 git 仓库
|
|
193
|
+
* - matched:是 git 仓库且任一 remote 匹配 configRepo
|
|
194
|
+
* - mismatched:是 git 仓库但没有任何 remote 匹配 configRepo
|
|
195
|
+
*/
|
|
196
|
+
function detectRepo(cwd, configRepo) {
|
|
197
|
+
const gitDir = path.join(cwd, '.git');
|
|
198
|
+
if (!fs.existsSync(gitDir)) {
|
|
199
|
+
return { state: 'not-git', remotes: [] };
|
|
200
|
+
}
|
|
201
|
+
const remotes = readGitRemotes(cwd);
|
|
202
|
+
const matched = remotes.some(r => matchesConfiguredRepo(r.url, configRepo));
|
|
203
|
+
return matched
|
|
204
|
+
? { state: 'matched', remotes }
|
|
205
|
+
: { state: 'mismatched', remotes };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 从 git remote URL 中提取仓库目录名(最后一段 path)。
|
|
210
|
+
* - git@host:group/repo.git → 'repo'
|
|
211
|
+
* - https://host/group/repo.git → 'repo'
|
|
212
|
+
* - group/sub/repo → 'repo'
|
|
213
|
+
* 非法输入返回空串。
|
|
214
|
+
*/
|
|
215
|
+
function extractRepoBasename(url) {
|
|
216
|
+
const normalized = normalizeRepoUrl(url);
|
|
217
|
+
if (!normalized) return '';
|
|
218
|
+
const parts = normalized.split('/').filter(Boolean);
|
|
219
|
+
return parts.length > 0 ? parts[parts.length - 1] : '';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
DEFAULT_CONFIG,
|
|
224
|
+
SAMPLE_CONFIG,
|
|
225
|
+
normalizeRepoUrl,
|
|
226
|
+
matchesConfiguredRepo,
|
|
227
|
+
loadWorkspaces,
|
|
228
|
+
validateWorkspaceConfig,
|
|
229
|
+
readGitRemotes,
|
|
230
|
+
detectRepo,
|
|
231
|
+
extractRepoBasename,
|
|
232
|
+
};
|