@ia-ccun/code-agent-cli 0.0.54-beta.1 → 0.0.54-beta.3

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 (142) hide show
  1. package/bin/cli.js +97 -69
  2. package/config/agent/extensions/aicode-advisor.ts +187 -64
  3. package/config/agent/extensions/aicode-banner.ts +101 -0
  4. package/config/agent/extensions/aicode-btw.ts +619 -182
  5. package/config/agent/extensions/aicode-cd.ts +246 -63
  6. package/config/agent/extensions/aicode-pets.ts +0 -11
  7. package/config/agent/extensions/aicode-plan.ts +2 -0
  8. package/config/agent/extensions/aicode-safety-guard.ts +3 -0
  9. package/config/agent/extensions/aicode-tips.ts +1 -16
  10. package/config/agent/extensions/context-monitor.ts +30 -15
  11. package/config/agent/extensions/context.ts +132 -60
  12. package/config/agent/extensions/custom-footer.ts +0 -5
  13. package/config/agent/extensions/custom.ts +41 -29
  14. package/config/agent/extensions/error-monitor.ts +37 -33
  15. package/config/agent/extensions/goal.ts +8 -6
  16. package/config/agent/extensions/msg-debug.ts +103 -112
  17. package/config/agent/extensions/review.ts +365 -164
  18. package/config/agent/extensions/working-msg.ts +2 -4
  19. package/config/agent/settings.json +15 -14
  20. package/config/agent/skills/hello/SKILL.md +1 -1
  21. package/config/agent/skills/refactor/SKILL.md +40 -0
  22. package/dist/banner.d.ts.map +1 -1
  23. package/dist/banner.js +8 -1
  24. package/dist/banner.js.map +1 -1
  25. package/dist/config-loader.d.ts.map +1 -1
  26. package/dist/config-loader.js +9 -2
  27. package/dist/config-loader.js.map +1 -1
  28. package/package.json +6 -7
  29. package/scripts/regression/README.md +224 -0
  30. package/scripts/regression/cases/01-version-and-deps.sh +94 -0
  31. package/scripts/regression/cases/02-extension-loading.sh +130 -0
  32. package/scripts/regression/cases/03-rpc-protocol.sh +259 -0
  33. package/scripts/regression/cases/04-prompt-stream.sh +130 -0
  34. package/scripts/regression/cases/05-slash-commands-and-skills.sh +119 -0
  35. package/scripts/regression/cases/06-tool-execution.sh +115 -0
  36. package/scripts/regression/cases/07-pi-new-features.sh +141 -0
  37. package/scripts/regression/lib/common.sh +287 -0
  38. package/scripts/regression/results/.last_pid +1 -0
  39. package/scripts/regression/results/.last_workdir +1 -0
  40. package/scripts/regression/results/02-extension-loading.log +21 -0
  41. package/scripts/regression/results/03-prompt-stream.log +16 -0
  42. package/scripts/regression/results/03-rpc-protocol.log +27 -0
  43. package/scripts/regression/results/04-slash-commands-and-skills.log +11 -0
  44. package/scripts/regression/results/05-tool-execution.log +8 -0
  45. package/scripts/regression/results/06-pi-new-features.log +27 -0
  46. package/scripts/regression/run-all.sh +187 -0
  47. package/config/agent/bin/fd.exe +0 -0
  48. package/config/agent/bin/rg.exe +0 -0
  49. package/config/agent/extensions/aicode-filter-outputs.ts +0 -131
  50. package/config/agent/extensions/aicode-snake.ts +0 -354
  51. /package/config/agent/{skills_bak → skills}/csv-data-summarizer/SKILL.md +0 -0
  52. /package/config/agent/{skills_bak → skills}/csv-data-summarizer/analyze.py +0 -0
  53. /package/config/agent/{skills_bak → skills}/csv-data-summarizer/examples/showcase_financial_pl_data.csv +0 -0
  54. /package/config/agent/{skills_bak → skills}/csv-data-summarizer/requirements.txt +0 -0
  55. /package/config/agent/{skills_bak → skills}/csv-data-summarizer/resources/sample.csv +0 -0
  56. /package/config/agent/{skills_bak → skills}/dispatching-parallel-agents/SKILL.md +0 -0
  57. /package/config/agent/{skills_bak → skills}/find-skills/SKILL.md +0 -0
  58. /package/config/agent/{skills_bak → skills}/finishing-a-development-branch/SKILL.md +0 -0
  59. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/LICENSE +0 -0
  60. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/README.md +0 -0
  61. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/README.zh.md +0 -0
  62. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/SKILL.md +0 -0
  63. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/agentloop-core.svg +0 -0
  64. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/agents/openai.yaml +0 -0
  65. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style1-flat.png +0 -0
  66. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style2-dark.png +0 -0
  67. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
  68. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style4-notion.png +0 -0
  69. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style5-glass.png +0 -0
  70. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style6-claude.png +0 -0
  71. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/assets/samples/sample-style7-openai.png +0 -0
  72. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/agent-memory-types-style4.json +0 -0
  73. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/api-flow-style7.json +0 -0
  74. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/mem0-style1.json +0 -0
  75. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/microservices-style3.json +0 -0
  76. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/multi-agent-style5.json +0 -0
  77. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/system-architecture-style6.json +0 -0
  78. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/fixtures/tool-call-style2.json +0 -0
  79. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/package.json +0 -0
  80. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/icons.md +0 -0
  81. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-1-flat-icon.md +0 -0
  82. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-2-dark-terminal.md +0 -0
  83. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-3-blueprint.md +0 -0
  84. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-4-notion-clean.md +0 -0
  85. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-5-glassmorphism.md +0 -0
  86. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-6-claude-official.md +0 -0
  87. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-7-openai.md +0 -0
  88. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/style-diagram-matrix.md +0 -0
  89. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/references/svg-layout-best-practices.md +0 -0
  90. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/README.md +0 -0
  91. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/generate-diagram.sh +0 -0
  92. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/generate-from-template.py +0 -0
  93. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/test-all-styles.sh +0 -0
  94. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/scripts/validate-svg.sh +0 -0
  95. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/agent-architecture.svg +0 -0
  96. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/architecture.svg +0 -0
  97. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/comparison-matrix.svg +0 -0
  98. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/data-flow.svg +0 -0
  99. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/er-diagram.svg +0 -0
  100. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/flowchart.svg +0 -0
  101. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/sequence.svg +0 -0
  102. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/state-machine.svg +0 -0
  103. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/timeline.svg +0 -0
  104. /package/config/agent/{skills_bak → skills}/fireworks-tech-graph/templates/use-case.svg +0 -0
  105. /package/config/agent/{skills_bak → skills}/librarian/SKILL.md +0 -0
  106. /package/config/agent/{skills_bak → skills}/markdown-to-html/SKILL.md +0 -0
  107. /package/config/agent/{skills_bak → skills}/milvus-skill/LICENSE +0 -0
  108. /package/config/agent/{skills_bak → skills}/milvus-skill/README.md +0 -0
  109. /package/config/agent/{skills_bak → skills}/milvus-skill/SKILL.md +0 -0
  110. /package/config/agent/{skills_bak → skills}/milvus-skill/references/collection.md +0 -0
  111. /package/config/agent/{skills_bak → skills}/milvus-skill/references/database.md +0 -0
  112. /package/config/agent/{skills_bak → skills}/milvus-skill/references/index.md +0 -0
  113. /package/config/agent/{skills_bak → skills}/milvus-skill/references/partition.md +0 -0
  114. /package/config/agent/{skills_bak → skills}/milvus-skill/references/patterns.md +0 -0
  115. /package/config/agent/{skills_bak → skills}/milvus-skill/references/user-role.md +0 -0
  116. /package/config/agent/{skills_bak → skills}/milvus-skill/references/vector.md +0 -0
  117. /package/config/agent/{skills_bak → skills}/pr/SKILL.md +0 -0
  118. /package/config/agent/{skills_bak → skills}/receiving-code-review/SKILL.md +0 -0
  119. /package/config/agent/{skills_bak → skills}/requesting-code-review/SKILL.md +0 -0
  120. /package/config/agent/{skills_bak → skills}/requesting-code-review/code-reviewer.md +0 -0
  121. /package/config/agent/{skills_bak → skills}/subagent-driven-development/SKILL.md +0 -0
  122. /package/config/agent/{skills_bak → skills}/subagent-driven-development/code-quality-reviewer-prompt.md +0 -0
  123. /package/config/agent/{skills_bak → skills}/subagent-driven-development/implementer-prompt.md +0 -0
  124. /package/config/agent/{skills_bak → skills}/subagent-driven-development/spec-reviewer-prompt.md +0 -0
  125. /package/config/agent/{skills_bak → skills}/systematic-debugging/CREATION-LOG.md +0 -0
  126. /package/config/agent/{skills_bak → skills}/systematic-debugging/SKILL.md +0 -0
  127. /package/config/agent/{skills_bak → skills}/systematic-debugging/condition-based-waiting-example.ts +0 -0
  128. /package/config/agent/{skills_bak → skills}/systematic-debugging/condition-based-waiting.md +0 -0
  129. /package/config/agent/{skills_bak → skills}/systematic-debugging/defense-in-depth.md +0 -0
  130. /package/config/agent/{skills_bak → skills}/systematic-debugging/find-polluter.sh +0 -0
  131. /package/config/agent/{skills_bak → skills}/systematic-debugging/root-cause-tracing.md +0 -0
  132. /package/config/agent/{skills_bak → skills}/systematic-debugging/test-academic.md +0 -0
  133. /package/config/agent/{skills_bak → skills}/systematic-debugging/test-pressure-1.md +0 -0
  134. /package/config/agent/{skills_bak → skills}/systematic-debugging/test-pressure-2.md +0 -0
  135. /package/config/agent/{skills_bak → skills}/systematic-debugging/test-pressure-3.md +0 -0
  136. /package/config/agent/{skills_bak → skills}/test-driven-development/SKILL.md +0 -0
  137. /package/config/agent/{skills_bak → skills}/test-driven-development/testing-anti-patterns.md +0 -0
  138. /package/config/agent/{skills_bak → skills}/unit-test/SKILL.md +0 -0
  139. /package/config/agent/{skills_bak → skills}/using-git-worktrees/SKILL.md +0 -0
  140. /package/config/agent/{skills_bak → skills}/zread/README.md +0 -0
  141. /package/config/agent/{skills_bak → skills}/zread/SKILL.md +0 -0
  142. /package/config/agent/{skills_bak → skills}/zread/references/stdio-protocol.md +0 -0
