deepspider 0.2.11 → 0.3.0

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 (146) hide show
  1. package/README.md +71 -24
  2. package/bin/cli.js +45 -0
  3. package/package.json +10 -4
  4. package/src/agent/core/PanelBridge.js +133 -0
  5. package/src/agent/core/RetryManager.js +51 -0
  6. package/src/agent/core/StreamHandler.js +263 -0
  7. package/src/agent/core/index.js +7 -0
  8. package/src/agent/errors/ErrorClassifier.js +43 -0
  9. package/src/agent/errors/SpiderError.js +68 -0
  10. package/src/agent/errors/index.js +19 -0
  11. package/src/agent/run.js +67 -460
  12. package/src/agent/setup.js +14 -14
  13. package/src/agent/subagents/factory.js +60 -0
  14. package/src/agent/subagents/index.js +3 -0
  15. package/src/agent/tools/report.js +36 -4
  16. package/src/browser/client.js +47 -10
  17. package/src/cli/commands/config.js +94 -0
  18. package/src/cli/commands/help.js +34 -0
  19. package/src/cli/commands/update.js +78 -0
  20. package/src/cli/commands/version.js +9 -0
  21. package/src/cli/config.js +15 -0
  22. package/src/config/settings.js +102 -0
  23. package/.claude/agents/check.md +0 -122
  24. package/.claude/agents/debug.md +0 -106
  25. package/.claude/agents/dispatch.md +0 -214
  26. package/.claude/agents/implement.md +0 -96
  27. package/.claude/agents/plan.md +0 -396
  28. package/.claude/agents/research.md +0 -120
  29. package/.claude/commands/evolve/merge.md +0 -80
  30. package/.claude/commands/trellis/before-backend-dev.md +0 -13
  31. package/.claude/commands/trellis/before-frontend-dev.md +0 -13
  32. package/.claude/commands/trellis/break-loop.md +0 -107
  33. package/.claude/commands/trellis/check-backend.md +0 -13
  34. package/.claude/commands/trellis/check-cross-layer.md +0 -153
  35. package/.claude/commands/trellis/check-frontend.md +0 -13
  36. package/.claude/commands/trellis/create-command.md +0 -154
  37. package/.claude/commands/trellis/finish-work.md +0 -129
  38. package/.claude/commands/trellis/integrate-skill.md +0 -219
  39. package/.claude/commands/trellis/onboard.md +0 -358
  40. package/.claude/commands/trellis/parallel.md +0 -193
  41. package/.claude/commands/trellis/record-session.md +0 -62
  42. package/.claude/commands/trellis/start.md +0 -280
  43. package/.claude/commands/trellis/update-spec.md +0 -213
  44. package/.claude/hooks/inject-subagent-context.py +0 -758
  45. package/.claude/hooks/ralph-loop.py +0 -374
  46. package/.claude/hooks/session-start.py +0 -126
  47. package/.claude/settings.json +0 -41
  48. package/.claude/skills/deepagents-guide/SKILL.md +0 -428
  49. package/.cursor/commands/trellis-before-backend-dev.md +0 -13
  50. package/.cursor/commands/trellis-before-frontend-dev.md +0 -13
  51. package/.cursor/commands/trellis-break-loop.md +0 -107
  52. package/.cursor/commands/trellis-check-backend.md +0 -13
  53. package/.cursor/commands/trellis-check-cross-layer.md +0 -153
  54. package/.cursor/commands/trellis-check-frontend.md +0 -13
  55. package/.cursor/commands/trellis-create-command.md +0 -154
  56. package/.cursor/commands/trellis-finish-work.md +0 -129
  57. package/.cursor/commands/trellis-integrate-skill.md +0 -219
  58. package/.cursor/commands/trellis-onboard.md +0 -358
  59. package/.cursor/commands/trellis-record-session.md +0 -62
  60. package/.cursor/commands/trellis-start.md +0 -156
  61. package/.cursor/commands/trellis-update-spec.md +0 -213
  62. package/.github/workflows/publish.yml +0 -63
  63. package/.husky/pre-commit +0 -1
  64. package/.mcp.json +0 -8
  65. package/.trellis/.template-hashes.json +0 -65
  66. package/.trellis/.version +0 -1
  67. package/.trellis/scripts/add-session.sh +0 -384
  68. package/.trellis/scripts/common/developer.sh +0 -129
  69. package/.trellis/scripts/common/git-context.sh +0 -263
  70. package/.trellis/scripts/common/paths.sh +0 -208
  71. package/.trellis/scripts/common/phase.sh +0 -150
  72. package/.trellis/scripts/common/registry.sh +0 -247
  73. package/.trellis/scripts/common/task-queue.sh +0 -142
  74. package/.trellis/scripts/common/task-utils.sh +0 -151
  75. package/.trellis/scripts/common/worktree.sh +0 -128
  76. package/.trellis/scripts/create-bootstrap.sh +0 -299
  77. package/.trellis/scripts/get-context.sh +0 -7
  78. package/.trellis/scripts/get-developer.sh +0 -15
  79. package/.trellis/scripts/init-developer.sh +0 -34
  80. package/.trellis/scripts/multi-agent/cleanup.sh +0 -396
  81. package/.trellis/scripts/multi-agent/create-pr.sh +0 -241
  82. package/.trellis/scripts/multi-agent/plan.sh +0 -207
  83. package/.trellis/scripts/multi-agent/start.sh +0 -310
  84. package/.trellis/scripts/multi-agent/status.sh +0 -828
  85. package/.trellis/scripts/task.sh +0 -1118
  86. package/.trellis/spec/backend/ci-cd-guidelines.md +0 -73
  87. package/.trellis/spec/backend/deepagents-guide.md +0 -380
  88. package/.trellis/spec/backend/directory-structure.md +0 -126
  89. package/.trellis/spec/backend/examples/skills/deepagents-guide/README.md +0 -11
  90. package/.trellis/spec/backend/examples/skills/deepagents-guide/agent.js.template +0 -20
  91. package/.trellis/spec/backend/examples/skills/deepagents-guide/skills-config.js.template +0 -13
  92. package/.trellis/spec/backend/examples/skills/deepagents-guide/subagent.js.template +0 -19
  93. package/.trellis/spec/backend/hook-guidelines.md +0 -218
  94. package/.trellis/spec/backend/index.md +0 -37
  95. package/.trellis/spec/backend/quality-guidelines.md +0 -302
  96. package/.trellis/spec/backend/state-management.md +0 -76
  97. package/.trellis/spec/backend/tool-guidelines.md +0 -144
  98. package/.trellis/spec/backend/type-safety.md +0 -71
  99. package/.trellis/spec/guides/code-reuse-thinking-guide.md +0 -92
  100. package/.trellis/spec/guides/cross-layer-thinking-guide.md +0 -94
  101. package/.trellis/spec/guides/index.md +0 -79
  102. package/.trellis/tasks/archive/02-02-evolving-skills/prd.md +0 -61
  103. package/.trellis/tasks/archive/02-02-evolving-skills/task.json +0 -29
  104. package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/prd.md +0 -86
  105. package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/task.json +0 -27
  106. package/.trellis/tasks/archive/2026-02/02-02-skills-system/check.jsonl +0 -3
  107. package/.trellis/tasks/archive/2026-02/02-02-skills-system/debug.jsonl +0 -2
  108. package/.trellis/tasks/archive/2026-02/02-02-skills-system/implement.jsonl +0 -5
  109. package/.trellis/tasks/archive/2026-02/02-02-skills-system/prd.md +0 -33
  110. package/.trellis/tasks/archive/2026-02/02-02-skills-system/task.json +0 -41
  111. package/.trellis/workflow.md +0 -407
  112. package/.trellis/workspace/index.md +0 -123
  113. package/.trellis/workspace/pony/index.md +0 -42
  114. package/.trellis/workspace/pony/journal-1.md +0 -125
  115. package/.trellis/worktree.yaml +0 -47
  116. package/AGENTS.md +0 -18
  117. package/CLAUDE.md +0 -315
  118. package/agents/deepspider.md +0 -142
  119. package/docs/DEBUG.md +0 -42
  120. package/docs/GUIDE.md +0 -334
  121. package/docs/PROMPT.md +0 -60
  122. package/docs/USAGE.md +0 -226
  123. package/eslint.config.js +0 -51
  124. package/test/analyze.test.js +0 -90
  125. package/test/envdump.test.js +0 -74
  126. package/test/flow.test.js +0 -90
  127. package/test/hooks.test.js +0 -138
  128. package/test/plugin.test.js +0 -35
  129. package/test/refactor-full.test.js +0 -30
  130. package/test/refactor.test.js +0 -21
  131. package/test/samples/obfuscated.js +0 -61
  132. package/test/samples/original.js +0 -66
  133. package/test/samples/v10_eval_chain.js +0 -52
  134. package/test/samples/v11_bytecode_vm.js +0 -81
  135. package/test/samples/v12_polymorphic.js +0 -69
  136. package/test/samples/v1_ob_basic.js +0 -98
  137. package/test/samples/v2_ob_advanced.js +0 -99
  138. package/test/samples/v3_jjencode.js +0 -77
  139. package/test/samples/v4_aaencode.js +0 -73
  140. package/test/samples/v5_control_flow.js +0 -86
  141. package/test/samples/v6_string_encryption.js +0 -71
  142. package/test/samples/v7_jsvmp.js +0 -83
  143. package/test/samples/v8_anti_debug.js +0 -79
  144. package/test/samples/v9_proxy_trap.js +0 -49
  145. package/test/samples.test.js +0 -96
  146. package/test/webcrack.test.js +0 -55
