@fpr1m3/opencode-pai-plugin 1.2.0 → 1.3.0

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/dist/index.js CHANGED
@@ -97,6 +97,9 @@ export const PAIPlugin = async ({ worktree }) => {
97
97
  const messageTextCache = new Map();
98
98
  // Track which messages we've already processed for archival (deduplication)
99
99
  const processedMessageIds = new Set();
100
+ // Track pending Task tool calls to capture subagent_type
101
+ // Key: callID, Value: subagent_type
102
+ const pendingTaskCalls = new Map();
100
103
  // Auto-initialize PAI infrastructure if needed
101
104
  ensurePAIStructure();
102
105
  // Load CORE skill content from $PAI_DIR/skill/core/SKILL.md
@@ -173,9 +176,14 @@ export const PAIPlugin = async ({ worktree }) => {
173
176
  const file = props?.input?.file_path?.split('/').pop() || 'file';
174
177
  process.stderr.write(`\x1b]0;Editing ${file}...\x07`);
175
178
  }
176
- else if (props?.tool === 'Task') {
179
+ else if (props?.tool === 'Task' || props?.tool === 'task') {
177
180
  const type = props?.input?.subagent_type || 'agent';
178
181
  process.stderr.write(`\x1b]0;Agent: ${type}...\x07`);
182
+ // Cache the subagent_type for when tool.execute.after fires
183
+ const callId = props?.id || props?.callId || props?.call_id;
184
+ if (callId && props?.input?.subagent_type) {
185
+ pendingTaskCalls.set(callId, props.input.subagent_type);
186
+ }
179
187
  }
180
188
  }
181
189
  // Handle assistant message completion (Tab Titles & Artifact Archival)
@@ -232,12 +240,30 @@ export const PAIPlugin = async ({ worktree }) => {
232
240
  // (In practice, messages are cleaned up after processing)
233
241
  }
234
242
  },
243
+ "tool.execute.before": async (input, output) => {
244
+ // Cache subagent_type from Task tool args for later use in tool.execute.after
245
+ if ((input.tool === 'Task' || input.tool === 'task') && input.callID) {
246
+ const args = output.args;
247
+ if (args?.subagent_type) {
248
+ pendingTaskCalls.set(input.callID, args.subagent_type);
249
+ }
250
+ }
251
+ },
235
252
  "tool.execute.after": async (input, output) => {
236
253
  const sessionId = input.sessionID;
237
254
  if (sessionId) {
238
255
  if (!loggers.has(sessionId)) {
239
256
  loggers.set(sessionId, new Logger(sessionId, worktree));
240
257
  }
258
+ // For Task tools, inject the cached subagent_type into metadata
259
+ if ((input.tool === 'Task' || input.tool === 'task') && input.callID) {
260
+ const cachedAgentType = pendingTaskCalls.get(input.callID);
261
+ if (cachedAgentType) {
262
+ output.metadata = output.metadata || {};
263
+ output.metadata.subagent_type = cachedAgentType;
264
+ pendingTaskCalls.delete(input.callID);
265
+ }
266
+ }
241
267
  loggers.get(sessionId).logToolExecution(input, output);
242
268
  }
243
269
  },
@@ -28,6 +28,15 @@ export declare class Logger {
28
28
  generateSessionSummary(): Promise<string | null>;
29
29
  private parseStructuredResponse;
30
30
  private isLearningCapture;
31
+ /**
32
+ * Normalize agent role from subagent_type patterns to base role.
33
+ * Handles patterns like:
34
+ * - "subagents/researcher-claude" → "researcher"
35
+ * - "subagents/sparc-architect" → "architect"
36
+ * - "subagents/sparc-dev" → "engineer"
37
+ * - "researcher" → "researcher" (passthrough)
38
+ */
39
+ private normalizeAgentRole;
31
40
  private determineArtifactType;
32
41
  private createArtifact;
33
42
  logError(context: string, error: any): void;
@@ -126,7 +126,7 @@ export class Logger {
126
126
  const sessionId = this.sessionId;
127
127
  this.toolsUsed.add(toolName);
128
128
  const metadata = output.metadata || {};
129
- if (toolName === 'Task' && metadata?.subagent_type) {
129
+ if ((toolName === 'Task' || toolName === 'task') && metadata?.subagent_type) {
130
130
  this.setAgentForSession(sessionId, metadata.subagent_type);
131
131
  }
132
132
  else if (toolName === 'subagent_stop' || toolName === 'stop') {
@@ -241,13 +241,56 @@ This session summary was automatically generated by the PAI OpenCode Plugin.
241
241
  count++;
242
242
  return count >= 2;
243
243
  }
244
+ /**
245
+ * Normalize agent role from subagent_type patterns to base role.
246
+ * Handles patterns like:
247
+ * - "subagents/researcher-claude" → "researcher"
248
+ * - "subagents/sparc-architect" → "architect"
249
+ * - "subagents/sparc-dev" → "engineer"
250
+ * - "researcher" → "researcher" (passthrough)
251
+ */
252
+ normalizeAgentRole(agentRole) {
253
+ if (!agentRole)
254
+ return 'pai';
255
+ // Remove "subagents/" prefix if present (case-insensitive)
256
+ let role = agentRole.replace(/^subagents\//i, '').toLowerCase();
257
+ // Role keywords to look for anywhere in the string
258
+ // Order matters: more specific patterns first
259
+ const roleKeywords = [
260
+ // Researcher patterns (prefix or contains)
261
+ [/researcher/i, 'researcher'],
262
+ [/research/i, 'researcher'],
263
+ // Architect patterns
264
+ [/architect/i, 'architect'],
265
+ // Engineer patterns (includes "dev" for sparc-dev)
266
+ [/engineer/i, 'engineer'],
267
+ [/\bdev\b/i, 'engineer'], // "sparc-dev" → engineer
268
+ // Designer patterns
269
+ [/designer/i, 'designer'],
270
+ // Security patterns
271
+ [/pentester/i, 'pentester'],
272
+ // These map to researcher
273
+ [/analyst/i, 'researcher'],
274
+ [/explorer/i, 'researcher'],
275
+ [/^explore$/i, 'researcher'],
276
+ [/^intern$/i, 'researcher'],
277
+ ];
278
+ for (const [pattern, normalized] of roleKeywords) {
279
+ if (pattern.test(role)) {
280
+ return normalized;
281
+ }
282
+ }
283
+ // Return lowercase role if no pattern matched
284
+ return role;
285
+ }
244
286
  determineArtifactType(agentRole, isLearning, sections) {
245
287
  const summary = (sections['SUMMARY'] || '').toLowerCase();
246
- if (agentRole === 'architect')
288
+ const normalizedRole = this.normalizeAgentRole(agentRole);
289
+ if (normalizedRole === 'architect')
247
290
  return 'DECISION';
248
- if (agentRole === 'researcher' || agentRole === 'pentester')
291
+ if (normalizedRole === 'researcher' || normalizedRole === 'pentester')
249
292
  return 'RESEARCH';
250
- if (agentRole === 'engineer' || agentRole === 'designer') {
293
+ if (normalizedRole === 'engineer' || normalizedRole === 'designer') {
251
294
  if (summary.includes('fix') || summary.includes('bug') || summary.includes('issue'))
252
295
  return 'BUG';
253
296
  if (summary.includes('refactor') || summary.includes('improve') || summary.includes('cleanup'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fpr1m3/opencode-pai-plugin",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Personal AI Infrastructure (PAI) plugin for OpenCode",
6
6
  "main": "dist/index.js",