0xkobold 0.0.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.
Files changed (258) hide show
  1. package/.agents/skills/nextjs-best-practices/SKILL.md +208 -0
  2. package/.agents/skills/sql-optimization-patterns/SKILL.md +509 -0
  3. package/HEARTBEAT.md +45 -0
  4. package/README.md +197 -0
  5. package/USAGE.md +191 -0
  6. package/dist/package.json +77 -0
  7. package/dist/src/agent/pi-adapter.js +307 -0
  8. package/dist/src/agent/pi-adapter.js.map +1 -0
  9. package/dist/src/agent/tool-adapter.js +86 -0
  10. package/dist/src/agent/tool-adapter.js.map +1 -0
  11. package/dist/src/approval/queue.js +114 -0
  12. package/dist/src/approval/queue.js.map +1 -0
  13. package/dist/src/ascii-kobold.js +76 -0
  14. package/dist/src/ascii-kobold.js.map +1 -0
  15. package/dist/src/cli/client.js +217 -0
  16. package/dist/src/cli/client.js.map +1 -0
  17. package/dist/src/cli/commands/agent.js +272 -0
  18. package/dist/src/cli/commands/agent.js.map +1 -0
  19. package/dist/src/cli/commands/chat.js +234 -0
  20. package/dist/src/cli/commands/chat.js.map +1 -0
  21. package/dist/src/cli/commands/config.js +202 -0
  22. package/dist/src/cli/commands/config.js.map +1 -0
  23. package/dist/src/cli/commands/daemon.js +203 -0
  24. package/dist/src/cli/commands/daemon.js.map +1 -0
  25. package/dist/src/cli/commands/gateway.js +184 -0
  26. package/dist/src/cli/commands/gateway.js.map +1 -0
  27. package/dist/src/cli/commands/init.js +175 -0
  28. package/dist/src/cli/commands/init.js.map +1 -0
  29. package/dist/src/cli/commands/kobold.js +21 -0
  30. package/dist/src/cli/commands/kobold.js.map +1 -0
  31. package/dist/src/cli/commands/logs.js +27 -0
  32. package/dist/src/cli/commands/logs.js.map +1 -0
  33. package/dist/src/cli/commands/mode.js +121 -0
  34. package/dist/src/cli/commands/mode.js.map +1 -0
  35. package/dist/src/cli/commands/persona.js +261 -0
  36. package/dist/src/cli/commands/persona.js.map +1 -0
  37. package/dist/src/cli/commands/start.js +66 -0
  38. package/dist/src/cli/commands/start.js.map +1 -0
  39. package/dist/src/cli/commands/status.js +117 -0
  40. package/dist/src/cli/commands/status.js.map +1 -0
  41. package/dist/src/cli/commands/stop.js +27 -0
  42. package/dist/src/cli/commands/stop.js.map +1 -0
  43. package/dist/src/cli/commands/system.js +128 -0
  44. package/dist/src/cli/commands/system.js.map +1 -0
  45. package/dist/src/cli/commands/tui.js +103 -0
  46. package/dist/src/cli/commands/tui.js.map +1 -0
  47. package/dist/src/cli/commands/update.js +133 -0
  48. package/dist/src/cli/commands/update.js.map +1 -0
  49. package/dist/src/cli/extensions/discord.js +113 -0
  50. package/dist/src/cli/extensions/discord.js.map +1 -0
  51. package/dist/src/cli/extensions/env.js +91 -0
  52. package/dist/src/cli/extensions/env.js.map +1 -0
  53. package/dist/src/cli/extensions/heartbeat.js +78 -0
  54. package/dist/src/cli/extensions/heartbeat.js.map +1 -0
  55. package/dist/src/cli/index.js +24 -0
  56. package/dist/src/cli/index.js.map +1 -0
  57. package/dist/src/cli/program.js +70 -0
  58. package/dist/src/cli/program.js.map +1 -0
  59. package/dist/src/cli/repl.js +184 -0
  60. package/dist/src/cli/repl.js.map +1 -0
  61. package/dist/src/cli/shared/discord-service.js +102 -0
  62. package/dist/src/cli/shared/discord-service.js.map +1 -0
  63. package/dist/src/config/index.js +10 -0
  64. package/dist/src/config/index.js.map +1 -0
  65. package/dist/src/config/loader.js +401 -0
  66. package/dist/src/config/loader.js.map +1 -0
  67. package/dist/src/config/paths.js +84 -0
  68. package/dist/src/config/paths.js.map +1 -0
  69. package/dist/src/config/types.js +8 -0
  70. package/dist/src/config/types.js.map +1 -0
  71. package/dist/src/context-detector.js +60 -0
  72. package/dist/src/context-detector.js.map +1 -0
  73. package/dist/src/discord/index.js +376 -0
  74. package/dist/src/discord/index.js.map +1 -0
  75. package/dist/src/event-bus/index.js +97 -0
  76. package/dist/src/event-bus/index.js.map +1 -0
  77. package/dist/src/extensions/command-args.js +68 -0
  78. package/dist/src/extensions/command-args.js.map +1 -0
  79. package/dist/src/extensions/core/agent-registry-extension.js +541 -0
  80. package/dist/src/extensions/core/agent-registry-extension.js.map +1 -0
  81. package/dist/src/extensions/core/agent-worker.js +148 -0
  82. package/dist/src/extensions/core/agent-worker.js.map +1 -0
  83. package/dist/src/extensions/core/compaction-safeguard.js +154 -0
  84. package/dist/src/extensions/core/compaction-safeguard.js.map +1 -0
  85. package/dist/src/extensions/core/confirm-destructive.js +43 -0
  86. package/dist/src/extensions/core/confirm-destructive.js.map +1 -0
  87. package/dist/src/extensions/core/context-aware-extension.js +124 -0
  88. package/dist/src/extensions/core/context-aware-extension.js.map +1 -0
  89. package/dist/src/extensions/core/context-pruning/extension.js +124 -0
  90. package/dist/src/extensions/core/context-pruning/extension.js.map +1 -0
  91. package/dist/src/extensions/core/context-pruning/pruner.js +312 -0
  92. package/dist/src/extensions/core/context-pruning/pruner.js.map +1 -0
  93. package/dist/src/extensions/core/context-pruning/runtime.js +48 -0
  94. package/dist/src/extensions/core/context-pruning/runtime.js.map +1 -0
  95. package/dist/src/extensions/core/context-pruning/settings.js +105 -0
  96. package/dist/src/extensions/core/context-pruning/settings.js.map +1 -0
  97. package/dist/src/extensions/core/dirty-repo-guard.js +42 -0
  98. package/dist/src/extensions/core/dirty-repo-guard.js.map +1 -0
  99. package/dist/src/extensions/core/discord-channel-extension.js +205 -0
  100. package/dist/src/extensions/core/discord-channel-extension.js.map +1 -0
  101. package/dist/src/extensions/core/discord-extension.js +142 -0
  102. package/dist/src/extensions/core/discord-extension.js.map +1 -0
  103. package/dist/src/extensions/core/env-loader-extension.js +157 -0
  104. package/dist/src/extensions/core/env-loader-extension.js.map +1 -0
  105. package/dist/src/extensions/core/fileops-extension.js +699 -0
  106. package/dist/src/extensions/core/fileops-extension.js.map +1 -0
  107. package/dist/src/extensions/core/gateway-extension.js +730 -0
  108. package/dist/src/extensions/core/gateway-extension.js.map +1 -0
  109. package/dist/src/extensions/core/git-checkpoint.js +46 -0
  110. package/dist/src/extensions/core/git-checkpoint.js.map +1 -0
  111. package/dist/src/extensions/core/handoff-extension.js +206 -0
  112. package/dist/src/extensions/core/handoff-extension.js.map +1 -0
  113. package/dist/src/extensions/core/heartbeat-extension.js +373 -0
  114. package/dist/src/extensions/core/heartbeat-extension.js.map +1 -0
  115. package/dist/src/extensions/core/mcp-extension.js +413 -0
  116. package/dist/src/extensions/core/mcp-extension.js.map +1 -0
  117. package/dist/src/extensions/core/mode-manager-extension.js +562 -0
  118. package/dist/src/extensions/core/mode-manager-extension.js.map +1 -0
  119. package/dist/src/extensions/core/multi-channel-extension.js +435 -0
  120. package/dist/src/extensions/core/multi-channel-extension.js.map +1 -0
  121. package/dist/src/extensions/core/ollama-provider-extension.js +66 -0
  122. package/dist/src/extensions/core/ollama-provider-extension.js.map +1 -0
  123. package/dist/src/extensions/core/onboarding-extension.js +122 -0
  124. package/dist/src/extensions/core/onboarding-extension.js.map +1 -0
  125. package/dist/src/extensions/core/persona-loader-extension.js +139 -0
  126. package/dist/src/extensions/core/persona-loader-extension.js.map +1 -0
  127. package/dist/src/extensions/core/pi-notify-extension.js +70 -0
  128. package/dist/src/extensions/core/pi-notify-extension.js.map +1 -0
  129. package/dist/src/extensions/core/protected-paths.js +24 -0
  130. package/dist/src/extensions/core/protected-paths.js.map +1 -0
  131. package/dist/src/extensions/core/questionnaire-extension.js +242 -0
  132. package/dist/src/extensions/core/questionnaire-extension.js.map +1 -0
  133. package/dist/src/extensions/core/self-update-extension.js +181 -0
  134. package/dist/src/extensions/core/self-update-extension.js.map +1 -0
  135. package/dist/src/extensions/core/session-bridge-extension.js +78 -0
  136. package/dist/src/extensions/core/session-bridge-extension.js.map +1 -0
  137. package/dist/src/extensions/core/session-manager-extension.js +319 -0
  138. package/dist/src/extensions/core/session-manager-extension.js.map +1 -0
  139. package/dist/src/extensions/core/session-name-extension.js +88 -0
  140. package/dist/src/extensions/core/session-name-extension.js.map +1 -0
  141. package/dist/src/extensions/core/session-pruning-extension.js +480 -0
  142. package/dist/src/extensions/core/session-pruning-extension.js.map +1 -0
  143. package/dist/src/extensions/core/task-manager-extension.js +661 -0
  144. package/dist/src/extensions/core/task-manager-extension.js.map +1 -0
  145. package/dist/src/extensions/core/update-extension.js +438 -0
  146. package/dist/src/extensions/core/update-extension.js.map +1 -0
  147. package/dist/src/extensions/core/websearch-extension.js +463 -0
  148. package/dist/src/extensions/core/websearch-extension.js.map +1 -0
  149. package/dist/src/extensions/index.js +5 -0
  150. package/dist/src/extensions/index.js.map +1 -0
  151. package/dist/src/extensions/loader.js +80 -0
  152. package/dist/src/extensions/loader.js.map +1 -0
  153. package/dist/src/gateway/index.js +353 -0
  154. package/dist/src/gateway/index.js.map +1 -0
  155. package/dist/src/index.js +150 -0
  156. package/dist/src/index.js.map +1 -0
  157. package/dist/src/llm/anthropic.js +86 -0
  158. package/dist/src/llm/anthropic.js.map +1 -0
  159. package/dist/src/llm/index.js +9 -0
  160. package/dist/src/llm/index.js.map +1 -0
  161. package/dist/src/llm/ollama.js +113 -0
  162. package/dist/src/llm/ollama.js.map +1 -0
  163. package/dist/src/llm/router.js +145 -0
  164. package/dist/src/llm/router.js.map +1 -0
  165. package/dist/src/llm/types.js +7 -0
  166. package/dist/src/llm/types.js.map +1 -0
  167. package/dist/src/memory/index.js +5 -0
  168. package/dist/src/memory/index.js.map +1 -0
  169. package/dist/src/memory/store.js +91 -0
  170. package/dist/src/memory/store.js.map +1 -0
  171. package/dist/src/pi-config.js +80 -0
  172. package/dist/src/pi-config.js.map +1 -0
  173. package/dist/src/skills/builtin/file.js +184 -0
  174. package/dist/src/skills/builtin/file.js.map +1 -0
  175. package/dist/src/skills/builtin/shell.js +100 -0
  176. package/dist/src/skills/builtin/shell.js.map +1 -0
  177. package/dist/src/skills/builtin/subagent.js +62 -0
  178. package/dist/src/skills/builtin/subagent.js.map +1 -0
  179. package/dist/src/skills/hello.js +42 -0
  180. package/dist/src/skills/hello.js.map +1 -0
  181. package/dist/src/skills/index.js +11 -0
  182. package/dist/src/skills/index.js.map +1 -0
  183. package/dist/src/skills/loader.js +382 -0
  184. package/dist/src/skills/loader.js.map +1 -0
  185. package/dist/src/skills/types.js +8 -0
  186. package/dist/src/skills/types.js.map +1 -0
  187. package/dist/src/utils/working-dir.js +71 -0
  188. package/dist/src/utils/working-dir.js.map +1 -0
  189. package/package.json +77 -0
  190. package/skills/1password/SKILL.md +70 -0
  191. package/skills/1password/references/cli-examples.md +29 -0
  192. package/skills/1password/references/get-started.md +17 -0
  193. package/skills/apple-notes/SKILL.md +77 -0
  194. package/skills/apple-reminders/SKILL.md +118 -0
  195. package/skills/bear-notes/SKILL.md +107 -0
  196. package/skills/blogwatcher/SKILL.md +69 -0
  197. package/skills/blucli/SKILL.md +47 -0
  198. package/skills/bluebubbles/SKILL.md +131 -0
  199. package/skills/camsnap/SKILL.md +45 -0
  200. package/skills/canvas/SKILL.md +198 -0
  201. package/skills/clawhub/SKILL.md +77 -0
  202. package/skills/coding-agent/SKILL.md +284 -0
  203. package/skills/discord/SKILL.md +197 -0
  204. package/skills/eightctl/SKILL.md +50 -0
  205. package/skills/food-order/SKILL.md +48 -0
  206. package/skills/gemini/SKILL.md +43 -0
  207. package/skills/gh-issues/SKILL.md +865 -0
  208. package/skills/gifgrep/SKILL.md +79 -0
  209. package/skills/github/SKILL.md +163 -0
  210. package/skills/gog/SKILL.md +116 -0
  211. package/skills/goplaces/SKILL.md +52 -0
  212. package/skills/healthcheck/SKILL.md +245 -0
  213. package/skills/himalaya/SKILL.md +257 -0
  214. package/skills/himalaya/references/configuration.md +184 -0
  215. package/skills/himalaya/references/message-composition.md +199 -0
  216. package/skills/imsg/SKILL.md +122 -0
  217. package/skills/mcporter/SKILL.md +61 -0
  218. package/skills/model-usage/SKILL.md +69 -0
  219. package/skills/model-usage/references/codexbar-cli.md +33 -0
  220. package/skills/model-usage/scripts/model_usage.py +310 -0
  221. package/skills/nano-banana-pro/SKILL.md +58 -0
  222. package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
  223. package/skills/nano-pdf/SKILL.md +38 -0
  224. package/skills/notion/SKILL.md +172 -0
  225. package/skills/obsidian/SKILL.md +81 -0
  226. package/skills/openai-image-gen/SKILL.md +89 -0
  227. package/skills/openai-image-gen/scripts/gen.py +240 -0
  228. package/skills/openai-whisper/SKILL.md +38 -0
  229. package/skills/openai-whisper-api/SKILL.md +52 -0
  230. package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
  231. package/skills/openhue/SKILL.md +112 -0
  232. package/skills/oracle/SKILL.md +125 -0
  233. package/skills/ordercli/SKILL.md +78 -0
  234. package/skills/peekaboo/SKILL.md +190 -0
  235. package/skills/sag/SKILL.md +87 -0
  236. package/skills/session-logs/SKILL.md +115 -0
  237. package/skills/sherpa-onnx-tts/SKILL.md +103 -0
  238. package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
  239. package/skills/skill-creator/SKILL.md +370 -0
  240. package/skills/skill-creator/license.txt +202 -0
  241. package/skills/skill-creator/scripts/init_skill.py +378 -0
  242. package/skills/skill-creator/scripts/package_skill.py +111 -0
  243. package/skills/skill-creator/scripts/quick_validate.py +101 -0
  244. package/skills/slack/SKILL.md +144 -0
  245. package/skills/songsee/SKILL.md +49 -0
  246. package/skills/sonoscli/SKILL.md +46 -0
  247. package/skills/spotify-player/SKILL.md +64 -0
  248. package/skills/summarize/SKILL.md +87 -0
  249. package/skills/things-mac/SKILL.md +86 -0
  250. package/skills/tmux/SKILL.md +153 -0
  251. package/skills/tmux/scripts/find-sessions.sh +112 -0
  252. package/skills/tmux/scripts/wait-for-text.sh +83 -0
  253. package/skills/trello/SKILL.md +95 -0
  254. package/skills/video-frames/SKILL.md +46 -0
  255. package/skills/video-frames/scripts/frame.sh +81 -0
  256. package/skills/voice-call/SKILL.md +45 -0
  257. package/skills/wacli/SKILL.md +72 -0
  258. package/skills/weather/SKILL.md +112 -0