@@ -0,0 +1,60 @@
1
+ /**
2
+ * DeepSpider - 子代理工厂函数
3
+ * 统一子代理创建,自动注入公共配置
4
+ */
5
+
6
+ import { createSkillsMiddleware } from 'deepagents';
7
+ import { SKILLS, skillsBackend } from '../skills/config.js';
8
+ import { createFilterToolsMiddleware } from '../middleware/filterTools.js';
9
+ import { evolveTools } from '../tools/evolve.js';
10
+
11
+ /**
12
+ * 创建子代理配置
13
+ * @param {Object} config - 子代理配置
14
+ * @param {string} config.name - 子代理名称
15
+ * @param {string} config.description - 子代理描述
16
+ * @param {string} config.systemPrompt - 系统提示
17
+ * @param {Array} config.tools - 工具列表
18
+ * @param {Array} [config.skills] - 技能源列表(SKILLS 枚举值)
19
+ * @param {Array} [config.middleware] - 额外的中间件
20
+ * @param {boolean} [config.includeEvolve=true] - 是否包含 evolve 工具
21
+ */
22
+ export function createSubagent(config) {
23
+ const {
24
+ name,
25
+ description,
26
+ systemPrompt,
27
+ tools,
28
+ skills = [],
29
+ middleware = [],
30
+ includeEvolve = true,
31
+ } = config;
32
+
33
+ // 合并工具列表
34
+ const finalTools = includeEvolve
35
+ ? [...tools, ...evolveTools]
36
+ : tools;
37
+
38
+ // 构建中间件列表
39
+ const finalMiddleware = [
40
+ createFilterToolsMiddleware(),
41
+ createSkillsMiddleware({
42
+ backend: skillsBackend,
43
+ sources: skills,
44
+ }),
45
+ ...middleware,
46
+ ];
47
+
48
+ return {
49
+ name,
50
+ description,
51
+ systemPrompt,
52
+ tools: finalTools,
53
+ middleware: finalMiddleware,
54
+ };
55
+ }
56
+
57
+ /**
58
+ * 预定义的技能映射
59
+ */
60
+ export { SKILLS };
@@ -2,6 +2,9 @@
2
2
  * DeepSpider - 子代理索引
