aiseerr 1.0.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.
- package/AGENTS.md +14 -0
- package/CODE-REVIEW.md +333 -0
- package/PRD.md +397 -0
- package/README.md +80 -0
- package/ana-suggestions.md +105 -0
- package/dist/cli.js +37 -0
- package/package.json +37 -0
- package/src/cli.ts +118 -0
- package/src/commands/diff.ts +128 -0
- package/src/commands/env.ts +234 -0
- package/src/commands/init.ts +82 -0
- package/src/commands/read.ts +113 -0
- package/src/commands/scout.ts +93 -0
- package/src/commands/tree.ts +133 -0
- package/src/utils/output.ts +123 -0
- package/tests/cli.test.ts +172 -0
- package/tests/diff.test.ts +169 -0
- package/tests/env.test.ts +69 -0
- package/tests/init.test.ts +164 -0
- package/tests/output.test.ts +49 -0
- package/tests/read.test.ts +169 -0
- package/tests/scout.test.ts +248 -0
- package/tests/tree.test.ts +222 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +11 -0
package/PRD.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# aiseerr: X-Ray Vision for AI Coding Agents (PRD)
|
|
2
|
+
|
|
3
|
+
> **版本:** v1.0 Revised
|
|
4
|
+
> **日期:** 2026-03-06
|
|
5
|
+
> **一句话定位:** Give your AI coding agent x-ray vision into any codebase.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 产品愿景
|
|
10
|
+
|
|
11
|
+
当 AI Agent 接手一个陌生项目时,它面对的是一间漆黑的房间——不知道技术栈、不知道目录结构、不知道怎么跑测试。
|
|
12
|
+
**`aiseerr` 就是那束穿透黑暗的 X 光。** 一条命令,瞬间看穿项目的骨骼结构。
|
|
13
|
+
|
|
14
|
+
- 不是 AI 助手(不帮你写代码)
|
|
15
|
+
- 不是 DevTools(不面向人类)
|
|
16
|
+
- 是 **AI 助手的眼睛**——专为 LLM 设计的、结构化的、Token 友好的本地环境探测器
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. 核心设计原则
|
|
21
|
+
|
|
22
|
+
### 2.1 Machine-First 协议
|
|
23
|
+
|
|
24
|
+
| 规则 | 含义 |
|
|
25
|
+
|---|---|
|
|
26
|
+
| **Pure JSON stdout** | 成功时 stdout 只包含一个合法 JSON 对象,无任何其他字符 |
|
|
27
|
+
| **Structured stderr** | 失败时 stderr 输出 `{"error": "...", "code": "..."}` + 非零 Exit Code |
|
|
28
|
+
| **Zero interactivity** | 绝不等待用户输入。所有选项必须通过参数传入 |
|
|
29
|
+
| **No ANSI escapes** | 输出中不含颜色码、进度条、emoji |
|
|
30
|
+
|
|
31
|
+
### 2.2 全局 Token 预算 `--budget`
|
|
32
|
+
|
|
33
|
+
**核心差异化特性。** 所有产生大量输出的命令都接受 `--budget <number>` 参数(近似 Token 数)。超出时自动截断并附加 `"truncated": true` 和下一步操作提示。
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 3. 技术栈
|
|
38
|
+
|
|
39
|
+
| 选型 | 选择 | 理由 |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| Language | TypeScript | 强类型保证 JSON Schema 严谨 |
|
|
42
|
+
| CLI Parsing | 手写 `process.argv` | 零依赖,启动极速 |
|
|
43
|
+
| Build | `tsup` → 单文件 `dist/cli.js` | 最小安装体积 |
|
|
44
|
+
| Runtime | Node.js ≥ 18 | 原生 `node:fs`, `node:child_process` |
|
|
45
|
+
| **运行时依赖** | **零** | 打包后完全自包含 |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 4. 核心命令集
|
|
50
|
+
|
|
51
|
+
### 命令速查表
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
aiseerr scout [--budget=N] 一键全局侦察(env+tree+readme+rules+diff)
|
|
55
|
+
aiseerr env 项目画像
|
|
56
|
+
aiseerr tree [dir] [--depth=N] [--budget=N] 目录结构透视
|
|
57
|
+
aiseerr read <file...> [--lines=N-M] [--keys=a,b] [--budget=N] 精准文件读取
|
|
58
|
+
aiseerr diff [--full] [--file=path] Git 变更摘要
|
|
59
|
+
aiseerr init [--format=cursorrules|claude|agents] 注入/生成 AI rules 文件
|
|
60
|
+
aiseerr help 获取专为 AI 设计的 JSON 格式命令指南
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
通用参数:
|
|
64
|
+
- `--budget=<tokens>` 限制输出的近似 Token 数
|
|
65
|
+
- `--pretty` 仅供人类调试时使用
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### 4.1⭐ `aiseerr help` — AI 专用使用指南
|
|
70
|
+
|
|
71
|
+
**场景:** AI Agent 或大模型在调用工具发生错误,或者忘记具体参数时执行的自我修复制导。
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
aiseerr help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Output:** (不同于传统人类易读的 `--help` 文本流,`aiseerr help` 返回纯结构的 JSON)
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"_help": "aiseerr: Structured Context Gathering for AI Agents",
|
|
81
|
+
"commands": {
|
|
82
|
+
"scout": { "usage": "...", "desc": "..." },
|
|
83
|
+
"tree": { "usage": "...", "desc": "..." }
|
|
84
|
+
},
|
|
85
|
+
"global_flags": {
|
|
86
|
+
"--budget=N": "Truncates/summarizes output to fit within N approximate tokens."
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 4.2 ⭐ `aiseerr scout` — 一键全局侦察
|
|
94
|
+
|
|
95
|
+
**这是 README 里第一个展示的命令,也是使用频率最高的命令。**
|
|
96
|
+
|
|
97
|
+
**场景:** AI 刚 clone 了一个陌生项目(尤其是僵尸项目狩猎场景),需要一次性获取全部关键上下文。
|
|
98
|
+
|
|
99
|
+
**内置逻辑(用户不需要写条件,工具自动判断):**
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
1. 执行 env (总是)
|
|
103
|
+
2. 执行 tree --depth=2 (总是)
|
|
104
|
+
3. IF README.md 存在 THEN 读取前 60 行 (常见)
|
|
105
|
+
4. IF CLAUDE.md / .cursorrules / AGENTS.md 存在
|
|
106
|
+
THEN 读取其内容 (这些是 AI 约定文件,极重要)
|
|
107
|
+
5. IF 是 git 仓库 THEN 输出 diff stat (常见)
|
|
108
|
+
6. IF package.json 有 workspaces 字段
|
|
109
|
+
THEN 列出所有 workspace 包名和路径 (monorepo 场景)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Output:**
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"env": {
|
|
116
|
+
"packageManager": { "name": "pnpm", "version": "9.1.0" },
|
|
117
|
+
"node": { "version": "20.11.0", "engines": ">=18" },
|
|
118
|
+
"frameworks": ["next", "tailwindcss", "prisma"],
|
|
119
|
+
"scripts": { "dev": "next dev", "build": "next build", "test": "vitest" },
|
|
120
|
+
"testFramework": {
|
|
121
|
+
"name": "vitest",
|
|
122
|
+
"runCommand": "npx vitest run --reporter=json",
|
|
123
|
+
"parseHint": "Look for testResults[].assertionResults[].failureMessages"
|
|
124
|
+
},
|
|
125
|
+
"lintFramework": {
|
|
126
|
+
"name": "eslint",
|
|
127
|
+
"runCommand": "npx eslint . --format=json"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"tree": {
|
|
131
|
+
"src/": {
|
|
132
|
+
"components/": { "_files": ["Button.tsx", "Modal.tsx"], "_more": 9 },
|
|
133
|
+
"pages/": { "index.tsx": {}, "auth/": { "login.tsx": {} } },
|
|
134
|
+
"lib/": { "_files": ["db.ts", "auth.ts", "utils.ts"] }
|
|
135
|
+
},
|
|
136
|
+
"package.json": {},
|
|
137
|
+
"README.md": {}
|
|
138
|
+
},
|
|
139
|
+
"readme": "# My Project\nA Next.js application for...\n...(truncated at 60 lines)",
|
|
140
|
+
"agentRules": null,
|
|
141
|
+
"diff": {
|
|
142
|
+
"branch": "main",
|
|
143
|
+
"ahead": 0,
|
|
144
|
+
"behind": 2,
|
|
145
|
+
"staged": [],
|
|
146
|
+
"unstaged": [],
|
|
147
|
+
"untracked": []
|
|
148
|
+
},
|
|
149
|
+
"workspaces": null,
|
|
150
|
+
"_meta": {
|
|
151
|
+
"tokensEstimate": 1850,
|
|
152
|
+
"budget": 3000,
|
|
153
|
+
"truncated": false
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**价值:** 将 AI 接手陌生项目时的 5 轮工具调用压缩为 1 轮。在僵尸项目狩猎场景下,"每个项目 2 小时" → "每个项目 30 分钟"。
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### 4.2 `aiseerr env` — 项目画像
|
|
163
|
+
|
|
164
|
+
**场景:** 只需要技术栈信息,不需要目录结构。
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
aiseerr env
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"packageManager": { "name": "pnpm", "version": "9.1.0" },
|
|
173
|
+
"node": { "version": "20.11.0", "engines": ">=18" },
|
|
174
|
+
"workspaces": ["packages/*"],
|
|
175
|
+
"frameworks": ["next", "tailwindcss", "prisma"],
|
|
176
|
+
"scripts": {
|
|
177
|
+
"dev": "next dev",
|
|
178
|
+
"build": "next build",
|
|
179
|
+
"test": "vitest",
|
|
180
|
+
"lint": "eslint ."
|
|
181
|
+
},
|
|
182
|
+
"testFramework": {
|
|
183
|
+
"name": "vitest",
|
|
184
|
+
"runCommand": "npx vitest run --reporter=json",
|
|
185
|
+
"parseHint": "Look for testResults[].assertionResults[].failureMessages"
|
|
186
|
+
},
|
|
187
|
+
"lintFramework": {
|
|
188
|
+
"name": "eslint",
|
|
189
|
+
"runCommand": "npx eslint . --format=json",
|
|
190
|
+
"parseHint": "Each element has filePath and messages[]"
|
|
191
|
+
},
|
|
192
|
+
"git": {
|
|
193
|
+
"branch": "main",
|
|
194
|
+
"hasUncommitted": true,
|
|
195
|
+
"remoteUrl": "https://github.com/10iii/aiseerr.git"
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### 4.3 `aiseerr tree` — 结构透视
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
aiseerr tree # 整个项目,默认 depth=3
|
|
206
|
+
aiseerr tree src/ # 只看 src
|
|
207
|
+
aiseerr tree --depth=1 # 只看第一层
|
|
208
|
+
aiseerr tree --budget=300 # Token 预算限制
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"root": ".",
|
|
214
|
+
"depth": 3,
|
|
215
|
+
"structure": {
|
|
216
|
+
"src/": {
|
|
217
|
+
"components/": {
|
|
218
|
+
"_files": ["Button.tsx", "Modal.tsx", "Header.tsx"],
|
|
219
|
+
"_more": 9
|
|
220
|
+
},
|
|
221
|
+
"pages/": {
|
|
222
|
+
"index.tsx": {},
|
|
223
|
+
"auth/": { "login.tsx": {}, "register.tsx": {} }
|
|
224
|
+
},
|
|
225
|
+
"lib/": {
|
|
226
|
+
"_files": ["db.ts", "auth.ts", "utils.ts"]
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
"tests/": { "_more": 8 },
|
|
230
|
+
"package.json": {},
|
|
231
|
+
"tsconfig.json": {}
|
|
232
|
+
},
|
|
233
|
+
"stats": {
|
|
234
|
+
"totalFiles": 47,
|
|
235
|
+
"displayed": 16,
|
|
236
|
+
"ignored": ["node_modules", "dist", ".next", ".git"]
|
|
237
|
+
},
|
|
238
|
+
"truncated": false
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**设计要点:**
|
|
243
|
+
- `_files` 数组:目录下只有文件时用扁平列表,极度节省 Token
|
|
244
|
+
- `_more: 9`:告诉 AI "还有 9 个文件被省略了",可据此决定是否深入
|
|
245
|
+
- `ignored`:明确告诉 AI 哪些目录被跳过,消除困惑
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
### 4.4 `aiseerr read` — 精准文件读取
|
|
250
|
+
|
|
251
|
+
支持四种模式:
|
|
252
|
+
|
|
253
|
+
**A. 读取整个文件**
|
|
254
|
+
```bash
|
|
255
|
+
aiseerr read src/auth.ts
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**B. 读取特定行范围**(AI 通常从报错信息中知道行号)
|
|
259
|
+
```bash
|
|
260
|
+
aiseerr read src/auth.ts --lines=40-65
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**C. 从 JSON 提取特定键**
|
|
264
|
+
```bash
|
|
265
|
+
aiseerr read package.json --keys=scripts,dependencies
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**D. 批量读取多文件**(一次调用替代多次,减少 Agent 轮次)
|
|
269
|
+
```bash
|
|
270
|
+
aiseerr read src/auth.ts src/db.ts src/utils.ts --budget=2000
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Output 示例(批量模式):
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"files": [
|
|
277
|
+
{ "file": "src/auth.ts", "lines": 142, "content": "..." },
|
|
278
|
+
{ "file": "src/db.ts", "lines": 58, "content": "..." },
|
|
279
|
+
{ "file": "src/utils.ts", "lines": 31, "content": "...", "truncated": true }
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
### 4.5 `aiseerr diff` — 变更摘要
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
aiseerr diff # 默认:stat 模式
|
|
290
|
+
aiseerr diff --full # 含完整 diff 内容
|
|
291
|
+
aiseerr diff --file=src/auth.ts # 只看某个文件
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**stat 模式输出:**
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"branch": "feature/auth",
|
|
298
|
+
"ahead": 2,
|
|
299
|
+
"behind": 0,
|
|
300
|
+
"staged": [
|
|
301
|
+
{ "file": "src/db.ts", "status": "M", "insertions": 5, "deletions": 1 }
|
|
302
|
+
],
|
|
303
|
+
"unstaged": [
|
|
304
|
+
{ "file": "src/auth.ts", "status": "M", "insertions": 15, "deletions": 2 }
|
|
305
|
+
],
|
|
306
|
+
"untracked": ["src/components/LoginForm.tsx"]
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
**`--full` 输出额外附带 `hunks` 字段包含实际变更内容。**
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### 4.6 `aiseerr init` — 智能注入 AI Rules
|
|
315
|
+
|
|
316
|
+
**场景:** 让项目"AI-Ready"。在项目的 AI 规则文件中注入 aiseerr 的使用指南,使得后续任何 AI Agent 打开该项目时都会自动使用 aiseerr。
|
|
317
|
+
|
|
318
|
+
**核心行为(零参数智能默认):**
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
1. 按优先级搜索已有的 AI 规则文件:
|
|
322
|
+
AGENTS.md > CLAUDE.md > .cursorrules > .github/copilot-instructions.md
|
|
323
|
+
|
|
324
|
+
2. IF 找到了已有文件
|
|
325
|
+
THEN 将 aiseerr 使用指南注入到该文件末尾
|
|
326
|
+
(附加一个 <!-- aiseerr --> 标记,防止重复注入)
|
|
327
|
+
|
|
328
|
+
3. IF 没有找到任何已有文件
|
|
329
|
+
THEN 创建 AGENTS.md(最通用的格式)
|
|
330
|
+
|
|
331
|
+
4. 输出操作摘要到 stdout(JSON 格式)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**注入的内容模板(自动根据当前项目技术栈定制):**
|
|
335
|
+
|
|
336
|
+
```markdown
|
|
337
|
+
<!-- aiseerr:start -->
|
|
338
|
+
## Project Context Protocol (powered by aiseerr)
|
|
339
|
+
|
|
340
|
+
Before modifying this project, gather context by running:
|
|
341
|
+
npx aiseerr scout --budget=2000
|
|
342
|
+
|
|
343
|
+
When debugging a failing test:
|
|
344
|
+
npx aiseerr read <file> --lines=<error-range>
|
|
345
|
+
|
|
346
|
+
Before committing changes:
|
|
347
|
+
npx aiseerr diff --full
|
|
348
|
+
<!-- aiseerr:end -->
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**可选参数:**
|
|
352
|
+
```bash
|
|
353
|
+
aiseerr init # 智能默认:找到就注入,没有就创建 AGENTS.md
|
|
354
|
+
aiseerr init --format=claude # 强制创建/注入 CLAUDE.md
|
|
355
|
+
aiseerr init --format=cursorrules # 强制创建/注入 .cursorrules
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Output:**
|
|
359
|
+
```json
|
|
360
|
+
{
|
|
361
|
+
"action": "injected",
|
|
362
|
+
"target": "AGENTS.md",
|
|
363
|
+
"linesAdded": 12,
|
|
364
|
+
"marker": "aiseerr:start"
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**战略价值:** 这是 aiseerr 的**自我传播引擎**。用户运行 `aiseerr init` → 规则文件被提交到 Git → 其他开发者/AI 打开项目时读取规则 → 它们也开始用 `npx aiseerr` → 安装量自然增长。
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 5. 未来考量 (v2.0+)
|
|
373
|
+
|
|
374
|
+
| 特性 | 描述 |
|
|
375
|
+
|---|---|
|
|
376
|
+
| **MCP 兼容** | 将 aiseerr 的命令暴露为 MCP Resources/Tools,使 Claude Desktop 等可原生调用 |
|
|
377
|
+
| **AST 感知 read** | `aiseerr read src/auth.ts --symbol=login` 只返回某个函数体 |
|
|
378
|
+
| **`search`** | 类 grep 但输出结构化 JSON(文件名 + 行号 + 上下文) |
|
|
379
|
+
| **多语言** | 支持 Python (pip/poetry)、Go、Rust (cargo) 项目 |
|
|
380
|
+
| **`run-test`** | 真正执行测试并提取精炼的失败摘要 |
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## 6. v1.0 路线图
|
|
385
|
+
|
|
386
|
+
| 里程碑 | 内容 |
|
|
387
|
+
|---|---|
|
|
388
|
+
| **M1** | `env` + `tree` + `scout` (core engine) |
|
|
389
|
+
| **M2** | `read` + `diff` |
|
|
390
|
+
| **M3** | `init` (self-propagation engine) |
|
|
391
|
+
| **M4** | `--budget` Token 预算系统 |
|
|
392
|
+
| **M5** | tsup 构建 → `aiseerr@1.0.0` 发布 |
|
|
393
|
+
|
|
394
|
+
M5 后进入**自我验证阶段**:用 aiseerr 自己去猎杀僵尸项目,根据实战体验反向优化 JSON 结构。
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
*PRD v1.0 Revised — 2026-03-06*
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# aiseerr
|
|
2
|
+
|
|
3
|
+
**Give your AI coding agent x-ray vision into any codebase.**
|
|
4
|
+
|
|
5
|
+
`aiseerr` is a lightning-fast, zero-dependency, local environment context gathering tool purpose-built for AI Coding Agents and LLMs.
|
|
6
|
+
|
|
7
|
+
Unlike human-facing tools, `aiseerr` always outputs deterministic, structured JSON without ANSI colors or interactive prompts. More importantly, it features an intelligent `--budget` system to protect your limited LLM context window by summarizing or truncating outputs when they get too large.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The "One Command" Context Injection
|
|
12
|
+
|
|
13
|
+
When an AI drops into a new repository, it usually takes ~5 round-trips to figure out what's going on. Compress that into **1 command**:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx aiseerr scout --budget=3000
|
|
17
|
+
```
|
|
18
|
+
`scout` automatically runs `env` (to detect package managers, Node version, frameworks, test suites), `tree` (to map directory structure), reads `README.md` and `.cursorrules` / `AGENTS.md`, and grabs `git status`. It combines it all into a single JSON object.
|
|
19
|
+
|
|
20
|
+
*(Try it yourself: `npx aiseerr scout --pretty`)*
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Injection & Self-Propagation
|
|
25
|
+
|
|
26
|
+
Make any repository instantly understandable to future AI agents by running:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx aiseerr init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This will automatically find your `AGENTS.md` or `.cursorrules` and inject a standard system prompt snippet instructing AIs to use `aiseerr` before modifying the project.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Core Commands
|
|
37
|
+
|
|
38
|
+
For AI tools, use these raw JSON endpoints:
|
|
39
|
+
|
|
40
|
+
### 1. `env`
|
|
41
|
+
Provides a snapshot of the tech stack, inferred frameworks, test commands, and basic git state.
|
|
42
|
+
```bash
|
|
43
|
+
npx aiseerr env
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. `tree`
|
|
47
|
+
Maps directory structure with smart array-compaction to save tokens. Honors `.gitignore` by default.
|
|
48
|
+
```bash
|
|
49
|
+
npx aiseerr tree src/ --depth=3
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. `read`
|
|
53
|
+
Surgically exact file reading. Supports line ranges and JSON key extraction.
|
|
54
|
+
```bash
|
|
55
|
+
# Extract only dependencies
|
|
56
|
+
npx aiseerr read package.json --keys=dependencies,scripts
|
|
57
|
+
|
|
58
|
+
# Read only the failing lines
|
|
59
|
+
npx aiseerr read src/auth.ts --lines=45-80
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 4. `diff`
|
|
63
|
+
Extract line modifications and status. Combine with `--full` for unified patch hunks.
|
|
64
|
+
```bash
|
|
65
|
+
npx aiseerr diff
|
|
66
|
+
npx aiseerr diff --full
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 5. `help`
|
|
70
|
+
If an agent forgets how to use the tool, it can self-heal by calling `help` to receive a structured JSON API guide instead of a human-formatted text blob.
|
|
71
|
+
```bash
|
|
72
|
+
npx aiseerr help
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Zero-Dependency & Speed
|
|
78
|
+
This package ships as a single, fully-bundled executable (`cli.js`). It uses only standard Node.js APIs (`node:fs`, `node:child_process`). Execution is essentially instantaneous.
|
|
79
|
+
|
|
80
|
+
*Designed specifically for Aider, Cursor, Cline, and custom LLM workflows where every token matters.*
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Ana 功能建议 — ai-xray 增强方向
|
|
2
|
+
|
|
3
|
+
> 来源:Ana 与用户讨论(2026-03-06)
|
|
4
|
+
> 状态:待评估,后续归纳到 PRD
|
|
5
|
+
|
|
6
|
+
## 背景
|
|
7
|
+
ai-xray 当前定位:**AI Agent 的上下文采集器**(Structured Context Gathering for AI Agents)。
|
|
8
|
+
以下建议基于通用 AI Agent 工作场景中的常见痛点,均为**通用需求**(非专项项目需求)。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 建议 1: `hotspots` — 文件重要性排序
|
|
13
|
+
|
|
14
|
+
**痛点**:Agent 进入陌生 repo 后不知道哪些文件最关键,容易在边缘文件上浪费 token。
|
|
15
|
+
|
|
16
|
+
**功能**:
|
|
17
|
+
```
|
|
18
|
+
ai-xray hotspots [dir] [--top=N] [--by=imports|commits|both]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**输出**:按重要性排序的文件列表,指标包括:
|
|
22
|
+
- **import 频率**:被其他文件引用最多的文件(核心模块)
|
|
23
|
+
- **commit 频率**:最近 N 天改动最频繁的文件(活跃热点)
|
|
24
|
+
- **综合评分**:两者加权
|
|
25
|
+
|
|
26
|
+
**价值**:Agent 可以优先阅读最重要的文件,避免在不重要文件上浪费上下文。
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 建议 2: `coverage` — 测试覆盖快照
|
|
31
|
+
|
|
32
|
+
**痛点**:Agent 修 bug 或加功能时,不知道哪些模块有测试保护、哪些是裸奔的。
|
|
33
|
+
|
|
34
|
+
**功能**:
|
|
35
|
+
```
|
|
36
|
+
ai-xray coverage [--format=summary|detail]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**输出**:
|
|
40
|
+
- 哪些源文件有对应的测试文件
|
|
41
|
+
- 哪些模块完全没有测试(风险区域)
|
|
42
|
+
- 测试文件与源文件的映射关系
|
|
43
|
+
|
|
44
|
+
**注意**:不是跑真实 coverage(太重),而是通过文件名匹配 + 目录结构推断(轻量级)。
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 建议 3: `pulse` — 项目活跃度脉搏
|
|
49
|
+
|
|
50
|
+
**痛点**:Agent(特别是做开源贡献的)需要判断项目是否还活跃、maintainer 是否还在。
|
|
51
|
+
|
|
52
|
+
**功能**:
|
|
53
|
+
```
|
|
54
|
+
ai-xray pulse [--days=90]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**输出**:
|
|
58
|
+
- 最近 N 天的 commit 趋势(每周/每月频率)
|
|
59
|
+
- 最近一次 commit 距今天数
|
|
60
|
+
- 活跃贡献者数量
|
|
61
|
+
- issue/PR 响应速度(如果能从 git log 推断)
|
|
62
|
+
- 一句话判断:`Active` / `Slow` / `Dormant` / `Abandoned`
|
|
63
|
+
|
|
64
|
+
**价值**:帮 Agent 快速判断是否值得在这个项目上投入时间。对大荒的谛听角色特别有用。
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 建议 4: `deps` — 依赖关系图
|
|
69
|
+
|
|
70
|
+
**痛点**:Agent 要修改某个文件时,不知道会影响多少其他文件。
|
|
71
|
+
|
|
72
|
+
**功能**:
|
|
73
|
+
```
|
|
74
|
+
ai-xray deps <file> [--depth=N] [--direction=in|out|both]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**输出**:
|
|
78
|
+
- **in**:谁引用了这个文件(影响范围)
|
|
79
|
+
- **out**:这个文件引用了谁(依赖链)
|
|
80
|
+
- 循环依赖检测
|
|
81
|
+
|
|
82
|
+
**价值**:Agent 修改前先查影响范围,避免引入 breaking changes。
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 建议 5: `entrypoints` — 入口点发现
|
|
87
|
+
|
|
88
|
+
**痛点**:Agent 需要快速定位项目的入口点(main、CLI entry、route handlers、exports)。
|
|
89
|
+
|
|
90
|
+
**功能**:
|
|
91
|
+
```
|
|
92
|
+
ai-xray entrypoints [--type=cli|web|lib|all]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**输出**:
|
|
96
|
+
- package.json 的 main/bin/exports
|
|
97
|
+
- 框架入口(Next.js pages、Express routes 等)
|
|
98
|
+
- CLI 命令注册点
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 后续
|
|
103
|
+
- [ ] 与用户评估优先级
|
|
104
|
+
- [ ] 选择 1-2 个最有价值的先实现
|
|
105
|
+
- [ ] 归纳到正式 PRD
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";function I(t){return Math.max(1,Math.ceil(t.length/4))}function X(t,e){if(!e||e<=0)return t;let i=JSON.stringify(t),s=I(i);if(s>e){let c=e*4;if(typeof t=="object"&&t!==null&&!Array.isArray(t)){let n={...t},g=Object.keys(n).filter(l=>l!=="_meta").map(l=>({key:l,size:JSON.stringify(n[l]).length})).sort((l,p)=>p.size-l.size);for(let{key:l}of g){let p=JSON.stringify(n).length;if(p<=c)break;if(Array.isArray(n[l])&&n[l].length>0){let d=[...n[l]];for(;d.length>0&&JSON.stringify({...n,[l]:d}).length>c;)d.pop();n[l]=d.length>0?d:`[TRUNCATED: ${n[l].length} items removed]`}else if(typeof n[l]=="string"&&n[l].length>200){let d=Math.max(100,n[l].length-(p-c));n[l]=n[l].slice(0,d)+"...[TRUNCATED]"}}let a=JSON.stringify(n);return a.length>c?{_truncated_raw:a.slice(0,c),_meta:{tokensEstimate:s,budget:e,truncated:!0,hint:`Output exceeded budget of ${e} tokens. Data was hard-truncated.`}}:{...n,_meta:{...n._meta||{},tokensEstimate:I(JSON.stringify(n)),budget:e,truncated:!0,hint:`Output exceeded budget of ${e} tokens. Some fields were pruned.`}}}return{_truncated_raw:i.slice(0,c),_meta:{tokensEstimate:s,budget:e,truncated:!0,hint:`Output exceeded budget of ${e} tokens. Data was hard-truncated.`}}}return typeof t=="object"&&t!==null&&!Array.isArray(t)?{...t,_meta:{...t._meta||{},tokensEstimate:s,budget:e,truncated:!1}}:t}function M(t,e){let i=X(t,e),s=JSON.stringify(i,null,process.argv.includes("--pretty")?2:void 0)+`
|
|
3
|
+
`;process.stdout.write(s,()=>process.exit(0))}function C(t,e=1){let s={error:t instanceof Error?t.message:String(t),code:e};process.stderr.write(JSON.stringify(s,null,process.argv.includes("--pretty")?2:void 0)+`
|
|
4
|
+
`),process.exit(e)}var b=require("fs"),D=require("path"),V=require("child_process"),q=1e4;function A(t,e){try{return(0,V.execFileSync)(t,e,{stdio:"pipe",encoding:"utf-8",timeout:q}).trim()}catch{return null}}function W(){let t=process.platform,e=process.env.SHELL||process.env.PSModulePath&&"powershell"||process.env.ComSpec||"",i=e.toLowerCase();return process.env.PSModulePath||i.includes("pwsh")||i.includes("powershell")?{name:i.includes("pwsh")?"pwsh":"powershell",path:e==="powershell"?null:e,chain:";",setEnv:'$env:VAR="value"',pathSep:"\\",pitfalls:["`&&` does NOT work in PowerShell 5.x. Use `;` or `pwsh 7+`.","Use `$env:VAR` instead of `export VAR=` or `set VAR=`.","Backtick ` is the escape char, not backslash.","Single quotes are literal strings; double quotes allow interpolation."]}:i.includes("fish")?{name:"fish",path:e,chain:"; and",setEnv:"set -x VAR value",pathSep:"/",pitfalls:["`&&` and `||` are NOT supported. Use `; and` / `; or`.","Use `set -x VAR value` instead of `export VAR=value`.","Subshells `$(...)` are not supported. Use `(command)` instead."]}:i.includes("zsh")?{name:"zsh",path:e,chain:"&&",setEnv:'export VAR="value"',pathSep:"/",pitfalls:["Array indexing starts at 1, not 0 (unlike bash)."]}:i.includes("bash")||i.includes("sh")?{name:i.includes("bash")?"bash":"sh",path:e,chain:"&&",setEnv:'export VAR="value"',pathSep:"/",pitfalls:[]}:t==="win32"&&(i.includes("cmd")||!process.env.SHELL)?{name:"cmd",path:e||"C:\\Windows\\System32\\cmd.exe",chain:"&&",setEnv:"set VAR=value",pathSep:"\\",pitfalls:["Use `set VAR=value` instead of `export`.","No native support for `$(...)` subshells.","Use `%VAR%` to expand env vars, not `$VAR`."]}:{name:"unknown",path:e||null,chain:"&&",setEnv:'export VAR="value"',pathSep:t==="win32"?"\\":"/",pitfalls:["Shell could not be detected. Assume POSIX-compatible."]}}function O(){let t=process.cwd(),e=(0,D.join)(t,"package.json");if(!(0,b.existsSync)(e))return{os:{platform:process.platform,arch:process.arch},shell:W(),packageManager:null,node:{version:process.version,engines:void 0},frameworks:[],scripts:{},_note:"Not a Node.js project: package.json not found."};let i;try{i=JSON.parse((0,b.readFileSync)(e,"utf-8"))}catch{throw new Error("Failed to parse package.json.")}let s={name:"npm",version:"unknown"};if((0,b.existsSync)((0,D.join)(t,"pnpm-lock.yaml"))){s.name="pnpm";let f=A("pnpm",["--version"]);f&&(s.version=f)}else if((0,b.existsSync)((0,D.join)(t,"yarn.lock"))){s.name="yarn";let f=A("yarn",["--version"]);f&&(s.version=f)}else if((0,b.existsSync)((0,D.join)(t,"bun.lockb"))){s.name="bun";let f=A("bun",["--version"]);f&&(s.version=f)}else{let f=A("npm",["--version"]);f&&(s.version=f)}let c={version:process.version,engines:i.engines?.node},o={...i.dependencies||{},...i.devDependencies||{}},n=Object.keys(o),g=["react","vue","next","nuxt","svelte","express","nestjs","tailwindcss","prisma","vite","webpack"].filter(f=>n.includes(f)),a=null;n.includes("vitest")?a={name:"vitest",runCommand:"npx vitest run --reporter=json",parseHint:"Look for 'testResults[].assertionResults[].failureMessages'"}:n.includes("jest")&&(a={name:"jest",runCommand:"npx jest --json",parseHint:"Look for 'testResults[].assertionResults[].failureMessages'"});let l=null;n.includes("eslint")&&(l={name:"eslint",runCommand:"npx eslint . --format=json",parseHint:"Array of objects with 'filePath' and 'messages[]'"});let p=null;if((0,b.existsSync)((0,D.join)(t,".git"))){let f=A("git",["rev-parse","--abbrev-ref","HEAD"]),k=A("git",["status","--porcelain"]),m=A("git",["config","--get","remote.origin.url"]);p={branch:f||"unknown",hasUncommitted:!!k,remoteUrl:m||null}}let d={os:{platform:process.platform,arch:process.arch},shell:W(),packageManager:s,node:c,frameworks:g,scripts:i.scripts||{}};return i.workspaces&&(d.workspaces=i.workspaces),a&&(d.testFramework=a),l&&(d.lintFramework=l),p&&(d.git=p),d}var v=require("fs"),N=require("path");function Y(t){let e=(0,N.join)(t,".gitignore"),i=["node_modules",".git","dist",".next","build","out","coverage"],s=[];(0,v.existsSync)(e)&&(s=(0,v.readFileSync)(e,"utf-8").split(`
|
|
5
|
+
`).map(n=>n.trim()).filter(n=>n&&!n.startsWith("#")).map(n=>n.replace(/\/$/,"")));let c=new Set([...i,...s]);return o=>{let n=(0,N.basename)(o);if(c.has(n))return!0;for(let r of c)if(r.startsWith("*")&&n.endsWith(r.slice(1)))return!0;return!1}}function F(t,e){let i=".",s=3;for(let a of t)a.startsWith("--depth=")?s=parseInt(a.split("=")[1],10)||3:a.startsWith("--")||(i=a);let c=(0,N.join)(process.cwd(),i);if(!(0,v.existsSync)(c))throw new Error(`Directory not found: ${i}`);let o=Y(process.cwd()),n={totalFiles:0,displayed:0,ignoredDirs:new Set};function r(a,l){if(l>s)return{_more:!0};let p=[];try{p=(0,v.readdirSync)(a)}catch{return{}}let d={},f=[],k=0;for(let m of p){let u=(0,N.join)(a,m);if(o(u)||m.startsWith(".DS_Store")){(0,v.statSync)(u,{throwIfNoEntry:!1})?.isDirectory()&&n.ignoredDirs.add(m);continue}let h=(0,v.statSync)(u,{throwIfNoEntry:!1});if(h)if(h.isDirectory()){let y=r(u,l+1);y&&Object.keys(y).length>0?d[m+"/"]=y:y&&y._more&&(d[m+"/"]={_more:!0})}else n.totalFiles++,n.displayed>200?k++:(f.push(m),n.displayed++)}if(f.length>0)if(Object.keys(d).length===0){let m={_files:f};return k>0&&(m._more=k),m}else{for(let m of f)d[m]={};k>0&&(d._moreReplacedFiles=k)}return d}let g=r(c,1);return{root:i,depth:s,structure:g,stats:{totalFiles:n.totalFiles,displayed:n.displayed,ignored:Array.from(n.ignoredDirs)}}}var R=require("fs");function $(t,e){let i=null,s=null,c=[];for(let r of t)if(r.startsWith("--lines=")){let g=r.split("=")[1].split("-");g.length===2&&(i=[parseInt(g[0],10),parseInt(g[1],10)])}else r.startsWith("--keys=")?s=r.split("=")[1].split(",").map(g=>g.trim()):r.startsWith("--")||c.push(r);if(c.length===0)throw new Error("No files provided to read.");let o=[],n=0;for(let r of c){if(!(0,R.existsSync)(r)){o.push({file:r,error:"File not found"});continue}if((0,R.statSync)(r).isDirectory()){o.push({file:r,error:"Target is a directory, not a file"});continue}try{let a=(0,R.readFileSync)(r,"utf-8"),l=a.split(`
|
|
6
|
+
`).length,p=a,d=!1,f;if(s&&r.endsWith(".json"))try{let m=JSON.parse(a),u={};for(let h of s)h in m&&(u[h]=m[h]);p=u}catch{}else if(i&&typeof p=="string"){let[m,u]=i,h=a.split(`
|
|
7
|
+
`);p=h.slice(Math.max(0,m-1),u).join(`
|
|
8
|
+
`),f={range:[m,Math.min(u,h.length)]}}if(e&&typeof p=="string"){let m=I(p);if(n+m>e){let h=Math.max(0,e-n)*4;if(h<50){o.push({file:r,lines:l,error:"Skipped due to token budget limits"});break}else p=p.slice(0,h)+`
|
|
9
|
+
...[TRUNCATED BY BUDGET]`,d=!0}n+=I(p)}let k={file:r,lines:l,content:p};f&&(k.context=f),d&&(k.truncated=!0),o.push(k)}catch(a){o.push({file:r,error:a.message||"Failed to read file"})}}return c.length===1&&o.length===1?o[0]:{files:o}}var J=require("child_process"),z=require("fs"),H=require("path"),K=1e4;function j(t,e){try{return(0,J.execFileSync)(t,e,{stdio:"pipe",encoding:"utf-8",timeout:K}).trim()}catch{return null}}function _(t,e){let i=!1,s=null;for(let u of t)u==="--full"?i=!0:u.startsWith("--file=")&&(s=u.split("=")[1]);let c=process.cwd();if(!(0,z.existsSync)((0,H.join)(c,".git")))throw new Error("Not a git repository");let o=j("git",["rev-parse","--abbrev-ref","HEAD"])||"unknown",n=0,r=0,g=j("git",["rev-list","--left-right","--count","HEAD...@{u}"]);if(g){let u=g.split(" ");u.length===2&&(n=parseInt(u[0],10)||0,r=parseInt(u[1],10)||0)}let a=j("git",["status","--porcelain"]),l=[],p=[],d=[];if(a){let u=a.split(`
|
|
10
|
+
`);for(let h of u){if(!h)continue;let y=h[0],w=h[1],S=h.slice(3);s&&S!==s||(y==="?"&&w==="?"?d.push(S):(y!==" "&&y!=="?"&&l.push({file:S,status:y}),w!==" "&&w!=="?"&&p.push({file:S,status:w})))}}let f=j("git",["diff","--cached","--numstat"]);if(f){let u=f.split(`
|
|
11
|
+
`);for(let h of u){if(!h)continue;let[y,w,S]=h.split(" "),E=l.find(U=>U.file===S);E&&(E.insertions=parseInt(y,10)||0,E.deletions=parseInt(w,10)||0)}}let k=j("git",["diff","--numstat"]);if(k){let u=k.split(`
|
|
12
|
+
`);for(let h of u){if(!h)continue;let[y,w,S]=h.split(" "),E=p.find(U=>U.file===S);E&&(E.insertions=parseInt(y,10)||0,E.deletions=parseInt(w,10)||0)}}let m={branch:o,ahead:n,behind:r,staged:l,unstaged:p,untracked:d};if(i){let u=s?["diff",s]:["diff"],h=s?["diff","--cached",s]:["diff","--cached"],y=j("git",u),w=j("git",h);w&&(m.stagedDiff=w),y&&(m.unstagedDiff=y)}return m}var T=require("fs"),P=require("path");function B(t){let e={},i=process.cwd();try{e.env=O()}catch(r){e.env={error:r.message}}try{let r=t?Math.floor(t*.5):void 0;e.tree=F(["--depth=2"],r)}catch(r){e.tree={error:r.message}}let s=["README.md","Readme.md","readme.md"],c=!1;for(let r of s){let g=(0,P.join)(i,r);if((0,T.existsSync)(g))try{let a=(0,T.readFileSync)(g,"utf-8"),l=a.split(`
|
|
13
|
+
`);l.length>60?e.readme=l.slice(0,60).join(`
|
|
14
|
+
`)+`
|
|
15
|
+
...[TRUNCATED TO 60 LINES]`:e.readme=a,c=!0;break}catch{}}c||(e.readme=null);let o=[".cursorrules","CLAUDE.md","AGENTS.md",".github/copilot-instructions.md"],n=!1;for(let r of o){let g=(0,P.join)(i,r);if((0,T.existsSync)(g))try{e.agentRules={source:r,content:(0,T.readFileSync)(g,"utf-8").split(`
|
|
16
|
+
`).slice(0,500).join(`
|
|
17
|
+
`)},n=!0;break}catch{}}n||(e.agentRules=null);try{let r=_([],void 0);delete r.stagedDiff,delete r.unstagedDiff,e.diff=r}catch{e.diff=null}return e.env&&e.env.workspaces?e.workspaces=e.env.workspaces:e.workspaces=null,e}var x=require("fs"),L=require("path");function G(t){let e=null;for(let a of t)a.startsWith("--format=")&&(e=a.split("=")[1]);let i=process.cwd(),s=`
|
|
18
|
+
<!-- aiseerr:start -->
|
|
19
|
+
## Project Context Protocol (powered by aiseerr)
|
|
20
|
+
|
|
21
|
+
Before modifying this project, gather context by running:
|
|
22
|
+
npx aiseerr scout --budget=2000
|
|
23
|
+
|
|
24
|
+
When debugging a failing test:
|
|
25
|
+
npx aiseerr read <file> --lines=<error-range>
|
|
26
|
+
|
|
27
|
+
Before committing changes:
|
|
28
|
+
npx aiseerr diff --full
|
|
29
|
+
<!-- aiseerr:end -->
|
|
30
|
+
`,c=["AGENTS.md","CLAUDE.md",".cursorrules",".github/copilot-instructions.md"],o="AGENTS.md";if(e)if(e==="claude")o="CLAUDE.md";else if(e==="cursorrules")o=".cursorrules";else if(e==="agents")o="AGENTS.md";else throw new Error(`Unknown format: ${e}`);else for(let a of c)if((0,x.existsSync)((0,L.join)(i,a))){o=a;break}let n=(0,L.join)(i,o),r="created",g=s.trim().split(`
|
|
31
|
+
`).length;if((0,x.existsSync)(n)){let a=(0,x.readFileSync)(n,"utf-8");if(a.includes("<!-- aiseerr:start -->"))return{action:"skipped",target:o,reason:"Already injected"};(0,x.writeFileSync)(n,a+`
|
|
32
|
+
`+s.trim()+`
|
|
33
|
+
`,"utf-8"),r="injected"}else{let a=`# AI Agent Rules
|
|
34
|
+
|
|
35
|
+
${s.trim()}
|
|
36
|
+
`;(0,x.writeFileSync)(n,a,"utf-8"),g=a.trim().split(`
|
|
37
|
+
`).length}return{action:r,target:o,linesAdded:g,marker:"aiseerr:start"}}process.on("uncaughtException",t=>{C(t,1)});process.on("unhandledRejection",t=>{C(t,1)});async function Q(){let t=process.argv.slice(2);if(t.length===0||t[0]==="help"||t.includes("--help")||t.includes("-h")){M({_help:"aiseerr: Structured Context Gathering for AI Agents",commands:{scout:{usage:"aiseerr scout [--budget=N]",desc:"One-shot comprehensive environment scan. Bundles env, tree, readmes, diffs."},env:{usage:"aiseerr env",desc:"Detect package manager, node version, frameworks, test setups."},tree:{usage:"aiseerr tree [dir] [--depth=N] [--budget=N]",desc:"Explore directory structure with smart json compaction."},read:{usage:"aiseerr read <file...> [--lines=N-M] [--keys=a,b] [--budget=N]",desc:"Extract exact files, specific line ranges, or json keys."},diff:{usage:"aiseerr diff [--full] [--file=path]",desc:"Get git status, changed lines count, or full patch hunks."},init:{usage:"aiseerr init [--format=cursorrules|claude|agents]",desc:"Auto-inject aiseerr usage instructions into project agent rules."}},global_flags:{"--budget=N":"Truncates/summarizes output to fit within N approximate tokens.","--pretty":"Human readable formatting (not recommended for AI LLMs natively)."}});return}let e=t[0],i=t.slice(1),s,c=[];for(let o of i)o.startsWith("--budget=")?(s=parseInt(o.split("=")[1],10),isNaN(s)&&C("Invalid --budget value. Must be a number.",1)):o==="--pretty"||c.push(o);try{let o;switch(e){case"scout":o=B(s);break;case"env":o=O();break;case"tree":o=F(c,s);break;case"read":o=$(c,s);break;case"diff":o=_(c,s);break;case"init":o=G(c);break;default:C(`Unknown command: ${e}`);return}M(o,s)}catch(o){C(o)}}Q();
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aiseerr",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Give your AI coding agent x-ray vision into any codebase.",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"aiseerr": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsup --watch",
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"test": "vitest"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ai",
|
|
16
|
+
"llm",
|
|
17
|
+
"agent",
|
|
18
|
+
"context",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"author": "10iii <zh@ngxil.in> (https://zha.ngxil.in)",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/10iii/aiseerr.git"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/10iii/aiseerr#readme",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/10iii/aiseerr/issues"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.0.0",
|
|
35
|
+
"vitest": "^1.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|