@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,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mermaid 语法守卫 — 在发布到 Confluence Wiki 前自动修复已知兼容性问题。
|
|
5
|
+
*
|
|
6
|
+
* Confluence 的 mermaid-macro 插件不支持 flowchart 关键字(仅支持 graph),
|
|
7
|
+
* 也不支持 stateDiagram-v2 版本号后缀。classDiagram 的继承箭头 <|-- 中的 <
|
|
8
|
+
* 会被 Confluence 编码为 < 导致渲染失败,需反转箭头方向。
|
|
9
|
+
* 同样不支持 :::内联样式、<|.. 反向虚线继承、classDef 样式定义等语法。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 对 mermaid 代码应用语法守卫转换
|
|
14
|
+
* @param {string} content - mermaid 代码块内容
|
|
15
|
+
* @returns {{ fixed: string, warnings: string[] }}
|
|
16
|
+
*/
|
|
17
|
+
function applyMermaidGuard(content) {
|
|
18
|
+
const warnings = [];
|
|
19
|
+
let fixed = content;
|
|
20
|
+
|
|
21
|
+
// 1. flowchart → graph(Confluence 不支持 flowchart 关键字)
|
|
22
|
+
if (/^\s*flowchart\b/m.test(fixed)) {
|
|
23
|
+
fixed = fixed.replace(/^\s*flowchart\b/gm, 'graph');
|
|
24
|
+
warnings.push('flowchart → graph');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 2. stateDiagram-v2 → stateDiagram(去除版本号后缀)
|
|
28
|
+
if (/^\s*stateDiagram-v2\b/m.test(fixed)) {
|
|
29
|
+
fixed = fixed.replace(/^\s*stateDiagram-v2\b/gm, 'stateDiagram');
|
|
30
|
+
warnings.push('stateDiagram-v2 → stateDiagram');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2b. classDiagram-v2 → classDiagram(去除版本号后缀)
|
|
34
|
+
if (/^\s*classDiagram-v2\b/m.test(fixed)) {
|
|
35
|
+
fixed = fixed.replace(/^\s*classDiagram-v2\b/gm, 'classDiagram');
|
|
36
|
+
warnings.push('classDiagram-v2 → classDiagram');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 3. classDiagram 箭头方向修正: A <|-- B → B --|> A
|
|
40
|
+
// Confluence 会将 < 编码为 <,导致渲染失败
|
|
41
|
+
if (/^\s*classDiagram\b/m.test(fixed)) {
|
|
42
|
+
const inheritancePattern = /^(\s*)(\w+)\s+<\|--\s+(\w+)/gm;
|
|
43
|
+
if (inheritancePattern.test(fixed)) {
|
|
44
|
+
inheritancePattern.lastIndex = 0;
|
|
45
|
+
fixed = fixed.replace(inheritancePattern, '$1$3 --|> $2');
|
|
46
|
+
warnings.push('classDiagram inheritance fix: <|-- → --|>');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 3b. 反向虚线继承: A <|.. B → B ..|> A
|
|
50
|
+
const dottedInheritance = /^(\s*)([\w.]+)\s+<\|\.\.\s+([\w.]+)/gm;
|
|
51
|
+
if (dottedInheritance.test(fixed)) {
|
|
52
|
+
dottedInheritance.lastIndex = 0;
|
|
53
|
+
fixed = fixed.replace(dottedInheritance, '$1$3 ..|> $2');
|
|
54
|
+
warnings.push('classDiagram dotted inheritance fix: <|.. → ..|>');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 3c. classDef 和 class Xxx styleName 在 Confluence classDiagram 中不支持,移除
|
|
58
|
+
const classDefPattern = /^\s*classDef\b.*$/gm;
|
|
59
|
+
if (classDefPattern.test(fixed)) {
|
|
60
|
+
classDefPattern.lastIndex = 0;
|
|
61
|
+
fixed = fixed.replace(classDefPattern, '');
|
|
62
|
+
warnings.push('classDiagram: removed unsupported classDef');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// class Xxx styleName(仅匹配引用已定义样式的行,非方法/属性定义)
|
|
66
|
+
const classStyleRef = /^\s*class\s+(\w+)\s+(\w+)\s*$/gm;
|
|
67
|
+
if (classStyleRef.test(fixed)) {
|
|
68
|
+
classStyleRef.lastIndex = 0;
|
|
69
|
+
fixed = fixed.replace(classStyleRef, '');
|
|
70
|
+
warnings.push('classDiagram: removed unsupported class style reference');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 4. :::内联样式 → 提取为 class 语句(Confluence 不支持 :::)
|
|
75
|
+
const inlineStylePattern = /(\w[\w\[\]"'.]*?):::(\w+)/g;
|
|
76
|
+
const styleAssignments = [];
|
|
77
|
+
let styleMatch;
|
|
78
|
+
while ((styleMatch = inlineStylePattern.exec(fixed)) !== null) {
|
|
79
|
+
const fullRef = styleMatch[1].trim();
|
|
80
|
+
const styleClass = styleMatch[2];
|
|
81
|
+
// 提取节点 ID(去掉 [Label] 部分)
|
|
82
|
+
const nodeId = fullRef.replace(/\[.*$/, '').trim();
|
|
83
|
+
if (nodeId && styleClass) {
|
|
84
|
+
styleAssignments.push(` class ${nodeId} ${styleClass}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (styleAssignments.length > 0) {
|
|
88
|
+
// 移除 ::: 内联样式
|
|
89
|
+
fixed = fixed.replace(/(\w[\w\[\]"'.]*?):::(\w+)/g, '$1');
|
|
90
|
+
// 在末尾追加 class 语句
|
|
91
|
+
fixed = fixed.trimEnd() + '\n' + styleAssignments.join('\n');
|
|
92
|
+
warnings.push(':::inline style → class statements');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 5. ~GenericType~ 泛型标注 — Confluence 可能不兼容,保留但警告
|
|
96
|
+
if (/~\w+~/.test(fixed)) {
|
|
97
|
+
warnings.push('~GenericType~ detected, may not render in Confluence');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { fixed, warnings };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = { applyMermaidGuard };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 内容拆分 — 当 design.md 转换后的 XHTML 超过阈值时,按章节切分为多个子页面,
|
|
5
|
+
* 主页面保留概述 + 章节导航 TOC。
|
|
6
|
+
*
|
|
7
|
+
* 拆分策略(自动降级):
|
|
8
|
+
* 1. 先按 # 一级标题切;若切出多于 1 段且每段 XHTML 均 ≤ 阈值 → 用 h1 段
|
|
9
|
+
* 2. 否则按 ## 二级标题切(design.md 常为单 h1,实际多走此路)
|
|
10
|
+
*
|
|
11
|
+
* 概述 = 文档开头到第一个切分标题之前的全部内容(含 # 标题、背景说明等)。
|
|
12
|
+
* 章节标题 = 该级标题文本;子页面标题 = "<主页面标题> - <章节标题>"。
|
|
13
|
+
* 导航用 Confluence <ac:link><ri:page ri:content-title="..."/> 按标题引用,
|
|
14
|
+
* 子页面建好后链接自动生效,无需 pageId 回填。
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const { markdownToXhtml } = require('./convert');
|
|
18
|
+
|
|
19
|
+
const DEFAULT_THRESHOLD = 200 * 1024; // 200KB
|
|
20
|
+
|
|
21
|
+
function escapeXml(str) {
|
|
22
|
+
return str
|
|
23
|
+
.replace(/&/g, '&')
|
|
24
|
+
.replace(/</g, '<')
|
|
25
|
+
.replace(/>/g, '>')
|
|
26
|
+
.replace(/"/g, '"');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function escapeCdata(str) {
|
|
30
|
+
return str.replace(/]]>/g, ']]]]><![CDATA[>');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 按指定层级标题切分 markdown
|
|
35
|
+
* @param {string} markdown
|
|
36
|
+
* @param {number} level - 1=#, 2=##
|
|
37
|
+
* @returns {{overview: string, sections: {title: string, content: string}[]}}
|
|
38
|
+
* overview = 开头到第一个该级标题前
|
|
39
|
+
* sections = 每个该级标题及其内容(到下一个同级标题前)
|
|
40
|
+
*/
|
|
41
|
+
function splitByHeading(markdown, level) {
|
|
42
|
+
const prefix = '#'.repeat(level);
|
|
43
|
+
const re = new RegExp(`^${prefix} (.+)$`, 'gm');
|
|
44
|
+
const matches = [];
|
|
45
|
+
let m;
|
|
46
|
+
while ((m = re.exec(markdown)) !== null) {
|
|
47
|
+
matches.push({ title: m[1].trim(), start: m.index, lineEnd: m.index + m[0].length });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (matches.length === 0) {
|
|
51
|
+
return { overview: markdown, sections: [] };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const overview = markdown.slice(0, matches[0].start).trim();
|
|
55
|
+
const sections = matches.map((mt, i) => {
|
|
56
|
+
const content = markdown.slice(mt.start, i + 1 < matches.length ? matches[i + 1].start : markdown.length);
|
|
57
|
+
return { title: mt.title, content: content.trim() };
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return { overview, sections };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 判断是否需要拆分(基于转换后的 XHTML 长度)
|
|
65
|
+
* @param {string} xhtml
|
|
66
|
+
* @param {number} [threshold]
|
|
67
|
+
*/
|
|
68
|
+
function shouldSplit(xhtml, threshold = DEFAULT_THRESHOLD) {
|
|
69
|
+
return xhtml.length > threshold;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 切分 markdown 为概述 + 章节列表,并预转换各段 XHTML(避免发布时重复转换)
|
|
74
|
+
* @param {string} markdown
|
|
75
|
+
* @param {number} [threshold]
|
|
76
|
+
* @returns {{overview: {markdown: string, xhtml: string}, sections: {title: string, markdown: string, xhtml: string}[], level: number}}
|
|
77
|
+
*/
|
|
78
|
+
function splitMarkdown(markdown, threshold = DEFAULT_THRESHOLD) {
|
|
79
|
+
// 先尝试 h1
|
|
80
|
+
let { overview, sections } = splitByHeading(markdown, 1);
|
|
81
|
+
let level = 1;
|
|
82
|
+
|
|
83
|
+
const h1Usable = sections.length > 1 && sections.every(s => markdownToXhtml(s.content).length <= threshold);
|
|
84
|
+
|
|
85
|
+
if (!h1Usable) {
|
|
86
|
+
// 降级 h2
|
|
87
|
+
const h2 = splitByHeading(markdown, 2);
|
|
88
|
+
if (h2.sections.length > 1) {
|
|
89
|
+
overview = h2.overview;
|
|
90
|
+
sections = h2.sections;
|
|
91
|
+
level = 2;
|
|
92
|
+
}
|
|
93
|
+
// 若 h2 也切不出多段,保持 h1 结果(sections 可能为空或 1 段),调用方按不拆处理
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
overview: { markdown: overview, xhtml: markdownToXhtml(overview) },
|
|
98
|
+
sections: sections.map(s => ({ title: s.title, markdown: s.content, xhtml: markdownToXhtml(s.content) })),
|
|
99
|
+
level,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 判断拆分结果是否实际可用(至少切出 1 个章节)
|
|
105
|
+
*/
|
|
106
|
+
function isSplittable(split) {
|
|
107
|
+
return split.sections.length > 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 构建主页面章节导航 TOC(XHTML)
|
|
112
|
+
* @param {string} mainTitle - 主页面标题,用于拼子页面标题
|
|
113
|
+
* @param {{title: string}[]} sections
|
|
114
|
+
* @returns {string}
|
|
115
|
+
*/
|
|
116
|
+
function buildNavXhtml(mainTitle, sections) {
|
|
117
|
+
const items = sections.map(s => {
|
|
118
|
+
const subTitle = `${mainTitle} - ${s.title}`;
|
|
119
|
+
return `<li><ac:link><ri:page ri:content-title="${escapeXml(subTitle)}"/><ac:plain-text-link-body><![CDATA[${escapeCdata(s.title)}]]></ac:plain-text-link-body></ac:link></li>`;
|
|
120
|
+
}).join('');
|
|
121
|
+
|
|
122
|
+
return `<h2>章节导航</h2><ul>${items}</ul>`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = {
|
|
126
|
+
DEFAULT_THRESHOLD,
|
|
127
|
+
shouldSplit,
|
|
128
|
+
splitMarkdown,
|
|
129
|
+
isSplittable,
|
|
130
|
+
buildNavXhtml,
|
|
131
|
+
};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cbb-decompile-jar
|
|
3
|
+
description: "JAR 包反编译工具。从本地 Maven 仓库的实际依赖 JAR 中提取类定义(Facade 方法签名、DTO 字段、枚举值),确保看到的是项目真正依赖的版本。触发场景:调用 RPC 接口但不确定入参出参结构、查看依赖库的接口/DTO/枚举定义、确认依赖实际版本的类签名、查看 .class 文件。需要 JDK + Python 3 + 本地 Maven 仓库。仅限 Java 后端场景。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# JAR 包反编译工具
|
|
7
|
+
|
|
8
|
+
从 Maven 本地仓库的实际依赖 JAR 中提取类定义,确保看到的是项目真正依赖的版本,而非本地可能过时的源码。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 一、触发场景
|
|
13
|
+
|
|
14
|
+
以下场景**必须使用此 skill**,而不是猜测或读取本地源码:
|
|
15
|
+
|
|
16
|
+
| 场景 | 说明 |
|
|
17
|
+
|------|------|
|
|
18
|
+
| 调用 RPC 接口 | 不确定 Facade 方法的入参出参,需要查看接口定义 |
|
|
19
|
+
| 使用第三方 DTO | 需要确认 Request/Response 类的字段列表和类型 |
|
|
20
|
+
| 查看枚举值 | 需要知道枚举类的所有可选值及其 code |
|
|
21
|
+
| 版本确认 | 本地源码可能不是实际依赖版本,需要从 JAR 确认 |
|
|
22
|
+
| 代码迁移 | 需要参考原项目的类结构 |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 二、核心工具
|
|
27
|
+
|
|
28
|
+
脚本:`<skill-dir>/scripts/decompile.py`(Python,全平台通用)
|
|
29
|
+
|
|
30
|
+
**前置要求**:需要安装 Python 3(`python3` 或 `python` 命令可用)。如未安装:
|
|
31
|
+
- Windows:从 [python.org](https://www.python.org/downloads/) 下载安装,安装时勾选 "Add Python to PATH"
|
|
32
|
+
- Mac:`brew install python3` 或从 python.org 下载
|
|
33
|
+
- Linux:`sudo apt install python3` / `sudo yum install python3`
|
|
34
|
+
|
|
35
|
+
### 命令说明
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
SCRIPT="<skill-dir>/scripts/decompile.py"
|
|
39
|
+
|
|
40
|
+
# 环境与定位
|
|
41
|
+
python $SCRIPT check # 环境预检(JDK + Maven + 仓库)
|
|
42
|
+
python $SCRIPT repo # 输出本地 Maven 仓库路径
|
|
43
|
+
python $SCRIPT resolve <FQN> [-p projectDir] # 类全限定名 → JAR 路径 + Maven 坐标
|
|
44
|
+
python $SCRIPT find <G> <A> <V> # Maven 坐标 → JAR 路径
|
|
45
|
+
|
|
46
|
+
# 反编译
|
|
47
|
+
python $SCRIPT list <jarPath> [keyword] # 列出 JAR 中的类(可按关键字过滤)
|
|
48
|
+
python $SCRIPT show <jarPath[;jarPath2;...]> <FQN> [-v] # 查看类的完整信息(-v 输出 verbose 字节码)
|
|
49
|
+
python $SCRIPT api <jarPath[;jarPath2;...]> <FQN> # 查看接口方法签名(支持多 JAR)
|
|
50
|
+
python $SCRIPT enum <jarPath[;jarPath2;...]> <FQN> # 查看枚举类的所有值及 code(支持多 JAR)
|
|
51
|
+
python $SCRIPT batch <jarPath[;jarPath2;...]> <FQN1> <FQN2> ... # 批量查看多个类(单次调用)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
> **多 JAR classpath**:`show`/`api`/`enum` 的第一个参数支持多个 JAR 路径,用系统路径分隔符(Windows 用 `;`,Mac/Linux 用 `:`)或 `,` 分隔。当一个类引用了其他 JAR 中的类型时,需要将相关 JAR 都加入 classpath,否则 javap 会报 class not found。
|
|
55
|
+
>
|
|
56
|
+
> 示例:`python $SCRIPT show "D:\repo\a.jar;D:\repo\b.jar" com.example.MyClass`
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 三、标准分析流程
|
|
61
|
+
|
|
62
|
+
### Step 1:环境预检
|
|
63
|
+
|
|
64
|
+
**每次使用前必须先执行**:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python $SCRIPT check
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
如果任何组件缺失,**立即停止并提示用户**:
|
|
71
|
+
- JDK 缺失 → "请提供 JDK 安装路径(如 D:\env\jdk\corretto-1.8),或设置 JAVA_HOME 环境变量"
|
|
72
|
+
- Maven 缺失 → "请提供 Maven 安装路径,或设置 MAVEN_HOME 环境变量"
|
|
73
|
+
|
|
74
|
+
### Step 2:锁定目标类
|
|
75
|
+
|
|
76
|
+
**若用户提供的是类的全限定名或 Maven 坐标**,直接使用,跳到 Step 3。
|
|
77
|
+
|
|
78
|
+
**若用户描述的是业务功能(如"查询企业已签署的合同")而非类名**,需要先在项目代码中搜索定位:
|
|
79
|
+
|
|
80
|
+
1. 在项目中用关键词搜索相关 Java 文件(如 `grep -rli "contract" --include="*.java" .`)
|
|
81
|
+
2. 找到调用方代码后,从 import 语句中提取外部依赖的全限定类名(FQN)
|
|
82
|
+
3. 确认目标:提取出 Facade 接口名、Request/Response 类名
|
|
83
|
+
|
|
84
|
+
支持的目标格式:
|
|
85
|
+
- 全限定类名 + 方法名:`com.ymm.uc.uic.common.facade.service.AccountCenterService#getAccountInfoByAccountId`
|
|
86
|
+
- 全限定类名:`com.ymm.uc.uic.common.facade.dto.AccountInfoDTO`
|
|
87
|
+
- Maven 坐标:`com.ymm.uc:uic-common-service-facade:2.0.5`
|
|
88
|
+
|
|
89
|
+
### Step 3:定位 JAR
|
|
90
|
+
|
|
91
|
+
如果用户提供的是类名(非 Maven 坐标):
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python $SCRIPT resolve com.ymm.uc.uic.common.facade.service.AccountCenterService -p D:\workspace\ka\ymm-ka-capacity-app
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
输出:
|
|
98
|
+
```
|
|
99
|
+
JAR: D:\env\maven\repository\com\ymm\uc\uic-common-service-facade\2.0.5\uic-common-service-facade-2.0.5.jar
|
|
100
|
+
Coordinates: com.ymm.uc:uic-common-service-facade:2.0.5
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
脚本内部自动完成:定位 Maven → 读取 settings.xml → 执行 `mvn dependency:tree` → 多级包名前缀缩窄候选 → `jar tf` 确认。
|
|
104
|
+
|
|
105
|
+
**resolve 增强逻辑**:
|
|
106
|
+
1. **dependency:tree 容错**:如果 `dependency:tree` 构建失败(POM 异常、snapshot 不可用等),自动跳过,进入本地仓库扫描
|
|
107
|
+
2. **多级前缀扫描**:包名和 Maven groupId 不总一致(如 `com.ymm.common.rpc` vs `com.ymm.common-plugins`),脚本会从最长前缀到最短前缀递减扫描本地仓库目录,确保覆盖
|
|
108
|
+
3. **JAR 自动下载**:当 JAR 在本地仓库不存在时,自动执行 `mvn dependency:get` 从远程仓库下载
|
|
109
|
+
|
|
110
|
+
如果用户提供的是 Maven 坐标,用 `find` 定位:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
python $SCRIPT find com.ymm.uc uic-common-service-facade 2.0.5
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Step 4:执行反编译
|
|
117
|
+
|
|
118
|
+
根据用户需求选择子命令:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 探索 JAR 内容
|
|
122
|
+
python $SCRIPT list $jarPath "Facade"
|
|
123
|
+
|
|
124
|
+
# 查看 RPC 接口方法签名
|
|
125
|
+
python $SCRIPT api $jarPath com.ymm.uc.uic.common.facade.service.AccountCenterService
|
|
126
|
+
|
|
127
|
+
# 查看类完整信息(字段 + 方法)
|
|
128
|
+
python $SCRIPT show $jarPath com.ymm.uc.uic.common.facade.dto.AccountInfoDTO
|
|
129
|
+
|
|
130
|
+
# 查看类完整信息(含 verbose 字节码,用于分析常量值等)
|
|
131
|
+
python $SCRIPT show $jarPath com.ymm.uc.uic.common.facade.dto.AccountInfoDTO -v
|
|
132
|
+
|
|
133
|
+
# 查看枚举值(自动提取 code/desc 字段值)
|
|
134
|
+
python $SCRIPT enum $jarPath com.ymm.uc.uic.common.facade.enums.AccountType
|
|
135
|
+
|
|
136
|
+
# 批量查看多个类(一次 javap 调用,比逐个 show 快 3 倍)
|
|
137
|
+
python $SCRIPT batch $jarPath com.ymm.xx.SomeRequest com.ymm.xx.SomeResponse com.ymm.xx.SomeDTO
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**跨 JAR 类引用处理**:当一个类引用了其他 JAR 中的类型(如 `YmmResult` 不在 Facade 所在的 JAR),`show`/`api` 会报 class not found 并给出提示。此时需要:
|
|
141
|
+
|
|
142
|
+
1. 用 `resolve` 找到该引用类所在的 JAR
|
|
143
|
+
2. 将多个 JAR 路径拼接后传给 `show`/`api`:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# 先 resolve 找到 YmmResult 所在 JAR
|
|
147
|
+
python $SCRIPT resolve com.ymm.common.rpc.YmmResult -p D:\workspace\ka\ymm-ka-capacity-app
|
|
148
|
+
# 输出: JAR: D:\repo\ymm-common-rpc-1.0.jar
|
|
149
|
+
|
|
150
|
+
# 多 JAR classpath 查看(Windows 用 ; 分隔,Mac/Linux 用 : 分隔)
|
|
151
|
+
python $SCRIPT show "D:\repo\ymm-ka-capacity-api-1.2.26.jar;D:\repo\ymm-common-rpc-1.0.jar" com.ymm.ka.capacity.core.api.facade.freight.FreightDiffFacade
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Step 5:级联查看
|
|
155
|
+
|
|
156
|
+
看到方法签名后,**必须继续查看** Request/Response 的字段,以及字段引用的枚举类型。
|
|
157
|
+
|
|
158
|
+
级联查看时,注意:
|
|
159
|
+
- **记录已发现的 JAR 路径**:每次 `resolve` 获取的 JAR 路径应积累起来,后续 `show`/`api`/`enum` 时作为多 JAR classpath 传入,避免重复报 class not found
|
|
160
|
+
- **优先用已有的 JAR 组合**:如果新类可能在已发现的 JAR 中,先尝试多 JAR classpath,不行再 resolve
|
|
161
|
+
- **典型流程**:`api` 看 Facade → 累积 JAR → `show` 看 Request/Response(带已累积的 JAR)→ `show`/`enum` 看引用类型(带已累积的 JAR)→ 如仍报错再 `resolve` 新 JAR → 继续累积
|
|
162
|
+
|
|
163
|
+
以结构化格式呈现:
|
|
164
|
+
|
|
165
|
+
```java
|
|
166
|
+
// === Facade 接口 ===
|
|
167
|
+
// 来源:xxx-api-2.3.1.jar
|
|
168
|
+
|
|
169
|
+
public interface SomeFacade {
|
|
170
|
+
// 方法签名
|
|
171
|
+
Result<SomeDTO> doSomething(SomeRequest request);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// === Request 类 ===
|
|
175
|
+
public class SomeRequest {
|
|
176
|
+
private Long orderId; // 订单ID
|
|
177
|
+
private Integer orderType; // 订单类型(枚举 OrderTypeEnum)
|
|
178
|
+
private String remark; // 备注
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// === Response/DTO 类 ===
|
|
182
|
+
public class SomeDTO {
|
|
183
|
+
private Long id;
|
|
184
|
+
private String name;
|
|
185
|
+
private Integer status; // 状态(枚举 StatusEnum)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// === 枚举 ===
|
|
189
|
+
// OrderTypeEnum: ORDER_NORMAL(code="normal", desc="普通订单")
|
|
190
|
+
// StatusEnum: INIT(code="0", desc="初始化"), DONE(code="2", desc="完成")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 四、错误处理
|
|
196
|
+
|
|
197
|
+
| 错误场景 | 原因 | 处理方式 |
|
|
198
|
+
|----------|------|----------|
|
|
199
|
+
| Python not found | 未安装 Python 3 或未加入 PATH | 安装 Python 3 并确保 `python` 或 `python3` 命令可用 |
|
|
200
|
+
| JDK not found | JAVA_HOME 未设置、PATH 中无 javap、已知路径未找到 | 提示用户设置 JAVA_HOME 或提供 JDK 路径 |
|
|
201
|
+
| Maven not found | MAVEN_HOME 未设置、PATH 中无 mvn、已知路径未找到 | 提示用户设置 MAVEN_HOME 或提供 Maven 路径 |
|
|
202
|
+
| JAR not found at path | 依赖未下载到本地仓库 | 自动执行 `mvn dependency:get` 下载,下载失败才报错 |
|
|
203
|
+
| Class not found in any JAR | 类不在项目依赖中 | 检查 pom.xml 是否声明了对应依赖 |
|
|
204
|
+
| javap 报 class not found | 目标类引用了其他 JAR 中的类型 | 1. 用 `resolve` 定位引用类所在的 JAR;2. 用多 JAR classpath 重新执行 `show`/`api`/`enum` |
|
|
205
|
+
| javap 报 class not found | 类名错误或内部类 | 内部类用 `$` 分隔,先用 `list` 确认类名 |
|
|
206
|
+
| dependency:tree 构建失败 | POM 异常、snapshot 不可用等 | 自动跳过,进入本地仓库多级前缀扫描 |
|
|
207
|
+
| 包名与 groupId 不一致 | Java 包名和 Maven groupId 不同 | 多级前缀递减扫描,从最长到最短覆盖更多目录 |
|
|
208
|
+
| settings.xml 无 localRepository | 未自定义仓库路径 | 自动兜底到 `~/.m2/repository` |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 五、注意事项
|
|
213
|
+
|
|
214
|
+
1. **必须从 JAR 获取**:不要读取本地源码代替,本地代码可能和实际依赖版本不一致
|
|
215
|
+
2. **级联查看**:看到方法签名后,继续查看 Request/Response 的字段,以及字段引用的枚举类型
|
|
216
|
+
3. **敏感字段**:身份证号、手机号、银行卡号等敏感字段需标注提醒,日志中禁止明文输出
|
|
217
|
+
4. **性能优化**:
|
|
218
|
+
- `resolve` 结果会缓存到项目目录下的 `.decompile-cache.json`,第二次起直接命中(<0.2s)
|
|
219
|
+
- 需要查看多个类时,优先使用 `batch` 命令一次性查看,比逐个 `show` 快约 3 倍
|
|
220
|
+
- `dependency:tree` 有 15 秒超时保护,超时自动 fallback 到本地仓库扫描
|