3
3
  */
4
4
 
5
+ // 工厂函数
6
+ export { createSubagent, SKILLS } from './factory.js';
7
+
5
8
  // 编排层
6
9
  export { crawlerSubagent } from './crawler.js';
7
10
 
@@ -102,8 +102,24 @@ function generateHtmlPage(title, markdown, pythonCode, jsCode) {
102
102
  * 2. 传入代码内容(兼容)- pythonCode/jsCode
103
103
  */
104
104
  export const saveAnalysisReport = tool(
105
- async ({ domain, title, markdown, pythonCode, pythonCodeFile, jsCode, jsCodeFile }) => {
105
+ async ({ domain, title, markdown, pythonCode, pythonCodeFile, jsCode, jsCodeFile, validationResult }) => {
106
106
  try {
107
+ // 验证状态检查
108
+ let validationWarning = '';
109
+ let validationStatus = 'unknown';
110
+
111
+ if (!validationResult) {
112
+ validationWarning = '⚠️ 警告:未提供端到端验证结果,建议先使用 verify_encryption 或 run_python 验证代码正确性';
113
+ validationStatus = 'not_verified';
114
+ console.warn('[report]', validationWarning);
115
+ } else if (validationResult.success === false) {
116
+ validationWarning = `⚠️ 警告:验证失败 - ${validationResult.error || '未知错误'}`;
117
+ validationStatus = 'failed';
118
+ console.warn('[report]', validationWarning);
119
+ } else {
120
+ validationStatus = 'passed';
121
+ }
122
+
107
123
  const domainDir = join(OUTPUT_DIR, extractDomain(domain));
108
124
  ensureDir(domainDir);
109
125
 
@@ -153,18 +169,30 @@ export const saveAnalysisReport = tool(
153
169
  const fileList = Object.entries(paths)
154
170
  .map(([type, p]) => `- ${type}: \`${p}\``)
155
171
  .join('\n');
156
- const finalMarkdown = markdown + '\n\n## 生成文件\n\n' + fileList;
172
+
173
+ // 添加验证状态标记
174
+ const validationSection = validationWarning
175
+ ? `\n\n## 验证状态\n\n${validationWarning}\n`
176
+ : '\n\n## 验证状态\n\n✅ 端到端验证通过\n';
177
+
178
+ const finalMarkdown = markdown + validationSection + '\n## 生成文件\n\n' + fileList;
157
179
  writeFileSync(paths.markdown, finalMarkdown, 'utf-8');
158
180
 
159
181
  console.log('[report] 已保存:', domainDir);
160
- return JSON.stringify({ success: true, paths, dir: domainDir });
182
+ return JSON.stringify({
183
+ success: true,
184
+ paths,
185
+ dir: domainDir,
186
+ validationStatus,
187
+ validationWarning: validationWarning || null,
188
+ });
161
189
  } catch (e) {
162
190
  return JSON.stringify({ success: false, error: e.message });
163
191
  }
164
192
  },
165
193
  {
166
194
  name: 'save_analysis_report',
167
- description: `保存分析报告。推荐先用 artifact_save 保存代码文件,再传入文件路径。`,
195
+ description: `保存分析报告。推荐先用 artifact_save 保存代码文件,再传入文件路径。建议先验证代码正确性再保存报告。`,
168
196
  schema: z.object({
169
197
  domain: z.string().describe('网站域名'),
170
198
  title: z.string().optional().describe('报告标题'),
@@ -173,6 +201,10 @@ export const saveAnalysisReport = tool(
173
201
  pythonCode: z.string().optional().describe('Python 代码内容(不推荐)'),
174
202
  jsCodeFile: z.string().optional().describe('JS 代码文件路径'),
175
203
  jsCode: z.string().optional().describe('JS 代码内容'),
204
+ validationResult: z.object({
205
+ success: z.boolean().describe('验证是否成功'),
206
+ error: z.string().optional().describe('错误信息'),
207
+ }).optional().describe('端到端验证结果(推荐提供)'),
176
208
  }),
177
209
  }
178
210
  );
@@ -4,13 +4,15 @@
4
4
  */
5
5
 
6
6
  import { chromium } from 'patchright';
7
+ import { EventEmitter } from 'events';
7
8
  import { getDefaultHookScript } from './defaultHooks.js';
8
9
  import { NetworkInterceptor } from './interceptors/NetworkInterceptor.js';
9
10
  import { ScriptInterceptor } from './interceptors/ScriptInterceptor.js';
10
11
  import { getDataStore } from '../store/DataStore.js';
11
12
 
12
- export class BrowserClient {
13
+ export class BrowserClient extends EventEmitter {
13
14
  constructor() {
15
+ super();
14
16
  this.browser = null;
15
17
  this.context = null;
16
18
  this.page = null;
@@ -20,6 +22,7 @@ export class BrowserClient {
20
22
  this.scriptInterceptor = null;
21
23
  this.hookScript = null;
22
24
  this.onMessage = null;
25
+ this._isCleaningUp = false;
23
26
  }
24
27
 
25
28
  /**
@@ -51,6 +54,8 @@ export class BrowserClient {
51
54
  }
52
55
 
53
56
  this.browser = await chromium.launch(launchOptions);
57
+ this.emit('launched', { headless });
58
+
54
59
  this.context = await this.browser.newContext({
55
60
  ignoreHTTPSErrors: true,
56
61
  });
@@ -183,15 +188,47 @@ export class BrowserClient {
183
188
  * 关闭浏览器
184
189
  */
185
190
  async close() {
186
- if (this.cdpSession) {
187
- await this.cdpSession.detach().catch(() => {});
188
- this.cdpSession = null;
189
- }
190
- if (this.browser) {
191
- await this.browser.close();
192
- this.browser = null;
193
- this.context = null;
194
- this.page = null;
191
+ await this.cleanup();
192
+ }
193
+
194
+ /**
195
+ * 清理所有资源
196
+ */
197
+ async cleanup() {
198
+ if (this._isCleaningUp) return;
199
+ this._isCleaningUp = true;
200
+
201
+ try {
202
+ // 停止拦截器
203
+ if (this.networkInterceptor) {
204
+ await this.networkInterceptor.stop?.().catch(() => {});
205
+ this.networkInterceptor = null;
206
+ }
207
+ if (this.scriptInterceptor) {
208
+ await this.scriptInterceptor.stop?.().catch(() => {});
209
+ this.scriptInterceptor = null;
210
+ }
211
+
212
+ // 分离 CDP session
213
+ if (this.cdpSession) {
214
+ await this.cdpSession.detach().catch(() => {});
215
+ this.cdpSession = null;
216
+ }
217
+
218
+ // 关闭浏览器
219
+ if (this.browser) {
220
+ await this.browser.close();
221
+ this.browser = null;
222
+ this.context = null;
223
+ this.page = null;
224
+ this.pages = [];
225
+ }
226
+
227
+ this.emit('closed');
228
+ } catch (e) {
229
+ this.emit('error', e);
230
+ } finally {
231
+ this._isCleaningUp = false;
195
232
  }
196
233
  }
197
234
  }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * deepspider config 子命令
3
+ */
4
+
5
+ import {
6
+ loadConfig,
7
+ saveConfig,
8
+ getEffectiveConfig,
9
+ resetConfig,
10
+ CONFIG_FILE,
11
+ ENV_MAP,
12
+ DEFAULTS,
13
+ } from '../config.js';
14
+
15
+ export function run(args) {
16
+ const sub = args[0] || 'list';
17
+
18
+ switch (sub) {
19
+ case 'list':
20
+ return list();
21
+ case 'get':
22
+ return get(args[1]);
23
+ case 'set':
24
+ return set(args[1], args[2]);
25
+ case 'reset':
26
+ return reset();
27
+ case 'path':
28
+ return path();
29
+ default:
30
+ console.error(`未知子命令: ${sub}\n可用: list, get, set, reset, path`);
31
+ process.exit(1);
32
+ }
33
+ }
34
+
35
+ function list() {
36
+ const effective = getEffectiveConfig();
37
+ console.log('配置项:');
38
+ for (const [key, { value, source }] of Object.entries(effective)) {
39
+ const display = key === 'apiKey' && value ? maskKey(value) : value || '(未设置)';
40
+ console.log(` ${key} = ${display} [${source}]`);
41
+ }
42
+ }
43
+
44
+ function get(key) {
45
+ if (!key) {
46
+ console.error('用法: deepspider config get <key>');
47
+ process.exit(1);
48
+ }
49
+ if (!Object.hasOwn(DEFAULTS, key)) {
50
+ console.error(`未知配置项: ${key}\n可用: ${Object.keys(DEFAULTS).join(', ')}`);
51
+ process.exit(1);
52
+ }
53
+ const effective = getEffectiveConfig();
54
+ const { value, source } = effective[key];
55
+ const display = key === 'apiKey' && value ? maskKey(value) : value || '(未设置)';
56
+ console.log(`${key} = ${display} [${source}]`);
57
+ }
58
+
59
+ function set(key, value) {
60
+ if (!key || value === undefined) {
61
+ console.error('用法: deepspider config set <key> <value>');
62
+ process.exit(1);
63
+ }
64
+ if (!Object.hasOwn(DEFAULTS, key)) {
65
+ console.error(`未知配置项: ${key}\n可用: ${Object.keys(DEFAULTS).join(', ')}`);
66
+ process.exit(1);
67
+ }
68
+ const config = loadConfig();
69
+ config[key] = value;
70
+ saveConfig(config);
71
+
72
+ const envVar = ENV_MAP[key];
73
+ console.log(`已设置 ${key} = ${key === 'apiKey' ? maskKey(value) : value}`);
74
+ if (process.env[envVar]) {
75
+ console.log(`注意: 环境变量 ${envVar} 已设置,将优先使用环境变量的值`);
76
+ }
77
+ }
78
+
79
+ function reset() {
80
+ if (resetConfig()) {
81
+ console.log('配置已重置');
82
+ } else {
83
+ console.log('配置文件不存在,无需重置');
84
+ }
85
+ }
86
+
87
+ function path() {
88
+ console.log(CONFIG_FILE);
89
+ }
90
+
91
+ function maskKey(key) {
92
+ if (key.length <= 8) return '****';
93
+ return key.slice(0, 4) + '****' + key.slice(-4);
94
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * deepspider --help
3
+ */
4
+
5
+ import { getVersion } from '../../config/settings.js';
6
+
7
+ export function run() {
8
+ console.log(`
9
+ deepspider v${getVersion()} - 智能爬虫工程平台
10
+
11
+ 用法:
12
+ deepspider 启动交互式 Agent
13
+ deepspider <url> 打开目标网站并启动 Agent
14
+ deepspider config 管理配置
15
+ deepspider update 检查更新
16
+
17
+ 选项:
18
+ -v, --version 显示版本号
19
+ -h, --help 显示帮助信息
20
+ --debug 启用调试模式
21
+
22
+ 配置命令:
23
+ deepspider config list 列出所有配置
24
+ deepspider config get <key> 获取配置项
25
+ deepspider config set <key> <val> 设置配置项
26
+ deepspider config reset 重置配置
27
+ deepspider config path 显示配置文件路径
28
+
29
+ 示例:
30
+ deepspider https://example.com 分析目标网站
31
+ deepspider config set apiKey sk-xxx
32
+ deepspider config set model gpt-4o
33
+ `.trim());
34
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * deepspider update
3
+ */
4
+
5
+ import readline from 'readline';
6
+ import { existsSync } from 'fs';
7
+ import { execSync } from 'child_process';
8
+ import { join } from 'path';
9
+ import { getVersion } from '../../config/settings.js';
10
+
11
+ export async function run() {
12
+ const current = getVersion();
13
+
14
+ console.log(`当前版本: v${current}`);
15
+ console.log('检查更新...');
16
+
17
+ let latest;
18
+ try {
19
+ const resp = await fetch('https://registry.npmjs.org/deepspider/latest');
20
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
21
+ const data = await resp.json();
22
+ latest = data.version;
23
+ } catch (e) {
24
+ console.error(`检查更新失败: ${e.message}`);
25
+ process.exit(1);
26
+ }
27
+
28
+ if (current === latest) {
29
+ console.log(`已是最新版本 v${current}`);
30
+ return;
31
+ }
32
+
33
+ console.log(`发现新版本: v${latest}`);
34
+
35
+ const isGlobal = detectGlobalInstall();
36
+
37
+ if (isGlobal) {
38
+ const confirmed = await confirm(`是否更新到 v${latest}?(y/N) `);
39
+ if (!confirmed) {
40
+ console.log('已取消');
41
+ return;
42
+ }
43
+ console.log('正在更新...');
44
+ try {
45
+ execSync('npm install -g deepspider@latest', { stdio: 'inherit' });
46
+ console.log(`已更新到 v${latest}`);
47
+ } catch {
48
+ console.error('更新失败,请手动执行: npm install -g deepspider@latest');
49
+ process.exit(1);
50
+ }
51
+ } else {
52
+ console.log('当前为本地安装,请手动更新:');
53
+ console.log(' git pull && pnpm install');
54
+ }
55
+ }
56
+
57
+ function confirm(question) {
58
+ return new Promise((resolve) => {
59
+ const rl = readline.createInterface({
60
+ input: process.stdin,
61
+ output: process.stdout,
62
+ });
63
+ rl.question(question, (answer) => {
64
+ rl.close();
65
+ resolve(answer.toLowerCase() === 'y');
66
+ });
67
+ });
68
+ }
69
+
70
+ function detectGlobalInstall() {
71
+ try {
72
+ const globalDir = execSync('npm root -g', { encoding: 'utf-8' }).trim();
73
+ const globalPkg = join(globalDir, 'deepspider');
74
+ return existsSync(globalPkg);
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * deepspider --version
3
+ */
4
+
5
+ import { getVersion } from '../../config/settings.js';
6
+
7
+ export function run() {
8
+ console.log(`deepspider v${getVersion()}`);
9
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * CLI 配置模块 - 从 config/ 核心层 re-export
3
+ */
4
+
5
+ export {
6
+ CONFIG_FILE,
7
+ DEFAULTS,
8
+ ENV_MAP,
9
+ loadConfig,
10
+ saveConfig,
11
+ getEffectiveConfig,
12
+ getConfigValues,
13
+ resetConfig,
14
+ getVersion,
15
+ } from '../config/settings.js';
@@ -0,0 +1,102 @@
1
+ /**
2
+ * DeepSpider 配置管理(核心层)
3
+ * 配置文件:~/.deepspider/config/settings.json
4
+ * 优先级:环境变量 > 配置文件 > 默认值
5
+ *
6
+ * 此模块位于 config/ 层,供 cli/ 和 agent/ 共同依赖
7
+ */
8
+
9
+ import { join } from 'path';
10
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
11
+ import { PATHS, ensureDir } from './paths.js';
12
+
13
+ export const CONFIG_FILE = join(PATHS.CONFIG_DIR, 'settings.json');
14
+
15
+ export const DEFAULTS = {
16
+ apiKey: '',
17
+ baseUrl: 'https://api.openai.com/v1',
18
+ model: 'gpt-4o',
19
+ };
20
+
21
+ export const ENV_MAP = {
22
+ apiKey: 'DEEPSPIDER_API_KEY',
23
+ baseUrl: 'DEEPSPIDER_BASE_URL',
24
+ model: 'DEEPSPIDER_MODEL',
25
+ };
26
+
27
+ /**
28
+ * 从配置文件加载
29
+ */
30
+ export function loadConfig() {
31
+ if (!existsSync(CONFIG_FILE)) return {};
32
+ try {
33
+ return JSON.parse(readFileSync(CONFIG_FILE, 'utf-8'));
34
+ } catch {
35
+ return {};
36
+ }
37
+ }
38
+
39
+ /**
40
+ * 保存到配置文件
41
+ */
42
+ export function saveConfig(config) {
43
+ ensureDir(PATHS.CONFIG_DIR);
44
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n');
45
+ }
46
+
47
+ /**
48
+ * 获取有效配置(合并环境变量、配置文件、默认值)
49
+ * 返回 { key: { value, source } }
50
+ */
51
+ export function getEffectiveConfig() {
52
+ const fileConfig = loadConfig();
53
+ const result = {};
54
+
55
+ for (const key of Object.keys(DEFAULTS)) {
56
+ const envVar = ENV_MAP[key];
57
+ const envVal = process.env[envVar];
58
+
59
+ if (envVal !== undefined && envVal !== '') {
60
+ result[key] = { value: envVal, source: 'env' };
61
+ } else if (fileConfig[key] !== undefined && fileConfig[key] !== '') {
62
+ result[key] = { value: fileConfig[key], source: 'file' };
63
+ } else {
64
+ result[key] = { value: DEFAULTS[key], source: 'default' };
65
+ }
66
+ }
67
+
68
+ return result;
69
+ }
70
+
71
+ /**
72
+ * 获取配置值(纯值,用于运行时)
73
+ */
74
+ export function getConfigValues() {
75
+ const effective = getEffectiveConfig();
76
+ const values = {};
77
+ for (const [key, { value }] of Object.entries(effective)) {
78
+ values[key] = value;
79
+ }
80
+ return values;
81
+ }
82
+
83
+ /**
84
+ * 重置配置文件
85
+ */
86
+ export function resetConfig() {
87
+ if (existsSync(CONFIG_FILE)) {
88
+ unlinkSync(CONFIG_FILE);
89
+ return true;
90
+ }
91
+ return false;
92
+ }
93
+
94
+ /**
95
+ * 读取 package.json 版本号
96
+ * 基于 import.meta.url 定位,不依赖文件调用位置
97
+ */
98
+ export function getVersion() {
99
+ const __dirname = new URL('.', import.meta.url).pathname;
100
+ const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf-8'));
101
+ return pkg.version;
102
+ }
@@ -1,122 +0,0 @@
1
- ---
2
- name: check
3
- description: |
4
- Code quality check expert. Reviews code changes against specs and self-fixes issues.
5
- tools: Read, Write, Edit, Bash, Glob, Grep, mcp__exa__web_search_exa, mcp__exa__get_code_context_exa
6
- model: opus
7
- ---
8
- # Check Agent
9
-
10
- You are the Check Agent in the Trellis workflow.
11
-
12
- ## Context
13
-
14
- Before checking, read:
15
- - `.trellis/spec/` - Development guidelines
16
- - Pre-commit checklist for quality standards
17
-
18
- ## Core Responsibilities
19
-
20
- 1. **Get code changes** - Use git diff to get uncommitted code
21
- 2. **Check against specs** - Verify code follows guidelines
22
- 3. **Self-fix** - Fix issues yourself, not just report them
23
- 4. **Run verification** - typecheck and lint
24
-
25
- ## Important
26
-
27
- **Fix issues yourself**, don't just report them.
28
-
29
- You have write and edit tools, you can modify code directly.
30
-
31
- ---
32
-
33
- ## Workflow
34
-
35
- ### Step 1: Get Changes
36
-
37
- ```bash
38
- git diff --name-only # List changed files
39
- git diff # View specific changes
40
- ```
41
-
42
- ### Step 2: Check Against Specs
43
-
44
- Read relevant specs in `.trellis/spec/` to check code:
45
-
46
- - Does it follow directory structure conventions
47
- - Does it follow naming conventions
48
- - Does it follow code patterns
49
- - Are there missing types
50
- - Are there potential bugs
51
-
52
- ### Step 3: Self-Fix
53
-
54
- After finding issues:
55
-
56
- 1. Fix the issue directly (use edit tool)
57
- 2. Record what was fixed
58
- 3. Continue checking other issues
59
-
60
- ### Step 4: Run Verification
61
-
62
- Run project's lint and typecheck commands to verify changes.
63
-
64
- If failed, fix issues and re-run.
65
-
66
- ---
67
-
68
- ## Completion Markers (Ralph Loop)
69
-
70
- **CRITICAL**: You are in a loop controlled by the Ralph Loop system.
71
- The loop will NOT stop until you output ALL required completion markers.
72
-
73
- Completion markers are generated from `check.jsonl` in the task directory.
74
- Each entry's `reason` field becomes a marker: `{REASON}_FINISH`
75
-
76
- For example, if check.jsonl contains:
77
- ```json
78
- {"file": "...", "reason": "TypeCheck"}
79
- {"file": "...", "reason": "Lint"}
80
- {"file": "...", "reason": "CodeReview"}
81
- ```
82
-
83
- You MUST output these markers when each check passes:
84
- - `TYPECHECK_FINISH` - After typecheck passes
85
- - `LINT_FINISH` - After lint passes
86
- - `CODEREVIEW_FINISH` - After code review passes
87
-
88
- If check.jsonl doesn't exist or has no reasons, output: `ALL_CHECKS_FINISH`
89
-
90
- **The loop will block you from stopping until all markers are present in your output.**
91
-
92
- ---
93
-
94
- ## Report Format
95
-
96
- ```markdown
97
- ## Self-Check Complete
98
-
99
- ### Files Checked
100
-
101
- - src/components/Feature.tsx
102
- - src/hooks/useFeature.ts
103
-
104
- ### Issues Found and Fixed
105
-
106
- 1. `<file>:<line>` - <what was fixed>
107
- 2. `<file>:<line>` - <what was fixed>
108
-
109
- ### Issues Not Fixed
110
-
111
- (If there are issues that cannot be self-fixed, list them here with reasons)
112
-
113
- ### Verification Results
114
-
115
- - TypeCheck: Passed TYPECHECK_FINISH
116
- - Lint: Passed LINT_FINISH
117
-
118
- ### Summary
119
-
120
- Checked X files, found Y issues, all fixed.
121
- ALL_CHECKS_FINISH
122
- ```