pi-cc-extensions 0.7.2 → 0.7.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.
package/README.md CHANGED
@@ -95,7 +95,7 @@ Session 模糊查询默认显示 5 个候选;若已加载 `pi-subagents`,也
95
95
  2. 从补全列表中选择 SubAgent。
96
96
  3. 提交后自动注入委托指令;提示词中提到的每个 SubAgent 都会分别交由 Agent 工具处理。
97
97
 
98
- 该补全不会占用 `@session:` 的历史 Session 引用语法;修改 SubAgent 定义后执行 `/reload` 即可重新加载。
98
+ 该补全不会占用 `@session:` 的历史 Session 引用语法;与 `@bacnh85/pi-fff` 同时启用时,SubAgent、Session 和 FFF 文件候选会合并显示。修改 SubAgent 定义后执行 `/reload` 即可重新加载。
99
99
 
100
100
  ## 安装
101
101
 
@@ -19,7 +19,7 @@ type AgentInfo = {
19
19
 
20
20
  const MAX_SUGGESTIONS = 10;
21
21
  // Match `@name` but NOT `@session:` (reserved by session-reference extension).
22
- const AGENT_NAME_PATTERN = /(?:^|[\t ])@([^\s:@][^\s:]*)$/;
22
+ const AGENT_NAME_PATTERN = /(?:^|[\t ])@((?:[^\s:@][^\s:]*)?)$/;
23
23
 
24
24
  function getAgentDir(): string {
25
25
  const override = process.env.PI_CODING_AGENT_DIR;
@@ -58,11 +58,12 @@ function loadAgents(): AgentInfo[] {
58
58
  });
59
59
  }
60
60
 
