@ia-ccun/code-agent-cli 0.0.47-beta.3 → 0.0.47-beta.5

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.
@@ -0,0 +1,172 @@
1
+ /**
2
+ * @author: xujianjiang
3
+ * Aicode Tips Extension — 在边框下方、状态栏上方随机显示使用技巧
4
+ *
5
+ * 显示 25 秒后消失,5 秒后显示下一条
6
+ * 技巧列表包含快捷键、命令使用及联系方式提示
7
+ */
8
+
9
+ import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
10
+
11
+ const AICODE_TIPS = [
12
+ "@ 快速搜索项目文件",
13
+ "Ctrl+L 快速切换模型",
14
+ "Ctrl+P/Shift+Ctrl+P 切换模型",
15
+ "Shift+Tab 切换思考级别",
16
+ "Ctrl+O 展开/折叠工具输出",
17
+ "Ctrl+T 展开/折叠思考块",
18
+ "Alt+Enter 队列跟进消息",
19
+ "Ctrl+V 粘贴图片",
20
+ "Shift+Enter 输入多行",
21
+ "/ 查看所有命令",
22
+ "/tree 查看会话历史",
23
+ "/session 查看统计",
24
+ "/settings 自定义设置",
25
+ "/export 导出会话为HTML",
26
+ "/copy 复制最后消息",
27
+ "/name 设置会话名称",
28
+ "/hotkeys 显示所有快捷键",
29
+ "/reload 重载扩展和技能",
30
+ "/new 开始新会话",
31
+ "!cmd 执行 bash",
32
+ "!!cmd 执行bash但不发送给AI",
33
+ "/compact 压缩上下文",
34
+ "/skill:name 调用技能",
35
+ "/resume 恢复会话",
36
+ "/fork 创建新分支",
37
+ "Ctrl+C 清空编辑器",
38
+ "双击Esc 打开树视图",
39
+ "拖拽文件到终端附加",
40
+ // 扩展命令提示
41
+ "/version 查看当前版本",
42
+ "/update 更新 aicode-cli",
43
+ "/status 显示会话状态",
44
+ "/clear 清理当前会话",
45
+ "/quit 退出当前会话",
46
+ // 常用Prompt提示
47
+ "/code-simplifier 自动简化刚修改的代码,提升清晰度和一致性",
48
+ "/feat 新功能开发,支持需求规划→讨论迭代→执行实施完整流程(推荐使用)",
49
+ "/git-commit 分析Git改动并生成Conventional Commits规范提交信息(推荐使用)",
50
+ "/init-project 初始化项目AI上下文,生成根级和模块级CLAUDE.md索引",
51
+ "/workflow 专业开发者6阶段结构化工作流(研究→构思→计划→执行→优化→评审)",
52
+ // 常用技能提示
53
+ "/skill:brainstorming 探索用户意图、需求分析和方案设计",
54
+ "/skill:code-review 执行全面的代码质量/安全/性能审查(推荐使用)",
55
+ "/skill:commit 分析代码变更并生成规范的Git提交信息",
56
+ "/skill:frontend-design 创建高质量生产级前端界面(推荐使用)",
57
+ "/skill:owasp-security 遵循OWASP Top 10的安全编码实践(推荐使用)",
58
+ "/skill:receiving-code-review 技术性处理代码评审反馈并验证(推荐使用)",
59
+ "/skill:requesting-code-review 请求代码评审确保满足合并要求",
60
+ "/skill:unit-test 为选定的代码文件生成单元测试",
61
+ "/skill:verification-before-completion 完成前验证实际工作是否完整",
62
+ "/skill:writing-plans 为多步骤任务编写详细的实施计划(推荐使用)",
63
+ "/skill:writing-skills 创建、修改和优化Agent技能并测量性能",
64
+ "/skill:session-learning 总结会话经验并沉淀为可复用的技能",
65
+ "/skill:test-driven-development 先写测试再写实现的TDD开发模式",
66
+ "/skill:subagent-driven-development 通过派发子Agent执行复杂实施计划",
67
+ "/skill:dispatching-parallel-agents 并行分发多个独立任务无共享状态",
68
+ "/skill:csv-data-summarizer 分析CSV文件并生成统计和可视化图表",
69
+ "/skill:executing-plans 执行带有检查点的书面实施计划",
70
+ "/skill:frontend-slides 使用HTML/CSS创建精美的演示幻灯片",
71
+ "/skill:markdown-to-html 将Markdown文档转换为精美的HTML页面",
72
+ "/skill:refactor 重构代码提升可读性和可维护性保持功能不变",
73
+ "/skill:skill-creator 创建、修改和提升技能性能的工具",
74
+ "/skill:ui-ux-pro-max 提供50种设计风格和21种配色方案的设计助手(推荐使用)",
75
+ // 常用技能提示
76
+ "/btw 并行侧边对话,可在不中断主流程的情况下进行临时讨论或查询 ",
77
+ "/rewind 回退到任意工具执行前的状态,支持快照恢复和撤销重做 ",
78
+ "/mcp 连接现有的MCP服务器(如xx平台、企业微信等等),扩展AI能力支持多种外部工具和服务 ",
79
+ // 联系方式提示
80
+ "💡 任何疑问或需求可联系 xujianjiang,支持需求对接开发、系统集成与扩展",
81
+ "📖 内网手册参考 UniDoc《2026.03 AICode Agent CLI编码智能体使用手册》",
82
+ "⚠️ LLM API 4xx/5xx 错误时请确认模型配置是否正确,或在企微群咨询 jiangchi、maliang",
83
+ ];
84
+
85
+ export default function (pi: ExtensionAPI) {
86
+ let currentTipIndex = -1;
87
+ let autoHideTimeoutId: NodeJS.Timeout | undefined;
88
+ let autoShowTimeoutId: NodeJS.Timeout | undefined;
89
+ let currentCtx: ExtensionContext | undefined;
90
+ let isAutoPlaying = false;
91
+
92
+ const hideTip = () => {
93
+ if (!currentCtx?.hasUI) return;
94
+ currentCtx.ui.setWidget("aicode-tips", [], { placement: "aboveEditor" });
95
+ };
96
+
97
+ const clearAllTimers = () => {
98
+ if (autoHideTimeoutId) {
99
+ clearTimeout(autoHideTimeoutId);
100
+ autoHideTimeoutId = undefined;
101
+ }
102
+ if (autoShowTimeoutId) {
103
+ clearTimeout(autoShowTimeoutId);
104
+ autoShowTimeoutId = undefined;
105
+ }
106
+ };
107
+
108
+ const padRight = (str: string, width: number): string => {
109
+ const chinese = (str.match(/[\u4e00-\u9fff]/g) || []).length;
110
+ const ascii = str.length - chinese;
111
+ const fullWidth = chinese * 2 + ascii;
112
+ const padding = Math.max(0, width - fullWidth);
113
+ return str + " ".repeat(padding);
114
+ };
115
+
116
+ const showRandomTip = () => {
117
+ if (!currentCtx?.hasUI) return;
118
+
119
+ // 清除之前的定时器
120
+ clearAllTimers();
121
+
122
+ let randomIndex = Math.floor(Math.random() * AICODE_TIPS.length);
123
+ if (randomIndex === currentTipIndex && AICODE_TIPS.length > 1) {
124
+ randomIndex = (randomIndex + 1) % AICODE_TIPS.length;
125
+ }
126
+ currentTipIndex = randomIndex;
127
+ const tip = AICODE_TIPS[randomIndex];
128
+ // 颜色 #9cc8cfc2,右对齐
129
+ const coloredTip = `\x1b[38;2;156;200;207m${padRight("tips: " + tip, 60)}\x1b[0m`;
130
+ currentCtx.ui.setWidget("aicode-tips", [coloredTip], { placement: "aboveEditor" });
131
+
132
+ // 自动轮播:25秒后隐藏,隐藏后5秒显示下一条
133
+ if (isAutoPlaying) {
134
+ autoHideTimeoutId = setTimeout(() => {
135
+ hideTip();
136
+ autoShowTimeoutId = setTimeout(showRandomTip, 5000);
137
+ }, 25000);
138
+ }
139
+ };
140
+
141
+ const startAutoPlay = () => {
142
+ isAutoPlaying = true;
143
+ showRandomTip();
144
+ };
145
+
146
+ const stopAutoPlay = () => {
147
+ isAutoPlaying = false;
148
+ clearAllTimers();
149
+ };
150
+
151
+ // 会话开始时启动自动轮播
152
+ pi.on("session_start", (_event, ctx) => {
153
+ currentCtx = ctx;
154
+ startAutoPlay();
155
+ });
156
+
157
+ // 会话结束时清理
158
+ pi.on("session_shutdown", () => {
159
+ stopAutoPlay();
160
+ currentCtx?.ui.setWidget("aicode-tips", undefined);
161
+ currentCtx = undefined;
162
+ });
163
+
164
+ // 手动切换技巧
165
+ pi.registerCommand("tip", {
166
+ description: "显示一个新的 aicode 使用技巧",
167
+ handler: async (_args, ctx) => {
168
+ currentCtx = ctx;
169
+ showRandomTip();
170
+ },
171
+ });
172
+ }
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * Bash 命令危险检测扩展
3
4
  *
