@pi-unipi/unipi 0.1.9 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +29 -0
  2. package/package.json +8 -2
  3. package/packages/ask-user/skills/ask-user/SKILL.md +52 -0
  4. package/packages/autocomplete/src/constants.ts +40 -13
  5. package/packages/autocomplete/src/provider.ts +21 -2
  6. package/packages/core/index.ts +1 -0
  7. package/packages/kanboard/README.md +137 -0
  8. package/packages/kanboard/index.ts +80 -0
  9. package/packages/kanboard/skills/kanboard-doctor/SKILL.md +71 -0
  10. package/packages/memory/index.ts +6 -0
  11. package/packages/milestone/README.md +123 -0
  12. package/packages/milestone/index.ts +72 -0
  13. package/packages/milestone/skills/milestone-onboard/SKILL.md +114 -0
  14. package/packages/milestone/skills/milestone-update/SKILL.md +109 -0
  15. package/packages/subagents/src/__tests__/badge-generation.test.ts +244 -0
  16. package/packages/subagents/src/agent-manager.ts +12 -1
  17. package/packages/subagents/src/agent-runner.ts +23 -8
  18. package/packages/subagents/src/conversation-viewer.ts +299 -0
  19. package/packages/subagents/src/index.ts +432 -49
  20. package/packages/subagents/src/types.ts +49 -0
  21. package/packages/subagents/src/widget.ts +332 -72
  22. package/packages/unipi/index.ts +4 -0
  23. package/packages/utility/README.md +15 -1
  24. package/packages/utility/src/commands.ts +101 -0
  25. package/packages/utility/src/index.ts +127 -9
  26. package/packages/utility/src/tui/badge-settings-tui.ts +388 -0
  27. package/packages/utility/src/tui/badge-settings.ts +108 -0
  28. package/packages/utility/src/tui/name-badge-state.ts +299 -0
  29. package/packages/utility/src/tui/name-badge.ts +117 -0
  30. package/packages/workflow/index.ts +1 -1
package/README.md CHANGED
@@ -24,6 +24,8 @@ pi install npm:@pi-unipi/notify
24
24
  pi install npm:@pi-unipi/utility
25
25
  pi install npm:@pi-unipi/mcp
26
26
  pi install npm:@pi-unipi/ask-user