61
- function createAgentAutocompleteProvider(
61
+ export function createAgentAutocompleteProvider(
62
62
  current: AutocompleteProvider,
63
63
  getAgents: () => AgentInfo[],
64
64
  ): AutocompleteProvider {
65
65
  return {
66
+ triggerCharacters: ["@"],
66
67
  async getSuggestions(
67
68
  lines: string[],
68
69
  cursorLine: number,
@@ -84,11 +85,15 @@ function createAgentAutocompleteProvider(
84
85
  return current.getSuggestions(lines, cursorLine, cursorCol, options);
85
86
  }
86
87
 
87
- const matches = query.trim()
88
- ? fuzzyFilter(agents, query, (a) => `${a.name} ${a.displayName} ${a.description}`)
89
- : agents;
88
+ const [baseSuggestions, matches] = await Promise.all([
89
+ current.getSuggestions(lines, cursorLine, cursorCol, options),
90
+ Promise.resolve(query.trim()
91
+ ? fuzzyFilter(agents, query, (a) => `${a.name} ${a.displayName} ${a.description}`)
92
+ : agents),
93
+ ]);
94
+ if (options.signal.aborted) return null;
90
95
 
91
- const items: AutocompleteItem[] = matches
96
+ const agentItems: AutocompleteItem[] = matches
92
97
  .slice(0, MAX_SUGGESTIONS)
93
98
  .map((agent) => ({
94
99
  value: `@${agent.name}`,
@@ -96,11 +101,18 @@ function createAgentAutocompleteProvider(
96
101
  description: agent.description
97
102
  + (agent.model ? ` · ${agent.model}` : ""),
98
103
  }));
99
-
100
- if (items.length === 0) {
101
- return current.getSuggestions(lines, cursorLine, cursorCol, options);
102
- }
103
-
104
+ const baseItems = baseSuggestions?.prefix === `@${query}`
105
+ ? baseSuggestions.items
106
+ : [];
107
+ const seen = new Set<string>();
108
+ const items = [...agentItems, ...baseItems].filter((item) => {
109
+ const key = `${item.value}\0${item.label}`;
110
+ if (seen.has(key)) return false;
111
+ seen.add(key);
112
+ return true;
113
+ });
114
+
115
+ if (items.length === 0) return baseSuggestions;
104
116
  return { items, prefix: `@${query}` };
105
117
  },
106
118
 
@@ -122,6 +134,7 @@ function createAgentAutocompleteProvider(
122
134
 
123
135
  export default function agentAutocompleteExtension(pi: ExtensionAPI): void {
124
136
  let cachedAgents: AgentInfo[] | undefined;
137
+ let sessionGeneration = 0;
125
138
 
126
139
  const getAgents = (): AgentInfo[] => {
127
140
  if (!cachedAgents) {
@@ -136,11 +149,22 @@ export default function agentAutocompleteExtension(pi: ExtensionAPI): void {
136
149
  });
137
150
 
138
151
  pi.on("session_start", (_event, ctx) => {
152
+ const generation = ++sessionGeneration;
139
153
  const agents = getAgents();
140
154
  if (agents.length === 0 || ctx.mode !== "tui") return;
141
- ctx.ui.addAutocompleteProvider((current) =>
142
- createAgentAutocompleteProvider(current, getAgents),
143
- );
155
+
156
+ // Register after other session_start handlers have installed their wrappers.
157
+ // pi-fff handles every @ prefix and otherwise shadows providers loaded before it.
158
+ setTimeout(() => {
159
+ if (generation !== sessionGeneration) return;
160
+ ctx.ui.addAutocompleteProvider((current) =>
161
+ createAgentAutocompleteProvider(current, getAgents),
162
+ );
163
+ }, 0);
164
+ });
165
+
166
+ pi.on("session_shutdown", () => {
167
+ sessionGeneration++;
144
168
  });
145
169
 
146
170
  // Inject instruction when user types @agent-name (not @session:) in prompt.
@@ -215,7 +215,7 @@ function mergeReferences(sessions: SessionInfo[], subagents: SessionReference[])
215
215
  return [...bySessionId.values()];
216
216
  }
217
217
 
218
- function createAutocompleteProvider(
218
+ export function createAutocompleteProvider(
219
219
  current: AutocompleteProvider,
220
220
  getReferences: () => Promise<SessionReference[]>,
221
221
  currentCwd: string,
@@ -263,6 +263,7 @@ function samePath(left: string | undefined, right: string): boolean {
263
263
 
264
264
  export default function sessionReferenceExtension(pi: ExtensionAPI): void {
265
265
  let getAvailableReferences: (() => Promise<SessionReference[]>) | undefined;
266
+ let sessionGeneration = 0;
266
267
  const subagentIds = new Set<string>();
267
268
  // pi-subagents emits "subagent:async-started" and "subagent:async-complete" events.
268
269
  // We track records locally so @[SubAgent] suggestions work even without a global manager.
@@ -290,6 +291,7 @@ export default function sessionReferenceExtension(pi: ExtensionAPI): void {
290
291
  });
291
292
 
292
293
  pi.on("session_start", (_event, ctx) => {
294
+ const generation = ++sessionGeneration;
293
295
  subagentIds.clear();
294
296
  let loadErrorShown = false;
295
297
  const currentSessionId = ctx.sessionManager.getSessionId();
@@ -320,7 +322,14 @@ export default function sessionReferenceExtension(pi: ExtensionAPI): void {
320
322
  getAvailableReferences = getReferences;
321
323
  if (ctx.mode === "tui") {
322
324
  void getReferences();
323
- ctx.ui.addAutocompleteProvider((current) => createAutocompleteProvider(current, getReferences, ctx.cwd));
325
+ // Register after other session_start handlers. pi-fff claims every @
326
+ // prefix, so a provider installed before it would never see session mentions.
327
+ setTimeout(() => {
328
+ if (generation !== sessionGeneration) return;
329
+ ctx.ui.addAutocompleteProvider((current) =>
330
+ createAutocompleteProvider(current, getReferences, ctx.cwd),
331
+ );
332
+ }, 0);
324
333
  }
325
334
  });
326
335
 
@@ -404,6 +413,7 @@ export default function sessionReferenceExtension(pi: ExtensionAPI): void {
404
413
  });
405
414
 
406
415
  pi.on("session_shutdown", () => {
416
+ sessionGeneration++;
407
417
  for (const unsubscribe of unsubscribeSubagentEvents) unsubscribe();
408
418
  subagentIds.clear();
409
419
  getAvailableReferences = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cc-extensions",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
4
4
  "description": "Personal extensions for the Pi coding agent",
5
5
  "type": "module",
6
6
  "keywords": [