4
5
  * 放置位置: config/agent/extensions/
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * Context Monitor Extension — 自动检测上下文大小并在接近阈值时警告
3
4
  *
4
5
  * 功能:
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * /context
3
4
  *
4
5
  * Small TUI view showing what's loaded/available:
@@ -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
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * 自定义扩展示例
3
4
  *
4
5
  * 放置位置: config/agent/extensions/
@@ -134,52 +135,24 @@ export default function (pi: ExtensionAPI) {
134
135
  // === 注册 /update 命令 ===
135
136
 
136
137
  pi.registerCommand("update", {
137
- description: "更新 CLI 工具",
138
+ description: "更新 aicode-cli 工具",
138
139
  usage: "/update",
139
140
  handler: async (_args, ctx) => {
140
- const options = [
141
- "更新自己 (aicode-cli)",
142
- "更新依赖 agent",
143
- ];
144
-
145
- // 让用户选择
146
- const selected = await ctx.ui.select("选择更新内容", options);
147
- if (!selected) {
141
+ const ok = await ctx.ui.confirm("更新确认", "确定要更新 aicode-cli 吗?");
142
+ if (!ok) {
148
143
  ctx.ui.notify("已取消", "info");
149
144
  return;
150
145
  }
151
146
 
152
- if (selected.includes("aicode-cli")) {
153
- const ok = await ctx.ui.confirm("更新确认", "确定要更新 aicode-cli 吗?");
154
- if (!ok) {
155
- ctx.ui.notify("已取消", "info");
156
- return;
157
- }
158
- ctx.ui.notify("🔄 正在更新 aicode-cli...", "info");
159
- const { exec } = await import("child_process");
160
- exec("npm install -g @ia-ccun/code-agent-cli", (error, stdout, stderr) => {
161
- if (error) {
162
- ctx.ui.notify(`❌ 更新失败: ${error.message},请重试`, "error");
163
- } else {
164
- ctx.ui.notify("✅ aicode-cli 更新成功!", "success");
165
- }
166
- });
167
- } else if (selected.includes("agent")) {
168
- const ok = await ctx.ui.confirm("更新确认", "确定要更新依赖的agent 吗?");
169
- if (!ok) {
170
- ctx.ui.notify("已取消", "info");
171
- 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");
172
154
  }
173
- ctx.ui.notify("🔄 正在更新 ...", "info");
174
- const { exec } = await import("child_process");
175
- exec("npm install -g @mariozechner/pi-coding-agent", (error, stdout, stderr) => {
176
- if (error) {
177
- ctx.ui.notify(`❌ 更新失败: ${error.message},请重试`, "error");
178
- } else {
179
- ctx.ui.notify("✅ 依赖的 agent 更新成功!", "success");
180
- }
181
- });
182
- }
155
+ });
183
156
  },
184
157
  });
