@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.
- package/README.md +3 -3
- package/hooks/hooks.json +11 -0
- package/manifests/install-modules.json +3 -2
- package/package.json +1 -2
- package/scripts/codegraph-preflight.js +68 -75
- package/scripts/hooks/codegraph-auto-init.js +324 -0
- package/scripts/install-codegraph.js +235 -25
- package/skills/codegraph/SKILL.md +6 -5
- package/skills/goframe-v2/examples/practices/quick-demo/manifest/config/config.yaml +14 -14
- package/skills/repo-scan/SKILL.md +63 -63
- package/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/__pycache__/build_platform_artifacts.cpython-311.pyc +0 -0
- package/scripts/__pycache__/install_platform.cpython-311.pyc +0 -0
- package/scripts/__pycache__/langfuse_trace.cpython-311.pyc +0 -0
- package/scripts/__pycache__/query_audit_logs.cpython-311.pyc +0 -0
- package/scripts/__pycache__/scan_leaked_keys.cpython-311.pyc +0 -0
- package/scripts/__pycache__/team_skills_platform.cpython-311.pyc +0 -0
- package/scripts/__pycache__/team_skills_platform.cpython-313.pyc +0 -0
- package/scripts/__pycache__/validate_library.cpython-311.pyc +0 -0
- package/scripts/__pycache__/validate_workflow_state.cpython-311.pyc +0 -0
- package/scripts/evolution/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/evolution/__pycache__/store.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/mcp_health_check.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/observe.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/session_end.cpython-311.pyc +0 -0
- package/scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/audit_logger.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/audit_query.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/hook_contract.cpython-311.pyc +0 -0
- package/scripts/lib/__pycache__/memory_store.cpython-311.pyc +0 -0
- 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
|
|
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
|
|
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": "
|
|
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.
|
|
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 {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
return
|
|
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
|
-
|
|
20
|
-
|
|
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
|
|
27
|
-
|
|
28
|
-
|
|
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:
|
|
38
|
-
|
|
39
|
-
metadata: JSON.parse(process.env.CODEGRAPH_PREFLIGHT_PACKAGE_JSON),
|
|
30
|
+
ok: false,
|
|
31
|
+
output: installer.reason,
|
|
40
32
|
}
|
|
41
33
|
}
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
const
|
|
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
|
-
|
|
48
|
-
metadata: JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')),
|
|
45
|
+
output: command,
|
|
49
46
|
}
|
|
50
|
-
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const curl = findOnPath('curl')
|
|
50
|
+
if (!curl) {
|
|
51
51
|
return {
|
|
52
52
|
ok: false,
|
|
53
|
-
|
|
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(
|
|
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
|
-
|
|
69
|
-
|
|
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:
|
|
77
|
+
output: 'CodeGraph standalone binary not found',
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
|
|
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:
|
|
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:
|
|
104
|
-
output:
|
|
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
|
|
114
|
-
const
|
|
115
|
-
const cli = detectCli(
|
|
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 (!
|
|
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(`-
|
|
114
|
+
console.log(`- Platform: ${platform.output}`)
|
|
125
115
|
} else {
|
|
126
|
-
console.log(`-
|
|
116
|
+
console.log(`- Platform: ${platform.output} (ok)`)
|
|
127
117
|
}
|
|
128
118
|
|
|
129
|
-
if (
|
|
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(`-
|
|
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('-
|
|
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
|
-
|
|
178
|
-
|
|
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
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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 (
|
|
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`,确认
|
|
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
|
-
-
|
|
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)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|