pulse-coder-engine 0.0.1-alpha.11 → 0.0.1-alpha.13
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 +550 -97
- package/dist/built-in/index.cjs.map +1 -1
- package/dist/built-in/index.d.cts +1 -1
- package/dist/built-in/index.d.ts +1 -1
- package/dist/built-in/index.js +537 -86
- package/dist/built-in/index.js.map +1 -1
- package/dist/{index-BeWyLfso.d.cts → index-CPgs_IXY.d.cts} +84 -2
- package/dist/{index-BeWyLfso.d.ts → index-CPgs_IXY.d.ts} +84 -2
- package/dist/index.cjs +479 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +477 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -43,12 +43,14 @@ __export(src_exports, {
|
|
|
43
43
|
PluginManager: () => PluginManager,
|
|
44
44
|
PulseAgent: () => Engine,
|
|
45
45
|
ReadTool: () => ReadTool,
|
|
46
|
+
TaskListService: () => TaskListService,
|
|
46
47
|
TavilyTool: () => TavilyTool,
|
|
47
48
|
WriteTool: () => WriteTool,
|
|
48
49
|
builtInMCPPlugin: () => builtInMCPPlugin,
|
|
49
50
|
builtInPlanModePlugin: () => builtInPlanModePlugin,
|
|
50
51
|
builtInPlugins: () => builtInPlugins,
|
|
51
52
|
builtInSkillsPlugin: () => builtInSkillsPlugin,
|
|
53
|
+
builtInTaskTrackingPlugin: () => builtInTaskTrackingPlugin,
|
|
52
54
|
getFinalToolsMap: () => getFinalToolsMap,
|
|
53
55
|
loop: () => loop,
|
|
54
56
|
maybeCompactContext: () => maybeCompactContext,
|
|
@@ -525,6 +527,9 @@ async function loop(context, options) {
|
|
|
525
527
|
}
|
|
526
528
|
}
|
|
527
529
|
if (finishReason === "stop") {
|
|
530
|
+
if (!text) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
528
533
|
return text || "Task completed.";
|
|
529
534
|
}
|
|
530
535
|
if (finishReason === "length") {
|
|
@@ -556,6 +561,9 @@ async function loop(context, options) {
|
|
|
556
561
|
}
|
|
557
562
|
continue;
|
|
558
563
|
}
|
|
564
|
+
if (!text) {
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
559
567
|
return text || "Task completed.";
|
|
560
568
|
} catch (error) {
|
|
561
569
|
if (options?.abortSignal?.aborted || error?.name === "AbortError") {
|
|
@@ -747,7 +755,7 @@ var GrepTool = {
|
|
|
747
755
|
}),
|
|
748
756
|
execute: async ({
|
|
749
757
|
pattern,
|
|
750
|
-
path:
|
|
758
|
+
path: path6 = ".",
|
|
751
759
|
glob: glob2,
|
|
752
760
|
type,
|
|
753
761
|
outputMode = "files_with_matches",
|
|
@@ -782,11 +790,11 @@ var GrepTool = {
|
|
|
782
790
|
if (type) {
|
|
783
791
|
args.push("--type", type);
|
|
784
792
|
}
|
|
785
|
-
if (
|
|
786
|
-
if (!(0, import_fs5.existsSync)(
|
|
787
|
-
throw new Error(`Path does not exist: ${
|
|
793
|
+
if (path6 && path6 !== ".") {
|
|
794
|
+
if (!(0, import_fs5.existsSync)(path6)) {
|
|
795
|
+
throw new Error(`Path does not exist: ${path6}`);
|
|
788
796
|
}
|
|
789
|
-
args.push(
|
|
797
|
+
args.push(path6);
|
|
790
798
|
}
|
|
791
799
|
let command = args.map((arg) => {
|
|
792
800
|
if (arg.includes(" ") || arg.includes("$") || arg.includes("*")) {
|
|
@@ -841,8 +849,8 @@ var LsTool = {
|
|
|
841
849
|
inputSchema: import_zod5.default.object({
|
|
842
850
|
path: import_zod5.default.string().optional().describe("The path to list files from (defaults to current directory)")
|
|
843
851
|
}),
|
|
844
|
-
execute: async ({ path:
|
|
845
|
-
const files = (0, import_fs6.readdirSync)(
|
|
852
|
+
execute: async ({ path: path6 = "." }) => {
|
|
853
|
+
const files = (0, import_fs6.readdirSync)(path6);
|
|
846
854
|
return { files };
|
|
847
855
|
}
|
|
848
856
|
};
|
|
@@ -1614,16 +1622,44 @@ var BuiltInSkillRegistry = class {
|
|
|
1614
1622
|
return Array.from(this.skills.values());
|
|
1615
1623
|
}
|
|
1616
1624
|
/**
|
|
1617
|
-
*
|
|
1625
|
+
* 注册或更新一个技能(支持插件在运行期追加技能)
|
|
1626
|
+
*/
|
|
1627
|
+
registerSkill(skill) {
|
|
1628
|
+
const name = skill.name?.trim();
|
|
1629
|
+
if (!name) {
|
|
1630
|
+
throw new Error("Skill name is required");
|
|
1631
|
+
}
|
|
1632
|
+
const existingKey = Array.from(this.skills.keys()).find((key) => key.toLowerCase() === name.toLowerCase());
|
|
1633
|
+
const replaced = existingKey !== void 0;
|
|
1634
|
+
if (existingKey && existingKey !== name) {
|
|
1635
|
+
this.skills.delete(existingKey);
|
|
1636
|
+
}
|
|
1637
|
+
this.skills.set(name, {
|
|
1638
|
+
...skill,
|
|
1639
|
+
name
|
|
1640
|
+
});
|
|
1641
|
+
return {
|
|
1642
|
+
skillName: name,
|
|
1643
|
+
replaced,
|
|
1644
|
+
total: this.skills.size
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* 根据名称获取技能(大小写不敏感)
|
|
1618
1649
|
*/
|
|
1619
1650
|
get(name) {
|
|
1620
|
-
|
|
1651
|
+
const exact = this.skills.get(name);
|
|
1652
|
+
if (exact) {
|
|
1653
|
+
return exact;
|
|
1654
|
+
}
|
|
1655
|
+
const target = name.toLowerCase();
|
|
1656
|
+
return this.getAll().find((skill) => skill.name.toLowerCase() === target);
|
|
1621
1657
|
}
|
|
1622
1658
|
/**
|
|
1623
1659
|
* 检查技能是否存在
|
|
1624
1660
|
*/
|
|
1625
1661
|
has(name) {
|
|
1626
|
-
return this.
|
|
1662
|
+
return !!this.get(name);
|
|
1627
1663
|
}
|
|
1628
1664
|
/**
|
|
1629
1665
|
* 搜索技能(模糊匹配)
|
|
@@ -1638,7 +1674,7 @@ var BuiltInSkillRegistry = class {
|
|
|
1638
1674
|
var skillToolSchema = import_zod10.z.object({
|
|
1639
1675
|
name: import_zod10.z.string().describe("The name of the skill to execute")
|
|
1640
1676
|
});
|
|
1641
|
-
function generateSkillTool(
|
|
1677
|
+
function generateSkillTool(registry) {
|
|
1642
1678
|
const getSkillsPrompt = (availableSkills) => {
|
|
1643
1679
|
return [
|
|
1644
1680
|
"If query matches an available skill's description or instruction [use skill], use the skill tool to get detailed instructions.",
|
|
@@ -1659,10 +1695,10 @@ function generateSkillTool(skills) {
|
|
|
1659
1695
|
};
|
|
1660
1696
|
return {
|
|
1661
1697
|
name: "skill",
|
|
1662
|
-
description: getSkillsPrompt(
|
|
1698
|
+
description: getSkillsPrompt(registry.getAll()),
|
|
1663
1699
|
inputSchema: skillToolSchema,
|
|
1664
1700
|
execute: async ({ name }) => {
|
|
1665
|
-
const skill =
|
|
1701
|
+
const skill = registry.get(name);
|
|
1666
1702
|
if (!skill) {
|
|
1667
1703
|
throw new Error(`Skill ${name} not found`);
|
|
1668
1704
|
}
|
|
@@ -1676,14 +1712,21 @@ var builtInSkillsPlugin = {
|
|
|
1676
1712
|
async initialize(context) {
|
|
1677
1713
|
const registry = new BuiltInSkillRegistry();
|
|
1678
1714
|
await registry.initialize(process.cwd());
|
|
1715
|
+
context.registerService("skillRegistry", registry);
|
|
1716
|
+
context.registerTool("skill", generateSkillTool(registry));
|
|
1717
|
+
context.registerHook("beforeRun", ({ tools }) => {
|
|
1718
|
+
return {
|
|
1719
|
+
tools: {
|
|
1720
|
+
...tools,
|
|
1721
|
+
skill: generateSkillTool(registry)
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
});
|
|
1679
1725
|
const skills = registry.getAll();
|
|
1680
1726
|
if (skills.length === 0) {
|
|
1681
1727
|
console.log("[Skills] No skills found");
|
|
1682
1728
|
return;
|
|
1683
1729
|
}
|
|
1684
|
-
const skillTool = generateSkillTool(skills);
|
|
1685
|
-
context.registerTool("skill", skillTool);
|
|
1686
|
-
context.registerService("skillRegistry", registry);
|
|
1687
1730
|
console.log(`[Skills] Registered ${skills.length} skill(s)`);
|
|
1688
1731
|
}
|
|
1689
1732
|
};
|
|
@@ -1794,6 +1837,26 @@ var KNOWN_TOOL_META = {
|
|
|
1794
1837
|
category: "other",
|
|
1795
1838
|
risk: "low",
|
|
1796
1839
|
description: "Ask the user a targeted clarification question."
|
|
1840
|
+
},
|
|
1841
|
+
task_create: {
|
|
1842
|
+
category: "other",
|
|
1843
|
+
risk: "low",
|
|
1844
|
+
description: "Create tracked task entries for planning and execution visibility."
|
|
1845
|
+
},
|
|
1846
|
+
task_get: {
|
|
1847
|
+
category: "other",
|
|
1848
|
+
risk: "low",
|
|
1849
|
+
description: "Inspect a task entry by ID."
|
|
1850
|
+
},
|
|
1851
|
+
task_list: {
|
|
1852
|
+
category: "other",
|
|
1853
|
+
risk: "low",
|
|
1854
|
+
description: "List task tracking entries and status summary."
|
|
1855
|
+
},
|
|
1856
|
+
task_update: {
|
|
1857
|
+
category: "other",
|
|
1858
|
+
risk: "low",
|
|
1859
|
+
description: "Update task tracking fields and status."
|
|
1797
1860
|
}
|
|
1798
1861
|
};
|
|
1799
1862
|
var BuiltInPlanModeService = class {
|
|
@@ -2073,17 +2136,404 @@ var builtInPlanModePlugin = {
|
|
|
2073
2136
|
}
|
|
2074
2137
|
};
|
|
2075
2138
|
|
|
2076
|
-
// src/built-in/
|
|
2077
|
-
var import_zod11 = require("zod");
|
|
2139
|
+
// src/built-in/task-tracking-plugin/index.ts
|
|
2078
2140
|
var import_fs10 = require("fs");
|
|
2079
2141
|
var import_path4 = __toESM(require("path"), 1);
|
|
2142
|
+
var import_os2 = require("os");
|
|
2143
|
+
var import_crypto2 = require("crypto");
|
|
2144
|
+
var import_zod11 = require("zod");
|
|
2145
|
+
var TASK_STATUSES = ["pending", "in_progress", "completed", "blocked"];
|
|
2146
|
+
var CREATE_TASK_ITEM_SCHEMA = import_zod11.z.object({
|
|
2147
|
+
title: import_zod11.z.string().min(1).describe("Task title"),
|
|
2148
|
+
details: import_zod11.z.string().optional().describe("Task details / acceptance notes"),
|
|
2149
|
+
status: import_zod11.z.enum(TASK_STATUSES).optional().describe("Initial status; defaults to pending"),
|
|
2150
|
+
dependencies: import_zod11.z.array(import_zod11.z.string()).optional().describe("Task IDs that must be completed first"),
|
|
2151
|
+
blockedReason: import_zod11.z.string().optional().describe("Reason when status is blocked"),
|
|
2152
|
+
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.any()).optional().describe("Additional machine-readable metadata")
|
|
2153
|
+
});
|
|
2154
|
+
var TASK_CREATE_INPUT_SCHEMA = import_zod11.z.object({
|
|
2155
|
+
title: import_zod11.z.string().min(1).optional().describe("Task title (single-task mode)"),
|
|
2156
|
+
details: import_zod11.z.string().optional().describe("Task details (single-task mode)"),
|
|
2157
|
+
status: import_zod11.z.enum(TASK_STATUSES).optional().describe("Initial status (single-task mode)"),
|
|
2158
|
+
dependencies: import_zod11.z.array(import_zod11.z.string()).optional().describe("Dependencies (single-task mode)"),
|
|
2159
|
+
blockedReason: import_zod11.z.string().optional().describe("Blocked reason (single-task mode)"),
|
|
2160
|
+
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.any()).optional().describe("Metadata (single-task mode)"),
|
|
2161
|
+
tasks: import_zod11.z.array(CREATE_TASK_ITEM_SCHEMA).min(1).optional().describe("Batch create mode")
|
|
2162
|
+
}).refine((value) => !!value.tasks?.length || !!value.title, {
|
|
2163
|
+
message: "Either provide `tasks` or `title` for single-task mode."
|
|
2164
|
+
});
|
|
2165
|
+
var TASK_GET_INPUT_SCHEMA = import_zod11.z.object({
|
|
2166
|
+
id: import_zod11.z.string().min(1).describe("Task ID to retrieve")
|
|
2167
|
+
});
|
|
2168
|
+
var TASK_LIST_INPUT_SCHEMA = import_zod11.z.object({
|
|
2169
|
+
statuses: import_zod11.z.array(import_zod11.z.enum(TASK_STATUSES)).optional().describe("Filter by statuses"),
|
|
2170
|
+
includeCompleted: import_zod11.z.boolean().optional().describe("Set false to hide completed tasks"),
|
|
2171
|
+
limit: import_zod11.z.number().int().positive().max(500).optional().describe("Maximum tasks to return")
|
|
2172
|
+
});
|
|
2173
|
+
var TASK_TRACKING_SKILL = {
|
|
2174
|
+
name: "task-tracking-workflow",
|
|
2175
|
+
description: "Track complex execution with task tools and proceed directly without confirmation unless the user explicitly asks for confirmation.",
|
|
2176
|
+
location: "pulse-coder-engine/built-in/task-tracking-plugin",
|
|
2177
|
+
content: `# Task Tracking Workflow
|
|
2178
|
+
|
|
2179
|
+
## When to use
|
|
2180
|
+
- The request has multiple phases or deliverables.
|
|
2181
|
+
- Work may span multiple tool calls or sessions.
|
|
2182
|
+
- You need explicit progress visibility and blocker management.
|
|
2183
|
+
|
|
2184
|
+
## Execution autonomy
|
|
2185
|
+
- Default behavior: execute directly and call tools without asking for confirmation.
|
|
2186
|
+
- Only ask for confirmation when the user explicitly requires confirmation.
|
|
2187
|
+
- If critical information is missing, ask only the minimum clarifying question needed to continue.
|
|
2188
|
+
|
|
2189
|
+
## Required flow
|
|
2190
|
+
1. Start by reviewing existing tasks with \`task_list\`.
|
|
2191
|
+
2. If no suitable tasks exist, create focused tasks with \`task_create\` (prefer batch mode).
|
|
2192
|
+
3. Keep exactly one primary task in \`in_progress\` where possible.
|
|
2193
|
+
4. Update status with \`task_update\` after meaningful progress.
|
|
2194
|
+
5. Mark blockers with \`status=blocked\` and a concrete \`blockedReason\`.
|
|
2195
|
+
6. Mark done tasks as \`completed\` and move to the next actionable item.
|
|
2196
|
+
|
|
2197
|
+
## Quality rules
|
|
2198
|
+
- Keep tasks atomic and verifiable.
|
|
2199
|
+
- Avoid single oversized umbrella tasks.
|
|
2200
|
+
- Reuse existing in-progress tasks instead of duplicating.
|
|
2201
|
+
- Keep dependency links explicit when order matters.`
|
|
2202
|
+
};
|
|
2203
|
+
var TASK_UPDATE_INPUT_SCHEMA = import_zod11.z.object({
|
|
2204
|
+
id: import_zod11.z.string().min(1).describe("Task ID to update"),
|
|
2205
|
+
title: import_zod11.z.string().min(1).optional().describe("New title"),
|
|
2206
|
+
details: import_zod11.z.string().optional().describe("New details"),
|
|
2207
|
+
status: import_zod11.z.enum(TASK_STATUSES).optional().describe("New status"),
|
|
2208
|
+
dependencies: import_zod11.z.array(import_zod11.z.string()).optional().describe("Replace dependencies with this list"),
|
|
2209
|
+
blockedReason: import_zod11.z.string().optional().describe("Blocked reason, used with status=blocked"),
|
|
2210
|
+
metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.any()).optional().describe("Replace metadata object"),
|
|
2211
|
+
delete: import_zod11.z.boolean().optional().describe("Delete this task")
|
|
2212
|
+
});
|
|
2213
|
+
function normalizeTaskListId(raw) {
|
|
2214
|
+
const trimmed = raw.trim();
|
|
2215
|
+
if (!trimmed) {
|
|
2216
|
+
return "default";
|
|
2217
|
+
}
|
|
2218
|
+
return trimmed.replace(/[^a-zA-Z0-9._-]/g, "-").slice(0, 120) || "default";
|
|
2219
|
+
}
|
|
2220
|
+
function now() {
|
|
2221
|
+
return Date.now();
|
|
2222
|
+
}
|
|
2223
|
+
function dedupeStrings(values) {
|
|
2224
|
+
if (!values?.length) {
|
|
2225
|
+
return [];
|
|
2226
|
+
}
|
|
2227
|
+
return Array.from(new Set(values.map((value) => String(value).trim()).filter(Boolean)));
|
|
2228
|
+
}
|
|
2229
|
+
var TaskListService = class _TaskListService {
|
|
2230
|
+
taskListId;
|
|
2231
|
+
storagePath;
|
|
2232
|
+
storageDir;
|
|
2233
|
+
initialized = false;
|
|
2234
|
+
createdAt = now();
|
|
2235
|
+
updatedAt = now();
|
|
2236
|
+
tasks = /* @__PURE__ */ new Map();
|
|
2237
|
+
constructor(taskListId = _TaskListService.resolveTaskListId(), storageDir = _TaskListService.resolveStorageDir()) {
|
|
2238
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
2239
|
+
this.storageDir = storageDir;
|
|
2240
|
+
this.taskListId = normalized;
|
|
2241
|
+
this.storagePath = import_path4.default.join(this.storageDir, `${normalized}.json`);
|
|
2242
|
+
}
|
|
2243
|
+
static resolveTaskListId() {
|
|
2244
|
+
return process.env.PULSE_CODER_TASK_LIST_ID || process.env.CLAUDE_CODE_TASK_LIST_ID || "default";
|
|
2245
|
+
}
|
|
2246
|
+
static resolveStorageDir() {
|
|
2247
|
+
return process.env.PULSE_CODER_TASKS_DIR || import_path4.default.join((0, import_os2.homedir)(), ".pulse-coder", "tasks");
|
|
2248
|
+
}
|
|
2249
|
+
async initialize() {
|
|
2250
|
+
if (this.initialized) {
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
await this.loadTaskList(this.taskListId);
|
|
2254
|
+
}
|
|
2255
|
+
async setTaskListId(taskListId) {
|
|
2256
|
+
await this.initialize();
|
|
2257
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
2258
|
+
if (normalized === this.taskListId) {
|
|
2259
|
+
return {
|
|
2260
|
+
switched: false,
|
|
2261
|
+
taskListId: this.taskListId,
|
|
2262
|
+
storagePath: this.storagePath
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
await this.loadTaskList(normalized);
|
|
2266
|
+
return {
|
|
2267
|
+
switched: true,
|
|
2268
|
+
taskListId: this.taskListId,
|
|
2269
|
+
storagePath: this.storagePath
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
async createTask(input) {
|
|
2273
|
+
await this.initialize();
|
|
2274
|
+
const timestamp = now();
|
|
2275
|
+
const status = input.status ?? "pending";
|
|
2276
|
+
const task = {
|
|
2277
|
+
id: (0, import_crypto2.randomUUID)(),
|
|
2278
|
+
title: input.title.trim(),
|
|
2279
|
+
details: input.details,
|
|
2280
|
+
status,
|
|
2281
|
+
dependencies: dedupeStrings(input.dependencies),
|
|
2282
|
+
blockedReason: status === "blocked" ? input.blockedReason ?? "Blocked without reason" : void 0,
|
|
2283
|
+
metadata: input.metadata,
|
|
2284
|
+
createdAt: timestamp,
|
|
2285
|
+
updatedAt: timestamp,
|
|
2286
|
+
completedAt: status === "completed" ? timestamp : void 0
|
|
2287
|
+
};
|
|
2288
|
+
this.tasks.set(task.id, task);
|
|
2289
|
+
this.updatedAt = timestamp;
|
|
2290
|
+
await this.persist();
|
|
2291
|
+
return task;
|
|
2292
|
+
}
|
|
2293
|
+
async createTasks(inputs) {
|
|
2294
|
+
const created = [];
|
|
2295
|
+
for (const input of inputs) {
|
|
2296
|
+
created.push(await this.createTask(input));
|
|
2297
|
+
}
|
|
2298
|
+
return created;
|
|
2299
|
+
}
|
|
2300
|
+
async listTasks(options) {
|
|
2301
|
+
await this.initialize();
|
|
2302
|
+
const statuses = options?.statuses?.length ? new Set(options.statuses) : null;
|
|
2303
|
+
const includeCompleted = options?.includeCompleted ?? true;
|
|
2304
|
+
let tasks = Array.from(this.tasks.values()).filter((task) => {
|
|
2305
|
+
if (!includeCompleted && task.status === "completed") {
|
|
2306
|
+
return false;
|
|
2307
|
+
}
|
|
2308
|
+
if (statuses && !statuses.has(task.status)) {
|
|
2309
|
+
return false;
|
|
2310
|
+
}
|
|
2311
|
+
return true;
|
|
2312
|
+
});
|
|
2313
|
+
tasks = tasks.sort((a, b) => a.createdAt - b.createdAt);
|
|
2314
|
+
if (options?.limit && options.limit > 0) {
|
|
2315
|
+
tasks = tasks.slice(0, options.limit);
|
|
2316
|
+
}
|
|
2317
|
+
return tasks;
|
|
2318
|
+
}
|
|
2319
|
+
async getTask(id) {
|
|
2320
|
+
await this.initialize();
|
|
2321
|
+
return this.tasks.get(id) ?? null;
|
|
2322
|
+
}
|
|
2323
|
+
async updateTask(input) {
|
|
2324
|
+
await this.initialize();
|
|
2325
|
+
if (input.delete) {
|
|
2326
|
+
const deleted = this.tasks.delete(input.id);
|
|
2327
|
+
if (!deleted) {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
this.updatedAt = now();
|
|
2331
|
+
await this.persist();
|
|
2332
|
+
return null;
|
|
2333
|
+
}
|
|
2334
|
+
const existing = this.tasks.get(input.id);
|
|
2335
|
+
if (!existing) {
|
|
2336
|
+
return null;
|
|
2337
|
+
}
|
|
2338
|
+
const timestamp = now();
|
|
2339
|
+
const nextStatus = input.status ?? existing.status;
|
|
2340
|
+
const next = {
|
|
2341
|
+
...existing,
|
|
2342
|
+
title: input.title !== void 0 ? input.title : existing.title,
|
|
2343
|
+
details: input.details !== void 0 ? input.details : existing.details,
|
|
2344
|
+
status: nextStatus,
|
|
2345
|
+
dependencies: input.dependencies !== void 0 ? dedupeStrings(input.dependencies) : existing.dependencies,
|
|
2346
|
+
metadata: input.metadata !== void 0 ? input.metadata : existing.metadata,
|
|
2347
|
+
blockedReason: this.resolveBlockedReason(nextStatus, input.blockedReason, existing.blockedReason),
|
|
2348
|
+
updatedAt: timestamp,
|
|
2349
|
+
completedAt: nextStatus === "completed" ? existing.completedAt ?? timestamp : void 0
|
|
2350
|
+
};
|
|
2351
|
+
this.tasks.set(input.id, next);
|
|
2352
|
+
this.updatedAt = timestamp;
|
|
2353
|
+
await this.persist();
|
|
2354
|
+
return next;
|
|
2355
|
+
}
|
|
2356
|
+
async snapshot(options) {
|
|
2357
|
+
const tasks = await this.listTasks(options);
|
|
2358
|
+
return {
|
|
2359
|
+
taskListId: this.taskListId,
|
|
2360
|
+
storagePath: this.storagePath,
|
|
2361
|
+
createdAt: this.createdAt,
|
|
2362
|
+
updatedAt: this.updatedAt,
|
|
2363
|
+
total: tasks.length,
|
|
2364
|
+
tasks
|
|
2365
|
+
};
|
|
2366
|
+
}
|
|
2367
|
+
resolveBlockedReason(status, blockedReasonInput, previous) {
|
|
2368
|
+
if (status !== "blocked") {
|
|
2369
|
+
return void 0;
|
|
2370
|
+
}
|
|
2371
|
+
if (blockedReasonInput !== void 0) {
|
|
2372
|
+
return blockedReasonInput || "Blocked without reason";
|
|
2373
|
+
}
|
|
2374
|
+
return previous ?? "Blocked without reason";
|
|
2375
|
+
}
|
|
2376
|
+
async loadTaskList(taskListId) {
|
|
2377
|
+
const normalized = normalizeTaskListId(taskListId);
|
|
2378
|
+
this.taskListId = normalized;
|
|
2379
|
+
this.storagePath = import_path4.default.join(this.storageDir, `${normalized}.json`);
|
|
2380
|
+
await import_fs10.promises.mkdir(this.storageDir, { recursive: true });
|
|
2381
|
+
try {
|
|
2382
|
+
const raw = await import_fs10.promises.readFile(this.storagePath, "utf-8");
|
|
2383
|
+
const parsed = JSON.parse(raw);
|
|
2384
|
+
const parsedTasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
|
|
2385
|
+
this.tasks = new Map(parsedTasks.filter((task) => !!task?.id).map((task) => [task.id, task]));
|
|
2386
|
+
this.createdAt = typeof parsed.createdAt === "number" ? parsed.createdAt : now();
|
|
2387
|
+
this.updatedAt = typeof parsed.updatedAt === "number" ? parsed.updatedAt : now();
|
|
2388
|
+
} catch (error) {
|
|
2389
|
+
if (error?.code !== "ENOENT") {
|
|
2390
|
+
throw error;
|
|
2391
|
+
}
|
|
2392
|
+
const timestamp = now();
|
|
2393
|
+
this.tasks = /* @__PURE__ */ new Map();
|
|
2394
|
+
this.createdAt = timestamp;
|
|
2395
|
+
this.updatedAt = timestamp;
|
|
2396
|
+
await this.persist();
|
|
2397
|
+
}
|
|
2398
|
+
this.initialized = true;
|
|
2399
|
+
}
|
|
2400
|
+
async persist() {
|
|
2401
|
+
const payload = {
|
|
2402
|
+
taskListId: this.taskListId,
|
|
2403
|
+
createdAt: this.createdAt,
|
|
2404
|
+
updatedAt: this.updatedAt,
|
|
2405
|
+
tasks: Array.from(this.tasks.values()).sort((a, b) => a.createdAt - b.createdAt)
|
|
2406
|
+
};
|
|
2407
|
+
await import_fs10.promises.writeFile(this.storagePath, JSON.stringify(payload, null, 2), "utf-8");
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
function buildTaskCreateTool(service) {
|
|
2411
|
+
return {
|
|
2412
|
+
name: "task_create",
|
|
2413
|
+
description: "Create one or more tracked tasks for complex work. Use this when work has multiple steps, dependencies, or blockers.",
|
|
2414
|
+
inputSchema: TASK_CREATE_INPUT_SCHEMA,
|
|
2415
|
+
execute: async ({ tasks, title, details, status, dependencies, blockedReason, metadata }) => {
|
|
2416
|
+
const items = tasks?.length ? tasks : [{ title, details, status, dependencies, blockedReason, metadata }];
|
|
2417
|
+
const created = await service.createTasks(items);
|
|
2418
|
+
return {
|
|
2419
|
+
taskListId: service.taskListId,
|
|
2420
|
+
storagePath: service.storagePath,
|
|
2421
|
+
createdCount: created.length,
|
|
2422
|
+
tasks: created
|
|
2423
|
+
};
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
function buildTaskGetTool(service) {
|
|
2428
|
+
return {
|
|
2429
|
+
name: "task_get",
|
|
2430
|
+
description: "Get full details for a single task by task ID.",
|
|
2431
|
+
inputSchema: TASK_GET_INPUT_SCHEMA,
|
|
2432
|
+
execute: async ({ id }) => {
|
|
2433
|
+
const task = await service.getTask(id);
|
|
2434
|
+
if (!task) {
|
|
2435
|
+
throw new Error(`Task not found: ${id}`);
|
|
2436
|
+
}
|
|
2437
|
+
return {
|
|
2438
|
+
taskListId: service.taskListId,
|
|
2439
|
+
storagePath: service.storagePath,
|
|
2440
|
+
task
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
function buildTaskListTool(service) {
|
|
2446
|
+
return {
|
|
2447
|
+
name: "task_list",
|
|
2448
|
+
description: "List tasks and their current status. Use this to check progress before and after major changes.",
|
|
2449
|
+
inputSchema: TASK_LIST_INPUT_SCHEMA,
|
|
2450
|
+
execute: async ({ statuses, includeCompleted, limit }) => {
|
|
2451
|
+
const snapshot = await service.snapshot({ statuses, includeCompleted, limit });
|
|
2452
|
+
const completed = snapshot.tasks.filter((task) => task.status === "completed").length;
|
|
2453
|
+
const inProgress = snapshot.tasks.filter((task) => task.status === "in_progress").length;
|
|
2454
|
+
const pending = snapshot.tasks.filter((task) => task.status === "pending").length;
|
|
2455
|
+
const blocked = snapshot.tasks.filter((task) => task.status === "blocked").length;
|
|
2456
|
+
return {
|
|
2457
|
+
...snapshot,
|
|
2458
|
+
summary: {
|
|
2459
|
+
total: snapshot.total,
|
|
2460
|
+
completed,
|
|
2461
|
+
inProgress,
|
|
2462
|
+
pending,
|
|
2463
|
+
blocked
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
function buildTaskUpdateTool(service) {
|
|
2470
|
+
return {
|
|
2471
|
+
name: "task_update",
|
|
2472
|
+
description: "Update task fields, move status (pending/in_progress/completed/blocked), or delete tasks.",
|
|
2473
|
+
inputSchema: TASK_UPDATE_INPUT_SCHEMA,
|
|
2474
|
+
execute: async (input) => {
|
|
2475
|
+
const task = await service.updateTask(input);
|
|
2476
|
+
if (input.delete) {
|
|
2477
|
+
return {
|
|
2478
|
+
taskListId: service.taskListId,
|
|
2479
|
+
storagePath: service.storagePath,
|
|
2480
|
+
deleted: true,
|
|
2481
|
+
id: input.id
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
if (!task) {
|
|
2485
|
+
throw new Error(`Task not found: ${input.id}`);
|
|
2486
|
+
}
|
|
2487
|
+
return {
|
|
2488
|
+
taskListId: service.taskListId,
|
|
2489
|
+
storagePath: service.storagePath,
|
|
2490
|
+
deleted: false,
|
|
2491
|
+
task
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
var builtInTaskTrackingPlugin = {
|
|
2497
|
+
name: "pulse-coder-engine/built-in-task-tracking",
|
|
2498
|
+
version: "1.0.0",
|
|
2499
|
+
dependencies: ["pulse-coder-engine/built-in-skills"],
|
|
2500
|
+
async initialize(context) {
|
|
2501
|
+
const service = new TaskListService();
|
|
2502
|
+
await service.initialize();
|
|
2503
|
+
context.registerService("taskListService", service);
|
|
2504
|
+
context.registerService("taskTracking", service);
|
|
2505
|
+
const skillRegistry = context.getService("skillRegistry");
|
|
2506
|
+
if (skillRegistry) {
|
|
2507
|
+
const registration = skillRegistry.registerSkill(TASK_TRACKING_SKILL);
|
|
2508
|
+
context.logger.info("[TaskTracking] Registered built-in task tracking skill", registration);
|
|
2509
|
+
} else {
|
|
2510
|
+
context.logger.warn("[TaskTracking] skillRegistry service unavailable; skipped task tracking skill registration");
|
|
2511
|
+
}
|
|
2512
|
+
context.registerTools({
|
|
2513
|
+
task_create: buildTaskCreateTool(service),
|
|
2514
|
+
task_get: buildTaskGetTool(service),
|
|
2515
|
+
task_list: buildTaskListTool(service),
|
|
2516
|
+
task_update: buildTaskUpdateTool(service)
|
|
2517
|
+
});
|
|
2518
|
+
context.logger.info("[TaskTracking] Registered task tools", {
|
|
2519
|
+
taskListId: service.taskListId,
|
|
2520
|
+
storagePath: service.storagePath,
|
|
2521
|
+
skillRegistryAvailable: !!skillRegistry
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2526
|
+
// src/built-in/sub-agent-plugin/index.ts
|
|
2527
|
+
var import_zod12 = require("zod");
|
|
2528
|
+
var import_fs11 = require("fs");
|
|
2529
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
2080
2530
|
var ConfigLoader = class {
|
|
2081
2531
|
async getAgentFilesInfo(configDirs) {
|
|
2082
2532
|
const fileInfos = [];
|
|
2083
2533
|
for (let configDir of configDirs) {
|
|
2084
2534
|
try {
|
|
2085
|
-
await
|
|
2086
|
-
const files = await
|
|
2535
|
+
await import_fs11.promises.access(configDir);
|
|
2536
|
+
const files = await import_fs11.promises.readdir(configDir);
|
|
2087
2537
|
fileInfos.push({ files, configDir });
|
|
2088
2538
|
} catch {
|
|
2089
2539
|
continue;
|
|
@@ -2100,7 +2550,7 @@ var ConfigLoader = class {
|
|
|
2100
2550
|
const files = fileInfo.files;
|
|
2101
2551
|
for (const file of files) {
|
|
2102
2552
|
if (file.endsWith(".md")) {
|
|
2103
|
-
const config = await this.parseConfig(
|
|
2553
|
+
const config = await this.parseConfig(import_path5.default.join(fileInfo.configDir, file));
|
|
2104
2554
|
if (config) configs.push(config);
|
|
2105
2555
|
}
|
|
2106
2556
|
}
|
|
@@ -2112,7 +2562,7 @@ var ConfigLoader = class {
|
|
|
2112
2562
|
}
|
|
2113
2563
|
async parseConfig(filePath) {
|
|
2114
2564
|
try {
|
|
2115
|
-
const content = await
|
|
2565
|
+
const content = await import_fs11.promises.readFile(filePath, "utf-8");
|
|
2116
2566
|
const lines = content.split("\n");
|
|
2117
2567
|
let name = "";
|
|
2118
2568
|
let description = "";
|
|
@@ -2141,7 +2591,7 @@ var ConfigLoader = class {
|
|
|
2141
2591
|
}
|
|
2142
2592
|
}
|
|
2143
2593
|
if (!name) {
|
|
2144
|
-
name =
|
|
2594
|
+
name = import_path5.default.basename(filePath, ".md");
|
|
2145
2595
|
}
|
|
2146
2596
|
return {
|
|
2147
2597
|
name: name.trim(),
|
|
@@ -2192,9 +2642,9 @@ var SubAgentPlugin = class {
|
|
|
2192
2642
|
const toolName = `${config.name}_agent`;
|
|
2193
2643
|
const tool2 = {
|
|
2194
2644
|
description: config.description,
|
|
2195
|
-
inputSchema:
|
|
2196
|
-
task:
|
|
2197
|
-
context:
|
|
2645
|
+
inputSchema: import_zod12.z.object({
|
|
2646
|
+
task: import_zod12.z.string().describe("\u8981\u6267\u884C\u7684\u4EFB\u52A1\u63CF\u8FF0"),
|
|
2647
|
+
context: import_zod12.z.any().optional().describe("\u4EFB\u52A1\u4E0A\u4E0B\u6587\u4FE1\u606F")
|
|
2198
2648
|
}),
|
|
2199
2649
|
execute: async ({ task, context: taskContext }) => {
|
|
2200
2650
|
const tools = { ...BuiltinToolsMap, ...context.getTools() };
|
|
@@ -2221,6 +2671,7 @@ var builtInPlugins = [
|
|
|
2221
2671
|
builtInMCPPlugin,
|
|
2222
2672
|
builtInSkillsPlugin,
|
|
2223
2673
|
builtInPlanModePlugin,
|
|
2674
|
+
builtInTaskTrackingPlugin,
|
|
2224
2675
|
new SubAgentPlugin()
|
|
2225
2676
|
];
|
|
2226
2677
|
|
|
@@ -2408,12 +2859,14 @@ var Engine = class {
|
|
|
2408
2859
|
PluginManager,
|
|
2409
2860
|
PulseAgent,
|
|
2410
2861
|
ReadTool,
|
|
2862
|
+
TaskListService,
|
|
2411
2863
|
TavilyTool,
|
|
2412
2864
|
WriteTool,
|
|
2413
2865
|
builtInMCPPlugin,
|
|
2414
2866
|
builtInPlanModePlugin,
|
|
2415
2867
|
builtInPlugins,
|
|
2416
2868
|
builtInSkillsPlugin,
|
|
2869
|
+
builtInTaskTrackingPlugin,
|
|
2417
2870
|
getFinalToolsMap,
|
|
2418
2871
|
loop,
|
|
2419
2872
|
maybeCompactContext,
|