@ia-ccun/code-agent-cli 0.0.53 → 0.0.54-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 (98) hide show
  1. package/config/agent/extensions/aicode-filter-outputs.ts +131 -0
  2. package/config/agent/extensions/aicode-snake.ts +354 -0
  3. package/config/agent/settings.json +14 -15
  4. package/config/agent/skills/hello/SKILL.md +1 -1
  5. package/package.json +1 -1
  6. package/config/agent/skills/refactor/SKILL.md +0 -40
  7. /package/config/agent/{skills → skills_bak}/csv-data-summarizer/SKILL.md +0 -0
  8. /package/config/agent/{skills → skills_bak}/csv-data-summarizer/analyze.py +0 -0
  9. /package/config/agent/{skills → skills_bak}/csv-data-summarizer/examples/showcase_financial_pl_data.csv +0 -0
  10. /package/config/agent/{skills → skills_bak}/csv-data-summarizer/requirements.txt +0 -0
  11. /package/config/agent/{skills → skills_bak}/csv-data-summarizer/resources/sample.csv +0 -0
  12. /package/config/agent/{skills → skills_bak}/dispatching-parallel-agents/SKILL.md +0 -0
  13. /package/config/agent/{skills → skills_bak}/find-skills/SKILL.md +0 -0
  14. /package/config/agent/{skills → skills_bak}/finishing-a-development-branch/SKILL.md +0 -0
  15. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/LICENSE +0 -0
  16. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/README.md +0 -0
  17. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/README.zh.md +0 -0
  18. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/SKILL.md +0 -0
  19. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/agentloop-core.svg +0 -0
  20. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/agents/openai.yaml +0 -0
  21. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style1-flat.png +0 -0
  22. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style2-dark.png +0 -0
  23. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
  24. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style4-notion.png +0 -0
  25. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style5-glass.png +0 -0
  26. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style6-claude.png +0 -0
  27. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style7-openai.png +0 -0
  28. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/agent-memory-types-style4.json +0 -0
  29. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/api-flow-style7.json +0 -0
  30. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/mem0-style1.json +0 -0
  31. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/microservices-style3.json +0 -0
  32. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/multi-agent-style5.json +0 -0
  33. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/system-architecture-style6.json +0 -0
  34. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/tool-call-style2.json +0 -0
  35. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/package.json +0 -0
  36. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/icons.md +0 -0
  37. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-1-flat-icon.md +0 -0
  38. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-2-dark-terminal.md +0 -0
  39. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-3-blueprint.md +0 -0
  40. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-4-notion-clean.md +0 -0
  41. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-5-glassmorphism.md +0 -0
  42. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-6-claude-official.md +0 -0
  43. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-7-openai.md +0 -0
  44. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-diagram-matrix.md +0 -0
  45. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/svg-layout-best-practices.md +0 -0
  46. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/README.md +0 -0
  47. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/generate-diagram.sh +0 -0
  48. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/generate-from-template.py +0 -0
  49. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/test-all-styles.sh +0 -0
  50. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/validate-svg.sh +0 -0
  51. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/agent-architecture.svg +0 -0
  52. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/architecture.svg +0 -0
  53. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/comparison-matrix.svg +0 -0
  54. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/data-flow.svg +0 -0
  55. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/er-diagram.svg +0 -0
  56. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/flowchart.svg +0 -0
  57. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/sequence.svg +0 -0
  58. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/state-machine.svg +0 -0
  59. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/timeline.svg +0 -0
  60. /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/use-case.svg +0 -0
  61. /package/config/agent/{skills → skills_bak}/librarian/SKILL.md +0 -0
  62. /package/config/agent/{skills → skills_bak}/markdown-to-html/SKILL.md +0 -0
  63. /package/config/agent/{skills → skills_bak}/milvus-skill/LICENSE +0 -0
  64. /package/config/agent/{skills → skills_bak}/milvus-skill/README.md +0 -0
  65. /package/config/agent/{skills → skills_bak}/milvus-skill/SKILL.md +0 -0
  66. /package/config/agent/{skills → skills_bak}/milvus-skill/references/collection.md +0 -0
  67. /package/config/agent/{skills → skills_bak}/milvus-skill/references/database.md +0 -0
  68. /package/config/agent/{skills → skills_bak}/milvus-skill/references/index.md +0 -0
  69. /package/config/agent/{skills → skills_bak}/milvus-skill/references/partition.md +0 -0
  70. /package/config/agent/{skills → skills_bak}/milvus-skill/references/patterns.md +0 -0
  71. /package/config/agent/{skills → skills_bak}/milvus-skill/references/user-role.md +0 -0
  72. /package/config/agent/{skills → skills_bak}/milvus-skill/references/vector.md +0 -0
  73. /package/config/agent/{skills → skills_bak}/pr/SKILL.md +0 -0
  74. /package/config/agent/{skills → skills_bak}/receiving-code-review/SKILL.md +0 -0
  75. /package/config/agent/{skills → skills_bak}/requesting-code-review/SKILL.md +0 -0
  76. /package/config/agent/{skills → skills_bak}/requesting-code-review/code-reviewer.md +0 -0
  77. /package/config/agent/{skills → skills_bak}/subagent-driven-development/SKILL.md +0 -0
  78. /package/config/agent/{skills → skills_bak}/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
  79. /package/config/agent/{skills → skills_bak}/subagent-driven-development/implementer-prompt.md +0 -0
  80. /package/config/agent/{skills → skills_bak}/subagent-driven-development/spec-reviewer-prompt.md +0 -0
  81. /package/config/agent/{skills → skills_bak}/systematic-debugging/CREATION-LOG.md +0 -0
  82. /package/config/agent/{skills → skills_bak}/systematic-debugging/SKILL.md +0 -0
  83. /package/config/agent/{skills → skills_bak}/systematic-debugging/condition-based-waiting-example.ts +0 -0
  84. /package/config/agent/{skills → skills_bak}/systematic-debugging/condition-based-waiting.md +0 -0
  85. /package/config/agent/{skills → skills_bak}/systematic-debugging/defense-in-depth.md +0 -0
  86. /package/config/agent/{skills → skills_bak}/systematic-debugging/find-polluter.sh +0 -0
  87. /package/config/agent/{skills → skills_bak}/systematic-debugging/root-cause-tracing.md +0 -0
  88. /package/config/agent/{skills → skills_bak}/systematic-debugging/test-academic.md +0 -0
  89. /package/config/agent/{skills → skills_bak}/systematic-debugging/test-pressure-1.md +0 -0
  90. /package/config/agent/{skills → skills_bak}/systematic-debugging/test-pressure-2.md +0 -0
  91. /package/config/agent/{skills → skills_bak}/systematic-debugging/test-pressure-3.md +0 -0
  92. /package/config/agent/{skills → skills_bak}/test-driven-development/SKILL.md +0 -0
  93. /package/config/agent/{skills → skills_bak}/test-driven-development/testing-anti-patterns.md +0 -0
  94. /package/config/agent/{skills → skills_bak}/unit-test/SKILL.md +0 -0
  95. /package/config/agent/{skills → skills_bak}/using-git-worktrees/SKILL.md +0 -0
  96. /package/config/agent/{skills → skills_bak}/zread/README.md +0 -0
  97. /package/config/agent/{skills → skills_bak}/zread/SKILL.md +0 -0
  98. /package/config/agent/{skills → skills_bak}/zread/references/stdio-protocol.md +0 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * @author: xujianjiang
