@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.
- package/README.md +6 -4
- package/agents/building.md +28 -541
- package/agents/documenting.md +39 -0
- package/agents/ff-research.md +18 -410
- package/agents/pipeline.md +20 -71
- package/agents/planning.md +28 -350
- package/agents/reviewing.md +27 -475
- package/bin/ff-deploy.js +81 -7
- package/commands/pipeline/building/breakdown.md +4 -3
- package/commands/pipeline/building/implement-batch.md +4 -3
- package/commands/pipeline/building/run.md +8 -8
- package/commands/pipeline/building/validate-batch.md +4 -3
- package/commands/pipeline/complete.md +1 -1
- package/commands/pipeline/documentation/{run-codex.md → document.md} +3 -4
- package/commands/pipeline/documentation/gate.md +3 -3
- package/commands/pipeline/documentation/{run-gemini.md → review.md} +4 -3
- package/commands/pipeline/documentation/run.md +6 -7
- package/commands/pipeline/planning/gate.md +8 -6
- package/commands/pipeline/planning/plan.md +25 -0
- package/commands/pipeline/planning/run.md +7 -7
- package/commands/pipeline/planning/synthesize.md +7 -3
- package/commands/pipeline/reviewing/gate.md +3 -3
- package/commands/pipeline/reviewing/review.md +20 -0
- package/commands/pipeline/reviewing/run.md +6 -6
- package/commands/pipeline/reviewing/synthesize.md +3 -3
- package/commands/pipeline/reviewing/triage.md +2 -2
- package/commands/pipeline/start.md +5 -5
- package/dist/agent-config.js +17 -51
- package/dist/index.d.ts +1 -2
- package/dist/index.js +3 -52
- package/dist/mcp-config.js +17 -51
- package/dist/opencode-global-config.d.ts +9 -0
- package/dist/opencode-global-config.js +79 -0
- package/dist/plugin-config.js +17 -54
- package/package.json +1 -1
- package/skills/ff-reviewing-architecture/SKILL.md +34 -0
- package/skills/ff-reviewing-code-quality/SKILL.md +34 -0
- package/skills/ff-reviewing-documentation/SKILL.md +34 -0
- package/skills/ff-reviewing-security/SKILL.md +34 -0
- package/agents/ff-acceptance.md +0 -285
- package/agents/ff-building-codex.md +0 -305
- package/agents/ff-building-gemini.md +0 -305
- package/agents/ff-building-opus.md +0 -305
- package/agents/ff-planning-codex.md +0 -335
- package/agents/ff-planning-gemini.md +0 -335
- package/agents/ff-planning-opus.md +0 -335
- package/agents/ff-review.md +0 -288
- package/agents/ff-reviewing-codex.md +0 -259
- package/agents/ff-reviewing-gemini.md +0 -259
- package/agents/ff-reviewing-opus.md +0 -259
- package/agents/ff-security.md +0 -322
- package/agents/ff-validate.md +0 -316
- package/agents/ff-well-architected.md +0 -284
- package/commands/pipeline/planning/run-codex.md +0 -22
- package/commands/pipeline/planning/run-gemini.md +0 -21
- package/commands/pipeline/planning/run-opus.md +0 -21
- package/commands/pipeline/reviewing/run-codex.md +0 -12
- package/commands/pipeline/reviewing/run-gemini.md +0 -11
- package/commands/pipeline/reviewing/run-opus.md +0 -11
- package/dist/agent-context.d.ts +0 -57
- package/dist/agent-context.js +0 -282
- package/dist/plugins/ff-agent-context-create-plugin.d.ts +0 -2
- package/dist/plugins/ff-agent-context-create-plugin.js +0 -82
- package/dist/plugins/ff-agent-context-update-plugin.d.ts +0 -2
- package/dist/plugins/ff-agent-context-update-plugin.js +0 -78
- package/dist/plugins/ff-agents-clear-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-clear-plugin.js +0 -40
- package/dist/plugins/ff-agents-current-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-current-plugin.js +0 -45
- package/dist/plugins/ff-agents-delete-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-delete-plugin.js +0 -32
- package/dist/plugins/ff-agents-get-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-get-plugin.js +0 -32
- package/dist/plugins/ff-agents-list-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-list-plugin.js +0 -42
- package/dist/plugins/ff-agents-show-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-show-plugin.js +0 -22
- package/dist/plugins/ff-agents-update-plugin.d.ts +0 -2
- package/dist/plugins/ff-agents-update-plugin.js +0 -32
- package/dist/plugins/ff-plan-create-plugin.d.ts +0 -2
- package/dist/plugins/ff-plan-create-plugin.js +0 -61
- package/dist/plugins/ff-plan-update-plugin.d.ts +0 -2
- package/dist/plugins/ff-plan-update-plugin.js +0 -142
- package/dist/plugins/ff-plans-delete-plugin.d.ts +0 -2
- package/dist/plugins/ff-plans-delete-plugin.js +0 -32
- package/dist/plugins/ff-plans-get-plugin.d.ts +0 -2
- package/dist/plugins/ff-plans-get-plugin.js +0 -32
- package/dist/plugins/ff-plans-list-plugin.d.ts +0 -2
- package/dist/plugins/ff-plans-list-plugin.js +0 -42
- package/dist/plugins/ff-plans-update-plugin.d.ts +0 -2
- package/dist/plugins/ff-plans-update-plugin.js +0 -32
- package/dist/plugins/ff-review-create-plugin.d.ts +0 -2
- package/dist/plugins/ff-review-create-plugin.js +0 -256
- package/dist/plugins/ff-reviews-get-plugin.d.ts +0 -2
- package/dist/plugins/ff-reviews-get-plugin.js +0 -32
- package/dist/plugins/ff-reviews-list-plugin.d.ts +0 -2
- package/dist/plugins/ff-reviews-list-plugin.js +0 -42
- package/dist/plugins/ff-reviews-update-plugin.d.ts +0 -2
- package/dist/plugins/ff-reviews-update-plugin.js +0 -32
- package/skills/ff-context-tracking/SKILL.md +0 -573
- package/skills/ff-delegation/SKILL.md +0 -457
- package/skills/ff-swarm/SKILL.md +0 -209
package/dist/agent-context.js
DELETED
|
@@ -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,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,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,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,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,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,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,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
|
-
}
|