@colin4k1024/tsp 2.4.8 → 2.4.9

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.
Files changed (32) hide show
  1. package/README.md +3 -3
  2. package/hooks/hooks.json +11 -0
  3. package/manifests/install-modules.json +3 -2
  4. package/package.json +1 -2
  5. package/scripts/codegraph-preflight.js +68 -75
  6. package/scripts/hooks/codegraph-auto-init.js +324 -0
  7. package/scripts/install-codegraph.js +235 -25
  8. package/skills/codegraph/SKILL.md +6 -5
  9. package/skills/goframe-v2/examples/practices/quick-demo/manifest/config/config.yaml +14 -14
  10. package/skills/repo-scan/SKILL.md +63 -63
  11. package/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  12. package/scripts/__pycache__/build_platform_artifacts.cpython-311.pyc +0 -0
  13. package/scripts/__pycache__/install_platform.cpython-311.pyc +0 -0
  14. package/scripts/__pycache__/langfuse_trace.cpython-311.pyc +0 -0
  15. package/scripts/__pycache__/query_audit_logs.cpython-311.pyc +0 -0
  16. package/scripts/__pycache__/scan_leaked_keys.cpython-311.pyc +0 -0
  17. package/scripts/__pycache__/team_skills_platform.cpython-311.pyc +0 -0
  18. package/scripts/__pycache__/team_skills_platform.cpython-313.pyc +0 -0
  19. package/scripts/__pycache__/validate_library.cpython-311.pyc +0 -0
  20. package/scripts/__pycache__/validate_workflow_state.cpython-311.pyc +0 -0
  21. package/scripts/evolution/__pycache__/__init__.cpython-311.pyc +0 -0
  22. package/scripts/evolution/__pycache__/store.cpython-311.pyc +0 -0
  23. package/scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  24. package/scripts/hooks/__pycache__/mcp_health_check.cpython-311.pyc +0 -0
  25. package/scripts/hooks/__pycache__/observe.cpython-311.pyc +0 -0
  26. package/scripts/hooks/__pycache__/session_end.cpython-311.pyc +0 -0
  27. package/scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
  28. package/scripts/lib/__pycache__/audit_logger.cpython-311.pyc +0 -0
  29. package/scripts/lib/__pycache__/audit_query.cpython-311.pyc +0 -0
  30. package/scripts/lib/__pycache__/hook_contract.cpython-311.pyc +0 -0
  31. package/scripts/lib/__pycache__/memory_store.cpython-311.pyc +0 -0
  32. package/scripts/lib/__pycache__/utils.cpython-311.pyc +0 -0
package/README.md CHANGED
@@ -65,7 +65,7 @@ TSP 整合了多个社区开源框架的精华能力,而非从零构建:
65
65
  |------|------|--------------|
66
66
  | **ECC** (Everything Claude Code) | 社区 | 125+ specialist skills、27 specialist agents、language rules packs、runtime hooks、安装工具链 |
67
67
  | **BMAD** | 方法来源(已吸收) | 单入口主链(`/team-help`)、Requirement Challenge、Design Review、Implementation Readiness、Story Slice、`artifact:persist` 落盘、Release→Closeout 收口 |
68
- | **CodeGraph** | 社区(`colbymchenry/codegraph`) | 默认内置 MCP-backed 代码图谱能力(符号搜索、调用链、impact、focused context),以 npm 依赖 + target-scoped installer wrapper + 本地 skill 接入 |
68
+ | **CodeGraph** | 社区(`colbymchenry/codegraph`) | 默认内置 MCP-backed 代码图谱能力(符号搜索、调用链、impact、focused context),以官方 standalone installer + target-scoped wrapper + Claude 自动初始化 hook 接入 |
69
69
  | **Graphify** | 社区(`safishamsi/graphify`) | 可选知识图谱能力(brownfield 结构扫描、依赖路径分析、架构问答证据),以 runbook + 本地 skill 接入,不替换 workflow-engine |
70
70
  | **GitNexus** | 社区(`abhigyanpatwari/GitNexus`) | 受控可选代码智能能力(MCP 查询、impact、detect_changes、多仓图谱证据),以 runbook + thin skill 接入,不内置依赖 |
71
71
  | **Open Design** | 社区(`nexu-io/open-design`) | 受控可选设计工作台能力(本地优先原型、deck、dashboard、mobile flow、`DESIGN.md`、导出 artifact),以 runbook + thin skill 接入,不内置 daemon |
@@ -209,7 +209,7 @@ TSP 的公开能力是在多个社区项目、技能仓库和工程方法论的
209
209
  | 仓库 | 在 TSP 中的关系 | 说明 |
210
210
  |------|------|------|
