@syntesseraai/opencode-feature-factory 0.6.7 → 0.6.9

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 (102) hide show
  1. package/README.md +6 -4
  2. package/agents/building.md +28 -541
  3. package/agents/documenting.md +39 -0
  4. package/agents/ff-research.md +18 -410
  5. package/agents/pipeline.md +20 -71
  6. package/agents/planning.md +28 -350
  7. package/agents/reviewing.md +27 -475
  8. package/bin/ff-deploy.js +81 -7
  9. package/commands/pipeline/building/breakdown.md +4 -3
  10. package/commands/pipeline/building/implement-batch.md +4 -3
  11. package/commands/pipeline/building/run.md +8 -8
  12. package/commands/pipeline/building/validate-batch.md +4 -3
  13. package/commands/pipeline/complete.md +1 -1
  14. package/commands/pipeline/documentation/{run-codex.md → document.md} +3 -4
  15. package/commands/pipeline/documentation/gate.md +3 -3
  16. package/commands/pipeline/documentation/{run-gemini.md → review.md} +4 -3
  17. package/commands/pipeline/documentation/run.md +6 -7
  18. package/commands/pipeline/planning/gate.md +8 -6
  19. package/commands/pipeline/planning/plan.md +25 -0
  20. package/commands/pipeline/planning/run.md +7 -7
  21. package/commands/pipeline/planning/synthesize.md +7 -3
  22. package/commands/pipeline/reviewing/gate.md +3 -3
  23. package/commands/pipeline/reviewing/review.md +20 -0
  24. package/commands/pipeline/reviewing/run.md +6 -6
  25. package/commands/pipeline/reviewing/synthesize.md +3 -3
  26. package/commands/pipeline/reviewing/triage.md +2 -2
  27. package/commands/pipeline/start.md +5 -5
  28. package/dist/agent-config.js +17 -51
  29. package/dist/index.d.ts +1 -2
  30. package/dist/index.js +3 -52
  31. package/dist/mcp-config.js +17 -51
  32. package/dist/opencode-global-config.d.ts +9 -0
  33. package/dist/opencode-global-config.js +79 -0
  34. package/dist/plugin-config.js +17 -54
  35. package/package.json +1 -1
  36. package/skills/ff-reviewing-architecture/SKILL.md +34 -0
  37. package/skills/ff-reviewing-code-quality/SKILL.md +34 -0
  38. package/skills/ff-reviewing-documentation/SKILL.md +34 -0
  39. package/skills/ff-reviewing-security/SKILL.md +34 -0
  40. package/agents/ff-acceptance.md +0 -285
  41. package/agents/ff-building-codex.md +0 -305
  42. package/agents/ff-building-gemini.md +0 -305
  43. package/agents/ff-building-opus.md +0 -305
  44. package/agents/ff-planning-codex.md +0 -335
  45. package/agents/ff-planning-gemini.md +0 -335
  46. package/agents/ff-planning-opus.md +0 -335
  47. package/agents/ff-review.md +0 -288
  48. package/agents/ff-reviewing-codex.md +0 -259
  49. package/agents/ff-reviewing-gemini.md +0 -259
  50. package/agents/ff-reviewing-opus.md +0 -259
  51. package/agents/ff-security.md +0 -322
  52. package/agents/ff-validate.md +0 -316
  53. package/agents/ff-well-architected.md +0 -284
  54. package/commands/pipeline/planning/run-codex.md +0 -22
  55. package/commands/pipeline/planning/run-gemini.md +0 -21
  56. package/commands/pipeline/planning/run-opus.md +0 -21
  57. package/commands/pipeline/reviewing/run-codex.md +0 -12
  58. package/commands/pipeline/reviewing/run-gemini.md +0 -11
  59. package/commands/pipeline/reviewing/run-opus.md +0 -11
  60. package/dist/agent-context.d.ts +0 -57
  61. package/dist/agent-context.js +0 -282
  62. package/dist/plugins/ff-agent-context-create-plugin.d.ts +0 -2
  63. package/dist/plugins/ff-agent-context-create-plugin.js +0 -82
  64. package/dist/plugins/ff-agent-context-update-plugin.d.ts +0 -2
  65. package/dist/plugins/ff-agent-context-update-plugin.js +0 -78
  66. package/dist/plugins/ff-agents-clear-plugin.d.ts +0 -2
  67. package/dist/plugins/ff-agents-clear-plugin.js +0 -40
  68. package/dist/plugins/ff-agents-current-plugin.d.ts +0 -2
  69. package/dist/plugins/ff-agents-current-plugin.js +0 -45
  70. package/dist/plugins/ff-agents-delete-plugin.d.ts +0 -2
  71. package/dist/plugins/ff-agents-delete-plugin.js +0 -32
  72. package/dist/plugins/ff-agents-get-plugin.d.ts +0 -2
  73. package/dist/plugins/ff-agents-get-plugin.js +0 -32
  74. package/dist/plugins/ff-agents-list-plugin.d.ts +0 -2
  75. package/dist/plugins/ff-agents-list-plugin.js +0 -42
  76. package/dist/plugins/ff-agents-show-plugin.d.ts +0 -2
  77. package/dist/plugins/ff-agents-show-plugin.js +0 -22
  78. package/dist/plugins/ff-agents-update-plugin.d.ts +0 -2
  79. package/dist/plugins/ff-agents-update-plugin.js +0 -32
  80. package/dist/plugins/ff-plan-create-plugin.d.ts +0 -2
  81. package/dist/plugins/ff-plan-create-plugin.js +0 -61
  82. package/dist/plugins/ff-plan-update-plugin.d.ts +0 -2
  83. package/dist/plugins/ff-plan-update-plugin.js +0 -142
  84. package/dist/plugins/ff-plans-delete-plugin.d.ts +0 -2
  85. package/dist/plugins/ff-plans-delete-plugin.js +0 -32
  86. package/dist/plugins/ff-plans-get-plugin.d.ts +0 -2
  87. package/dist/plugins/ff-plans-get-plugin.js +0 -32
  88. package/dist/plugins/ff-plans-list-plugin.d.ts +0 -2
  89. package/dist/plugins/ff-plans-list-plugin.js +0 -42
  90. package/dist/plugins/ff-plans-update-plugin.d.ts +0 -2
  91. package/dist/plugins/ff-plans-update-plugin.js +0 -32
  92. package/dist/plugins/ff-review-create-plugin.d.ts +0 -2
  93. package/dist/plugins/ff-review-create-plugin.js +0 -256
  94. package/dist/plugins/ff-reviews-get-plugin.d.ts +0 -2
  95. package/dist/plugins/ff-reviews-get-plugin.js +0 -32
  96. package/dist/plugins/ff-reviews-list-plugin.d.ts +0 -2
  97. package/dist/plugins/ff-reviews-list-plugin.js +0 -42
  98. package/dist/plugins/ff-reviews-update-plugin.d.ts +0 -2
  99. package/dist/plugins/ff-reviews-update-plugin.js +0 -32
  100. package/skills/ff-context-tracking/SKILL.md +0 -573
  101. package/skills/ff-delegation/SKILL.md +0 -457
  102. package/skills/ff-swarm/SKILL.md +0 -209