27
+ pi install npm:@pi-unipi/milestone
28
+ pi install npm:@pi-unipi/kanboard
27
29
  ```
28
30
 
29
31
  ## Packages
@@ -43,6 +45,8 @@ pi install npm:@pi-unipi/ask-user
43
45
  | `@pi-unipi/utility` | Environment info, diagnostics, settings inspector, cleanup |
44
46
  | `@pi-unipi/mcp` | MCP server discovery, connection, and tool integration |
45
47
  | `@pi-unipi/ask-user` | Structured user input with options and freeform text |
48
+ | `@pi-unipi/milestone` | Milestone tracking and project progress management |
49
+ | `@pi-unipi/kanboard` | Kanboard visualization server with TUI overlay |
46
50
 
47
51
  ## Commands
48
52
 
@@ -152,6 +156,9 @@ pi install npm:@pi-unipi/ask-user
152
156
  | `/unipi:status` | Show module status |
153
157
  | `/unipi:cleanup` | Clean stale temp files |
154
158
  | `/unipi:reload` | Reload extensions |
159
+ | `/unipi:name-badge` | Toggle session name badge overlay |
160
+ | `/unipi:badge-gen` | Generate session name via background agent |
161
+ | `/unipi:badge-toggle` | Configure badge settings |
155
162
 
156
163
  ### MCP (`/unipi:mcp-*`)
157
164
 
@@ -191,6 +198,24 @@ pi install npm:@pi-unipi/ask-user
191
198
  | `ctx_fetch_and_index` | compactor | Fetch and index web content |
192
199
  | `ctx_stats` | compactor | Show compaction statistics |
193
200
  | `ctx_doctor` | compactor | Diagnose compactor issues |
201
+ | `set_session_name` | utility | Set session name for badge display |
202
+ | `ctx_batch` | utility | Atomic batch execution with rollback |
203
+ | `ctx_env` | utility | Show environment information |
204
+
205
+ ### Milestone (`/unipi:milestone-*`)
206
+
207
+ | Command | Description |
208
+ |---------|-------------|
209
+ | `/unipi:milestone-onboard` | Create MILESTONES.md from existing workflow docs |
210
+ | `/unipi:milestone-update` | Sync MILESTONES.md with completed work |
211
+
212
+ ### Kanboard (`/unipi:kanboard*`)
213
+
214
+ | Command | Description |
215
+ |---------|-------------|
216
+ | `/unipi:kanboard` | Toggle kanboard visualization server |
217
+ | `/unipi:kanboard-doctor` | Diagnose and fix kanboard parser issues |
218
+ | `/unipi:name-gen` | Generate session name badge via background agent |
194
219
 
195
220
  ## How It Works
196
221
 
@@ -220,6 +245,10 @@ pi install npm:@pi-unipi/ask-user
220
245
 
221
246
  **Ask User** provides structured user input with multiple-choice, multi-select, and freeform text options.
222
247
 
248
+ **Milestone** tracks project progress with MILESTONES.md — onboards existing work and syncs with completed tasks.
249
+
250
+ **Kanboard** provides a visualization server with htmx + Alpine.js UI for kanban boards, workflow timelines, and milestone progress. Includes a TUI overlay for quick access.
251
+
223
252
  ## Module Discovery
224
253
 
225
254
  Modules announce presence via `pi.events`. When `@pi-unipi/workflow` detects `@pi-unipi/ralph`, it enables loop integration. Each module works standalone.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/unipi",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "description": "All-in-one extension suite for Pi coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,6 +48,8 @@
48
48
  "node_modules/@pi-unipi/mcp/src/index.ts",
49
49
  "node_modules/@pi-unipi/compactor/src/index.ts",
50
50
  "node_modules/@pi-unipi/notify/index.ts",
51
+ "node_modules/@pi-unipi/milestone/index.ts",
52
+ "node_modules/@pi-unipi/kanboard/index.ts",
51
53
  "node_modules/@pi-unipi/command-enchantment/src/index.ts"
52
54
  ],
53
55
  "skills": [
@@ -59,7 +61,9 @@
59
61
  "node_modules/@pi-unipi/mcp/skills",
60
62
  "node_modules/@pi-unipi/utility/skills",
61
63
  "node_modules/@pi-unipi/compactor/skills",
62
- "node_modules/@pi-unipi/notify/skills"
64
+ "node_modules/@pi-unipi/notify/skills",
65
+ "node_modules/@pi-unipi/milestone/skills",
66
+ "node_modules/@pi-unipi/kanboard/skills"
63
67
  ]
64
68
  },
65
69
  "peerDependencies": {
@@ -81,6 +85,8 @@
81
85
  "@pi-unipi/ralph": "*",
82
86
  "@pi-unipi/subagents": "*",
83
87
  "@pi-unipi/utility": "*",
88
+ "@pi-unipi/milestone": "*",
89
+ "@pi-unipi/kanboard": "*",
84
90
  "@pi-unipi/web-api": "*",
85
91
  "@pi-unipi/workflow": "*"
86
92
  },
@@ -38,6 +38,26 @@ Use the `ask_user` tool to collect structured input from the user.
38
38
  | `allowFreeform` | boolean? | true | Add "Custom response" checkable option |
39
39
  | `timeout` | number? | — | Auto-dismiss after N ms |
40
40
 
41
+ ### Option Properties
42
+
43
+ | Property | Type | Default | Description |
44
+ |----------|------|---------|-------------|
45
+ | `label` | string | required | Display label |
46
+ | `description` | string? | — | Description shown below label |
47
+ | `value` | string? | label | Value returned when selected |
48
+ | `allowCustom` | boolean? | false | Allow user to add custom text for this option (shorthand for `action: "input"`) |
49
+ | `action` | string? | "select" | Special action: `"select"`, `"input"`, `"end_turn"`, `"new_session"` |
50
+ | `prefill` | string? | — | Prefill message for `"new_session"` action |
51
+
52
+ ### Action Types
53
+
54
+ | Action | Behavior |
55
+ |--------|----------|
56
+ | `"select"` | Normal selection (default). Returns immediately. |
57
+ | `"input"` | Enters text input mode. Returns `combined` response with selection + text. |
58
+ | `"end_turn"` | Signals end of agent turn. Returns `end_turn` response kind. |
59
+ | `"new_session"` | Starts a new session. Returns `new_session` response kind with optional `prefill`. |
60
+
41
61
  ## Examples
42
62
 
43
63
  Single choice:
@@ -98,3 +118,35 @@ ask_user({
98
118
  })
99
119
  ```
