@compilr-dev/agents 0.2.0 → 0.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/agent.d.ts +8 -0
- package/dist/agent.js +187 -121
- package/dist/anchors/manager.d.ts +34 -0
- package/dist/anchors/manager.js +86 -0
- package/dist/anchors/types.d.ts +29 -0
- package/dist/skills/index.js +2 -179
- package/dist/tools/builtin/ask-user-simple.d.ts +64 -0
- package/dist/tools/builtin/ask-user-simple.js +149 -0
- package/dist/tools/builtin/ask-user.d.ts +85 -0
- package/dist/tools/builtin/ask-user.js +195 -0
- package/dist/tools/builtin/backlog.d.ts +121 -0
- package/dist/tools/builtin/backlog.js +368 -0
- package/dist/tools/builtin/bash.js +180 -13
- package/dist/tools/builtin/index.d.ts +11 -1
- package/dist/tools/builtin/index.js +16 -0
- package/dist/tools/builtin/task.d.ts +14 -4
- package/dist/tools/builtin/task.js +9 -9
- package/dist/tools/define.d.ts +7 -0
- package/dist/tools/define.js +1 -0
- package/dist/tools/registry.d.ts +3 -2
- package/dist/tools/registry.js +19 -6
- package/dist/tools/types.d.ts +29 -2
- package/package.json +4 -4
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backlog Tools - File-based project backlog management
|
|
3
|
+
*
|
|
4
|
+
* Provides backlog_read and backlog_write tools for managing project backlogs.
|
|
5
|
+
* Uses a JSON file stored in .compilr/backlog.json (or backlog.json in project root).
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Read with filters (status, type, search, limit)
|
|
9
|
+
* - Write/update individual items or full backlog
|
|
10
|
+
* - Support for common item types (feature, bug, chore, spike)
|
|
11
|
+
* - Priority levels (critical, high, medium, low)
|
|
12
|
+
*/
|
|
13
|
+
import type { Tool } from '../types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Status of a backlog item
|
|
16
|
+
*/
|
|
17
|
+
export type BacklogStatus = 'backlog' | 'in-progress' | 'done' | 'blocked';
|
|
18
|
+
/**
|
|
19
|
+
* Type of backlog item
|
|
20
|
+
*/
|
|
21
|
+
export type BacklogItemType = 'feature' | 'bug' | 'chore' | 'spike';
|
|
22
|
+
/**
|
|
23
|
+
* Priority level
|
|
24
|
+
*/
|
|
25
|
+
export type BacklogPriority = 'critical' | 'high' | 'medium' | 'low';
|
|
26
|
+
/**
|
|
27
|
+
* A backlog item
|
|
28
|
+
*/
|
|
29
|
+
export interface BacklogItem {
|
|
30
|
+
/** Unique identifier (e.g., "FEAT-001", "BUG-002") */
|
|
31
|
+
id: string;
|
|
32
|
+
/** Type of item */
|
|
33
|
+
type: BacklogItemType;
|
|
34
|
+
/** Short title */
|
|
35
|
+
title: string;
|
|
36
|
+
/** Detailed description (optional) */
|
|
37
|
+
description?: string;
|
|
38
|
+
/** Current status */
|
|
39
|
+
status: BacklogStatus;
|
|
40
|
+
/** Priority level (optional, defaults to medium) */
|
|
41
|
+
priority?: BacklogPriority;
|
|
42
|
+
/** Acceptance criteria (optional) */
|
|
43
|
+
acceptanceCriteria?: string[];
|
|
44
|
+
/** Dependencies on other items (optional) */
|
|
45
|
+
dependencies?: string[];
|
|
46
|
+
/** Labels/tags (optional) */
|
|
47
|
+
labels?: string[];
|
|
48
|
+
/** Created timestamp */
|
|
49
|
+
createdAt?: string;
|
|
50
|
+
/** Last updated timestamp */
|
|
51
|
+
updatedAt?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Input parameters for backlog_read tool
|
|
55
|
+
*/
|
|
56
|
+
export interface BacklogReadInput {
|
|
57
|
+
/** Get a specific item by ID */
|
|
58
|
+
id?: string;
|
|
59
|
+
/** Filter by status */
|
|
60
|
+
status?: BacklogStatus;
|
|
61
|
+
/** Filter by type */
|
|
62
|
+
type?: BacklogItemType;
|
|
63
|
+
/** Search in title and description */
|
|
64
|
+
search?: string;
|
|
65
|
+
/** Filter by priority */
|
|
66
|
+
priority?: BacklogPriority;
|
|
67
|
+
/** Maximum items to return (default: 20) */
|
|
68
|
+
limit?: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Result from backlog_read
|
|
72
|
+
*/
|
|
73
|
+
export interface BacklogReadResult {
|
|
74
|
+
items: BacklogItem[];
|
|
75
|
+
total: number;
|
|
76
|
+
filtered: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Input parameters for backlog_write tool
|
|
80
|
+
*/
|
|
81
|
+
export interface BacklogWriteInput {
|
|
82
|
+
/** Action to perform */
|
|
83
|
+
action: 'add' | 'update' | 'delete' | 'replace';
|
|
84
|
+
/** Item to add or update (required for add/update) */
|
|
85
|
+
item?: Partial<BacklogItem>;
|
|
86
|
+
/** Item ID to delete (required for delete) */
|
|
87
|
+
deleteId?: string;
|
|
88
|
+
/** Full list of items (required for replace action) */
|
|
89
|
+
items?: BacklogItem[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Result from backlog_write
|
|
93
|
+
*/
|
|
94
|
+
export interface BacklogWriteResult {
|
|
95
|
+
success: boolean;
|
|
96
|
+
action: string;
|
|
97
|
+
itemId?: string;
|
|
98
|
+
itemCount?: number;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Read backlog items with optional filters
|
|
102
|
+
*/
|
|
103
|
+
export declare const backlogReadTool: Tool<BacklogReadInput>;
|
|
104
|
+
/**
|
|
105
|
+
* Write/update backlog items
|
|
106
|
+
*/
|
|
107
|
+
export declare const backlogWriteTool: Tool<BacklogWriteInput>;
|
|
108
|
+
/**
|
|
109
|
+
* Options for creating custom backlog tools
|
|
110
|
+
*/
|
|
111
|
+
export interface BacklogToolOptions {
|
|
112
|
+
/** Custom base path for backlog file */
|
|
113
|
+
basePath?: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Create backlog tools with custom options
|
|
117
|
+
*/
|
|
118
|
+
export declare function createBacklogTools(_options?: BacklogToolOptions): {
|
|
119
|
+
backlogRead: Tool<BacklogReadInput>;
|
|
120
|
+
backlogWrite: Tool<BacklogWriteInput>;
|
|
121
|
+
};
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backlog Tools - File-based project backlog management
|
|
3
|
+
*
|
|
4
|
+
* Provides backlog_read and backlog_write tools for managing project backlogs.
|
|
5
|
+
* Uses a JSON file stored in .compilr/backlog.json (or backlog.json in project root).
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Read with filters (status, type, search, limit)
|
|
9
|
+
* - Write/update individual items or full backlog
|
|
10
|
+
* - Support for common item types (feature, bug, chore, spike)
|
|
11
|
+
* - Priority levels (critical, high, medium, low)
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { defineTool, createSuccessResult, createErrorResult } from '../define.js';
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// File Operations
|
|
18
|
+
// =============================================================================
|
|
19
|
+
const BACKLOG_PATHS = ['.compilr/backlog.json', 'backlog.json', '.claude/backlog.json'];
|
|
20
|
+
function findBacklogFile(cwd = process.cwd()) {
|
|
21
|
+
for (const relPath of BACKLOG_PATHS) {
|
|
22
|
+
const fullPath = path.join(cwd, relPath);
|
|
23
|
+
if (fs.existsSync(fullPath)) {
|
|
24
|
+
return fullPath;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function getDefaultBacklogPath(cwd = process.cwd()) {
|
|
30
|
+
// Prefer .compilr directory if it exists
|
|
31
|
+
const compilrDir = path.join(cwd, '.compilr');
|
|
32
|
+
if (fs.existsSync(compilrDir)) {
|
|
33
|
+
return path.join(compilrDir, 'backlog.json');
|
|
34
|
+
}
|
|
35
|
+
// Otherwise use project root
|
|
36
|
+
return path.join(cwd, 'backlog.json');
|
|
37
|
+
}
|
|
38
|
+
function loadBacklog(filePath) {
|
|
39
|
+
try {
|
|
40
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
41
|
+
const data = JSON.parse(content);
|
|
42
|
+
return {
|
|
43
|
+
version: data.version || '1.0',
|
|
44
|
+
items: Array.isArray(data.items) ? data.items : [],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { version: '1.0', items: [] };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function saveBacklog(filePath, backlog) {
|
|
52
|
+
// Ensure directory exists
|
|
53
|
+
const dir = path.dirname(filePath);
|
|
54
|
+
if (!fs.existsSync(dir)) {
|
|
55
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
// Update timestamps
|
|
58
|
+
const now = new Date().toISOString();
|
|
59
|
+
backlog.items = backlog.items.map((item) => ({
|
|
60
|
+
...item,
|
|
61
|
+
updatedAt: now,
|
|
62
|
+
createdAt: item.createdAt || now,
|
|
63
|
+
}));
|
|
64
|
+
fs.writeFileSync(filePath, JSON.stringify(backlog, null, 2), 'utf-8');
|
|
65
|
+
}
|
|
66
|
+
function generateItemId(type, items) {
|
|
67
|
+
const prefix = {
|
|
68
|
+
feature: 'FEAT',
|
|
69
|
+
bug: 'BUG',
|
|
70
|
+
chore: 'CHORE',
|
|
71
|
+
spike: 'SPIKE',
|
|
72
|
+
}[type];
|
|
73
|
+
// Find highest number for this type
|
|
74
|
+
const existingIds = items
|
|
75
|
+
.filter((i) => i.id.startsWith(prefix))
|
|
76
|
+
.map((i) => parseInt(i.id.split('-')[1], 10))
|
|
77
|
+
.filter((n) => !isNaN(n));
|
|
78
|
+
const nextNum = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1;
|
|
79
|
+
return `${prefix}-${String(nextNum).padStart(3, '0')}`;
|
|
80
|
+
}
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// Backlog Read Tool
|
|
83
|
+
// =============================================================================
|
|
84
|
+
/**
|
|
85
|
+
* Read backlog items with optional filters
|
|
86
|
+
*/
|
|
87
|
+
export const backlogReadTool = defineTool({
|
|
88
|
+
name: 'backlog_read',
|
|
89
|
+
description: 'Read backlog items from the project. ' +
|
|
90
|
+
'Use filters to narrow results. Use id parameter to get a specific item. ' +
|
|
91
|
+
'Returns items sorted by priority (critical first) then by creation date.',
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
id: {
|
|
96
|
+
type: 'string',
|
|
97
|
+
description: 'Get a specific item by ID (e.g., "FEAT-001")',
|
|
98
|
+
},
|
|
99
|
+
status: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
enum: ['backlog', 'in-progress', 'done', 'blocked'],
|
|
102
|
+
description: 'Filter by status',
|
|
103
|
+
},
|
|
104
|
+
type: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
enum: ['feature', 'bug', 'chore', 'spike'],
|
|
107
|
+
description: 'Filter by item type',
|
|
108
|
+
},
|
|
109
|
+
search: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: 'Search in title and description',
|
|
112
|
+
},
|
|
113
|
+
priority: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
enum: ['critical', 'high', 'medium', 'low'],
|
|
116
|
+
description: 'Filter by priority',
|
|
117
|
+
},
|
|
118
|
+
limit: {
|
|
119
|
+
type: 'number',
|
|
120
|
+
description: 'Maximum items to return (default: 20)',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
125
|
+
execute: async (input) => {
|
|
126
|
+
try {
|
|
127
|
+
const filePath = findBacklogFile();
|
|
128
|
+
if (!filePath) {
|
|
129
|
+
return createSuccessResult({
|
|
130
|
+
items: [],
|
|
131
|
+
total: 0,
|
|
132
|
+
filtered: 0,
|
|
133
|
+
message: 'No backlog file found. Use backlog_write to create one.',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const backlog = loadBacklog(filePath);
|
|
137
|
+
let items = [...backlog.items];
|
|
138
|
+
const total = items.length;
|
|
139
|
+
// Filter by ID (exact match)
|
|
140
|
+
if (input.id) {
|
|
141
|
+
items = items.filter((i) => i.id === input.id);
|
|
142
|
+
}
|
|
143
|
+
// Filter by status
|
|
144
|
+
if (input.status) {
|
|
145
|
+
items = items.filter((i) => i.status === input.status);
|
|
146
|
+
}
|
|
147
|
+
// Filter by type
|
|
148
|
+
if (input.type) {
|
|
149
|
+
items = items.filter((i) => i.type === input.type);
|
|
150
|
+
}
|
|
151
|
+
// Filter by priority
|
|
152
|
+
if (input.priority) {
|
|
153
|
+
items = items.filter((i) => i.priority === input.priority);
|
|
154
|
+
}
|
|
155
|
+
// Search in title and description
|
|
156
|
+
if (input.search) {
|
|
157
|
+
const query = input.search.toLowerCase();
|
|
158
|
+
items = items.filter((i) => i.title.toLowerCase().includes(query) || i.description?.toLowerCase().includes(query));
|
|
159
|
+
}
|
|
160
|
+
// Sort by priority then by creation date
|
|
161
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
162
|
+
items.sort((a, b) => {
|
|
163
|
+
const pa = priorityOrder[a.priority || 'medium'];
|
|
164
|
+
const pb = priorityOrder[b.priority || 'medium'];
|
|
165
|
+
if (pa !== pb)
|
|
166
|
+
return pa - pb;
|
|
167
|
+
return (a.createdAt || '').localeCompare(b.createdAt || '');
|
|
168
|
+
});
|
|
169
|
+
// Apply limit
|
|
170
|
+
const limit = input.limit ?? 20;
|
|
171
|
+
const filtered = items.length;
|
|
172
|
+
items = items.slice(0, limit);
|
|
173
|
+
return createSuccessResult({
|
|
174
|
+
items,
|
|
175
|
+
total,
|
|
176
|
+
filtered,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
// =============================================================================
|
|
185
|
+
// Backlog Write Tool
|
|
186
|
+
// =============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Write/update backlog items
|
|
189
|
+
*/
|
|
190
|
+
export const backlogWriteTool = defineTool({
|
|
191
|
+
name: 'backlog_write',
|
|
192
|
+
description: 'Create, update, or delete backlog items. ' +
|
|
193
|
+
'Actions: add (new item), update (modify existing), delete (remove), replace (full list). ' +
|
|
194
|
+
'IDs are auto-generated for new items based on type (e.g., FEAT-001, BUG-002).',
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
action: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
enum: ['add', 'update', 'delete', 'replace'],
|
|
201
|
+
description: 'Action to perform',
|
|
202
|
+
},
|
|
203
|
+
item: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
description: 'Item to add or update (for add/update actions)',
|
|
206
|
+
properties: {
|
|
207
|
+
id: { type: 'string', description: 'Item ID (required for update)' },
|
|
208
|
+
type: {
|
|
209
|
+
type: 'string',
|
|
210
|
+
enum: ['feature', 'bug', 'chore', 'spike'],
|
|
211
|
+
description: 'Item type (required for add)',
|
|
212
|
+
},
|
|
213
|
+
title: { type: 'string', description: 'Item title' },
|
|
214
|
+
description: { type: 'string', description: 'Detailed description' },
|
|
215
|
+
status: {
|
|
216
|
+
type: 'string',
|
|
217
|
+
enum: ['backlog', 'in-progress', 'done', 'blocked'],
|
|
218
|
+
description: 'Item status',
|
|
219
|
+
},
|
|
220
|
+
priority: {
|
|
221
|
+
type: 'string',
|
|
222
|
+
enum: ['critical', 'high', 'medium', 'low'],
|
|
223
|
+
description: 'Priority level',
|
|
224
|
+
},
|
|
225
|
+
acceptanceCriteria: {
|
|
226
|
+
type: 'array',
|
|
227
|
+
items: { type: 'string' },
|
|
228
|
+
description: 'Acceptance criteria list',
|
|
229
|
+
},
|
|
230
|
+
dependencies: {
|
|
231
|
+
type: 'array',
|
|
232
|
+
items: { type: 'string' },
|
|
233
|
+
description: 'IDs of items this depends on',
|
|
234
|
+
},
|
|
235
|
+
labels: {
|
|
236
|
+
type: 'array',
|
|
237
|
+
items: { type: 'string' },
|
|
238
|
+
description: 'Labels/tags',
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
deleteId: {
|
|
243
|
+
type: 'string',
|
|
244
|
+
description: 'Item ID to delete (for delete action)',
|
|
245
|
+
},
|
|
246
|
+
items: {
|
|
247
|
+
type: 'array',
|
|
248
|
+
description: 'Full list of items (for replace action)',
|
|
249
|
+
items: {
|
|
250
|
+
type: 'object',
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
required: ['action'],
|
|
255
|
+
},
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
257
|
+
execute: async (input) => {
|
|
258
|
+
try {
|
|
259
|
+
const existingPath = findBacklogFile();
|
|
260
|
+
const filePath = existingPath || getDefaultBacklogPath();
|
|
261
|
+
const backlog = existingPath ? loadBacklog(existingPath) : { version: '1.0', items: [] };
|
|
262
|
+
switch (input.action) {
|
|
263
|
+
case 'add': {
|
|
264
|
+
if (!input.item) {
|
|
265
|
+
return createErrorResult('Item is required for add action');
|
|
266
|
+
}
|
|
267
|
+
const itemType = input.item.type;
|
|
268
|
+
const itemTitle = input.item.title;
|
|
269
|
+
if (!itemType) {
|
|
270
|
+
return createErrorResult('Item type is required for add action');
|
|
271
|
+
}
|
|
272
|
+
if (!itemTitle) {
|
|
273
|
+
return createErrorResult('Item title is required for add action');
|
|
274
|
+
}
|
|
275
|
+
const newItem = {
|
|
276
|
+
id: generateItemId(itemType, backlog.items),
|
|
277
|
+
type: itemType,
|
|
278
|
+
title: itemTitle,
|
|
279
|
+
description: input.item.description,
|
|
280
|
+
status: input.item.status ?? 'backlog',
|
|
281
|
+
priority: input.item.priority,
|
|
282
|
+
acceptanceCriteria: input.item.acceptanceCriteria,
|
|
283
|
+
dependencies: input.item.dependencies,
|
|
284
|
+
labels: input.item.labels,
|
|
285
|
+
};
|
|
286
|
+
backlog.items.push(newItem);
|
|
287
|
+
saveBacklog(filePath, backlog);
|
|
288
|
+
return createSuccessResult({
|
|
289
|
+
success: true,
|
|
290
|
+
action: 'add',
|
|
291
|
+
itemId: newItem.id,
|
|
292
|
+
itemCount: backlog.items.length,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
case 'update': {
|
|
296
|
+
const updateItem = input.item;
|
|
297
|
+
const updateId = updateItem?.id;
|
|
298
|
+
if (!updateItem || !updateId) {
|
|
299
|
+
return createErrorResult('Item ID is required for update action');
|
|
300
|
+
}
|
|
301
|
+
const idx = backlog.items.findIndex((i) => i.id === updateId);
|
|
302
|
+
if (idx === -1) {
|
|
303
|
+
return createErrorResult(`Item not found: ${updateId}`);
|
|
304
|
+
}
|
|
305
|
+
// Merge updates
|
|
306
|
+
backlog.items[idx] = {
|
|
307
|
+
...backlog.items[idx],
|
|
308
|
+
...updateItem,
|
|
309
|
+
id: backlog.items[idx].id, // Preserve original ID
|
|
310
|
+
};
|
|
311
|
+
saveBacklog(filePath, backlog);
|
|
312
|
+
return createSuccessResult({
|
|
313
|
+
success: true,
|
|
314
|
+
action: 'update',
|
|
315
|
+
itemId: updateId,
|
|
316
|
+
itemCount: backlog.items.length,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
case 'delete': {
|
|
320
|
+
const deleteId = input.deleteId || input.item?.id;
|
|
321
|
+
if (!deleteId) {
|
|
322
|
+
return createErrorResult('deleteId or item.id is required for delete action');
|
|
323
|
+
}
|
|
324
|
+
const initialCount = backlog.items.length;
|
|
325
|
+
backlog.items = backlog.items.filter((i) => i.id !== deleteId);
|
|
326
|
+
if (backlog.items.length === initialCount) {
|
|
327
|
+
return createErrorResult(`Item not found: ${deleteId}`);
|
|
328
|
+
}
|
|
329
|
+
saveBacklog(filePath, backlog);
|
|
330
|
+
return createSuccessResult({
|
|
331
|
+
success: true,
|
|
332
|
+
action: 'delete',
|
|
333
|
+
itemId: deleteId,
|
|
334
|
+
itemCount: backlog.items.length,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
case 'replace': {
|
|
338
|
+
if (!input.items) {
|
|
339
|
+
return createErrorResult('Items array is required for replace action');
|
|
340
|
+
}
|
|
341
|
+
backlog.items = input.items;
|
|
342
|
+
saveBacklog(filePath, backlog);
|
|
343
|
+
return createSuccessResult({
|
|
344
|
+
success: true,
|
|
345
|
+
action: 'replace',
|
|
346
|
+
itemCount: backlog.items.length,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
default:
|
|
350
|
+
return createErrorResult(`Unknown action: ${input.action}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
/**
|
|
359
|
+
* Create backlog tools with custom options
|
|
360
|
+
*/
|
|
361
|
+
export function createBacklogTools(_options = {}) {
|
|
362
|
+
// For now, just return the default tools
|
|
363
|
+
// Future: could customize file paths, storage backend, etc.
|
|
364
|
+
return {
|
|
365
|
+
backlogRead: backlogReadTool,
|
|
366
|
+
backlogWrite: backlogWriteTool,
|
|
367
|
+
};
|
|
368
|
+
}
|