@@ -1,282 +0,0 @@
1
- import { isValidUUID } from './uuid.js';
2
- import { writeFile, readFile, readdir, stat, unlink } from 'fs/promises';
3
- /**
4
- * Generate the content for an agent context file
5
- */
6
- function generateContextFileContent(context) {
7
- const frontmatter = `---
8
- id: "${context.id}"
9
- agent: ${context.agent}
10
- title: "${context.title}"
11
- description: "${context.description}"
12
- folder: "${context.folder}"
13
- status: ${context.status}
14
- started: "${context.started}"
15
- session: "${context.session}"
16
- ${context.parent ? `parent: "${context.parent}"` : 'parent: null'}
17
- ${context.delegated_to && context.delegated_to.length > 0 ? `delegated_to:\n${context.delegated_to.map((id) => ` - "${id}"`).join('\n')}` : 'delegated_to: []'}
18
- ---`;
19
- const body = `
20
-
21
- ## Task Context
22
-
23
- ${context.notes || 'No additional notes.'}
24
-
25
- ## Progress
26
-
27
- - [ ] Task started
28
-
29
- ## Delegated Work
30
-
31
- ${context.delegated_to && context.delegated_to.length > 0 ? context.delegated_to.map((id) => `- Agent ${id} (pending)`).join('\n') : 'No delegated work.'}
32
- `;
33
- return frontmatter + body;
34
- }
35
- /**
36
- * Write an agent context file
37
- * File naming: {agent}-{uuid}.md
38
- */
39
- export async function writeAgentContext(directory, context) {
40
- const fileName = `${context.agent}-${context.id}.md`;
41
- const filePath = `${directory}/.feature-factory/agents/${fileName}`;
42
- const content = generateContextFileContent(context);
43
- try {
44
- await writeFile(filePath, content, 'utf-8');
45
- return filePath;
46
- }
47
- catch (error) {
48
- throw new Error(`Failed to write agent context file: ${error}`);
49
- }
50
- }
51
- /**
52
- * Read an agent context file by UUID
53
- */
54
- export async function readAgentContextById(directory, id) {
55
- if (!isValidUUID(id)) {
56
- return null;
57
- }
58
- try {
59
- // Read directory and find file with this UUID
60
- const agentsDir = `${directory}/.feature-factory/agents`;
61
- const entries = await readdir(agentsDir, { withFileTypes: true });
62
- const fileName = entries
63
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
64
- .map((entry) => entry.name)
65
- .find((name) => name.includes(`-${id}.md`));
66
- if (!fileName) {
67
- return null;
68
- }
69
- const filePath = `${agentsDir}/${fileName}`;
70
- const content = await readFile(filePath, 'utf-8');
71
- return parseAgentContext(content);
72
- }
73
- catch {
74
- return null;
75
- }
76
- }
77
- /**
78
- * Parse agent context from markdown content
79
- */
80
- function parseAgentContext(content) {
81
- try {
82
- // Extract frontmatter
83
- const frontmatterMatch = content.match(/---\n([\s\S]*?)\n---/);
84
- if (!frontmatterMatch) {
85
- return null;
86
- }
87
- const frontmatter = frontmatterMatch[1];
88
- const lines = frontmatter.split('\n');
89
- const context = {};
90
- for (const line of lines) {
91
- const match = line.match(/^([a-z_]+):\s*(.*)$/);
92
- if (match) {
93
- const [, key, value] = match;
94
- const cleanValue = value.replace(/^["']|["']$/g, ''); // Remove quotes
95
- if (key === 'delegated_to') {
96
- // Handle array - this is simplified, real YAML parsing would be better
97
- continue;
98
- }
99
- else if (key === 'parent' && cleanValue === 'null') {
100
- context[key] = undefined;
101
- }
102
- else {
103
- context[key] = cleanValue;
104
- }
105
- }
106
- }
107
- // Parse delegated_to array manually from content
108
- const delegatedMatch = content.match(/delegated_to:\n((?: {2}- ".*"\n?)*)/);
109
- if (delegatedMatch) {
110
- const delegatedLines = delegatedMatch[1].trim().split('\n');
111
- context.delegated_to = delegatedLines
112
- .map((line) => line.match(/- "([^"]+)"/)?.[1])
113
- .filter((id) => !!id);
114
- }
115
- return context;
116
- }
117
- catch {
118
- return null;
119
- }
120
- }
121
- /**
122
- * Update agent status in context file
123
- */
124
- export async function updateAgentStatus(directory, id, status) {
125
- try {
126
- const agentsDir = `${directory}/.feature-factory/agents`;
127
- const entries = await readdir(agentsDir, { withFileTypes: true });
128
- const fileName = entries
129
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
130
- .map((entry) => entry.name)
131
- .find((name) => name.includes(`-${id}.md`));
132
- if (!fileName) {
133
- return false;
134
- }
135
- const filePath = `${agentsDir}/${fileName}`;
136
- // Read current content
137
- const content = await readFile(filePath, 'utf-8');
138
- let text = content;
139
- // Replace status line
140
- text = text.replace(/status: \w+/, `status: ${status}`);
141
- // Write back
142
- await writeFile(filePath, text, 'utf-8');
143
- return true;
144
- }
145
- catch {
146
- return false;
147
- }
148
- }
149
- /**
150
- * List all active agents
151
- */
152
- export async function listActiveAgents(directory, sessionId, agentType) {
153
- const agentsDir = `${directory}/.feature-factory/agents`;
154
- try {
155
- // Check if directory exists
156
- await stat(agentsDir);
157
- }
158
- catch {
159
- return [];
160
- }
161
- try {
162
- const entries = await readdir(agentsDir, { withFileTypes: true });
163
- const files = entries
164
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
165
- .map((entry) => `${agentsDir}/${entry.name}`);
166
- const agents = [];
167
- for (const filePath of files) {
168
- try {
169
- const content = await readFile(filePath, 'utf-8');
170
- const context = parseAgentContext(content);
171
- if (context) {
172
- // Apply filters
173
- if (sessionId && context.session !== sessionId) {
174
- continue;
175
- }
176
- if (agentType && context.agent !== agentType) {
177
- continue;
178
- }
179
- agents.push(context);
180
- }
181
- }
182
- catch {
183
- // Skip files that can't be read
184
- continue;
185
- }
186
- }
187
- return agents;
188
- }
189
- catch {
190
- return [];
191
- }
192
- }
193
- /**
194
- * Find agent files by various criteria
195
- */
196
- export async function findAgentFiles(directory, agentType, sessionId) {
197
- const agentsDir = `${directory}/.feature-factory/agents`;
198
- try {
199
- await stat(agentsDir);
200
- }
201
- catch {
202
- return [];
203
- }
204
- try {
205
- const entries = await readdir(agentsDir, { withFileTypes: true });
206
- let files = entries
207
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
208
- .map((entry) => `${agentsDir}/${entry.name}`);
209
- if (agentType) {
210
- files = files.filter((file) => file.includes(`${agentType}-`));
211
- }
212
- if (sessionId) {
213
- // Filter by session ID (need to read files)
214
- const filteredFiles = [];
215
- for (const file of files) {
216
- try {
217
- const content = await readFile(file, 'utf-8');
218
- if (content.includes(`session: "${sessionId}"`)) {
219
- filteredFiles.push(file);
220
- }
221
- }
222
- catch {
223
- continue;
224
- }
225
- }
226
- return filteredFiles;
227
- }
228
- return files;
229
- }
230
- catch {
231
- return [];
232
- }
233
- }
234
- /**
235
- * Find agent file by UUID
236
- */
237
- export async function findAgentFilesById(directory, id) {
238
- if (!isValidUUID(id)) {
239
- return [];
240
- }
241
- try {
242
- const agentsDir = `${directory}/.feature-factory/agents`;
243
- const entries = await readdir(agentsDir, { withFileTypes: true });
244
- return entries
245
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md') && entry.name.includes(`-${id}.md`))
246
- .map((entry) => `${agentsDir}/${entry.name}`);
247
- }
248
- catch {
249
- return [];
250
- }
251
- }
252
- /**
253
- * Find all agent files
254
- */
255
- export async function findAllAgentFiles(directory) {
256
- const agentsDir = `${directory}/.feature-factory/agents`;
257
- try {
258
- const entries = await readdir(agentsDir, { withFileTypes: true });
259
- return entries
260
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
261
- .map((entry) => `${agentsDir}/${entry.name}`);
262
- }
263
- catch {
264
- return [];
265
- }
266
- }
267
- /**
268
- * Delete agent files
269
- */
270
- export async function deleteAgentFiles(directory, files) {
271
- let deletedCount = 0;
272
- for (const file of files) {
273
- try {
274
- await unlink(file);
275
- deletedCount++;
276
- }
277
- catch {
278
- // Continue even if one file fails
279
- }
280
- }
281
- return deletedCount;
282
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentContextCreateTool(): ReturnType<typeof tool>;
@@ -1,82 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { writeFile, mkdir } from 'fs/promises';
3
- import { dirname } from 'path';
4
- export function createFFAgentContextCreateTool() {
5
- return tool({
6
- description: 'Create a new agent context file in .feature-factory/agents/. Use this to document agent tasks, track progress, and maintain delegation chains.',
7
- args: {
8
- id: tool.schema.string().describe('Unique UUID for this agent instance'),
9
- agent: tool.schema
10
- .string()
11
- .describe('Agent type (e.g., planning, building, reviewing, ff-research)'),
12
- title: tool.schema.string().describe('Task title'),
13
- description: tool.schema.string().describe('Detailed task description'),
14
- status: tool.schema
15
- .enum(['in-progress', 'completed', 'delegated', 'failed'])
16
- .default('in-progress')
17
- .describe('Current status'),
18
- parent: tool.schema.string().optional().describe('Parent agent UUID if this was delegated'),
19
- delegatedTo: tool.schema
20
- .array(tool.schema.string())
21
- .optional()
22
- .describe('Array of child agent UUIDs'),
23
- notes: tool.schema.string().optional().describe('Additional notes or context'),
24
- },
25
- async execute(args, toolCtx) {
26
- try {
27
- const timestamp = new Date().toISOString();
28
- const filePath = `${toolCtx.directory}/.feature-factory/agents/${args.agent}-${args.id}.md`;
29
- // Generate frontmatter
30
- const frontmatter = `---
31
-
32
- id: "${args.id}"
33
- agent: ${args.agent}
34
- title: "${args.title}"
35
- description: "${args.description}"
36
- folder: "${toolCtx.directory}"
37
- status: ${args.status}
38
- started: "${timestamp}"
39
- session: "${args.agent}-${args.id}"
40
- ${args.parent ? `parent: "${args.parent}"` : 'parent: null'}
41
- ${args.delegatedTo && args.delegatedTo.length > 0 ? `delegated_to:\n${args.delegatedTo.map((id) => ` - "${id}"`).join('\n')}` : 'delegated_to: []'}
42
-
43
- ---`;
44
- const body = `
45
-
46
- ## Task Context
47
-
48
- ${args.description}
49
-
50
- ## Progress
51
-
52
- - [x] Task started
53
- - [ ] Analysis complete
54
- - [ ] Implementation pending
55
-
56
- ## Delegated Work
57
-
58
- No delegated work yet.
59
-
60
- ${args.notes ? `## Notes\n\n${args.notes}` : ''}
61
- `;
62
- const fullContent = `${frontmatter}${body}`;
63
- // Ensure directory exists
64
- await mkdir(dirname(filePath), { recursive: true });
65
- // Write file
66
- await writeFile(filePath, fullContent, 'utf-8');
67
- return JSON.stringify({
68
- success: true,
69
- agentId: args.id,
70
- filePath: `.feature-factory/agents/${args.agent}-${args.id}.md`,
71
- message: `Agent context created successfully at .feature-factory/agents/${args.agent}-${args.id}.md`,
72
- }, null, 2);
73
- }
74
- catch (error) {
75
- return JSON.stringify({
76
- success: false,
77
- error: `Failed to create agent context: ${error}`,
78
- }, null, 2);
79
- }
80
- },
81
- });
82
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentContextUpdateTool(): ReturnType<typeof tool>;
@@ -1,78 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { readFile, writeFile } from 'fs/promises';
3
- export function createFFAgentContextUpdateTool() {
4
- return tool({
5
- description: 'Update an existing agent context file in .feature-factory/agents/. Use this to update status, add delegated agents, or append notes.',
6
- args: {
7
- agentId: tool.schema.string().describe('Agent UUID to update'),
8
- agent: tool.schema.string().describe('Agent type'),
9
- status: tool.schema
10
- .enum(['in-progress', 'completed', 'delegated', 'failed'])
11
- .optional()
12
- .describe('New status'),
13
- addDelegatedTo: tool.schema
14
- .string()
15
- .optional()
16
- .describe('Add a child agent UUID to delegated_to list'),
17
- notes: tool.schema.string().optional().describe('Notes to append to the file'),
18
- progressUpdate: tool.schema
19
- .string()
20
- .optional()
21
- .describe('Progress update to add (e.g., "- [x] Step completed")'),
22
- },
23
- async execute(args, toolCtx) {
24
- try {
25
- const filePath = `${toolCtx.directory}/.feature-factory/agents/${args.agent}-${args.agentId}.md`;
26
- // Read existing file
27
- let content;
28
- try {
29
- content = await readFile(filePath, 'utf-8');
30
- }
31
- catch {
32
- return JSON.stringify({
33
- success: false,
34
- error: `Agent context file not found: ${filePath}`,
35
- }, null, 2);
36
- }
37
- // Update status if provided
38
- if (args.status) {
39
- content = content.replace(/status: (in-progress|completed|delegated|failed)/, `status: ${args.status}`);
40
- }
41
- // Add delegated agent if provided
42
- if (args.addDelegatedTo) {
43
- if (content.includes('delegated_to: []')) {
44
- content = content.replace('delegated_to: []', `delegated_to:\n - "${args.addDelegatedTo}"`);
45
- }
46
- else if (content.includes('delegated_to:')) {
47
- content = content.replace(/(delegated_to:\n(?: {2}- ".*"\n)*)/, `$1 - "${args.addDelegatedTo}"\n`);
48
- }
49
- }
50
- // Append notes if provided
51
- if (args.notes) {
52
- const timestamp = new Date().toISOString();
53
- content += `\n\n## Update - ${timestamp}\n\n${args.notes}\n`;
54
- }
55
- // Add progress update if provided
56
- if (args.progressUpdate) {
57
- if (content.includes('## Progress')) {
58
- content = content.replace(/(## Progress\n\n)/, `$1${args.progressUpdate}\n`);
59
- }
60
- }
61
- // Write updated file
62
- await writeFile(filePath, content, 'utf-8');
63
- return JSON.stringify({
64
- success: true,
65
- agentId: args.agentId,
66
- filePath: `.feature-factory/agents/${args.agent}-${args.agentId}.md`,
67
- message: `Agent context updated successfully`,
68
- }, null, 2);
69
- }
70
- catch (error) {
71
- return JSON.stringify({
72
- success: false,
73
- error: `Failed to update agent context: ${error}`,
74
- }, null, 2);
75
- }
76
- },
77
- });
78
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentsClearTool(): ReturnType<typeof tool>;
@@ -1,40 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { findAgentFiles, findAgentFilesById, findAllAgentFiles, deleteAgentFiles, } from '../agent-context.js';
3
- export function createFFAgentsClearTool() {
4
- return tool({
5
- description: 'Clear agent context files. Can clear all, or filter by session, agent type, or specific UUID',
6
- args: {
7
- sessionID: tool.schema.string().optional().describe('Clear only agents for specific session'),
8
- agent: tool.schema.string().optional().describe('Clear only specific agent type'),
9
- id: tool.schema.string().optional().describe('Clear specific agent by UUID'),
10
- },
11
- async execute(args, toolCtx) {
12
- try {
13
- let files = [];
14
- if (args.id) {
15
- files = await findAgentFilesById(toolCtx.directory, args.id);
16
- }
17
- else if (args.agent && args.sessionID) {
18
- files = await findAgentFiles(toolCtx.directory, args.agent, args.sessionID);
19
- }
20
- else if (args.sessionID) {
21
- files = await findAgentFiles(toolCtx.directory, undefined, args.sessionID);
22
- }
23
- else if (args.agent) {
24
- files = await findAgentFiles(toolCtx.directory, args.agent);
25
- }
26
- else {
27
- files = await findAllAgentFiles(toolCtx.directory);
28
- }
29
- if (files.length === 0) {
30
- return 'No agent context files found to clear.';
31
- }
32
- const deletedCount = await deleteAgentFiles(toolCtx.directory, files);
33
- return `Cleared ${deletedCount} agent context file(s)`;
34
- }
35
- catch (error) {
36
- return `Error clearing agents: ${error}`;
37
- }
38
- },
39
- });
40
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentsCurrentTool(): ReturnType<typeof tool>;
@@ -1,45 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { listActiveAgents } from '../agent-context.js';
3
- export function createFFAgentsCurrentTool() {
4
- return tool({
5
- description: 'List all currently active Feature Factory agents with their UUIDs and status',
6
- args: {
7
- sessionID: tool.schema.string().optional().describe('Filter by specific session ID'),
8
- agent: tool.schema
9
- .string()
10
- .optional()
11
- .describe('Filter by agent type (e.g., planning, research)'),
12
- },
13
- async execute(args, toolCtx) {
14
- try {
15
- const agents = await listActiveAgents(toolCtx.directory, args.sessionID, args.agent);
16
- if (agents.length === 0) {
17
- return JSON.stringify({
18
- count: 0,
19
- message: 'No active agents found',
20
- agents: [],
21
- }, null, 2);
22
- }
23
- return JSON.stringify({
24
- count: agents.length,
25
- agents: agents.map((a) => ({
26
- id: a.id,
27
- agent: a.agent,
28
- title: a.title,
29
- folder: a.folder,
30
- status: a.status,
31
- started: a.started,
32
- session: a.session,
33
- parent: a.parent || null,
34
- delegated_to: a.delegated_to || [],
35
- })),
36
- }, null, 2);
37
- }
38
- catch (error) {
39
- return JSON.stringify({
40
- error: `Failed to list agents: ${error}`,
41
- }, null, 2);
42
- }
43
- },
44
- });
45
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentsDeleteTool(): ReturnType<typeof tool>;
@@ -1,32 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { validateSafePath, resolveSafePath, getFeatureFactoryDir } from '../utils/file-utils.js';
3
- import { unlink } from 'fs/promises';
4
- export function createFFAgentsDeleteTool() {
5
- return tool({
6
- description: 'Delete an agent context file from .feature-factory/agents',
7
- args: {
8
- fileName: tool.schema
9
- .string()
10
- .describe('Name of the agent file to delete (e.g., "planning-abc123.md")'),
11
- },
12
- async execute(args, toolCtx) {
13
- try {
14
- const agentsDir = getFeatureFactoryDir(toolCtx.directory, 'agents');
15
- // Validate the file path
16
- if (!validateSafePath(agentsDir, args.fileName)) {
17
- return `Error: Invalid or unsafe file name "${args.fileName}". Only .md files with alphanumeric names are allowed.`;
18
- }
19
- const filePath = resolveSafePath(agentsDir, args.fileName);
20
- // Delete the file
21
- await unlink(filePath);
22
- return `Successfully deleted agent file: ${args.fileName}`;
23
- }
24
- catch (error) {
25
- if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
26
- return `Error: File "${args.fileName}" not found in .feature-factory/agents`;
27
- }
28
- return `Error deleting agent file: ${error}`;
29
- }
30
- },
31
- });
32
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentsGetTool(): ReturnType<typeof tool>;
@@ -1,32 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { validateSafePath, resolveSafePath, getFeatureFactoryDir } from '../utils/file-utils.js';
3
- import { readFile } from 'fs/promises';
4
- export function createFFAgentsGetTool() {
5
- return tool({
6
- description: 'Read an agent context file by name from .feature-factory/agents',
7
- args: {
8
- fileName: tool.schema
9
- .string()
10
- .describe('Name of the agent file (e.g., "planning-abc123.md")'),
11
- },
12
- async execute(args, toolCtx) {
13
- try {
14
- const agentsDir = getFeatureFactoryDir(toolCtx.directory, 'agents');
15
- // Validate the file path
16
- if (!validateSafePath(agentsDir, args.fileName)) {
17
- return `Error: Invalid or unsafe file name "${args.fileName}". Only .md files with alphanumeric names are allowed.`;
18
- }
19
- const filePath = resolveSafePath(agentsDir, args.fileName);
20
- // Read file content
21
- const content = await readFile(filePath, 'utf-8');
22
- return content;
23
- }
24
- catch (error) {
25
- if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
26
- return `Error: File "${args.fileName}" not found in .feature-factory/agents`;
27
- }
28
- return `Error reading agent file: ${error}`;
29
- }
30
- },
31
- });
32
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentsListTool(): ReturnType<typeof tool>;
@@ -1,42 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- import { validateSafePattern, ensureDirectoryExists, getFeatureFactoryDir, } from '../utils/file-utils.js';
3
- import { readdir } from 'fs/promises';
4
- export function createFFAgentsListTool() {
5
- return tool({
6
- description: 'List all agent context files in .feature-factory/agents',
7
- args: {
8
- pattern: tool.schema
9
- .string()
10
- .optional()
11
- .describe('Optional filter pattern (e.g., "planning-*.md")'),
12
- },
13
- async execute(args, toolCtx) {
14
- try {
15
- const agentsDir = getFeatureFactoryDir(toolCtx.directory, 'agents');
16
- // Ensure the agents directory exists
17
- await ensureDirectoryExists(agentsDir);
18
- // List files
19
- const entries = await readdir(agentsDir, { withFileTypes: true });
20
- let files = entries
21
- .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
22
- .map((entry) => entry.name);
23
- // Apply pattern filter if provided
24
- if (args.pattern) {
25
- if (!validateSafePattern(args.pattern)) {
26
- return `Error: Invalid pattern "${args.pattern}". Only safe pattern characters are allowed.`;
27
- }
28
- // Simple glob-like matching
29
- const regex = new RegExp('^' + args.pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
30
- files = files.filter((f) => regex.test(f));
31
- }
32
- if (files.length === 0) {
33
- return 'No agent files found in .feature-factory/agents';
34
- }
35
- return JSON.stringify(files, null, 2);
36
- }
37
- catch (error) {
38
- return `Error listing agent files: ${error}`;
39
- }
40
- },
41
- });
42
- }
@@ -1,2 +0,0 @@
1
- import { tool } from '@opencode-ai/plugin/tool';
2
- export declare function createFFAgentsShowTool(): ReturnType<typeof tool>;