185
158
 
@@ -1,331 +1,103 @@
1
1
  /**
2
- * Error Monitor Extension - Monitor and alert on errors
3
- *
4
- * This extension monitors for errors during agent execution and displays
5
- * formatted console alerts with instructions to follow enterprise WeChat groups.
6
- *
7
- * Features:
8
- * - Monitors tool execution results for errors
9
- * - Monitors API responses for error patterns (including JSON format errors)
10
- * - Monitors tool call outcomes for failure indicators
11
- * - Displays formatted alerts with troubleshooting tips
12
- * - Categorizes errors by severity
13
- *
14
- * Triggers on:
15
- * - Tool execution errors (bash failures, file operations, etc.)
16
- * - Agent runtime errors
17
- * - API errors in JSON format (e.g., 500 {"type":"error",...})
18
- * - Error messages in tool results
2
+ * Error Monitor Extension
3
+ *
4
+ * @author: xujianjiang
5
+ *
6
+ * Logs errors to ./.aicode-cli/debug-logs/aicode-error.log
19
7
  */
20
8
 
21
9
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ import fs from "fs";
11
+ import path from "path";
22
12
 
23
- // Error categories with descriptions and suggested actions
24
- const ERROR_CATEGORIES: Record<string, { name: string; severity: "info" | "warning" | "critical"; action: string }> = {
25
- // Common error patterns
26
- timeout: { name: "执行超时", severity: "warning", action: "请重试请求或增加超时时间" },
27
- unauthorized: { name: "未授权访问", severity: "warning", action: "请检查 API Key 是否正确配置" },
28
- forbidden: { name: "访问被拒绝", severity: "warning", action: "请检查账户权限或配额" },
29
- not_found: { name: "资源不存在", severity: "info", action: "请检查请求的端点或资源路径" },
30
- rate_limit: { name: "请求过于频繁", severity: "warning", action: "请降低请求频率或等待后重试" },
31
- connection: { name: "连接错误", severity: "warning", action: "请检查网络连接" },
32
- network: { name: "网络错误", severity: "warning", action: "请检查网络连接" },
33
- econnrefused: { name: "连接被拒绝", severity: "warning", action: "请检查服务是否运行" },
34
-
35
- // Server-side errors
36
- 500: { name: "服务器内部错误", severity: "critical", action: "服务端问题,请稍后重试" },
37
- 502: { name: "网关错误", severity: "critical", action: "上游服务异常,请稍后重试" },
38
- 503: { name: "服务不可用", severity: "critical", action: "服务暂时不可用,请关注服务公告" },
39
- 504: { name: "网关超时", severity: "critical", action: "上游服务响应超时,请稍后重试" },
40
- 507: { name: "存储空间不足", severity: "critical", action: "请联系管理员检查服务器资源" },
41
- 509: { name: "带宽限制超出", severity: "critical", action: "已达到带宽限制,请升级套餐或等待重置" },
42
-
43
- // Client errors
44
- 400: { name: "请求参数错误", severity: "warning", action: "请检查请求参数是否正确" },
45
- 401: { name: "未授权访问", severity: "warning", action: "请检查 API Key 是否正确配置" },
46
- 403: { name: "访问被拒绝", severity: "warning", action: "请检查账户权限或配额" },
47
- 404: { name: "资源不存在", severity: "info", action: "请检查请求的端点或资源路径" },
48
- 408: { name: "请求超时", severity: "warning", action: "请重试请求" },
49
- 422: { name: "无法处理的请求", severity: "warning", action: "请检查请求格式和数据类型" },
50
- 429: { name: "请求过于频繁", severity: "warning", action: "请降低请求频率或等待后重试" },
51
- ECONNREFUSED: { name: "连接被拒绝", severity: "critical", action: "请检查服务是否运行" },
52
- ETIMEDOUT: { name: "连接超时", severity: "warning", action: "请检查网络连接或服务状态" },
53
-
54
- // API/Model specific errors
55
- "api_error": { name: "API 错误", severity: "critical", action: "API 服务异常,请稍后重试或联系管理员" },
56
- "model_not_supported": { name: "模型不支持", severity: "critical", action: "请检查账户套餐是否支持该模型,或切换到支持的模型" },
57
- };
13
+ // Error log file configuration
14
+ const ERROR_LOG_DIR = path.resolve(process.cwd(), "./.aicode-cli/debug-logs");
15
+ const ERROR_LOG_FILE = path.join(ERROR_LOG_DIR, "aicode-error.log");
58
16
 
