@ia-ccun/code-agent-cli 0.0.3 → 0.0.4-8.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/README.md +92 -11
  2. package/bin/cli.js +212 -59
  3. package/config/agent/APPEND_SYSTEM.md +46 -5
  4. package/config/agent/bin/fd +0 -0
  5. package/config/agent/bin/rg +0 -0
  6. package/config/agent/dangerous-patterns.json +106 -0
  7. package/config/agent/extensions/aicode-tips.ts +191 -0
  8. package/config/agent/extensions/bash-check.ts +227 -0
  9. package/config/agent/extensions/context-monitor.ts +275 -0
  10. package/config/agent/extensions/context.ts +1 -0
  11. package/config/agent/extensions/custom-footer.ts +33 -2
  12. package/config/agent/extensions/custom.ts +24 -91
  13. package/config/agent/extensions/error-monitor.ts +142 -0
  14. package/config/agent/extensions/msg-debug.ts +185 -0
  15. package/config/agent/extensions/review.ts +4 -0
  16. package/config/agent/extensions/working-msg.ts +39 -20
  17. package/config/agent/mcp.json +4 -0
  18. package/config/agent/models.json +16 -43
  19. package/config/agent/prompts/code-simplifier.md +51 -0
  20. package/config/agent/prompts/feat.md +2 -2
  21. package/config/agent/prompts/git-commit.md +9 -15
  22. package/config/agent/prompts/init-project.md +2 -1
  23. package/config/agent/prompts/workflow.md +3 -2
  24. package/config/agent/settings.json +17 -5
  25. package/config/agent/skills/brainstorming/SKILL.md +167 -0
  26. package/config/agent/skills/brainstorming/scripts/frame-template.html +214 -0
  27. package/config/agent/skills/brainstorming/scripts/helper.js +88 -0
  28. package/config/agent/skills/brainstorming/scripts/server.cjs +338 -0
  29. package/config/agent/skills/brainstorming/scripts/start-server.sh +153 -0
  30. package/config/agent/skills/brainstorming/scripts/stop-server.sh +55 -0
  31. package/config/agent/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  32. package/config/agent/skills/brainstorming/visual-companion.md +286 -0
  33. package/config/agent/skills/code-review/SKILL.md +4 -2
  34. package/config/agent/skills/commit/SKILL.md +10 -5
  35. package/config/agent/skills/csv-data-summarizer/SKILL.md +2 -1
  36. package/config/agent/skills/design-md/README.md +34 -0
  37. package/config/agent/skills/design-md/SKILL.md +172 -0
  38. package/config/agent/skills/design-md/examples/DESIGN.md +154 -0
  39. package/config/agent/skills/dispatching-parallel-agents/SKILL.md +185 -0
  40. package/config/agent/skills/executing-plans/SKILL.md +73 -0
  41. package/config/agent/skills/find-skills/SKILL.md +5 -1
  42. package/config/agent/skills/finishing-a-development-branch/SKILL.md +204 -0
  43. package/config/agent/skills/fireworks-tech-graph/LICENSE +21 -0
  44. package/config/agent/skills/fireworks-tech-graph/README.md +528 -0
  45. package/config/agent/skills/fireworks-tech-graph/README.zh.md +499 -0
  46. package/config/agent/skills/fireworks-tech-graph/SKILL.md +537 -0
  47. package/config/agent/skills/fireworks-tech-graph/agentloop-core.svg +101 -0
  48. package/config/agent/skills/fireworks-tech-graph/agents/openai.yaml +4 -0
  49. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style1-flat.png +0 -0
  50. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style2-dark.png +0 -0
  51. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
  52. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style4-notion.png +0 -0
  53. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style5-glass.png +0 -0
  54. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style6-claude.png +0 -0
  55. package/config/agent/skills/fireworks-tech-graph/assets/samples/sample-style7-openai.png +0 -0
  56. package/config/agent/skills/fireworks-tech-graph/fixtures/agent-memory-types-style4.json +181 -0
  57. package/config/agent/skills/fireworks-tech-graph/fixtures/api-flow-style7.json +40 -0
  58. package/config/agent/skills/fireworks-tech-graph/fixtures/mem0-style1.json +297 -0
  59. package/config/agent/skills/fireworks-tech-graph/fixtures/microservices-style3.json +64 -0
  60. package/config/agent/skills/fireworks-tech-graph/fixtures/multi-agent-style5.json +45 -0
  61. package/config/agent/skills/fireworks-tech-graph/fixtures/system-architecture-style6.json +48 -0
  62. package/config/agent/skills/fireworks-tech-graph/fixtures/tool-call-style2.json +182 -0
  63. package/config/agent/skills/fireworks-tech-graph/package.json +42 -0
  64. package/config/agent/skills/fireworks-tech-graph/references/icons.md +406 -0
  65. package/config/agent/skills/fireworks-tech-graph/references/style-1-flat-icon.md +108 -0
  66. package/config/agent/skills/fireworks-tech-graph/references/style-2-dark-terminal.md +107 -0
  67. package/config/agent/skills/fireworks-tech-graph/references/style-3-blueprint.md +113 -0
  68. package/config/agent/skills/fireworks-tech-graph/references/style-4-notion-clean.md +94 -0
  69. package/config/agent/skills/fireworks-tech-graph/references/style-5-glassmorphism.md +125 -0
  70. package/config/agent/skills/fireworks-tech-graph/references/style-6-claude-official.md +209 -0
  71. package/config/agent/skills/fireworks-tech-graph/references/style-7-openai.md +215 -0
  72. package/config/agent/skills/fireworks-tech-graph/references/style-diagram-matrix.md +135 -0
  73. package/config/agent/skills/fireworks-tech-graph/references/svg-layout-best-practices.md +100 -0
  74. package/config/agent/skills/fireworks-tech-graph/scripts/README.md +244 -0
  75. package/config/agent/skills/fireworks-tech-graph/scripts/generate-diagram.sh +157 -0
  76. package/config/agent/skills/fireworks-tech-graph/scripts/generate-from-template.py +1556 -0
  77. package/config/agent/skills/fireworks-tech-graph/scripts/test-all-styles.sh +135 -0
  78. package/config/agent/skills/fireworks-tech-graph/scripts/validate-svg.sh +292 -0
  79. package/config/agent/skills/fireworks-tech-graph/templates/agent-architecture.svg +28 -0
  80. package/config/agent/skills/fireworks-tech-graph/templates/architecture.svg +23 -0
  81. package/config/agent/skills/fireworks-tech-graph/templates/comparison-matrix.svg +14 -0
  82. package/config/agent/skills/fireworks-tech-graph/templates/data-flow.svg +28 -0
  83. package/config/agent/skills/fireworks-tech-graph/templates/er-diagram.svg +21 -0
  84. package/config/agent/skills/fireworks-tech-graph/templates/flowchart.svg +21 -0
  85. package/config/agent/skills/fireworks-tech-graph/templates/sequence.svg +20 -0
  86. package/config/agent/skills/fireworks-tech-graph/templates/state-machine.svg +20 -0
  87. package/config/agent/skills/fireworks-tech-graph/templates/timeline.svg +19 -0
  88. package/config/agent/skills/fireworks-tech-graph/templates/use-case.svg +21 -0
  89. package/config/agent/skills/frontend-design/SKILL.md +4 -1
  90. package/config/agent/skills/hello/SKILL.md +5 -2
  91. package/config/agent/skills/impeccable/adapt/SKILL.md +204 -0
  92. package/config/agent/skills/impeccable/animate/SKILL.md +177 -0
  93. package/config/agent/skills/impeccable/arrange/SKILL.md +127 -0
  94. package/config/agent/skills/impeccable/audit/SKILL.md +128 -0
  95. package/config/agent/skills/impeccable/bolder/SKILL.md +119 -0
  96. package/config/agent/skills/impeccable/clarify/SKILL.md +185 -0
  97. package/config/agent/skills/impeccable/colorize/SKILL.md +145 -0
  98. package/config/agent/skills/impeccable/critique/SKILL.md +122 -0
  99. package/config/agent/skills/impeccable/delight/SKILL.md +306 -0
  100. package/config/agent/skills/impeccable/distill/SKILL.md +124 -0
  101. package/config/agent/skills/impeccable/extract/SKILL.md +94 -0
  102. package/config/agent/skills/impeccable/frontend-design/reference/color-and-contrast.md +132 -0
  103. package/config/agent/skills/impeccable/frontend-design/reference/interaction-design.md +123 -0
  104. package/config/agent/skills/impeccable/frontend-design/reference/motion-design.md +99 -0
  105. package/config/agent/skills/impeccable/frontend-design/reference/responsive-design.md +114 -0
  106. package/config/agent/skills/impeccable/frontend-design/reference/spatial-design.md +100 -0
  107. package/config/agent/skills/impeccable/frontend-design/reference/typography.md +133 -0
  108. package/config/agent/skills/impeccable/frontend-design/reference/ux-writing.md +107 -0
  109. package/config/agent/skills/impeccable/harden/SKILL.md +357 -0
  110. package/config/agent/skills/impeccable/normalize/SKILL.md +73 -0
  111. package/config/agent/skills/impeccable/onboard/SKILL.md +248 -0
  112. package/config/agent/skills/impeccable/optimize/SKILL.md +268 -0
  113. package/config/agent/skills/impeccable/overdrive/SKILL.md +144 -0
  114. package/config/agent/skills/impeccable/polish/SKILL.md +205 -0
  115. package/config/agent/skills/impeccable/quieter/SKILL.md +105 -0
  116. package/config/agent/skills/impeccable/teach-impeccable/SKILL.md +71 -0
  117. package/config/agent/skills/impeccable/typeset/SKILL.md +118 -0
  118. package/config/agent/skills/librarian/SKILL.md +5 -1
  119. package/config/agent/skills/markdown-to-html/SKILL.md +5 -2
  120. package/config/agent/skills/owasp-security/SKILL.md +539 -0
  121. package/config/agent/skills/pr/SKILL.md +5 -2
  122. package/config/agent/skills/receiving-code-review/SKILL.md +216 -0
  123. package/config/agent/skills/refactor/SKILL.md +5 -2
  124. package/config/agent/skills/requesting-code-review/SKILL.md +108 -0
  125. package/config/agent/skills/requesting-code-review/code-reviewer.md +146 -0
  126. package/config/agent/skills/session-learning/SKILL.md +185 -0
  127. package/config/agent/skills/skill-creator/SKILL.md +339 -213
  128. package/config/agent/skills/skill-creator/agents/analyzer.md +274 -0
  129. package/config/agent/skills/skill-creator/agents/comparator.md +202 -0
  130. package/config/agent/skills/skill-creator/agents/grader.md +223 -0
  131. package/config/agent/skills/skill-creator/assets/eval_review.html +146 -0
  132. package/config/agent/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  133. package/config/agent/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  134. package/config/agent/skills/skill-creator/references/schemas.md +430 -0
  135. package/config/agent/skills/skill-creator/scripts/__init__.py +0 -0
  136. package/config/agent/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  137. package/config/agent/skills/skill-creator/scripts/generate_report.py +326 -0
  138. package/config/agent/skills/skill-creator/scripts/improve_description.py +248 -0
  139. package/config/agent/skills/skill-creator/scripts/package_skill.py +33 -7
  140. package/config/agent/skills/skill-creator/scripts/quick_validate.py +11 -3
  141. package/config/agent/skills/skill-creator/scripts/run_eval.py +310 -0
  142. package/config/agent/skills/skill-creator/scripts/run_loop.py +332 -0
  143. package/config/agent/skills/skill-creator/scripts/utils.py +47 -0
  144. package/config/agent/skills/subagent-driven-development/SKILL.md +280 -0
  145. package/config/agent/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  146. package/config/agent/skills/subagent-driven-development/implementer-prompt.md +113 -0
  147. package/config/agent/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  148. package/config/agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
  149. package/config/agent/skills/systematic-debugging/SKILL.md +300 -0
  150. package/config/agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  151. package/config/agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
  152. package/config/agent/skills/systematic-debugging/defense-in-depth.md +122 -0
  153. package/config/agent/skills/systematic-debugging/find-polluter.sh +63 -0
  154. package/config/agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
  155. package/config/agent/skills/systematic-debugging/test-academic.md +14 -0
  156. package/config/agent/skills/systematic-debugging/test-pressure-1.md +58 -0
  157. package/config/agent/skills/systematic-debugging/test-pressure-2.md +68 -0
  158. package/config/agent/skills/systematic-debugging/test-pressure-3.md +69 -0
  159. package/config/agent/skills/test-driven-development/SKILL.md +375 -0
  160. package/config/agent/skills/test-driven-development/testing-anti-patterns.md +299 -0
  161. package/config/agent/skills/ui-ux-pro-max/SKILL.md +4 -1
  162. package/config/agent/skills/unit-test/SKILL.md +4 -2
  163. package/config/agent/skills/using-git-worktrees/SKILL.md +222 -0
  164. package/config/agent/skills/using-superpowers/SKILL.md +118 -0
  165. package/config/agent/skills/using-superpowers/references/codex-tools.md +25 -0
  166. package/config/agent/skills/using-superpowers/references/gemini-tools.md +33 -0
  167. package/config/agent/skills/verification-before-completion/SKILL.md +142 -0
  168. package/config/agent/skills/writing-plans/SKILL.md +148 -0
  169. package/config/agent/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  170. package/config/agent/skills/writing-skills/SKILL.md +669 -0
  171. package/config/agent/skills/writing-skills/anthropic-best-practices.md +1150 -0
  172. package/config/agent/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  173. package/config/agent/skills/writing-skills/graphviz-conventions.dot +172 -0
  174. package/config/agent/skills/writing-skills/persuasion-principles.md +187 -0
  175. package/config/agent/skills/writing-skills/render-graphs.js +168 -0
  176. package/config/agent/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  177. package/config.json +23 -2
  178. package/dist/banner.js +3 -3
  179. package/dist/banner.js.map +1 -1
  180. package/package.json +21 -16
  181. package/scripts/banner.txt +4 -3
  182. package/scripts/postinstall.js +203 -11
  183. package/config/agent/prompts/git-rollback.md +0 -91
  184. package/config/agent/prompts/git-worktree.md +0 -277
  185. package/config/agent/prompts/help.md +0 -10
  186. package/config/agent/skills/github/SKILL.md +0 -47
  187. package/config/agent/skills/skill-creator/references/output-patterns.md +0 -82
  188. package/config/agent/skills/skill-creator/references/workflows.md +0 -28
  189. package/config/agent/skills/skill-creator/scripts/init_skill.py +0 -303
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * Custom Footer Extension — Single-line compact powerline style
3
4
  *