@@ -0,0 +1,661 @@
1
+ /**
2
+ * Task Manager Extension
3
+ *
4
+ * Kanban-style task management with states:
5
+ * - Backlog: Ideas and future work
6
+ * - Needs Assignment: Ready to start but unassigned
7
+ * - In Progress: Actively being worked on
8
+ * - Needs Review: Completed, awaiting review
9
+ * - Blocked: Stalled, needs human intervention
10
+ * - Done: Completed and verified
11
+ */
12
+ import { Database } from "bun:sqlite";
13
+ import { join } from "path";
14
+ import { homedir } from "os";
15
+ import { existsSync, mkdirSync } from "fs";
16
+ import { parseArgs, CommonArgs } from "../command-args.js";
17
+ const KOBOLD_DIR = join(homedir(), ".0xkobold");
18
+ const TASK_DB = join(KOBOLD_DIR, "tasks.db");
19
+ const TASK_COLUMNS = [
20
+ { id: "backlog", label: "Backlog", emoji: "📋", color: "gray" },
21
+ { id: "needs-assignment", label: "Needs Assignment", emoji: "👤", color: "blue" },
22
+ { id: "in-progress", label: "In Progress", emoji: "🏗️", color: "yellow" },
23
+ { id: "needs-review", label: "Needs Review", emoji: "👀", color: "purple" },
24
+ { id: "blocked", label: "Blocked", emoji: "🚫", color: "red" },
25
+ { id: "done", label: "Done", emoji: "✅", color: "green" },
26
+ ];
27
+ let db = null;
28
+ let currentSessionId = "default";
29
+ /**
30
+ * Initialize task database
31
+ */
32
+ function initDatabase() {
33
+ if (db)
34
+ return db;
35
+ if (!existsSync(KOBOLD_DIR)) {
36
+ mkdirSync(KOBOLD_DIR, { recursive: true });
37
+ }
38
+ db = new Database(TASK_DB);
39
+ db.run("PRAGMA journal_mode = WAL;");
40
+ // Tasks table
41
+ db.run(`
42
+ CREATE TABLE IF NOT EXISTS tasks (
43
+ id TEXT PRIMARY KEY,
44
+ title TEXT NOT NULL,
45
+ description TEXT,
46
+ status TEXT NOT NULL DEFAULT 'backlog',
47
+ priority TEXT NOT NULL DEFAULT 'medium',
48
+ assignee TEXT,
49
+ session_id TEXT NOT NULL,
50
+ parent_id TEXT,
51
+ tags TEXT,
52
+ created_at INTEGER NOT NULL,
53
+ updated_at INTEGER NOT NULL,
54
+ completed_at INTEGER,
55
+ metadata TEXT,
56
+ FOREIGN KEY (parent_id) REFERENCES tasks(id) ON DELETE CASCADE
57
+ )
58
+ `);
59
+ // Task comments
60
+ db.run(`
61
+ CREATE TABLE IF NOT EXISTS task_comments (
62
+ id TEXT PRIMARY KEY,
63
+ task_id TEXT NOT NULL,
64
+ author TEXT NOT NULL,
65
+ content TEXT NOT NULL,
66
+ timestamp INTEGER NOT NULL,
67
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
68
+ )
69
+ `);
70
+ // Task history (status changes)
71
+ db.run(`
72
+ CREATE TABLE IF NOT EXISTS task_history (
73
+ id TEXT PRIMARY KEY,
74
+ task_id TEXT NOT NULL,
75
+ from_status TEXT,
76
+ to_status TEXT NOT NULL,
77
+ changed_by TEXT,
78
+ timestamp INTEGER NOT NULL,
79
+ note TEXT,
80
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
81
+ )
82
+ `);
83
+ // Indexes
84
+ db.run(`CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_id)`);
85
+ db.run(`CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)`);
86
+ db.run(`CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee)`);
87
+ console.log("[TaskManager] Database initialized");
88
+ return db;
89
+ }
90
+ /**
91
+ * Generate task ID
92
+ */
93
+ function generateTaskId() {
94
+ return `task-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
95
+ }
96
+ /**
97
+ * Task Manager Extension
98
+ */
99
+ export default function taskManagerExtension(pi) {
100
+ const database = initDatabase();
101
+ // Track current session from session or environment
102
+ pi.on("session_start", async (_event, ctx) => {
103
+ currentSessionId = ctx.sessionManager.getSessionId();
104
+ });
105
+ // Update session ID on session switch
106
+ pi.on("session_switch", async (event, ctx) => {
107
+ currentSessionId = ctx.sessionManager.getSessionId();
108
+ });
109
+ /**
110
+ * Create a new task
111
+ */
112
+ function createTask(title, description = "", options = {}) {
113
+ const id = generateTaskId();
114
+ const now = Date.now();
115
+ const task = {
116
+ id,
117
+ title,
118
+ description,
119
+ status: options.status || "backlog",
120
+ priority: options.priority || "medium",
121
+ assignee: options.assignee,
122
+ sessionId: options.sessionId || currentSessionId,
123
+ parentId: options.parentId,
124
+ tags: options.tags || [],
125
+ createdAt: now,
126
+ updatedAt: now,
127
+ metadata: options.metadata || {},
128
+ };
129
+ // @ts-ignore SQLite binding
130
+ database.run(`INSERT INTO tasks (id, title, description, status, priority, assignee, session_id, parent_id, tags, created_at, updated_at, metadata)
131
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [task.id,
132
+ task.title,
133
+ task.description,
134
+ task.status,
135
+ task.priority,
136
+ task.assignee || null,
137
+ task.sessionId || null,
138
+ task.parentId || null,
139
+ JSON.stringify(task.tags),
140
+ task.createdAt,
141
+ task.updatedAt,
142
+ JSON.stringify(task.metadata)]);
143
+ // Log creation in history
144
+ // @ts-ignore SQLite binding
145
+ database.run(`INSERT INTO task_history (id, task_id, from_status, to_status, changed_by, timestamp, note)
146
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [`hist-${Date.now()}`,
147
+ task.id,
148
+ null,
149
+ task.status,
150
+ currentSessionId,
151
+ now,
152
+ "Task created"]);
153
+ return task;
154
+ }
155
+ /**
156
+ * Get task by ID
157
+ */
158
+ function getTask(id) {
159
+ // @ts-ignore SQLite binding
160
+ const row = database.query("SELECT * FROM tasks WHERE id = ?").get([id]);
161
+ if (!row)
162
+ return null;
163
+ return {
164
+ id: row.id,
165
+ title: row.title,
166
+ description: row.description,
167
+ status: row.status,
168
+ priority: row.priority,
169
+ assignee: row.assignee,
170
+ sessionId: row.session_id,
171
+ parentId: row.parent_id,
172
+ tags: JSON.parse(String(row.tags || "[]")),
173
+ createdAt: row.created_at,
174
+ updatedAt: row.updated_at,
175
+ completedAt: row.completed_at,
176
+ metadata: JSON.parse(String(row.metadata || "{}")),
177
+ };
178
+ }
179
+ /**
180
+ * Update task status
181
+ */
182
+ function updateTaskStatus(id, newStatus, note) {
183
+ const task = getTask(id);
184
+ if (!task)
185
+ return false;
186
+ const oldStatus = task.status;
187
+ const now = Date.now();
188
+ // @ts-ignore SQLite binding
189
+ database.run(`UPDATE tasks SET status = ?, updated_at = ?, completed_at = ? WHERE id = ?`, [newStatus,
190
+ now,
191
+ newStatus === "done" ? now : task.completedAt || null,
192
+ id]);
193
+ // Log in history
194
+ // @ts-ignore SQLite binding
195
+ database.run(`INSERT INTO task_history (id, task_id, from_status, to_status, changed_by, timestamp, note)
196
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [`hist-${Date.now()}`,
197
+ id,
198
+ oldStatus,
199
+ newStatus,
200
+ currentSessionId,
201
+ now,
202
+ note || `Moved from ${oldStatus} to ${newStatus}`]);
203
+ return true;
204
+ }
205
+ /**
206
+ * Assign task
207
+ */
208
+ function assignTask(id, assignee) {
209
+ const task = getTask(id);
210
+ if (!task)
211
+ return false;
212
+ // @ts-ignore SQLite binding
213
+ database.run(`UPDATE tasks SET assignee = ?, updated_at = ? WHERE id = ?`, [assignee,
214
+ Date.now(),
215
+ id]);
216
+ // Also move to in-progress if in backlog
217
+ if (task.status === "backlog" || task.status === "needs-assignment") {
218
+ updateTaskStatus(id, "in-progress", `Assigned to ${assignee}`);
219
+ }
220
+ return true;
221
+ }
222
+ /**
223
+ * List tasks by status
224
+ */
225
+ function listTasks(filter) {
226
+ let query = "SELECT * FROM tasks WHERE 1=1";
227
+ const params = [];
228
+ if (filter?.status) {
229
+ query += " AND status = ?";
230
+ params.push(filter.status);
231
+ }
232
+ if (filter?.sessionId) {
233
+ query += " AND session_id = ?";
234
+ params.push(filter.sessionId);
235
+ }
236
+ if (filter?.assignee) {
237
+ query += " AND assignee = ?";
238
+ params.push(filter.assignee);
239
+ }
240
+ query += " ORDER BY priority DESC, updated_at DESC";
241
+ const rows = database.query(query).all(...params);
242
+ return rows.map((row) => ({
243
+ id: row.id,
244
+ title: row.title,
245
+ description: row.description,
246
+ status: row.status,
247
+ priority: row.priority,
248
+ assignee: row.assignee,
249
+ sessionId: row.session_id,
250
+ parentId: row.parent_id,
251
+ tags: JSON.parse(String(row.tags || "[]")),
252
+ createdAt: row.created_at,
253
+ updatedAt: row.updated_at,
254
+ completedAt: row.completed_at,
255
+ metadata: JSON.parse(String(row.metadata || "{}")),
256
+ }));
257
+ }
258
+ /**
259
+ * Get task board (all columns)
260
+ */
261
+ function getBoard() {
262
+ const board = {
263
+ backlog: [],
264
+ "needs-assignment": [],
265
+ "in-progress": [],
266
+ "needs-review": [],
267
+ blocked: [],
268
+ done: [],
269
+ };
270
+ const tasks = listTasks();
271
+ for (const task of tasks) {
272
+ board[task.status].push(task);
273
+ }
274
+ return board;
275
+ }
276
+ /**
277
+ * Add comment to task
278
+ */
279
+ function addComment(taskId, content) {
280
+ // @ts-ignore SQLite binding
281
+ database.run(`INSERT INTO task_comments (id, task_id, author, content, timestamp)
282
+ VALUES (?, ?, ?, ?, ?)`, [`comment-${Date.now()}`,
283
+ taskId,
284
+ currentSessionId,
285
+ content,
286
+ Date.now()]);
287
+ return true;
288
+ }
289
+ /**
290
+ * Get task comments
291
+ */
292
+ function getComments(taskId) {
293
+ const rows = database
294
+ .query(`SELECT * FROM task_comments WHERE task_id = ? ORDER BY timestamp`)
295
+ .all(taskId);
296
+ return rows.map((row) => ({
297
+ id: row.id,
298
+ taskId: row.task_id,
299
+ author: row.author,
300
+ content: row.content,
301
+ timestamp: row.timestamp,
302
+ }));
303
+ }
304
+ // ═════════════════════════════════════════════════════════════════
305
+ // COMMANDS
306
+ // ═════════════════════════════════════════════════════════════════
307
+ pi.registerCommand("task", {
308
+ description: "Create a new task",
309
+ // @ts-ignore Command args property
310
+ args: [
311
+ { name: "title", description: "Task title", required: true },
312
+ { name: "description", description: "Task description", required: false },
313
+ ],
314
+ handler: async (args, ctx) => {
315
+ const parsed = parseArgs(args, [
316
+ CommonArgs.title,
317
+ CommonArgs.description,
318
+ ]);
319
+ const title = parsed.title;
320
+ const description = parsed.description || "";
321
+ if (!title) {
322
+ ctx.ui?.notify?.("Usage: /task <title> [description]", "warning");
323
+ return;
324
+ }
325
+ const task = createTask(title, description, {
326
+ sessionId: currentSessionId,
327
+ });
328
+ // @ts-ignore Notify type
329
+ // @ts-ignore Notify type
330
+ ctx.ui?.notify?.(`✅ Created task: ${task.title}\nID: ${task.id.slice(0, 20)}...\nStatus: ${task.status}`,
331
+ // @ts-ignore Notify type
332
+ "success");
333
+ },
334
+ });
335
+ pi.registerCommand("tasks", {
336
+ description: "Show task board",
337
+ handler: async (_args, ctx) => {
338
+ const board = getBoard();
339
+ const lines = ["📋 Task Board\n"];
340
+ for (const col of TASK_COLUMNS) {
341
+ const tasks = board[col.id];
342
+ lines.push(`${col.emoji} ${col.label} (${tasks.length})`);
343
+ if (tasks.length === 0) {
344
+ lines.push(" (empty)");
345
+ }
346
+ else {
347
+ for (const task of tasks.slice(0, 5)) {
348
+ const assignee = task.assignee ? ` @${task.assignee}` : "";
349
+ const priority = task.priority === "critical" ? "🔴" : task.priority === "high" ? "🟡" : "";
350
+ lines.push(` ${priority} ${task.title.slice(0, 40)}${assignee}`);
351
+ }
352
+ if (tasks.length > 5) {
353
+ lines.push(` ... and ${tasks.length - 5} more`);
354
+ }
355
+ }
356
+ lines.push("");
357
+ }
358
+ ctx.ui?.notify?.(lines.join("\n"), "info");
359
+ },
360
+ });
361
+ pi.registerCommand("task-show", {
362
+ description: "Show task details",
363
+ // @ts-ignore Command args property
364
+ args: [{ name: "id", description: "Task ID (or first few characters)", required: true }],
365
+ handler: async (args, ctx) => {
366
+ const parsed = parseArgs(args, [CommonArgs.id]);
367
+ const searchId = parsed.id;
368
+ if (!searchId) {
369
+ ctx.ui?.notify?.("Usage: /task-show <task-id>", "warning");
370
+ return;
371
+ }
372
+ // Try exact match first, then prefix match
373
+ let task = getTask(searchId);
374
+ if (!task) {
375
+ // Search by prefix
376
+ const all = listTasks();
377
+ task = all.find((t) => t.id.startsWith(searchId)) || null;
378
+ }
379
+ if (!task) {
380
+ ctx.ui?.notify?.(`Task not found: ${searchId}`, "error");
381
+ return;
382
+ }
383
+ const col = TASK_COLUMNS.find((c) => c.id === task.status);
384
+ const comments = getComments(task.id);
385
+ let details = `📝 ${task.title}\n` +
386
+ `ID: ${task.id}\n` +
387
+ `Status: ${col?.emoji} ${col?.label}\n` +
388
+ `Priority: ${task.priority}\n` +
389
+ `Assignee: ${task.assignee || "unassigned"}\n` +
390
+ `Tags: ${task.tags.join(", ") || "none"}\n` +
391
+ `Created: ${new Date(task.createdAt).toLocaleString()}\n\n` +
392
+ `Description:\n${task.description || "(no description)"}`;
393
+ if (comments.length > 0) {
394
+ details += `\n\nComments (${comments.length}):`;
395
+ for (const comment of comments.slice(-3)) {
396
+ const time = new Date(comment.timestamp).toLocaleTimeString();
397
+ details += `\n [${time}] ${comment.content.slice(0, 60)}`;
398
+ }
399
+ }
400
+ ctx.ui?.notify?.(details, "info");
401
+ },
402
+ });
403
+ pi.registerCommand("task-move", {
404
+ description: "Move task to a different column",
405
+ // @ts-ignore Command args property
406
+ args: [
407
+ { name: "id", description: "Task ID", required: true },
408
+ { name: "status", description: "New status (backlog|needs-assignment|in-progress|needs-review|blocked|done)", required: true },
409
+ ],
410
+ handler: async (args, ctx) => {
411
+ const parsed = parseArgs(args, [
412
+ { name: "id", description: "Task ID", required: true },
413
+ { name: "status", description: "New status", required: true },
414
+ ]);
415
+ const id = parsed.id;
416
+ const status = parsed.status;
417
+ const validStatuses = [
418
+ "backlog",
419
+ "needs-assignment",
420
+ "in-progress",
421
+ "needs-review",
422
+ "blocked",
423
+ "done",
424
+ ];
425
+ if (!validStatuses.includes(status)) {
426
+ ctx.ui?.notify?.(`Invalid status. Use: ${validStatuses.join(", ")}`, "error");
427
+ return;
428
+ }
429
+ // Find task by prefix
430
+ const all = listTasks();
431
+ const task = all.find((t) => t.id.startsWith(id));
432
+ if (!task) {
433
+ ctx.ui?.notify?.(`Task not found: ${id}`, "error");
434
+ return;
435
+ }
436
+ updateTaskStatus(task.id, status);
437
+ // @ts-ignore Notify type
438
+ ctx.ui?.notify?.(`Moved "${task.title.slice(0, 30)}..." to ${status}`, "success");
439
+ },
440
+ });
441
+ pi.registerCommand("task-assign", {
442
+ description: "Assign task to someone",
443
+ // @ts-ignore Command args property
444
+ args: [
445
+ { name: "id", description: "Task ID", required: true },
446
+ { name: "assignee", description: "Who to assign to", required: true },
447
+ ],
448
+ handler: async (args, ctx) => {
449
+ const parsed = parseArgs(args, [
450
+ { name: "id", description: "Task ID", required: true },
451
+ { name: "assignee", description: "Who to assign to", required: true },
452
+ ]);
453
+ const id = parsed.id;
454
+ const assignee = parsed.assignee;
455
+ const all = listTasks();
456
+ const task = all.find((t) => t.id.startsWith(id));
457
+ if (!task) {
458
+ ctx.ui?.notify?.(`Task not found: ${id}`, "error");
459
+ return;
460
+ }
461
+ assignTask(task.id, assignee);
462
+ // @ts-ignore Notify type
463
+ ctx.ui?.notify?.("success"
464
+ // @ts-ignore Notify type
465
+ );
466
+ },
467
+ });
468
+ pi.registerCommand("task-comment", {
469
+ description: "Add comment to task",
470
+ // @ts-ignore Command args property
471
+ args: [
472
+ { name: "id", description: "Task ID", required: true },
473
+ { name: "content", description: "Comment text", required: true },
474
+ ],
475
+ handler: async (args, ctx) => {
476
+ const parsed = parseArgs(args, [
477
+ { name: "id", description: "Task ID", required: true },
478
+ { name: "content", description: "Comment text", required: true },
479
+ ]);
480
+ const id = parsed.id;
481
+ const content = parsed.content;
482
+ const all = listTasks();
483
+ const task = all.find((t) => t.id.startsWith(id));
484
+ if (!task) {
485
+ ctx.ui?.notify?.(`Task not found: ${id}`, "error");
486
+ return;
487
+ }
488
+ addComment(task.id, content);
489
+ // @ts-ignore Notify type
490
+ ctx.ui?.notify?.(`Comment added to "${task.title.slice(0, 30)}..."`, "success");
491
+ },
492
+ });
493
+ pi.registerCommand("task-delete", {
494
+ description: "Delete a task",
495
+ // @ts-ignore Command args property
496
+ args: [{ name: "id", description: "Task ID", required: true }],
497
+ handler: async (args, ctx) => {
498
+ const parsed = parseArgs(args, [CommonArgs.id]);
499
+ const id = parsed.id;
500
+ const all = listTasks();
501
+ const task = all.find((t) => t.id.startsWith(id));
502
+ if (!task) {
503
+ ctx.ui?.notify?.(`Task not found: ${id}`, "error");
504
+ return;
505
+ }
506
+ // @ts-ignore SQLite binding
507
+ database.run("DELETE FROM tasks WHERE id = ?", task.id);
508
+ // @ts-ignore Notify type
509
+ // @ts-ignore Notify type
510
+ ctx.ui?.notify?.(`Deleted: ${task.title}`, "success");
511
+ },
512
+ });
513
+ // ═════════════════════════════════════════════════════════════════
514
+ // TOOLS
515
+ // ═════════════════════════════════════════════════════════════════
516
+ pi.registerTool({
517
+ name: "task_breakdown",
518
+ description: "Break down a request into subtasks and create them",
519
+ // @ts-ignore TSchema mismatch
520
+ parameters: {
521
+ type: "object",
522
+ properties: {
523
+ request: { type: "string", description: "The original request" },
524
+ subtasks: {
525
+ type: "array",
526
+ items: {
527
+ type: "object",
528
+ properties: {
529
+ title: { type: "string" },
530
+ description: { type: "string" },
531
+ priority: {
532
+ type: "string",
533
+ enum: ["low", "medium", "high", "critical"],
534
+ },
535
+ },
536
+ required: ["title"],
537
+ },
538
+ },
539
+ },
540
+ required: ["request", "subtasks"],
541
+ },
542
+ async execute(args) {
543
+ const { request, subtasks } = args;
544
+ const parentTask = createTask(request, "Auto-generated from breakdown", {
545
+ status: "in-progress",
546
+ sessionId: currentSessionId,
547
+ });
548
+ const created = [];
549
+ for (const st of subtasks) {
550
+ const child = createTask(st.title, st.description || "", {
551
+ parentId: parentTask.id,
552
+ priority: st.priority || "medium",
553
+ status: "backlog",
554
+ sessionId: currentSessionId,
555
+ });
556
+ created.push(child);
557
+ }
558
+ return {
559
+ content: [
560
+ {
561
+ type: "text",
562
+ text: `Created ${created.length} subtasks for: ${request}\n\n${created
563
+ .map((t) => `- ${t.title}`)
564
+ .join("\n")}`,
565
+ },
566
+ ],
567
+ details: {
568
+ parentTask: parentTask.id,
569
+ subtasks: created.map((t) => ({ id: t.id, title: t.title })),
570
+ },
571
+ };
572
+ },
573
+ });
574
+ pi.registerTool({
575
+ name: "task_list",
576
+ description: "List tasks by status",
577
+ // @ts-ignore TSchema mismatch
578
+ parameters: {
579
+ type: "object",
580
+ properties: {
581
+ status: {
582
+ type: "string",
583
+ enum: ["backlog", "needs-assignment", "in-progress", "needs-review", "blocked", "done"],
584
+ },
585
+ limit: { type: "number", default: 20 },
586
+ },
587
+ },
588
+ async execute(args) {
589
+ const tasks = listTasks(args.status ? { status: args.status, sessionId: currentSessionId } : { sessionId: currentSessionId }).slice(0, args.limit || 20);
590
+ return {
591
+ content: [
592
+ {
593
+ type: "text",
594
+ text: tasks.length === 0
595
+ ? "No tasks found"
596
+ : tasks.map((t) => `[${t.status}] ${t.title}`).join("\n"),
597
+ },
598
+ ],
599
+ details: { count: tasks.length, tasks },
600
+ };
601
+ },
602
+ });
603
+ pi.registerTool({
604
+ name: "task_update",
605
+ description: "Update task status or assignee",
606
+ // @ts-ignore TSchema mismatch
607
+ parameters: {
608
+ type: "object",
609
+ properties: {
610
+ taskId: { type: "string" },
611
+ status: {
612
+ type: "string",
613
+ enum: ["backlog", "needs-assignment", "in-progress", "needs-review", "blocked", "done"],
614
+ },
615
+ assignee: { type: "string" },
616
+ note: { type: "string" },
617
+ },
618
+ required: ["taskId"],
619
+ },
620
+ async execute(args) {
621
+ const { taskId, status, assignee, note } = args;
622
+ const task = getTask(taskId);
623
+ if (!task) {
624
+ return {
625
+ content: [{ type: "text", text: "Task not found" }],
626
+ details: { error: "not_found", taskId },
627
+ };
628
+ }
629
+ if (status) {
630
+ updateTaskStatus(taskId, status, note);
631
+ }
632
+ if (assignee) {
633
+ assignTask(taskId, assignee);
634
+ }
635
+ return {
636
+ content: [{ type: "text", text: `Updated task: ${task.title}` }],
637
+ details: { taskId, status, assignee },
638
+ };
639
+ },
640
+ });
641
+ // Status bar item
642
+ // // @ts-ignore ExtensionAPI property
643
+ // pi.registerStatusBarItem("tasks", {
644
+ // render() {
645
+ // const counts = getBoard();
646
+ // const inProgress = counts["in-progress"].length;
647
+ // const blocked = counts["blocked"].length;
648
+ //
649
+ // if (blocked > 0) {
650
+ // return `🚫 ${blocked} blocked`;
651
+ // }
652
+ // if (inProgress > 0) {
653
+ // return `🏗️ ${inProgress} active`;
654
+ // }
655
+ // return "";
656
+ // },
657
+ // });
658
+ //
659
+ console.log("[TaskManager] Extension loaded with Kanban workflow");
660
+ }
661
+ //# sourceMappingURL=task-manager-extension.js.map