@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.
- package/config/agent/extensions/aicode-filter-outputs.ts +131 -0
- package/config/agent/extensions/aicode-snake.ts +354 -0
- package/config/agent/settings.json +14 -15
- package/config/agent/skills/hello/SKILL.md +1 -1
- package/package.json +1 -1
- package/config/agent/skills/refactor/SKILL.md +0 -40
- /package/config/agent/{skills → skills_bak}/csv-data-summarizer/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/csv-data-summarizer/analyze.py +0 -0
- /package/config/agent/{skills → skills_bak}/csv-data-summarizer/examples/showcase_financial_pl_data.csv +0 -0
- /package/config/agent/{skills → skills_bak}/csv-data-summarizer/requirements.txt +0 -0
- /package/config/agent/{skills → skills_bak}/csv-data-summarizer/resources/sample.csv +0 -0
- /package/config/agent/{skills → skills_bak}/dispatching-parallel-agents/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/find-skills/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/finishing-a-development-branch/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/LICENSE +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/README.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/README.zh.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/agentloop-core.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/agents/openai.yaml +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style1-flat.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style2-dark.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style4-notion.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style5-glass.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style6-claude.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/assets/samples/sample-style7-openai.png +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/agent-memory-types-style4.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/api-flow-style7.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/mem0-style1.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/microservices-style3.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/multi-agent-style5.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/system-architecture-style6.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/tool-call-style2.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/package.json +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/icons.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-1-flat-icon.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-2-dark-terminal.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-3-blueprint.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-4-notion-clean.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-5-glassmorphism.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-6-claude-official.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-7-openai.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-diagram-matrix.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/svg-layout-best-practices.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/README.md +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/generate-diagram.sh +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/generate-from-template.py +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/test-all-styles.sh +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/validate-svg.sh +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/agent-architecture.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/architecture.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/comparison-matrix.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/data-flow.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/er-diagram.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/flowchart.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/sequence.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/state-machine.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/timeline.svg +0 -0
- /package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/use-case.svg +0 -0
- /package/config/agent/{skills → skills_bak}/librarian/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/markdown-to-html/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/LICENSE +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/README.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/collection.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/database.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/index.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/partition.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/patterns.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/user-role.md +0 -0
- /package/config/agent/{skills → skills_bak}/milvus-skill/references/vector.md +0 -0
- /package/config/agent/{skills → skills_bak}/pr/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/receiving-code-review/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/requesting-code-review/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/requesting-code-review/code-reviewer.md +0 -0
- /package/config/agent/{skills → skills_bak}/subagent-driven-development/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
- /package/config/agent/{skills → skills_bak}/subagent-driven-development/implementer-prompt.md +0 -0
- /package/config/agent/{skills → skills_bak}/subagent-driven-development/spec-reviewer-prompt.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/CREATION-LOG.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/condition-based-waiting-example.ts +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/condition-based-waiting.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/defense-in-depth.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/find-polluter.sh +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/root-cause-tracing.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/test-academic.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/test-pressure-1.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/test-pressure-2.md +0 -0
- /package/config/agent/{skills → skills_bak}/systematic-debugging/test-pressure-3.md +0 -0
- /package/config/agent/{skills → skills_bak}/test-driven-development/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/test-driven-development/testing-anti-patterns.md +0 -0
- /package/config/agent/{skills → skills_bak}/unit-test/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/using-git-worktrees/SKILL.md +0 -0
- /package/config/agent/{skills → skills_bak}/zread/README.md +0 -0
- /package/config/agent/{skills → skills_bak}/zread/SKILL.md +0 -0
- /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.
|
|
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
|
|
18
|
-
"npm:pi-rewind
|
|
19
|
-
"npm:pi-mcp-adapter
|
|
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-
|
|
21
|
+
"npm:@samfp/pi-memor"
|
|
23
22
|
],
|
|
24
23
|
"memory": {
|
|
25
24
|
"localPath": "~/.aicode-cli/memory",
|
|
26
25
|
"lessonInjection": "selective",
|
|
27
|
-
"consolidationModel": "qwen3.
|
|
26
|
+
"consolidationModel": "qwen3.6-35b-a3b"
|
|
28
27
|
},
|
|
29
28
|
"subagents": {
|
|
30
29
|
"agentOverrides": {
|
|
31
30
|
"scout": {
|
|
32
|
-
"model": "qwen3.
|
|
31
|
+
"model": "qwen3.6-35b-a3b"
|
|
33
32
|
},
|
|
34
33
|
"researcher": {
|
|
35
|
-
"model": "qwen3.
|
|
34
|
+
"model": "qwen3.6-35b-a3b"
|
|
36
35
|
},
|
|
37
36
|
"planner": {
|
|
38
|
-
"model": "qwen3.
|
|
37
|
+
"model": "qwen3.6-35b-a3b"
|
|
39
38
|
},
|
|
40
39
|
"worker": {
|
|
41
|
-
"model": "qwen3.
|
|
40
|
+
"model": "qwen3.6-35b-a3b"
|
|
42
41
|
},
|
|
43
42
|
"reviewer": {
|
|
44
|
-
"model": "qwen3.
|
|
43
|
+
"model": "qwen3.6-35b-a3b"
|
|
45
44
|
},
|
|
46
45
|
"context-builder": {
|
|
47
|
-
"model": "qwen3.
|
|
46
|
+
"model": "qwen3.6-35b-a3b"
|
|
48
47
|
},
|
|
49
48
|
"oracle": {
|
|
50
|
-
"model": "qwen3.
|
|
49
|
+
"model": "qwen3.6-35b-a3b"
|
|
51
50
|
},
|
|
52
51
|
"delegate": {
|
|
53
|
-
"model": "qwen3.
|
|
52
|
+
"model": "qwen3.6-35b-a3b"
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
}
|
package/package.json
CHANGED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/api-flow-style7.json
RENAMED
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/microservices-style3.json
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/multi-agent-style5.json
RENAMED
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/fixtures/tool-call-style2.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-1-flat-icon.md
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-2-dark-terminal.md
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-3-blueprint.md
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-4-notion-clean.md
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-5-glassmorphism.md
RENAMED
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-7-openai.md
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/references/style-diagram-matrix.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/generate-diagram.sh
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/scripts/generate-from-template.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/agent-architecture.svg
RENAMED
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/comparison-matrix.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/fireworks-tech-graph/templates/state-machine.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/subagent-driven-development/implementer-prompt.md
RENAMED
|
File without changes
|
/package/config/agent/{skills → skills_bak}/subagent-driven-development/spec-reviewer-prompt.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/systematic-debugging/condition-based-waiting-example.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/config/agent/{skills → skills_bak}/test-driven-development/testing-anti-patterns.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|