211
211
  | [affaan-m/everything-claude-code](https://github.com/affaan-m/everything-claude-code) | 上游能力来源 | ECC harness layer、specialist agents、skills、runtime hooks 与安装工具链的重要参考来源 |
212
- | [colbymchenry/codegraph](https://github.com/colbymchenry/codegraph) | 默认内置接入 | 为 brownfield 符号搜索、调用链、impact 和 focused context 提供本地 MCP-backed 代码图谱能力;TSP 通过 target-scoped wrapper 调用上游 installer |
212
+ | [colbymchenry/codegraph](https://github.com/colbymchenry/codegraph) | 默认内置接入 | 为 brownfield 符号搜索、调用链、impact 和 focused context 提供本地 MCP-backed 代码图谱能力;TSP 通过 target-scoped wrapper 调用上游 standalone installer |
213
213
  | [safishamsi/graphify](https://github.com/safishamsi/graphify) | 已吸收并本地化 | 为 brownfield 结构扫描、依赖路径分析与架构问答补充知识图谱能力 |
214
214
  | [abhigyanpatwari/GitNexus](https://github.com/abhigyanpatwari/GitNexus) | 受控可选接入 | 为 brownfield MCP 查询、impact、detect_changes 和多仓代码图谱证据提供外部能力;因许可证与 Node 20 要求,不内置依赖 |
215
215
  | [nexu-io/open-design](https://github.com/nexu-io/open-design) | 受控可选接入 | 为本地优先原型、deck、dashboard、mobile flow、`DESIGN.md` 与可导出视觉 artifact 提供外部设计工作台;不内置 daemon 或上游源码 |
@@ -279,7 +279,7 @@ TSP 的公开能力是在多个社区项目、技能仓库和工程方法论的
279
279
  - 定位:CodeGraph 作为默认内置 MCP-backed 代码图谱能力,Graphify 作为轻量结构证据层,GitNexus 作为受控可选深代码智能层;三者都不替代 workflow-engine 或 `/team-*` 主链。
280
280
  - 入口:CodeGraph 先执行 `npm run codegraph:doctor`,Graphify 先执行 `npm run graphify:doctor`,GitNexus 先执行 `npm run gitnexus:doctor`;详细操作见 [codegraph-code-intelligence-usage.md](docs/runbooks/codegraph-code-intelligence-usage.md)、[graphify-knowledge-graph-usage.md](docs/runbooks/graphify-knowledge-graph-usage.md) 与 [gitnexus-code-intelligence-usage.md](docs/runbooks/gitnexus-code-intelligence-usage.md)。
281
281
  - 分发:通过安装模块 `knowledge-graph` 与组件 `capability:knowledge-graph` 提供;默认纳入 `developer`、`team`、`research` 与 `full` profile,`core` 保持轻量。
282
- - 治理边界:TSP 只通过 `scripts/install-codegraph.js` 以当前 target 调用 CodeGraph installer,不使用 `--target=auto`,也不在安装时运行 `codegraph init -i`;Graphify/GitNexus 的自动 setup 类命令仍不在本仓库执行。
282
+ - 治理边界:TSP 通过 `scripts/install-codegraph.js` 使用 CodeGraph 官方 standalone installer,不使用 `--target=auto`;Claude `SessionStart` 可在新项目静默执行 `codegraph init -i`,Codex/OpenCode 仅写入说明和诊断边界;Graphify/GitNexus 的自动 setup 类命令仍不在本仓库执行。
283
283
 
284
284
  ## 近期新增功能(v2.0.0 → v2.3.0)
285
285
 
package/hooks/hooks.json CHANGED
@@ -232,6 +232,17 @@
232
232
  ],
233
233
  "description": "Load previous context and detect package manager on new session",
234
234
  "id": "session:start"
235
+ },
236
+ {
237
+ "matcher": "*",
238
+ "hooks": [
239
+ {
240
+ "type": "command",
241
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"session:codegraph:auto-init\" \"scripts/hooks/codegraph-auto-init.js\" \"minimal,standard,strict\""
242
+ }
243
+ ],
244
+ "description": "Silently initialize CodeGraph for new Claude projects when the index is missing",
245
+ "id": "session:codegraph:auto-init"
235
246
  }
236
247
  ],
237
248
  "PostToolUse": [
@@ -305,11 +305,12 @@
305
305
  "docs/runbooks/graphify-knowledge-graph-usage.md",
306
306
  "docs/runbooks/gitnexus-code-intelligence-usage.md",
307
307
  "scripts/codegraph-preflight.js",
308
- "scripts/install-codegraph.js"
308
+ "scripts/install-codegraph.js",
309
+ "scripts/hooks/codegraph-auto-init.js"
309
310
  ],
310
311
  "externalInstall": {
311
312
  "id": "codegraph",
312
- "description": "Run the upstream CodeGraph installer through a TSP target-scoped wrapper; never uses --target=auto and never initializes project indexes.",
313
+ "description": "Install the upstream CodeGraph standalone CLI when needed, then run the target-scoped CodeGraph installer; never uses --target=auto.",
313
314
  "command": "node",
314
315
  "script": "scripts/install-codegraph.js",
315
316
  "args": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colin4k1024/tsp",
3
- "version": "2.4.8",
3
+ "version": "2.4.9",
4
4
  "description": "Open-source Team Skills Platform for role-based AI delivery workflows, shared skills, hooks, commands, and multi-platform installs.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -103,7 +103,6 @@
103
103
  "LICENSE"
104
104
  ],
105
105
  "dependencies": {
106
- "@colbymchenry/codegraph": "0.7.10",
107
106
  "@iarna/toml": "^2.2.5",
108
107
  "@inquirer/prompts": "^7.5.0",
109
108
  "ajv": "^8.18.0",
@@ -1,62 +1,66 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
3
 
4
- const fs = require('fs')
5
- const path = require('path')
6
4
  const { spawnSync } = require('child_process')
7
- const { buildInstallCommand, mapTarget } = require('./install-codegraph')
8
-
9
- const MIN_NODE_MAJOR = 18
10
- const MAX_NODE_MAJOR_EXCLUSIVE = 25
11
- const PACKAGE_NAME = '@colbymchenry/codegraph'
12
-
13
- function parseNodeVersion(text) {
14
- const match = String(text || '').trim().match(/^v?(\d+)\.(\d+)\.(\d+)/)
15
- if (!match) {
16
- return null
5
+ const {
6
+ buildStandaloneInstallerInfo,
7
+ findOnPath,
8
+ mapTarget,
9
+ resolveCodeGraphBin,
10
+ } = require('./install-codegraph')
11
+
12
+ function detectPlatform(platform = process.platform) {
13
+ if (platform === 'darwin' || platform === 'linux' || platform === 'win32') {
14
+ return {
15
+ ok: true,
16
+ output: platform,
17
+ }
17
18
  }
19
+
18
20
  return {
19
- major: Number(match[1]),
20
- minor: Number(match[2]),
21
- patch: Number(match[3]),
22
- raw: `${match[1]}.${match[2]}.${match[3]}`,
21
+ ok: false,
22
+ output: `unsupported platform: ${platform}`,
23
23
  }
24
24
  }
25
25
 
26
- function isNodeSupported(version) {
27
- return Boolean(
28
- version
29
- && version.major >= MIN_NODE_MAJOR
30
- && version.major < MAX_NODE_MAJOR_EXCLUSIVE
31
- )
32
- }
33
-
34
- function loadPackageMetadata() {
35
- if (process.env.CODEGRAPH_PREFLIGHT_PACKAGE_JSON) {
26
+ function detectDownloader(platform = process.platform) {
27
+ const installer = buildStandaloneInstallerInfo(platform)
28
+ if (!installer.supported) {
36
29
  return {
37
- ok: true,
38
- source: 'env',
39
- metadata: JSON.parse(process.env.CODEGRAPH_PREFLIGHT_PACKAGE_JSON),
30
+ ok: false,
31
+ output: installer.reason,
40
32
  }
41
33
  }
42
34
 
43
- try {
44
- const packageJsonPath = require.resolve(`${PACKAGE_NAME}/package.json`)
35
+ if (platform === 'win32') {
36
+ const command = findOnPath('powershell.exe') || findOnPath('powershell') || findOnPath('pwsh')
37
+ if (!command) {
38
+ return {
39
+ ok: false,
40
+ output: 'PowerShell not found; install PowerShell or provide CODEGRAPH_INSTALL_BIN',
41
+ }
42
+ }
45
43
  return {
46
44
  ok: true,
47
- source: packageJsonPath,
48
- metadata: JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')),
45
+ output: command,
49
46
  }
50
- } catch (error) {
47
+ }
48
+
49
+ const curl = findOnPath('curl')
50
+ if (!curl) {
51
51
  return {
52
52
  ok: false,
53
- source: 'node-resolution',
54
- warning: error.message || String(error),
53
+ output: 'curl not found; install curl or provide CODEGRAPH_INSTALL_BIN',
55
54
  }
56
55
  }
56
+
57
+ return {
58
+ ok: true,
59
+ output: curl,
60
+ }
57
61
  }
58
62
 
59
- function detectCli(target) {
63
+ function detectCli() {
60
64
  if (process.env.CODEGRAPH_PREFLIGHT_SKIP_CLI === '1') {
61
65
  return {
62
66
  ok: true,
@@ -65,27 +69,16 @@ function detectCli(target) {
65
69
  }
66
70
  }
67
71
 
68
- let install
69
- try {
70
- install = buildInstallCommand(target || 'codex')
71
- } catch (error) {
72
+ const resolved = resolveCodeGraphBin()
73
+ if (!resolved) {
72
74
  return {
73
75
  ok: false,
74
76
  command: 'codegraph',
75
- output: error.message || String(error),
77
+ output: 'CodeGraph standalone binary not found',
76
78
  }
77
79
  }
78
80
 
79
- if (!install.supported) {
80
- return {
81
- ok: true,
82
- command: '(unsupported target skipped)',
83
- output: install.reason,
84
- }
85
- }
86
-
87
- const args = install.args.slice(0, install.args.length - 4).concat(['--help'])
88
- const result = spawnSync(install.command, args, {
81
+ const result = spawnSync(resolved.command, [...resolved.argsPrefix, '--help'], {
89
82
  encoding: 'utf8',
90
83
  timeout: 8000,
91
84
  })
@@ -93,15 +86,15 @@ function detectCli(target) {
93
86
  if (result.error || result.status !== 0) {
94
87
  return {
95
88
  ok: false,
96
- command: install.command,
89
+ command: resolved.command,
97
90
  output: result.error ? result.error.message : `${result.stdout || ''}${result.stderr || ''}`.trim(),
98
91
  }
99
92
  }
100
93
 
101
94
  return {
102
95
  ok: true,
103
- command: install.command,
104
- output: 'codegraph --help',
96
+ command: resolved.command,
97
+ output: `${resolved.displayCommand} (${resolved.source})`,
105
98
  }
106
99
  }
107
100
 
@@ -110,30 +103,24 @@ function main() {
110
103
  console.log('=========================')
111
104
 
112
105
  const target = process.env.TSP_INSTALL_TARGET || process.env.CODEGRAPH_PREFLIGHT_TARGET || ''
113
- const nodeVersion = parseNodeVersion(process.env.CODEGRAPH_PREFLIGHT_NODE_VERSION || process.version)
114
- const packageInfo = loadPackageMetadata()
115
- const cli = detectCli(target)
106
+ const platform = detectPlatform()
107
+ const downloader = detectDownloader()
108
+ const cli = detectCli()
116
109
  const mappedTarget = target ? mapTarget(target) : null
117
110
  let hasFailure = false
118
111
 
119
- if (!nodeVersion) {
120
- hasFailure = true
121
- console.log(`- Node: unable to detect version (requires >= ${MIN_NODE_MAJOR} and < ${MAX_NODE_MAJOR_EXCLUSIVE})`)
122
- } else if (!isNodeSupported(nodeVersion)) {
112
+ if (!platform.ok) {
123
113
  hasFailure = true
124
- console.log(`- Node: ${nodeVersion.raw} (requires >= ${MIN_NODE_MAJOR} and < ${MAX_NODE_MAJOR_EXCLUSIVE})`)
114
+ console.log(`- Platform: ${platform.output}`)
125
115
  } else {
126
- console.log(`- Node: ${nodeVersion.raw} (ok)`)
116
+ console.log(`- Platform: ${platform.output} (ok)`)
127
117
  }
128
118
 
129
- if (packageInfo.ok && packageInfo.metadata) {
130
- const engine = packageInfo.metadata.engines && packageInfo.metadata.engines.node
131
- ? packageInfo.metadata.engines.node
132
- : '(not declared)'
133
- console.log(`- CodeGraph package: ${packageInfo.metadata.version || '(unknown)'} (license ${packageInfo.metadata.license || '(unknown)'}, node ${engine})`)
134
- } else {
119
+ if (!downloader.ok) {
135
120
  hasFailure = true
136
- console.log(`- CodeGraph package: unavailable (${packageInfo.warning || 'not installed'})`)
121
+ console.log(`- Standalone installer dependency: ${downloader.output}`)
122
+ } else {
123
+ console.log(`- Standalone installer dependency: ${downloader.output} (ok)`)
137
124
  }
138
125
 
139
126
  if (!cli.ok) {
@@ -154,18 +141,23 @@ function main() {
154
141
  }
155
142
 
156
143
  console.log('\nControlled integration boundaries:')
144
+ console.log('- TSP installs CodeGraph through the official standalone curl/PowerShell installer when no binary is available.')
157
145
  console.log('- TSP calls CodeGraph installer with the current target only; it never uses --target=auto.')
158
- console.log('- TSP install does not run `codegraph init -i`; initialize indexes inside target projects only.')
146
+ console.log('- Claude SessionStart may initialize project indexes automatically; Codex/OpenCode rely on instructions and diagnostics.')
159
147
  console.log('- Do not commit `.codegraph/` databases as TSP artifacts.')
160
148
 
161
149
  if (hasFailure) {
150
+ const installer = buildStandaloneInstallerInfo()
162
151
  console.log('\nFix failed checks before applying the CodeGraph integration.')
152
+ if (installer.supported) {
153
+ console.log(`Recommended standalone install: ${installer.display}`)
154
+ }
155
+ console.log('Offline fallback: set CODEGRAPH_INSTALL_BIN=/path/to/codegraph')
163
156
  process.exit(1)
164
157
  }
165
158
 
166
159
  console.log('\nRecommended next commands:')
167
160
  console.log('- npm run codegraph:doctor')
168
- console.log('- codegraph init -i')
169
161
  console.log('- codegraph status')
170
162
  }
171
163
 
@@ -174,6 +166,7 @@ if (require.main === module) {
174
166
  }
175
167
 
176
168
  module.exports = {
177
- isNodeSupported,
178
- parseNodeVersion,
169
+ detectCli,
170
+ detectDownloader,
171
+ detectPlatform,
179
172
  }
@@ -0,0 +1,324 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+ const { spawnSync } = require('child_process');
8
+ const { resolveCodeGraphBin } = require('../install-codegraph');
9
+
10
+ const STATE_FILE = path.join('.codegraph', 'tsp-auto-init-state.json');
11
+ const DEFAULT_RETRY_MS = 60 * 60 * 1000;
12
+ const DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;
13
+ const PROJECT_MARKERS = Object.freeze([
14
+ 'package.json',
15
+ 'pyproject.toml',
16
+ 'Cargo.toml',
17
+ 'go.mod',
18
+ 'pom.xml',
19
+ 'build.gradle',
20
+ 'build.gradle.kts',
21
+ 'Gemfile',
22
+ 'composer.json',
23
+ 'mix.exs',
24
+ 'deno.json',
25
+ 'deno.jsonc',
26
+ 'README.md',
27
+ 'AGENTS.md',
28
+ 'CLAUDE.md',
29
+ ]);
30
+
31
+ function isDisabled() {
32
+ const value = String(process.env.TSP_CODEGRAPH_AUTO_INIT || '').trim().toLowerCase();
33
+ return value === '0' || value === 'false' || value === 'off';
34
+ }
35
+
36
+ function readHookInput(rawInput) {
37
+ if (!rawInput || !String(rawInput).trim()) {
38
+ return {};
39
+ }
40
+
41
+ try {
42
+ const parsed = JSON.parse(rawInput);
43
+ return parsed && typeof parsed === 'object' ? parsed : {};
44
+ } catch {
45
+ return {};
46
+ }
47
+ }
48
+
49
+ function normalizePath(candidate) {
50
+ try {
51
+ return fs.realpathSync(candidate);
52
+ } catch {
53
+ return path.resolve(candidate);
54
+ }
55
+ }
56
+
57
+ function getStartDir(rawInput) {
58
+ if (process.env.CODEGRAPH_AUTO_INIT_PROJECT_ROOT) {
59
+ return path.resolve(process.env.CODEGRAPH_AUTO_INIT_PROJECT_ROOT);
60
+ }
61
+
62
+ const input = readHookInput(rawInput);
63
+ const candidates = [
64
+ input.cwd,
65
+ input.projectDir,
66
+ input.project_dir,
67
+ input.projectPath,
68
+ input.project_path,
69
+ input.workspaceDir,
70
+ input.workspace_dir,
71
+ ];
72
+
73
+ for (const candidate of candidates) {
74
+ if (typeof candidate === 'string' && candidate.trim()) {
75
+ return path.resolve(candidate.trim());
76
+ }
77
+ }
78
+
79
+ return process.cwd();
80
+ }
81
+
82
+ function parseGitDirFile(filePath) {
83
+ try {
84
+ const content = fs.readFileSync(filePath, 'utf8').trim();
85
+ const match = content.match(/^gitdir:\s*(.+)$/i);
86
+ if (!match) {
87
+ return null;
88
+ }
89
+ const gitDir = match[1].trim();
90
+ return path.resolve(path.dirname(filePath), gitDir);
91
+ } catch {
92
+ return null;
93
+ }
94
+ }
95
+
96
+ function findGitRoot(startDir) {
97
+ let current = normalizePath(startDir);
98
+ const root = path.parse(current).root;
99
+
100
+ while (current && current !== root) {
101
+ const gitPath = path.join(current, '.git');
102
+ if (fs.existsSync(gitPath)) {
103
+ return current;
104
+ }
105
+ current = path.dirname(current);
106
+ }
107
+
108
+ const rootGitPath = path.join(root, '.git');
109
+ return fs.existsSync(rootGitPath) ? root : null;
110
+ }
111
+
112
+ function findMarkerRoot(startDir) {
113
+ let current = normalizePath(startDir);
114
+ const root = path.parse(current).root;
115
+ const home = normalizePath(os.homedir());
116
+
117
+ while (current && current !== root) {
118
+ if (current === home) {
119
+ return null;
120
+ }
121
+ if (PROJECT_MARKERS.some(marker => fs.existsSync(path.join(current, marker)))) {
122
+ return current;
123
+ }
124
+ current = path.dirname(current);
125
+ }
126
+
127
+ return null;
128
+ }
129
+
130
+ function resolveProjectRoot(rawInput) {
131
+ const startDir = getStartDir(rawInput);
132
+ if (!fs.existsSync(startDir)) {
133
+ return null;
134
+ }
135
+
136
+ return findGitRoot(startDir) || findMarkerRoot(startDir);
137
+ }
138
+
139
+ function codegraphDbPath(projectRoot) {
140
+ return path.join(projectRoot, '.codegraph', 'codegraph.db');
141
+ }
142
+
143
+ function statePath(projectRoot) {
144
+ return path.join(projectRoot, STATE_FILE);
145
+ }
146
+
147
+ function readState(projectRoot) {
148
+ try {
149
+ return JSON.parse(fs.readFileSync(statePath(projectRoot), 'utf8'));
150
+ } catch {
151
+ return {};
152
+ }
153
+ }
154
+
155
+ function writeState(projectRoot, state) {
156
+ const filePath = statePath(projectRoot);
157
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
158
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
159
+ }
160
+
161
+ function retryDelayMs() {
162
+ const parsed = Number(process.env.TSP_CODEGRAPH_AUTO_INIT_RETRY_MS || DEFAULT_RETRY_MS);
163
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_RETRY_MS;
164
+ }
165
+
166
+ function shouldThrottleFailure(projectRoot, now = Date.now()) {
167
+ const state = readState(projectRoot);
168
+ if (state.status !== 'failed' || !state.failedAt) {
169
+ return false;
170
+ }
171
+
172
+ const failedAt = Date.parse(state.failedAt);
173
+ if (!Number.isFinite(failedAt)) {
174
+ return false;
175
+ }
176
+
177
+ return now - failedAt < retryDelayMs();
178
+ }
179
+
180
+ function resolveGitDir(projectRoot) {
181
+ const gitPath = path.join(projectRoot, '.git');
182
+ try {
183
+ const stat = fs.statSync(gitPath);
184
+ if (stat.isDirectory()) {
185
+ return gitPath;
186
+ }
187
+ if (stat.isFile()) {
188
+ return parseGitDirFile(gitPath);
189
+ }
190
+ } catch {
191
+ return null;
192
+ }
193
+
194
+ return null;
195
+ }
196
+
197
+ function ensureGitExclude(projectRoot) {
198
+ const gitDir = resolveGitDir(projectRoot);
199
+ if (!gitDir) {
200
+ return false;
201
+ }
202
+
203
+ const excludePath = path.join(gitDir, 'info', 'exclude');
204
+ fs.mkdirSync(path.dirname(excludePath), { recursive: true });
205
+ const existing = fs.existsSync(excludePath) ? fs.readFileSync(excludePath, 'utf8') : '';
206
+ const lines = existing.split(/\r?\n/).map(line => line.trim());
207
+ if (lines.includes('.codegraph/')) {
208
+ return false;
209
+ }
210
+
211
+ const prefix = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
212
+ fs.appendFileSync(excludePath, `${prefix}.codegraph/\n`, 'utf8');
213
+ return true;
214
+ }
215
+
216
+ function timeoutMs() {
217
+ const parsed = Number(process.env.TSP_CODEGRAPH_AUTO_INIT_TIMEOUT_MS || DEFAULT_TIMEOUT_MS);
218
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS;
219
+ }
220
+
221
+ function truncate(text, maxLength = 2000) {
222
+ const value = String(text || '');
223
+ return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
224
+ }
225
+
226
+ function initializeProject(projectRoot) {
227
+ const resolved = resolveCodeGraphBin({ ignoreForceStandalone: true });
228
+ if (!resolved) {
229
+ throw new Error('CodeGraph binary not found. Install it with the official standalone installer or set CODEGRAPH_INSTALL_BIN.');
230
+ }
231
+
232
+ const result = spawnSync(resolved.command, [...resolved.argsPrefix, 'init', '-i', projectRoot], {
233
+ cwd: projectRoot,
234
+ env: process.env,
235
+ encoding: 'utf8',
236
+ timeout: timeoutMs(),
237
+ });
238
+
239
+ if (result.error) {
240
+ throw result.error;
241
+ }
242
+
243
+ if (result.status !== 0) {
244
+ throw new Error(truncate(`${result.stdout || ''}${result.stderr || ''}`.trim() || `codegraph exited with status ${result.status}`));
245
+ }
246
+
247
+ return {
248
+ binary: resolved.displayCommand,
249
+ };
250
+ }
251
+
252
+ function runAutoInit(rawInput = '') {
253
+ if (isDisabled()) {
254
+ return { status: 'skipped', reason: 'disabled' };
255
+ }
256
+
257
+ const projectRoot = resolveProjectRoot(rawInput);
258
+ if (!projectRoot) {
259
+ return { status: 'skipped', reason: 'no-project-root' };
260
+ }
261
+
262
+ if (fs.existsSync(codegraphDbPath(projectRoot))) {
263
+ return { status: 'skipped', reason: 'already-initialized', projectRoot };
264
+ }
265
+
266
+ if (shouldThrottleFailure(projectRoot)) {
267
+ return { status: 'skipped', reason: 'recent-failure', projectRoot };
268
+ }
269
+
270
+ try {
271
+ ensureGitExclude(projectRoot);
272
+ const initialized = initializeProject(projectRoot);
273
+ writeState(projectRoot, {
274
+ status: 'initialized',
275
+ projectRoot,
276
+ initializedAt: new Date().toISOString(),
277
+ binary: initialized.binary,
278
+ });
279
+ return { status: 'initialized', projectRoot };
280
+ } catch (error) {
281
+ writeState(projectRoot, {
282
+ status: 'failed',
283
+ projectRoot,
284
+ failedAt: new Date().toISOString(),
285
+ error: truncate(error && error.message ? error.message : error),
286
+ });
287
+ return { status: 'failed', projectRoot, error };
288
+ }
289
+ }
290
+
291
+ function run(rawInput = '') {
292
+ const result = runAutoInit(rawInput);
293
+ if (result.status === 'failed' && process.env.TSP_CODEGRAPH_AUTO_INIT_VERBOSE === '1') {
294
+ return {
295
+ stderr: `[CodeGraph] auto-init failed for ${result.projectRoot}: ${result.error && result.error.message ? result.error.message : result.error}\n`,
296
+ exitCode: 0,
297
+ };
298
+ }
299
+ return { exitCode: 0 };
300
+ }
301
+
302
+ if (require.main === module) {
303
+ let raw = '';
304
+ process.stdin.setEncoding('utf8');
305
+ process.stdin.on('data', chunk => {
306
+ raw += chunk;
307
+ });
308
+ process.stdin.on('end', () => {
309
+ run(raw);
310
+ });
311
+ }
312
+
313
+ module.exports = {
314
+ codegraphDbPath,
315
+ ensureGitExclude,
316
+ findGitRoot,
317
+ findMarkerRoot,
318
+ readState,
319
+ resolveProjectRoot,
320
+ run,
321
+ runAutoInit,
322
+ shouldThrottleFailure,
323
+ statePath,
324
+ };
@@ -1,9 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
3
 
4
+ const fs = require('fs')
5
+ const os = require('os')
4
6
  const path = require('path')
5
7
  const { spawnSync } = require('child_process')
6
8
 
9
+ const INSTALL_SH_URL = 'https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh'
10
+ const INSTALL_PS1_URL = 'https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1'
11
+
7
12
  const SUPPORTED_TARGETS = Object.freeze({
8
13
  claude: 'claude',
9
14
  codex: 'codex',
@@ -50,29 +55,144 @@ function parseArgs(argv) {
50
55
  return parsed
51
56
  }
52
57
 
53
- function resolveCodeGraphBin() {
58
+ function isWindows(platform = process.platform) {
59
+ return platform === 'win32'
60
+ }
61
+
62
+ function executableNames(name, platform = process.platform) {
63
+ if (!isWindows(platform)) {
64
+ return [name]
65
+ }
66
+
67
+ const extensions = String(process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
68
+ .split(';')
69
+ .map(value => value.trim().toLowerCase())
70
+ .filter(Boolean)
71
+ const lowerName = name.toLowerCase()
72
+ if (extensions.some(ext => lowerName.endsWith(ext))) {
73
+ return [name]
74
+ }
75
+ return [name, ...extensions.map(ext => `${name}${ext}`)]
76
+ }
77
+
78
+ function canExecute(filePath) {
79
+ try {
80
+ fs.accessSync(filePath, fs.constants.X_OK)
81
+ return true
82
+ } catch {
83
+ return false
84
+ }
85
+ }
86
+
87
+ function findOnPath(commandName, options = {}) {
88
+ const searchPath = options.pathValue !== undefined ? options.pathValue : process.env.PATH
89
+ const platform = options.platform || process.platform
90
+ const delimiter = isWindows(platform) ? ';' : path.delimiter
91
+ const names = executableNames(commandName, platform)
92
+
93
+ for (const entry of String(searchPath || '').split(delimiter)) {
94
+ if (!entry) {
95
+ continue
96
+ }
97
+ for (const name of names) {
98
+ const candidate = path.join(entry, name)
99
+ if (canExecute(candidate)) {
100
+ return candidate
101
+ }
102
+ }
103
+ }
104
+
105
+ return null
106
+ }
107
+
108
+ function defaultCodeGraphCandidates(options = {}) {
109
+ const homeDir = options.homeDir || os.homedir()
110
+ const platform = options.platform || process.platform
111
+
112
+ if (isWindows(platform)) {
113
+ const localAppData = process.env.LOCALAPPDATA || path.join(homeDir, 'AppData', 'Local')
114
+ return [
115
+ path.join(localAppData, 'codegraph', 'current', 'bin', 'codegraph.exe'),
116
+ path.join(localAppData, 'codegraph', 'current', 'bin', 'codegraph.cmd'),
117
+ ]
118
+ }
119
+
120
+ return [
121
+ path.join(homeDir, '.local', 'bin', 'codegraph'),
122
+ path.join(homeDir, '.codegraph', 'current', 'bin', 'codegraph'),
123
+ ]
124
+ }
125
+
126
+ function resolveCodeGraphBin(options = {}) {
54
127
  if (process.env.CODEGRAPH_INSTALL_BIN) {
55
128
  return {
56
129
  command: process.env.CODEGRAPH_INSTALL_BIN,
57
130
  argsPrefix: [],
58
131
  displayCommand: process.env.CODEGRAPH_INSTALL_BIN,
132
+ source: 'env',
59
133
  }
60
134
  }
61
135
 
62
- let packageJsonPath
63
- try {
64
- packageJsonPath = require.resolve('@colbymchenry/codegraph/package.json')
65
- } catch (error) {
66
- throw new Error(
67
- 'Unable to resolve @colbymchenry/codegraph. Run npm install before applying the CodeGraph integration.'
68
- )
136
+ if (process.env.CODEGRAPH_INSTALL_FORCE_STANDALONE === '1' && !options.ignoreForceStandalone) {
137
+ return null
138
+ }
139
+
140
+ const pathCandidate = findOnPath('codegraph', options)
141
+ if (pathCandidate) {
142
+ return {
143
+ command: pathCandidate,
144
+ argsPrefix: [],
145
+ displayCommand: pathCandidate,
146
+ source: 'path',
147
+ }
148
+ }
149
+
150
+ for (const candidate of defaultCodeGraphCandidates(options)) {
151
+ if (canExecute(candidate)) {
152
+ return {
153
+ command: candidate,
154
+ argsPrefix: [],
155
+ displayCommand: candidate,
156
+ source: 'default-location',
157
+ }
158
+ }
159
+ }
160
+
161
+ return null
162
+ }
163
+
164
+ function resolvePowerShellCommand() {
165
+ return findOnPath('powershell.exe') || findOnPath('powershell') || findOnPath('pwsh') || 'powershell.exe'
166
+ }
167
+
168
+ function buildStandaloneInstallerInfo(platform = process.platform) {
169
+ if (platform === 'darwin' || platform === 'linux') {
170
+ return {
171
+ supported: true,
172
+ platform,
173
+ display: `curl -fsSL ${INSTALL_SH_URL} -o "$TMPDIR/codegraph-install.sh" && sh "$TMPDIR/codegraph-install.sh"`,
174
+ requiredCommand: 'curl',
175
+ url: INSTALL_SH_URL,
176
+ }
177
+ }
178
+
179
+ if (platform === 'win32') {
180
+ return {
181
+ supported: true,
182
+ platform,
183
+ display: `irm ${INSTALL_PS1_URL} | iex`,
184
+ requiredCommand: resolvePowerShellCommand(),
185
+ url: INSTALL_PS1_URL,
186
+ }
69
187
  }
70
188
 
71
- const binPath = path.join(path.dirname(packageJsonPath), 'dist', 'bin', 'codegraph.js')
72
189
  return {
73
- command: process.execPath,
74
- argsPrefix: [binPath],
75
- displayCommand: `${process.execPath} ${binPath}`,
190
+ supported: false,
191
+ platform,
192
+ display: '',
193
+ requiredCommand: '',
194
+ url: '',
195
+ reason: `CodeGraph standalone installer does not support platform: ${platform}`,
76
196
  }
77
197
  }
78
198
 
@@ -88,27 +208,101 @@ function buildInstallCommand(target) {
88
208
 
89
209
  const resolved = resolveCodeGraphBin()
90
210
  const args = [
91
- ...resolved.argsPrefix,
211
+ ...(resolved ? resolved.argsPrefix : []),
92
212
  'install',
93
213
  `--target=${mappedTarget}`,
94
214
  '--location=global',
95
215
  '--yes',
96
216
  ]
217
+ const displayCommand = resolved ? resolved.displayCommand : 'codegraph'
97
218
 
98
219
  return {
99
220
  supported: true,
100
221
  target: mappedTarget,
222
+ command: resolved ? resolved.command : null,
223
+ args,
224
+ display: [displayCommand, ...args.slice(resolved ? resolved.argsPrefix.length : 0)].join(' '),
225
+ needsBootstrap: !resolved,
226
+ bootstrap: buildStandaloneInstallerInfo(),
227
+ binarySource: resolved ? resolved.source : null,
228
+ }
229
+ }
230
+
231
+ function spawnChecked(command, args, options = {}) {
232
+ const result = spawnSync(command, args, {
233
+ cwd: options.cwd || process.cwd(),
234
+ env: options.env || process.env,
235
+ encoding: 'utf8',
236
+ stdio: options.stdio || ['inherit', process.stderr, process.stderr],
237
+ timeout: options.timeout,
238
+ })
239
+
240
+ return typeof result.status === 'number' ? result.status : 1
241
+ }
242
+
243
+ function runStandaloneInstaller() {
244
+ const installer = buildStandaloneInstallerInfo()
245
+ if (!installer.supported) {
246
+ throw new Error(installer.reason)
247
+ }
248
+
249
+ if (installer.platform === 'darwin' || installer.platform === 'linux') {
250
+ const curl = findOnPath('curl') || 'curl'
251
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-install-'))
252
+ const scriptPath = path.join(tempDir, 'install.sh')
253
+ try {
254
+ let status = spawnChecked(curl, ['-fsSL', INSTALL_SH_URL, '-o', scriptPath])
255
+ if (status !== 0) {
256
+ return status
257
+ }
258
+ status = spawnChecked('sh', [scriptPath])
259
+ return status
260
+ } finally {
261
+ fs.rmSync(tempDir, { recursive: true, force: true })
262
+ }
263
+ }
264
+
265
+ return spawnChecked(resolvePowerShellCommand(), [
266
+ '-NoProfile',
267
+ '-ExecutionPolicy',
268
+ 'Bypass',
269
+ '-Command',
270
+ `irm ${INSTALL_PS1_URL} | iex`,
271
+ ])
272
+ }
273
+
274
+ function buildTargetInstallInvocation(target) {
275
+ const mappedTarget = mapTarget(target)
276
+ const resolved = resolveCodeGraphBin({ ignoreForceStandalone: true })
277
+ if (!mappedTarget || !resolved) {
278
+ return null
279
+ }
280
+
281
+ const args = [
282
+ ...resolved.argsPrefix,
283
+ 'install',
284
+ `--target=${mappedTarget}`,
285
+ '--location=global',
286
+ '--yes',
287
+ ]
288
+
289
+ return {
101
290
  command: resolved.command,
102
291
  args,
103
- display: [resolved.displayCommand, ...args.slice(resolved.argsPrefix.length)].join(' '),
104
292
  }
105
293
  }
106
294
 
107
295
  function printHelp() {
108
296
  console.log(`Usage: node scripts/install-codegraph.js [--target <claude|codex|cursor|opencode>] [--dry-run]
109
297
 
110
- Runs the upstream CodeGraph installer for the current TSP install target only.
111
- The project index is not initialized here; run codegraph init -i inside a target project when needed.
298
+ Installs the standalone CodeGraph CLI with the official upstream curl/PowerShell installer when needed,
299
+ then configures CodeGraph for the current TSP install target only.
300
+
301
+ Environment:
302
+ CODEGRAPH_INSTALL_BIN=<path> Use an existing CodeGraph binary, including offline installs.
303
+ CODEGRAPH_INSTALL_FORCE_STANDALONE=1 Re-run the official standalone installer before target config.
304
+
305
+ Project indexes are created by the Claude SessionStart auto-init hook or by running codegraph init -i.
112
306
  `)
113
307
  }
114
308
 
@@ -126,19 +320,31 @@ function run(argv = process.argv.slice(2)) {
126
320
  }
127
321
 
128
322
  if (options.dryRun) {
129
- console.log(`CodeGraph install command: ${install.display}`)
323
+ if (install.needsBootstrap) {
324
+ console.log(`CodeGraph standalone install command: ${install.bootstrap.display}`)
325
+ } else {
326
+ console.log(`CodeGraph binary: ${install.command} (${install.binarySource})`)
327
+ }
328
+ console.log(`CodeGraph target install command: ${install.display}`)
130
329
  return 0
131
330
  }
132
331
 
133
- console.error(`Running CodeGraph installer for ${install.target}`)
134
- const result = spawnSync(install.command, install.args, {
135
- cwd: process.cwd(),
136
- env: process.env,
137
- encoding: 'utf8',
138
- stdio: ['inherit', process.stderr, process.stderr],
139
- })
332
+ if (install.needsBootstrap) {
333
+ console.error('Installing CodeGraph standalone CLI with the official upstream installer')
334
+ const bootstrapStatus = runStandaloneInstaller()
335
+ if (bootstrapStatus !== 0) {
336
+ return bootstrapStatus
337
+ }
338
+ }
140
339
 
141
- return typeof result.status === 'number' ? result.status : 1
340
+ const invocation = buildTargetInstallInvocation(install.target)
341
+ if (!invocation) {
342
+ console.error('CodeGraph binary is still unavailable after standalone install. Add it to PATH or set CODEGRAPH_INSTALL_BIN.')
343
+ return 1
344
+ }
345
+
346
+ console.error(`Running CodeGraph installer for ${install.target}`)
347
+ return spawnChecked(invocation.command, invocation.args)
142
348
  }
143
349
 
144
350
  if (require.main === module) {
@@ -152,7 +358,11 @@ if (require.main === module) {
152
358
 
153
359
  module.exports = {
154
360
  buildInstallCommand,
361
+ buildStandaloneInstallerInfo,
362
+ defaultCodeGraphCandidates,
363
+ findOnPath,
155
364
  mapTarget,
156
365
  parseArgs,
366
+ resolveCodeGraphBin,
157
367
  run,
158
368
  }
@@ -3,7 +3,7 @@ name: codegraph
3
3
  description: >
4
4
  将 CodeGraph 作为默认内置 MCP-backed 代码图谱能力接入,用于 brownfield 符号搜索、
5
5
  调用链、影响面和上下文查询。输出必须回落到 `/team-*` 主链和 artifacts。
6
- origin: colbymchenry/codegraph (default dependency with upstream installer wrapper)
6
+ origin: colbymchenry/codegraph (official standalone installer with target-scoped wrapper)
7
7
  ---
8
8
 
9
9
  # CodeGraph
@@ -22,8 +22,8 @@ origin: colbymchenry/codegraph (default dependency with upstream installer wrapp
22
22
 
23
23
  ## 默认工作流
24
24
 
25
- 1. 先跑 `npm run codegraph:doctor`,确认 Node、CodeGraph 包和当前 target wrapper 可用。
26
- 2. 在消费方目标项目根目录手动初始化索引:
25
+ 1. 先跑 `npm run codegraph:doctor`,确认 standalone CodeGraph binary、官方 installer 依赖和当前 target wrapper 可用。
26
+ 2. Claude 新项目会通过 `SessionStart` 自动初始化索引;非 Claude 或关闭自动初始化时,在消费方目标项目根目录手动初始化:
27
27
  ```bash
28
28
  codegraph init -i
29
29
  ```
@@ -46,12 +46,13 @@ origin: colbymchenry/codegraph (default dependency with upstream installer wrapp
46
46
  ## 边界与禁用项
47
47
 
48
48
  - TSP 安装时只运行 `scripts/install-codegraph.js` wrapper,不使用上游 `--target=auto`。
49
- - TSP 安装流程不运行 `codegraph init -i`,避免在平台仓库或消费方仓库隐式写入 `.codegraph/`。
49
+ - Claude `SessionStart` 可在新项目缺少 `.codegraph/codegraph.db` 时静默执行 `codegraph init -i <projectRoot>`;用 `TSP_CODEGRAPH_AUTO_INIT=0` 可关闭。
50
+ - Codex / OpenCode 不做侵入式自动 hook,只依赖全局 MCP 配置、说明和 doctor 诊断。
50
51
  - 不提交 `.codegraph/` 数据库或将其作为 TSP artifact。
51
52
  - CodeGraph 结论不能绕过 `/team-plan`、`/team-review` 或验证门禁。
52
53
 
53
54
  ## 推荐组合
54
55
 
55
- - 默认 brownfield 结构证据:`/team-help -> /update-codemaps -> npm run codegraph:doctor -> codegraph init -i -> /team-plan`
56
+ - 默认 brownfield 结构证据:`/team-help -> /update-codemaps -> npm run codegraph:doctor -> Claude 自动初始化或 codegraph init -i -> /team-plan`
56
57
  - 快速影响面确认:`/team-execute -> CodeGraph impact/callers/callees -> /handoff -> /team-review`
57
58
  - 深度多仓或许可证受限场景:按需选择 GitNexus 或 Graphify,并把结论统一回落到主链。
@@ -1,15 +1,15 @@
1
- # https://goframe.org/docs/web/server-config-file-template
2
- server:
3
- address: ":8000"
4
- openapiPath: "/api.json"
5
- swaggerPath: "/swagger"
6
-
7
- # https://goframe.org/docs/core/glog-config
8
- logger:
9
- level : "all"
10
- stdout: true
11
-
12
- # https://goframe.org/docs/core/gdb-config-file
13
- database:
14
- default:
1
+ # https://goframe.org/docs/web/server-config-file-template
2
+ server:
3
+ address: ":8000"
4
+ openapiPath: "/api.json"
5
+ swaggerPath: "/swagger"
6
+
7
+ # https://goframe.org/docs/core/glog-config
8
+ logger:
9
+ level : "all"
10
+ stdout: true
11
+
12
+ # https://goframe.org/docs/core/gdb-config-file
13
+ database:
14
+ default:
15
15
  link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
@@ -1,22 +1,22 @@
1
- ---
2
- name: repo-scan
3
- description: Cross-stack source code asset audit — classifies every file, detects embedded third-party libraries, and delivers actionable four-level verdicts per module with interactive HTML reports.
4
- origin: community
5
- ---
6
-
7
- # repo-scan
8
-
9
- > Every ecosystem has its own dependency manager, but no tool looks across C++, Android, iOS, and Web to tell you: how much code is actually yours, what's third-party, and what's dead weight.
10
-
11
- ## When to Use
12
-
13
- - Taking over a large legacy codebase and need a structural overview
14
- - Before major refactoring — identify what's core, what's duplicate, what's dead
15
- - Auditing third-party dependencies embedded directly in source (not declared in package managers)
16
- - Preparing architecture decision records for monorepo reorganization
17
-
18
- ## Installation
19
-
1
+ ---
2
+ name: repo-scan
3
+ description: Cross-stack source code asset audit — classifies every file, detects embedded third-party libraries, and delivers actionable four-level verdicts per module with interactive HTML reports.
4
+ origin: community
5
+ ---
6
+
7
+ # repo-scan
8
+
9
+ > Every ecosystem has its own dependency manager, but no tool looks across C++, Android, iOS, and Web to tell you: how much code is actually yours, what's third-party, and what's dead weight.
10
+
11
+ ## When to Use
12
+
13
+ - Taking over a large legacy codebase and need a structural overview
14
+ - Before major refactoring — identify what's core, what's duplicate, what's dead
15
+ - Auditing third-party dependencies embedded directly in source (not declared in package managers)
16
+ - Preparing architecture decision records for monorepo reorganization
17
+
18
+ ## Installation
19
+
20
20
  ```bash
21
21
  # Fetch only the pinned commit for reproducibility
22
22
  mkdir -p ~/.claude/skills/repo-scan
@@ -27,52 +27,52 @@ git fetch --depth 1 origin 2742664
27
27
  git checkout --detach FETCH_HEAD
28
28
  cp -r . ~/.claude/skills/repo-scan
29
29
  ```
30
-
31
- > Review the source before installing any agent skill.
32
-
33
- ## Core Capabilities
34
-
35
- | Capability | Description |
36
- |---|---|
37
- | **Cross-stack scanning** | C/C++, Java/Android, iOS (OC/Swift), Web (TS/JS/Vue) in one pass |
38
- | **File classification** | Every file tagged as project code, third-party, or build artifact |
39
- | **Library detection** | 50+ known libraries (FFmpeg, Boost, OpenSSL…) with version extraction |
40
- | **Four-level verdicts** | Core Asset / Extract & Merge / Rebuild / Deprecate |
41
- | **HTML reports** | Interactive dark-theme pages with drill-down navigation |
42
- | **Monorepo support** | Hierarchical scanning with summary + sub-project reports |
43
-
44
- ## Analysis Depth Levels
45
-
46
- | Level | Files Read | Use Case |
47
- |---|---|---|
48
- | `fast` | 1-2 per module | Quick inventory of huge directories |
49
- | `standard` | 2-5 per module | Default audit with full dependency + architecture checks |
50
- | `deep` | 5-10 per module | Adds thread safety, memory management, API consistency |
51
- | `full` | All files | Pre-merge comprehensive review |
52
-
53
- ## How It Works
54
-
30
+
31
+ > Review the source before installing any agent skill.
32
+
33
+ ## Core Capabilities
34
+
35
+ | Capability | Description |
36
+ |---|---|
37
+ | **Cross-stack scanning** | C/C++, Java/Android, iOS (OC/Swift), Web (TS/JS/Vue) in one pass |
38
+ | **File classification** | Every file tagged as project code, third-party, or build artifact |
39
+ | **Library detection** | 50+ known libraries (FFmpeg, Boost, OpenSSL…) with version extraction |
40
+ | **Four-level verdicts** | Core Asset / Extract & Merge / Rebuild / Deprecate |
41
+ | **HTML reports** | Interactive dark-theme pages with drill-down navigation |
42
+ | **Monorepo support** | Hierarchical scanning with summary + sub-project reports |
43
+
44
+ ## Analysis Depth Levels
45
+
46
+ | Level | Files Read | Use Case |
47
+ |---|---|---|
48
+ | `fast` | 1-2 per module | Quick inventory of huge directories |
49
+ | `standard` | 2-5 per module | Default audit with full dependency + architecture checks |
50
+ | `deep` | 5-10 per module | Adds thread safety, memory management, API consistency |
51
+ | `full` | All files | Pre-merge comprehensive review |
52
+
53
+ ## How It Works
54
+
55
55
  1. **Classify the repo surface**: enumerate files, then tag each as project code, embedded third-party code, or build artifact.
56
56
  2. **Detect embedded libraries**: inspect directory names, headers, license files, and version markers to identify bundled dependencies and likely versions.
57
57
  3. **Score each module**: group files by module or subsystem, then assign one of the four verdicts based on ownership, duplication, and maintenance cost.
58
58
  4. **Highlight structural risks**: call out dead-weight artifacts, duplicated wrappers, outdated vendored code, and modules that should be extracted, rebuilt, or deprecated.
59
59
  5. **Produce the report**: return a concise summary plus the interactive HTML output with per-module drill-down so the audit can be reviewed asynchronously.
60
-
61
- ## Examples
62
-
63
- On a 50,000-file C++ monorepo:
64
- - Found FFmpeg 2.x (2015 vintage) still in production
65
- - Discovered the same SDK wrapper duplicated 3 times
66
- - Identified 636 MB of committed Debug/ipch/obj build artifacts
67
- - Classified: 3 MB project code vs 596 MB third-party
68
-
69
- ## Best Practices
70
-
71
- - Start with `standard` depth for first-time audits
72
- - Use `fast` for monorepos with 100+ modules to get a quick inventory
73
- - Run `deep` incrementally on modules flagged for refactoring
74
- - Review the cross-module analysis for duplicate detection across sub-projects
75
-
76
- ## Links
77
-
78
- - [GitHub Repository](https://github.com/haibindev/repo-scan)
60
+
61
+ ## Examples
62
+
63
+ On a 50,000-file C++ monorepo:
64
+ - Found FFmpeg 2.x (2015 vintage) still in production
65
+ - Discovered the same SDK wrapper duplicated 3 times
66
+ - Identified 636 MB of committed Debug/ipch/obj build artifacts
67
+ - Classified: 3 MB project code vs 596 MB third-party
68
+
69
+ ## Best Practices
70
+
71
+ - Start with `standard` depth for first-time audits
72
+ - Use `fast` for monorepos with 100+ modules to get a quick inventory
73
+ - Run `deep` incrementally on modules flagged for refactoring
74
+ - Review the cross-module analysis for duplicate detection across sub-projects
75
+
76
+ ## Links
77
+
78
+ - [GitHub Repository](https://github.com/haibindev/repo-scan)