@@ -14,12 +14,14 @@
14
14
  * - 100% : 阻止继续并强制提示压缩
15
15
  */
16
16
 
17
- import type { ExtensionAPI, ExtensionContext, ToolResultEvent } from "@earendil-works/pi-coding-agent";
18
-
17
+ import type {
18
+ ExtensionAPI,
19
+ ExtensionContext,
20
+ } from "@earendil-works/pi-coding-agent";
19
21
  interface ContextThresholds {
20
- info: number; // 70%
22
+ info: number; // 70%
21
23
  warning: number; // 80%
22
- error: number; // 90%
24
+ error: number; // 90%
23
25
  critical: number; // 100%
24
26
  }
25
27
 
@@ -50,7 +52,9 @@ function formatTokens(n: number): string {
50
52
  return `${(n / 1000000).toFixed(1)}M`;
51
53
  }
52
54
 
53
- function getContextLevel(percent: number): "normal" | "info" | "warning" | "error" | "critical" {
55
+ function getContextLevel(
56
+ percent: number,
57
+ ): "normal" | "info" | "warning" | "error" | "critical" {
54
58
  if (percent >= DEFAULT_THRESHOLDS.critical) return "critical";
55
59
  if (percent >= DEFAULT_THRESHOLDS.error) return "error";
56
60
  if (percent >= DEFAULT_THRESHOLDS.warning) return "warning";
@@ -58,7 +62,11 @@ function getContextLevel(percent: number): "normal" | "info" | "warning" | "erro
58
62
  return "normal";
59
63
  }
60
64
 
61
- function getContextMessage(stats: ContextStats): { level: "info" | "warning" | "error"; title: string; body: string } {
65
+ function getContextMessage(stats: ContextStats): {
66
+ level: "info" | "warning" | "error" | "normal" | "critical";
67
+ title: string;
68
+ body: string;
69
+ } {
62
70
  const level = getContextLevel(stats.percent);
63
71
  const contextBar = renderContextBar(stats.percent);
64
72
 
@@ -97,7 +105,11 @@ function renderContextBar(percent: number, width: number = 20): string {
97
105
  const filledChar = "█";
98
106
  const emptyChar = "░";
99
107
 
100
- return filledChar.repeat(filled) + emptyChar.repeat(empty) + ` ${percent.toFixed(1)}%`;
108
+ return (
109
+ filledChar.repeat(filled) +
110
+ emptyChar.repeat(empty) +
111
+ ` ${percent.toFixed(1)}%`
112
+ );
101
113
  }
102
114
 
103
115
  export default function contextMonitorExtension(pi: ExtensionAPI) {
@@ -127,9 +139,12 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
127
139
  pi.getActiveTools().reduce((sum, name) => {
128
140
  const info = pi.getAllTools().find((t) => t.name === name);
129
141
  return sum + estimateTokens(`${name}\n${info?.description || ""}`);
130
- }, 0) * 1.5
142
+ }, 0) * 1.5,
143
+ );
144
+ const conversationTokens = Math.max(
145
+ 0,
146
+ usedTokens - systemTokens - toolsTokens,
131
147
  );
132
- const conversationTokens = Math.max(0, usedTokens - systemTokens - toolsTokens);
133
148
 
134
149
  return {
135
150
  contextWindow,
@@ -145,7 +160,10 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
145
160
  /**
146
161
  * 处理上下文警告
147
162
  */
148
- async function handleContextWarning(stats: ContextStats, ctx: ExtensionContext) {
163
+ async function handleContextWarning(
164
+ stats: ContextStats,
165
+ ctx: ExtensionContext,
166
+ ) {
149
167
  if (!autoWarnEnabled) return;
150
168
 
151
169
  const level = getContextLevel(stats.percent);
@@ -180,7 +198,7 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
180
198
  });
181
199
 
182
200
  // 监听会话开始,初始化
183
- pi.on("session_start", async (_event, ctx: ExtensionContext) => {
201
+ pi.on("session_start", async (_event, _ctx: ExtensionContext) => {
184
202
  lastWarnedLevel = "normal";
185
203
  warnCooldown = 0;
186
204
  });
@@ -190,7 +208,6 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
190
208
  */
191
209
  pi.registerCommand("context-stats", {
192
210
  description: "显示当前上下文详细统计",
193
- usage: "/context-stats",
194
211
  handler: async (_args, ctx: ExtensionContext) => {
195
212
  const stats = getContextStats(ctx);
196
213
 
@@ -200,7 +217,6 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
200
217
  }
201
218
 
202
219
  const message = getContextMessage(stats);
203
- const lines = message.body.split("\n");
204
220
 
205
221
  // 构建详细统计文本
206
222
  const modelName = ctx.model?.name || "unknown";
@@ -244,7 +260,7 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
244
260
  content: fullText,
245
261
  display: true,
246
262
  },
247
- { triggerTurn: false }
263
+ { triggerTurn: false },
248
264
  );
249
265
 
250
266
  // 如果超过阈值也显示通知
@@ -259,7 +275,6 @@ export default function contextMonitorExtension(pi: ExtensionAPI) {
259
275
  */
260
276
  pi.registerCommand("context-warn", {
261
277
  description: "开启/关闭上下文警告",
262
- usage: "/context-warn [on|off]",
263
278
  handler: async (args, ctx: ExtensionContext) => {
264
279
  if (!args || args === "on") {
265
280
  autoWarnEnabled = true;
@@ -9,9 +9,21 @@
9
9
  * - current context window usage + session totals (tokens/cost)
10
10
  */
11
11
 
12
- import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext, ToolResultEvent } from "@earendil-works/pi-coding-agent";
12
+ import type {
13
+ ExtensionAPI,
14
+ ExtensionCommandContext,
15
+ ExtensionContext,
16
+ ToolResultEvent,
17
+ } from "@earendil-works/pi-coding-agent";
13
18
  import { DynamicBorder } from "@earendil-works/pi-coding-agent";
14
- import { Container, Key, Text, matchesKey, type Component, type TUI } from "@earendil-works/pi-tui";
19
+ import {
20
+ Container,
21
+ Key,
22
+ Text,
23
+ matchesKey,
24
+ type Component,
25
+ type TUI,
26
+ } from "@earendil-works/pi-tui";
15
27
  import os from "node:os";
16
28
  import path from "node:path";
17
29
  import fs from "node:fs/promises";
@@ -66,17 +78,25 @@ function getAgentDir(): string {
66
78
  return path.join(os.homedir(), ".pi", "agent");
67
79
  }
68
80
 
69
- async function readFileIfExists(filePath: string): Promise<{ path: string; content: string; bytes: number } | null> {
81
+ async function readFileIfExists(
82
+ filePath: string,
83
+ ): Promise<{ path: string; content: string; bytes: number } | null> {
70
84
  if (!existsSync(filePath)) return null;
71
85
  try {
72
86
  const buf = await fs.readFile(filePath);
73
- return { path: filePath, content: buf.toString("utf8"), bytes: buf.byteLength };
87
+ return {
88
+ path: filePath,
89
+ content: buf.toString("utf8"),
90
+ bytes: buf.byteLength,
91
+ };
74
92
  } catch {
75
93
  return null;
76
94
  }
77
95
  }
78
96
 
79
- async function loadProjectContextFiles(cwd: string): Promise<Array<{ path: string; tokens: number; bytes: number }>> {
97
+ async function loadProjectContextFiles(
98
+ cwd: string,
99
+ ): Promise<Array<{ path: string; tokens: number; bytes: number }>> {
80
100
  const out: Array<{ path: string; tokens: number; bytes: number }> = [];
81
101
  const seen = new Set<string>();
82
102
 
@@ -86,7 +106,11 @@ async function loadProjectContextFiles(cwd: string): Promise<Array<{ path: strin
86
106
  const f = await readFileIfExists(p);
87
107
  if (f && !seen.has(f.path)) {
88
108
  seen.add(f.path);
89
- out.push({ path: f.path, tokens: estimateTokens(f.content), bytes: f.bytes });
109
+ out.push({
110
+ path: f.path,
111
+ tokens: estimateTokens(f.content),
112
+ bytes: f.bytes,
113
+ });
90
114
  // pi loads at most one of those per dir
91
115
  return;
92
116
  }
@@ -125,7 +149,8 @@ function buildSkillIndex(pi: ExtensionAPI, cwd: string): SkillIndexEntry[] {
125
149
  .getCommands()
126
150
  .filter((c) => c.source === "skill")
127
151
  .map((c) => {
128
- const p = c.path ? normalizeReadPath(c.path, cwd) : "";
152
+ // pi 1.1.0: SlashCommandInfo 不再含 path 字段,用 sourceInfo.path 替代
153
+ const p = normalizeReadPath(c.sourceInfo.path, cwd);
129
154
  return {
130
155
  name: normalizeSkillName(c.name),
131
156
  skillFilePath: p,
@@ -225,9 +250,9 @@ function renderUsageBar(
225
250
  if (total <= 0) return "";
226
251
 
227
252
  const toCols = (n: number) => Math.round((n / total) * w);
228
- let sys = toCols(parts.system);
229
- let tools = toCols(parts.tools);
230
- let con = toCols(parts.convo);
253
+ const sys = toCols(parts.system);
254
+ const tools = toCols(parts.tools);
255
+ const con = toCols(parts.convo);
231
256
  let rem = w - sys - tools - con;
232
257
  if (rem < 0) rem = 0;
233
258
  // adjust rounding drift
@@ -246,26 +271,28 @@ function joinComma(items: string[]): string {
246
271
  return items.join(", ");
247
272
  }
248
273
 
249
- function joinCommaStyled(items: string[], renderItem: (item: string) => string, sep: string): string {
274
+ function joinCommaStyled(
275
+ items: string[],
276
+ renderItem: (item: string) => string,
277
+ sep: string,
278
+ ): string {
250
279
  return items.map(renderItem).join(sep);
251
280
  }
252
281
 
253
282
  type ContextViewData = {
254
- usage:
255
- | {
256
- // message-based context usage estimate from ctx.getContextUsage()
257
- messageTokens: number;
258
- contextWindow: number;
259
- // effective usage incl. a rough tool-definition estimate
260
- effectiveTokens: number;
261
- percent: number;
262
- remainingTokens: number;
263
- systemPromptTokens: number;
264
- agentTokens: number;
265
- toolsTokens: number;
266
- activeTools: number;
267
- }
268
- | null;
283
+ usage: {
284
+ // message-based context usage estimate from ctx.getContextUsage()
285
+ messageTokens: number;
286
+ contextWindow: number;
287
+ // effective usage incl. a rough tool-definition estimate
288
+ effectiveTokens: number;
289
+ percent: number;
290
+ remainingTokens: number;
291
+ systemPromptTokens: number;
292
+ agentTokens: number;
293
+ toolsTokens: number;
294
+ activeTools: number;
295
+ } | null;
269
296
  agentFiles: string[];
270
297
  extensions: string[];
271
298
  skills: string[];
@@ -274,16 +301,13 @@ type ContextViewData = {
274
301
  };
275
302
 
276
303
  class ContextView implements Component {
277
- private tui: TUI;
278
304
  private theme: any;
279
305
  private onDone: () => void;
280
306
  private data: ContextViewData;
281
307
  private container: Container;
282
308
  private body: Text;
283
309
  private cachedWidth?: number;
284
-
285
- constructor(tui: TUI, theme: any, data: ContextViewData, onDone: () => void) {
286
- this.tui = tui;
310
+ constructor(_tui: TUI, theme: any, data: ContextViewData, onDone: () => void) {
287
311
  this.theme = theme;
288
312
  this.data = data;
289
313
  this.onDone = onDone;
@@ -292,7 +316,8 @@ class ContextView implements Component {
292
316
  this.container.addChild(new DynamicBorder((s) => theme.fg("accent", s)));
293
317
  this.container.addChild(
294
318
  new Text(
295
- theme.fg("accent", theme.bold("Context")) + theme.fg("dim", " (Esc/q/Enter to close)"),
319
+ theme.fg("accent", theme.bold("Context")) +
320
+ theme.fg("dim", " (Esc/q/Enter to close)"),
296
321
  1,
297
322
  0,
298
323
  ),
@@ -320,8 +345,12 @@ class ContextView implements Component {
320
345
  const u = this.data.usage;
321
346
  lines.push(
322
347
  muted("Window: ") +
323
- text(`~${u.effectiveTokens.toLocaleString()} / ${u.contextWindow.toLocaleString()}`) +
324
- muted(` (${u.percent.toFixed(1)}% used, ~${u.remainingTokens.toLocaleString()} left)`),
348
+ text(
349
+ `~${u.effectiveTokens.toLocaleString()} / ${u.contextWindow.toLocaleString()}`,
350
+ ) +
351
+ muted(
352
+ ` (${u.percent.toFixed(1)}% used, ~${u.remainingTokens.toLocaleString()} left)`,
353
+ ),
325
354
  );
326
355
 
327
356
  // bar width tries to fit within the viewport
@@ -374,15 +403,28 @@ class ContextView implements Component {
374
403
  );
375
404
  }
376
405
 
377
- lines.push(muted(`AGENTS (${this.data.agentFiles.length}): `) + text(this.data.agentFiles.length ? joinComma(this.data.agentFiles) : "(none)"));
406
+ lines.push(
407
+ muted(`AGENTS (${this.data.agentFiles.length}): `) +
408
+ text(
409
+ this.data.agentFiles.length ? joinComma(this.data.agentFiles) : "(none)",
410
+ ),
411
+ );
378
412
  lines.push("");
379
- lines.push(muted(`Extensions (${this.data.extensions.length}): `) + text(this.data.extensions.length ? joinComma(this.data.extensions) : "(none)"));
413
+ lines.push(
414
+ muted(`Extensions (${this.data.extensions.length}): `) +
415
+ text(
416
+ this.data.extensions.length ? joinComma(this.data.extensions) : "(none)",
417
+ ),
418
+ );
380
419
 
381
420
  const loaded = new Set(this.data.loadedSkills);
382
421
  const skillsRendered = this.data.skills.length
383
422
  ? joinCommaStyled(
384
423
  this.data.skills,
385
- (name) => (loaded.has(name) ? this.theme.fg("success", name) : this.theme.fg("muted", name)),
424
+ (name) =>
425
+ loaded.has(name)
426
+ ? this.theme.fg("success", name)
427
+ : this.theme.fg("muted", name),
386
428
  this.theme.fg("muted", ", "),
387
429
  )
388
430
  : "(none)";
@@ -444,7 +486,10 @@ export default function contextExtension(pi: ExtensionAPI) {
444
486
  let best: SkillIndexEntry | null = null;
445
487
  for (const s of cachedSkillIndex) {
446
488
  if (!s.skillDir) continue;
447
- if (absPath === s.skillFilePath || absPath.startsWith(s.skillDir + path.sep)) {
489
+ if (
490
+ absPath === s.skillFilePath ||
491
+ absPath.startsWith(s.skillDir + path.sep)
492
+ ) {
448
493
  if (!best || s.skillDir.length > best.skillDir.length) best = s;
449
494
  }
450
495
  }
@@ -467,7 +512,10 @@ export default function contextExtension(pi: ExtensionAPI) {
467
512
 
468
513
  if (!cachedLoadedSkills.has(skillName)) {
469
514
  cachedLoadedSkills.add(skillName);
470
- pi.appendEntry<SkillLoadedEntryData>(SKILL_LOADED_ENTRY, { name: skillName, path: abs });
515
+ pi.appendEntry<SkillLoadedEntryData>(SKILL_LOADED_ENTRY, {
516
+ name: skillName,
517
+ path: abs,
518
+ });
471
519
  }
472
520
  });
473
521
 
@@ -480,7 +528,8 @@ export default function contextExtension(pi: ExtensionAPI) {
480
528
 
481
529
  const extensionsByPath = new Map<string, string[]>();
482
530
  for (const c of extensionCmds) {
483
- const p = c.path ?? "<unknown>";
531
+ // pi 1.1.0: SlashCommandInfo 不再含 path 字段
532
+ const p = c.sourceInfo.path;
484
533
  const arr = extensionsByPath.get(p) ?? [];
485
534
  arr.push(c.name);
486
535
  extensionsByPath.set(p, arr);
@@ -509,7 +558,9 @@ export default function contextExtension(pi: ExtensionAPI) {
509
558
  // factor to account for parameters/schema/formatting.
510
559
  const TOOL_FUDGE = 1.5;
511
560
  const activeToolNames = pi.getActiveTools();
512
- const toolInfoByName = new Map(pi.getAllTools().map((t) => [t.name, t] as const));
561
+ const toolInfoByName = new Map(
562
+ pi.getAllTools().map((t) => [t.name, t] as const),
563
+ );
513
564
  let toolsTokens = 0;
514
565
  for (const name of activeToolNames) {
515
566
  const info = toolInfoByName.get(name);
@@ -520,7 +571,8 @@ export default function contextExtension(pi: ExtensionAPI) {
520
571
 
521
572
  const effectiveTokens = messageTokens + toolsTokens;
522
573
  const percent = ctxWindow > 0 ? (effectiveTokens / ctxWindow) * 100 : 0;
523
- const remainingTokens = ctxWindow > 0 ? Math.max(0, ctxWindow - effectiveTokens) : 0;
574
+ const remainingTokens =
575
+ ctxWindow > 0 ? Math.max(0, ctxWindow - effectiveTokens) : 0;
524
576
 
525
577
  const sessionUsage = sumSessionUsage(ctx);
526
578
 
@@ -534,41 +586,61 @@ export default function contextExtension(pi: ExtensionAPI) {
534
586
  } else {
535
587
  lines.push("Window: (unknown)");
536
588
  }
537
- lines.push(`System: ~${systemPromptTokens.toLocaleString()} tok (AGENTS ~${agentTokens.toLocaleString()})`);
538
- lines.push(`Tools: ~${toolsTokens.toLocaleString()} tok (${activeToolNames.length} active)`);
539
- lines.push(`AGENTS: ${agentFilePaths.length ? joinComma(agentFilePaths) : "(none)"}`);
540
- lines.push(`Extensions (${extensionFiles.length}): ${extensionFiles.length ? joinComma(extensionFiles) : "(none)"}`);
541
- lines.push(`Skills (${skills.length}): ${skills.length ? joinComma(skills) : "(none)"}`);
542
- lines.push(`Session: ${sessionUsage.totalTokens.toLocaleString()} tokens · ${formatUsd(sessionUsage.totalCost)}`);
589
+ lines.push(
590
+ `System: ~${systemPromptTokens.toLocaleString()} tok (AGENTS ~${agentTokens.toLocaleString()})`,
591
+ );
592
+ lines.push(
593
+ `Tools: ~${toolsTokens.toLocaleString()} tok (${activeToolNames.length} active)`,
594
+ );
595
+ lines.push(
596
+ `AGENTS: ${agentFilePaths.length ? joinComma(agentFilePaths) : "(none)"}`,
597
+ );
598
+ lines.push(
599
+ `Extensions (${extensionFiles.length}): ${extensionFiles.length ? joinComma(extensionFiles) : "(none)"}`,
600
+ );
601
+ lines.push(
602
+ `Skills (${skills.length}): ${skills.length ? joinComma(skills) : "(none)"}`,
603
+ );
604
+ lines.push(
605
+ `Session: ${sessionUsage.totalTokens.toLocaleString()} tokens · ${formatUsd(sessionUsage.totalCost)}`,
606
+ );
543
607
  return lines.join("\n");
544
608
  };
545
609
 
546
610
  if (!ctx.hasUI) {
547
- pi.sendMessage({ customType: "context", content: makePlainText(), display: true }, { triggerTurn: false });
611
+ pi.sendMessage(
612
+ { customType: "context", content: makePlainText(), display: true },
613
+ { triggerTurn: false },
614
+ );
548
615
  return;
549
616
  }
550
617
 
551
- const loadedSkills = Array.from(getLoadedSkillsFromSession(ctx)).sort((a, b) => a.localeCompare(b));
618
+ const loadedSkills = Array.from(getLoadedSkillsFromSession(ctx)).sort(
619
+ (a, b) => a.localeCompare(b),
620
+ );
552
621
 
553
622
  const viewData: ContextViewData = {
554
623
  usage: usage
555
624
  ? {
556
- messageTokens,
557
- contextWindow: ctxWindow,
558
- effectiveTokens,
559
- percent,
560
- remainingTokens,
561
- systemPromptTokens,
562
- agentTokens,
563
- toolsTokens,
564
- activeTools: activeToolNames.length,
565
- }
625
+ messageTokens,
626
+ contextWindow: ctxWindow,
627
+ effectiveTokens,
628
+ percent,
629
+ remainingTokens,
630
+ systemPromptTokens,
631
+ agentTokens,
632
+ toolsTokens,
633
+ activeTools: activeToolNames.length,
634
+ }
566
635
  : null,
567
636
  agentFiles: agentFilePaths,
568
637
  extensions: extensionFiles,
569
638
  skills,
570
639
  loadedSkills,
571
- session: { totalTokens: sessionUsage.totalTokens, totalCost: sessionUsage.totalCost },
640
+ session: {
641
+ totalTokens: sessionUsage.totalTokens,
642
+ totalCost: sessionUsage.totalCost,
643
+ },
572
644
  };
573
645
 
574
646
  await ctx.ui.custom<void>((tui, theme, _kb, done) => {
@@ -108,11 +108,6 @@ export default function (pi: ExtensionAPI) {
108
108
 
109
109
  // ── Assemble: figure out what fits ──────────────────────
110
110
  // Always show: path | ctx | model. Stats only if room.
111
- const fixedRight = ctxRaw + modelRaw;
112
- const fixedRightWidth = visibleWidth(fixedRight);
113
- const sepsNeeded = statsRaw ? 3 : 2; // number of separators
114
- const sepsWidth = sepsNeeded * sepWidth;
115
-
116
111
  // Available width for path after reserving right segments
117
112
  const rightBlockWidth = (statsRaw ? visibleWidth(statsRaw) + sepWidth : 0)
118
113
  + visibleWidth(ctxRaw) + sepWidth + visibleWidth(modelRaw);
@@ -9,12 +9,8 @@
9
9
  */
10
10
 
11
11
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
12
- import type { AssistantMessage } from "@earendil-works/pi-ai";
13
12
 
14
13
  export default function (pi: ExtensionAPI) {
15
- // === 记录开始时间 ===
16
- let agentStartTime = 0;
17
-
18
14
  // === 拦截工具调用 ===
19
15
 
20
16
  pi.on("tool_call", async (event, ctx) => {
@@ -33,13 +29,13 @@ export default function (pi: ExtensionAPI) {
33
29
  } catch (e) {
34
30
  // 忽略错误(ctx 可能已过期)
35
31
  }
32
+ return;
36
33
  });
37
34
 
38
35
  // === 注册自定义命令 ===
39
36
 
40
37
  pi.registerCommand("hello", {
41
38
  description: "打招呼命令",
42
- usage: "/hello [名字]",
43
39
  handler: async (args, ctx) => {
44
40
  try {
45
41
  const name = args || "大佬,您吃了吗";
@@ -55,15 +51,14 @@ export default function (pi: ExtensionAPI) {
55
51
  handler: async (_args, ctx) => {
56
52
  try {
57
53
  // 保存需要的数据,避免多次触发 getter
58
- const model = ctx.session.model?.name || "未知";
59
- const workspace = ctx.session.workspace || "未知";
60
- const messages = ctx.session.messages?.length || 0;
61
-
54
+ const model = ctx.model?.name || "未知";
55
+ const entries = ctx.sessionManager.getEntries();
56
+ const messages = entries.filter((e) => e.type === "message").length;
57
+
62
58
  const info = `
63
59
  📊 会话状态
64
60
 
65
61
  - 当前模型:${model}
66
- - 项目目录:${workspace}
67
62
  - 消息数量:${messages}
68
63
  `.trim();
69
64
  ctx.ui.notify(info, "info");
@@ -75,13 +70,12 @@ export default function (pi: ExtensionAPI) {
75
70
 
76
71
  pi.registerCommand("clear", {
77
72
  description: "清理当前会话上下文",
78
- usage: "/clear",
79
73
  handler: async (_args, ctx) => {
80
74
  try {
81
75
  // 获取当前会话条目数(在 await 前完成所有 ctx 访问)
82
76
  const entries = ctx.sessionManager.getBranch();
83
- const msgCount = entries.filter(e => e.type === "message").length;
84
- const uiContext = ctx.ui; // ← 保存 UI context
77
+ const msgCount = entries.filter((e) => e.type === "message").length;
78
+ const uiContext = ctx.ui; // ← 保存 UI context
85
79
 
86
80
  if (msgCount === 0) {
87
81
  uiContext.notify("会话已经是空的", "info");
@@ -89,7 +83,10 @@ export default function (pi: ExtensionAPI) {
89
83
  }
90
84
 
91
85
  // 确认清理
92
- const ok = await uiContext.confirm("清理会话", `确定要清理当前会话的所有 ${msgCount} 条消息吗?`);
86
+ const ok = await uiContext.confirm(
87
+ "清理会话",
88
+ `确定要清理当前会话的所有 ${msgCount} 条消息吗?`,
89
+ );
93
90
  if (!ok) {
94
91
  uiContext.notify("已取消", "info");
95
92
  return;
@@ -98,7 +95,10 @@ export default function (pi: ExtensionAPI) {
98
95
  // 创建新会话来清空上下文
99
96
  await ctx.newSession({
100
97
  withSession: async (newCtx) => {
101
- newCtx.ui.notify(`✅ 已清理 ${msgCount} 条消息,进入新会话`, "success");
98
+ newCtx.ui.notify(
99
+ `✅ 已清理 ${msgCount} 条消息,进入新会话`,
100
+ "info",
101
+ );
102
102
  },
103
103
  });
104
104
  } catch (e) {
@@ -111,7 +111,6 @@ export default function (pi: ExtensionAPI) {
111
111
 
112
112
  pi.registerCommand("exit", {
113
113
  description: "退出当前会话",
114
- usage: "/exit",
115
114
  handler: async (_args, ctx) => {
116
115
  try {
117
116
  ctx.ui.notify("👋 再见!", "info");
@@ -126,28 +125,33 @@ export default function (pi: ExtensionAPI) {
126
125
 
127
126
  pi.registerCommand("update", {
128
127
  description: "更新 aicode-cli 工具",
129
- usage: "/update",
130
128
  handler: async (_args, ctx) => {
131
129
  try {
132
130
  // ← 在 await 前保存 UI context
133
131
  const uiContext = ctx.ui;
134
-
135
- const ok = await uiContext.confirm("更新确认", "确定要更新 aicode-cli 吗?");
132
+
133
+ const ok = await uiContext.confirm(
134
+ "更新确认",
135
+ "确定要更新 aicode-cli 吗?",
136
+ );
136
137
  if (!ok) {
137
138
  uiContext.notify("已取消", "info");
138
139
  return;
139
140
  }
140
141
 
141
142
  uiContext.notify("🔄 正在更新 aicode-cli...", "info");
142
-
143
+
143
144
  // 使用 Promise 包装 exec
144
145
  const { exec } = await import("child_process");
145
146
  await new Promise<void>((resolve) => {
146
- exec("npm install -g @ia-ccun/code-agent-cli", (error, stdout, stderr) => {
147
+ exec("npm install -g @ia-ccun/code-agent-cli", (error) => {
147
148
  if (error) {
148
- uiContext.notify(`❌ 更新失败:${error.message},请重试`, "error");
149
+ uiContext.notify(
150
+ `❌ 更新失败:${error.message},请重试`,
151
+ "error",
152
+ );
149
153
  } else {
150
- uiContext.notify("✅ aicode-cli 更新成功!请重启 CLI", "success");
154
+ uiContext.notify("✅ aicode-cli 更新成功!请重启 CLI", "info");
151
155
  }
152
156
  resolve();
153
157
  });
@@ -167,7 +171,6 @@ export default function (pi: ExtensionAPI) {
167
171
 
168
172
  pi.registerCommand("version", {
169
173
  description: "查看当前版本和最新版本",
170
- usage: "/version",
171
174
  handler: async (_args, ctx) => {
172
175
  const { execSync } = await import("child_process");
173
176
  const { readFileSync, existsSync } = await import("fs");
@@ -191,15 +194,24 @@ export default function (pi: ExtensionAPI) {
191
194
  let latestVersion = "未知";
192
195
  let updateMsg = "";
193
196
  try {
194
- const registry = execSync("npm config get registry", { encoding: "utf8" }).trim();
195
- latestVersion = execSync(`npm view @ia-ccun/code-agent-cli version --registry=${registry}`, { encoding: "utf8" }).trim();
197
+ const registry = execSync("npm config get registry", {
198
+ encoding: "utf8",
199
+ }).trim();
200
+ latestVersion = execSync(
201
+ `npm view @ia-ccun/code-agent-cli version --registry=${registry}`,
202
+ { encoding: "utf8" },
203
+ ).trim();
196
204
 
197
205
  // 比较版本
198
206
  const localParts = localVersion.split(".").map(Number);
199
207
  const latestParts = latestVersion.split(".").map(Number);
200
- const needsUpdate = latestParts[0] > localParts[0] ||
201
- (latestParts[0] === localParts[0] && latestParts[1] > localParts[1]) ||
202
- (latestParts[0] === localParts[0] && latestParts[1] === localParts[1] && latestParts[2] > localParts[2]);
208
+ const needsUpdate =
209
+ latestParts[0] > localParts[0] ||
210
+ (latestParts[0] === localParts[0] &&
211
+ latestParts[1] > localParts[1]) ||
212
+ (latestParts[0] === localParts[0] &&
213
+ latestParts[1] === localParts[1] &&
214
+ latestParts[2] > localParts[2]);
203
215
 
204
216
  if (needsUpdate) {
205
217
  updateMsg = `\n\n⚠️ 发现新版本 v${localVersion} → v${latestVersion}\n 运行:npm install -g @ia-ccun/code-agent-cli`;