@wrongstack/tools 0.89.3 → 0.107.2

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/pack.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as Core from '@wrongstack/core';
2
- import { buildChildEnv, expectDefined, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, compileGlob, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, resolveWstackPaths } from '@wrongstack/core';
2
+ import { buildChildEnv, expectDefined, detectNewlineStyle, normalizeToLf, toStyle, atomicWrite, unifiedDiff, compileGlob, loadPlan, emptyPlan, clearPlan, savePlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, formatPlan, loadTasks, emptyTaskFile, saveTasks, computeTaskItemProgress, formatTaskList, resolveWstackPaths } from '@wrongstack/core';
3
3
  import { spawn, execFileSync, spawnSync } from 'node:child_process';
4
4
  import * as fs12 from 'node:fs/promises';
5
5
  import * as path from 'node:path';
@@ -516,7 +516,7 @@ var ProcessRegistryImpl = class {
516
516
  this.breaker = new CircuitBreaker(breakerConfig);
517
517
  }
518
518
  register(info) {
519
- this.processes.set(info.pid, { ...info, killed: false });
519
+ this.processes.set(info.pid, { ...info, killed: false, protected: info.protected ?? false });
520
520
  }
521
521
  /** Unregister a process by PID. Called on 'close' / 'exit' events. */
522
522
  unregister(pid) {
@@ -602,6 +602,7 @@ var ProcessRegistryImpl = class {
602
602
  const p = this.processes.get(pid);
603
603
  if (!p) return false;
604
604
  if (p.killed) return true;
605
+ if (p.protected) return false;
605
606
  const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
606
607
  const isWin = os.platform() === "win32";
607
608
  if (isWin) {
@@ -652,7 +653,8 @@ var ProcessRegistryImpl = class {
652
653
  const pids = Array.from(this.processes.keys());
653
654
  const killed = [];
654
655
  for (const pid of pids) {
655
- if (this.kill(pid, opts)) killed.push(pid);
656
+ const p = this.processes.get(pid);
657
+ if (p && !p.protected && this.kill(pid, opts)) killed.push(pid);
656
658
  }
657
659
  return killed;
658
660
  }
@@ -6550,6 +6552,147 @@ function anySignal(...signals) {
6550
6552
  function stripTags2(html) {
6551
6553
  return html.replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").trim();
6552
6554
  }
6555
+ var taskTool = {
6556
+ name: "task",
6557
+ category: "Session",
6558
+ description: "Manage structured work items with dependencies, types, and priorities. Use this for complex, multi-step work where tasks have ordering constraints. Unlike `todo` (flat, tactical), `task` supports typed work (feature/bugfix/refactor/etc.), dependencies between items, priority ranking, and agent assignment. The task list persists across session resumes.",
6559
+ usageHint: 'USE FOR STRUCTURED WORK:\n- `action: "replace"` \u2014 set the complete task list (tasks ordered by priority)\n- `action: "add"` \u2014 append a single task\n- `action: "status"` \u2014 update a task\'s status (e.g. pending\u2192in_progress, in_progress\u2192completed)\n- `action: "show"` \u2014 view current tasks without changing them\n\nTask fields:\n- `dependsOn`: list of task IDs this one waits for\n- `type`: "feature" | "bugfix" | "refactor" | "docs" | "test" | "chore"\n- `priority`: "critical" | "high" | "medium" | "low"\n- `assignee`: agent/subagent name (e.g. "bug-hunter", "refactor-planner")\n- `estimateHours`: rough time estimate',
6560
+ permission: "auto",
6561
+ mutating: false,
6562
+ timeoutMs: 2e3,
6563
+ inputSchema: {
6564
+ type: "object",
6565
+ properties: {
6566
+ action: {
6567
+ type: "string",
6568
+ enum: ["replace", "add", "status", "show"],
6569
+ description: "replace = set full list, add = append, status = update task status, show = view only."
6570
+ },
6571
+ tasks: {
6572
+ type: "array",
6573
+ items: {
6574
+ type: "object",
6575
+ properties: {
6576
+ id: { type: "string", description: 'Unique id (e.g. "t1", "auth-flow").' },
6577
+ title: { type: "string", description: "Short title." },
6578
+ description: { type: "string", description: "Optional details." },
6579
+ type: { type: "string", enum: ["feature", "bugfix", "refactor", "docs", "test", "chore"] },
6580
+ priority: { type: "string", enum: ["critical", "high", "medium", "low"] },
6581
+ status: { type: "string", enum: ["pending", "in_progress", "blocked", "failed", "review", "completed"] },
6582
+ dependsOn: {
6583
+ type: "array",
6584
+ items: { type: "string" },
6585
+ description: "IDs of tasks this one depends on."
6586
+ },
6587
+ assignee: { type: "string", description: "Agent/subagent assigned." },
6588
+ estimateHours: { type: "number", description: "Estimated hours." },
6589
+ tags: { type: "array", items: { type: "string" }, description: "Optional tags." },
6590
+ createdAt: { type: "string" },
6591
+ updatedAt: { type: "string" }
6592
+ },
6593
+ required: ["id", "title", "type", "priority", "status"]
6594
+ },
6595
+ description: "Complete task list. Replaces previous list entirely."
6596
+ },
6597
+ task: {
6598
+ type: "object",
6599
+ properties: {
6600
+ title: { type: "string" },
6601
+ description: { type: "string" },
6602
+ type: { type: "string", enum: ["feature", "bugfix", "refactor", "docs", "test", "chore"] },
6603
+ priority: { type: "string", enum: ["critical", "high", "medium", "low"] },
6604
+ status: { type: "string", enum: ["pending", "in_progress", "blocked", "failed", "review", "completed"] },
6605
+ dependsOn: { type: "array", items: { type: "string" } },
6606
+ assignee: { type: "string" },
6607
+ estimateHours: { type: "number" },
6608
+ tags: { type: "array", items: { type: "string" } }
6609
+ },
6610
+ required: ["title", "type", "priority"],
6611
+ description: "Single task to append (id/createdAt/updatedAt auto-generated)."
6612
+ },
6613
+ id: { type: "string", description: "Task id for action=status." },
6614
+ status: {
6615
+ type: "string",
6616
+ enum: ["pending", "in_progress", "blocked", "failed", "review", "completed"],
6617
+ description: "New status for action=status."
6618
+ }
6619
+ },
6620
+ required: ["action"]
6621
+ },
6622
+ async execute(input, ctx) {
6623
+ const taskPath = ctx.meta["task.path"];
6624
+ if (typeof taskPath !== "string" || !taskPath) {
6625
+ return { ok: false, message: "Task storage path not configured.", count: 0, completed: 0, inProgress: 0 };
6626
+ }
6627
+ const sessionId = ctx.session?.id ?? "unknown";
6628
+ let file = await loadTasks(taskPath) ?? emptyTaskFile(sessionId);
6629
+ switch (input.action) {
6630
+ case "show":
6631
+ break;
6632
+ case "replace": {
6633
+ if (!Array.isArray(input.tasks)) {
6634
+ return { ok: false, message: "action=replace requires `tasks` array.", count: 0, completed: 0, inProgress: 0 };
6635
+ }
6636
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6637
+ file.tasks = input.tasks.map((t) => ({
6638
+ ...t,
6639
+ createdAt: t.createdAt || now,
6640
+ updatedAt: now
6641
+ }));
6642
+ await saveTasks(taskPath, file);
6643
+ break;
6644
+ }
6645
+ case "add": {
6646
+ const t = input.task;
6647
+ if (!t || !t.title) {
6648
+ return { ok: false, message: "action=add requires `task` with at least `title`.", count: 0, completed: 0, inProgress: 0 };
6649
+ }
6650
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6651
+ const newTask = {
6652
+ id: `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
6653
+ title: t.title,
6654
+ description: t.description,
6655
+ type: t.type || "feature",
6656
+ priority: t.priority || "medium",
6657
+ status: t.status || "pending",
6658
+ dependsOn: t.dependsOn,
6659
+ assignee: t.assignee,
6660
+ estimateHours: t.estimateHours,
6661
+ tags: t.tags,
6662
+ createdAt: now,
6663
+ updatedAt: now
6664
+ };
6665
+ file.tasks.push(newTask);
6666
+ await saveTasks(taskPath, file);
6667
+ break;
6668
+ }
6669
+ case "status": {
6670
+ if (!input.id || !input.status) {
6671
+ return { ok: false, message: "action=status requires `id` and `status`.", count: 0, completed: 0, inProgress: 0 };
6672
+ }
6673
+ const task = file.tasks.find((t) => t.id === input.id);
6674
+ if (!task) {
6675
+ return { ok: false, message: `Task "${input.id}" not found.`, count: 0, completed: 0, inProgress: 0 };
6676
+ }
6677
+ task.status = input.status;
6678
+ task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
6679
+ await saveTasks(taskPath, file);
6680
+ break;
6681
+ }
6682
+ default:
6683
+ return { ok: false, message: `Unknown action "${input.action}". Use replace | add | status | show.`, count: 0, completed: 0, inProgress: 0 };
6684
+ }
6685
+ const p = computeTaskItemProgress(file.tasks);
6686
+ const summary = file.tasks.length > 0 ? formatTaskList(file.tasks) : "No tasks.";
6687
+ return {
6688
+ ok: true,
6689
+ message: summary,
6690
+ count: file.tasks.length,
6691
+ completed: p.completed,
6692
+ inProgress: p.inProgress
6693
+ };
6694
+ }
6695
+ };
6553
6696
  var testTool = {
6554
6697
  name: "test",
6555
6698
  category: "Code Quality",
@@ -7371,6 +7514,7 @@ var builtinTools = [
7371
7514
  searchTool,
7372
7515
  todoTool,
7373
7516
  planTool,
7517
+ taskTool,
7374
7518
  gitTool,
7375
7519
  patchTool,
7376
7520
  jsonTool,