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.
- package/.agents/skills/nextjs-best-practices/SKILL.md +208 -0
- package/.agents/skills/sql-optimization-patterns/SKILL.md +509 -0
- package/HEARTBEAT.md +45 -0
- package/README.md +197 -0
- package/USAGE.md +191 -0
- package/dist/package.json +77 -0
- package/dist/src/agent/pi-adapter.js +307 -0
- package/dist/src/agent/pi-adapter.js.map +1 -0
- package/dist/src/agent/tool-adapter.js +86 -0
- package/dist/src/agent/tool-adapter.js.map +1 -0
- package/dist/src/approval/queue.js +114 -0
- package/dist/src/approval/queue.js.map +1 -0
- package/dist/src/ascii-kobold.js +76 -0
- package/dist/src/ascii-kobold.js.map +1 -0
- package/dist/src/cli/client.js +217 -0
- package/dist/src/cli/client.js.map +1 -0
- package/dist/src/cli/commands/agent.js +272 -0
- package/dist/src/cli/commands/agent.js.map +1 -0
- package/dist/src/cli/commands/chat.js +234 -0
- package/dist/src/cli/commands/chat.js.map +1 -0
- package/dist/src/cli/commands/config.js +202 -0
- package/dist/src/cli/commands/config.js.map +1 -0
- package/dist/src/cli/commands/daemon.js +203 -0
- package/dist/src/cli/commands/daemon.js.map +1 -0
- package/dist/src/cli/commands/gateway.js +184 -0
- package/dist/src/cli/commands/gateway.js.map +1 -0
- package/dist/src/cli/commands/init.js +175 -0
- package/dist/src/cli/commands/init.js.map +1 -0
- package/dist/src/cli/commands/kobold.js +21 -0
- package/dist/src/cli/commands/kobold.js.map +1 -0
- package/dist/src/cli/commands/logs.js +27 -0
- package/dist/src/cli/commands/logs.js.map +1 -0
- package/dist/src/cli/commands/mode.js +121 -0
- package/dist/src/cli/commands/mode.js.map +1 -0
- package/dist/src/cli/commands/persona.js +261 -0
- package/dist/src/cli/commands/persona.js.map +1 -0
- package/dist/src/cli/commands/start.js +66 -0
- package/dist/src/cli/commands/start.js.map +1 -0
- package/dist/src/cli/commands/status.js +117 -0
- package/dist/src/cli/commands/status.js.map +1 -0
- package/dist/src/cli/commands/stop.js +27 -0
- package/dist/src/cli/commands/stop.js.map +1 -0
- package/dist/src/cli/commands/system.js +128 -0
- package/dist/src/cli/commands/system.js.map +1 -0
- package/dist/src/cli/commands/tui.js +103 -0
- package/dist/src/cli/commands/tui.js.map +1 -0
- package/dist/src/cli/commands/update.js +133 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/extensions/discord.js +113 -0
- package/dist/src/cli/extensions/discord.js.map +1 -0
- package/dist/src/cli/extensions/env.js +91 -0
- package/dist/src/cli/extensions/env.js.map +1 -0
- package/dist/src/cli/extensions/heartbeat.js +78 -0
- package/dist/src/cli/extensions/heartbeat.js.map +1 -0
- package/dist/src/cli/index.js +24 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/program.js +70 -0
- package/dist/src/cli/program.js.map +1 -0
- package/dist/src/cli/repl.js +184 -0
- package/dist/src/cli/repl.js.map +1 -0
- package/dist/src/cli/shared/discord-service.js +102 -0
- package/dist/src/cli/shared/discord-service.js.map +1 -0
- package/dist/src/config/index.js +10 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/config/loader.js +401 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/config/paths.js +84 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/config/types.js +8 -0
- package/dist/src/config/types.js.map +1 -0
- package/dist/src/context-detector.js +60 -0
- package/dist/src/context-detector.js.map +1 -0
- package/dist/src/discord/index.js +376 -0
- package/dist/src/discord/index.js.map +1 -0
- package/dist/src/event-bus/index.js +97 -0
- package/dist/src/event-bus/index.js.map +1 -0
- package/dist/src/extensions/command-args.js +68 -0
- package/dist/src/extensions/command-args.js.map +1 -0
- package/dist/src/extensions/core/agent-registry-extension.js +541 -0
- package/dist/src/extensions/core/agent-registry-extension.js.map +1 -0
- package/dist/src/extensions/core/agent-worker.js +148 -0
- package/dist/src/extensions/core/agent-worker.js.map +1 -0
- package/dist/src/extensions/core/compaction-safeguard.js +154 -0
- package/dist/src/extensions/core/compaction-safeguard.js.map +1 -0
- package/dist/src/extensions/core/confirm-destructive.js +43 -0
- package/dist/src/extensions/core/confirm-destructive.js.map +1 -0
- package/dist/src/extensions/core/context-aware-extension.js +124 -0
- package/dist/src/extensions/core/context-aware-extension.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/extension.js +124 -0
- package/dist/src/extensions/core/context-pruning/extension.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/pruner.js +312 -0
- package/dist/src/extensions/core/context-pruning/pruner.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/runtime.js +48 -0
- package/dist/src/extensions/core/context-pruning/runtime.js.map +1 -0
- package/dist/src/extensions/core/context-pruning/settings.js +105 -0
- package/dist/src/extensions/core/context-pruning/settings.js.map +1 -0
- package/dist/src/extensions/core/dirty-repo-guard.js +42 -0
- package/dist/src/extensions/core/dirty-repo-guard.js.map +1 -0
- package/dist/src/extensions/core/discord-channel-extension.js +205 -0
- package/dist/src/extensions/core/discord-channel-extension.js.map +1 -0
- package/dist/src/extensions/core/discord-extension.js +142 -0
- package/dist/src/extensions/core/discord-extension.js.map +1 -0
- package/dist/src/extensions/core/env-loader-extension.js +157 -0
- package/dist/src/extensions/core/env-loader-extension.js.map +1 -0
- package/dist/src/extensions/core/fileops-extension.js +699 -0
- package/dist/src/extensions/core/fileops-extension.js.map +1 -0
- package/dist/src/extensions/core/gateway-extension.js +730 -0
- package/dist/src/extensions/core/gateway-extension.js.map +1 -0
- package/dist/src/extensions/core/git-checkpoint.js +46 -0
- package/dist/src/extensions/core/git-checkpoint.js.map +1 -0
- package/dist/src/extensions/core/handoff-extension.js +206 -0
- package/dist/src/extensions/core/handoff-extension.js.map +1 -0
- package/dist/src/extensions/core/heartbeat-extension.js +373 -0
- package/dist/src/extensions/core/heartbeat-extension.js.map +1 -0
- package/dist/src/extensions/core/mcp-extension.js +413 -0
- package/dist/src/extensions/core/mcp-extension.js.map +1 -0
- package/dist/src/extensions/core/mode-manager-extension.js +562 -0
- package/dist/src/extensions/core/mode-manager-extension.js.map +1 -0
- package/dist/src/extensions/core/multi-channel-extension.js +435 -0
- package/dist/src/extensions/core/multi-channel-extension.js.map +1 -0
- package/dist/src/extensions/core/ollama-provider-extension.js +66 -0
- package/dist/src/extensions/core/ollama-provider-extension.js.map +1 -0
- package/dist/src/extensions/core/onboarding-extension.js +122 -0
- package/dist/src/extensions/core/onboarding-extension.js.map +1 -0
- package/dist/src/extensions/core/persona-loader-extension.js +139 -0
- package/dist/src/extensions/core/persona-loader-extension.js.map +1 -0
- package/dist/src/extensions/core/pi-notify-extension.js +70 -0
- package/dist/src/extensions/core/pi-notify-extension.js.map +1 -0
- package/dist/src/extensions/core/protected-paths.js +24 -0
- package/dist/src/extensions/core/protected-paths.js.map +1 -0
- package/dist/src/extensions/core/questionnaire-extension.js +242 -0
- package/dist/src/extensions/core/questionnaire-extension.js.map +1 -0
- package/dist/src/extensions/core/self-update-extension.js +181 -0
- package/dist/src/extensions/core/self-update-extension.js.map +1 -0
- package/dist/src/extensions/core/session-bridge-extension.js +78 -0
- package/dist/src/extensions/core/session-bridge-extension.js.map +1 -0
- package/dist/src/extensions/core/session-manager-extension.js +319 -0
- package/dist/src/extensions/core/session-manager-extension.js.map +1 -0
- package/dist/src/extensions/core/session-name-extension.js +88 -0
- package/dist/src/extensions/core/session-name-extension.js.map +1 -0
- package/dist/src/extensions/core/session-pruning-extension.js +480 -0
- package/dist/src/extensions/core/session-pruning-extension.js.map +1 -0
- package/dist/src/extensions/core/task-manager-extension.js +661 -0
- package/dist/src/extensions/core/task-manager-extension.js.map +1 -0
- package/dist/src/extensions/core/update-extension.js +438 -0
- package/dist/src/extensions/core/update-extension.js.map +1 -0
- package/dist/src/extensions/core/websearch-extension.js +463 -0
- package/dist/src/extensions/core/websearch-extension.js.map +1 -0
- package/dist/src/extensions/index.js +5 -0
- package/dist/src/extensions/index.js.map +1 -0
- package/dist/src/extensions/loader.js +80 -0
- package/dist/src/extensions/loader.js.map +1 -0
- package/dist/src/gateway/index.js +353 -0
- package/dist/src/gateway/index.js.map +1 -0
- package/dist/src/index.js +150 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/llm/anthropic.js +86 -0
- package/dist/src/llm/anthropic.js.map +1 -0
- package/dist/src/llm/index.js +9 -0
- package/dist/src/llm/index.js.map +1 -0
- package/dist/src/llm/ollama.js +113 -0
- package/dist/src/llm/ollama.js.map +1 -0
- package/dist/src/llm/router.js +145 -0
- package/dist/src/llm/router.js.map +1 -0
- package/dist/src/llm/types.js +7 -0
- package/dist/src/llm/types.js.map +1 -0
- package/dist/src/memory/index.js +5 -0
- package/dist/src/memory/index.js.map +1 -0
- package/dist/src/memory/store.js +91 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/pi-config.js +80 -0
- package/dist/src/pi-config.js.map +1 -0
- package/dist/src/skills/builtin/file.js +184 -0
- package/dist/src/skills/builtin/file.js.map +1 -0
- package/dist/src/skills/builtin/shell.js +100 -0
- package/dist/src/skills/builtin/shell.js.map +1 -0
- package/dist/src/skills/builtin/subagent.js +62 -0
- package/dist/src/skills/builtin/subagent.js.map +1 -0
- package/dist/src/skills/hello.js +42 -0
- package/dist/src/skills/hello.js.map +1 -0
- package/dist/src/skills/index.js +11 -0
- package/dist/src/skills/index.js.map +1 -0
- package/dist/src/skills/loader.js +382 -0
- package/dist/src/skills/loader.js.map +1 -0
- package/dist/src/skills/types.js +8 -0
- package/dist/src/skills/types.js.map +1 -0
- package/dist/src/utils/working-dir.js +71 -0
- package/dist/src/utils/working-dir.js.map +1 -0
- package/package.json +77 -0
- package/skills/1password/SKILL.md +70 -0
- package/skills/1password/references/cli-examples.md +29 -0
- package/skills/1password/references/get-started.md +17 -0
- package/skills/apple-notes/SKILL.md +77 -0
- package/skills/apple-reminders/SKILL.md +118 -0
- package/skills/bear-notes/SKILL.md +107 -0
- package/skills/blogwatcher/SKILL.md +69 -0
- package/skills/blucli/SKILL.md +47 -0
- package/skills/bluebubbles/SKILL.md +131 -0
- package/skills/camsnap/SKILL.md +45 -0
- package/skills/canvas/SKILL.md +198 -0
- package/skills/clawhub/SKILL.md +77 -0
- package/skills/coding-agent/SKILL.md +284 -0
- package/skills/discord/SKILL.md +197 -0
- package/skills/eightctl/SKILL.md +50 -0
- package/skills/food-order/SKILL.md +48 -0
- package/skills/gemini/SKILL.md +43 -0
- package/skills/gh-issues/SKILL.md +865 -0
- package/skills/gifgrep/SKILL.md +79 -0
- package/skills/github/SKILL.md +163 -0
- package/skills/gog/SKILL.md +116 -0
- package/skills/goplaces/SKILL.md +52 -0
- package/skills/healthcheck/SKILL.md +245 -0
- package/skills/himalaya/SKILL.md +257 -0
- package/skills/himalaya/references/configuration.md +184 -0
- package/skills/himalaya/references/message-composition.md +199 -0
- package/skills/imsg/SKILL.md +122 -0
- package/skills/mcporter/SKILL.md +61 -0
- package/skills/model-usage/SKILL.md +69 -0
- package/skills/model-usage/references/codexbar-cli.md +33 -0
- package/skills/model-usage/scripts/model_usage.py +310 -0
- package/skills/nano-banana-pro/SKILL.md +58 -0
- package/skills/nano-banana-pro/scripts/generate_image.py +184 -0
- package/skills/nano-pdf/SKILL.md +38 -0
- package/skills/notion/SKILL.md +172 -0
- package/skills/obsidian/SKILL.md +81 -0
- package/skills/openai-image-gen/SKILL.md +89 -0
- package/skills/openai-image-gen/scripts/gen.py +240 -0
- package/skills/openai-whisper/SKILL.md +38 -0
- package/skills/openai-whisper-api/SKILL.md +52 -0
- package/skills/openai-whisper-api/scripts/transcribe.sh +85 -0
- package/skills/openhue/SKILL.md +112 -0
- package/skills/oracle/SKILL.md +125 -0
- package/skills/ordercli/SKILL.md +78 -0
- package/skills/peekaboo/SKILL.md +190 -0
- package/skills/sag/SKILL.md +87 -0
- package/skills/session-logs/SKILL.md +115 -0
- package/skills/sherpa-onnx-tts/SKILL.md +103 -0
- package/skills/sherpa-onnx-tts/bin/sherpa-onnx-tts +178 -0
- package/skills/skill-creator/SKILL.md +370 -0
- package/skills/skill-creator/license.txt +202 -0
- package/skills/skill-creator/scripts/init_skill.py +378 -0
- package/skills/skill-creator/scripts/package_skill.py +111 -0
- package/skills/skill-creator/scripts/quick_validate.py +101 -0
- package/skills/slack/SKILL.md +144 -0
- package/skills/songsee/SKILL.md +49 -0
- package/skills/sonoscli/SKILL.md +46 -0
- package/skills/spotify-player/SKILL.md +64 -0
- package/skills/summarize/SKILL.md +87 -0
- package/skills/things-mac/SKILL.md +86 -0
- package/skills/tmux/SKILL.md +153 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/trello/SKILL.md +95 -0
- package/skills/video-frames/SKILL.md +46 -0
- package/skills/video-frames/scripts/frame.sh +81 -0
- package/skills/voice-call/SKILL.md +45 -0
- package/skills/wacli/SKILL.md +72 -0
- 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
|