59
- // WeChat groups to follow
60
- const ALERT_CHANNELS = [
61
- { name: "NUAI助手", description: "通用服务通知和帮助" },
62
- { name: "大模型服务群(服务变更消息)", description: "服务变更、维护通知" },
63
- ];
64
-
65
- function getSeverityIcon(severity: "info" | "warning" | "critical"): string {
66
- return { info: "ℹ️", warning: "⚠️", critical: "🔴" }[severity];
67
- }
68
-
69
- function formatErrorAlert(title: string, message: string, details?: Record<string, unknown>): string {
70
- const channelsList = ALERT_CHANNELS.map(
71
- (ch) => ` 📢 ${ch.name} - ${ch.description}`
72
- ).join("\n");
73
-
74
- let detailsStr = "";
75
- if (details && Object.keys(details).length > 0) {
76
- detailsStr = `
77
- 错误详情:
78
- ${JSON.stringify(details, null, 2)}`;
17
+ // Ensure log directory exists
18
+ function ensureLogDir(): void {
19
+ if (!fs.existsSync(ERROR_LOG_DIR)) {
20
+ fs.mkdirSync(ERROR_LOG_DIR, { recursive: true });
79
21
  }
80
-
81
- return `
82
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
83
- ${title}
84
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
85
- ${message}${detailsStr}
86
-
87
- 请关注以下企业微信群获取服务信息:
88
- ${channelsList}
89
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
90
- `;
91
22
  }
92
23
 
93
24
  /**
94
- * Parse JSON error format like: "500 {"type":"error","message":"..."}"
25
+ * @author: xujianjiang
26
+ * Append error message to log file
95
27
  */
96
- function parseJsonErrorFormat(text: string): { statusCode?: string; errorType?: string; errorMessage?: string } | null {
97
- // Match pattern like: "500 {"type":"error","message":"..."}" or just "{"type":"error","message":"..."}"
98
- const match = text.match(/^(?:(\d{3})\s*)?(\{.*\})$/s);
99
- if (match) {
100
- try {
101
- const json = JSON.parse(match[2]);
102
- if (json && json.type === "error") {
103
- return {
104
- statusCode: match[1],
105
- errorType: json.error?.type,
106
- errorMessage: json.error?.message || json.message,
107
- };
108
- }
109
- } catch {
110
- // Not valid JSON, continue with other detection
111
- }
112
- }
113
- return null;
114
- }
115
-
116
- function detectErrorCategory(errorMsg: string): { name: string; severity: "info" | "warning" | "critical"; action: string } {
117
- const lowerMsg = errorMsg.toLowerCase();
118
-
119
- // First, check for JSON error format (e.g., "500 {"type":"error",...}")
120
- const jsonError = parseJsonErrorFormat(errorMsg);
121
- if (jsonError) {
122
- // Check for model_not_supported or similar errors
123
- if (jsonError.errorMessage?.toLowerCase().includes("not support") ||
124
- jsonError.errorMessage?.toLowerCase().includes("not supported") ||
125
- jsonError.errorMessage?.toLowerCase().includes("model")) {
126
- return ERROR_CATEGORIES["model_not_supported"];
127
- }
128
- if (jsonError.errorType === "api_error") {
129
- return ERROR_CATEGORIES["api_error"];
130
- }
131
- // Fall back to status code if available
132
- if (jsonError.statusCode && ERROR_CATEGORIES[jsonError.statusCode]) {
133
- return ERROR_CATEGORIES[jsonError.statusCode];
134
- }
135
- }
136
-
137
- // Check for HTTP status codes in error message
138
- const statusMatch = errorMsg.match(/\b(4\d{2}|5\d{2})\b/);
139
- if (statusMatch) {
140
- const status = statusMatch[1];
141
- if (ERROR_CATEGORIES[status]) {
142
- return ERROR_CATEGORIES[status];
143
- }
144
- }
145
-
146
- // Check for known error patterns
147
- if (lowerMsg.includes("timeout") || lowerMsg.includes("timed out") || lowerMsg.includes("etimedout")) {
148
- return ERROR_CATEGORIES.timeout;
149
- }
150
- if (lowerMsg.includes("unauthorized") || lowerMsg.includes("401")) {
151
- return ERROR_CATEGORIES.unauthorized;
152
- }
153
- if (lowerMsg.includes("forbidden") || lowerMsg.includes("403")) {
154
- return ERROR_CATEGORIES.forbidden;
155
- }
156
- if (lowerMsg.includes("not found") || lowerMsg.includes("404")) {
157
- return ERROR_CATEGORIES.not_found;
158
- }
159
- if (lowerMsg.includes("rate limit") || lowerMsg.includes("429")) {
160
- return ERROR_CATEGORIES.rate_limit;
161
- }
162
- if (lowerMsg.includes("econnrefused") || lowerMsg.includes("connection refused")) {
163
- return ERROR_CATEGORIES.econnrefused;
28
+ function appendToErrorLog(message: string, context?: Record<string, unknown>): void {
29
+ ensureLogDir();
30
+ const timestamp = new Date().toISOString();
31
+ let logEntry = `[${timestamp}] ${message}`;
32
+ if (context && Object.keys(context).length > 0) {
33
+ logEntry += `\nContext: ${JSON.stringify(context, null, 2)}`;
164
34
  }
165
- if (lowerMsg.includes("connection") || lowerMsg.includes("network")) {
166
- return ERROR_CATEGORIES.connection;
167
- }
168
-
169
- // Check for model-related errors
170
- if (lowerMsg.includes("not support") || lowerMsg.includes("not supported") ||
171
- lowerMsg.includes("model") || lowerMsg.includes("token plan")) {
172
- return ERROR_CATEGORIES["model_not_supported"];
173
- }
174
-
175
- // Default
176
- return { name: "未知错误", severity: "warning", action: "请查看详细错误信息" };
35
+ logEntry += "\n---\n";
36
+ fs.appendFileSync(ERROR_LOG_FILE, logEntry);
177
37
  }
178
38
 
179
39
  export default function (pi: ExtensionAPI) {
180
40
  // Counter for error tracking
181
- let errorCount = 0;
182
41
  let toolErrorCount = 0;
183
- let apiErrorCount = 0;
42
+ let bashErrorCount = 0;
184
43
  const startTime = Date.now();
185
44
 
186
- // Monitor API responses for errors (this catches the JSON format errors)
187
- pi.on("model_response", (event) => {
188
- const { response } = event;
189
-
190
- // Check for JSON error format in response text
191
- const responseText = typeof response === "string" ? response : JSON.stringify(response);
192
- const jsonError = parseJsonErrorFormat(responseText);
193
-
194
- if (jsonError && jsonError.errorType) {
195
- apiErrorCount++;
196
- errorCount++;
197
-
198
- let category: { name: string; severity: "info" | "warning" | "critical"; action: string };
199
- let title: string;
200
-
201
- // Determine error category based on error type/message
202
- if (jsonError.errorMessage?.toLowerCase().includes("not support") ||
203
- jsonError.errorMessage?.toLowerCase().includes("not supported") ||
204
- jsonError.errorMessage?.toLowerCase().includes("model") ||
205
- jsonError.errorMessage?.toLowerCase().includes("token plan")) {
206
- category = ERROR_CATEGORIES["model_not_supported"];
207
- title = "🔴 API 错误 - 模型不支持";
208
- } else {
209
- category = ERROR_CATEGORIES[jsonError.errorType] ||
210
- { name: "API 错误", severity: "critical", action: "API 服务异常,请稍后重试" };
211
- title = `${getSeverityIcon(category.severity)} API 错误 - ${jsonError.errorType}`;
212
- }
213
-
214
- console.log(formatErrorAlert(
215
- title,
216
- `错误类型: ${jsonError.errorType}\n严重程度: ${category.severity.toUpperCase()}\n建议操作: ${category.action}`,
217
- {
218
- statusCode: jsonError.statusCode,
219
- errorType: jsonError.errorType,
220
- errorMessage: jsonError.errorMessage,
221
- request_id: responseText.match(/"request_id":"([^"]+)"/)?.[1],
222
- }
223
- ));
224
- }
225
- });
226
-
227
- // Monitor tool execution results for errors
228
- pi.on("tool_result", (event) => {
229
- const { toolCallId, toolName, result } = event;
230
-
231
- // Check if result is an error
232
- const isError = result?.isError === true;
233
- const content = typeof result?.content === "string" ? result.content :
234
- Array.isArray(result?.content) ? result.content.map((c: any) => c.text || c.content).join("\n") : "";
235
-
236
- if (isError || content.toLowerCase().includes("error") || content.toLowerCase().includes("failed")) {
237
- toolErrorCount++;
238
- errorCount++;
239
-
240
- const category = detectErrorCategory(content);
241
- const icon = getSeverityIcon(category.severity);
242
-
243
- console.log(formatErrorAlert(
244
- `${icon} 工具执行错误 - ${toolName}`,
245
- `错误类型: ${category.name}\n严重程度: ${category.severity.toUpperCase()}\n工具名称: ${toolName}\n建议操作: ${category.action}`,
246
- {
247
- toolCallId,
248
- errorMessage: content.substring(0, 500),
249
- }
250
- ));
251
- }
252
- });
253
-
254
45
  // Monitor tool execution end for errors
255
46
  pi.on("tool_execution_end", (event) => {
256
47
  const { toolCallId, toolName, result, isError } = event;
257
-
48
+
258
49
  if (isError) {
259
- errorCount++;
260
-
50
+ toolErrorCount++;
261
51
  const errorMsg = typeof result === "string" ? result : JSON.stringify(result);
262
- const category = detectErrorCategory(errorMsg);
263
- const icon = getSeverityIcon(category.severity);
264
-
265
- console.log(formatErrorAlert(
266
- `${icon} 工具执行失败 - ${toolName}`,
267
- `错误类型: ${category.name}\n严重程度: ${category.severity.toUpperCase()}\n工具名称: ${toolName}\n建议操作: ${category.action}`,
52
+
53
+ // Append to error log file
54
+ appendToErrorLog(
55
+ `Tool Execution Failed: ${toolName}`,
268
56
  {
269
57
  toolCallId,
58
+ toolName,
270
59
  errorMessage: errorMsg?.substring(0, 500),
271
60
  }
272
- ));
273
-
274
- // Debug logging
275
- if (process.env.DEBUG_ERRORS) {
276
- console.error("[error-monitor] Tool execution error:", {
277
- toolCallId,
278
- toolName,
279
- result,
280
- isError,
281
- });
282
- }
61
+ );
283
62
  }
284
63
  });
285
64
 
286
65
  // Monitor user bash events for command failures
287
66
  pi.on("user_bash", (event) => {
288
67
  const { command, result } = event;
289
-
68
+
290
69
  if (result?.exitCode !== 0) {
291
- errorCount++;
292
-
293
- const category = detectErrorCategory(result?.stderr || "");
294
- const icon = getSeverityIcon(category.severity);
295
-
296
- console.log(formatErrorAlert(
297
- `${icon} Bash 命令执行失败`,
298
- `命令: ${command}\n退出码: ${result?.exitCode}\n错误类型: ${category.name}\n建议操作: ${category.action}`,
70
+ bashErrorCount++;
71
+
72
+ // Append to error log file
73
+ appendToErrorLog(
74
+ `Bash Command Failed: ${command} - exit code ${result?.exitCode}`,
299
75
  {
300
76
  command,
301
77
  exitCode: result?.exitCode,
302
78
  stderr: result?.stderr?.substring(0, 500),
303
79
  stdout: result?.stdout?.substring(0, 500),
304
80
  }
305
- ));
81
+ );
306
82
  }
307
83
  });
308
84
 
309
85
  // Summary on session shutdown
310
86
  pi.on("session_shutdown", () => {
311
87
  const uptime = Math.round((Date.now() - startTime) / 1000);
312
-
313
- if (errorCount > 0) {
88
+ const totalErrors = toolErrorCount + bashErrorCount;
89
+
90
+ if (totalErrors > 0) {
314
91
  console.log(`
315
92
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
316
93
  📊 Error Monitor 会话统计
317
94
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
318
- 总错误数: ${errorCount}
319
- API 错误数: ${apiErrorCount}
95
+ 总错误数: ${totalErrors}
320
96
  工具错误数: ${toolErrorCount}
97
+ Bash 错误数: ${bashErrorCount}
321
98
  运行时长: ${uptime}秒
322
99
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
323
100
  `);
324
101
  }
325
-
326
- // Always show debug info if enabled
327
- if (process.env.DEBUG_ERRORS) {
328
- console.log(`[error-monitor] Session ended. Total errors: ${errorCount}, API errors: ${apiErrorCount}, Tool errors: ${toolErrorCount}`);
329
- }
330
102
  });
331
103
  }
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * Msg Debug Extension - Log LLM request/response messages to file
3
4
  *
4
5
  * This extension logs all requests and responses sent to/from the LLM provider
@@ -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.
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * @author: xujianjiang
2
3
  * working-message
3
4
  *
4
5
  * 自定义工作消息,格式类似 Claude Code: ✶ xxx… (时间 · tokens)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ia-ccun/code-agent-cli",
3
- "version": "0.0.47-beta.3",
3
+ "version": "0.0.47-beta.5",
4
4
  "description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent(pi)原理封装的编码智能体(供学习使用)。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -175,6 +175,7 @@ const FORCE_OVERWRITE_LIST = [
175
175
  'extensions/custom-footer.ts',
176
176
  'extensions/custom.ts',
177
177
  'extensions/bash-check.ts',
178
+ 'extensions/aicode-tips.ts',
178
179
  // Force overwrite all prompts .md files
179
180
  'prompts/*.md',
180
181
  // Force overwrite all skills SKILL.md files