pulse-coder-engine 0.0.1-alpha.10 → 0.0.1-alpha.12
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/built-in/index.cjs +591 -131
- package/dist/built-in/index.cjs.map +1 -1
- package/dist/built-in/index.d.cts +2 -2
- package/dist/built-in/index.d.ts +2 -2
- package/dist/built-in/index.js +578 -120
- package/dist/built-in/index.js.map +1 -1
- package/dist/{index-C7fdydbL.d.cts → index-CPgs_IXY.d.cts} +143 -30
- package/dist/{index-C7fdydbL.d.ts → index-CPgs_IXY.d.ts} +143 -30
- package/dist/index.cjs +586 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -18
- package/dist/index.d.ts +32 -18
- package/dist/index.js +584 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/built-in/index.js
CHANGED
|
@@ -159,16 +159,44 @@ var BuiltInSkillRegistry = class {
|
|
|
159
159
|
return Array.from(this.skills.values());
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
|
-
*
|
|
162
|
+
* 注册或更新一个技能(支持插件在运行期追加技能)
|
|
163
|
+
*/
|
|
164
|
+
registerSkill(skill) {
|
|
165
|
+
const name = skill.name?.trim();
|
|
166
|
+
if (!name) {
|
|
167
|
+
throw new Error("Skill name is required");
|
|
168
|
+
}
|
|
169
|
+
const existingKey = Array.from(this.skills.keys()).find((key) => key.toLowerCase() === name.toLowerCase());
|
|
170
|
+
const replaced = existingKey !== void 0;
|
|
171
|
+
if (existingKey && existingKey !== name) {
|
|
172
|
+
this.skills.delete(existingKey);
|
|
173
|
+
}
|
|
174
|
+
this.skills.set(name, {
|
|
175
|
+
...skill,
|
|
176
|
+
name
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
skillName: name,
|
|
180
|
+
replaced,
|
|
181
|
+
total: this.skills.size
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 根据名称获取技能(大小写不敏感)
|
|
163
186
|
*/
|
|
164
187
|
get(name) {
|
|
165
|
-
|
|
188
|
+
const exact = this.skills.get(name);
|
|
189
|
+
if (exact) {
|
|
190
|
+
return exact;
|
|
191
|
+
}
|
|
192
|
+
const target = name.toLowerCase();
|
|
193
|
+
return this.getAll().find((skill) => skill.name.toLowerCase() === target);
|
|
166
194
|
}
|
|
167
195
|
/**
|
|
168
196
|
* 检查技能是否存在
|
|
169
197
|
*/
|
|
170
198
|
has(name) {
|
|
171
|
-
return this.
|
|
199
|
+
return !!this.get(name);
|
|
172
200
|
}
|
|
173
201
|
/**
|
|
174
202
|
* 搜索技能(模糊匹配)
|
|
@@ -183,7 +211,7 @@ var BuiltInSkillRegistry = class {
|
|
|
183
211
|
var skillToolSchema = z.object({
|
|
184
212
|
name: z.string().describe("The name of the skill to execute")
|
|
185
213
|
});
|
|
186
|
-
function generateSkillTool(
|
|
214
|
+
function generateSkillTool(registry) {
|
|
187
215
|
const getSkillsPrompt = (availableSkills) => {
|
|
188
216
|
return [
|
|
189
217
|
"If query matches an available skill's description or instruction [use skill], use the skill tool to get detailed instructions.",
|
|
@@ -204,10 +232,10 @@ function generateSkillTool(skills) {
|
|
|
204
232
|
};
|
|
205
233
|
return {
|
|
206
234
|
name: "skill",
|
|
207
|
-
description: getSkillsPrompt(
|
|
235
|
+
description: getSkillsPrompt(registry.getAll()),
|
|
208
236
|
inputSchema: skillToolSchema,
|
|
209
237
|
execute: async ({ name }) => {
|
|
210
|
-
const skill =
|
|
238
|
+
const skill = registry.get(name);
|
|
211
239
|
if (!skill) {
|
|
212
240
|
throw new Error(`Skill ${name} not found`);
|
|
213
241
|
}
|
|
@@ -221,14 +249,21 @@ var builtInSkillsPlugin = {
|
|
|
221
249
|
async initialize(context) {
|
|
222
250
|
const registry = new BuiltInSkillRegistry();
|
|
223
251
|
await registry.initialize(process.cwd());
|
|
252
|
+
context.registerService("skillRegistry", registry);
|
|
253
|
+
context.registerTool("skill", generateSkillTool(registry));
|
|
254
|
+
context.registerHook("beforeRun", ({ tools }) => {
|
|
255
|
+
return {
|
|
256
|
+
tools: {
|
|
257
|
+
...tools,
|
|
258
|
+
skill: generateSkillTool(registry)
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
});
|
|
224
262
|
const skills = registry.getAll();
|
|
225
263
|
if (skills.length === 0) {
|
|
226
264
|
console.log("[Skills] No skills found");
|
|
227
265
|
return;
|
|
228
266
|
}
|
|
229
|
-
const skillTool = generateSkillTool(skills);
|
|
230
|
-
context.registerTool("skill", skillTool);
|
|
231
|
-
context.registerService("skillRegistry", registry);
|
|
232
267
|
console.log(`[Skills] Registered ${skills.length} skill(s)`);
|
|
233
268
|
}
|
|
234
269
|
};
|
|
@@ -339,6 +374,26 @@ var KNOWN_TOOL_META = {
|
|
|
339
374
|
category: "other",
|
|
340
375
|
risk: "low",
|
|
341
376
|
description: "Ask the user a targeted clarification question."
|
|
377
|
+
},
|
|
378
|
+
task_create: {
|
|
379
|
+
category: "other",
|
|
380
|
+
risk: "low",
|
|
381
|
+
description: "Create tracked task entries for planning and execution visibility."
|
|
382
|
+
},
|
|
383
|
+
task_get: {
|
|
384
|
+
category: "other",
|
|
385
|
+
risk: "low",
|
|
386
|
+
description: "Inspect a task entry by ID."
|
|
387
|
+
},
|
|
388
|
+
task_list: {
|
|
389
|
+
category: "other",
|
|
390
|
+
risk: "low",
|
|
391
|
+
description: "List task tracking entries and status summary."
|
|
392
|
+
},
|
|
393
|
+
task_update: {
|
|
394
|
+
category: "other",
|
|
395
|
+
risk: "low",
|
|
396
|
+
description: "Update task tracking fields and status."
|
|
342
397
|
}
|
|
343
398
|
};
|
|
344
399
|
var BuiltInPlanModeService = class {
|
|
@@ -461,25 +516,6 @@ var BuiltInPlanModeService = class {
|
|
|
461
516
|
}
|
|
462
517
|
return lines.join("\n");
|
|
463
518
|
}
|
|
464
|
-
applyHooks(baseHooks) {
|
|
465
|
-
return {
|
|
466
|
-
onBeforeToolCall: async (name, input) => {
|
|
467
|
-
this.observePotentialPolicyViolation(name, input);
|
|
468
|
-
if (baseHooks?.onBeforeToolCall) {
|
|
469
|
-
const nextInput = await baseHooks.onBeforeToolCall(name, input);
|
|
470
|
-
return nextInput ?? input;
|
|
471
|
-
}
|
|
472
|
-
return input;
|
|
473
|
-
},
|
|
474
|
-
onAfterToolCall: async (name, input, output) => {
|
|
475
|
-
if (baseHooks?.onAfterToolCall) {
|
|
476
|
-
const nextOutput = await baseHooks.onAfterToolCall(name, input, output);
|
|
477
|
-
return nextOutput ?? output;
|
|
478
|
-
}
|
|
479
|
-
return output;
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
519
|
getEvents(limit = 50) {
|
|
484
520
|
return this.events.slice(-Math.max(0, limit));
|
|
485
521
|
}
|
|
@@ -616,21 +652,18 @@ var builtInPlanModePlugin = {
|
|
|
616
652
|
version: "1.0.0",
|
|
617
653
|
async initialize(context) {
|
|
618
654
|
const service = new BuiltInPlanModeService(context.logger, context.events, "executing");
|
|
619
|
-
context.
|
|
655
|
+
context.registerHook("beforeLLMCall", ({ context: runContext, tools, systemPrompt }) => {
|
|
620
656
|
const mode = service.getMode();
|
|
621
657
|
if (mode === "executing") {
|
|
622
|
-
return
|
|
623
|
-
systemPrompt,
|
|
624
|
-
hooks
|
|
625
|
-
};
|
|
658
|
+
return;
|
|
626
659
|
}
|
|
627
660
|
const transition = service.processContextMessages(runContext.messages);
|
|
628
661
|
const append = service.buildPromptAppend(Object.keys(tools), transition);
|
|
629
662
|
const finalSystemPrompt = appendSystemPrompt(systemPrompt, append);
|
|
630
|
-
return {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
663
|
+
return { systemPrompt: finalSystemPrompt };
|
|
664
|
+
});
|
|
665
|
+
context.registerHook("beforeToolCall", ({ name, input }) => {
|
|
666
|
+
service.observePotentialPolicyViolation(name, input);
|
|
634
667
|
});
|
|
635
668
|
context.registerService("planMode", service);
|
|
636
669
|
context.registerService("planModeService", service);
|
|
@@ -640,10 +673,397 @@ var builtInPlanModePlugin = {
|
|
|
640
673
|
}
|
|
641
674
|
};
|
|
642
675
|
|
|
676
|
+
// src/built-in/task-tracking-plugin/index.ts
|
|
677
|
+
import { promises as fs } from "fs";
|
|
678
|
+
import path2 from "path";
|
|
679
|
+
import { homedir as homedir2 } from "os";
|
|
680
|
+
import { randomUUID } from "crypto";
|
|
681
|
+
import { z as z2 } from "zod";
|
|
682
|
+
var TASK_STATUSES = ["pending", "in_progress", "completed", "blocked"];
|
|
683
|
+
var CREATE_TASK_ITEM_SCHEMA = z2.object({
|
|
684
|
+
title: z2.string().min(1).describe("Task title"),
|
|
685
|
+
details: z2.string().optional().describe("Task details / acceptance notes"),
|
|
686
|
+
status: z2.enum(TASK_STATUSES).optional().describe("Initial status; defaults to pending"),
|
|
687
|
+
dependencies: z2.array(z2.string()).optional().describe("Task IDs that must be completed first"),
|
|
688
|
+
blockedReason: z2.string().optional().describe("Reason when status is blocked"),
|
|
689
|
+
metadata: z2.record(z2.string(), z2.any()).optional().describe("Additional machine-readable metadata")
|
|
690
|
+
});
|
|
691
|
+
var TASK_CREATE_INPUT_SCHEMA = z2.object({
|
|
692
|
+
title: z2.string().min(1).optional().describe("Task title (single-task mode)"),
|
|
693
|
+
details: z2.string().optional().describe("Task details (single-task mode)"),
|
|
694
|
+
status: z2.enum(TASK_STATUSES).optional().describe("Initial status (single-task mode)"),
|
|
695
|
+
dependencies: z2.array(z2.string()).optional().describe("Dependencies (single-task mode)"),
|
|
696
|
+
blockedReason: z2.string().optional().describe("Blocked reason (single-task mode)"),
|
|
697
|
+
metadata: z2.record(z2.string(), z2.any()).optional().describe("Metadata (single-task mode)"),
|
|
698
|
+
tasks: z2.array(CREATE_TASK_ITEM_SCHEMA).min(1).optional().describe("Batch create mode")
|
|
699
|
+
}).refine((value) => !!value.tasks?.length || !!value.title, {
|
|
700
|
+
message: "Either provide `tasks` or `title` for single-task mode."
|
|
701
|
+
});
|
|
702
|
+
var TASK_GET_INPUT_SCHEMA = z2.object({
|
|
703
|
+
id: z2.string().min(1).describe("Task ID to retrieve")
|
|
704
|
+
});
|
|
705
|
+
var TASK_LIST_INPUT_SCHEMA = z2.object({
|
|
706
|
+
statuses: z2.array(z2.enum(TASK_STATUSES)).optional().describe("Filter by statuses"),
|
|
707
|
+
includeCompleted: z2.boolean().optional().describe("Set false to hide completed tasks"),
|
|
708
|
+
limit: z2.number().int().positive().max(500).optional().describe("Maximum tasks to return")
|
|
709
|
+
});
|
|
710
|
+
var TASK_TRACKING_SKILL = {
|
|
711
|
+
name: "task-tracking-workflow",
|
|
712
|
+
description: "Track complex execution with task tools and proceed directly without confirmation unless the user explicitly asks for confirmation.",
|
|
713
|
+
location: "pulse-coder-engine/built-in/task-tracking-plugin",
|
|
714
|
+
content: `# Task Tracking Workflow
|
|
715
|
+
|
|
716
|
+
## When to use
|
|
717
|
+
- The request has multiple phases or deliverables.
|
|
718
|
+
- Work may span multiple tool calls or sessions.
|
|
719
|
+
- You need explicit progress visibility and blocker management.
|
|
720
|
+
|
|
721
|
+
## Execution autonomy
|
|
722
|
+
- Default behavior: execute directly and call tools without asking for confirmation.
|
|
723
|
+
- Only ask for confirmation when the user explicitly requires confirmation.
|
|
724
|
+
- If critical information is missing, ask only the minimum clarifying question needed to continue.
|
|
725
|
+
|
|
726
|
+
## Required flow
|
|
727
|
+
1. Start by reviewing existing tasks with \`task_list\`.
|
|
728
|
+
2. If no suitable tasks exist, create focused tasks with \`task_create\` (prefer batch mode).
|
|
729
|
+
3. Keep exactly one primary task in \`in_progress\` where possible.
|
|
730
|
+
4. Update status with \`task_update\` after meaningful progress.
|
|
731
|
+
5. Mark blockers with \`status=blocked\` and a concrete \`blockedReason\`.
|
|
732
|
+
6. Mark done tasks as \`completed\` and move to the next actionable item.
|
|
733
|
+
|
|
734
|
+
## Quality rules
|
|
735
|
+
- Keep tasks atomic and verifiable.
|
|
736
|
+
- Avoid single oversized umbrella tasks.
|
|
737
|
+
- Reuse existing in-progress tasks instead of duplicating.
|
|
738
|
+
- Keep dependency links explicit when order matters.`
|
|
739
|
+
};
|
|
740
|
+
var TASK_UPDATE_INPUT_SCHEMA = z2.object({
|
|
741
|
+
id: z2.string().min(1).describe("Task ID to update"),
|
|
742
|
+
title: z2.string().min(1).optional().describe("New title"),
|
|
743
|
+
details: z2.string().optional().describe("New details"),
|
|
744
|
+
status: z2.enum(TASK_STATUSES).optional().describe("New status"),
|
|
745
|
+
dependencies: z2.array(z2.string()).optional().describe("Replace dependencies with this list"),
|
|
746
|
+
blockedReason: z2.string().optional().describe("Blocked reason, used with status=blocked"),
|
|
747
|
+
metadata: z2.record(z2.string(), z2.any()).optional().describe("Replace metadata object"),
|
|
748
|
+
delete: z2.boolean().optional().describe("Delete this task")
|
|
749
|
+
});
|
|
750
|
+
function normalizeTaskListId(raw) {
|
|
751
|
+
const trimmed = raw.trim();
|
|
752
|
+
if (!trimmed) {
|
|
753
|
+
return "default";
|
|
754
|
+
}
|
|
755
|
+
return trimmed.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 120) || "default";
|
|
756
|
+
}
|
|
757
|
+
function now() {
|
|
758
|
+
return Date.now();
|
|
759
|
+
}
|
|
760
|
+
function dedupeStrings(values) {
|
|
761
|
+
if (!values?.length) {
|
|
762
|
+
return [];
|
|
763
|
+
}
|
|
764
|
+
return Array.from(new Set(values.map((value) => String(value).trim()).filter(Boolean)));
|
|
765
|
+
}
|
|
766
|
+
var TaskListService = class _TaskListService {
|
|
767
|
+
taskListId;
|
|
768
|
+
storagePath;
|
|
769
|
+
storageDir;
|
|
770
|
+
initialized = false;
|
|
771
|
+
createdAt = now();
|
|
772
|
+
updatedAt = now();
|
|
773
|
+
tasks = /* @__PURE__ */ new Map();
|
|
774
|
+
constructor(taskListId = _TaskListService.resolveTaskListId(), storageDir = _TaskListService.resolveStorageDir()) {
|
|
775
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
776
|
+
this.storageDir = storageDir;
|
|
777
|
+
this.taskListId = normalized;
|
|
778
|
+
this.storagePath = path2.join(this.storageDir, `${normalized}.json`);
|
|
779
|
+
}
|
|
780
|
+
static resolveTaskListId() {
|
|
781
|
+
return process.env.PULSE_CODER_TASK_LIST_ID || process.env.CLAUDE_CODE_TASK_LIST_ID || "default";
|
|
782
|
+
}
|
|
783
|
+
static resolveStorageDir() {
|
|
784
|
+
return process.env.PULSE_CODER_TASKS_DIR || path2.join(homedir2(), ".pulse-coder", "tasks");
|
|
785
|
+
}
|
|
786
|
+
async initialize() {
|
|
787
|
+
if (this.initialized) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
await this.loadTaskList(this.taskListId);
|
|
791
|
+
}
|
|
792
|
+
async setTaskListId(taskListId) {
|
|
793
|
+
await this.initialize();
|
|
794
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
795
|
+
if (normalized === this.taskListId) {
|
|
796
|
+
return {
|
|
797
|
+
switched: false,
|
|
798
|
+
taskListId: this.taskListId,
|
|
799
|
+
storagePath: this.storagePath
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
await this.loadTaskList(normalized);
|
|
803
|
+
return {
|
|
804
|
+
switched: true,
|
|
805
|
+
taskListId: this.taskListId,
|
|
806
|
+
storagePath: this.storagePath
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
async createTask(input) {
|
|
810
|
+
await this.initialize();
|
|
811
|
+
const timestamp = now();
|
|
812
|
+
const status = input.status ?? "pending";
|
|
813
|
+
const task = {
|
|
814
|
+
id: randomUUID(),
|
|
815
|
+
title: input.title.trim(),
|
|
816
|
+
details: input.details,
|
|
817
|
+
status,
|
|
818
|
+
dependencies: dedupeStrings(input.dependencies),
|
|
819
|
+
blockedReason: status === "blocked" ? input.blockedReason ?? "Blocked without reason" : void 0,
|
|
820
|
+
metadata: input.metadata,
|
|
821
|
+
createdAt: timestamp,
|
|
822
|
+
updatedAt: timestamp,
|
|
823
|
+
completedAt: status === "completed" ? timestamp : void 0
|
|
824
|
+
};
|
|
825
|
+
this.tasks.set(task.id, task);
|
|
826
|
+
this.updatedAt = timestamp;
|
|
827
|
+
await this.persist();
|
|
828
|
+
return task;
|
|
829
|
+
}
|
|
830
|
+
async createTasks(inputs) {
|
|
831
|
+
const created = [];
|
|
832
|
+
for (const input of inputs) {
|
|
833
|
+
created.push(await this.createTask(input));
|
|
834
|
+
}
|
|
835
|
+
return created;
|
|
836
|
+
}
|
|
837
|
+
async listTasks(options) {
|
|
838
|
+
await this.initialize();
|
|
839
|
+
const statuses = options?.statuses?.length ? new Set(options.statuses) : null;
|
|
840
|
+
const includeCompleted = options?.includeCompleted ?? true;
|
|
841
|
+
let tasks = Array.from(this.tasks.values()).filter((task) => {
|
|
842
|
+
if (!includeCompleted && task.status === "completed") {
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
if (statuses && !statuses.has(task.status)) {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
return true;
|
|
849
|
+
});
|
|
850
|
+
tasks = tasks.sort((a, b) => a.createdAt - b.createdAt);
|
|
851
|
+
if (options?.limit && options.limit > 0) {
|
|
852
|
+
tasks = tasks.slice(0, options.limit);
|
|
853
|
+
}
|
|
854
|
+
return tasks;
|
|
855
|
+
}
|
|
856
|
+
async getTask(id) {
|
|
857
|
+
await this.initialize();
|
|
858
|
+
return this.tasks.get(id) ?? null;
|
|
859
|
+
}
|
|
860
|
+
async updateTask(input) {
|
|
861
|
+
await this.initialize();
|
|
862
|
+
if (input.delete) {
|
|
863
|
+
const deleted = this.tasks.delete(input.id);
|
|
864
|
+
if (!deleted) {
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
this.updatedAt = now();
|
|
868
|
+
await this.persist();
|
|
869
|
+
return null;
|
|
870
|
+
}
|
|
871
|
+
const existing = this.tasks.get(input.id);
|
|
872
|
+
if (!existing) {
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
const timestamp = now();
|
|
876
|
+
const nextStatus = input.status ?? existing.status;
|
|
877
|
+
const next = {
|
|
878
|
+
...existing,
|
|
879
|
+
title: input.title !== void 0 ? input.title : existing.title,
|
|
880
|
+
details: input.details !== void 0 ? input.details : existing.details,
|
|
881
|
+
status: nextStatus,
|
|
882
|
+
dependencies: input.dependencies !== void 0 ? dedupeStrings(input.dependencies) : existing.dependencies,
|
|
883
|
+
metadata: input.metadata !== void 0 ? input.metadata : existing.metadata,
|
|
884
|
+
blockedReason: this.resolveBlockedReason(nextStatus, input.blockedReason, existing.blockedReason),
|
|
885
|
+
updatedAt: timestamp,
|
|
886
|
+
completedAt: nextStatus === "completed" ? existing.completedAt ?? timestamp : void 0
|
|
887
|
+
};
|
|
888
|
+
this.tasks.set(input.id, next);
|
|
889
|
+
this.updatedAt = timestamp;
|
|
890
|
+
await this.persist();
|
|
891
|
+
return next;
|
|
892
|
+
}
|
|
893
|
+
async snapshot(options) {
|
|
894
|
+
const tasks = await this.listTasks(options);
|
|
895
|
+
return {
|
|
896
|
+
taskListId: this.taskListId,
|
|
897
|
+
storagePath: this.storagePath,
|
|
898
|
+
createdAt: this.createdAt,
|
|
899
|
+
updatedAt: this.updatedAt,
|
|
900
|
+
total: tasks.length,
|
|
901
|
+
tasks
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
resolveBlockedReason(status, blockedReasonInput, previous) {
|
|
905
|
+
if (status !== "blocked") {
|
|
906
|
+
return void 0;
|
|
907
|
+
}
|
|
908
|
+
if (blockedReasonInput !== void 0) {
|
|
909
|
+
return blockedReasonInput || "Blocked without reason";
|
|
910
|
+
}
|
|
911
|
+
return previous ?? "Blocked without reason";
|
|
912
|
+
}
|
|
913
|
+
async loadTaskList(taskListId) {
|
|
914
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
915
|
+
this.taskListId = normalized;
|
|
916
|
+
this.storagePath = path2.join(this.storageDir, `${normalized}.json`);
|
|
917
|
+
await fs.mkdir(this.storageDir, { recursive: true });
|
|
918
|
+
try {
|
|
919
|
+
const raw = await fs.readFile(this.storagePath, "utf-8");
|
|
920
|
+
const parsed = JSON.parse(raw);
|
|
921
|
+
const parsedTasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
|
|
922
|
+
this.tasks = new Map(parsedTasks.filter((task) => !!task?.id).map((task) => [task.id, task]));
|
|
923
|
+
this.createdAt = typeof parsed.createdAt === "number" ? parsed.createdAt : now();
|
|
924
|
+
this.updatedAt = typeof parsed.updatedAt === "number" ? parsed.updatedAt : now();
|
|
925
|
+
} catch (error) {
|
|
926
|
+
if (error?.code !== "ENOENT") {
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
929
|
+
const timestamp = now();
|
|
930
|
+
this.tasks = /* @__PURE__ */ new Map();
|
|
931
|
+
this.createdAt = timestamp;
|
|
932
|
+
this.updatedAt = timestamp;
|
|
933
|
+
await this.persist();
|
|
934
|
+
}
|
|
935
|
+
this.initialized = true;
|
|
936
|
+
}
|
|
937
|
+
async persist() {
|
|
938
|
+
const payload = {
|
|
939
|
+
taskListId: this.taskListId,
|
|
940
|
+
createdAt: this.createdAt,
|
|
941
|
+
updatedAt: this.updatedAt,
|
|
942
|
+
tasks: Array.from(this.tasks.values()).sort((a, b) => a.createdAt - b.createdAt)
|
|
943
|
+
};
|
|
944
|
+
await fs.writeFile(this.storagePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
function buildTaskCreateTool(service) {
|
|
948
|
+
return {
|
|
949
|
+
name: "task_create",
|
|
950
|
+
description: "Create one or more tracked tasks for complex work. Use this when work has multiple steps, dependencies, or blockers.",
|
|
951
|
+
inputSchema: TASK_CREATE_INPUT_SCHEMA,
|
|
952
|
+
execute: async ({ tasks, title, details, status, dependencies, blockedReason, metadata }) => {
|
|
953
|
+
const items = tasks?.length ? tasks : [{ title, details, status, dependencies, blockedReason, metadata }];
|
|
954
|
+
const created = await service.createTasks(items);
|
|
955
|
+
return {
|
|
956
|
+
taskListId: service.taskListId,
|
|
957
|
+
storagePath: service.storagePath,
|
|
958
|
+
createdCount: created.length,
|
|
959
|
+
tasks: created
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function buildTaskGetTool(service) {
|
|
965
|
+
return {
|
|
966
|
+
name: "task_get",
|
|
967
|
+
description: "Get full details for a single task by task ID.",
|
|
968
|
+
inputSchema: TASK_GET_INPUT_SCHEMA,
|
|
969
|
+
execute: async ({ id }) => {
|
|
970
|
+
const task = await service.getTask(id);
|
|
971
|
+
if (!task) {
|
|
972
|
+
throw new Error(`Task not found: ${id}`);
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
taskListId: service.taskListId,
|
|
976
|
+
storagePath: service.storagePath,
|
|
977
|
+
task
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function buildTaskListTool(service) {
|
|
983
|
+
return {
|
|
984
|
+
name: "task_list",
|
|
985
|
+
description: "List tasks and their current status. Use this to check progress before and after major changes.",
|
|
986
|
+
inputSchema: TASK_LIST_INPUT_SCHEMA,
|
|
987
|
+
execute: async ({ statuses, includeCompleted, limit }) => {
|
|
988
|
+
const snapshot = await service.snapshot({ statuses, includeCompleted, limit });
|
|
989
|
+
const completed = snapshot.tasks.filter((task) => task.status === "completed").length;
|
|
990
|
+
const inProgress = snapshot.tasks.filter((task) => task.status === "in_progress").length;
|
|
991
|
+
const pending = snapshot.tasks.filter((task) => task.status === "pending").length;
|
|
992
|
+
const blocked = snapshot.tasks.filter((task) => task.status === "blocked").length;
|
|
993
|
+
return {
|
|
994
|
+
...snapshot,
|
|
995
|
+
summary: {
|
|
996
|
+
total: snapshot.total,
|
|
997
|
+
completed,
|
|
998
|
+
inProgress,
|
|
999
|
+
pending,
|
|
1000
|
+
blocked
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function buildTaskUpdateTool(service) {
|
|
1007
|
+
return {
|
|
1008
|
+
name: "task_update",
|
|
1009
|
+
description: "Update task fields, move status (pending/in_progress/completed/blocked), or delete tasks.",
|
|
1010
|
+
inputSchema: TASK_UPDATE_INPUT_SCHEMA,
|
|
1011
|
+
execute: async (input) => {
|
|
1012
|
+
const task = await service.updateTask(input);
|
|
1013
|
+
if (input.delete) {
|
|
1014
|
+
return {
|
|
1015
|
+
taskListId: service.taskListId,
|
|
1016
|
+
storagePath: service.storagePath,
|
|
1017
|
+
deleted: true,
|
|
1018
|
+
id: input.id
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
if (!task) {
|
|
1022
|
+
throw new Error(`Task not found: ${input.id}`);
|
|
1023
|
+
}
|
|
1024
|
+
return {
|
|
1025
|
+
taskListId: service.taskListId,
|
|
1026
|
+
storagePath: service.storagePath,
|
|
1027
|
+
deleted: false,
|
|
1028
|
+
task
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
var builtInTaskTrackingPlugin = {
|
|
1034
|
+
name: "pulse-coder-engine/built-in-task-tracking",
|
|
1035
|
+
version: "1.0.0",
|
|
1036
|
+
dependencies: ["pulse-coder-engine/built-in-skills"],
|
|
1037
|
+
async initialize(context) {
|
|
1038
|
+
const service = new TaskListService();
|
|
1039
|
+
await service.initialize();
|
|
1040
|
+
context.registerService("taskListService", service);
|
|
1041
|
+
context.registerService("taskTracking", service);
|
|
1042
|
+
const skillRegistry = context.getService("skillRegistry");
|
|
1043
|
+
if (skillRegistry) {
|
|
1044
|
+
const registration = skillRegistry.registerSkill(TASK_TRACKING_SKILL);
|
|
1045
|
+
context.logger.info("[TaskTracking] Registered built-in task tracking skill", registration);
|
|
1046
|
+
} else {
|
|
1047
|
+
context.logger.warn("[TaskTracking] skillRegistry service unavailable; skipped task tracking skill registration");
|
|
1048
|
+
}
|
|
1049
|
+
context.registerTools({
|
|
1050
|
+
task_create: buildTaskCreateTool(service),
|
|
1051
|
+
task_get: buildTaskGetTool(service),
|
|
1052
|
+
task_list: buildTaskListTool(service),
|
|
1053
|
+
task_update: buildTaskUpdateTool(service)
|
|
1054
|
+
});
|
|
1055
|
+
context.logger.info("[TaskTracking] Registered task tools", {
|
|
1056
|
+
taskListId: service.taskListId,
|
|
1057
|
+
storagePath: service.storagePath,
|
|
1058
|
+
skillRegistryAvailable: !!skillRegistry
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
|
|
643
1063
|
// src/built-in/sub-agent-plugin/index.ts
|
|
644
|
-
import { z as
|
|
645
|
-
import { promises as
|
|
646
|
-
import
|
|
1064
|
+
import { z as z11 } from "zod";
|
|
1065
|
+
import { promises as fs3 } from "fs";
|
|
1066
|
+
import path4 from "path";
|
|
647
1067
|
|
|
648
1068
|
// src/ai/index.ts
|
|
649
1069
|
import { generateText, streamText } from "ai";
|
|
@@ -675,8 +1095,8 @@ var CLARIFICATION_TIMEOUT = Number(process.env.CLARIFICATION_TIMEOUT ?? 3e5);
|
|
|
675
1095
|
var CLARIFICATION_ENABLED = process.env.CLARIFICATION_ENABLED !== "false";
|
|
676
1096
|
|
|
677
1097
|
// src/prompt/system.ts
|
|
678
|
-
import
|
|
679
|
-
import
|
|
1098
|
+
import fs2 from "fs";
|
|
1099
|
+
import path3 from "path";
|
|
680
1100
|
var DEFAULT_PROMPT = `
|
|
681
1101
|
You are Pulse Coder, the best coding agent on the planet.
|
|
682
1102
|
|
|
@@ -812,13 +1232,13 @@ var AGENTS_FILE_REGEX = /^agents\.md$/i;
|
|
|
812
1232
|
var loadAgentsPrompt = () => {
|
|
813
1233
|
try {
|
|
814
1234
|
const cwd = process.cwd();
|
|
815
|
-
const entries =
|
|
1235
|
+
const entries = fs2.readdirSync(cwd, { withFileTypes: true });
|
|
816
1236
|
const target = entries.find((entry) => entry.isFile() && AGENTS_FILE_REGEX.test(entry.name));
|
|
817
1237
|
if (!target) {
|
|
818
1238
|
return null;
|
|
819
1239
|
}
|
|
820
|
-
const filePath =
|
|
821
|
-
const content =
|
|
1240
|
+
const filePath = path3.join(cwd, target.name);
|
|
1241
|
+
const content = fs2.readFileSync(filePath, "utf8").trim();
|
|
822
1242
|
return content.length > 0 ? content : null;
|
|
823
1243
|
} catch {
|
|
824
1244
|
return null;
|
|
@@ -1004,15 +1424,28 @@ var maybeCompactContext = async (context, options) => {
|
|
|
1004
1424
|
};
|
|
1005
1425
|
|
|
1006
1426
|
// src/core/loop.ts
|
|
1007
|
-
function
|
|
1427
|
+
function wrapToolsWithHooks(tools, beforeHooks, afterHooks) {
|
|
1008
1428
|
const wrapped = {};
|
|
1009
1429
|
for (const [name, t] of Object.entries(tools)) {
|
|
1010
1430
|
wrapped[name] = {
|
|
1011
1431
|
...t,
|
|
1012
1432
|
execute: async (input, ctx) => {
|
|
1013
|
-
|
|
1433
|
+
let finalInput = input;
|
|
1434
|
+
for (const hook of beforeHooks) {
|
|
1435
|
+
const result = await hook({ name, input: finalInput });
|
|
1436
|
+
if (result && "input" in result) {
|
|
1437
|
+
finalInput = result.input;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1014
1440
|
const output = await t.execute(finalInput, ctx);
|
|
1015
|
-
|
|
1441
|
+
let finalOutput = output;
|
|
1442
|
+
for (const hook of afterHooks) {
|
|
1443
|
+
const result = await hook({ name, input: finalInput, output: finalOutput });
|
|
1444
|
+
if (result && "output" in result) {
|
|
1445
|
+
finalOutput = result.output;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
return finalOutput;
|
|
1016
1449
|
}
|
|
1017
1450
|
};
|
|
1018
1451
|
}
|
|
@@ -1022,6 +1455,7 @@ async function loop(context, options) {
|
|
|
1022
1455
|
let errorCount = 0;
|
|
1023
1456
|
let totalSteps = 0;
|
|
1024
1457
|
let compactionAttempts = 0;
|
|
1458
|
+
const loopHooks = options?.hooks ?? {};
|
|
1025
1459
|
while (true) {
|
|
1026
1460
|
try {
|
|
1027
1461
|
if (compactionAttempts < MAX_COMPACTION_ATTEMPTS) {
|
|
@@ -1038,8 +1472,24 @@ async function loop(context, options) {
|
|
|
1038
1472
|
}
|
|
1039
1473
|
}
|
|
1040
1474
|
let tools = options?.tools || {};
|
|
1041
|
-
|
|
1042
|
-
|
|
1475
|
+
let systemPrompt = options?.systemPrompt;
|
|
1476
|
+
if (loopHooks.beforeLLMCall?.length) {
|
|
1477
|
+
for (const hook of loopHooks.beforeLLMCall) {
|
|
1478
|
+
const result2 = await hook({ context, systemPrompt, tools });
|
|
1479
|
+
if (result2) {
|
|
1480
|
+
if ("systemPrompt" in result2 && result2.systemPrompt !== void 0) {
|
|
1481
|
+
systemPrompt = result2.systemPrompt;
|
|
1482
|
+
}
|
|
1483
|
+
if ("tools" in result2 && result2.tools !== void 0) {
|
|
1484
|
+
tools = result2.tools;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
const beforeToolHooks = loopHooks.beforeToolCall ?? [];
|
|
1490
|
+
const afterToolHooks = loopHooks.afterToolCall ?? [];
|
|
1491
|
+
if (beforeToolHooks.length || afterToolHooks.length) {
|
|
1492
|
+
tools = wrapToolsWithHooks(tools, beforeToolHooks, afterToolHooks);
|
|
1043
1493
|
}
|
|
1044
1494
|
const toolExecutionContext = {
|
|
1045
1495
|
onClarificationRequest: options?.onClarificationRequest,
|
|
@@ -1050,7 +1500,7 @@ async function loop(context, options) {
|
|
|
1050
1500
|
toolExecutionContext,
|
|
1051
1501
|
provider: options?.provider,
|
|
1052
1502
|
model: options?.model,
|
|
1053
|
-
systemPrompt
|
|
1503
|
+
systemPrompt,
|
|
1054
1504
|
onStepFinish: (step) => {
|
|
1055
1505
|
options?.onStepFinish?.(step);
|
|
1056
1506
|
},
|
|
@@ -1078,6 +1528,11 @@ async function loop(context, options) {
|
|
|
1078
1528
|
options?.onResponse?.(messages);
|
|
1079
1529
|
}
|
|
1080
1530
|
}
|
|
1531
|
+
if (loopHooks.afterLLMCall?.length) {
|
|
1532
|
+
for (const hook of loopHooks.afterLLMCall) {
|
|
1533
|
+
await hook({ context, finishReason, text });
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1081
1536
|
if (finishReason === "stop") {
|
|
1082
1537
|
return text || "Task completed.";
|
|
1083
1538
|
}
|
|
@@ -1137,8 +1592,8 @@ function sleep(ms) {
|
|
|
1137
1592
|
}
|
|
1138
1593
|
|
|
1139
1594
|
// src/tools/read.ts
|
|
1140
|
-
import
|
|
1141
|
-
import { readFileSync as readFileSync3, existsSync as
|
|
1595
|
+
import z3 from "zod";
|
|
1596
|
+
import { readFileSync as readFileSync3, existsSync as existsSync2, statSync } from "fs";
|
|
1142
1597
|
|
|
1143
1598
|
// src/tools/utils.ts
|
|
1144
1599
|
var truncateOutput = (output) => {
|
|
@@ -1158,13 +1613,13 @@ var truncateOutput = (output) => {
|
|
|
1158
1613
|
var ReadTool = {
|
|
1159
1614
|
name: "read",
|
|
1160
1615
|
description: "Read the contents of a file. Supports reading specific line ranges with offset and limit.",
|
|
1161
|
-
inputSchema:
|
|
1162
|
-
filePath:
|
|
1163
|
-
offset:
|
|
1164
|
-
limit:
|
|
1616
|
+
inputSchema: z3.object({
|
|
1617
|
+
filePath: z3.string().describe("The absolute path to the file to read"),
|
|
1618
|
+
offset: z3.number().optional().describe("The line number to start reading from (0-based). Only provide if the file is too large to read at once."),
|
|
1619
|
+
limit: z3.number().optional().describe("The number of lines to read. Only provide if the file is too large to read at once.")
|
|
1165
1620
|
}),
|
|
1166
1621
|
execute: async ({ filePath, offset, limit }) => {
|
|
1167
|
-
if (!
|
|
1622
|
+
if (!existsSync2(filePath)) {
|
|
1168
1623
|
throw new Error(`File does not exist: ${filePath}`);
|
|
1169
1624
|
}
|
|
1170
1625
|
const stats = statSync(filePath);
|
|
@@ -1198,20 +1653,20 @@ var ReadTool = {
|
|
|
1198
1653
|
};
|
|
1199
1654
|
|
|
1200
1655
|
// src/tools/write.ts
|
|
1201
|
-
import
|
|
1202
|
-
import { writeFileSync, mkdirSync, existsSync as
|
|
1656
|
+
import z4 from "zod";
|
|
1657
|
+
import { writeFileSync, mkdirSync, existsSync as existsSync3 } from "fs";
|
|
1203
1658
|
import { dirname } from "path";
|
|
1204
1659
|
var WriteTool = {
|
|
1205
1660
|
name: "write",
|
|
1206
1661
|
description: "Write contents to a file. Automatically creates parent directories if they do not exist. Will overwrite existing files.",
|
|
1207
|
-
inputSchema:
|
|
1208
|
-
filePath:
|
|
1209
|
-
content:
|
|
1662
|
+
inputSchema: z4.object({
|
|
1663
|
+
filePath: z4.string().describe("The absolute path to the file to write (must be absolute, not relative)"),
|
|
1664
|
+
content: z4.string().describe("The content to write to the file")
|
|
1210
1665
|
}),
|
|
1211
1666
|
execute: async ({ filePath, content }) => {
|
|
1212
|
-
const fileExists =
|
|
1667
|
+
const fileExists = existsSync3(filePath);
|
|
1213
1668
|
const dir = dirname(filePath);
|
|
1214
|
-
if (!
|
|
1669
|
+
if (!existsSync3(dir)) {
|
|
1215
1670
|
mkdirSync(dir, { recursive: true });
|
|
1216
1671
|
}
|
|
1217
1672
|
writeFileSync(filePath, content, "utf-8");
|
|
@@ -1225,16 +1680,16 @@ var WriteTool = {
|
|
|
1225
1680
|
};
|
|
1226
1681
|
|
|
1227
1682
|
// src/tools/edit.ts
|
|
1228
|
-
import
|
|
1683
|
+
import z5 from "zod";
|
|
1229
1684
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
1230
1685
|
var EditTool = {
|
|
1231
1686
|
name: "edit",
|
|
1232
1687
|
description: "Performs exact string replacements in files. Use this to edit existing files by replacing old_string with new_string.",
|
|
1233
|
-
inputSchema:
|
|
1234
|
-
filePath:
|
|
1235
|
-
oldString:
|
|
1236
|
-
newString:
|
|
1237
|
-
replaceAll:
|
|
1688
|
+
inputSchema: z5.object({
|
|
1689
|
+
filePath: z5.string().describe("The absolute path to the file to modify"),
|
|
1690
|
+
oldString: z5.string().describe("The exact text to replace (must match exactly)"),
|
|
1691
|
+
newString: z5.string().describe("The text to replace it with (must be different from old_string)"),
|
|
1692
|
+
replaceAll: z5.boolean().optional().default(false).describe("Replace all occurrences of old_string (default false)")
|
|
1238
1693
|
}),
|
|
1239
1694
|
execute: async ({ filePath, oldString, newString, replaceAll = false }) => {
|
|
1240
1695
|
if (oldString === newString) {
|
|
@@ -1281,27 +1736,27 @@ var EditTool = {
|
|
|
1281
1736
|
};
|
|
1282
1737
|
|
|
1283
1738
|
// src/tools/grep.ts
|
|
1284
|
-
import
|
|
1739
|
+
import z6 from "zod";
|
|
1285
1740
|
import { execSync } from "child_process";
|
|
1286
|
-
import { existsSync as
|
|
1741
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1287
1742
|
var GrepTool = {
|
|
1288
1743
|
name: "grep",
|
|
1289
1744
|
description: "A powerful search tool built on ripgrep. Supports regex patterns, file filtering, and multiple output modes.",
|
|
1290
|
-
inputSchema:
|
|
1291
|
-
pattern:
|
|
1292
|
-
path:
|
|
1293
|
-
glob:
|
|
1294
|
-
type:
|
|
1295
|
-
outputMode:
|
|
1296
|
-
context:
|
|
1297
|
-
caseInsensitive:
|
|
1298
|
-
headLimit:
|
|
1299
|
-
offset:
|
|
1300
|
-
multiline:
|
|
1745
|
+
inputSchema: z6.object({
|
|
1746
|
+
pattern: z6.string().describe("The regular expression pattern to search for in file contents"),
|
|
1747
|
+
path: z6.string().optional().describe("File or directory to search in. Defaults to current working directory."),
|
|
1748
|
+
glob: z6.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}")'),
|
|
1749
|
+
type: z6.string().optional().describe("File type to search (e.g., js, py, rust, go, java, ts, tsx, json, md)"),
|
|
1750
|
+
outputMode: z6.enum(["content", "files_with_matches", "count"]).optional().default("files_with_matches").describe('Output mode: "content" shows matching lines, "files_with_matches" shows file paths, "count" shows match counts'),
|
|
1751
|
+
context: z6.number().optional().describe('Number of lines to show before and after each match (only with output_mode: "content")'),
|
|
1752
|
+
caseInsensitive: z6.boolean().optional().default(false).describe("Case insensitive search"),
|
|
1753
|
+
headLimit: z6.number().optional().default(0).describe("Limit output to first N lines/entries. 0 means unlimited."),
|
|
1754
|
+
offset: z6.number().optional().default(0).describe("Skip first N lines/entries before applying head_limit"),
|
|
1755
|
+
multiline: z6.boolean().optional().default(false).describe("Enable multiline mode where patterns can span lines")
|
|
1301
1756
|
}),
|
|
1302
1757
|
execute: async ({
|
|
1303
1758
|
pattern,
|
|
1304
|
-
path:
|
|
1759
|
+
path: path5 = ".",
|
|
1305
1760
|
glob,
|
|
1306
1761
|
type,
|
|
1307
1762
|
outputMode = "files_with_matches",
|
|
@@ -1336,11 +1791,11 @@ var GrepTool = {
|
|
|
1336
1791
|
if (type) {
|
|
1337
1792
|
args.push("--type", type);
|
|
1338
1793
|
}
|
|
1339
|
-
if (
|
|
1340
|
-
if (!
|
|
1341
|
-
throw new Error(`Path does not exist: ${
|
|
1794
|
+
if (path5 && path5 !== ".") {
|
|
1795
|
+
if (!existsSync4(path5)) {
|
|
1796
|
+
throw new Error(`Path does not exist: ${path5}`);
|
|
1342
1797
|
}
|
|
1343
|
-
args.push(
|
|
1798
|
+
args.push(path5);
|
|
1344
1799
|
}
|
|
1345
1800
|
let command = args.map((arg) => {
|
|
1346
1801
|
if (arg.includes(" ") || arg.includes("$") || arg.includes("*")) {
|
|
@@ -1387,31 +1842,31 @@ Command: ${command}`
|
|
|
1387
1842
|
};
|
|
1388
1843
|
|
|
1389
1844
|
// src/tools/ls.ts
|
|
1390
|
-
import
|
|
1845
|
+
import z7 from "zod";
|
|
1391
1846
|
import { readdirSync } from "fs";
|
|
1392
1847
|
var LsTool = {
|
|
1393
1848
|
name: "ls",
|
|
1394
1849
|
description: "List files and directories in a given path",
|
|
1395
|
-
inputSchema:
|
|
1396
|
-
path:
|
|
1850
|
+
inputSchema: z7.object({
|
|
1851
|
+
path: z7.string().optional().describe("The path to list files from (defaults to current directory)")
|
|
1397
1852
|
}),
|
|
1398
|
-
execute: async ({ path:
|
|
1399
|
-
const files = readdirSync(
|
|
1853
|
+
execute: async ({ path: path5 = "." }) => {
|
|
1854
|
+
const files = readdirSync(path5);
|
|
1400
1855
|
return { files };
|
|
1401
1856
|
}
|
|
1402
1857
|
};
|
|
1403
1858
|
|
|
1404
1859
|
// src/tools/bash.ts
|
|
1405
|
-
import
|
|
1860
|
+
import z8 from "zod";
|
|
1406
1861
|
import { execSync as execSync2 } from "child_process";
|
|
1407
1862
|
var BashTool = {
|
|
1408
1863
|
name: "bash",
|
|
1409
1864
|
description: "Execute a bash command and return the output. Supports timeout and working directory configuration.",
|
|
1410
|
-
inputSchema:
|
|
1411
|
-
command:
|
|
1412
|
-
timeout:
|
|
1413
|
-
cwd:
|
|
1414
|
-
description:
|
|
1865
|
+
inputSchema: z8.object({
|
|
1866
|
+
command: z8.string().describe("The bash command to execute"),
|
|
1867
|
+
timeout: z8.number().optional().describe("Optional timeout in milliseconds (max 600000ms / 10 minutes). Defaults to 120000ms (2 minutes)."),
|
|
1868
|
+
cwd: z8.string().optional().describe("Optional working directory for command execution. Defaults to current directory."),
|
|
1869
|
+
description: z8.string().optional().describe("Optional description of what this command does (for logging/debugging)")
|
|
1415
1870
|
}),
|
|
1416
1871
|
execute: async ({ command, timeout = 12e4, cwd, description }) => {
|
|
1417
1872
|
if (timeout && (timeout < 0 || timeout > 6e5)) {
|
|
@@ -1452,13 +1907,13 @@ ${stderr}`),
|
|
|
1452
1907
|
};
|
|
1453
1908
|
|
|
1454
1909
|
// src/tools/tavily.ts
|
|
1455
|
-
import
|
|
1910
|
+
import z9 from "zod";
|
|
1456
1911
|
var TavilyTool = {
|
|
1457
1912
|
name: "tavily",
|
|
1458
1913
|
description: "Search the web using Tavily API",
|
|
1459
|
-
inputSchema:
|
|
1460
|
-
query:
|
|
1461
|
-
maxResults:
|
|
1914
|
+
inputSchema: z9.object({
|
|
1915
|
+
query: z9.string().describe("The search query"),
|
|
1916
|
+
maxResults: z9.number().optional().default(5).describe("Maximum number of results to return")
|
|
1462
1917
|
}),
|
|
1463
1918
|
execute: async ({ query, maxResults = 5 }) => {
|
|
1464
1919
|
const apiKey = process.env.TAVILY_API_KEY;
|
|
@@ -1496,23 +1951,23 @@ var TavilyTool = {
|
|
|
1496
1951
|
};
|
|
1497
1952
|
|
|
1498
1953
|
// src/tools/clarify.ts
|
|
1499
|
-
import
|
|
1500
|
-
import { randomUUID } from "crypto";
|
|
1954
|
+
import z10 from "zod";
|
|
1955
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1501
1956
|
var ClarifyTool = {
|
|
1502
1957
|
name: "clarify",
|
|
1503
1958
|
description: "Ask the user a clarifying question and wait for their response. Use this when you need information from the user to proceed with the task.",
|
|
1504
|
-
inputSchema:
|
|
1505
|
-
question:
|
|
1506
|
-
context:
|
|
1507
|
-
defaultAnswer:
|
|
1508
|
-
timeout:
|
|
1959
|
+
inputSchema: z10.object({
|
|
1960
|
+
question: z10.string().describe("The question to ask the user"),
|
|
1961
|
+
context: z10.string().optional().describe("Additional context to help the user answer"),
|
|
1962
|
+
defaultAnswer: z10.string().optional().describe("Default answer if user does not respond within timeout"),
|
|
1963
|
+
timeout: z10.number().optional().describe("Timeout in milliseconds (default: 5 minutes)")
|
|
1509
1964
|
}),
|
|
1510
1965
|
execute: async (input, toolContext) => {
|
|
1511
1966
|
if (!toolContext?.onClarificationRequest) {
|
|
1512
1967
|
throw new Error("Clarification is not supported in this context. The clarify tool requires a CLI interface with user interaction.");
|
|
1513
1968
|
}
|
|
1514
1969
|
const timeout = input.timeout ?? CLARIFICATION_TIMEOUT;
|
|
1515
|
-
const requestId =
|
|
1970
|
+
const requestId = randomUUID2();
|
|
1516
1971
|
try {
|
|
1517
1972
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1518
1973
|
setTimeout(() => {
|
|
@@ -1567,8 +2022,8 @@ var ConfigLoader = class {
|
|
|
1567
2022
|
const fileInfos = [];
|
|
1568
2023
|
for (let configDir of configDirs) {
|
|
1569
2024
|
try {
|
|
1570
|
-
await
|
|
1571
|
-
const files = await
|
|
2025
|
+
await fs3.access(configDir);
|
|
2026
|
+
const files = await fs3.readdir(configDir);
|
|
1572
2027
|
fileInfos.push({ files, configDir });
|
|
1573
2028
|
} catch {
|
|
1574
2029
|
continue;
|
|
@@ -1585,7 +2040,7 @@ var ConfigLoader = class {
|
|
|
1585
2040
|
const files = fileInfo.files;
|
|
1586
2041
|
for (const file of files) {
|
|
1587
2042
|
if (file.endsWith(".md")) {
|
|
1588
|
-
const config = await this.parseConfig(
|
|
2043
|
+
const config = await this.parseConfig(path4.join(fileInfo.configDir, file));
|
|
1589
2044
|
if (config) configs.push(config);
|
|
1590
2045
|
}
|
|
1591
2046
|
}
|
|
@@ -1597,7 +2052,7 @@ var ConfigLoader = class {
|
|
|
1597
2052
|
}
|
|
1598
2053
|
async parseConfig(filePath) {
|
|
1599
2054
|
try {
|
|
1600
|
-
const content = await
|
|
2055
|
+
const content = await fs3.readFile(filePath, "utf-8");
|
|
1601
2056
|
const lines = content.split("\n");
|
|
1602
2057
|
let name = "";
|
|
1603
2058
|
let description = "";
|
|
@@ -1626,7 +2081,7 @@ var ConfigLoader = class {
|
|
|
1626
2081
|
}
|
|
1627
2082
|
}
|
|
1628
2083
|
if (!name) {
|
|
1629
|
-
name =
|
|
2084
|
+
name = path4.basename(filePath, ".md");
|
|
1630
2085
|
}
|
|
1631
2086
|
return {
|
|
1632
2087
|
name: name.trim(),
|
|
@@ -1677,9 +2132,9 @@ var SubAgentPlugin = class {
|
|
|
1677
2132
|
const toolName = `${config.name}_agent`;
|
|
1678
2133
|
const tool2 = {
|
|
1679
2134
|
description: config.description,
|
|
1680
|
-
inputSchema:
|
|
1681
|
-
task:
|
|
1682
|
-
context:
|
|
2135
|
+
inputSchema: z11.object({
|
|
2136
|
+
task: z11.string().describe("\u8981\u6267\u884C\u7684\u4EFB\u52A1\u63CF\u8FF0"),
|
|
2137
|
+
context: z11.any().optional().describe("\u4EFB\u52A1\u4E0A\u4E0B\u6587\u4FE1\u606F")
|
|
1683
2138
|
}),
|
|
1684
2139
|
execute: async ({ task, context: taskContext }) => {
|
|
1685
2140
|
const tools = { ...BuiltinToolsMap, ...context.getTools() };
|
|
@@ -1706,6 +2161,7 @@ var builtInPlugins = [
|
|
|
1706
2161
|
builtInMCPPlugin,
|
|
1707
2162
|
builtInSkillsPlugin,
|
|
1708
2163
|
builtInPlanModePlugin,
|
|
2164
|
+
builtInTaskTrackingPlugin,
|
|
1709
2165
|
new SubAgentPlugin()
|
|
1710
2166
|
];
|
|
1711
2167
|
var built_in_default = builtInPlugins;
|
|
@@ -1713,10 +2169,12 @@ export {
|
|
|
1713
2169
|
BuiltInPlanModeService,
|
|
1714
2170
|
BuiltInSkillRegistry,
|
|
1715
2171
|
SubAgentPlugin,
|
|
2172
|
+
TaskListService,
|
|
1716
2173
|
builtInMCPPlugin,
|
|
1717
2174
|
builtInPlanModePlugin,
|
|
1718
2175
|
builtInPlugins,
|
|
1719
2176
|
builtInSkillsPlugin,
|
|
2177
|
+
builtInTaskTrackingPlugin,
|
|
1720
2178
|
built_in_default as default
|
|
1721
2179
|
};
|
|
1722
2180
|
//# sourceMappingURL=index.js.map
|