100
120
  User can check "Auth", "Cache", and "Custom response" to type additional features.
121
+
122
+ With per-option custom text:
123
+ ```
124
+ ask_user({
125
+ question: "Does this look right?",
126
+ options: [
127
+ { label: "Yes", value: "yes" },
128
+ { label: "Partially", value: "partial", allowCustom: true },
129
+ { label: "No", value: "no", allowCustom: true }
130
+ ],
131
+ allowFreeform: false
132
+ })
133
+ ```
134
+ Selecting "Partially" or "No" enters text input so the user can explain what needs to change.
135
+
136
+ With end_turn and new_session actions:
137
+ ```
138
+ ask_user({
139
+ question: "How would you like to proceed?",
140
+ options: [
141
+ { label: "Looks good, proceed", value: "proceed" },
142
+ { label: "I want changes", value: "changes", action: "input" },
143
+ { label: "Done for now", value: "done", action: "end_turn" },
144
+ { label: "Start fresh", value: "new", action: "new_session", prefill: "Let's redesign the..." }
145
+ ],
146
+ allowFreeform: false
147
+ })
148
+ ```
149
+ - "Looks good" returns immediately with selection
150
+ - "I want changes" enters text input mode for the user to explain
151
+ - "Done for now" signals the agent to end its turn
152
+ - "Start fresh" starts a new session with the prefill message
@@ -20,6 +20,7 @@ export const PACKAGE_ORDER: string[] = [
20
20
  "workflow",
21
21
  "ralph",
22
22
  "memory",
23
+ "milestone",
23
24
  "mcp",
24
25
  "utility",
25
26
  "ask-user",
@@ -27,6 +28,7 @@ export const PACKAGE_ORDER: string[] = [
27
28
  "web-api",
28
29
  "compact",
29
30
  "notify",
31
+ "kanboard",
30
32
  ];
31
33
 
32
34
  // ─── Package Colors ──────────────────────────────────────────────────
@@ -35,6 +37,7 @@ export const PACKAGE_COLORS: Record<string, string> = {
35
37
  workflow: `${ESC}[91m`, // Bright Red
36
38
  ralph: `${ESC}[33m`, // Yellow/Orange
37
39
  memory: `${ESC}[93m`, // Bright Yellow
40
+ milestone: `${ESC}[32m`, // Green
38
41
  mcp: `${ESC}[32m`, // Green
39
42
  utility: `${ESC}[36m`, // Cyan
40
43
  "ask-user": `${ESC}[94m`, // Bright Blue
@@ -42,6 +45,7 @@ export const PACKAGE_COLORS: Record<string, string> = {
42
45
  "web-api": `${ESC}[95m`, // Bright Magenta
43
46
  compact: `${ESC}[37m`, // White
44
47
  notify: `${ESC}[96m`, // Bright Cyan
48
+ kanboard: `${ESC}[92m`, // Bright Green
45
49
  };
46
50
 
47
51
  // ─── Command Registry ────────────────────────────────────────────────
@@ -89,13 +93,17 @@ export const COMMAND_REGISTRY: Record<string, string> = {
89
93
  "unipi:mcp-settings": "mcp",
90
94
  "unipi:mcp-reload": "mcp",
91
95
 
92
- // utility (6 commands)
93
- "unipi:continue": "utility",
94
- "unipi:reload": "utility",
95
- "unipi:status": "utility",
96
- "unipi:cleanup": "utility",
97
- "unipi:env": "utility",
98
- "unipi:doctor": "utility",
96
+ // utility (10 commands)
97
+ "unipi:continue": "utility",
98
+ "unipi:reload": "utility",
99
+ "unipi:status": "utility",
100
+ "unipi:cleanup": "utility",
101
+ "unipi:env": "utility",
102
+ "unipi:doctor": "utility",
103
+ "unipi:badge-name": "utility",
104
+ "unipi:badge-gen": "utility",
105
+ "unipi:badge-toggle": "utility",
106
+ "unipi:badge-settings": "utility",
99
107
 
100
108
  // ask-user (1 command)
101
109
  "unipi:ask-user-settings": "ask-user",
@@ -119,11 +127,19 @@ export const COMMAND_REGISTRY: Record<string, string> = {
119
127
  "unipi:compact-search": "compact",
120
128
  "unipi:compact-purge": "compact",
121
129
 
130
+ // milestone (2 commands)
131
+ "unipi:milestone-onboard": "milestone",
132
+ "unipi:milestone-update": "milestone",
133
+
122
134
  // notify (4 commands)
123
135
  "unipi:notify-settings": "notify",
124
136
  "unipi:notify-set-gotify": "notify",
125
137
  "unipi:notify-set-tg": "notify",
126
138
  "unipi:notify-test": "notify",
139
+
140
+ // kanboard (3 commands)
141
+ "unipi:kanboard": "kanboard",
142
+ "unipi:kanboard-doctor": "kanboard",
127
143
  };
128
144
 
129
145
  // ─── Description Map ─────────────────────────────────────────────────
@@ -167,12 +183,18 @@ export const COMMAND_DESCRIPTIONS: Record<string, string> = {
167
183
  "unipi:mcp-settings": "Configure MCP settings",
168
184
  "unipi:mcp-reload": "Reload MCP connections",
169
185
 
170
- "unipi:continue": "Continue the last conversation",
171
- "unipi:reload": "Reload extensions and settings",
172
- "unipi:status": "Show system status",
173
- "unipi:cleanup": "Clean up old sessions and cache",
174
- "unipi:env": "Show environment info",
175
- "unipi:doctor": "Run diagnostics",
186
+ "unipi:continue": "Continue the last conversation",
187
+ "unipi:reload": "Reload extensions and settings",
188
+ "unipi:status": "Show system status",
189
+ "unipi:cleanup": "Clean up old sessions and cache",
190
+ "unipi:env": "Show environment info",
191
+ "unipi:doctor": "Run diagnostics",
192
+ "unipi:badge-name": "Toggle session name badge overlay",
193
+ "unipi:badge-gen": "Generate session name via background agent",
194
+ "unipi:badge-toggle": "Configure badge settings (autoGen, badgeEnabled, agentTool)",
195
+ "unipi:badge-settings": "Configure badge settings via TUI overlay",
196
+ "unipi:kanboard": "Start the kanboard visualization server",
197
+ "unipi:kanboard-doctor": "Diagnose and fix kanboard parser issues",
176
198
 
177
199
  "unipi:ask-user-settings": "Configure ask-user settings",
178
200
 
@@ -196,6 +218,9 @@ export const COMMAND_DESCRIPTIONS: Record<string, string> = {
196
218
  "unipi:notify-set-gotify": "Set up Gotify push notifications",
197
219
  "unipi:notify-set-tg": "Set up Telegram bot notifications",
198
220
  "unipi:notify-test": "Test all enabled notification platforms",
221
+
222
+ "unipi:milestone-onboard": "Create MILESTONES.md from existing workflow docs",
223
+ "unipi:milestone-update": "Sync MILESTONES.md with completed work",
199
224
  };
200
225
 
201
226
  // ─── Package Display Names ───────────────────────────────────────────
@@ -204,6 +229,7 @@ export const PACKAGE_LABELS: Record<string, string> = {
204
229
  workflow: "workflow",
205
230
  ralph: "ralph",
206
231
  memory: "memory",
232
+ milestone: "milestone",
207
233
  mcp: "mcp",
208
234
  utility: "utility",
209
235
  "ask-user": "ask-user",
@@ -211,4 +237,5 @@ export const PACKAGE_LABELS: Record<string, string> = {
211
237
  "web-api": "web-api",
212
238
  compact: "compact",
213
239
  notify: "notify",
240
+ kanboard: "kanboard",
214
241
  };
@@ -52,6 +52,7 @@ function detectNamespaceBoost(query: string): string | null {
52
52
  workflow: "workflow",
53
53
  ralph: "ralph",
54
54
  memory: "memory",
55
+ milestone: "milestone",
55
56
  mcp: "mcp",
56
57
  utility: "utility",
57
58
  "ask-user": "ask-user",
@@ -61,6 +62,8 @@ function detectNamespaceBoost(query: string): string | null {
61
62
  notify: "notify",
62
63
  // Unambiguous short aliases
63
64
  mem: "memory",
65
+ ms: "milestone",
66
+ goal: "milestone",
64
67
  util: "utility",
65
68
  web: "web-api",
66
69
  notification: "notify",
@@ -117,7 +120,18 @@ function getEnhancedUnipiItems(
117
120
  });
118
121
  }
119
122
 
120
- // Sort: boosted package first, then by PACKAGE_ORDER, then alphabetically.
123
+ // Determine match quality for ranking exact > prefix > fuzzy
124
+ const getMatchPriority = (cmd: string): number => {
125
+ const name = isPastUnipiColon
126
+ ? cmd.replace("unipi:", "").toLowerCase()
127
+ : cmd.toLowerCase();
128
+ if (name === query) return 0; // Exact match
129
+ if (name.startsWith(query)) return 1; // Prefix match
130
+ return 2; // Fuzzy match
131
+ };
132
+
133
+ // Sort: boosted package first, then exact > prefix > fuzzy,
134
+ // then by PACKAGE_ORDER, then alphabetically.
121
135
  matched.sort((a, b) => {
122
136
  const pkgA = a[1];
123
137
  const pkgB = b[1];
@@ -129,6 +143,11 @@ function getEnhancedUnipiItems(
129
143
  if (!aIsBoosted && bIsBoosted) return 1;
130
144
  }
131
145
 
146
+ // Rank by match quality: exact > prefix > fuzzy
147
+ const priA = getMatchPriority(a[0]);
148
+ const priB = getMatchPriority(b[0]);
149
+ if (priA !== priB) return priA - priB;
150
+
132
151
  const orderA = PACKAGE_ORDER.indexOf(pkgA);
133
152
  const orderB = PACKAGE_ORDER.indexOf(pkgB);
134
153
  if (orderA !== orderB) return orderA - orderB;
@@ -262,7 +281,7 @@ export function createEnchantedProvider(
262
281
  : null;
263
282
  }
264
283
 
265
- // Merge: non-unipi first, then enhanced unipi (sorted by package)
284
+ // Merge: system commands first, then enhanced unipi (sorted by package)
266
285
  return {
267
286
  items: [...nonUnipiItems, ...enhancedUnipiItems],
268
287
  prefix: effectivePrefix,
@@ -8,3 +8,4 @@ export * from "./constants.js";
8
8
  export * from "./events.js";
9
9
  export * from "./sandbox.js";
10
10
  export * from "./utils.js";
11
+ export * from "./model-cache.js";
@@ -0,0 +1,137 @@
1
+ # @pi-unipi/kanboard
2
+
3
+ Visualization layer for unipi workflow data. Kanboard provides an HTTP server with htmx + Alpine.js UI, modular parsers for all workflow document types, two web pages (Milestones + Workflow), a TUI overlay with tasks list and kanban board, and a doctor skill for parser diagnostics.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Start the kanboard server
9
+ /unipi:kanboard
10
+
11
+ # Diagnose parser issues
12
+ /unipi:kanboard-doctor
13
+ ```
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ kanboard/
19
+ ├── server/ # HTTP server with port allocation
20
+ │ ├── index.ts # Server core (KanboardServer class)
21
+ │ └── routes/ # Route handlers (milestone, workflow)
22
+ ├── parser/ # Document parsers
23
+ │ ├── index.ts # ParserRegistry + createDefaultRegistry()
24
+ │ ├── specs.ts # Spec parser (checklist items)
25
+ │ ├── plans.ts # Plan parser (task statuses)
26
+ │ ├── milestones.ts # Milestone parser
27
+ │ └── remaining.ts # Quick-work, debug, fix, chore, review
28
+ ├── ui/ # Web UI
29
+ │ ├── layouts/ # Base HTML layout
30
+ │ ├── static/ # CSS + JS (style.css, app.js)
31
+ │ ├── components/ # Reusable components
32
+ │ ├── milestone/ # Milestone page renderer
33
+ │ └── workflow/ # Workflow page renderer
34
+ ├── tui/ # TUI overlay
35
+ │ └── kanboard-overlay.ts
36
+ ├── skills/ # Skills
37
+ │ └── kanboard-doctor/ # Parser diagnostics
38
+ ├── commands.ts # Command registration
39
+ ├── types.ts # Shared TypeScript types
40
+ └── index.ts # Extension entry point
41
+ ```
42
+
43
+ ## Web Pages
44
+
45
+ ### Milestones (`/`)
46
+ - Displays MILESTONES.md phases with progress bars
47
+ - Checklist items with status indicators (✓ done, ○ todo)
48
+ - Collapsible sections per phase
49
+ - Copy-to-clipboard for `/unipi:milestone-update`
50
+
51
+ ### Workflow (`/workflow`)
52
+ - Cards grouped by document type (specs, plans, fixes, etc.)
53
+ - Progress indicators per card
54
+ - Alpine.js filtering by status (All, To Do, In Progress, Done)
55
+ - Copy-to-clipboard for relevant commands
56
+
57
+ ## TUI Overlay
58
+
59
+ Two tabs accessible via the kanboard overlay:
60
+
61
+ - **Tasks** — Flat list of all tasks from all documents with status icons
62
+ - **Board** — Kanban columns (To Do / In Progress / Done)
63
+
64
+ ### Controls
65
+ - `j/k` — Navigate up/down
66
+ - `h/l` — Switch columns (Board tab)
67
+ - `Tab` or `b` — Switch between Tasks/Board tabs
68
+ - `t` — Switch to Tasks tab
69
+ - `gg/G` — Jump to top/bottom
70
+ - `q/Esc` — Close overlay
71
+
72
+ ## API Endpoints
73
+
74
+ | Method | Path | Description |
75
+ |--------|------|-------------|
76
+ | GET | `/` | Milestone page |
77
+ | GET | `/workflow` | Workflow page |
78
+ | GET | `/api/milestones` | Milestone JSON data |
79
+ | GET | `/api/workflow` | Workflow JSON data |
80
+ | POST | `/api/docs/:type/:file/items/:line` | Update item status |
81
+
82
+ ## Parser System
83
+
84
+ Kanboard parses 8 document types from `.unipi/docs/`:
85
+
86
+ | Type | Directory | What's Parsed |
87
+ |------|-----------|---------------|
88
+ | Spec | `specs/` | `- [ ]` / `- [x]` checklist items |
89
+ | Plan | `plans/` | `unstarted:` / `in-progress:` / `completed:` statuses |
90
+ | Milestone | `MILESTONES.md` | Phase headers + checklist items |
91
+ | Quick-work | `quick-work/` | Title + checklist items |
92
+ | Debug | `debug/` | Headers + checklists |
93
+ | Fix | `fix/` | Headers + checklists + related debug ref |
94
+ | Chore | `chore/` | Chore steps as checklist items |
95
+ | Review | `reviews/` | Review remarks as checklist items |
96
+
97
+ ### Parser Warnings
98
+
99
+ Parsers are resilient — they collect warnings per file and return partial results:
100
+ - Empty checkbox text
101
+ - Malformed checkboxes
102
+ - Missing frontmatter
103
+ - Unparseable lines
104
+
105
+ Warnings are surfaced in the kanboard-doctor skill.
106
+
107
+ ## Doctor Skill
108
+
109
+ The `kanboard-doctor` skill runs all parsers and produces a diagnostic report:
110
+
111
+ 1. **Run All Parsers** — Parse every document
112
+ 2. **Collect Errors** — Group warnings by file
113
+ 3. **Present Report** — Structured error listing
114
+ 4. **Fix One by One** — Suggest fixes, ask user to confirm
115
+ 5. **Re-validate** — Re-run parser after each fix
116
+
117
+ ## Server Configuration
118
+
119
+ Default configuration (from `@pi-unipi/core`):
120
+
121
+ ```typescript
122
+ KANBOARD_DEFAULTS = {
123
+ PORT: 8165, // Starting port
124
+ MAX_PORT: 8175, // Maximum port to try
125
+ }
126
+ ```
127
+
128
+ - Port allocation: tries 8165, increments on EADDRINUSE
129
+ - PID file: `.unipi/kanboard.pid`
130
+ - Graceful shutdown on SIGINT/SIGTERM
131
+ - Static files served from `ui/static/`
132
+
133
+ ## Dependencies
134
+
135
+ - `@pi-unipi/core` — Shared constants and utilities
136
+ - `@mariozechner/pi-coding-agent` — Extension API
137
+ - `@mariozechner/pi-tui` — TUI overlay API
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @pi-unipi/kanboard — Extension entry
3
+ *
4
+ * Visualization layer for unipi workflow data.
5
+ * HTTP server with htmx + Alpine.js UI, modular parsers, TUI overlay, and kanban board.
6
+ */
7
+
8
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
9
+ import { MODULES, KANBOARD_COMMANDS } from "@pi-unipi/core";
10
+ import { registerCommands } from "./commands.js";
11
+
12
+ /** Package version */
13
+ const VERSION = "0.1.0";
14
+
15
+ export default function (pi: ExtensionAPI): void {
16
+ // Register skills directory
17
+ const skillsDir = new URL("./skills", import.meta.url).pathname;
18
+
19
+ pi.on("resources_discover", async () => {
20
+ return {
21
+ skillPaths: [skillsDir],
22
+ };
23
+ });
24
+
25
+ // Register commands
26
+ registerCommands(pi);
27
+
28
+ // Note: Badge generation on first message is handled by the utility module.
29
+ // Kanboard no longer manages badge generation to avoid duplication.
30
+
31
+ // Register info-screen group
32
+ const globalObj = globalThis as any;
33
+ const registry = globalObj.__unipi_info_registry;
34
+ if (registry) {
35
+ registry.registerGroup({
36
+ id: "kanboard",
37
+ name: "Kanboard",
38
+ icon: "📋",
39
+ priority: 50,
40
+ config: {
41
+ showByDefault: true,
42
+ stats: [
43
+ { id: "status", label: "Server Status", show: true },
44
+ { id: "url", label: "URL", show: true },
45
+ { id: "docs", label: "Documents", show: true },
46
+ { id: "tasks", label: "Tasks", show: true },
47
+ ],
48
+ },
49
+ dataProvider: async () => {
50
+ const { createDefaultRegistry } = await import("./parser/index.js");
51
+ const registry = await createDefaultRegistry();
52
+ const docs = registry.parseAll(".unipi/docs");
53
+ const totalItems = docs.reduce((sum, d) => sum + d.items.length, 0);
54
+ const doneItems = docs.reduce(
55
+ (sum, d) => sum + d.items.filter((i) => i.status === "done").length,
56
+ 0,
57
+ );
58
+
59
+ return {
60
+ status: {
61
+ value: "Ready",
62
+ detail: "Server not running (use /unipi:kanboard to start)",
63
+ },
64
+ url: {
65
+ value: "—",
66
+ detail: "Start server to get URL",
67
+ },
68
+ docs: {
69
+ value: String(docs.length),
70
+ detail: `${docs.length} documents parsed`,
71
+ },
72
+ tasks: {
73
+ value: `${doneItems}/${totalItems}`,
74
+ detail: `${totalItems > 0 ? Math.round((doneItems / totalItems) * 100) : 0}% complete`,
75
+ },
76
+ };
77
+ },
78
+ });
79
+ }
80
+ }
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: kanboard-doctor
3
+ description: "Diagnose and fix kanboard parser issues — validates all workflow documents, reports errors, suggests fixes."
4
+ ---
5
+
6
+ # Kanboard Doctor
7
+
8
+ Diagnose parser issues across all workflow documents. Non-destructive — only suggests fixes, asks user to confirm.
9
+
10
+ ## Phase 1: Run All Parsers
11
+
12
+ Execute each parser against its document type directory:
13
+
14
+ 1. Load the parser registry from `@pi-unipi/kanboard`
15
+ 2. Run `registry.parseAll(".unipi/docs")` to parse all documents
16
+ 3. Collect all `ParsedDoc` results including their `warnings` arrays
17
+
18
+ ## Phase 2: Collect Errors
19
+
20
+ Group warnings and errors by file with line numbers:
21
+
22
+ 1. For each `ParsedDoc` with `warnings.length > 0`:
23
+ - Group by `filePath`
24
+ - Include line numbers where available
25
+ - Categorize: malformed checkboxes, empty fields, parse failures
26
+ 2. Also flag documents that returned 0 items (may indicate parsing failure)
27
+
28
+ ## Phase 3: Present Report
29
+
30
+ Show a structured error report:
31
+
32
+ ```
33
+ 📋 Kanboard Doctor Report
34
+
35
+ Files scanned: N
36
+ Files with issues: M
37
+
38
+ 📄 .unipi/docs/specs/example.md
39
+ ⚠ Line 15: Empty checkbox text
40
+ ⚠ Line 23: Malformed checkbox (missing bracket)
41
+
42
+ 📄 .unipi/docs/plans/old-plan.md
43
+ ⚠ Line 5: Empty task name after status
44
+ ```
45
+
46
+ ## Phase 4: Fix One by One
47
+
48
+ For each issue, suggest a fix and ask user to confirm:
49
+
50
+ 1. Show the problematic line with context
51
+ 2. Suggest the corrected version
52
+ 3. Ask: "Apply this fix? (y/n)"
53
+ 4. If yes, apply the fix using the edit tool
54
+ 5. Move to next issue
55
+
56
+ **Non-destructive rules:**
57
+ - Never modify without asking
58
+ - Show before/after for each change
59
+ - Allow skipping individual fixes
60
+ - Allow "fix all" for simple patterns (e.g., trailing whitespace)
61
+
62
+ ## Phase 5: Re-validate
63
+
64
+ After each fix, re-run the parser on the modified file:
65
+
66
+ 1. Parse the file again
67
+ 2. Verify the specific error is resolved
68
+ 3. Check that no new errors were introduced
69
+ 4. Report: "✓ Fixed" or "✗ Still has issues"
70
+
71
+ After all fixes, run full `registry.parseAll()` to confirm clean state.
@@ -75,6 +75,12 @@ export default function (pi: ExtensionAPI) {
75
75
  projectStorage = new MemoryStorage(projectName);
76
76
  try {
77
77
  projectStorage.init();
78
+
79
+ // Sync any orphaned markdown files into the database
80
+ const synced = projectStorage.syncOrphanedFiles();
81
+ if (synced > 0) {
82
+ console.warn(`[unipi/memory] Synced ${synced} orphaned memory files into database`);
83
+ }
78
84
  } catch (err) {
79
85
  console.warn("[unipi/memory] Failed to initialize storage, running without memory:", (err as any)?.message ?? err);
80
86
  projectStorage = null;