@pellux/goodvibes-agent 0.1.58 → 0.1.60

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.
@@ -1,223 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import type { CommandContext, CommandRegistry } from '../command-registry.ts';
4
- import { CodeIntelligence } from '@pellux/goodvibes-sdk/platform/intelligence';
5
- import type { DocumentSymbol } from '@pellux/goodvibes-sdk/platform/intelligence';
6
- import type { SymbolInfo } from '@pellux/goodvibes-sdk/platform/intelligence';
7
- import { openCommandPanel, requireReadModels, requireShellPaths } from './runtime-services.ts';
8
-
9
- function resolveTargetPath(pathArg: string, ctx: CommandContext): string {
10
- return requireShellPaths(ctx).resolveWorkspacePath(pathArg);
11
- }
12
-
13
- function parsePosition(lineArg: string | undefined, columnArg: string | undefined): { line: number; column: number } | null {
14
- const line = Number.parseInt(lineArg ?? '', 10);
15
- const column = Number.parseInt(columnArg ?? '', 10);
16
- if (!Number.isFinite(line) || line < 1 || !Number.isFinite(column) || column < 1) return null;
17
- return { line: line - 1, column: column - 1 };
18
- }
19
-
20
- function formatSymbolKind(kind: number | string | undefined): string {
21
- if (typeof kind === 'number') return `kind=${kind}`;
22
- if (typeof kind === 'string' && kind.trim().length > 0) return kind;
23
- return 'symbol';
24
- }
25
-
26
- function formatDocumentSymbol(symbol: DocumentSymbol): string {
27
- const line = (symbol.selectionRange?.start.line ?? symbol.range.start.line) + 1;
28
- const column = (symbol.selectionRange?.start.character ?? symbol.range.start.character) + 1;
29
- return ` ${symbol.name} ${formatSymbolKind(symbol.kind)} ${line}:${column}`;
30
- }
31
-
32
- function formatTreeSitterSymbol(symbol: SymbolInfo): string {
33
- return ` ${symbol.name} ${formatSymbolKind(symbol.kind)} ${symbol.line + 1}:${symbol.column + 1}`;
34
- }
35
-
36
- function ensureExistingFile(pathArg: string | undefined, ctx: CommandContext): string | null {
37
- if (!pathArg) {
38
- ctx.print('Intelligence Review\n Missing file path.');
39
- return null;
40
- }
41
- const targetPath = resolveTargetPath(pathArg, ctx);
42
- if (!existsSync(targetPath)) {
43
- ctx.print(`Intelligence Review\n File not found: ${targetPath}`);
44
- return null;
45
- }
46
- return targetPath;
47
- }
48
-
49
- export function registerIntelligenceRuntimeCommands(registry: CommandRegistry): void {
50
- registry.register({
51
- name: 'intelligence',
52
- aliases: ['intel'],
53
- description: 'Review workspace intelligence readiness, diagnostics posture, and symbol search availability',
54
- usage: '[review|panel|diagnostics [file]|symbols <file>|outline <file>|definition <file> <line> <column>|references <file> <line> <column>|hover <file> <line> <column>|repair]',
55
- async handler(args, ctx) {
56
- const sub = (args[0] ?? 'review').toLowerCase();
57
- if (sub === 'panel' || sub === 'open') {
58
- openCommandPanel(ctx, 'intelligence');
59
- return;
60
- }
61
-
62
- const intelligence = new CodeIntelligence();
63
- const state = requireReadModels(ctx).intelligence.getSnapshot();
64
-
65
- if (sub === 'symbols' || sub === 'outline') {
66
- const targetPath = ensureExistingFile(args[1], ctx);
67
- if (!targetPath) return;
68
- const content = readFileSync(targetPath, 'utf-8');
69
- if (sub === 'symbols') {
70
- const symbols = await intelligence.getDocumentSymbols(targetPath, content);
71
- const entries = symbols.slice(0, 12).map((symbol) => ('selectionRange' in symbol ? formatDocumentSymbol(symbol) : formatTreeSitterSymbol(symbol)));
72
- ctx.print([
73
- `Intelligence Symbols: ${targetPath}`,
74
- ` source: ${state.symbolSearchStatus === 'ready' ? 'LSP/tree-sitter' : 'best-effort tree-sitter/LSP fallback'}`,
75
- ` status: ${state.symbolSearchStatus}`,
76
- ` results: ${symbols.length}`,
77
- ...(entries.length > 0 ? entries : [' No symbols available for this file.']),
78
- ' next: /health intelligence',
79
- ].join('\n'));
80
- return;
81
- }
82
-
83
- const outline = await intelligence.getOutline(targetPath, content);
84
- ctx.print([
85
- `Intelligence Outline: ${targetPath}`,
86
- ` source: tree-sitter outline extraction`,
87
- ` language ready: ${intelligence.hasTreeSitter(targetPath) ? 'yes' : 'no'}`,
88
- ` results: ${outline.length}`,
89
- ...(outline.slice(0, 12).map((entry) => ` ${entry.signature || entry.name} line ${entry.line}`)),
90
- ...(outline.length === 0 ? [' No outline entries available for this file.'] : []),
91
- ' next: /intelligence symbols ' + targetPath,
92
- ].join('\n'));
93
- return;
94
- }
95
-
96
- if (sub === 'definition' || sub === 'references' || sub === 'hover') {
97
- const targetPath = ensureExistingFile(args[1], ctx);
98
- if (!targetPath) return;
99
- const position = parsePosition(args[2], args[3]);
100
- if (!position) {
101
- ctx.print(`Intelligence ${sub[0]!.toUpperCase()}${sub.slice(1)}\n Usage: /intelligence ${sub} <file> <line> <column>`);
102
- return;
103
- }
104
-
105
- if (sub === 'definition') {
106
- const definition = await intelligence.getDefinition(targetPath, position.line, position.column);
107
- ctx.print([
108
- `Intelligence Definition: ${targetPath}:${position.line + 1}:${position.column + 1}`,
109
- ` status: ${state.hoverStatus === 'ready' || state.symbolSearchStatus === 'ready' ? 'available' : 'best-effort'}`,
110
- ...(definition
111
- ? [
112
- ` target: ${definition.uri}`,
113
- ` line: ${definition.range.start.line + 1}`,
114
- ` column: ${definition.range.start.character + 1}`,
115
- ' next: open the target file or use /intelligence references on the same symbol',
116
- ]
117
- : [' No definition was returned for that position.', ' next: /health intelligence']),
118
- ].join('\n'));
119
- return;
120
- }
121
-
122
- if (sub === 'references') {
123
- const references = await intelligence.getReferences(targetPath, position.line, position.column);
124
- ctx.print([
125
- `Intelligence References: ${targetPath}:${position.line + 1}:${position.column + 1}`,
126
- ` status: ${state.symbolSearchStatus}`,
127
- ` results: ${references.length}`,
128
- ...(references.slice(0, 12).map((reference) => ` ${reference.uri} ${reference.range.start.line + 1}:${reference.range.start.character + 1}`)),
129
- ...(references.length === 0 ? [' No references were returned for that position.'] : []),
130
- ' next: /intelligence definition ' + `${targetPath} ${position.line + 1} ${position.column + 1}`,
131
- ].join('\n'));
132
- return;
133
- }
134
-
135
- const hover = await intelligence.getHover(targetPath, position.line, position.column);
136
- const hoverLines = typeof hover?.contents === 'string'
137
- ? hover.contents.split('\n')
138
- : Array.isArray(hover?.contents)
139
- ? hover.contents.flatMap((entry) => typeof entry === 'string' ? entry : entry.value.split('\n'))
140
- : hover?.contents && 'value' in hover.contents
141
- ? hover.contents.value.split('\n')
142
- : [];
143
- ctx.print([
144
- `Intelligence Hover: ${targetPath}:${position.line + 1}:${position.column + 1}`,
145
- ` status: ${state.hoverStatus}`,
146
- ...(hoverLines.length > 0 ? hoverLines.slice(0, 8).map((line) => ` ${line}`) : [' No hover information was returned for that position.']),
147
- ' next: /health intelligence',
148
- ].join('\n'));
149
- return;
150
- }
151
-
152
- if (sub === 'diagnostics') {
153
- const file = args[1];
154
- const entries = [...state.diagnostics.entries()]
155
- .map(([filePath, diagnostics]) => ({
156
- filePath,
157
- diagnostics,
158
- errors: diagnostics.filter((entry) => entry.severity === 'error').length,
159
- warnings: diagnostics.filter((entry) => entry.severity === 'warning').length,
160
- }))
161
- .sort((a, b) => (b.errors - a.errors) || (b.warnings - a.warnings) || a.filePath.localeCompare(b.filePath));
162
- const selected = file
163
- ? entries.find((entry) => entry.filePath === file)
164
- : entries[0];
165
- if (!selected) {
166
- ctx.print('Intelligence Diagnostics\n No diagnostics are currently tracked.');
167
- return;
168
- }
169
- ctx.print([
170
- `Intelligence Diagnostics: ${selected.filePath}`,
171
- ` errors: ${selected.errors}`,
172
- ` warnings: ${selected.warnings}`,
173
- ...selected.diagnostics.slice(0, 8).map((diagnostic) => (
174
- ` [${diagnostic.severity}] ${diagnostic.line + 1}:${diagnostic.column + 1} ${diagnostic.message}`
175
- )),
176
- ...(entries.length > 1 ? [` next: /intelligence diagnostics ${entries[1]!.filePath}`] : []),
177
- ].join('\n'));
178
- return;
179
- }
180
-
181
- if (sub === 'repair') {
182
- const lines = [
183
- 'Intelligence Repair',
184
- ' verify: /health intelligence',
185
- ...(state.diagnosticsStatus !== 'ready' ? [' /setup review', ' /health intelligence'] : []),
186
- ...(state.symbolSearchStatus !== 'ready' ? [' /symbols', ' /intelligence symbols <file>', ' /health intelligence'] : []),
187
- ...(state.completionsStatus !== 'ready' || state.hoverStatus !== 'ready'
188
- ? [' /intelligence review', ' /intelligence hover <file> <line> <column>', ' /setup onboarding']
189
- : []),
190
- ];
191
- ctx.print(lines.length > 1 ? lines.join('\n') : 'Intelligence Repair\n No active repair actions suggested.');
192
- return;
193
- }
194
-
195
- const issues: string[] = [];
196
- if (state.diagnosticsStatus !== 'ready') issues.push(`diagnostics=${state.diagnosticsStatus}`);
197
- if (state.symbolSearchStatus !== 'ready') issues.push(`symbols=${state.symbolSearchStatus}`);
198
- if (state.completionsStatus !== 'ready') issues.push(`completions=${state.completionsStatus}`);
199
- if (state.hoverStatus !== 'ready') issues.push(`hover=${state.hoverStatus}`);
200
-
201
- ctx.print([
202
- 'Intelligence Review',
203
- ` diagnostics: ${state.diagnosticsStatus}`,
204
- ` symbols: ${state.symbolSearchStatus}`,
205
- ` completions: ${state.completionsStatus}`,
206
- ` hover: ${state.hoverStatus}`,
207
- ` errors: ${state.errorCount}`,
208
- ` warnings: ${state.warningCount}`,
209
- ` requests: ${state.totalRequests}`,
210
- ` avg latency: ${Math.round(state.avgLatencyMs)}ms`,
211
- ...(issues.length > 0 ? [` issues: ${issues.join(', ')}`] : [' issues: none']),
212
- ` diagnostic files: ${state.diagnostics.size}`,
213
- ...(state.diagnostics.size > 0 ? [' next: /intelligence diagnostics'] : []),
214
- ' next: /intelligence symbols <file>',
215
- ' next: /intelligence outline <file>',
216
- ' next: /intelligence references <file> <line> <column>',
217
- ' next: /intelligence definition <file> <line> <column>',
218
- ' next: /intelligence hover <file> <line> <column>',
219
- ' next: /intelligence repair',
220
- ].join('\n'));
221
- },
222
- });
223
- }
@@ -1,339 +0,0 @@
1
- import type { CommandRegistry, CommandContext } from '../command-registry.ts';
2
- import { join } from 'node:path';
3
- import { AGENT_TEMPLATES } from '@pellux/goodvibes-sdk/platform/tools';
4
- import { ArchetypeLoader, type AgentArchetype } from '@pellux/goodvibes-sdk/platform/agents';
5
- import { requireReadModels, requireShellPaths } from './runtime-services.ts';
6
-
7
- type TeamworkModeId =
8
- | 'local-engineer'
9
- | 'local-shell'
10
- | 'remote-engineer'
11
- | 'teammate'
12
- | 'research'
13
- | 'dream'
14
- | 'review'
15
- | 'verifier'
16
- | 'integration';
17
-
18
- interface TeamworkMode {
19
- readonly id: TeamworkModeId;
20
- readonly label: string;
21
- readonly taskKind: 'agent' | 'exec' | 'acp' | 'integration';
22
- readonly owner: string;
23
- readonly template?: keyof typeof AGENT_TEMPLATES;
24
- readonly reviewMode: 'none' | 'wrfc';
25
- readonly executionProtocol: 'direct' | 'gather-plan-apply';
26
- }
27
-
28
- interface TeamworkRecipe {
29
- readonly id: string;
30
- readonly summary: string;
31
- readonly steps: readonly string[];
32
- }
33
-
34
- interface ResolvedArchetypeMode {
35
- readonly id: string;
36
- readonly label: string;
37
- readonly owner: string;
38
- readonly taskKind: 'agent' | 'exec' | 'acp' | 'integration';
39
- readonly template?: string;
40
- readonly reviewMode: 'none' | 'wrfc';
41
- readonly executionProtocol: 'direct' | 'gather-plan-apply';
42
- readonly source: 'builtin' | 'custom';
43
- readonly family: 'implement' | 'review' | 'test' | 'research' | 'general';
44
- readonly sourcePath?: string;
45
- readonly validationIssues: readonly string[];
46
- readonly tools: readonly string[];
47
- }
48
-
49
- interface TeamworkReviewSnapshot {
50
- readonly builtinArchetypes: number;
51
- readonly customArchetypes: number;
52
- readonly archetypesWithIssues: number;
53
- readonly implementArchetypes: number;
54
- readonly reviewArchetypes: number;
55
- readonly researchArchetypes: number;
56
- readonly activeTaskCount: number;
57
- readonly blockedTaskCount: number;
58
- readonly reviewTaskCount: number;
59
- }
60
-
61
- const TEAMWORK_MODES: readonly TeamworkMode[] = [
62
- { id: 'local-engineer', label: 'Local engineer task', taskKind: 'agent', owner: 'local-engineer', template: 'engineer', reviewMode: 'wrfc', executionProtocol: 'gather-plan-apply' },
63
- { id: 'local-shell', label: 'Local shell task', taskKind: 'exec', owner: 'local-shell', reviewMode: 'none', executionProtocol: 'direct' },
64
- { id: 'remote-engineer', label: 'Remote engineer task', taskKind: 'acp', owner: 'remote-engineer', template: 'engineer', reviewMode: 'wrfc', executionProtocol: 'gather-plan-apply' },
65
- { id: 'teammate', label: 'In-process teammate task', taskKind: 'agent', owner: 'teammate', template: 'general', reviewMode: 'wrfc', executionProtocol: 'gather-plan-apply' },
66
- { id: 'research', label: 'Research task', taskKind: 'agent', owner: 'research', template: 'researcher', reviewMode: 'none', executionProtocol: 'gather-plan-apply' },
67
- { id: 'dream', label: 'Speculative dream task', taskKind: 'agent', owner: 'dream', template: 'researcher', reviewMode: 'none', executionProtocol: 'gather-plan-apply' },
68
- { id: 'review', label: 'Review-only task', taskKind: 'agent', owner: 'review', template: 'reviewer', reviewMode: 'wrfc', executionProtocol: 'gather-plan-apply' },
69
- { id: 'verifier', label: 'Verifier task', taskKind: 'agent', owner: 'verifier', template: 'reviewer', reviewMode: 'wrfc', executionProtocol: 'gather-plan-apply' },
70
- { id: 'integration', label: 'Integration task', taskKind: 'integration', owner: 'integration', template: 'engineer', reviewMode: 'wrfc', executionProtocol: 'gather-plan-apply' },
71
- ];
72
-
73
- const TEAMWORK_RECIPES: readonly TeamworkRecipe[] = [
74
- {
75
- id: 'research-implement-review',
76
- summary: 'Research first, implement second, certify with WRFC review.',
77
- steps: ['research', 'local-engineer', 'review', 'verifier'],
78
- },
79
- {
80
- id: 'remote-certification',
81
- summary: 'Remote engineer execution with local review and integration.',
82
- steps: ['remote-engineer', 'review', 'integration'],
83
- },
84
- {
85
- id: 'triage-delegate-integrate',
86
- summary: 'Triage locally, delegate focused teammate work, then integrate.',
87
- steps: ['teammate', 'review', 'integration'],
88
- },
89
- {
90
- id: 'dream-then-certify',
91
- summary: 'Run a speculative research pass, then ground it through engineer, review, and verifier steps.',
92
- steps: ['dream', 'local-engineer', 'review', 'verifier'],
93
- },
94
- ];
95
-
96
- function formatMode(mode: TeamworkMode): string {
97
- return ` ${mode.id.padEnd(18)} ${mode.taskKind.padEnd(11)} ${mode.owner.padEnd(16)} ${mode.reviewMode.padEnd(4)} ${mode.executionProtocol}${mode.template ? ` template=${mode.template}` : ''}`;
98
- }
99
-
100
- function classifyArchetype(archetype: AgentArchetype): ResolvedArchetypeMode['family'] {
101
- const haystack = `${archetype.name} ${archetype.description}`.toLowerCase();
102
- const tools = new Set(archetype.tools.map((tool) => tool.toLowerCase()));
103
- if (haystack.includes('review') || haystack.includes('audit')) return 'review';
104
- if (haystack.includes('test') || tools.has('exec') && tools.has('write') && !tools.has('edit')) return 'test';
105
- if (haystack.includes('research') || haystack.includes('analysis') || (tools.has('find') && tools.has('analyze') && !tools.has('write') && !tools.has('edit'))) return 'research';
106
- if (tools.has('write') || tools.has('edit')) return 'implement';
107
- return 'general';
108
- }
109
-
110
- function buildArchetypeMode(archetype: AgentArchetype): ResolvedArchetypeMode {
111
- const family = classifyArchetype(archetype);
112
- const source = archetype.isCustom ? 'custom' : 'builtin';
113
- return {
114
- id: archetype.name,
115
- label: archetype.description || `${archetype.name} archetype`,
116
- owner: source === 'custom' ? `custom:${archetype.name}` : archetype.name,
117
- taskKind: 'agent',
118
- template: archetype.name,
119
- reviewMode: family === 'review' ? 'wrfc' : 'none',
120
- executionProtocol: family === 'research' ? 'gather-plan-apply' : family === 'implement' ? 'gather-plan-apply' : 'direct',
121
- source,
122
- family,
123
- sourcePath: archetype.sourcePath,
124
- validationIssues: archetype.validationIssues ?? [],
125
- tools: archetype.tools,
126
- };
127
- }
128
-
129
- function listArchetypeModes(projectRoot: string): ResolvedArchetypeMode[] {
130
- const loader = new ArchetypeLoader(join(projectRoot, '.goodvibes', 'agents'));
131
- return loader.listArchetypes()
132
- .map(buildArchetypeMode)
133
- .sort((a, b) => a.id.localeCompare(b.id));
134
- }
135
-
136
- function buildTeamworkReviewSnapshot(ctx: CommandContext): TeamworkReviewSnapshot {
137
- const projectRoot = requireShellPaths(ctx).workingDirectory;
138
- const archetypes = listArchetypeModes(projectRoot);
139
- const tasks = [...requireReadModels(ctx).tasks.getSnapshot().tasks];
140
- return {
141
- builtinArchetypes: archetypes.filter((entry) => entry.source === 'builtin').length,
142
- customArchetypes: archetypes.filter((entry) => entry.source === 'custom').length,
143
- archetypesWithIssues: archetypes.filter((entry) => entry.validationIssues.length > 0).length,
144
- implementArchetypes: archetypes.filter((entry) => entry.family === 'implement').length,
145
- reviewArchetypes: archetypes.filter((entry) => entry.family === 'review').length,
146
- researchArchetypes: archetypes.filter((entry) => entry.family === 'research').length,
147
- activeTaskCount: tasks.filter((task) => task.status === 'running' || task.status === 'queued').length,
148
- blockedTaskCount: tasks.filter((task) => task.status === 'blocked').length,
149
- reviewTaskCount: tasks.filter((task) => task.owner === 'review' || task.owner === 'verifier').length,
150
- };
151
- }
152
-
153
- function printAgentDelegationBoundary(ctx: CommandContext, requestedAction: string): void {
154
- ctx.print([
155
- 'GoodVibes Agent does not create local teamwork, Engineer, Reviewer, Tester, Verifier, or WRFC-owned tasks.',
156
- ` requested: ${requestedAction}`,
157
- ' policy: keep ordinary work serial in the main assistant conversation',
158
- ' build/fix/review: delegate one request to GoodVibes TUI through the public shared-session/build-delegation contract',
159
- ' preserve: full original user ask and executionIntent; let TUI own WRFC when explicitly requested',
160
- ].join('\n'));
161
- }
162
-
163
- export function registerTeamworkRuntimeCommands(registry: CommandRegistry): void {
164
- registry.register({
165
- name: 'teamwork',
166
- aliases: ['teammates'],
167
- description: 'Packaged task modes, teammate templates, and orchestration recipes',
168
- usage: '[review|modes|mode <id>|create-mode <id> <title...>|recipes|recipe <id>|templates|archetypes|validate|archetype <name>|create-archetype <name> <title...>]',
169
- handler(args, ctx) {
170
- const sub = args[0]?.toLowerCase() ?? 'review';
171
-
172
- if (sub === 'review') {
173
- const snapshot = buildTeamworkReviewSnapshot(ctx);
174
- ctx.print([
175
- 'Teamwork Review',
176
- ` modes: ${TEAMWORK_MODES.length}`,
177
- ` recipes: ${TEAMWORK_RECIPES.length}`,
178
- ` builtin archetypes: ${snapshot.builtinArchetypes}`,
179
- ` custom archetypes: ${snapshot.customArchetypes}`,
180
- ` archetypes with issues: ${snapshot.archetypesWithIssues}`,
181
- ` implement/review/research: ${snapshot.implementArchetypes}/${snapshot.reviewArchetypes}/${snapshot.researchArchetypes}`,
182
- ` active tasks: ${snapshot.activeTaskCount}`,
183
- ` blocked tasks: ${snapshot.blockedTaskCount}`,
184
- ` review tasks: ${snapshot.reviewTaskCount}`,
185
- ' next: /teamwork archetypes',
186
- ' next: /teamwork validate',
187
- ' next: /tasks',
188
- ].join('\n'));
189
- return;
190
- }
191
-
192
- if (sub === 'modes') {
193
- ctx.print([
194
- 'Teamwork Modes',
195
- ...TEAMWORK_MODES.map(formatMode),
196
- ].join('\n'));
197
- return;
198
- }
199
-
200
- if (sub === 'mode') {
201
- const modeId = args[1] as TeamworkModeId | undefined;
202
- const mode = TEAMWORK_MODES.find((entry) => entry.id === modeId);
203
- if (!mode) {
204
- ctx.print('Usage: /teamwork mode <local-engineer|local-shell|remote-engineer|teammate|research|dream|review|verifier|integration>');
205
- return;
206
- }
207
- ctx.print([
208
- `Teamwork Mode ${mode.id}`,
209
- ` label: ${mode.label}`,
210
- ` taskKind: ${mode.taskKind}`,
211
- ` owner: ${mode.owner}`,
212
- ` template: ${mode.template ?? '(none)'}`,
213
- ` reviewMode: ${mode.reviewMode}`,
214
- ` executionProtocol: ${mode.executionProtocol}`,
215
- ].join('\n'));
216
- return;
217
- }
218
-
219
- if (sub === 'create-mode') {
220
- const modeId = args[1] as TeamworkModeId | undefined;
221
- const title = args.slice(2).join(' ').trim();
222
- const mode = TEAMWORK_MODES.find((entry) => entry.id === modeId);
223
- if (!mode || !title) {
224
- ctx.print('Usage: /teamwork create-mode <local-engineer|local-shell|remote-engineer|teammate|research|dream|review|verifier|integration> <title...>');
225
- return;
226
- }
227
- printAgentDelegationBoundary(ctx, `/teamwork create-mode ${mode.id} ${title}`);
228
- return;
229
- }
230
-
231
- if (sub === 'recipes') {
232
- ctx.print([
233
- 'Teamwork Recipes',
234
- ...TEAMWORK_RECIPES.map((recipe) => ` ${recipe.id.padEnd(26)} ${recipe.summary}`),
235
- ].join('\n'));
236
- return;
237
- }
238
-
239
- if (sub === 'recipe') {
240
- const recipeId = args[1];
241
- const recipe = TEAMWORK_RECIPES.find((entry) => entry.id === recipeId);
242
- if (!recipe) {
243
- ctx.print('Usage: /teamwork recipe <id>');
244
- return;
245
- }
246
- ctx.print([
247
- `Teamwork Recipe ${recipe.id}`,
248
- ` summary: ${recipe.summary}`,
249
- ` steps: ${recipe.steps.join(' -> ')}`,
250
- ].join('\n'));
251
- return;
252
- }
253
-
254
- if (sub === 'templates') {
255
- const archetypes = listArchetypeModes(requireShellPaths(ctx).workingDirectory);
256
- ctx.print([
257
- 'Teamwork Templates',
258
- ...Object.entries(AGENT_TEMPLATES).map(([name, template]) => (
259
- ` ${name.padEnd(12)} ${template.description}`
260
- )),
261
- '',
262
- 'Discovered Archetypes',
263
- ...archetypes.map((entry) => ` ${entry.id.padEnd(12)} ${entry.family.padEnd(10)} ${entry.source.padEnd(7)} ${entry.label}`),
264
- ].join('\n'));
265
- return;
266
- }
267
-
268
- if (sub === 'archetypes') {
269
- const archetypes = listArchetypeModes(requireShellPaths(ctx).workingDirectory);
270
- ctx.print([
271
- 'Teamwork Archetypes',
272
- ...archetypes.map((entry) => ` ${entry.id.padEnd(18)} ${entry.family.padEnd(10)} ${entry.source.padEnd(7)} ${entry.reviewMode.padEnd(4)} ${entry.executionProtocol}${entry.validationIssues.length > 0 ? ' issues' : ''}`),
273
- ].join('\n'));
274
- return;
275
- }
276
-
277
- if (sub === 'validate') {
278
- const archetypes = listArchetypeModes(requireShellPaths(ctx).workingDirectory);
279
- const invalid = archetypes.filter((entry) => entry.validationIssues.length > 0);
280
- ctx.print(invalid.length > 0
281
- ? [
282
- 'Teamwork Archetype Validation',
283
- ...invalid.flatMap((entry) => [
284
- ` ${entry.id} (${entry.source})`,
285
- ...entry.validationIssues.map((issue) => ` issue: ${issue}`),
286
- ]),
287
- ].join('\n')
288
- : 'Teamwork Archetype Validation\n All discovered archetypes are currently valid.');
289
- return;
290
- }
291
-
292
- if (sub === 'archetype') {
293
- const archetypeName = args[1];
294
- if (!archetypeName) {
295
- ctx.print('Usage: /teamwork archetype <name>');
296
- return;
297
- }
298
- const mode = listArchetypeModes(requireShellPaths(ctx).workingDirectory).find((entry) => entry.id === archetypeName);
299
- if (!mode) {
300
- ctx.print(`Unknown archetype: ${archetypeName}`);
301
- return;
302
- }
303
- ctx.print([
304
- `Teamwork Archetype ${mode.id}`,
305
- ` label: ${mode.label}`,
306
- ` family: ${mode.family}`,
307
- ` source: ${mode.source}`,
308
- ` owner: ${mode.owner}`,
309
- ` reviewMode: ${mode.reviewMode}`,
310
- ` executionProtocol: ${mode.executionProtocol}`,
311
- ` tools: ${mode.tools.join(', ') || '(none)'}`,
312
- ` sourcePath: ${mode.sourcePath ?? '(builtin)'}`,
313
- ...(mode.validationIssues.length > 0
314
- ? mode.validationIssues.map((issue) => ` issue: ${issue}`)
315
- : [' validation: clean']),
316
- ].join('\n'));
317
- return;
318
- }
319
-
320
- if (sub === 'create-archetype') {
321
- const archetypeName = args[1];
322
- const title = args.slice(2).join(' ').trim();
323
- if (!archetypeName || !title) {
324
- ctx.print('Usage: /teamwork create-archetype <name> <title...>');
325
- return;
326
- }
327
- const mode = listArchetypeModes(requireShellPaths(ctx).workingDirectory).find((entry) => entry.id === archetypeName);
328
- if (!mode) {
329
- ctx.print(`Unknown archetype: ${archetypeName}`);
330
- return;
331
- }
332
- printAgentDelegationBoundary(ctx, `/teamwork create-archetype ${mode.id} ${title}`);
333
- return;
334
- }
335
-
336
- ctx.print('Usage: /teamwork [review|modes|mode <id>|create-mode <id> <title...>|recipes|recipe <id>|templates|archetypes|validate|archetype <name>|create-archetype <name> <title...>]');
337
- },
338
- });
339
- }