mcp-task-server 0.1.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/tools.js ADDED
@@ -0,0 +1,732 @@
1
+ import { z } from "zod";
2
+ import { readFile, writeFile, mkdir } from "fs/promises";
3
+ import { dirname } from "path";
4
+ import { existsSync, statSync, readdirSync } from "fs";
5
+ import { join } from "path";
6
+ import { loadTasks, saveTasks, generateId, findTask, updateTask as updateTaskInStore, addSubtaskToTask, } from "./storage.js";
7
+ import { hasPermission, canActOnTask, getOrCreateAgent } from "./agents.js";
8
+ import { expandTaskPrompt, parsePrdPrompt, researchTaskPrompt, analyzeComplexityPrompt, checkCompliancePrompt, } from "./prompts.js";
9
+ import { resolvePath } from "./config.js";
10
+ import { getTemplateFiles, getInitialTasksJson } from "./templates.js";
11
+ // ============================================================================
12
+ // Schema Definitions
13
+ // ============================================================================
14
+ const AgentContextSchema = z.object({
15
+ agent_id: z.string().optional(),
16
+ role: z.enum(["planner", "worker", "judge"]).optional(),
17
+ });
18
+ export const ListTasksSchema = AgentContextSchema.extend({
19
+ status: z
20
+ .enum([
21
+ "pending",
22
+ "claimed",
23
+ "in_progress",
24
+ "review",
25
+ "approved",
26
+ "rejected",
27
+ "completed",
28
+ "cancelled",
29
+ "done",
30
+ "deprecated",
31
+ ])
32
+ .optional(),
33
+ assigned_to: z.string().optional(),
34
+ });
35
+ export const GetTaskSchema = AgentContextSchema.extend({
36
+ task_id: z.string(),
37
+ });
38
+ export const AddTaskSchema = AgentContextSchema.extend({
39
+ title: z.string().optional(),
40
+ content: z.string().optional(), // Legacy alias for title
41
+ description: z.string().optional(),
42
+ details: z.string().optional(),
43
+ testStrategy: z.string().optional(),
44
+ priority: z.enum(["low", "medium", "high", "critical"]).default("medium"),
45
+ dependencies: z.array(z.string()).optional(),
46
+ });
47
+ export const UpdateTaskSchema = AgentContextSchema.extend({
48
+ task_id: z.string(),
49
+ title: z.string().optional(),
50
+ content: z.string().optional(), // Legacy alias for title
51
+ description: z.string().optional(),
52
+ details: z.string().optional(),
53
+ testStrategy: z.string().optional(),
54
+ status: z
55
+ .enum([
56
+ "pending",
57
+ "claimed",
58
+ "in_progress",
59
+ "review",
60
+ "approved",
61
+ "rejected",
62
+ "completed",
63
+ "cancelled",
64
+ "done",
65
+ "deprecated",
66
+ ])
67
+ .optional(),
68
+ priority: z.enum(["low", "medium", "high", "critical"]).optional(),
69
+ metadata: z.record(z.unknown()).optional(),
70
+ });
71
+ export const CompleteTaskSchema = AgentContextSchema.extend({
72
+ task_id: z.string(),
73
+ });
74
+ export const NextTaskSchema = AgentContextSchema.extend({});
75
+ export const ClaimTaskSchema = AgentContextSchema.extend({
76
+ task_id: z.string(),
77
+ });
78
+ export const ReleaseTaskSchema = AgentContextSchema.extend({
79
+ task_id: z.string(),
80
+ });
81
+ export const HandoffTaskSchema = AgentContextSchema.extend({
82
+ task_id: z.string(),
83
+ to_role: z.enum(["planner", "worker", "judge"]),
84
+ notes: z.string().optional(),
85
+ });
86
+ export const ReviewTaskSchema = AgentContextSchema.extend({
87
+ task_id: z.string(),
88
+ });
89
+ export const ApproveTaskSchema = AgentContextSchema.extend({
90
+ task_id: z.string(),
91
+ notes: z.string().optional(),
92
+ });
93
+ export const RejectTaskSchema = AgentContextSchema.extend({
94
+ task_id: z.string(),
95
+ feedback: z.string(),
96
+ });
97
+ export const ExpandTaskSchema = AgentContextSchema.extend({
98
+ task_id: z.string(),
99
+ });
100
+ export const AddSubtaskSchema = AgentContextSchema.extend({
101
+ parent_id: z.string(),
102
+ title: z.string().optional(),
103
+ content: z.string().optional(), // Legacy alias for title
104
+ description: z.string().optional(),
105
+ details: z.string().optional(),
106
+ dependencies: z.array(z.number()).optional(),
107
+ });
108
+ export const SetDependenciesSchema = AgentContextSchema.extend({
109
+ task_id: z.string(),
110
+ dependencies: z.array(z.string()),
111
+ });
112
+ export const RemoveTaskSchema = AgentContextSchema.extend({
113
+ task_id: z.string(),
114
+ });
115
+ export const ParsePrdSchema = AgentContextSchema.extend({
116
+ prd_content: z.string(),
117
+ });
118
+ export const ResearchTaskSchema = AgentContextSchema.extend({
119
+ task_id: z.string(),
120
+ topic: z.string().optional(),
121
+ });
122
+ export const AnalyzeComplexitySchema = AgentContextSchema.extend({
123
+ task_id: z.string(),
124
+ });
125
+ export const CheckComplianceSchema = AgentContextSchema.extend({
126
+ path: z.string(),
127
+ fix: z.boolean().default(false),
128
+ });
129
+ export const InitProjectSchema = z.object({
130
+ project_name: z.string().optional(),
131
+ force: z.boolean().default(false),
132
+ });
133
+ function result(data) {
134
+ return {
135
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
136
+ };
137
+ }
138
+ function error(message) {
139
+ return {
140
+ content: [{ type: "text", text: JSON.stringify({ error: message }) }],
141
+ };
142
+ }
143
+ function checkPermission(role, tool) {
144
+ if (!hasPermission(role, tool)) {
145
+ return error(`Role '${role}' does not have permission to use '${tool}'`);
146
+ }
147
+ return null;
148
+ }
149
+ // ============================================================================
150
+ // Core Tools
151
+ // ============================================================================
152
+ export async function listTasks(config, params) {
153
+ const permError = checkPermission(params.role, "list_tasks");
154
+ if (permError)
155
+ return permError;
156
+ const store = await loadTasks(config);
157
+ let tasks = store.tasks;
158
+ if (params.status) {
159
+ tasks = tasks.filter((t) => t.status === params.status);
160
+ }
161
+ if (params.assigned_to) {
162
+ tasks = tasks.filter((t) => t.assigned_to === params.assigned_to);
163
+ }
164
+ return result({ tasks, total: tasks.length });
165
+ }
166
+ export async function getTask(config, params) {
167
+ const permError = checkPermission(params.role, "get_task");
168
+ if (permError)
169
+ return permError;
170
+ const store = await loadTasks(config);
171
+ const task = findTask(store, params.task_id);
172
+ if (!task) {
173
+ return error(`Task '${params.task_id}' not found`);
174
+ }
175
+ return result({ task });
176
+ }
177
+ export async function addTask(config, params) {
178
+ const permError = checkPermission(params.role, "add_task");
179
+ if (permError)
180
+ return permError;
181
+ // Use title or content (legacy)
182
+ const title = params.title || params.content;
183
+ if (!title) {
184
+ return error("Either 'title' or 'content' is required");
185
+ }
186
+ const store = await loadTasks(config);
187
+ const id = generateId(store);
188
+ const task = {
189
+ id,
190
+ title,
191
+ description: params.description,
192
+ details: params.details,
193
+ testStrategy: params.testStrategy,
194
+ status: "pending",
195
+ priority: params.priority,
196
+ dependencies: params.dependencies,
197
+ created_at: new Date().toISOString(),
198
+ updated_at: new Date().toISOString(),
199
+ };
200
+ store.tasks.push(task);
201
+ await saveTasks(config, store);
202
+ return result({ task, message: `Task ${id} created` });
203
+ }
204
+ export async function updateTask(config, params) {
205
+ const permError = checkPermission(params.role, "update_task");
206
+ if (permError)
207
+ return permError;
208
+ const store = await loadTasks(config);
209
+ const canAct = canActOnTask(store, params.task_id, params.agent_id, "update");
210
+ if (!canAct.allowed) {
211
+ return error(canAct.reason);
212
+ }
213
+ const updates = {};
214
+ // Use title or content (legacy)
215
+ if (params.title)
216
+ updates.title = params.title;
217
+ else if (params.content)
218
+ updates.title = params.content;
219
+ if (params.description)
220
+ updates.description = params.description;
221
+ if (params.details)
222
+ updates.details = params.details;
223
+ if (params.testStrategy)
224
+ updates.testStrategy = params.testStrategy;
225
+ if (params.status)
226
+ updates.status = params.status;
227
+ if (params.priority)
228
+ updates.priority = params.priority;
229
+ if (params.metadata)
230
+ updates.metadata = params.metadata;
231
+ const task = updateTaskInStore(store, params.task_id, updates);
232
+ if (!task) {
233
+ return error(`Task '${params.task_id}' not found`);
234
+ }
235
+ await saveTasks(config, store);
236
+ return result({ task, message: `Task ${params.task_id} updated` });
237
+ }
238
+ export async function completeTask(config, params) {
239
+ const permError = checkPermission(params.role, "complete_task");
240
+ if (permError)
241
+ return permError;
242
+ const store = await loadTasks(config);
243
+ const canAct = canActOnTask(store, params.task_id, params.agent_id, "complete");
244
+ if (!canAct.allowed) {
245
+ return error(canAct.reason);
246
+ }
247
+ const task = updateTaskInStore(store, params.task_id, {
248
+ status: "completed",
249
+ completed_at: new Date().toISOString(),
250
+ });
251
+ if (!task) {
252
+ return error(`Task '${params.task_id}' not found`);
253
+ }
254
+ await saveTasks(config, store);
255
+ return result({ task, message: `Task ${params.task_id} completed` });
256
+ }
257
+ export async function nextTask(config, params) {
258
+ const permError = checkPermission(params.role, "next_task");
259
+ if (permError)
260
+ return permError;
261
+ const store = await loadTasks(config);
262
+ // Find top-level tasks that are pending and have all dependencies met
263
+ const pendingTasks = store.tasks.filter((t) => {
264
+ if (t.parent_id)
265
+ return false; // Skip subtasks
266
+ if (t.status !== "pending")
267
+ return false;
268
+ // Check dependencies
269
+ if (t.dependencies && t.dependencies.length > 0) {
270
+ const unmetDeps = t.dependencies.filter((depId) => {
271
+ const dep = findTask(store, depId);
272
+ return !dep || (dep.status !== "completed" && dep.status !== "done");
273
+ });
274
+ if (unmetDeps.length > 0)
275
+ return false;
276
+ }
277
+ return true;
278
+ });
279
+ if (pendingTasks.length === 0) {
280
+ return result({
281
+ task: null,
282
+ message: "No tasks available (all completed or blocked by dependencies)",
283
+ });
284
+ }
285
+ // Sort by priority (critical > high > medium > low)
286
+ const priorityOrder = {
287
+ critical: 0,
288
+ high: 1,
289
+ medium: 2,
290
+ low: 3,
291
+ };
292
+ pendingTasks.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
293
+ const nextTask = pendingTasks[0];
294
+ return result({
295
+ task: nextTask,
296
+ message: `Recommended next task: ${nextTask.id}`,
297
+ available_count: pendingTasks.length,
298
+ });
299
+ }
300
+ // ============================================================================
301
+ // Multi-Agent Coordination Tools
302
+ // ============================================================================
303
+ export async function claimTask(config, params) {
304
+ const permError = checkPermission(params.role, "claim_task");
305
+ if (permError)
306
+ return permError;
307
+ const store = await loadTasks(config);
308
+ const task = findTask(store, params.task_id);
309
+ if (!task) {
310
+ return error(`Task '${params.task_id}' not found`);
311
+ }
312
+ if (task.assigned_to) {
313
+ return error(`Task '${params.task_id}' is already assigned to ${task.assigned_to}`);
314
+ }
315
+ if (task.status !== "pending") {
316
+ return error(`Task '${params.task_id}' is not pending (status: ${task.status})`);
317
+ }
318
+ // Check dependencies
319
+ if (task.dependencies && task.dependencies.length > 0) {
320
+ const unmetDeps = task.dependencies.filter((depId) => {
321
+ const dep = findTask(store, depId);
322
+ return !dep || (dep.status !== "completed" && dep.status !== "done");
323
+ });
324
+ if (unmetDeps.length > 0) {
325
+ return error(`Task has unmet dependencies: ${unmetDeps.join(", ")}`);
326
+ }
327
+ }
328
+ const agentId = params.agent_id || "default-worker";
329
+ if (params.agent_id && params.role) {
330
+ getOrCreateAgent(store, params.agent_id, params.role);
331
+ }
332
+ updateTaskInStore(store, params.task_id, {
333
+ status: "claimed",
334
+ assigned_to: agentId,
335
+ claimed_at: new Date().toISOString(),
336
+ });
337
+ await saveTasks(config, store);
338
+ return result({ task, message: `Task ${params.task_id} claimed by ${agentId}` });
339
+ }
340
+ export async function releaseTask(config, params) {
341
+ const permError = checkPermission(params.role, "release_task");
342
+ if (permError)
343
+ return permError;
344
+ const store = await loadTasks(config);
345
+ const canAct = canActOnTask(store, params.task_id, params.agent_id, "release");
346
+ if (!canAct.allowed) {
347
+ return error(canAct.reason);
348
+ }
349
+ const task = updateTaskInStore(store, params.task_id, {
350
+ status: "pending",
351
+ assigned_to: undefined,
352
+ claimed_at: undefined,
353
+ });
354
+ if (!task) {
355
+ return error(`Task '${params.task_id}' not found`);
356
+ }
357
+ await saveTasks(config, store);
358
+ return result({ task, message: `Task ${params.task_id} released` });
359
+ }
360
+ export async function handoffTask(config, params) {
361
+ const permError = checkPermission(params.role, "handoff_task");
362
+ if (permError)
363
+ return permError;
364
+ const store = await loadTasks(config);
365
+ const canAct = canActOnTask(store, params.task_id, params.agent_id, "release");
366
+ if (!canAct.allowed) {
367
+ return error(canAct.reason);
368
+ }
369
+ const newStatus = params.to_role === "judge" ? "review" : "pending";
370
+ const task = updateTaskInStore(store, params.task_id, {
371
+ status: newStatus,
372
+ assigned_to: undefined,
373
+ claimed_at: undefined,
374
+ metadata: params.notes
375
+ ? { ...findTask(store, params.task_id)?.metadata, handoff_notes: params.notes }
376
+ : undefined,
377
+ });
378
+ if (!task) {
379
+ return error(`Task '${params.task_id}' not found`);
380
+ }
381
+ await saveTasks(config, store);
382
+ return result({
383
+ task,
384
+ message: `Task ${params.task_id} handed off to ${params.to_role}`,
385
+ });
386
+ }
387
+ export async function reviewTask(config, params) {
388
+ const permError = checkPermission(params.role, "review_task");
389
+ if (permError)
390
+ return permError;
391
+ const store = await loadTasks(config);
392
+ const task = findTask(store, params.task_id);
393
+ if (!task) {
394
+ return error(`Task '${params.task_id}' not found`);
395
+ }
396
+ return result({
397
+ task,
398
+ message: `Review task ${params.task_id}. Use approve_task or reject_task when done.`,
399
+ });
400
+ }
401
+ export async function approveTask(config, params) {
402
+ const permError = checkPermission(params.role, "approve_task");
403
+ if (permError)
404
+ return permError;
405
+ const store = await loadTasks(config);
406
+ const task = findTask(store, params.task_id);
407
+ if (!task) {
408
+ return error(`Task '${params.task_id}' not found`);
409
+ }
410
+ if (task.status !== "review") {
411
+ return error(`Task '${params.task_id}' is not in review status`);
412
+ }
413
+ updateTaskInStore(store, params.task_id, {
414
+ status: "completed",
415
+ completed_at: new Date().toISOString(),
416
+ metadata: params.notes
417
+ ? { ...task.metadata, approval_notes: params.notes }
418
+ : task.metadata,
419
+ });
420
+ await saveTasks(config, store);
421
+ return result({ task, message: `Task ${params.task_id} approved and completed` });
422
+ }
423
+ export async function rejectTask(config, params) {
424
+ const permError = checkPermission(params.role, "reject_task");
425
+ if (permError)
426
+ return permError;
427
+ const store = await loadTasks(config);
428
+ const task = findTask(store, params.task_id);
429
+ if (!task) {
430
+ return error(`Task '${params.task_id}' not found`);
431
+ }
432
+ if (task.status !== "review") {
433
+ return error(`Task '${params.task_id}' is not in review status`);
434
+ }
435
+ updateTaskInStore(store, params.task_id, {
436
+ status: "rejected",
437
+ metadata: { ...task.metadata, rejection_feedback: params.feedback },
438
+ });
439
+ await saveTasks(config, store);
440
+ return result({
441
+ task,
442
+ message: `Task ${params.task_id} rejected. Feedback: ${params.feedback}`,
443
+ });
444
+ }
445
+ // ============================================================================
446
+ // Task Breakdown Tools
447
+ // ============================================================================
448
+ export async function expandTask(config, params) {
449
+ const permError = checkPermission(params.role, "expand_task");
450
+ if (permError)
451
+ return permError;
452
+ const store = await loadTasks(config);
453
+ const task = findTask(store, params.task_id);
454
+ if (!task) {
455
+ return error(`Task '${params.task_id}' not found`);
456
+ }
457
+ const promptResult = expandTaskPrompt(task);
458
+ return result(promptResult);
459
+ }
460
+ export async function addSubtask(config, params) {
461
+ const permError = checkPermission(params.role, "add_subtask");
462
+ if (permError)
463
+ return permError;
464
+ const store = await loadTasks(config);
465
+ const parent = findTask(store, params.parent_id);
466
+ if (!parent) {
467
+ return error(`Parent task '${params.parent_id}' not found`);
468
+ }
469
+ // Use title or content (legacy)
470
+ const title = params.title || params.content;
471
+ if (!title) {
472
+ return error("Either 'title' or 'content' is required");
473
+ }
474
+ const subtask = addSubtaskToTask(parent, {
475
+ title,
476
+ description: params.description,
477
+ details: params.details,
478
+ status: "pending",
479
+ dependencies: params.dependencies,
480
+ });
481
+ await saveTasks(config, store);
482
+ return result({ subtask, message: `Subtask ${subtask.id} added to task ${params.parent_id}` });
483
+ }
484
+ export async function setDependencies(config, params) {
485
+ const permError = checkPermission(params.role, "set_dependencies");
486
+ if (permError)
487
+ return permError;
488
+ const store = await loadTasks(config);
489
+ const task = findTask(store, params.task_id);
490
+ if (!task) {
491
+ return error(`Task '${params.task_id}' not found`);
492
+ }
493
+ // Validate all dependencies exist
494
+ for (const depId of params.dependencies) {
495
+ if (!findTask(store, depId)) {
496
+ return error(`Dependency task '${depId}' not found`);
497
+ }
498
+ }
499
+ // Check for circular dependencies (simple check)
500
+ if (params.dependencies.includes(params.task_id)) {
501
+ return error("Task cannot depend on itself");
502
+ }
503
+ updateTaskInStore(store, params.task_id, {
504
+ dependencies: params.dependencies,
505
+ });
506
+ await saveTasks(config, store);
507
+ return result({
508
+ task,
509
+ message: `Dependencies set for task ${params.task_id}: ${params.dependencies.join(", ")}`,
510
+ });
511
+ }
512
+ export async function removeTask(config, params) {
513
+ const permError = checkPermission(params.role, "remove_task");
514
+ if (permError)
515
+ return permError;
516
+ const store = await loadTasks(config);
517
+ const taskIndex = store.tasks.findIndex((t) => t.id === params.task_id);
518
+ if (taskIndex === -1) {
519
+ return error(`Task '${params.task_id}' not found`);
520
+ }
521
+ const task = store.tasks[taskIndex];
522
+ // Remove task
523
+ store.tasks.splice(taskIndex, 1);
524
+ // Remove this task from any dependencies
525
+ for (const t of store.tasks) {
526
+ if (t.dependencies) {
527
+ t.dependencies = t.dependencies.filter((id) => id !== params.task_id);
528
+ }
529
+ }
530
+ await saveTasks(config, store);
531
+ return result({ message: `Task ${params.task_id} removed` });
532
+ }
533
+ // ============================================================================
534
+ // Prompt-Based Tools
535
+ // ============================================================================
536
+ export async function parsePrd(config, params) {
537
+ const permError = checkPermission(params.role, "parse_prd");
538
+ if (permError)
539
+ return permError;
540
+ const promptResult = parsePrdPrompt(params.prd_content);
541
+ return result(promptResult);
542
+ }
543
+ export async function researchTask(config, params) {
544
+ const permError = checkPermission(params.role, "research_task");
545
+ if (permError)
546
+ return permError;
547
+ const store = await loadTasks(config);
548
+ const task = findTask(store, params.task_id);
549
+ if (!task) {
550
+ return error(`Task '${params.task_id}' not found`);
551
+ }
552
+ const promptResult = researchTaskPrompt(task, params.topic);
553
+ return result(promptResult);
554
+ }
555
+ export async function analyzeComplexity(config, params) {
556
+ const permError = checkPermission(params.role, "analyze_complexity");
557
+ if (permError)
558
+ return permError;
559
+ const store = await loadTasks(config);
560
+ const task = findTask(store, params.task_id);
561
+ if (!task) {
562
+ return error(`Task '${params.task_id}' not found`);
563
+ }
564
+ const promptResult = analyzeComplexityPrompt(task);
565
+ return result(promptResult);
566
+ }
567
+ /**
568
+ * Read file contents, handling text files only
569
+ */
570
+ async function readFileContent(filePath) {
571
+ const content = await readFile(filePath, "utf-8");
572
+ return content;
573
+ }
574
+ /**
575
+ * Get all text files in a directory (non-recursive for safety)
576
+ */
577
+ function getTextFilesInDir(dirPath) {
578
+ const textExtensions = new Set([
579
+ ".md", ".txt", ".json", ".yaml", ".yml", ".ts", ".tsx", ".js", ".jsx",
580
+ ".html", ".css", ".scss", ".py", ".rb", ".go", ".rs", ".toml", ".ini",
581
+ ".sh", ".bash", ".zsh", ".fish", ".mdx", ".astro", ".vue", ".svelte",
582
+ ]);
583
+ const files = [];
584
+ const entries = readdirSync(dirPath, { withFileTypes: true });
585
+ for (const entry of entries) {
586
+ if (entry.isFile()) {
587
+ const ext = entry.name.substring(entry.name.lastIndexOf("."));
588
+ if (textExtensions.has(ext)) {
589
+ files.push(join(dirPath, entry.name));
590
+ }
591
+ }
592
+ }
593
+ return files;
594
+ }
595
+ export async function checkCompliance(config, params) {
596
+ // No permission check - anyone can check compliance
597
+ const fullPath = resolvePath(config, params.path);
598
+ if (!existsSync(fullPath)) {
599
+ return error(`Path not found: ${params.path}`);
600
+ }
601
+ const stat = statSync(fullPath);
602
+ let content;
603
+ let pathDescription;
604
+ if (stat.isDirectory()) {
605
+ // Read all text files in directory
606
+ const files = getTextFilesInDir(fullPath);
607
+ if (files.length === 0) {
608
+ return error(`No text files found in directory: ${params.path}`);
609
+ }
610
+ const fileContents = [];
611
+ for (const file of files.slice(0, 10)) {
612
+ // Limit to 10 files
613
+ try {
614
+ const fileContent = await readFileContent(file);
615
+ const relativePath = file.replace(config.workspaceRoot + "/", "");
616
+ fileContents.push(`### ${relativePath}\n\`\`\`\n${fileContent.slice(0, 2000)}\n\`\`\``);
617
+ }
618
+ catch {
619
+ // Skip files that can't be read
620
+ }
621
+ }
622
+ content = fileContents.join("\n\n");
623
+ pathDescription = `${params.path} (${files.length} files${files.length > 10 ? ", showing first 10" : ""})`;
624
+ }
625
+ else {
626
+ // Single file
627
+ try {
628
+ content = await readFileContent(fullPath);
629
+ pathDescription = params.path;
630
+ }
631
+ catch {
632
+ return error(`Could not read file: ${params.path}`);
633
+ }
634
+ }
635
+ // Truncate very large content
636
+ if (content.length > 20000) {
637
+ content = content.slice(0, 20000) + "\n\n... (truncated)";
638
+ }
639
+ const promptResult = checkCompliancePrompt(pathDescription, content, params.fix);
640
+ return result(promptResult);
641
+ }
642
+ // ============================================================================
643
+ // Project Initialization
644
+ // ============================================================================
645
+ /**
646
+ * Ensure directory exists
647
+ */
648
+ async function ensureDir(filePath) {
649
+ const dir = dirname(filePath);
650
+ if (!existsSync(dir)) {
651
+ await mkdir(dir, { recursive: true });
652
+ }
653
+ }
654
+ /**
655
+ * Initialize a project with agent-kit, memory_bank, and cursor rules
656
+ */
657
+ export async function initProject(config, params) {
658
+ // Derive project name from workspace root if not provided
659
+ const projectName = params.project_name || config.workspaceRoot.split("/").pop() || "project";
660
+ const templateFiles = getTemplateFiles(projectName);
661
+ const created = [];
662
+ const skipped = [];
663
+ const errors = [];
664
+ // Process template files
665
+ for (const template of templateFiles) {
666
+ const fullPath = resolvePath(config, template.path);
667
+ // Check if file exists and has content
668
+ if (existsSync(fullPath) && !params.force) {
669
+ try {
670
+ const content = await readFile(fullPath, "utf-8");
671
+ if (content.trim().length > 0) {
672
+ skipped.push(template.path);
673
+ continue;
674
+ }
675
+ }
676
+ catch {
677
+ // File exists but can't be read, skip it
678
+ skipped.push(template.path);
679
+ continue;
680
+ }
681
+ }
682
+ // Create the file
683
+ try {
684
+ await ensureDir(fullPath);
685
+ await writeFile(fullPath, template.content, "utf-8");
686
+ created.push(template.path);
687
+ }
688
+ catch (e) {
689
+ errors.push(`${template.path}: ${e}`);
690
+ }
691
+ }
692
+ // Create tasks directory with empty tasks.json
693
+ const tasksJsonPath = resolvePath(config, "memory_bank/tasks/tasks.json");
694
+ if (!existsSync(tasksJsonPath) || params.force) {
695
+ try {
696
+ await ensureDir(tasksJsonPath);
697
+ await writeFile(tasksJsonPath, getInitialTasksJson(), "utf-8");
698
+ created.push("memory_bank/tasks/tasks.json");
699
+ }
700
+ catch (e) {
701
+ errors.push(`memory_bank/tasks/tasks.json: ${e}`);
702
+ }
703
+ }
704
+ else {
705
+ skipped.push("memory_bank/tasks/tasks.json");
706
+ }
707
+ // Build result message
708
+ const messages = [];
709
+ if (created.length > 0) {
710
+ messages.push(`Created ${created.length} files:`);
711
+ created.forEach((f) => messages.push(` ✓ ${f}`));
712
+ }
713
+ if (skipped.length > 0) {
714
+ messages.push(`\nSkipped ${skipped.length} existing files:`);
715
+ skipped.forEach((f) => messages.push(` - ${f}`));
716
+ }
717
+ if (errors.length > 0) {
718
+ messages.push(`\nErrors (${errors.length}):`);
719
+ errors.forEach((e) => messages.push(` ✗ ${e}`));
720
+ }
721
+ if (created.length === 0 && skipped.length > 0) {
722
+ messages.push("\nProject already initialised. Use force: true to overwrite.");
723
+ }
724
+ return result({
725
+ project_name: projectName,
726
+ created,
727
+ skipped,
728
+ errors,
729
+ message: messages.join("\n"),
730
+ });
731
+ }
732
+ //# sourceMappingURL=tools.js.map