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