4
5
  * ~/path (branch) │ ↑in ↓out $cost │ 42%/200k │ ⚡ model • thinking
@@ -16,11 +17,35 @@ function fmtTokens(n: number): string {
16
17
  return `${(n / 1000000).toFixed(1)}M`;
17
18
  }
18
19
 
19
- // Catppuccin Mocha Yellow — #f9e2af
20
- const gold = (s: string) => `\x1b[38;2;166;227;161m${s}\x1b[0m`;
20
+ // #49bccf
21
+ const gold = (s: string) => `\x1b[38;2;73;188;207m${s}\x1b[0m`;
22
+
23
+ // 获取本地版本号
24
+ function getVersion() {
25
+ return process.env.AICODE_CLI_VERSION || null;
26
+ }
21
27
 
22
28
  export default function (pi: ExtensionAPI) {
23
29
  pi.on("session_start", async (_event, ctx) => {
30
+ // 检查版本更新并显示通知
31
+ const cachePath = process.env.AICODE_CLI_VERSION_CACHE;
32
+ if (cachePath) {
33
+ try {
34
+ const { existsSync, readFileSync } = await import("fs");
35
+ if (existsSync(cachePath)) {
36
+ const cache = JSON.parse(readFileSync(cachePath, 'utf-8'));
37
+ // 缓存有效期 1 小时
38
+ if (cache.needsUpdate && Date.now() - cache.timestamp < 3600000) {
39
+ // 在输入框上方显示版本更新提示
40
+ const msg = `\x1b[38;2;243;139;168m⚠️ 发现新版本 v${cache.localVersion} → v${cache.latestVersion}\x1b[0m \x1b[36mnpm install -g @ia-ccun/code-agent-cli\x1b[0m`;
41
+ ctx.ui.setWidget("version-update", [msg], { placement: "aboveEditor" });
42
+ }
43
+ }
44
+ } catch (e) {
45
+ // 忽略
46
+ }
47
+ }
48
+
24
49
  ctx.ui.setFooter((tui, theme, footerData) => {
25
50
  const unsub = footerData.onBranchChange(() => tui.requestRender());
26
51
 
@@ -161,6 +186,12 @@ export default function (pi: ExtensionAPI) {
161
186
  segments.push(modelColored);
162
187
  segments.push(theme.fg("muted", quote));
163
188
 
189
+ // ── Version segment (rightmost) ─────────────────────────
190
+ const version = getVersion();
191
+ if (version) {
192
+ segments.push(theme.fg("muted", `v${version}`));
193
+ }
194
+
164
195
  const joined = segments.join(sep);
165
196
  return [truncateToWidth(joined, width)];
166
197
  },
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * 自定义扩展示例
3
4
  *
4
5
  * 放置位置: config/agent/extensions/
@@ -35,9 +36,9 @@ export default function (pi: ExtensionAPI) {
35
36
  timeStr = `${mins}m ${secs}s`;
36
37
  }
37
38
 
38
- // 格式化时间
39
+ // 格式化完整时间
39
40
  const now = new Date();
40
- const timeStr2 = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
41
+ const timeStr2 = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
41
42
 
42
43
  // agent_end 表示一轮对话结束,显示完成提示
43
44
  ctx.ui.notify(`${timeStr2} | ✨ 消息处理完成 (${timeStr})`, "info");
@@ -57,51 +58,6 @@ export default function (pi: ExtensionAPI) {
57
58
  await ctx.ui.notify("⚠️ logout 功能已禁用", "warning");
58
59
  return { block: true, reason: "logout 功能已禁用" };
59
60
  }
60
-
61
- // 拦截其他工具
62
- if (event.toolName === "bash") {
63
- const command = event.input.command;
64
- if (command.includes("rm -rf")) {
65
- // 解析要删除的路径
66
- const match = command.match(/rm\s+-rf\s+('[^']+'|"[^"]+"|\S+)/g);
67
- if (match) {
68
- const paths = match.map(m => m.replace(/^rm\s+-rf\s+/, '').replace(/^['"]|['"]$/g, ''));
69
-
70
- // 尝试列出文件
71
- const { readdirSync, statSync, existsSync } = await import("fs");
72
- let fileList: string[] = [];
73
- for (const p of paths) {
74
- try {
75
- if (existsSync(p)) {
76
- const stat = statSync(p);
77
- if (stat.isDirectory()) {
78
- const files = readdirSync(p);
79
- fileList.push(`📁 ${p}/ (${files.length} 个文件)`);
80
- } else {
81
- fileList.push(`📄 ${p}`);
82
- }
83
- }
84
- } catch (e) { /* 忽略读取错误 */ }
85
- }
86
-
87
- const msg = fileList.length > 0
88
- ? `将删除以下文件/目录:\n${fileList.join('\n')}`
89
- : '确定要执行此删除命令吗?';
90
-
91
- // 第一次确认
92
- const firstConfirm = await ctx.ui.confirm("🔴 危险操作 - rm -rf", msg);
93
- if (!firstConfirm) {
94
- return { block: true, reason: "被用户拒绝" };
95
- }
96
-
97
- // 二次确认
98
- const secondConfirm = await ctx.ui.confirm("🔴 二次确认 - 不可恢复", "确定要继续删除吗?此操作不可恢复!");
99
- if (!secondConfirm) {
100
- return { block: true, reason: "被用户二次拒绝" };
101
- }
102
- }
103
- }
104
- }
105
61
  });
106
62
 
107
63
  // === 注册自定义命令 ===
@@ -179,52 +135,24 @@ export default function (pi: ExtensionAPI) {
179
135
  // === 注册 /update 命令 ===
180
136
 
181
137
  pi.registerCommand("update", {
182
- description: "更新 CLI 工具",
138
+ description: "更新 aicode-cli 工具",
183
139
  usage: "/update",
184
140
  handler: async (_args, ctx) => {
185
- const options = [
186
- "更新自己 (aicode-cli)",
187
- "更新依赖 agent",
188
- ];
189
-
190
- // 让用户选择
191
- const selected = await ctx.ui.select("选择更新内容", options);
192
- if (!selected) {
141
+ const ok = await ctx.ui.confirm("更新确认", "确定要更新 aicode-cli 吗?");
142
+ if (!ok) {
193
143
  ctx.ui.notify("已取消", "info");
194
144
  return;
195
145
  }
196
146
 
197
- if (selected.includes("aicode-cli")) {
198
- const ok = await ctx.ui.confirm("更新确认", "确定要更新 aicode-cli 吗?");
199
- if (!ok) {
200
- ctx.ui.notify("已取消", "info");
201
- return;
202
- }
203
- ctx.ui.notify("🔄 正在更新 aicode-cli...", "info");
204
- const { exec } = await import("child_process");
205
- exec("npm install -g @ia-ccun/aicode-cli", (error, stdout, stderr) => {
206
- if (error) {
207
- ctx.ui.notify(`❌ 更新失败: ${error.message},请重试`, "error");
208
- } else {
209
- ctx.ui.notify("✅ aicode-cli 更新成功!", "success");
210
- }
211
- });
212
- } else if (selected.includes("agent")) {
213
- const ok = await ctx.ui.confirm("更新确认", "确定要更新依赖的agent 吗?");
214
- if (!ok) {
215
- ctx.ui.notify("已取消", "info");
216
- return;
147
+ ctx.ui.notify("🔄 正在更新 aicode-cli...", "info");
148
+ const { exec } = await import("child_process");
149
+ exec("npm install -g @ia-ccun/code-agent-cli", (error, stdout, stderr) => {
150
+ if (error) {
151
+ ctx.ui.notify(`❌ 更新失败: ${error.message},请重试`, "error");
152
+ } else {
153
+ ctx.ui.notify("✅ aicode-cli 更新成功!请重启 CLI", "success");
217
154
  }
218
- ctx.ui.notify("🔄 正在更新 ...", "info");
219
- const { exec } = await import("child_process");
220
- exec("npm install -g @mariozechner/pi-coding-agent", (error, stdout, stderr) => {
221
- if (error) {
222
- ctx.ui.notify(`❌ 更新失败: ${error.message},请重试`, "error");
223
- } else {
224
- ctx.ui.notify("✅ 依赖的 agent 更新成功!", "success");
225
- }
226
- });
227
- }
155
+ });
228
156
  },
229
157
  });
230
158
 
@@ -240,14 +168,19 @@ export default function (pi: ExtensionAPI) {
240
168
  usage: "/version",
241
169
  handler: async (_args, ctx) => {
242
170
  const { execSync } = await import("child_process");
243
- const pkgPath = "./package.json";
244
- const { readFileSync } = await import("fs");
171
+ const { readFileSync, existsSync } = await import("fs");
245
172
 
246
- // 读取本地版本
173
+ // 读取全局安装的 aicode-cli 版本
247
174
  let localVersion = "未知";
248
175
  try {
249
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
250
- localVersion = pkg.version;
176
+ // 获取全局 npm 包路径
177
+ const globalPath = execSync("npm root -g", { encoding: "utf8" }).trim();
178
+ const pkgPath = `${globalPath}/@ia-ccun/code-agent-cli/package.json`;
179
+
180
+ if (existsSync(pkgPath)) {
181
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
182
+ localVersion = pkg.version;
183
+ }
251
184
  } catch (e) {
252
185
  // 忽略
253
186
  }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Error Monitor Extension
3
+ *
4
+ * @author: xujianjiang
5
+ *
6
+ * Logs errors to ./.aicode-cli/debug-logs/aicode-error-YYYY-MM-DD.log
7
+ */
8
+
9
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ import fs from "fs";
11
+ import path from "path";
12
+
13
+ // Error log file configuration
14
+ const ERROR_LOG_DIR = path.resolve(process.cwd(), "./.aicode-cli/debug-logs");
15
+ const KEEP_DAYS = 2; // 保留最近2天
16
+
17
+ // 北京时间格式化函数 (UTC+8)
18
+ function formatBeijingTime(date: Date = new Date()): string {
19
+ return date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
20
+ }
21
+
22
+ // 格式化为文件名日期字符串 (北京时间)
23
+ function getLogFileName(date: Date = new Date()): string {
24
+ const beijingDate = new Date(date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
25
+ const year = beijingDate.getFullYear();
26
+ const month = String(beijingDate.getMonth() + 1).padStart(2, "0");
27
+ const day = String(beijingDate.getDate()).padStart(2, "0");
28
+ return `aicode-error-${year}-${month}-${day}.log`;
29
+ }
30
+
31
+ // 清理过期日志文件(保留最近 KEEP_DAYS 天)
32
+ function cleanOldLogs(): void {
33
+ if (!fs.existsSync(ERROR_LOG_DIR)) return;
34
+
35
+ const files = fs.readdirSync(ERROR_LOG_DIR);
36
+ const now = new Date(new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
37
+
38
+ for (const file of files) {
39
+ if (!file.startsWith("aicode-error-") || !file.endsWith(".log")) continue;
40
+
41
+ // 从文件名提取日期
42
+ const match = file.match(/aicode-error-(\d{4})-(\d{2})-(\d{2})\.log/);
43
+ if (!match) continue;
44
+
45
+ const fileDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]));
46
+ const diffDays = Math.floor((now.getTime() - fileDate.getTime()) / (1000 * 60 * 60 * 24));
47
+
48
+ if (diffDays > KEEP_DAYS) {
49
+ fs.unlinkSync(path.join(ERROR_LOG_DIR, file));
50
+ }
51
+ }
52
+ }
53
+
54
+ // Ensure log directory exists
55
+ function ensureLogDir(): void {
56
+ if (!fs.existsSync(ERROR_LOG_DIR)) {
57
+ fs.mkdirSync(ERROR_LOG_DIR, { recursive: true });
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @author: xujianjiang
63
+ * Append error message to log file
64
+ */
65
+ function appendToErrorLog(message: string, context?: Record<string, unknown>): void {
66
+ ensureLogDir();
67
+ cleanOldLogs(); // 每次写日志时清理过期文件
68
+ const timestamp = formatBeijingTime();
69
+ const logFile = path.join(ERROR_LOG_DIR, getLogFileName());
70
+ let logEntry = `[${timestamp}] ${message}`;
71
+ if (context && Object.keys(context).length > 0) {
72
+ logEntry += `\nContext: ${JSON.stringify(context, null, 2)}`;
73
+ }
74
+ logEntry += "\n---\n";
75
+ fs.appendFileSync(logFile, logEntry);
76
+ }
77
+
78
+ export default function (pi: ExtensionAPI) {
79
+ // Counter for error tracking
80
+ let toolErrorCount = 0;
81
+ let bashErrorCount = 0;
82
+ const startTime = Date.now();
83
+
84
+ // Monitor tool execution end for errors
85
+ pi.on("tool_execution_end", (event) => {
86
+ const { toolCallId, toolName, result, isError } = event;
87
+
88
+ if (isError) {
89
+ toolErrorCount++;
90
+ const errorMsg = typeof result === "string" ? result : JSON.stringify(result);
91
+
92
+ // Append to error log file
93
+ appendToErrorLog(
94
+ `Tool Execution Failed: ${toolName}`,
95
+ {
96
+ toolCallId,
97
+ toolName,
98
+ errorMessage: errorMsg?.substring(0, 500),
99
+ }
100
+ );
101
+ }
102
+ });
103
+
104
+ // Monitor user bash events for command failures
105
+ pi.on("user_bash", (event) => {
106
+ const { command, result } = event;
107
+
108
+ if (result?.exitCode !== 0) {
109
+ bashErrorCount++;
110
+
111
+ // Append to error log file
112
+ appendToErrorLog(
113
+ `Bash Command Failed: ${command} - exit code ${result?.exitCode}`,
114
+ {
115
+ command,
116
+ exitCode: result?.exitCode,
117
+ stderr: result?.stderr?.substring(0, 500),
118
+ stdout: result?.stdout?.substring(0, 500),
119
+ }
120
+ );
121
+ }
122
+ });
123
+
124
+ // Summary on session shutdown
125
+ pi.on("session_shutdown", () => {
126
+ const uptime = Math.round((Date.now() - startTime) / 1000);
127
+ const totalErrors = toolErrorCount + bashErrorCount;
128
+
129
+ if (totalErrors > 0) {
130
+ console.log(`
131
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
132
+ 📊 Error Monitor 会话统计
133
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
134
+ 总错误数: ${totalErrors}
135
+ 工具错误数: ${toolErrorCount}
136
+ Bash 错误数: ${bashErrorCount}
137
+ 运行时长: ${uptime}秒
138
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
139
+ `);
140
+ }
141
+ });
142
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @author: xujianjiang
3
+ * Msg Debug Extension - Log LLM request/response messages to file
4
+ *
5
+ * This extension logs all requests and responses sent to/from the LLM provider
6
+ * for debugging purposes. Logs are saved to ./.aicode-cli/debug-logs/
7
+ *
8
+ * Features:
9
+ * - Logs before_provider_request: messages, parameters, raw payload
10
+ * - Logs after_provider_response: usage stats, response content, stop reason
11
+ * - Logs errors with context
12
+ * - Console output removed (logs to file only)
13
+ * - Session-based log files (one file per session)
14
+ */
15
+
16
+ import { appendFileSync, existsSync, mkdirSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
19
+
20
+ // 北京时间格式化函数 (UTC+8)
21
+ function formatBeijingTime(date: Date = new Date()): string {
22
+ return date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
23
+ }
24
+
25
+ // 格式化为文件名日期字符串 (北京时间)
26
+ function formatDateStr(date: Date = new Date()): string {
27
+ const beijingDate = new Date(date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }));
28
+ const year = beijingDate.getFullYear();
29
+ const month = String(beijingDate.getMonth() + 1).padStart(2, "0");
30
+ const day = String(beijingDate.getDate()).padStart(2, "0");
31
+ return `${year}-${month}-${day}`;
32
+ }
33
+
34
+ export default function (pi: ExtensionAPI) {
35
+ // Create log directory in project root: ./.aicode-cli/debug-logs
36
+ const logDir = join(process.cwd(), ".aicode-cli", "debug-logs");
37
+ if (!existsSync(logDir)) {
38
+ mkdirSync(logDir, { recursive: true });
39
+ }
40
+
41
+ // Format date as YYYY-MM-DD for daily log files
42
+ const dateStr = formatDateStr();
43
+ const logFile = join(logDir, `llm-requests-${dateStr}.log`);
44
+
45
+ // Filter to only log user and assistant messages
46
+ const ALLOWED_ROLES = ["user", "assistant"];
47
+
48
+ function formatMessage(message: any): string {
49
+ if (!message) return "undefined";
50
+
51
+ const role = message.role || "unknown";
52
+ let content = "";
53
+
54
+ if (typeof message.content === "string") {
55
+ content = message.content;
56
+ } else if (Array.isArray(message.content)) {
57
+ content = message.content.map((item: any) => {
58
+ if (item.type === "text") {
59
+ return item.text || "";
60
+ } else if (item.type === "image") {
61
+ return `[image: ${item.source?.type || "unknown"}]`;
62
+ } else if (item.type === "tool_use") {
63
+ return `[tool_use: ${item.name || "unknown"}]`;
64
+ } else if (item.type === "tool_result") {
65
+ return `[tool_result: ${item.tool_use_id || ""}]`;
66
+ }
67
+ return JSON.stringify(item);
68
+ }).join("\n");
69
+ }
70
+
71
+ return `[${role}]: ${content}`;
72
+ }
73
+
74
+ // Filter messages to only include user and assistant
75
+ function filterMessages(messages: any[]): any[] {
76
+ if (!Array.isArray(messages)) return [];
77
+ return messages.filter((msg) => ALLOWED_ROLES.includes(msg.role));
78
+ }
79
+
80
+ // Log before request is sent
81
+ pi.on("before_provider_request", (event) => {
82
+ const timestamp = formatBeijingTime();
83
+ const { provider, model, payload } = event;
84
+
85
+ // Filter messages to only user and assistant
86
+ const filteredMessages = filterMessages(payload.messages);
87
+ const originalCount = payload.messages?.length || 0;
88
+
89
+ const logEntry = `
90
+ ================================================================================
91
+ [${timestamp}] BEFORE PROVIDER REQUEST
92
+ ================================================================================
93
+ Provider: ${provider || "unknown"}
94
+ Model: ${model || "unknown"}
95
+ Messages: ${filteredMessages.length} (filtered from ${originalCount}, showing user/assistant only)
96
+
97
+ --- Messages ---
98
+ ${filteredMessages.map((msg: any, i: number) =>
99
+ `[${i}] ${formatMessage(msg)}`
100
+ ).join("\n\n") || "No messages"}
101
+
102
+ --- Request Parameters ---
103
+ ${JSON.stringify({
104
+ maxTokens: payload.max_tokens,
105
+ temperature: payload.temperature,
106
+ topP: payload.top_p,
107
+ topK: payload.top_k,
108
+ stopSequences: payload.stop_sequences,
109
+ thinking: payload.thinking ? "enabled" : "disabled",
110
+ budgetTokens: payload.budget_tokens
111
+ }, null, 2)}
112
+
113
+ --- Raw Payload (filtered) ---
114
+ ${JSON.stringify({ ...payload, messages: filteredMessages }, null, 2)}
115
+
116
+ ================================================================================
117
+ `;
118
+
119
+ // Save to file
120
+ appendFileSync(logFile, logEntry, "utf8");
121
+ });
122
+
123
+ // Log after response is received
124
+ pi.on("after_provider_response", (event) => {
125
+ const timestamp = formatBeijingTime();
126
+ const { response, usage, cached } = event;
127
+
128
+ const logEntry = `
129
+ ================================================================================
130
+ [${timestamp}] AFTER PROVIDER RESPONSE
131
+ ================================================================================
132
+ Cached: ${cached ? "YES" : "NO"}
133
+
134
+ --- Usage Stats ---
135
+ ${JSON.stringify(usage, null, 2)}
136
+
137
+ --- Response Content ---
138
+ ${response.content?.map((c: any, i: number) => {
139
+ if (c.type === "text") {
140
+ return `[${i}] text: ${c.text}`;
141
+ } else if (c.type === "tool_use") {
142
+ return `[${i}] tool_use: ${c.name}(${JSON.stringify(c.input)})`;
143
+ }
144
+ return `[${i}] ${c.type}: ${JSON.stringify(c)}`;
145
+ }).join("\n") || "No content"}
146
+
147
+ --- Stop Reason ---
148
+ ${response.stopReason || "unknown"}
149
+
150
+ --- Raw Response ---
151
+ ${JSON.stringify(response, null, 2)}
152
+
153
+ ================================================================================
154
+
155
+ `;
156
+
157
+ // Save to file
158
+ appendFileSync(logFile, logEntry, "utf8");
159
+ });
160
+
161
+ // Log request errors
162
+ pi.on("error", (event) => {
163
+ const timestamp = formatBeijingTime();
164
+ const { error, context } = event;
165
+
166
+ const logEntry = `
167
+ ################################################################################
168
+ [${timestamp}] ERROR
169
+ ################################################################################
170
+ Error: ${error.message || String(error)}
171
+ Stack: ${error.stack || "No stack trace"}
172
+
173
+ Context:
174
+ ${JSON.stringify(context, null, 2)}
175
+
176
+ ################################################################################
177
+
178
+ `;
179
+
180
+ // Save to file
181
+ appendFileSync(logFile, logEntry, "utf8");
182
+ });
183
+
184
+ // Extension enabled silently (logs to file only, no console output)
185
+ }
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * Code Review Extension (inspired by Codex's review feature)
3
4
  *
4
5
  * Provides a `/review` command that prompts the agent to review code changes.
@@ -916,6 +917,9 @@ export default function reviewExtension(pi: ExtensionAPI) {
916
917
  fullPrompt += `\n\nThis project has additional instructions for code reviews:\n\n${projectGuidelines}`;
917
918
  }
918
919
 
920
+ // 要求 AI 使用中文输出
921
+ fullPrompt += `\n\n---\n\n**重要:请使用中文撰写审查反馈。**`;
922
+
919
923
  const modeHint = useFreshSession ? " (fresh session)" : "";
920
924
  ctx.ui.notify(`Starting review: ${hint}${modeHint}`, "info");
921
925
 
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * working-message
3
4
  *
4
5
  * 自定义工作消息,格式类似 Claude Code: ✶ xxx… (时间 · tokens)
@@ -11,33 +12,52 @@ import type { AssistantMessage } from "@mariozechner/pi-ai";
11
12
  const accentColor = "\x1b[38;2;73;188;207m";
12
13
  const reset = "\x1b[0m";
13
14
 
14
- // 随机消息列表
15
+ // 随机消息列表 - 金融/支付/科技术语
15
16
  const workingMessages = [
16
- "Proofing", "Thinking", "Analyzing", "Reading", "Writing", "干活儿中",
17
- "Searching", "Generating", "Compiling", "Running", "Optimizing", "打卡了吗",
18
- "Debugging", "Learning", "Processing", "Reasoning", "Summarizing", "一会儿吃啥",
19
- "Planning", "Executing", "Fetching", "Parsing", "Validating","双人复核",
20
- "Building", "Testing", "Deploying", "Monitoring", "Refactoring", "aicoding",
21
- "Documenting", "Researching", "Evaluating", "Comparing", "Calculating",
22
- ];
17
+ // 金融支付类
18
+ "备付金", "网银支付", "快捷支付", "代付", "代收", "清算", "结算", "对账",
19
+ "交易路由", "风控", "反欺诈", "欺诈检测", "洗钱防控", "合规审查", "反洗钱",
20
+ "支付通道", "渠道商", "商户", "收款", "退款", "拒付", "冲正", "挂账",
21
+ "资金池", "沉淀资金", "T+1结算", "实时结算", "分账", "分润", "抽成",
22
+ "银行卡", "信用卡", "借记卡", "IC卡", "磁条卡", "虚拟卡", "预付费卡",
23
+ "二维码支付", "NFC支付", "刷脸支付", "指纹支付", "声纹支付",
24
+ "跨境支付", "外汇结算", "汇率", "国际清算", "SWIFT", "CIPS",
23
25
 
24
- let currentIndex = 0;
26
+ // 区块链/网络安全类
27
+ "区块链", "智能合约", "共识机制", "PoW", "PoS", "DPoS", "Gas费",
28
+ "加密货币", "比特币", "以太坊", "稳定币", "DeFi", "NFT", "Web3",
29
+ "数字签名", "哈希", "非对称加密", "对称加密", "公钥", "私钥", "证书",
30
+ "数字证书", "PKI体系", "CA中心", "SSL/TLS", "HTTPS", "国密算法",
31
+ "防火墙", "入侵检测", "DDoS防护", "WAF", "漏洞扫描", "渗透测试",
25
32
 
26
- function getNextMessage(): string {
27
- const msg = workingMessages[currentIndex];
28
- currentIndex = (currentIndex + 1) % workingMessages.length;
29
- return msg;
30
- }
33
+ // 生产变更/运维类
34
+ "变更管理", "发布", "回滚", "灰度发布", "蓝绿部署", "金丝雀发布",
35
+ "熔断", "限流", "降级", "切流", "流量切换", "流量调度",
36
+ "监控", "告警", "日志", "链路追踪", "APM", "性能监控", "可用性",
37
+
38
+ // 技术通用类
39
+ "Proofing", "Thinking", "Analyzing", "Reading", "Writing", "Searching",
40
+ "Generating", "Compiling", "Running", "Optimizing", "Debugging", "Learning",
41
+ "Processing", "Reasoning", "Summarizing", "Planning", "Executing", "Fetching",
42
+ "Parsing", "Validating", "Building", "Testing", "Deploying", "Refactoring",
43
+ "Documenting", "Researching", "Evaluating", "Comparing", "Calculating",
44
+ "Algorithm", "Architecture", "Database", "Cache", "Queue", "Microservice",
45
+ ];
31
46
 
32
47
  let intervalId: ReturnType<typeof setInterval> | null = null;
33
48
  let startTime = 0;
34
49
  let totalTokens = 0;
35
50
 
51
+ function getNextMessage(): string {
52
+ const randomIndex = Math.floor(Math.random() * workingMessages.length);
53
+ return workingMessages[randomIndex];
54
+ }
55
+
36
56
  function formatTime(seconds: number): string {
37
- if (seconds < 60) return `${seconds}s`;
57
+ if (seconds < 60) return `${seconds} s`;
38
58
  const mins = Math.floor(seconds / 60);
39
59
  const secs = seconds % 60;
40
- return `${mins}m ${secs}s`;
60
+ return `${mins} m ${secs} s`;
41
61
  }
42
62
 
43
63
  function formatTokens(tokens: number): string {
@@ -53,7 +73,6 @@ export default function workingMessageExtension(pi: ExtensionAPI) {
53
73
  // 记录开始时间
54
74
  startTime = Date.now();
55
75
  totalTokens = 0;
56
- currentIndex = 0;
57
76
 
58
77
  // 立即设置第一条
59
78
  const updateMessage = () => {
@@ -61,7 +80,7 @@ export default function workingMessageExtension(pi: ExtensionAPI) {
61
80
  const timeStr = formatTime(elapsed);
62
81
  const tokensStr = formatTokens(totalTokens);
63
82
  // ✶ 和单词显示为主题色
64
- const msg = `${accentColor}✶${getNextMessage()}…${reset} (${timeStr} · ↓ ${tokensStr}tokens)`;
83
+ const msg = `${accentColor} ✶ ${getNextMessage()}…${reset} (${timeStr} · ↓ ${tokensStr} tokens)`;
65
84
  ctx.ui?.setWorkingMessage(msg);
66
85
  };
67
86
 
@@ -74,8 +93,8 @@ export default function workingMessageExtension(pi: ExtensionAPI) {
74
93
  for (const entry of ctx.sessionManager.getBranch()) {
75
94
  if (entry.type === "message" && (entry as any).message?.role === "assistant") {
76
95
  const msg = (entry as any).message as AssistantMessage;
77
- if (msg.usage?.outputTokens) {
78
- totalTokens = msg.usage.outputTokens;
96
+ if (msg.usage?.output) {
97
+ totalTokens = msg.usage.output;
79
98
  }
80
99
  }
81
100
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "mcpServers": {
3
+ }
4
+ }