3
+ * AICode 输出脱敏扩展 — 在 LLM 看到工具结果之前,对内容进行过滤和敏感信息替换
4
+ *
5
+ * 功能说明:
6
+ * - 内容级脱敏:API Key、Token、密码、JWT、数据库连接串、私钥等
7
+ * - 文件级拦截:.env、secrets、credentials 等敏感文件的读取结果整体替换
8
+ * - 错误透传:工具执行失败时不拦截,保留调试线索
9
+ * - UI 友好:TUI 模式下弹出 AICode 风格的通知提示
10
+ *
11
+ * 触发时机:tool_result 钩子(在 Pi 注入到 LLM 上下文之前生效)
12
+ */
13
+
14
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
15
+
16
+ type SensitivePattern = {
17
+ pattern: RegExp;
18
+ replacement: string;
19
+ };
20
+
21
+ function redactText(
22
+ text: string,
23
+ patterns: SensitivePattern[],
24
+ ): { text: string; modified: boolean } {
25
+ let result = text;
26
+ let modified = false;
27
+
28
+ for (const { pattern, replacement } of patterns) {
29
+ const redacted = result.replace(pattern, replacement);
30
+ if (redacted !== result) {
31
+ modified = true;
32
+ result = redacted;
33
+ }
34
+ }
35
+
36
+ return { text: result, modified };
37
+ }
38
+
39
+ /**
40
+ * 在 LLM 看到工具结果之前,对内容进行过滤或转换。
41
+ * 对 API Key、Token、密码等敏感数据进行脱敏。
42
+ */
43
+ export default function (pi: ExtensionAPI) {
44
+ const sensitivePatterns: SensitivePattern[] = [
45
+ { pattern: /\b(sk-[a-zA-Z0-9]{20,})\b/g, replacement: "[OPENAI_KEY_REDACTED]" },
46
+ { pattern: /\b(sk-ant-[a-zA-Z0-9_-]{20,})\b/g, replacement: "[ANTHROPIC_KEY_REDACTED]" },
47
+ { pattern: /\b(sk-or-v1-[a-zA-Z0-9_-]{20,})\b/g, replacement: "[OPENROUTER_KEY_REDACTED]" },
48
+ { pattern: /\b(AIza[a-zA-Z0-9_-]{30,})\b/g, replacement: "[GOOGLE_KEY_REDACTED]" },
49
+ {
50
+ pattern: /\b(cf(?:k|ut|at)_[a-zA-Z0-9_-]{41,})\b/g,
51
+ replacement: "[CLOUDFLARE_TOKEN_REDACTED]",
52
+ },
53
+ {
54
+ pattern: /\b(CLOUDFLARE_API_TOKEN|CF_API_TOKEN)\s*=\s*['"]?[a-zA-Z0-9_-]{40,}['"]?/gi,
55
+ replacement: "$1=[CLOUDFLARE_TOKEN_REDACTED]",
56
+ },
57
+ {
58
+ pattern: /\b(CLOUDFLARE_API_KEY|CF_API_KEY)\s*=\s*['"]?[a-f0-9]{37,45}['"]?/gi,
59
+ replacement: "$1=[CLOUDFLARE_KEY_REDACTED]",
60
+ },
61
+ { pattern: /\b(npm_[a-zA-Z0-9]{20,})\b/g, replacement: "[NPM_TOKEN_REDACTED]" },
62
+ { pattern: /\b(glpat-[a-zA-Z0-9_-]{20,})\b/g, replacement: "[GITLAB_TOKEN_REDACTED]" },
63
+ { pattern: /\b(gh[pousr]_[a-zA-Z0-9]{36,})\b/g, replacement: "[GITHUB_TOKEN_REDACTED]" },
64
+ { pattern: /\b(xox[baprs]-[a-zA-Z0-9-]{10,})\b/g, replacement: "[SLACK_TOKEN_REDACTED]" },
65
+ { pattern: /\b(AKIA[A-Z0-9]{16})\b/g, replacement: "[AWS_KEY_REDACTED]" },
66
+ {
67
+ pattern: /\b(api[_-]?key|apikey)\s*[=:]\s*['"]?([a-zA-Z0-9_-]{20,})['"]?/gi,
68
+ replacement: "$1=[REDACTED]",
69
+ },
70
+ {
71
+ pattern: /\b(secret|token|password|passwd|pwd)\s*[=:]\s*['"]?([^\s'"]{8,})['"]?/gi,
72
+ replacement: "$1=[REDACTED]",
73
+ },
74
+ { pattern: /\b(bearer)\s+([a-zA-Z0-9._-]{20,})\b/gi, replacement: "Bearer [REDACTED]" },
75
+ {
76
+ pattern: /\beyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}\b/g,
77
+ replacement: "[JWT_REDACTED]",
78
+ },
79
+ { pattern: /(mongodb(\+srv)?:\/\/[^:]+:)[^@]+(@)/gi, replacement: "$1[REDACTED]$3" },
80
+ { pattern: /(postgres(ql)?:\/\/[^:]+:)[^@]+(@)/gi, replacement: "$1[REDACTED]$3" },
81
+ { pattern: /(mysql:\/\/[^:]+:)[^@]+(@)/gi, replacement: "$1[REDACTED]$3" },
82
+ { pattern: /(redis:\/\/[^:]+:)[^@]+(@)/gi, replacement: "$1[REDACTED]$3" },
83
+ {
84
+ pattern:
85
+ /-----BEGIN (RSA |EC |OPENSSH |)PRIVATE KEY-----[\s\S]*?-----END \1PRIVATE KEY-----/g,
86
+ replacement: "[PRIVATE_KEY_REDACTED]",
87
+ },
88
+ ];
89
+
90
+ const sensitiveFiles = [
91
+ /(^|\/)\.env$/,
92
+ /(^|\/)\.env\.(?!example$)[^/]+$/,
93
+ /(^|\/)\.dev\.vars($|\.[^/]+$)/,
94
+ /(^|\/)secrets?\.(json|ya?ml|toml)$/i,
95
+ /(^|\/)credentials/i,
96
+ ];
97
+
98
+ pi.on("tool_result", async (event, ctx) => {
99
+ if (event.isError) return undefined;
100
+
101
+ if (event.toolName === "read" && typeof event.input.path === "string") {
102
+ const filePath = event.input.path;
103
+ if (/(^|\/)\.env\.example$/i.test(filePath)) {
104
+ return undefined;
105
+ }
106
+ for (const pattern of sensitiveFiles) {
107
+ if (pattern.test(filePath)) {
108
+ if (ctx.hasUI) ctx.ui.notify(`[AICode] 已脱敏敏感文件内容:${filePath}`, "info");
109
+ return {
110
+ content: [{ type: "text", text: `[AICode] 文件 ${filePath} 内容已脱敏,出于安全考虑不予展示` }],
111
+ };
112
+ }
113
+ }
114
+ }
115
+
116
+ let wasModified = false;
117
+ const content = event.content.map((item) => {
118
+ if (item.type !== "text") return item;
119
+ const redacted = redactText(item.text, sensitivePatterns);
120
+ if (redacted.modified) wasModified = true;
121
+ return redacted.modified ? { ...item, text: redacted.text } : item;
122
+ });
123
+
124
+ if (wasModified) {
125
+ if (ctx.hasUI) ctx.ui.notify("[AICode] 已对输出中的敏感信息进行脱敏", "info");
126
+ return { content };
127
+ }
128
+
129
+ return undefined;
130
+ });
131
+ }
@@ -0,0 +1,354 @@
1
+ /**
2
+ * @author: xujianjiang
3
+ * AICode 贪吃蛇游戏扩展 — 通过 /snake 命令在终端内玩经典贪吃蛇
4
+ *
5
+ * 功能说明:
6
+ * - 经典玩法:方向键或 WASD 控制方向,吃到食物加分
7
+ * - 状态持久化:游戏可暂停,下次执行 /snake 自动恢复上次进度
8
+ * - 最高分记录:跨会话保留历史最高分(High Score)
9
+ * - 自适应渲染:根据终端宽度自动调整游戏区域尺寸
10
+ * - 简洁控制:ESC 暂停并保存,Q 退出不保存,R/空格 重开
11
+ *
12
+ * 使用方法:
13
+ * /snake 启动贪吃蛇游戏(无参数则新建游戏或恢复上次进度)
14
+ */
15
+
16
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
17
+ import { matchesKey, visibleWidth } from "@earendil-works/pi-tui";
18
+
19
+ const GAME_WIDTH = 40;
20
+ const GAME_HEIGHT = 15;
21
+ const TICK_MS = 100;
22
+
23
+ type Direction = "up" | "down" | "left" | "right";
24
+ type Point = { x: number; y: number };
25
+
26
+ interface GameState {
27
+ snake: Point[];
28
+ food: Point;
29
+ direction: Direction;
30
+ nextDirection: Direction;
31
+ score: number;
32
+ gameOver: boolean;
33
+ highScore: number;
34
+ }
35
+
36
+ function createInitialState(): GameState {
37
+ const startX = Math.floor(GAME_WIDTH / 2);
38
+ const startY = Math.floor(GAME_HEIGHT / 2);
39
+ return {
40
+ snake: [
41
+ { x: startX, y: startY },
42
+ { x: startX - 1, y: startY },
43
+ { x: startX - 2, y: startY },
44
+ ],
45
+ food: spawnFood([{ x: startX, y: startY }]),
46
+ direction: "right",
47
+ nextDirection: "right",
48
+ score: 0,
49
+ gameOver: false,
50
+ highScore: 0,
51
+ };
52
+ }
53
+
54
+ function spawnFood(snake: Point[]): Point {
55
+ let food: Point;
56
+ do {
57
+ food = {
58
+ x: Math.floor(Math.random() * GAME_WIDTH),
59
+ y: Math.floor(Math.random() * GAME_HEIGHT),
60
+ };
61
+ } while (snake.some((s) => s.x === food.x && s.y === food.y));
62
+ return food;
63
+ }
64
+
65
+ class SnakeComponent {
66
+ private state: GameState;
67
+ private interval: ReturnType<typeof setInterval> | null = null;
68
+ private onClose: () => void;
69
+ private onSave: (state: GameState | null) => void;
70
+ private tui: { requestRender: () => void };
71
+ private cachedLines: string[] = [];
72
+ private cachedWidth = 0;
73
+ private version = 0;
74
+ private cachedVersion = -1;
75
+ private paused: boolean;
76
+
77
+ constructor(
78
+ tui: { requestRender: () => void },
79
+ onClose: () => void,
80
+ onSave: (state: GameState | null) => void,
81
+ savedState?: GameState,
82
+ ) {
83
+ this.tui = tui;
84
+ if (savedState && !savedState.gameOver) {
85
+ // Resume from saved state, start paused
86
+ this.state = savedState;
87
+ this.paused = true;
88
+ } else {
89
+ // New game or saved game was over
90
+ this.state = createInitialState();
91
+ if (savedState) {
92
+ this.state.highScore = savedState.highScore;
93
+ }
94
+ this.paused = false;
95
+ this.startGame();
96
+ }
97
+ this.onClose = onClose;
98
+ this.onSave = onSave;
99
+ }
100
+
101
+ private startGame(): void {
102
+ this.interval = setInterval(() => {
103
+ if (!this.state.gameOver) {
104
+ this.tick();
105
+ this.version++;
106
+ this.tui.requestRender();
107
+ }
108
+ }, TICK_MS);
109
+ }
110
+
111
+ private tick(): void {
112
+ // Apply queued direction change
113
+ this.state.direction = this.state.nextDirection;
114
+
115
+ // Calculate new head position
116
+ const head = this.state.snake[0];
117
+ let newHead: Point;
118
+
119
+ switch (this.state.direction) {
120
+ case "up":
121
+ newHead = { x: head.x, y: head.y - 1 };
122
+ break;
123
+ case "down":
124
+ newHead = { x: head.x, y: head.y + 1 };
125
+ break;
126
+ case "left":
127
+ newHead = { x: head.x - 1, y: head.y };
128
+ break;
129
+ case "right":
130
+ newHead = { x: head.x + 1, y: head.y };
131
+ break;
132
+ }
133
+
134
+ // Check wall collision
135
+ if (newHead.x < 0 || newHead.x >= GAME_WIDTH || newHead.y < 0 || newHead.y >= GAME_HEIGHT) {
136
+ this.state.gameOver = true;
137
+ return;
138
+ }
139
+
140
+ // Check self collision
141
+ if (this.state.snake.some((s) => s.x === newHead.x && s.y === newHead.y)) {
142
+ this.state.gameOver = true;
143
+ return;
144
+ }
145
+
146
+ // Move snake
147
+ this.state.snake.unshift(newHead);
148
+
149
+ // Check food collision
150
+ if (newHead.x === this.state.food.x && newHead.y === this.state.food.y) {
151
+ this.state.score += 10;
152
+ if (this.state.score > this.state.highScore) {
153
+ this.state.highScore = this.state.score;
154
+ }
155
+ this.state.food = spawnFood(this.state.snake);
156
+ } else {
157
+ this.state.snake.pop();
158
+ }
159
+ }
160
+
161
+ handleInput(data: string): void {
162
+ // If paused (resuming), wait for any key
163
+ if (this.paused) {
164
+ if (matchesKey(data, "escape") || data === "q" || data === "Q") {
165
+ // Quit without clearing save
166
+ this.dispose();
167
+ this.onClose();
168
+ return;
169
+ }
170
+ // Any other key resumes
171
+ this.paused = false;
172
+ this.startGame();
173
+ return;
174
+ }
175
+
176
+ // ESC to pause and save
177
+ if (matchesKey(data, "escape")) {
178
+ this.dispose();
179
+ this.onSave(this.state);
180
+ this.onClose();
181
+ return;
182
+ }
183
+
184
+ // Q to quit without saving (clears saved state)
185
+ if (data === "q" || data === "Q") {
186
+ this.dispose();
187
+ this.onSave(null); // Clear saved state
188
+ this.onClose();
189
+ return;
190
+ }
191
+
192
+ // Arrow keys or WASD
193
+ if (matchesKey(data, "up") || data === "w" || data === "W") {
194
+ if (this.state.direction !== "down") this.state.nextDirection = "up";
195
+ } else if (matchesKey(data, "down") || data === "s" || data === "S") {
196
+ if (this.state.direction !== "up") this.state.nextDirection = "down";
197
+ } else if (matchesKey(data, "right") || data === "d" || data === "D") {
198
+ if (this.state.direction !== "left") this.state.nextDirection = "right";
199
+ } else if (matchesKey(data, "left") || data === "a" || data === "A") {
200
+ if (this.state.direction !== "right") this.state.nextDirection = "left";
201
+ }
202
+
203
+ // Restart on game over
204
+ if (this.state.gameOver && (data === "r" || data === "R" || data === " ")) {
205
+ const highScore = this.state.highScore;
206
+ this.state = createInitialState();
207
+ this.state.highScore = highScore;
208
+ this.onSave(null); // Clear saved state on restart
209
+ this.version++;
210
+ this.tui.requestRender();
211
+ }
212
+ }
213
+
214
+ invalidate(): void {
215
+ this.cachedWidth = 0;
216
+ }
217
+
218
+ render(width: number): string[] {
219
+ if (width === this.cachedWidth && this.cachedVersion === this.version) {
220
+ return this.cachedLines;
221
+ }
222
+
223
+ const lines: string[] = [];
224
+
225
+ // Each game cell is 2 chars wide to appear square (terminal cells are ~2:1 aspect)
226
+ const cellWidth = 2;
227
+ const effectiveWidth = Math.min(GAME_WIDTH, Math.floor((width - 4) / cellWidth));
228
+ const effectiveHeight = GAME_HEIGHT;
229
+
230
+ // Colors
231
+ const dim = (s: string) => `\x1b[2m${s}\x1b[22m`;
232
+ const green = (s: string) => `\x1b[32m${s}\x1b[0m`;
233
+ const red = (s: string) => `\x1b[31m${s}\x1b[0m`;
234
+ const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`;
235
+ const bold = (s: string) => `\x1b[1m${s}\x1b[22m`;
236
+
237
+ const boxWidth = effectiveWidth * cellWidth;
238
+
239
+ // Helper to pad content inside box
240
+ const boxLine = (content: string) => {
241
+ const contentLen = visibleWidth(content);
242
+ const padding = Math.max(0, boxWidth - contentLen);
243
+ return dim(" │") + content + " ".repeat(padding) + dim("│");
244
+ };
245
+
246
+ // Top border
247
+ lines.push(this.padLine(dim(` ╭${"─".repeat(boxWidth)}╮`), width));
248
+
249
+ // Header with score
250
+ const scoreText = `Score: ${bold(yellow(String(this.state.score)))}`;
251
+ const highText = `High: ${bold(yellow(String(this.state.highScore)))}`;
252
+ const title = `${bold(green("SNAKE"))} │ ${scoreText} │ ${highText}`;
253
+ lines.push(this.padLine(boxLine(title), width));
254
+
255
+ // Separator
256
+ lines.push(this.padLine(dim(` ├${"─".repeat(boxWidth)}┤`), width));
257
+
258
+ // Game grid
259
+ for (let y = 0; y < effectiveHeight; y++) {
260
+ let row = "";
261
+ for (let x = 0; x < effectiveWidth; x++) {
262
+ const isHead = this.state.snake[0].x === x && this.state.snake[0].y === y;
263
+ const isBody = this.state.snake.slice(1).some((s) => s.x === x && s.y === y);
264
+ const isFood = this.state.food.x === x && this.state.food.y === y;
265
+
266
+ if (isHead) {
267
+ row += green("██"); // Snake head (2 chars)
268
+ } else if (isBody) {
269
+ row += green("▓▓"); // Snake body (2 chars)
270
+ } else if (isFood) {
271
+ row += red("◆ "); // Food (2 chars)
272
+ } else {
273
+ row += " "; // Empty cell (2 spaces)
274
+ }
275
+ }
276
+ lines.push(this.padLine(dim(" │") + row + dim("│"), width));
277
+ }
278
+
279
+ // Separator
280
+ lines.push(this.padLine(dim(` ├${"─".repeat(boxWidth)}┤`), width));
281
+
282
+ // Footer
283
+ let footer: string;
284
+ if (this.paused) {
285
+ footer = `${yellow(bold("PAUSED"))} Press any key to continue, ${bold("Q")} to quit`;
286
+ } else if (this.state.gameOver) {
287
+ footer = `${red(bold("GAME OVER!"))} Press ${bold("R")} to restart, ${bold("Q")} to quit`;
288
+ } else {
289
+ footer = `↑↓←→ or WASD to move, ${bold("ESC")} pause, ${bold("Q")} quit`;
290
+ }
291
+ lines.push(this.padLine(boxLine(footer), width));
292
+
293
+ // Bottom border
294
+ lines.push(this.padLine(dim(` ╰${"─".repeat(boxWidth)}╯`), width));
295
+
296
+ this.cachedLines = lines;
297
+ this.cachedWidth = width;
298
+ this.cachedVersion = this.version;
299
+
300
+ return lines;
301
+ }
302
+
303
+ private padLine(line: string, width: number): string {
304
+ // Calculate visible length (strip ANSI codes)
305
+ const visibleLen = line.replace(/\x1b\[[0-9;]*m/g, "").length;
306
+ const padding = Math.max(0, width - visibleLen);
307
+ return line + " ".repeat(padding);
308
+ }
309
+
310
+ dispose(): void {
311
+ if (this.interval) {
312
+ clearInterval(this.interval);
313
+ this.interval = null;
314
+ }
315
+ }
316
+ }
317
+
318
+ const SNAKE_SAVE_TYPE = "snake-save";
319
+
320
+ export default function (pi: ExtensionAPI) {
321
+ pi.registerCommand("snake", {
322
+ description: "AICode 贪吃蛇游戏",
323
+
324
+ handler: async (_args, ctx) => {
325
+ if (!ctx.hasUI) {
326
+ ctx.ui.notify("[AICode] 贪吃蛇需要交互模式才能运行", "error");
327
+ return;
328
+ }
329
+
330
+ // Load saved state from session
331
+ const entries = ctx.sessionManager.getEntries();
332
+ let savedState: GameState | undefined;
333
+ for (let i = entries.length - 1; i >= 0; i--) {
334
+ const entry = entries[i];
335
+ if (entry.type === "custom" && entry.customType === SNAKE_SAVE_TYPE) {
336
+ savedState = entry.data as GameState;
337
+ break;
338
+ }
339
+ }
340
+
341
+ await ctx.ui.custom((tui, _theme, _kb, done) => {
342
+ return new SnakeComponent(
343
+ tui,
344
+ () => done(undefined),
345
+ (state) => {
346
+ // Save or clear state
347
+ pi.appendEntry(SNAKE_SAVE_TYPE, state);
348
+ },
349
+ savedState,
350
+ );
351
+ });
352
+ },
353
+ });
354
+ }
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "quietStartup":true,
3
3
  "defaultProvider": "aicode",
4
- "defaultModel": "qwen3.5-35b-a3b",
4
+ "defaultModel": "qwen3.6-35b-a3b",
5
5
  "theme":"light",
6
6
  "extensions":[
7
- "~/.aicode-cli/agent/extensions/bash-check.ts",
8
7
  "~/.aicode-cli/agent/extensions/context-monitor.ts",
9
8
  "~/.aicode-cli/agent/extensions/context.ts",
10
9
  "~/.aicode-cli/agent/extensions/custom-footer.ts",
@@ -14,43 +13,43 @@
14
13
  "-extensions/aicode-pets.ts"
15
14
  ],
16
15
  "packages":[
17
- "npm:pi-subagents@0.17.1",
18
- "npm:pi-rewind@0.5.0",
19
- "npm:pi-mcp-adapter@2.4.0",
16
+ "npm:pi-subagents",
17
+ "npm:pi-rewind",
18
+ "npm:pi-mcp-adapter",
20
19
  "npm:@juicesharp/rpiv-todo",
21
20
  "npm:@juicesharp/rpiv-ask-user-question",
22
- "npm:@samfp/pi-memory@1.1.1"
21
+ "npm:@samfp/pi-memor"
23
22
  ],
24
23
  "memory": {
25
24
  "localPath": "~/.aicode-cli/memory",
26
25
  "lessonInjection": "selective",
27
- "consolidationModel": "qwen3.5-35b-a3b"
26
+ "consolidationModel": "qwen3.6-35b-a3b"
28
27
  },
29
28
  "subagents": {
30
29
  "agentOverrides": {
31
30
  "scout": {
32
- "model": "qwen3.5-35b-a3b"
31
+ "model": "qwen3.6-35b-a3b"
33
32
  },
34
33
  "researcher": {
35
- "model": "qwen3.5-35b-a3b"
34
+ "model": "qwen3.6-35b-a3b"
36
35
  },
37
36
  "planner": {
38
- "model": "qwen3.5-35b-a3b"
37
+ "model": "qwen3.6-35b-a3b"
39
38
  },
40
39
  "worker": {
41
- "model": "qwen3.5-35b-a3b"
40
+ "model": "qwen3.6-35b-a3b"
42
41
  },
43
42
  "reviewer": {
44
- "model": "qwen3.5-35b-a3b"
43
+ "model": "qwen3.6-35b-a3b"
45
44
  },
46
45
  "context-builder": {
47
- "model": "qwen3.5-35b-a3b"
46
+ "model": "qwen3.6-35b-a3b"
48
47
  },
49
48
  "oracle": {
50
- "model": "qwen3.5-35b-a3b"
49
+ "model": "qwen3.6-35b-a3b"
51
50
  },
52
51
  "delegate": {
53
- "model": "qwen3.5-35b-a3b"
52
+ "model": "qwen3.6-35b-a3b"
54
53
  }
55
54
  }
56
55
  }
@@ -1,4 +1,4 @@
1
- ---
1
+ ---
2
2
  name: hello
3
3
  description: "Custom skill example. Use when testing skill functionality or as a template for creating new skills."
4
4
  disable-model-invocation: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ia-ccun/code-agent-cli",
3
- "version": "0.0.53",
3
+ "version": "0.0.54-beta.1",
4
4
  "description": "AI Coding Agent CLI - 基于OpenClaw🦞底层Agent原理封装的编码智能体(供学习使用)。",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,40 +0,0 @@
1
- ---
2
- name: refactor
3
- description: "对代码进行重构,提高可读性和可维护性,同时保持功能不变。Use when refactoring code, improving code readability, or optimizing code structure."
4
- disable-model-invocation: true
5
- metadata:
6
- version: "1.0.0"
7
- author: "xujianjiang"
8
- ---
9
-
10
- # 重构代码 Skill
11
-
12
- ## 描述
13
- 对代码进行重构,提高可读性和可维护性,同时保持功能不变。
14
-
15
- ## 触发方式
16
- `/skill:refactor`
17
-
18
- ## 执行步骤
19
-
20
- 1. **分析现有代码**
21
- - 读取目标文件
22
- - 识别代码异味 (code smells)
23
- - 找出可复用的代码
24
-
25
- 2. **执行重构**
26
- - 提取重复代码为函数
27
- - 简化复杂的条件逻辑
28
- - 重命名变量/函数使其更清晰
29
- - 优化类结构
30
- - 添加适当的注释
31
-
32
- 3. **验证功能**
33
- - 确保重构后代码功能不变
34
- - 运行现有测试确保通过
35
-
36
- ## 重构原则
37
- - 保持功能不变
38
- - 每次只做一个小改动
39
- - 提交前运行测试
40
- - 优先处理高风险/高价值部分
File without changes