@wrongstack/tools 0.297.0 → 0.298.1
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/_spawn-stream.d.ts +2 -0
- package/dist/_util.d.ts +29 -1
- package/dist/audit.js +113 -65
- package/dist/bash.js +5 -4
- package/dist/batch-tool-use.js +70 -7
- package/dist/browser/index.js +31 -1
- package/dist/builtin.js +908 -390
- package/dist/codebase-index/index.js +68 -16
- package/dist/codebase-index/project-server-protocol.d.ts +26 -0
- package/dist/codebase-index/project-server.js +60 -16
- package/dist/codebase-index/worker.js +36 -13
- package/dist/codebase-index/writer-search-helpers.d.ts +16 -0
- package/dist/codebase-index/writer.d.ts +13 -1
- package/dist/design.d.ts +1 -1
- package/dist/design.js +4 -3
- package/dist/diff.js +11 -1
- package/dist/e2e.js +9 -6
- package/dist/edit.js +9 -6
- package/dist/exec-allowlist.d.ts +15 -0
- package/dist/exec.js +627 -568
- package/dist/format.js +116 -65
- package/dist/git.js +22 -2
- package/dist/glob.js +11 -5
- package/dist/grep.js +160 -104
- package/dist/index.js +859 -329
- package/dist/install.js +120 -68
- package/dist/json.js +64 -18
- package/dist/kanban-board-inputs.d.ts +21 -4
- package/dist/kanban.js +20 -5
- package/dist/languages/index.js +128 -71
- package/dist/lint.js +113 -65
- package/dist/outdated.js +86 -26
- package/dist/pack.js +908 -390
- package/dist/patch.js +11 -1
- package/dist/plan.js +32 -10
- package/dist/read.js +77 -23
- package/dist/replace.js +11 -3
- package/dist/scaffold.js +9 -6
- package/dist/session-kanban.js +21 -5
- package/dist/task.js +3 -1
- package/dist/test.js +113 -65
- package/dist/todo.js +3 -1
- package/dist/tool-tier.js +908 -390
- package/dist/tool-use.d.ts +1 -1
- package/dist/tool-use.js +28 -4
- package/dist/tree.d.ts +1 -0
- package/dist/tree.js +44 -8
- package/dist/typecheck.js +113 -65
- package/dist/write.js +9 -6
- package/package.json +7 -6
- package/dist/_concurrency.d.ts.map +0 -1
- package/dist/_danger-detect.d.ts.map +0 -1
- package/dist/_edit-match.d.ts.map +0 -1
- package/dist/_env.d.ts.map +0 -1
- package/dist/_fetch-guard.d.ts.map +0 -1
- package/dist/_output-spool.d.ts.map +0 -1
- package/dist/_redact-command.d.ts.map +0 -1
- package/dist/_regex.d.ts.map +0 -1
- package/dist/_session-shell.d.ts.map +0 -1
- package/dist/_shell-pick.d.ts.map +0 -1
- package/dist/_spawn-stream.d.ts.map +0 -1
- package/dist/_syntax-check.d.ts.map +0 -1
- package/dist/_util.d.ts.map +0 -1
- package/dist/_win32-resolve.d.ts.map +0 -1
- package/dist/audit.d.ts.map +0 -1
- package/dist/audit.js.map +0 -7
- package/dist/auto-proceed-loop-guard.d.ts.map +0 -1
- package/dist/auto-proceed-loop-guard.js.map +0 -7
- package/dist/bash-kill-guard.d.ts.map +0 -1
- package/dist/bash.d.ts.map +0 -1
- package/dist/bash.js.map +0 -7
- package/dist/batch-tool-use.d.ts.map +0 -1
- package/dist/batch-tool-use.js.map +0 -7
- package/dist/browser/artifacts.d.ts.map +0 -1
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js.map +0 -7
- package/dist/browser/manager.d.ts.map +0 -1
- package/dist/browser/network-guard-proxy.d.ts.map +0 -1
- package/dist/browser/security.d.ts.map +0 -1
- package/dist/browser/tools.d.ts.map +0 -1
- package/dist/browser/types.d.ts.map +0 -1
- package/dist/builtin.d.ts.map +0 -1
- package/dist/builtin.js.map +0 -7
- package/dist/circuit-breaker.d.ts.map +0 -1
- package/dist/circuit-breaker.js.map +0 -7
- package/dist/codebase-index/background-indexer.d.ts.map +0 -1
- package/dist/codebase-index/bm25.d.ts.map +0 -1
- package/dist/codebase-index/circuit-breaker.d.ts.map +0 -1
- package/dist/codebase-index/codebase-index-tool.d.ts.map +0 -1
- package/dist/codebase-index/codebase-search-tool.d.ts.map +0 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts.map +0 -1
- package/dist/codebase-index/dead-code-scan.d.ts.map +0 -1
- package/dist/codebase-index/generic-parser.d.ts.map +0 -1
- package/dist/codebase-index/gitignore.d.ts.map +0 -1
- package/dist/codebase-index/go-parser.d.ts.map +0 -1
- package/dist/codebase-index/index-service.d.ts.map +0 -1
- package/dist/codebase-index/index.d.ts.map +0 -1
- package/dist/codebase-index/index.js.map +0 -7
- package/dist/codebase-index/indexer.d.ts.map +0 -1
- package/dist/codebase-index/json-parser.d.ts.map +0 -1
- package/dist/codebase-index/languages.d.ts.map +0 -1
- package/dist/codebase-index/lsp-kind.d.ts.map +0 -1
- package/dist/codebase-index/parser-dispatch.d.ts.map +0 -1
- package/dist/codebase-index/project-server-cache.d.ts.map +0 -1
- package/dist/codebase-index/project-server-client.d.ts.map +0 -1
- package/dist/codebase-index/project-server-endpoint.d.ts.map +0 -1
- package/dist/codebase-index/project-server-protocol.d.ts.map +0 -1
- package/dist/codebase-index/project-server.d.ts.map +0 -1
- package/dist/codebase-index/project-server.js.map +0 -7
- package/dist/codebase-index/py-parser.d.ts.map +0 -1
- package/dist/codebase-index/refs-extractor.d.ts.map +0 -1
- package/dist/codebase-index/rs-parser.d.ts.map +0 -1
- package/dist/codebase-index/schema.d.ts.map +0 -1
- package/dist/codebase-index/spawn-gate.d.ts.map +0 -1
- package/dist/codebase-index/sqlite-runtime.d.ts.map +0 -1
- package/dist/codebase-index/ts-parser.d.ts.map +0 -1
- package/dist/codebase-index/worker-protocol.d.ts.map +0 -1
- package/dist/codebase-index/worker.d.ts.map +0 -1
- package/dist/codebase-index/worker.js.map +0 -7
- package/dist/codebase-index/writer-admin.d.ts.map +0 -1
- package/dist/codebase-index/writer-bulk-insert.d.ts.map +0 -1
- package/dist/codebase-index/writer-graph-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-graph-reader.d.ts.map +0 -1
- package/dist/codebase-index/writer-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-pragmas.d.ts.map +0 -1
- package/dist/codebase-index/writer-ref-mapper.d.ts.map +0 -1
- package/dist/codebase-index/writer-schema.d.ts.map +0 -1
- package/dist/codebase-index/writer-search-helpers.d.ts.map +0 -1
- package/dist/codebase-index/writer-store-pool.d.ts.map +0 -1
- package/dist/codebase-index/writer.d.ts.map +0 -1
- package/dist/codebase-index/yaml-parser.d.ts.map +0 -1
- package/dist/design.d.ts.map +0 -1
- package/dist/design.js.map +0 -7
- package/dist/diff.d.ts.map +0 -1
- package/dist/diff.js.map +0 -7
- package/dist/document.d.ts.map +0 -1
- package/dist/document.js.map +0 -7
- package/dist/e2e.d.ts.map +0 -1
- package/dist/e2e.js.map +0 -7
- package/dist/edit.d.ts.map +0 -1
- package/dist/edit.js.map +0 -7
- package/dist/exec-kill-guard.d.ts.map +0 -1
- package/dist/exec.d.ts.map +0 -1
- package/dist/exec.js.map +0 -7
- package/dist/fetch.d.ts.map +0 -1
- package/dist/fetch.js.map +0 -7
- package/dist/format.d.ts.map +0 -1
- package/dist/format.js.map +0 -7
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js.map +0 -7
- package/dist/glob.d.ts.map +0 -1
- package/dist/glob.js.map +0 -7
- package/dist/grep.d.ts.map +0 -1
- package/dist/grep.js.map +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -7
- package/dist/install.d.ts.map +0 -1
- package/dist/install.js.map +0 -7
- package/dist/json.d.ts.map +0 -1
- package/dist/json.js.map +0 -7
- package/dist/kanban-board-inputs.d.ts.map +0 -1
- package/dist/kanban-decomposition-actions.d.ts.map +0 -1
- package/dist/kanban-detail-actions.d.ts.map +0 -1
- package/dist/kanban-evidence-bridge.d.ts.map +0 -1
- package/dist/kanban-presence.d.ts.map +0 -1
- package/dist/kanban-split-task-handler.d.ts.map +0 -1
- package/dist/kanban-task-inputs.d.ts.map +0 -1
- package/dist/kanban-tool-results.d.ts.map +0 -1
- package/dist/kanban-tool-schema.d.ts.map +0 -1
- package/dist/kanban-tool-types.d.ts.map +0 -1
- package/dist/kanban.d.ts.map +0 -1
- package/dist/kanban.js.map +0 -7
- package/dist/languages/detect.d.ts.map +0 -1
- package/dist/languages/diagnostics.d.ts.map +0 -1
- package/dist/languages/execute-tool.d.ts.map +0 -1
- package/dist/languages/execute.d.ts.map +0 -1
- package/dist/languages/index.d.ts.map +0 -1
- package/dist/languages/index.js.map +0 -7
- package/dist/languages/legacy-bridge.d.ts.map +0 -1
- package/dist/languages/package-tool.d.ts.map +0 -1
- package/dist/languages/plan.d.ts.map +0 -1
- package/dist/languages/profile-helpers.d.ts.map +0 -1
- package/dist/languages/profiles/additional.d.ts.map +0 -1
- package/dist/languages/profiles/primary.d.ts.map +0 -1
- package/dist/languages/registry.d.ts.map +0 -1
- package/dist/languages/tool.d.ts.map +0 -1
- package/dist/languages/types.d.ts.map +0 -1
- package/dist/lint.d.ts.map +0 -1
- package/dist/lint.js.map +0 -7
- package/dist/logs.d.ts.map +0 -1
- package/dist/logs.js.map +0 -7
- package/dist/memory.d.ts.map +0 -1
- package/dist/memory.js.map +0 -7
- package/dist/mode.d.ts.map +0 -1
- package/dist/mode.js.map +0 -7
- package/dist/next-steps.d.ts.map +0 -1
- package/dist/next-steps.js.map +0 -7
- package/dist/outdated.d.ts.map +0 -1
- package/dist/outdated.js.map +0 -7
- package/dist/pack.d.ts.map +0 -1
- package/dist/pack.js.map +0 -7
- package/dist/patch.d.ts.map +0 -1
- package/dist/patch.js.map +0 -7
- package/dist/plan.d.ts.map +0 -1
- package/dist/plan.js.map +0 -7
- package/dist/process-guardian.d.ts.map +0 -1
- package/dist/process-registry-persistent.d.ts.map +0 -1
- package/dist/process-registry.d.ts.map +0 -1
- package/dist/process-registry.js.map +0 -7
- package/dist/ps-slash.d.ts.map +0 -1
- package/dist/ps-slash.js.map +0 -7
- package/dist/read.d.ts.map +0 -1
- package/dist/read.js.map +0 -7
- package/dist/replace.d.ts.map +0 -1
- package/dist/replace.js.map +0 -7
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js.map +0 -7
- package/dist/search.d.ts.map +0 -1
- package/dist/search.js.map +0 -7
- package/dist/session-kanban.d.ts.map +0 -1
- package/dist/session-kanban.js.map +0 -7
- package/dist/set-working-dir.d.ts.map +0 -1
- package/dist/skill.d.ts.map +0 -1
- package/dist/skill.js.map +0 -7
- package/dist/spawn-background.d.ts.map +0 -1
- package/dist/task.d.ts.map +0 -1
- package/dist/task.js.map +0 -7
- package/dist/test.d.ts.map +0 -1
- package/dist/test.js.map +0 -7
- package/dist/todo.d.ts.map +0 -1
- package/dist/todo.js.map +0 -7
- package/dist/tool-diff.d.ts.map +0 -1
- package/dist/tool-diff.js.map +0 -7
- package/dist/tool-help.d.ts.map +0 -1
- package/dist/tool-help.js.map +0 -7
- package/dist/tool-icon-map.d.ts.map +0 -1
- package/dist/tool-icons.d.ts.map +0 -1
- package/dist/tool-icons.js.map +0 -7
- package/dist/tool-search.d.ts.map +0 -1
- package/dist/tool-search.js.map +0 -7
- package/dist/tool-summary.d.ts.map +0 -1
- package/dist/tool-summary.js.map +0 -7
- package/dist/tool-tier.d.ts.map +0 -1
- package/dist/tool-tier.js.map +0 -7
- package/dist/tool-use.d.ts.map +0 -1
- package/dist/tool-use.js.map +0 -7
- package/dist/tree.d.ts.map +0 -1
- package/dist/tree.js.map +0 -7
- package/dist/typecheck.d.ts.map +0 -1
- package/dist/typecheck.js.map +0 -7
- package/dist/win32.d.ts.map +0 -1
- package/dist/win32.js.map +0 -7
- package/dist/write.d.ts.map +0 -1
- package/dist/write.js.map +0 -7
package/dist/todo.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"todo.d.ts","sourceRoot":"","sources":["../src/todo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAInD,UAAU,SAAS;IACjB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,CA8IhD,CAAC"}
|
package/dist/todo.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/todo.ts", "../src/session-kanban.ts"],
|
|
4
|
-
"sourcesContent": ["import type { TodoItem } from '@wrongstack/core/agent';\nimport type { Tool } from '@wrongstack/core/types';\nimport { loadPlan, loadTasks, savePlan, saveTasks, setPlanItemStatus } from '@wrongstack/core/storage';\nimport { mirrorSessionTodosToKanban } from './session-kanban.js';\n\ninterface TodoInput {\n todos: TodoItem[];\n}\n\ninterface TodoOutput {\n count: number;\n in_progress: number;\n}\n\nexport const todoTool: Tool<TodoInput, TodoOutput> = {\n name: 'todo',\n category: 'Session',\n description:\n 'Manage the session-level todo list. This is the primary mechanism for tracking multi-step work. ' +\n 'The list is fully replaced on every call (not appended).',\n usageHint:\n 'BEST PRACTICE for complex tasks:\\n' +\n '- At the beginning of a non-trivial task, create a clear todo list with specific, actionable items.\\n' +\n '- Only **one** item should be `in_progress` at any time.\\n' +\n '- Update the list frequently as work progresses (mark items done, add new ones, change status).\\n' +\n '- **Re-order items** to reflect current priorities \u2014 the full list is replaced each call, so item order is entirely under your control.\\n' +\n '- When all items are completed the board auto-clears \u2014 you do NOT need to send an empty list.\\n' +\n '- The system and user can see this list, so keep it honest and up-to-date.\\n' +\n 'This tool is extremely valuable for maintaining focus and giving the user visibility into your plan.',\n permission: 'auto',\n mutating: false, // mutates only conversation state (ctx.todos), not external state \u2014 no confirmation needed\n timeoutMs: 5_000,\n capabilities: ['session.todo'],\n icon: 'todo',\n inputSchema: {\n type: 'object',\n properties: {\n todos: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier for the todo item (e.g. \"1\", \"auth-flow\").',\n },\n content: {\n type: 'string',\n description: 'Clear, actionable description of the task.',\n },\n status: {\n type: 'string',\n enum: ['pending', 'in_progress', 'completed'],\n description: 'Current status. Only one item should be \"in_progress\" at a time.',\n },\n activeForm: {\n type: 'string',\n description:\n 'Optional present-tense form shown while the task is active (e.g. \"Fixing auth bug\").',\n },\n },\n required: ['id', 'content', 'status'],\n },\n description: 'The complete new list of todos. This replaces the previous list entirely.',\n },\n },\n required: ['todos'],\n },\n async execute(input, ctx) {\n if (!Array.isArray(input?.todos)) {\n throw new Error('todo: todos must be an array');\n }\n const items = input.todos.filter((t): t is TodoItem => Boolean(t?.id && t.content));\n const inProgress = items.filter((t) => t.status === 'in_progress');\n if (inProgress.length > 1) {\n // Keep only the first as in_progress, mark rest pending\n let seenInProgress = false;\n for (const item of items) {\n if (item.status === 'in_progress') {\n if (seenInProgress) item.status = 'pending';\n seenInProgress = true;\n }\n }\n }\n ctx.state.replaceTodos(items);\n\n // Kanban is the session work surface. Mirror the requested list (rather\n // than ctx.todos) so the final all-completed snapshot reaches Done even\n // though ConversationState auto-clears an all-done tactical todo list.\n // Fire-and-forget: the mirror is observational and must not block the tool\n // response (or consume the tool's timeout budget) waiting on kanban file\n // locks that may be held by other agents.\n mirrorSessionTodosToKanban(ctx.projectRoot, items, ctx.session?.id ?? 'session');\n\n // Auto-complete parent plan items / tasks when all their promoted\n // todos are done. Runs after state mutation so the UI sees the new\n // todo list before we touch the plan/task files.\n const completedPlanIds = new Set<string>();\n const completedTaskIds = new Set<string>();\n const pendingPlanIds = new Set<string>();\n const pendingTaskIds = new Set<string>();\n\n for (const item of items) {\n if (item.promotedFromPlan) {\n (item.status === 'completed' ? completedPlanIds : pendingPlanIds).add(\n item.promotedFromPlan,\n );\n }\n if (item.promotedFromTask) {\n (item.status === 'completed' ? completedTaskIds : pendingTaskIds).add(\n item.promotedFromTask,\n );\n }\n }\n\n // Mark fully-completed plan items as done\n for (const planId of completedPlanIds) {\n if (pendingPlanIds.has(planId)) continue; // not all done yet\n const planPath = (ctx.meta as Record<string, unknown>)['plan.path'];\n if (typeof planPath !== 'string' || !planPath) continue;\n try {\n const plan = await loadPlan(planPath);\n if (plan) {\n const updated = setPlanItemStatus(plan, planId, 'done');\n await savePlan(planPath, updated);\n }\n } catch {\n /* best-effort */\n }\n }\n\n // Mark fully-completed tasks as completed\n for (const taskId of completedTaskIds) {\n if (pendingTaskIds.has(taskId)) continue; // not all done yet\n const taskPath = (ctx.meta as Record<string, unknown>)['task.path'];\n if (typeof taskPath !== 'string' || !taskPath) continue;\n try {\n const file = await loadTasks(taskPath);\n if (file) {\n const task = file.tasks.find((t) => t.id === taskId);\n if (task && task.status !== 'completed') {\n task.status = 'completed';\n task.updatedAt = new Date().toISOString();\n await saveTasks(taskPath, file);\n }\n }\n } catch {\n /* best-effort */\n }\n }\n\n return {\n count: items.length,\n in_progress: items.filter((t) => t.status === 'in_progress').length,\n };\n },\n};\n", "import { type FSWatcher, watch } from 'node:fs';\nimport { basename, dirname } from 'node:path';\nimport type { Context, TodoItem } from '@wrongstack/core/agent';\nimport { getSharedProjectMailbox } from '@wrongstack/core/coordination';\nimport {\n loadPlan,\n loadTasks,\n mutatePlan,\n mutateTasks,\n type PlanFile,\n type PlanItem,\n type TaskFile,\n} from '@wrongstack/core/storage';\nimport { deserializeTaskGraph } from '@wrongstack/core/tasking';\nimport type { SerializedTaskGraph, TaskStatus } from '@wrongstack/core/types';\nimport { resolveWstackPaths, type TaskItem } from '@wrongstack/core/utils';\nimport {\n bridgeKanbanSupervisor,\n createBoard,\n getBoard,\n type KanbanBoard,\n type KanbanColumn,\n type KanbanTask,\n listBoards,\n removeBoard,\n syncBoardFromTaskGraph,\n touchKanbanPresence,\n updateBoard,\n} from '@wrongstack/kanban';\n\nconst SESSION_BOARD_TAG = 'session-work';\nconst MIRROR_DISABLED_ENV = 'WRONGSTACK_KANBAN_TASK_MIRROR';\n\n/** The canonical workflow shared by WebUI and TUI session boards. */\nexport const SESSION_KANBAN_COLUMNS: KanbanColumn[] = [\n { id: 'todo', title: 'Todo', order: 0, wipLimit: 0, color: '#2563eb' },\n { id: 'in-progress', title: 'Running', order: 1, wipLimit: 1, color: '#d97706' },\n { id: 'review', title: 'Preview', order: 2, wipLimit: 0, color: '#7c3aed' },\n { id: 'done', title: 'Done', order: 3, wipLimit: 0, color: '#16a34a' },\n];\n\nconst boardQueue = new Map<string, Promise<void>>();\nconst boardEnsures = new Map<string, Promise<KanbanBoard>>();\ntype PendingMirror = {\n projectRoot: string;\n sessionId: string;\n graph: SerializedTaskGraph;\n sourceSystem: 'session-todo' | 'session-task' | 'session-plan';\n};\nconst pendingMirrors = new Map<string, PendingMirror>();\nconst activeMirrors = new Set<string>();\nconst bindings = new WeakMap<Context, () => void>();\nconst suppressedTodoMirrors = new WeakSet<Context>();\nconst activeSessionBoards = new Map<string, number>();\n\nfunction boardKey(projectRoot: string, sessionId: string): string {\n return `${projectRoot}\\0${sessionId}`;\n}\n\nfunction mirrorKey(\n projectRoot: string,\n sessionId: string,\n sourceSystem: PendingMirror['sourceSystem'],\n): string {\n return `${boardKey(projectRoot, sessionId)}\\0${sourceSystem}`;\n}\n\nfunction sessionTag(sessionId: string): string {\n return `session:${sessionId}`;\n}\n\nfunction sessionBoardTitle(sessionId: string): string {\n const leaf = sessionId.split(/[\\\\/]/).filter(Boolean).pop() ?? sessionId;\n return `Session ${leaf.slice(0, 12)}`;\n}\n\nfunction sessionBoardTags(sessionId: string): string[] {\n return ['session', SESSION_BOARD_TAG, sessionTag(sessionId)];\n}\n\nfunction sessionIdFromTags(tags: readonly string[] | undefined): string | null {\n const tag = tags?.find((candidate) => candidate.startsWith('session:'));\n return tag?.slice('session:'.length) || null;\n}\n\nfunction isOwnedSessionBoard(tags: readonly string[] | undefined): boolean {\n return Boolean(tags?.includes(SESSION_BOARD_TAG) && sessionIdFromTags(tags));\n}\n\nfunction retainActiveSessionBoard(projectRoot: string, sessionId: string): void {\n const key = boardKey(projectRoot, sessionId);\n activeSessionBoards.set(key, (activeSessionBoards.get(key) ?? 0) + 1);\n}\n\nfunction releaseActiveSessionBoard(projectRoot: string, sessionId: string): void {\n const key = boardKey(projectRoot, sessionId);\n const remaining = (activeSessionBoards.get(key) ?? 0) - 1;\n if (remaining > 0) activeSessionBoards.set(key, remaining);\n else activeSessionBoards.delete(key);\n}\n\nfunction isSessionBoardActive(projectRoot: string, sessionId: string): boolean {\n return (activeSessionBoards.get(boardKey(projectRoot, sessionId)) ?? 0) > 0;\n}\n\nfunction sameColumns(columns: readonly KanbanColumn[]): boolean {\n return (\n columns.length === SESSION_KANBAN_COLUMNS.length &&\n columns.every((column, index) => column.id === SESSION_KANBAN_COLUMNS[index]?.id)\n );\n}\n\n/** Create (or migrate) the single Kanban board owned by a session. */\nexport async function ensureSessionKanbanBoard(\n projectRoot: string | undefined,\n sessionId: string,\n): Promise<KanbanBoard | null> {\n if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === '0') return null;\n const key = boardKey(projectRoot, sessionId);\n const inFlight = boardEnsures.get(key);\n if (inFlight) return inFlight;\n\n const promise = (async () => {\n const summary = (await listBoards(projectRoot)).find((board) =>\n board.tags?.includes(sessionTag(sessionId)),\n );\n let board = summary ? await getBoard(projectRoot, summary.id) : null;\n if (!board) {\n return createBoard(projectRoot, {\n title: sessionBoardTitle(sessionId),\n description: 'Live session work: todos, tasks, and plan items.',\n tags: sessionBoardTags(sessionId),\n columns: SESSION_KANBAN_COLUMNS,\n generatedBy: `session-kanban:${sessionId}`,\n });\n }\n\n // Boards produced by the older task/plan mirrors used the generic five\n // columns. Migrate only session-owned boards; updateBoard reconciles cards\n // from Backlog into Todo while preserving Running/Preview/Done cards.\n if (!sameColumns(board.columns) || !board.tags?.includes(SESSION_BOARD_TAG)) {\n board =\n (await updateBoard(projectRoot, board.id, {\n title: sessionBoardTitle(sessionId),\n description: 'Live session work: todos, tasks, and plan items.',\n tags: [...new Set([...(board.tags ?? []), ...sessionBoardTags(sessionId)])],\n columns: SESSION_KANBAN_COLUMNS,\n })) ?? board;\n }\n return board;\n })();\n\n boardEnsures.set(key, promise);\n try {\n return await promise;\n } finally {\n boardEnsures.delete(key);\n }\n}\n\nfunction enqueueBoardWork<T>(\n projectRoot: string,\n sessionId: string,\n work: () => Promise<T>,\n): Promise<T> {\n const key = boardKey(projectRoot, sessionId);\n const previous = boardQueue.get(key) ?? Promise.resolve();\n const result = previous.catch(() => undefined).then(work);\n const tail = result.then(\n () => undefined,\n () => undefined,\n );\n boardQueue.set(key, tail);\n void tail.then(() => {\n if (boardQueue.get(key) === tail) boardQueue.delete(key);\n });\n return result;\n}\n\nasync function removeEmptySessionBoard(\n projectRoot: string,\n boardId: string,\n sessionId: string,\n): Promise<string | null> {\n return enqueueBoardWork(projectRoot, sessionId, async () => {\n if (isSessionBoardActive(projectRoot, sessionId)) return null;\n const board = await getBoard(projectRoot, boardId);\n if (!board || board.tasks.length > 0 || !isOwnedSessionBoard(board.tags)) return null;\n if (sessionIdFromTags(board.tags) !== sessionId) return null;\n return (await removeBoard(projectRoot, board.id)) ? board.id : null;\n });\n}\n\n/** Remove a particular inactive session's system-owned board when it has no cards. */\nexport async function cleanupSessionKanbanBoardIfEmpty(\n projectRoot: string | undefined,\n sessionId: string,\n): Promise<string[]> {\n if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === '0') return [];\n if (isSessionBoardActive(projectRoot, sessionId)) return [];\n const candidates = (await listBoards(projectRoot)).filter(\n (board) =>\n board.taskCount === 0 &&\n isOwnedSessionBoard(board.tags) &&\n sessionIdFromTags(board.tags) === sessionId,\n );\n const removed = await Promise.all(\n candidates.map((board) => removeEmptySessionBoard(projectRoot, board.id, sessionId)),\n );\n return removed.filter((boardId): boardId is string => Boolean(boardId));\n}\n\n/** Prune stale empty session boards while preserving manual and live boards. */\nexport async function cleanupEmptySessionKanbanBoards(\n projectRoot: string | undefined,\n activeSessionId = '',\n): Promise<string[]> {\n if (!projectRoot || process.env[MIRROR_DISABLED_ENV] === '0') return [];\n const candidates = (await listBoards(projectRoot)).flatMap((board) => {\n const ownerSessionId = sessionIdFromTags(board.tags);\n return board.taskCount === 0 &&\n isOwnedSessionBoard(board.tags) &&\n ownerSessionId &&\n ownerSessionId !== activeSessionId &&\n !isSessionBoardActive(projectRoot, ownerSessionId)\n ? [{ boardId: board.id, sessionId: ownerSessionId }]\n : [];\n });\n const removed = await Promise.all(\n candidates.map(({ boardId, sessionId }) =>\n removeEmptySessionBoard(projectRoot, boardId, sessionId),\n ),\n );\n return removed.filter((boardId): boardId is string => Boolean(boardId));\n}\n\nasync function projectGraph(\n projectRoot: string | undefined,\n sessionId: string,\n graph: SerializedTaskGraph,\n sourceSystem: 'session-todo' | 'session-task' | 'session-plan',\n): Promise<KanbanBoard | null> {\n if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === '0') return null;\n return enqueueBoardWork(projectRoot, sessionId, async () => {\n const board = await ensureSessionKanbanBoard(projectRoot, sessionId);\n if (!board) return null;\n const result = await syncBoardFromTaskGraph(\n projectRoot,\n board.id,\n deserializeTaskGraph(graph),\n {\n sourceSystem,\n tags: [...new Set([...(board.tags ?? []), ...sessionBoardTags(sessionId)])],\n archiveMissingTasks: true,\n includeCompletedTasks: true,\n },\n );\n return result?.board ?? null;\n });\n}\n\n/**\n * Queue an observational mirror without retaining every intermediate state.\n * Todo/task/plan streams are independent, but within each stream only the\n * newest pending graph matters. This bounds a stalled file lock to one active\n * and one pending projection instead of an arbitrarily long Promise chain.\n */\nfunction queueLatestMirror(\n projectRoot: string | undefined,\n sessionId: string,\n graph: SerializedTaskGraph,\n sourceSystem: PendingMirror['sourceSystem'],\n): void {\n if (!projectRoot || !sessionId || process.env[MIRROR_DISABLED_ENV] === '0') return;\n const key = mirrorKey(projectRoot, sessionId, sourceSystem);\n pendingMirrors.set(key, { projectRoot, sessionId, graph, sourceSystem });\n if (activeMirrors.has(key)) return;\n activeMirrors.add(key);\n void (async () => {\n try {\n for (;;) {\n const pending = pendingMirrors.get(key);\n if (!pending) break;\n pendingMirrors.delete(key);\n try {\n await projectGraph(\n pending.projectRoot,\n pending.sessionId,\n pending.graph,\n pending.sourceSystem,\n );\n } catch {\n // Mirrors are observational. A newer pending snapshot, if present,\n // still gets a chance after a transient lock or filesystem failure.\n }\n }\n } finally {\n activeMirrors.delete(key);\n // A mirror can arrive between the last Map read and deleting the active\n // marker. Re-arm once so that snapshot cannot be stranded.\n const pending = pendingMirrors.get(key);\n if (pending) {\n pendingMirrors.delete(key);\n queueLatestMirror(\n pending.projectRoot,\n pending.sessionId,\n pending.graph,\n pending.sourceSystem,\n );\n }\n }\n })();\n}\n\nexport function todoListToSerializedGraph(\n todos: readonly TodoItem[],\n sessionId: string,\n): SerializedTaskGraph {\n const nodes = todos.map((todo, index) => ({\n id: todo.id,\n title: todo.content,\n description: todo.activeForm ?? '',\n type: 'chore' as const,\n priority: 'medium' as const,\n status: todo.status,\n createdAt: index,\n updatedAt: index,\n }));\n return {\n id: `todo:${sessionId}`,\n specId: `todo:${sessionId}`,\n title: 'Session todos',\n nodes,\n edges: [],\n rootNodes: nodes.map((node) => node.id),\n createdAt: 0,\n updatedAt: 0,\n };\n}\n\nexport function taskFileToSerializedGraph(\n tasks: readonly TaskItem[],\n sessionId: string,\n): SerializedTaskGraph {\n const ids = new Set(tasks.map((task) => task.id));\n const nodes = tasks.map((task, index) => ({\n id: task.id,\n title: task.title,\n description: task.description ?? '',\n type: task.type,\n priority: task.priority,\n status: task.status,\n ...(task.assignee ? { assignee: task.assignee } : {}),\n ...(task.estimateHours !== undefined ? { estimateHours: task.estimateHours } : {}),\n createdAt: index,\n updatedAt: index,\n }));\n const edges = tasks.flatMap((task) =>\n (task.dependsOn ?? [])\n .filter((dependency) => ids.has(dependency))\n .map((dependency) => ({\n id: `${dependency}->${task.id}`,\n from: dependency,\n to: task.id,\n type: 'depends_on' as const,\n })),\n );\n const hasIncoming = new Set(edges.map((edge) => edge.to));\n const rootNodes = nodes.filter((node) => !hasIncoming.has(node.id)).map((node) => node.id);\n return {\n // Keep the historical graph id so existing mirrored task cards are reused.\n id: `session:${sessionId}`,\n specId: `session:${sessionId}`,\n title: 'Session tasks',\n nodes,\n edges,\n rootNodes: rootNodes.length ? rootNodes : nodes[0] ? [nodes[0].id] : [],\n createdAt: 0,\n updatedAt: 0,\n };\n}\n\nconst PLAN_STATUS_TO_TASK: Record<PlanItem['status'], TaskStatus> = {\n open: 'pending',\n in_progress: 'in_progress',\n done: 'completed',\n};\n\nexport function planFileToSerializedGraph(\n items: readonly PlanItem[],\n sessionId: string,\n): SerializedTaskGraph {\n const nodes = items.map((item, index) => ({\n id: item.id,\n title: item.title,\n description: item.details ?? '',\n type: 'chore' as const,\n priority: 'medium' as const,\n status: PLAN_STATUS_TO_TASK[item.status],\n createdAt: index,\n updatedAt: index,\n }));\n return {\n id: `plan:${sessionId}`,\n specId: `plan:${sessionId}`,\n title: 'Session plan',\n nodes,\n edges: [],\n rootNodes: nodes.map((node) => node.id),\n createdAt: 0,\n updatedAt: 0,\n };\n}\n\nexport function projectSessionTodosToKanban(\n projectRoot: string | undefined,\n todos: readonly TodoItem[],\n sessionId: string,\n): Promise<KanbanBoard | null> {\n return projectGraph(\n projectRoot,\n sessionId,\n todoListToSerializedGraph(todos, sessionId),\n 'session-todo',\n );\n}\n\nexport function projectSessionTasksToKanban(\n projectRoot: string | undefined,\n tasks: readonly TaskItem[],\n sessionId: string,\n): Promise<KanbanBoard | null> {\n return projectGraph(\n projectRoot,\n sessionId,\n taskFileToSerializedGraph(tasks, sessionId),\n 'session-task',\n );\n}\n\nexport function projectSessionPlanToKanban(\n projectRoot: string | undefined,\n items: readonly PlanItem[],\n sessionId: string,\n): Promise<KanbanBoard | null> {\n return projectGraph(\n projectRoot,\n sessionId,\n planFileToSerializedGraph(items, sessionId),\n 'session-plan',\n );\n}\n\nfunction fireAndForget(context: string, work: Promise<unknown>): void {\n void work.catch((err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(JSON.stringify({ level: 'warn', event: 'session-kanban', context, message, timestamp: new Date().toISOString() }));\n });\n}\n\nfunction broadcastTodoUpdate(context: Context, todos: readonly TodoItem[]): void {\n const sessionId = context.session?.id ?? '';\n if (!context.agentId || !sessionId) return;\n const projectDir = resolveWstackPaths({ projectRoot: context.projectRoot }).projectDir;\n const mailbox = getSharedProjectMailbox(projectDir);\n void mailbox\n .send({\n from: context.agentId,\n to: '*',\n type: 'status',\n subject: `Kanban todo list updated (${todos.length} item${todos.length === 1 ? '' : 's'})`,\n body: JSON.stringify({\n kind: 'kanban.todos.updated',\n sessionId,\n revision: context.state.revision,\n todos,\n }),\n priority: 'normal',\n senderSessionId: sessionId,\n ttlMs: 6 * 60 * 60 * 1000,\n })\n .catch(() => {\n // Mailbox awareness is best-effort; canonical state is already updated.\n });\n}\n\nfunction notifyTodoUpdate(context: Context, todos: readonly TodoItem[]): void {\n const summary = todos.length\n ? todos.map((todo) => `- [${todo.status}] ${todo.content} (${todo.id})`).join('\\n')\n : '- No active todos remain.';\n const text = `[KANBAN TODO UPDATE]\\nAnother Kanban agent reassessed the shared board. The canonical todo list is now:\\n${summary}\\nReassess your current plan before continuing; do not rely on the initial todo snapshot.`;\n const state = context.state as Partial<Context['state']>;\n if (typeof state.appendBlockToLastUserMessage === 'function') {\n if (state.appendBlockToLastUserMessage({ type: 'text', text })) return;\n }\n if (typeof state.appendMessage === 'function') {\n state.appendMessage({ role: 'user', content: [{ type: 'text', text }] });\n }\n}\n\nexport function mirrorSessionTodosToKanban(\n projectRoot: string | undefined,\n todos: readonly TodoItem[],\n sessionId: string,\n): void {\n queueLatestMirror(\n projectRoot,\n sessionId,\n todoListToSerializedGraph(todos, sessionId),\n 'session-todo',\n );\n}\n\nexport function mirrorSessionTasksToKanban(\n projectRoot: string | undefined,\n tasks: readonly TaskItem[],\n sessionId: string,\n): void {\n queueLatestMirror(\n projectRoot,\n sessionId,\n taskFileToSerializedGraph(tasks, sessionId),\n 'session-task',\n );\n}\n\nexport function mirrorSessionPlanToKanban(\n projectRoot: string | undefined,\n items: readonly PlanItem[],\n sessionId: string,\n): void {\n queueLatestMirror(\n projectRoot,\n sessionId,\n planFileToSerializedGraph(items, sessionId),\n 'session-plan',\n );\n}\n\n/**\n * Bind all live session work paths to Kanban. Todo changes are observed from\n * ConversationState; plan/task sidecars are watched so slash commands, WebUI,\n * plugins, and tools all pass through the same board.\n */\nexport function attachSessionKanbanMirror(context: Context): () => void {\n const existing = bindings.get(context);\n if (existing) return existing;\n\n const attachedProjectRoot = context.projectRoot ?? '';\n let registeredSessionId = '';\n const syncActiveSessionRegistration = () => {\n if (!attachedProjectRoot) return;\n const currentSessionId = context.session?.id ?? '';\n if (currentSessionId === registeredSessionId) return;\n if (registeredSessionId) {\n releaseActiveSessionBoard(attachedProjectRoot, registeredSessionId);\n fireAndForget(\n 'cleanup-board',\n cleanupSessionKanbanBoardIfEmpty(attachedProjectRoot, registeredSessionId),\n );\n }\n registeredSessionId = currentSessionId;\n if (registeredSessionId) {\n retainActiveSessionBoard(attachedProjectRoot, registeredSessionId);\n }\n };\n syncActiveSessionRegistration();\n\n let watcher: FSWatcher | null = null;\n let watchedDir = '';\n let timer: NodeJS.Timeout | null = null;\n let unsubscribeBoardEvents: (() => void) | null = null;\n let watchedBoardId = '';\n let boardTimer: NodeJS.Timeout | null = null;\n let presenceTimer: NodeJS.Timeout | null = null;\n\n const sessionId = () => context.session?.id ?? '';\n const refreshFiles = async () => {\n const id = sessionId();\n if (!id) return;\n const planPath = context.meta['plan.path'];\n if (typeof planPath === 'string' && planPath) {\n const plan = await loadPlan(planPath);\n if (plan) await projectSessionPlanToKanban(context.projectRoot, plan.items, id);\n }\n const taskPath = context.meta['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n const tasks = await loadTasks(taskPath);\n if (tasks) await projectSessionTasksToKanban(context.projectRoot, tasks.tasks, id);\n }\n };\n\n const refreshBoard = async () => {\n if (!watchedBoardId) return;\n const board = await getBoard(context.projectRoot, watchedBoardId);\n if (board) applySessionKanbanBoardToTodos(context, board);\n };\n\n const configureBoardWatcher = async () => {\n const id = sessionId();\n const board = id ? await ensureSessionKanbanBoard(context.projectRoot, id) : null;\n if (!board || board.id === watchedBoardId) return;\n unsubscribeBoardEvents?.();\n unsubscribeBoardEvents = null;\n watchedBoardId = board.id;\n try {\n unsubscribeBoardEvents = bridgeKanbanSupervisor(\n context.projectRoot,\n (event) => {\n const data = event.data as { boardId?: string } | undefined;\n if (data?.boardId !== board.id) return;\n if (boardTimer) clearTimeout(boardTimer);\n boardTimer = setTimeout(() => fireAndForget('refresh-board', refreshBoard()), 60);\n },\n { autoReconnect: true, reconnectDelayMs: 1_000 },\n );\n const touchPresence = () =>\n touchKanbanPresence(context.projectRoot, board.id, {\n sessionId: id,\n agentId: context.agentId,\n agentName: context.agentName,\n });\n fireAndForget('touch-presence', touchPresence());\n if (presenceTimer) clearInterval(presenceTimer);\n presenceTimer = setInterval(() => fireAndForget('touch-presence', touchPresence()), 60_000);\n presenceTimer.unref?.();\n } catch {\n unsubscribeBoardEvents = null;\n watchedBoardId = '';\n }\n };\n\n const configureWatcher = () => {\n const planPath = context.meta['plan.path'];\n const taskPath = context.meta['task.path'];\n const candidate =\n typeof planPath === 'string' && planPath\n ? dirname(planPath)\n : typeof taskPath === 'string' && taskPath\n ? dirname(taskPath)\n : '';\n if (!candidate || candidate === watchedDir) return;\n watcher?.close();\n watcher = null;\n watchedDir = candidate;\n try {\n watcher = watch(candidate, { persistent: false }, (_event, filename) => {\n const name = filename?.toString();\n const currentPlanPath = context.meta['plan.path'];\n const currentTaskPath = context.meta['task.path'];\n const planName = typeof currentPlanPath === 'string' ? basename(currentPlanPath) : '';\n const taskName = typeof currentTaskPath === 'string' ? basename(currentTaskPath) : '';\n if (name && name !== planName && name !== taskName) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => fireAndForget('refresh-files', refreshFiles()), 60);\n });\n watcher.on('error', () => watcher?.close());\n } catch {\n // The session directory can briefly disappear during project/session\n // switches; the next meta update re-attempts the binding.\n watcher = null;\n watchedDir = '';\n }\n };\n\n const unsubscribe = context.state.onChange((change) => {\n if (change.kind === 'todos_replaced' && !suppressedTodoMirrors.has(context)) {\n // ConversationState auto-clears an all-done tactical list. Project the\n // pre-clear completion snapshot so every card reaches Done atomically.\n mirrorSessionTodosToKanban(\n context.projectRoot,\n change.completedSnapshot ?? change.todos,\n sessionId(),\n );\n return;\n }\n if (change.kind === 'meta_set' && (change.key === 'plan.path' || change.key === 'task.path')) {\n syncActiveSessionRegistration();\n configureWatcher();\n fireAndForget('ensure-board', ensureSessionKanbanBoard(context.projectRoot, sessionId()));\n fireAndForget('configure-watcher', configureBoardWatcher());\n fireAndForget('refresh-files', refreshFiles());\n }\n });\n\n configureWatcher();\n fireAndForget('configure-watcher', configureBoardWatcher());\n\n const detach = () => {\n unsubscribe();\n if (timer) clearTimeout(timer);\n if (boardTimer) clearTimeout(boardTimer);\n if (presenceTimer) clearInterval(presenceTimer);\n watcher?.close();\n unsubscribeBoardEvents?.();\n bindings.delete(context);\n if (attachedProjectRoot && registeredSessionId) {\n releaseActiveSessionBoard(attachedProjectRoot, registeredSessionId);\n fireAndForget(\n 'cleanup-board',\n cleanupSessionKanbanBoardIfEmpty(attachedProjectRoot, registeredSessionId),\n );\n registeredSessionId = '';\n }\n };\n bindings.set(context, detach);\n return detach;\n}\n\n/** Fully hydrate a session board before a host announces the session as ready. */\nexport async function hydrateSessionKanban(context: Context): Promise<KanbanBoard | null> {\n const id = context.session?.id ?? '';\n if (!id) return null;\n await cleanupEmptySessionKanbanBoards(context.projectRoot, id);\n let board = await ensureSessionKanbanBoard(context.projectRoot, id);\n if (context.todos.length) {\n board = await projectSessionTodosToKanban(context.projectRoot, context.todos, id);\n }\n const planPath = context.meta['plan.path'];\n if (typeof planPath === 'string' && planPath) {\n const plan = await loadPlan(planPath);\n if (plan) board = await projectSessionPlanToKanban(context.projectRoot, plan.items, id);\n }\n const taskPath = context.meta['task.path'];\n if (typeof taskPath === 'string' && taskPath) {\n const tasks = await loadTasks(taskPath);\n if (tasks) board = await projectSessionTasksToKanban(context.projectRoot, tasks.tasks, id);\n }\n return board;\n}\n\nexport interface SessionKanbanSourceUpdate {\n source: 'todo' | 'task' | 'plan' | null;\n todos?: TodoItem[] | undefined;\n tasks?: TaskFile | undefined;\n plan?: PlanFile | undefined;\n}\n\nfunction sourceStatus(task: KanbanTask): TaskStatus {\n if (task.status === 'completed') return 'completed';\n if (task.status === 'in_progress') return 'in_progress';\n if (task.status === 'review') return 'review';\n if (task.status === 'blocked') return 'blocked';\n if (task.status === 'failed') return 'failed';\n return 'pending';\n}\n\nfunction todoStatus(task: KanbanTask): TodoItem['status'] {\n const status = sourceStatus(task);\n if (status === 'completed') return 'completed';\n if (status === 'in_progress' || status === 'review') return 'in_progress';\n return 'pending';\n}\n\nfunction sessionTodoFromTask(task: KanbanTask): TodoItem {\n return {\n id: task.origin?.taskId ?? task.id,\n content: task.title,\n status: todoStatus(task),\n ...(task.description ? { activeForm: task.description } : {}),\n };\n}\n\nfunction sameTodos(left: readonly TodoItem[], right: readonly TodoItem[]): boolean {\n return (\n left.length === right.length &&\n left.every((todo, index) => {\n const candidate = right[index];\n return (\n candidate?.id === todo.id &&\n candidate.content === todo.content &&\n candidate.status === todo.status &&\n candidate.activeForm === todo.activeForm &&\n candidate.promotedFromPlan === todo.promotedFromPlan &&\n candidate.promotedFromTask === todo.promotedFromTask\n );\n })\n );\n}\n\n/**\n * Replace the tactical todo list from the current cards on a session-owned board.\n *\n * Existing mirrored todo cards retain their stable source ids. New cards created\n * by a Kanban worker become todos under their card ids, so reassessment can add,\n * split, merge, reprioritize, or remove work without being overwritten by the\n * todo list that happened to exist when the run started.\n */\nexport function applySessionKanbanBoardToTodos(context: Context, board: KanbanBoard): TodoItem[] {\n const sessionId = context.session?.id ?? '';\n if (\n !sessionId ||\n sessionIdFromTags(board.tags) !== sessionId ||\n !isOwnedSessionBoard(board.tags)\n ) {\n return [...context.todos];\n }\n\n const projectedTodos = board.tasks\n .filter(\n (task) =>\n task.status !== 'archived' &&\n (!task.origin ||\n task.origin.system === 'session-todo' ||\n (task.origin.graphId ?? '').startsWith('todo:')),\n )\n .sort((left, right) => {\n const leftColumn = board.columns.find((column) => column.id === left.columnId)?.order ?? 0;\n const rightColumn = board.columns.find((column) => column.id === right.columnId)?.order ?? 0;\n return (\n leftColumn - rightColumn ||\n left.order - right.order ||\n left.createdAt.localeCompare(right.createdAt)\n );\n })\n .map(sessionTodoFromTask);\n\n const allCompleted =\n projectedTodos.length > 0 && projectedTodos.every((todo) => todo.status === 'completed');\n const effectiveTodos = allCompleted ? [] : projectedTodos;\n if (sameTodos(context.todos, effectiveTodos)) return [...context.todos];\n suppressedTodoMirrors.add(context);\n try {\n context.state.replaceTodos(projectedTodos);\n } finally {\n suppressedTodoMirrors.delete(context);\n }\n notifyTodoUpdate(context, context.todos);\n broadcastTodoUpdate(context, context.todos);\n return [...context.todos];\n}\n\n/** Reflect a TUI/WebUI Kanban card edit back to its originating work list. */\nexport async function applySessionKanbanTaskToSource(\n context: Context,\n task: KanbanTask,\n options: { remove?: boolean | undefined } = {},\n): Promise<SessionKanbanSourceUpdate> {\n const originId = task.origin?.taskId;\n const graphId = task.origin?.graphId ?? '';\n if (!originId) return { source: null };\n\n if (task.origin?.system === 'session-todo' || graphId.startsWith('todo:')) {\n const next = options.remove\n ? context.todos.filter((todo) => todo.id !== originId)\n : context.todos.map((todo) =>\n todo.id === originId\n ? {\n ...todo,\n content: task.title,\n status:\n sourceStatus(task) === 'completed'\n ? ('completed' as const)\n : sourceStatus(task) === 'in_progress' || sourceStatus(task) === 'review'\n ? ('in_progress' as const)\n : ('pending' as const),\n }\n : todo,\n );\n suppressedTodoMirrors.add(context);\n try {\n context.state.replaceTodos(next);\n } finally {\n suppressedTodoMirrors.delete(context);\n }\n return { source: 'todo', todos: [...context.todos] };\n }\n\n const id = context.session?.id ?? '';\n if (task.origin?.system === 'session-plan' || graphId.startsWith('plan:')) {\n const planPath = context.meta['plan.path'];\n if (typeof planPath !== 'string' || !planPath) return { source: 'plan' };\n const plan = await mutatePlan(planPath, id, (file) => ({\n ...file,\n updatedAt: new Date().toISOString(),\n items: options.remove\n ? file.items.filter((item) => item.id !== originId)\n : file.items.map((item) =>\n item.id === originId\n ? {\n ...item,\n title: task.title,\n details: task.description,\n status:\n task.status === 'completed'\n ? ('done' as const)\n : task.status === 'in_progress' || task.status === 'review'\n ? ('in_progress' as const)\n : ('open' as const),\n updatedAt: new Date().toISOString(),\n }\n : item,\n ),\n }));\n return { source: 'plan', plan };\n }\n\n if (\n task.origin?.system === 'session-task' ||\n task.origin?.system === 'session' ||\n graphId.startsWith('session:')\n ) {\n const taskPath = context.meta['task.path'];\n if (typeof taskPath !== 'string' || !taskPath) return { source: 'task' };\n const tasks = await mutateTasks(taskPath, id, (file) => ({\n ...file,\n tasks: options.remove\n ? file.tasks.filter((item) => item.id !== originId)\n : file.tasks.map((item) =>\n item.id === originId\n ? {\n ...item,\n title: task.title,\n description: task.description,\n status: sourceStatus(task),\n updatedAt: new Date().toISOString(),\n }\n : item,\n ),\n }));\n return { source: 'task', tasks };\n }\n\n return { source: null };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["loadPlan", "loadTasks", "board", "loadPlan", "loadTasks"]
|
|
7
|
-
}
|
package/dist/tool-diff.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-diff.d.ts","sourceRoot":"","sources":["../src/tool-diff.ts"],"names":[],"mappings":"AAiBA,mCAAmC;AACnC,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AACzD,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oFAAoF;AACpF,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,qEAAqE;AACrE,eAAO,MAAM,cAAc,OAAO,CAAC;AAmBnC;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAiC/F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAgClF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CA2B7D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,OAAO,GACb;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAKpD;AAgBD,gFAAgF;AAChF,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AACnE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAED,uEAAuE;AACvE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAA6B,GACrC,WAAW,CAsDb;AAYD,eAAO,MAAM,qBAAqB,EAAE,MAyDxB,CAAC"}
|
package/dist/tool-diff.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tool-diff.ts"],
|
|
4
|
-
"sourcesContent": ["// Tool-output diff model \u2014 the single source of truth for turning an\n// edit/write/patch tool call into a red/green diff, shared by the WebUI\n// (DiffView) and the HQ dashboard chat-history sidebar.\n//\n// PURE + browser-safe (no node imports). Two consumption modes, mirroring the\n// pattern in ./tool-summary:\n// 1. WebUI/TS: import { diffFromToolInput, computeLineDiff } directly.\n// 2. HQ: it is a served template-literal string that cannot `import`, so it\n// embeds the *_BROWSER_SRC transcriptions and a parity test guarantees the\n// two never drift.\n//\n// Three tool families produce three diff shapes:\n// - edit / str_replace -> { mode:'lcs', oldText, newText } (LCS line diff)\n// - write / create -> { mode:'lcs', oldText:'', newText } (all additions)\n// - patch -> { mode:'unified', patchText } (already a unified\n// diff \u2014 parse its hunks directly, do NOT recompute)\n\n/** A single rendered diff line. */\nexport type DiffRowKind = 'add' | 'del' | 'ctx' | 'meta';\nexport interface DiffRow {\n kind: DiffRowKind;\n text: string;\n}\n\n/** Extraction result: enough for a caller to render without re-inspecting input. */\nexport type ToolDiff =\n | { mode: 'lcs'; oldText: string; newText: string; caption: string }\n | { mode: 'unified'; patchText: string; caption: string };\n\n/** Max lines per side before we bail out of the O(n*m) LCS table. */\nexport const DIFF_MAX_LINES = 5000;\n\nfunction asObject(input: unknown): Record<string, unknown> | null {\n if (typeof input === 'string') {\n const s = input.trim();\n if (s.startsWith('{') || s.startsWith('[')) {\n try {\n const parsed = JSON.parse(s);\n if (parsed && typeof parsed === 'object') return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n return null;\n }\n if (input && typeof input === 'object') return input as Record<string, unknown>;\n return null;\n}\n\n/**\n * Recognise the edit-family tools and pull a renderable diff descriptor out of\n * their input. Returns null when the tool doesn't carry diffable input.\n *\n * @param toolName canonical or aliased tool name.\n * @param input tool input \u2014 a parsed object OR a JSON string.\n */\nexport function diffFromToolInput(toolName: string | undefined, input: unknown): ToolDiff | null {\n if (!toolName) return null;\n const obj = asObject(input);\n if (!obj) return null;\n const name = toolName.toLowerCase();\n const filePath = String(obj.file_path ?? obj.path ?? '');\n\n switch (name) {\n case 'edit':\n case 'str_replace':\n case 'edit_file':\n case 'multi_edit': {\n const oldText = typeof obj.old_string === 'string' ? obj.old_string : '';\n const newText = typeof obj.new_string === 'string' ? obj.new_string : '';\n if (!oldText && !newText) return null;\n return { mode: 'lcs', oldText, newText, caption: `edit ${filePath}`.trim() };\n }\n case 'write':\n case 'write_file':\n case 'create_file': {\n const content = typeof obj.content === 'string' ? obj.content : '';\n if (!content) return null;\n // Fresh write: no \"old\" side, everything shows green.\n return { mode: 'lcs', oldText: '', newText: content, caption: `write ${filePath} (new)`.trim() };\n }\n case 'patch': {\n const patchText = typeof obj.patch === 'string' ? obj.patch : '';\n if (!patchText.trim()) return null;\n return { mode: 'unified', patchText, caption: filePath ? `patch ${filePath}` : 'patch' };\n }\n default:\n return null;\n }\n}\n\n/**\n * LCS-based line diff. Returns null when either side exceeds DIFF_MAX_LINES\n * (an O(n*m) memory guard \u2014 fine for a normal edit, prohibitive for a huge\n * generated-file rewrite).\n */\nexport function computeLineDiff(oldText: string, newText: string): DiffRow[] | null {\n const a = oldText.split('\\n');\n const b = newText.split('\\n');\n if (a.length > DIFF_MAX_LINES || b.length > DIFF_MAX_LINES) return null;\n const n = a.length;\n const m = b.length;\n // dp[i][j] = LCS length of a[i..] and b[j..]\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array<number>(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n for (let j = m - 1; j >= 0; j--) {\n dp[i]![j] = a[i] === b[j] ? dp[i + 1]![j + 1]! + 1 : Math.max(dp[i + 1]![j]!, dp[i]![j + 1]!);\n }\n }\n const rows: DiffRow[] = [];\n let i = 0;\n let j = 0;\n while (i < n && j < m) {\n if (a[i] === b[j]) {\n rows.push({ kind: 'ctx', text: a[i]! });\n i++;\n j++;\n } else if (dp[i + 1]![j]! >= dp[i]![j + 1]!) {\n rows.push({ kind: 'del', text: a[i]! });\n i++;\n } else {\n rows.push({ kind: 'add', text: b[j]! });\n j++;\n }\n }\n while (i < n) rows.push({ kind: 'del', text: a[i++]! });\n while (j < m) rows.push({ kind: 'add', text: b[j++]! });\n return rows;\n}\n\n/**\n * Parse a unified-diff string into renderable rows. Hunk headers (`@@ ... @@`)\n * and file headers (`--- `, `+++ `, `diff `, `index `) become `meta` rows; body\n * lines map to add/del/ctx by their leading char. `\`\n * is kept as a meta row.\n */\nexport function parseUnifiedDiff(patchText: string): DiffRow[] {\n const rows: DiffRow[] = [];\n const lines = patchText.split('\\n');\n for (const raw of lines) {\n if (\n raw.startsWith('@@') ||\n raw.startsWith('--- ') ||\n raw.startsWith('+++ ') ||\n raw.startsWith('diff ') ||\n raw.startsWith('index ') ||\n raw.startsWith('\\\\ ')\n ) {\n rows.push({ kind: 'meta', text: raw });\n } else if (raw.startsWith('+')) {\n rows.push({ kind: 'add', text: raw.slice(1) });\n } else if (raw.startsWith('-')) {\n rows.push({ kind: 'del', text: raw.slice(1) });\n } else {\n // context line (leading space) or a bare line\n rows.push({ kind: 'ctx', text: raw.startsWith(' ') ? raw.slice(1) : raw });\n }\n }\n // Drop a trailing empty row that a final newline in patchText introduces.\n if (rows.length > 0 && rows[rows.length - 1]!.kind === 'ctx' && rows[rows.length - 1]!.text === '') {\n rows.pop();\n }\n return rows;\n}\n\n/**\n * Convenience: extract + render to rows in one call. Returns null when the tool\n * carries no diffable input, or `{ caption, rows: null }` when the diff is too\n * large to render (LCS guard).\n */\nexport function diffRowsFromToolInput(\n toolName: string | undefined,\n input: unknown,\n): { caption: string; rows: DiffRow[] | null } | null {\n const d = diffFromToolInput(toolName, input);\n if (!d) return null;\n if (d.mode === 'unified') return { caption: d.caption, rows: parseUnifiedDiff(d.patchText) };\n return { caption: d.caption, rows: computeLineDiff(d.oldText, d.newText) };\n}\n\n// ===========================================================================\n// Rich preview model (line-number gutters + maxLines cap + hidden counts).\n//\n// Promoted verbatim from the TUI's code-block.tsx so the terminal can consume\n// this instead of a third local copy. It is a SUPERSET of the flat DiffRow\n// model above: `DiffLineRow` carries per-row old/new line numbers and a `hunk`\n// kind, and `parseUnifiedDiffPreview` returns a `DiffPreview` with add/remove\n// totals plus a maxLines cap that folds the overflow into hidden counts.\n//\n// This is pure and browser-safe; the TUI-only rendering (ink/theme/wrap) stays\n// in code-block.tsx. Kept as separate names from the flat `parseUnifiedDiff`\n// above so WebUI/HQ callers are unaffected.\n// ===========================================================================\n\n/** Rich diff row: like DiffRow but with a dedicated `hunk` kind and gutters. */\nexport type DiffLineKind = 'add' | 'del' | 'hunk' | 'ctx' | 'meta';\nexport interface DiffLineRow {\n kind: DiffLineKind;\n text: string;\n oldLine?: number | undefined;\n newLine?: number | undefined;\n}\n\n/** A parsed unified-diff preview: rows plus visible/hidden tallies. */\nexport interface DiffPreview {\n rows: DiffLineRow[];\n hidden: number;\n added: number;\n removed: number;\n hiddenAdded: number;\n hiddenRemoved: number;\n}\n\n/**\n * Safety cap on a single diff row's stored text \u2014 guards against pathological\n * one-line files (minified bundles, huge JSON) flooding a consumer. Real code\n * lines are never truncated at this length.\n */\nexport const DIFF_LINE_SAFETY_CAP = 4000;\n\n/** Truncate the middle-out: keep the head, append an ellipsis, past `max`. */\nexport function truncMid(s: string, max: number): string {\n if (s.length <= max) return s;\n return `${s.slice(0, max - 1)}\u2026`;\n}\n\n/**\n * Parse a unified-diff string into a {@link DiffPreview} with per-row line\n * numbers and add/remove tallies. `maxLines` caps the visible rows; the rest\n * fold into `hidden`/`hiddenAdded`/`hiddenRemoved`. Pass\n * `Number.POSITIVE_INFINITY` to render everything.\n *\n * This is the richer sibling of {@link parseUnifiedDiff}: it keeps a dedicated\n * `hunk` kind (rather than folding hunk headers into `meta`) and tracks old/new\n * line gutters, which a terminal/gutter renderer needs.\n */\nexport function parseUnifiedDiffPreview(\n diff: string,\n maxLines: number,\n lineCap: number = DIFF_LINE_SAFETY_CAP,\n): DiffPreview {\n const all: DiffLineRow[] = [];\n let oldLn = 0;\n let newLn = 0;\n for (const raw of diff.split('\\n')) {\n const line = raw.replace(/\\r$/, '');\n if (line.startsWith('+++') || line.startsWith('---')) continue;\n if (line.startsWith('diff --git') || line.startsWith('index ')) continue;\n if (line.startsWith('@@')) {\n const m = line.match(/^@@\\s+-(\\d+)(?:,\\d+)?\\s+\\+(\\d+)(?:,\\d+)?\\s+@@/);\n if (m) {\n oldLn = Number.parseInt(m[1] ?? '0', 10) || 0;\n newLn = Number.parseInt(m[2] ?? '0', 10) || 0;\n }\n all.push({ kind: 'hunk', text: truncMid(line, 60) });\n continue;\n }\n if (line.startsWith('+')) {\n all.push({ kind: 'add', text: truncMid(line, lineCap), newLine: newLn });\n newLn++;\n continue;\n }\n if (line.startsWith('-')) {\n all.push({ kind: 'del', text: truncMid(line, lineCap), oldLine: oldLn });\n oldLn++;\n continue;\n }\n if (line.startsWith('\\\\ No newline')) {\n all.push({ kind: 'meta', text: line });\n continue;\n }\n if (line.length === 0) continue;\n all.push({ kind: 'ctx', text: truncMid(line, lineCap), oldLine: oldLn, newLine: newLn });\n oldLn++;\n newLn++;\n }\n const added = all.filter((row) => row.kind === 'add').length;\n const removed = all.filter((row) => row.kind === 'del').length;\n if (all.length === 0) {\n return { rows: [], hidden: 0, added: 0, removed: 0, hiddenAdded: 0, hiddenRemoved: 0 };\n }\n if (all.length <= maxLines) {\n return { rows: all, hidden: 0, added, removed, hiddenAdded: 0, hiddenRemoved: 0 };\n }\n const rows = all.slice(0, maxLines);\n const hiddenRows = all.slice(maxLines);\n return {\n rows,\n hidden: hiddenRows.length,\n added,\n removed,\n hiddenAdded: hiddenRows.filter((row) => row.kind === 'add').length,\n hiddenRemoved: hiddenRows.filter((row) => row.kind === 'del').length,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Browser-JS transcriptions for HQ (served template literal, cannot import).\n// Authored with string concatenation only: NO backticks, NO ${...} \u2014 the HQ\n// template escapes those. A parity test asserts identical output to the TS\n// functions above. Together these define, on the global scope:\n// diffFromToolInput(name, rawInput) -> {mode,...}|null\n// computeLineDiff(oldText, newText) -> rows|null\n// parseUnifiedDiff(patchText) -> rows\n// diffRowsFromToolInput(name, raw) -> {caption, rows}|null\n// ---------------------------------------------------------------------------\nexport const TOOL_DIFF_BROWSER_SRC: string = [\n 'var DIFF_MAX_LINES = 5000;',\n 'function __diffObj(input){',\n ' if(typeof input===\"string\"){ var s=input.trim(); if(s.charAt(0)===\"{\"||s.charAt(0)===\"[\"){ try{ var p=JSON.parse(s); if(p&&typeof p===\"object\") return p; }catch(_e){} } return null; }',\n ' if(input&&typeof input===\"object\") return input; return null;',\n '}',\n 'function diffFromToolInput(name, input){',\n ' if(!name) return null;',\n ' var obj=__diffObj(input); if(!obj) return null;',\n ' var n=String(name).toLowerCase();',\n ' var fp=String(obj.file_path!=null?obj.file_path:(obj.path!=null?obj.path:\"\"));',\n ' if(n===\"edit\"||n===\"str_replace\"||n===\"edit_file\"||n===\"multi_edit\"){',\n ' var oldT=typeof obj.old_string===\"string\"?obj.old_string:\"\";',\n ' var newT=typeof obj.new_string===\"string\"?obj.new_string:\"\";',\n ' if(!oldT&&!newT) return null;',\n ' return { mode:\"lcs\", oldText:oldT, newText:newT, caption:(\"edit \"+fp).replace(/\\\\s+$/,\"\") };',\n ' }',\n ' if(n===\"write\"||n===\"write_file\"||n===\"create_file\"){',\n ' var c=typeof obj.content===\"string\"?obj.content:\"\";',\n ' if(!c) return null;',\n ' return { mode:\"lcs\", oldText:\"\", newText:c, caption:(\"write \"+fp+\" (new)\").replace(/\\\\s+/g,\" \").replace(/\\\\s+$/,\"\") };',\n ' }',\n ' if(n===\"patch\"){',\n ' var pt=typeof obj.patch===\"string\"?obj.patch:\"\";',\n ' if(!pt.replace(/\\\\s/g,\"\")) return null;',\n ' return { mode:\"unified\", patchText:pt, caption: fp ? (\"patch \"+fp) : \"patch\" };',\n ' }',\n ' return null;',\n '}',\n 'function computeLineDiff(oldText, newText){',\n ' var a=oldText.split(\"\\\\n\"), b=newText.split(\"\\\\n\");',\n ' if(a.length>DIFF_MAX_LINES||b.length>DIFF_MAX_LINES) return null;',\n ' var nn=a.length, mm=b.length, i, j;',\n ' var dp=new Array(nn+1); for(i=0;i<=nn;i++){ dp[i]=new Array(mm+1); for(j=0;j<=mm;j++) dp[i][j]=0; }',\n ' for(i=nn-1;i>=0;i--){ for(j=mm-1;j>=0;j--){ dp[i][j]= a[i]===b[j] ? dp[i+1][j+1]+1 : Math.max(dp[i+1][j], dp[i][j+1]); } }',\n ' var rows=[]; i=0; j=0;',\n ' while(i<nn&&j<mm){ if(a[i]===b[j]){ rows.push({kind:\"ctx\",text:a[i]}); i++; j++; } else if(dp[i+1][j]>=dp[i][j+1]){ rows.push({kind:\"del\",text:a[i]}); i++; } else { rows.push({kind:\"add\",text:b[j]}); j++; } }',\n ' while(i<nn){ rows.push({kind:\"del\",text:a[i]}); i++; }',\n ' while(j<mm){ rows.push({kind:\"add\",text:b[j]}); j++; }',\n ' return rows;',\n '}',\n 'function parseUnifiedDiff(patchText){',\n ' var rows=[], lines=patchText.split(\"\\\\n\"), k, raw;',\n ' for(k=0;k<lines.length;k++){ raw=lines[k];',\n ' if(raw.indexOf(\"@@\")===0||raw.indexOf(\"--- \")===0||raw.indexOf(\"+++ \")===0||raw.indexOf(\"diff \")===0||raw.indexOf(\"index \")===0||raw.indexOf(\"\\\\\\\\ \")===0){ rows.push({kind:\"meta\",text:raw}); }',\n ' else if(raw.charAt(0)===\"+\"){ rows.push({kind:\"add\",text:raw.slice(1)}); }',\n ' else if(raw.charAt(0)===\"-\"){ rows.push({kind:\"del\",text:raw.slice(1)}); }',\n ' else { rows.push({kind:\"ctx\",text: raw.charAt(0)===\" \" ? raw.slice(1) : raw}); }',\n ' }',\n ' if(rows.length>0 && rows[rows.length-1].kind===\"ctx\" && rows[rows.length-1].text===\"\"){ rows.pop(); }',\n ' return rows;',\n '}',\n 'function diffRowsFromToolInput(name, input){',\n ' var d=diffFromToolInput(name, input); if(!d) return null;',\n ' if(d.mode===\"unified\") return { caption:d.caption, rows:parseUnifiedDiff(d.patchText) };',\n ' return { caption:d.caption, rows:computeLineDiff(d.oldText, d.newText) };',\n '}',\n].join('\\n');\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["i", "j"]
|
|
7
|
-
}
|
package/dist/tool-help.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-help.d.ts","sourceRoot":"","sources":["../src/tool-help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;IACnD,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC;AAED,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,OAAO,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,OAAO,CAAC;KACnB,EAAE,CAAC;IACJ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAoF5D,CAAC"}
|
package/dist/tool-help.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tool-help.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Tool } from '@wrongstack/core/types';\n\ninterface ToolHelpInput {\n tool?: string | undefined;\n format?: 'short' | 'full' | 'markdown' | undefined;\n include_examples?: boolean | undefined;\n}\n\ninterface ToolHelpOutput {\n tool?: string | undefined;\n help: string;\n tools: {\n name: string;\n description: string;\n usageHint: string;\n inputSchema: unknown;\n permission: string;\n mutating: boolean;\n }[];\n total: number;\n}\n\nexport const toolHelpTool: Tool<ToolHelpInput, ToolHelpOutput> = {\n name: 'tool_help',\n category: 'Meta',\n description:\n 'Get detailed help for a specific tool, including its full input schema and usage guidance. ' +\n 'If you do not know which tool to use, search with `tool_search` first, then call this with the tool name.',\n usageHint:\n 'USE WHEN YOU NEED PRECISE TOOL INFORMATION:\\n\\n' +\n '- Call with a specific `tool` name when you want the full schema and current usageHint.\\n' +\n '- Omit `tool` to get an overview of all available tools.\\n' +\n '- Different `format` options give you different levels of detail.\\n' +\n '- Tip: use `tool_search` to find the right tool name, then `tool_help` for the full schema.\\n' +\n 'This tool is extremely valuable for self-correction when you are unsure about a tool\\'s interface.',\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n capabilities: ['tool.meta'],\n icon: 'meta',\n inputSchema: {\n type: 'object',\n properties: {\n tool: {\n type: 'string',\n description: 'Specific tool name to get detailed help for. Omit to get a list of all tools.',\n },\n format: {\n type: 'string',\n enum: ['short', 'full', 'markdown'],\n description: 'Level of detail: \"short\" (summary), \"full\" (with full schema), \"markdown\" (human readable).',\n },\n include_examples: {\n type: 'boolean',\n description: 'Whether to include example usage in the response.',\n },\n },\n },\n async execute(input, ctx) {\n const format = input.format ?? 'short';\n const includeExamples = input.include_examples ?? false;\n\n if (input.tool) {\n const tool = ctx.tools.find((t: Tool) => t.name === input.tool);\n if (!tool) {\n return {\n tool: input.tool,\n help: `No tool found with name \"${input.tool}\"`,\n tools: [],\n total: 0,\n };\n }\n\n return {\n tool: tool.name,\n help: formatToolHelp(tool, format, includeExamples),\n tools: [\n {\n name: tool.name,\n description: tool.description,\n usageHint: tool.usageHint ?? '',\n inputSchema: tool.inputSchema,\n permission: tool.permission,\n mutating: tool.mutating,\n },\n ],\n total: 1,\n };\n }\n\n const allTools = ctx.tools.map((t: Tool) => ({\n name: t.name,\n description: t.description,\n usageHint: t.usageHint ?? '',\n inputSchema: format === 'full' ? t.inputSchema : undefined,\n permission: t.permission,\n mutating: t.mutating,\n }));\n\n return {\n help:\n format === 'markdown' ? formatAllToolsMarkdown(allTools) : formatAllToolsShort(allTools),\n tools: allTools,\n total: allTools.length,\n };\n },\n};\n\nfunction formatToolHelp(tool: Tool, format: string, includeExamples: boolean): string {\n const lines: string[] = [];\n\n if (format === 'short') {\n lines.push(`${tool.name}: ${tool.description}`);\n if (tool.usageHint) lines.push(`Hint: ${tool.usageHint}`);\n return lines.join('\\n');\n }\n\n if (format === 'markdown') {\n lines.push(`## ${tool.name}`);\n lines.push('');\n lines.push(tool.description);\n lines.push('');\n lines.push('**Permission:** ' + tool.permission);\n lines.push('**Mutating:** ' + (tool.mutating ? 'yes' : 'no'));\n if (tool.usageHint) {\n lines.push('');\n lines.push('### Usage Hint');\n lines.push(tool.usageHint);\n }\n if (includeExamples && tool.inputSchema) {\n lines.push('');\n lines.push('### Input Schema');\n lines.push('```json');\n lines.push(JSON.stringify(tool.inputSchema, null, 2));\n lines.push('```');\n }\n return lines.join('\\n');\n }\n\n lines.push(`Tool: ${tool.name}`);\n lines.push(`Description: ${tool.description}`);\n lines.push(`Permission: ${tool.permission}`);\n lines.push(`Mutating: ${tool.mutating}`);\n if (tool.usageHint) lines.push(`Usage: ${tool.usageHint}`);\n if (format === 'full' && tool.inputSchema) {\n lines.push('Schema: ' + JSON.stringify(tool.inputSchema, null, 2));\n }\n return lines.join('\\n');\n}\n\nfunction formatAllToolsShort(tools: { name: string; description: string }[]): string {\n return tools.map((t) => ` ${t.name.padEnd(16)} ${t.description}`).join('\\n');\n}\n\nfunction formatAllToolsMarkdown(\n tools: { name: string; description: string; usageHint: string }[],\n): string {\n const lines: string[] = ['## Available Tools', ''];\n lines.push('| Tool | Description |');\n lines.push('|------|-------------|');\n for (const t of tools) {\n lines.push(`| \\`${t.name}\\` | ${t.description} |`);\n }\n return lines.join('\\n');\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-icon-map.d.ts","sourceRoot":"","sources":["../src/tool-icon-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,KAAK,GACL,KAAK,GACL,MAAM,GACN,MAAM,GACN,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,GACP,UAAU,CAAC;AAEf;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAsIpD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAExD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,cAAc,CAyB/D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAAuB,CAAC"}
|
package/dist/tool-icons.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-icons.d.ts","sourceRoot":"","sources":["../src/tool-icons.ts"],"names":[],"mappings":"AAYA,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,KAAK,GACL,KAAK,GACL,MAAM,GACN,MAAM,GACN,MAAM,GACN,SAAS,GACT,UAAU,GACV,UAAU,GACV,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,GACP,UAAU,CAAC;AAEf,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAyBlE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAsFpD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAGpD"}
|
package/dist/tool-icons.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tool-icons.ts"],
|
|
4
|
-
"sourcesContent": ["// Canonical, surface-agnostic per-tool icon identity + color.\n//\n// This is PURE DATA (no node imports) so it is safe to import from the browser\n// (WebUI) as well as the TUI. Each surface maps a `ToolIconId` to its own\n// rendering primitive \u2014 the WebUI to a lucide-react component (`tool-icon.ts`),\n// the TUI to a unicode glyph (`tool-glyph.ts`) \u2014 and pulls the shared color\n// from `TOOL_ICON_CONFIG` so both stay in lockstep.\n//\n// Add a new tool by giving it an entry in `TOOL_ICON_MAP` (name \u2192 id). The id\n// must be one of the `ToolIconId` union members below; every id already has a\n// color, a lucide component, and a glyph.\n\n/** The closed set of visual identities a tool can map to. */\nexport type ToolIconId =\n | 'file'\n | 'edit'\n | 'search'\n | 'folder'\n | 'terminal'\n | 'web'\n | 'git'\n | 'tree'\n | 'code'\n | 'test'\n | 'package'\n | 'document'\n | 'scaffold'\n | 'todo'\n | 'plan'\n | 'task'\n | 'meta'\n | 'index'\n | 'json'\n | 'diff'\n | 'logs'\n | 'settings'\n | 'brain'\n | 'fallback';\n\n/** Canonical hex color per icon id \u2014 the single source both surfaces read. */\nexport const TOOL_ICON_CONFIG: Record<ToolIconId, { color: string }> = {\n file: { color: '#60a5fa' }, // blue\n edit: { color: '#fbbf24' }, // amber\n search: { color: '#a78bfa' }, // violet\n folder: { color: '#38bdf8' }, // sky\n terminal: { color: '#67e8f9' }, // cyan-light\n web: { color: '#34d399' }, // emerald\n git: { color: '#a3e635' }, // lime\n tree: { color: '#22d3ee' }, // cyan\n code: { color: '#818cf8' }, // indigo\n test: { color: '#4ade80' }, // green\n package: { color: '#f472b6' }, // pink\n document: { color: '#94a3b8' }, // slate\n scaffold: { color: '#c084fc' }, // purple\n todo: { color: '#facc15' }, // yellow\n plan: { color: '#2dd4bf' }, // teal\n task: { color: '#5eead4' }, // teal-light\n meta: { color: '#cbd5e1' }, // slate-light\n index: { color: '#06b6d4' }, // cyan-dark\n json: { color: '#eab308' }, // yellow-dark\n diff: { color: '#93c5fd' }, // blue-light\n logs: { color: '#a3a3a3' }, // neutral\n settings: { color: '#9ca3af' }, // gray\n brain: { color: '#e879f9' }, // fuchsia\n fallback: { color: '#9ca3af' }, // gray\n};\n\n/**\n * Tool name (and common alias) \u2192 icon id. Keys are lowercase; `getToolIcon`\n * lowercases the query, so lookups are case-insensitive. Covers every builtin\n * tool plus the aliases models commonly emit.\n */\nexport const TOOL_ICON_MAP: Record<string, ToolIconId> = {\n // \u2500\u2500 file IO \u2500\u2500\n read: 'file',\n cat: 'file',\n view: 'file',\n write: 'file',\n create: 'file',\n edit: 'edit',\n replace: 'edit',\n str_replace: 'edit',\n multi_edit: 'edit',\n patch: 'diff',\n // \u2500\u2500 search \u2500\u2500\n grep: 'search',\n search: 'search',\n rg: 'search',\n ripgrep: 'search',\n glob: 'search',\n find: 'search',\n // \u2500\u2500 navigation \u2500\u2500\n folder: 'folder',\n ls: 'folder',\n list: 'folder',\n set_working_dir: 'folder',\n tree: 'tree',\n // \u2500\u2500 shell \u2500\u2500\n bash: 'terminal',\n shell: 'terminal',\n sh: 'terminal',\n exec: 'terminal',\n run: 'terminal',\n command: 'terminal',\n // \u2500\u2500 web \u2500\u2500\n fetch: 'web',\n web_fetch: 'web',\n web_search: 'web',\n // \u2500\u2500 vcs \u2500\u2500\n git: 'git',\n diff: 'diff',\n // \u2500\u2500 code quality \u2500\u2500\n lint: 'code',\n format: 'settings',\n typecheck: 'code',\n language_info: 'code',\n language: 'code',\n language_package: 'package',\n test: 'test',\n // \u2500\u2500 packages \u2500\u2500\n install: 'package',\n outdated: 'package',\n audit: 'package',\n // \u2500\u2500 docs / scaffold \u2500\u2500\n document: 'document',\n scaffold: 'scaffold',\n // \u2500\u2500 planning / work tracking \u2500\u2500\n todo: 'todo',\n plan: 'plan',\n task: 'task',\n // \u2500\u2500 data \u2500\u2500\n json: 'json',\n index: 'index',\n logs: 'logs',\n // \u2500\u2500 meta / tooling \u2500\u2500\n mode: 'meta',\n tool_search: 'meta',\n tool_use: 'meta',\n batch_tool_use: 'meta',\n tool_help: 'meta',\n // \u2500\u2500 memory \u2500\u2500\n remember: 'brain',\n forget: 'brain',\n search_memory: 'brain',\n find_related_memories: 'brain',\n memory_for_file: 'brain',\n memory_for_path: 'brain',\n memory_search: 'brain',\n memory_graph: 'brain',\n memory_verify: 'brain',\n memory_hygiene: 'brain',\n memory_candidates: 'brain',\n // \u2500\u2500 language intelligence \u2500\u2500\n lsp_diagnostics: 'code',\n lsp_definition: 'search',\n lsp_completion: 'code',\n lsp_rename: 'edit',\n 'codebase-lsp-search': 'index',\n};\n\n/**\n * Resolve a tool name to its canonical `ToolIconId`. Case-insensitive.\n * Unknown names \u2014 including `mcp__*` server tools and plugin tools \u2014 fall back\n * to `'fallback'`.\n */\nexport function getToolIcon(name: string): ToolIconId {\n if (!name) return 'fallback';\n return TOOL_ICON_MAP[name.toLowerCase()] ?? 'fallback';\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-search.d.ts","sourceRoot":"","sources":["../src/tool-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,UAAU,eAAe;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,UAAU,gBAAgB;IACxB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,OAAO,CAAC;KACnB,EAAE,CAAC;IACJ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAmGlE,CAAC"}
|
package/dist/tool-search.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tool-search.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Tool } from '@wrongstack/core/types';\n\ninterface ToolSearchInput {\n query?: string | undefined;\n tags?: string[] | undefined;\n permission?: 'auto' | 'confirm' | 'deny' | undefined;\n mutating?: boolean | undefined;\n limit?: number | undefined;\n}\n\ninterface ToolSearchOutput {\n tools: {\n name: string;\n description: string;\n permission: string;\n mutating: boolean;\n }[];\n total: number;\n truncated: boolean;\n /** Total count of tools in the registry (for \"no results\" hints). */\n _available?: number;\n}\n\nexport const toolSearchTool: Tool<ToolSearchInput, ToolSearchOutput> = {\n name: 'tool_search',\n category: 'Meta',\n description:\n 'Search the catalog of available tools by name or description. Use this to discover which tool to use for a task. ' +\n 'For the full schema and usage details of a specific tool, use `tool_help` instead.',\n usageHint:\n 'SELF-DISCOVERY TOOL:\\n\\n' +\n '- Use when you need to find the right tool for a job.\\n' +\n '- `query` searches names and descriptions.\\n' +\n '- You can filter by `tags` (category), `permission`, or `mutating`.\\n' +\n '- Once you find the right tool name, use `tool_help` with that name for full schema details.\\n' +\n 'Call this before guessing tool names. It helps you discover the best tool for the current situation.',\n permission: 'auto',\n mutating: false,\n timeoutMs: 1_000,\n capabilities: ['tool.meta'],\n icon: 'meta',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query for tool name or description',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'Filter by tags (e.g. \"filesystem\", \"network\", \"dev\")',\n },\n permission: {\n type: 'string',\n enum: ['auto', 'confirm', 'deny'],\n description: 'Filter by required permission level',\n },\n mutating: {\n type: 'boolean',\n description: 'Filter by mutating flag (true=filters that modify, false=read-only)',\n },\n limit: {\n type: 'integer',\n description: 'Maximum results to return (default: 20)',\n minimum: 1,\n maximum: 100,\n },\n },\n },\n async execute(input, ctx) {\n const limit = Math.min(input.limit ?? 20, 100);\n const tools = ctx.tools;\n const query = input.query?.toLowerCase() ?? '';\n\n const filtered = tools.filter((t: Tool) => {\n if (\n query &&\n !t.name.toLowerCase().includes(query) &&\n !t.description.toLowerCase().includes(query)\n ) {\n return false;\n }\n if (input.tags && input.tags.length > 0) {\n const toolCat = (t.category ?? '').toLowerCase();\n if (!input.tags.some((tag: string) => toolCat.includes(tag.toLowerCase()))) {\n return false;\n }\n }\n if (input.permission && t.permission !== input.permission) {\n return false;\n }\n if (typeof input.mutating === 'boolean' && t.mutating !== input.mutating) {\n return false;\n }\n return true;\n });\n\n const results = filtered.slice(0, limit).map((t: Tool) => ({\n name: t.name,\n description: t.description,\n permission: t.permission,\n mutating: t.mutating,\n }));\n\n // When no tools match, give the model actionable guidance so it\n // doesn't spiral through random queries. Point it at tool-help\n // which lists every available tool with descriptions.\n const totalAvailable = tools.length;\n const hint =\n results.length === 0 && query\n ? `No tools matched \"${input.query}\". Use tool-help (without arguments) to see all ${totalAvailable} available tools.`\n : undefined;\n\n return {\n tools: results,\n total: filtered.length,\n truncated: filtered.length > limit,\n ...(hint ? { hint } : {}),\n _available: totalAvailable,\n };\n },\n};"],
|
|
5
|
-
"mappings": ";AAuBO,IAAM,iBAA0D;AAAA,EACrE,MAAM;AAAA,EACN,UAAU;AAAA,EACV,aACE;AAAA,EAEF,WACE;AAAA,EAMF,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AAAA,EACX,cAAc,CAAC,WAAW;AAAA,EAC1B,MAAM;AAAA,EACN,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aAAa;AAAA,MACf;AAAA,MACA,YAAY;AAAA,QACV,MAAM;AAAA,QACN,MAAM,CAAC,QAAQ,WAAW,MAAM;AAAA,QAChC,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,QACb,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,OAAO,KAAK;AACxB,UAAM,QAAQ,KAAK,IAAI,MAAM,SAAS,IAAI,GAAG;AAC7C,UAAM,QAAQ,IAAI;AAClB,UAAM,QAAQ,MAAM,OAAO,YAAY,KAAK;AAE5C,UAAM,WAAW,MAAM,OAAO,CAAC,MAAY;AACzC,UACE,SACA,CAAC,EAAE,KAAK,YAAY,EAAE,SAAS,KAAK,KACpC,CAAC,EAAE,YAAY,YAAY,EAAE,SAAS,KAAK,GAC3C;AACA,eAAO;AAAA,MACT;AACA,UAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AACvC,cAAM,WAAW,EAAE,YAAY,IAAI,YAAY;AAC/C,YAAI,CAAC,MAAM,KAAK,KAAK,CAAC,QAAgB,QAAQ,SAAS,IAAI,YAAY,CAAC,CAAC,GAAG;AAC1E,iBAAO;AAAA,QACT;AAAA,MACF;AACA,UAAI,MAAM,cAAc,EAAE,eAAe,MAAM,YAAY;AACzD,eAAO;AAAA,MACT;AACA,UAAI,OAAO,MAAM,aAAa,aAAa,EAAE,aAAa,MAAM,UAAU;AACxE,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,CAAC;AAED,UAAM,UAAU,SAAS,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,OAAa;AAAA,MACzD,MAAM,EAAE;AAAA,MACR,aAAa,EAAE;AAAA,MACf,YAAY,EAAE;AAAA,MACd,UAAU,EAAE;AAAA,IACd,EAAE;AAKF,UAAM,iBAAiB,MAAM;AAC7B,UAAM,OACJ,QAAQ,WAAW,KAAK,QACpB,qBAAqB,MAAM,KAAK,mDAAmD,cAAc,sBACjG;AAEN,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,SAAS;AAAA,MAChB,WAAW,SAAS,SAAS;AAAA,MAC7B,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,YAAY;AAAA,IACd;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-summary.d.ts","sourceRoot":"","sources":["../src/tool-summary.ts"],"names":[],"mappings":"AA0BA,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,YAC/B,MAAM,EACN,WAAW,EACX,SAAS,EACT,SAAS,EACT,KAAK,EACL,KAAK,EACL,OAAO,EACP,aAAa,EACb,SAAS,CACD,CAAC;AAyCX;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CA4GvF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gCAAgC,EAAE,MA2EnC,CAAC"}
|
package/dist/tool-summary.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/tool-summary.ts"],
|
|
4
|
-
"sourcesContent": ["// Tool-aware one-line input summary \u2014 the single source of truth shared by the\n// WebUI chat bubbles and the HQ dashboard chat-history sidebar.\n//\n// The whole point: 8 parallel `read` calls should NOT look identical, and a\n// TodoWrite with 8 todos shouldn't dump 800 chars of JSON when \"8 todos \u00B7 3\n// done\" tells the whole story.\n//\n// PURE DATA + PURE FUNCTION \u2014 no node imports \u2014 so it is safe to import from the\n// browser (WebUI). The HQ dashboard is a served template-literal string that\n// cannot `import` at runtime, so it embeds `SUMMARIZE_TOOL_INPUT_BROWSER_SRC`\n// (an authored-once browser-JS transcription of the same logic) instead of a\n// second hand-rolled copy. A test (`tool-summary.parity`) runs both against one\n// fixture table to guarantee they never drift.\n//\n// Cases that earn a custom branch:\n// - TodoWrite -> \"8 todos \u00B7 3 done \u00B7 2 in-progress\"\n// - batch/parallel -> \"N sub-tools \u00B7 read, grep +2\"\n// - edit / str_replace -> \"edit foo.ts (3 \u2192 5 lines)\"\n// - write / create -> \"write foo.ts \u00B7 12 lines\"\n// - bash / shell / exec -> \"$ <command snippet>\"\n// - fetch / http / web -> \"GET https://\u2026\"\n// - grep / search -> \"grep <pattern> in <scope>\"\n// - glob / find -> \"glob <pattern>\"\n// - read -> \"read path (N\u2026M)\"\n// - fallback -> first non-empty head field, else compact JSON.\n\n/** Head-field lookup order for the generic fallback. */\nexport const FALLBACK_HEAD_FIELDS = [\n 'path',\n 'file_path',\n 'pattern',\n 'command',\n 'cmd',\n 'url',\n 'query',\n 'description',\n 'content',\n] as const;\n\nfunction clip(s: string, n: number): string {\n return s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;\n}\n\nfunction pickPath(obj: Record<string, unknown>): string {\n const p = obj.file_path ?? obj.path ?? obj.filepath;\n return typeof p === 'string' ? p : '';\n}\n\nfunction safeJson(v: unknown): string {\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n\n/**\n * Normalize the two shapes callers hold: WebUI passes a parsed object; HQ holds\n * a JSON *string*. A string that parses to an object is treated as that object;\n * a string that does not parse is returned clipped as-is.\n */\nfunction coerceInput(input: unknown): { obj: Record<string, unknown> | null; raw: unknown } {\n if (typeof input === 'string') {\n const s = input.trim();\n if (s.startsWith('{') || s.startsWith('[')) {\n try {\n const parsed = JSON.parse(s);\n if (parsed && typeof parsed === 'object') return { obj: parsed as Record<string, unknown>, raw: parsed };\n } catch {\n // fall through \u2014 non-JSON string\n }\n }\n return { obj: null, raw: input };\n }\n if (input && typeof input === 'object') return { obj: input as Record<string, unknown>, raw: input };\n return { obj: null, raw: input };\n}\n\n/**\n * One-line, tool-aware summary of a tool call's input.\n *\n * @param toolName canonical or aliased tool name (case-insensitive).\n * @param input the tool input \u2014 a parsed object OR a JSON string.\n */\nexport function summarizeToolInput(toolName: string | undefined, input: unknown): string {\n if (input === null || input === undefined || input === '') return '';\n const { obj, raw } = coerceInput(input);\n if (!obj) return clip(String(raw), 120);\n\n const name = (toolName ?? '').toLowerCase();\n\n // ---- TodoWrite ----------------------------------------------------\n if (/^todo(_?write)?$|^todos$/.test(name) || Array.isArray(obj.todos)) {\n const todos = (obj.todos ?? []) as Array<{ status?: string | undefined }>;\n if (Array.isArray(todos)) {\n const done = todos.filter((t) => t?.status === 'completed').length;\n const wip = todos.filter((t) => t?.status === 'in_progress').length;\n const parts = [`${todos.length} todo${todos.length === 1 ? '' : 's'}`];\n if (done > 0) parts.push(`${done} done`);\n if (wip > 0) parts.push(`${wip} in-progress`);\n return parts.join(' \u00B7 ');\n }\n }\n\n // ---- batch_tool_use / parallel_tool_use ---------------------------\n if (/batch|parallel/.test(name) || Array.isArray(obj.tool_uses) || Array.isArray(obj.calls)) {\n const list = (obj.tool_uses ?? obj.calls ?? obj.batch) as unknown[];\n if (Array.isArray(list)) {\n const subNames = new Set<string>();\n for (const item of list) {\n if (item && typeof item === 'object' && 'name' in item) {\n subNames.add(String((item as { name: unknown }).name));\n } else if (item && typeof item === 'object' && 'tool' in item) {\n subNames.add(String((item as { tool: unknown }).tool));\n }\n }\n const preview = [...subNames].slice(0, 3).join(', ');\n const more = subNames.size > 3 ? ` +${subNames.size - 3}` : '';\n return `${list.length} sub-tool${list.length === 1 ? '' : 's'}${preview ? ` \u00B7 ${preview}${more}` : ''}`;\n }\n }\n\n // ---- edit / str_replace / patch -----------------------------------\n if (/^(edit|str_replace|edit_file|patch)$/.test(name)) {\n const fp = pickPath(obj);\n const oldS = typeof obj.old_string === 'string' ? obj.old_string : '';\n const newS = typeof obj.new_string === 'string' ? obj.new_string : '';\n const oldLines = oldS ? oldS.split('\\n').length : 0;\n const newLines = newS ? newS.split('\\n').length : 0;\n return `edit ${fp || '(file)'}${oldLines || newLines ? ` (${oldLines} \u2192 ${newLines} lines)` : ''}`;\n }\n\n // ---- write / create -----------------------------------------------\n if (/^(write|write_file|create_file|new_file)$/.test(name)) {\n const fp = pickPath(obj);\n const c = typeof obj.content === 'string' ? obj.content : '';\n const lines = c ? c.split('\\n').length : 0;\n return `write ${fp || '(file)'}${lines ? ` \u00B7 ${lines} lines` : ''}`;\n }\n\n // ---- bash / shell / exec / run ------------------------------------\n if (/^(bash|shell|sh|exec|run|run_command|run_shell|command)$/.test(name)) {\n const cmd = (obj.command ?? obj.cmd ?? obj.script) as string | undefined;\n if (typeof cmd === 'string') return `$ ${clip(cmd, 110)}`;\n }\n\n // ---- fetch / http / web -------------------------------------------\n if (/^(fetch|http|web|web_fetch|web_search|webfetch|curl|request)$/.test(name)) {\n const url = obj.url as string | undefined;\n if (typeof url === 'string') {\n const method = (obj.method as string | undefined) ?? 'GET';\n return `${method.toUpperCase()} ${clip(url, 100)}`;\n }\n }\n\n // ---- grep / search ------------------------------------------------\n if (/^(grep|search|ripgrep|rg)$/.test(name)) {\n const pattern = obj.pattern as string | undefined;\n const scope = (obj.path ?? obj.glob ?? obj.type) as string | undefined;\n if (typeof pattern === 'string') {\n return scope ? `grep ${clip(pattern, 60)} in ${scope}` : `grep ${clip(pattern, 100)}`;\n }\n }\n\n // ---- glob / find --------------------------------------------------\n if (/^(glob|find)$/.test(name)) {\n const p = (obj.pattern ?? obj.glob) as string | undefined;\n if (typeof p === 'string') return `glob ${clip(p, 80)}`;\n }\n\n // ---- read with offset/limit --------------------------------------\n if (/^(read|read_file|cat|view)$/.test(name)) {\n const fp = pickPath(obj);\n const offset = obj.offset as number | undefined;\n const limit = obj.limit as number | undefined;\n if (fp && (typeof offset === 'number' || typeof limit === 'number')) {\n const start = offset ?? 0;\n const end = typeof limit === 'number' ? start + limit : '';\n return `read ${fp} (${start}\u2026${end})`;\n }\n if (fp) return `read ${fp}`;\n }\n\n // ---- Fallback: first non-empty headline string field --------------\n for (const k of FALLBACK_HEAD_FIELDS) {\n const v = obj[k];\n if (typeof v === 'string' && v.length > 0) {\n return `${k}: ${clip(v, 100)}`;\n }\n }\n // Last resort: compact JSON.\n return clip(safeJson(raw), 120);\n}\n\n/**\n * Authored-once browser-JS transcription of `summarizeToolInput`, for surfaces\n * that cannot `import` at runtime (the HQ dashboard is a served template-literal\n * string). This is DELIBERATELY written with string concatenation and single\n * backslashes only: the HQ template literal escapes `\\` and `` ` ``/`${` before\n * embedding, so this text must contain NO backticks and NO `${` sequences.\n *\n * Defines one global function: `toolInputSummary(name, rawInput)` where\n * rawInput is a JSON string (HQ's on-wire shape) or null.\n *\n * A parity test asserts this produces identical output to the TS function above.\n */\nexport const SUMMARIZE_TOOL_INPUT_BROWSER_SRC: string = [\n 'function __clip(s,n){ s=String(s==null?\"\":s); return s.length>n ? s.slice(0,n-1)+\"\\\\u2026\" : s; }',\n 'function toolInputSummary(name, rawInput){',\n ' if(rawInput==null || rawInput===\"\") return \"\";',\n ' var obj=null, raw=rawInput;',\n ' if(typeof rawInput===\"string\"){',\n ' var s=rawInput.trim();',\n ' if(s.charAt(0)===\"{\" || s.charAt(0)===\"[\"){ try{ var pj=JSON.parse(s); if(pj && typeof pj===\"object\"){ obj=pj; raw=pj; } }catch(_e){} }',\n ' } else if(rawInput && typeof rawInput===\"object\"){ obj=rawInput; raw=rawInput; }',\n ' if(!obj) return __clip(String(raw),120);',\n ' var n=String(name==null?\"\":name).toLowerCase();',\n ' if(/^todo(_?write)?$|^todos$/.test(n) || Array.isArray(obj.todos)){',\n ' var todos=obj.todos||[];',\n ' if(Array.isArray(todos)){',\n ' var done=0, wip=0, ti;',\n ' for(ti=0; ti<todos.length; ti++){ var st=todos[ti]&&todos[ti].status; if(st===\"completed\") done++; else if(st===\"in_progress\") wip++; }',\n ' var tp=[todos.length+\" todo\"+(todos.length===1?\"\":\"s\")];',\n ' if(done>0) tp.push(done+\" done\"); if(wip>0) tp.push(wip+\" in-progress\");',\n ' return tp.join(\" \\\\u00b7 \");',\n ' }',\n ' }',\n ' if(/batch|parallel/.test(n) || Array.isArray(obj.tool_uses) || Array.isArray(obj.calls)){',\n ' var list=obj.tool_uses||obj.calls||obj.batch;',\n ' if(Array.isArray(list)){',\n ' var names={}, ord=[], li, it, nm;',\n ' for(li=0; li<list.length; li++){ it=list[li]; if(it && typeof it===\"object\"){ nm=(\"name\" in it)?it.name:((\"tool\" in it)?it.tool:null); if(nm!=null){ nm=String(nm); if(!names[nm]){ names[nm]=1; ord.push(nm); } } } }',\n ' var preview=ord.slice(0,3).join(\", \");',\n ' var more=ord.length>3 ? \" +\"+(ord.length-3) : \"\";',\n ' return list.length+\" sub-tool\"+(list.length===1?\"\":\"s\")+(preview?(\" \\\\u00b7 \"+preview+more):\"\");',\n ' }',\n ' }',\n ' var fp;',\n ' if(/^(edit|str_replace|edit_file|patch)$/.test(n)){',\n ' fp=(obj.file_path!=null?obj.file_path:(obj.path!=null?obj.path:obj.filepath));',\n ' var oldS=typeof obj.old_string===\"string\"?obj.old_string:\"\";',\n ' var newS=typeof obj.new_string===\"string\"?obj.new_string:\"\";',\n ' var ol=oldS?oldS.split(\"\\\\n\").length:0, nl=newS?newS.split(\"\\\\n\").length:0;',\n ' var efp=typeof fp===\"string\"?fp:\"\";',\n ' return \"edit \"+(efp||\"(file)\")+((ol||nl)?(\" (\"+ol+\" \\\\u2192 \"+nl+\" lines)\"):\"\");',\n ' }',\n ' if(/^(write|write_file|create_file|new_file)$/.test(n)){',\n ' fp=(obj.file_path!=null?obj.file_path:(obj.path!=null?obj.path:obj.filepath));',\n ' var wc=typeof obj.content===\"string\"?obj.content:\"\";',\n ' var wl=wc?wc.split(\"\\\\n\").length:0;',\n ' var wfp=typeof fp===\"string\"?fp:\"\";',\n ' return \"write \"+(wfp||\"(file)\")+(wl?(\" \\\\u00b7 \"+wl+\" lines\"):\"\");',\n ' }',\n ' if(/^(bash|shell|sh|exec|run|run_command|run_shell|command)$/.test(n)){',\n ' var cmd=(obj.command!=null?obj.command:(obj.cmd!=null?obj.cmd:obj.script));',\n ' if(typeof cmd===\"string\") return \"$ \"+__clip(cmd,110);',\n ' }',\n ' if(/^(fetch|http|web|web_fetch|web_search|webfetch|curl|request)$/.test(n)){',\n ' if(typeof obj.url===\"string\"){ var mth=(typeof obj.method===\"string\"?obj.method:\"GET\"); return mth.toUpperCase()+\" \"+__clip(obj.url,100); }',\n ' }',\n ' if(/^(grep|search|ripgrep|rg)$/.test(n)){',\n ' var pat=obj.pattern, scope=(obj.path!=null?obj.path:(obj.glob!=null?obj.glob:obj.type));',\n ' if(typeof pat===\"string\"){ return scope ? (\"grep \"+__clip(pat,60)+\" in \"+scope) : (\"grep \"+__clip(pat,100)); }',\n ' }',\n ' if(/^(glob|find)$/.test(n)){',\n ' var gp=(obj.pattern!=null?obj.pattern:obj.glob);',\n ' if(typeof gp===\"string\") return \"glob \"+__clip(gp,80);',\n ' }',\n ' if(/^(read|read_file|cat|view)$/.test(n)){',\n ' fp=(obj.file_path!=null?obj.file_path:(obj.path!=null?obj.path:obj.filepath));',\n ' if(typeof fp===\"string\" && fp){',\n ' var off=obj.offset, lim=obj.limit;',\n ' if(typeof off===\"number\" || typeof lim===\"number\"){ var start=(typeof off===\"number\"?off:0); var end=(typeof lim===\"number\"?(start+lim):\"\"); return \"read \"+fp+\" (\"+start+\"\\\\u2026\"+end+\")\"; }',\n ' return \"read \"+fp;',\n ' }',\n ' }',\n ' var HEAD=[\"path\",\"file_path\",\"pattern\",\"command\",\"cmd\",\"url\",\"query\",\"description\",\"content\"], hi;',\n ' for(hi=0; hi<HEAD.length; hi++){ var hv=obj[HEAD[hi]]; if(typeof hv===\"string\" && hv.length>0){ return HEAD[hi]+\": \"+__clip(hv,100); } }',\n ' var jsonStr; try{ jsonStr=JSON.stringify(raw); }catch(_e2){ jsonStr=String(raw); }',\n ' return __clip(jsonStr,120);',\n '}',\n].join('\\n');\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/dist/tool-tier.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-tier.d.ts","sourceRoot":"","sources":["../src/tool-tier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAQ9D;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,uBAAuB,EAC7B,QAAQ,EAAE,SAAS,IAAI,EAAE,GACxB,IAAI,EAAE,CA0BR;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,oBAAoB,CAAC,CAAC;IACnD,IAAI,EAAE,uBAAuB,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,GAAG,SAAS,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,gFAAgF;AAChF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,8BAA8B,GAAG,IAAI,EAAE,CAOvF"}
|