kanban-lite 1.0.4

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 (99) hide show
  1. package/.editorconfig +9 -0
  2. package/.github/workflows/ci.yml +59 -0
  3. package/.github/workflows/release.yml +75 -0
  4. package/.prettierignore +6 -0
  5. package/.prettierrc.yaml +4 -0
  6. package/.vscode/extensions.json +3 -0
  7. package/.vscode/launch.json +17 -0
  8. package/.vscode/settings.json +21 -0
  9. package/.vscode/tasks.json +22 -0
  10. package/.vscodeignore +11 -0
  11. package/CHANGELOG.md +184 -0
  12. package/CLAUDE.md +58 -0
  13. package/CONTRIBUTING.md +114 -0
  14. package/LICENSE +22 -0
  15. package/README.md +482 -0
  16. package/SKILL.md +237 -0
  17. package/dist/cli.js +8716 -0
  18. package/dist/extension.js +8463 -0
  19. package/dist/mcp-server.js +1327 -0
  20. package/dist/standalone-webview/icons-Dx9MGYqN.js +180 -0
  21. package/dist/standalone-webview/icons-Dx9MGYqN.js.map +1 -0
  22. package/dist/standalone-webview/index.js +85 -0
  23. package/dist/standalone-webview/index.js.map +1 -0
  24. package/dist/standalone-webview/react-vendor-DkYdDBET.js +25 -0
  25. package/dist/standalone-webview/react-vendor-DkYdDBET.js.map +1 -0
  26. package/dist/standalone-webview/style.css +1 -0
  27. package/dist/standalone.js +7513 -0
  28. package/dist/webview/icons-Dx9MGYqN.js +180 -0
  29. package/dist/webview/icons-Dx9MGYqN.js.map +1 -0
  30. package/dist/webview/index.js +85 -0
  31. package/dist/webview/index.js.map +1 -0
  32. package/dist/webview/react-vendor-DkYdDBET.js +25 -0
  33. package/dist/webview/react-vendor-DkYdDBET.js.map +1 -0
  34. package/dist/webview/style.css +1 -0
  35. package/docs/images/board-overview.png +0 -0
  36. package/docs/images/editor-view.png +0 -0
  37. package/docs/plans/2026-02-20-kanban-json-config-design.md +74 -0
  38. package/docs/plans/2026-02-20-kanban-json-config.md +690 -0
  39. package/eslint.config.mjs +31 -0
  40. package/package.json +161 -0
  41. package/postcss.config.js +6 -0
  42. package/resources/icon-light.png +0 -0
  43. package/resources/icon-light.svg +105 -0
  44. package/resources/icon.png +0 -0
  45. package/resources/icon.svg +105 -0
  46. package/resources/kanban-dark.svg +21 -0
  47. package/resources/kanban-light.svg +21 -0
  48. package/resources/kanban.svg +21 -0
  49. package/src/cli/index.ts +846 -0
  50. package/src/extension/FeatureHeaderProvider.ts +370 -0
  51. package/src/extension/KanbanPanel.ts +973 -0
  52. package/src/extension/SidebarViewProvider.ts +507 -0
  53. package/src/extension/featureFileUtils.ts +82 -0
  54. package/src/extension/index.ts +234 -0
  55. package/src/mcp-server/index.ts +632 -0
  56. package/src/sdk/KanbanSDK.ts +349 -0
  57. package/src/sdk/__tests__/KanbanSDK.test.ts +468 -0
  58. package/src/sdk/__tests__/parser.test.ts +170 -0
  59. package/src/sdk/fileUtils.ts +76 -0
  60. package/src/sdk/index.ts +6 -0
  61. package/src/sdk/parser.ts +70 -0
  62. package/src/sdk/types.ts +15 -0
  63. package/src/shared/config.ts +113 -0
  64. package/src/shared/editorTypes.ts +14 -0
  65. package/src/shared/types.ts +120 -0
  66. package/src/standalone/__tests__/server.integration.test.ts +1916 -0
  67. package/src/standalone/__tests__/webhooks.test.ts +357 -0
  68. package/src/standalone/fileUtils.ts +70 -0
  69. package/src/standalone/index.ts +71 -0
  70. package/src/standalone/server.ts +1046 -0
  71. package/src/standalone/webhooks.ts +135 -0
  72. package/src/webview/App.tsx +469 -0
  73. package/src/webview/assets/main.css +329 -0
  74. package/src/webview/assets/standalone-theme.css +130 -0
  75. package/src/webview/components/ColumnDialog.tsx +119 -0
  76. package/src/webview/components/CreateFeatureDialog.tsx +524 -0
  77. package/src/webview/components/DatePicker.tsx +185 -0
  78. package/src/webview/components/FeatureCard.tsx +186 -0
  79. package/src/webview/components/FeatureEditor.tsx +623 -0
  80. package/src/webview/components/KanbanBoard.tsx +144 -0
  81. package/src/webview/components/KanbanColumn.tsx +159 -0
  82. package/src/webview/components/MarkdownEditor.tsx +291 -0
  83. package/src/webview/components/PrioritySelect.tsx +39 -0
  84. package/src/webview/components/QuickAddInput.tsx +72 -0
  85. package/src/webview/components/SettingsPanel.tsx +284 -0
  86. package/src/webview/components/Toolbar.tsx +175 -0
  87. package/src/webview/components/UndoToast.tsx +70 -0
  88. package/src/webview/index.html +12 -0
  89. package/src/webview/lib/utils.ts +6 -0
  90. package/src/webview/main.tsx +11 -0
  91. package/src/webview/standalone-main.tsx +13 -0
  92. package/src/webview/standalone-shim.ts +132 -0
  93. package/src/webview/standalone.html +12 -0
  94. package/src/webview/store/index.ts +241 -0
  95. package/tailwind.config.js +53 -0
  96. package/tsconfig.json +22 -0
  97. package/vite.config.ts +36 -0
  98. package/vite.standalone.config.ts +62 -0
  99. package/vitest.config.ts +15 -0
@@ -0,0 +1,690 @@
1
+ # .kanban.json Config Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Move all configuration from VSCode settings to a `.kanban.json` file at the project root, auto-created with full defaults on first non-default settings change.
6
+
7
+ **Architecture:** Create a shared `src/shared/config.ts` module with `KanbanConfig` interface, defaults, and read/write helpers. Replace all `vscode.workspace.getConfiguration('kanban-lite')` calls with `readConfig()`. Replace `onDidChangeConfiguration` with file watchers on `.kanban.json`. Remove `contributes.configuration` from `package.json`.
8
+
9
+ **Tech Stack:** TypeScript, Node.js `fs` module, VSCode `FileSystemWatcher` API
10
+
11
+ ---
12
+
13
+ ### Task 1: Create shared config module
14
+
15
+ **Files:**
16
+ - Create: `src/shared/config.ts`
17
+
18
+ **Step 1: Create the config module**
19
+
20
+ ```typescript
21
+ // src/shared/config.ts
22
+ import * as fs from 'fs'
23
+ import * as path from 'path'
24
+ import type { KanbanColumn, CardDisplaySettings, Priority, FeatureStatus } from './types'
25
+
26
+ export interface KanbanConfig {
27
+ featuresDirectory: string
28
+ defaultPriority: Priority
29
+ defaultStatus: FeatureStatus
30
+ columns: KanbanColumn[]
31
+ aiAgent: string
32
+ showPriorityBadges: boolean
33
+ showAssignee: boolean
34
+ showDueDate: boolean
35
+ showLabels: boolean
36
+ showBuildWithAI: boolean
37
+ showFileName: boolean
38
+ compactMode: boolean
39
+ markdownEditorMode: boolean
40
+ }
41
+
42
+ export const DEFAULT_CONFIG: KanbanConfig = {
43
+ featuresDirectory: '.kanban',
44
+ defaultPriority: 'medium',
45
+ defaultStatus: 'backlog',
46
+ columns: [
47
+ { id: 'backlog', name: 'Backlog', color: '#6b7280' },
48
+ { id: 'todo', name: 'To Do', color: '#3b82f6' },
49
+ { id: 'in-progress', name: 'In Progress', color: '#f59e0b' },
50
+ { id: 'review', name: 'Review', color: '#8b5cf6' },
51
+ { id: 'done', name: 'Done', color: '#22c55e' }
52
+ ],
53
+ aiAgent: 'claude',
54
+ showPriorityBadges: true,
55
+ showAssignee: true,
56
+ showDueDate: true,
57
+ showLabels: true,
58
+ showBuildWithAI: true,
59
+ showFileName: false,
60
+ compactMode: false,
61
+ markdownEditorMode: false
62
+ }
63
+
64
+ export const CONFIG_FILENAME = '.kanban.json'
65
+
66
+ export function configPath(workspaceRoot: string): string {
67
+ return path.join(workspaceRoot, CONFIG_FILENAME)
68
+ }
69
+
70
+ export function readConfig(workspaceRoot: string): KanbanConfig {
71
+ const filePath = configPath(workspaceRoot)
72
+ try {
73
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
74
+ return { ...DEFAULT_CONFIG, ...raw }
75
+ } catch {
76
+ return { ...DEFAULT_CONFIG }
77
+ }
78
+ }
79
+
80
+ export function writeConfig(workspaceRoot: string, config: KanbanConfig): void {
81
+ const filePath = configPath(workspaceRoot)
82
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n', 'utf-8')
83
+ }
84
+
85
+ /** Extract CardDisplaySettings from a KanbanConfig */
86
+ export function configToSettings(config: KanbanConfig): CardDisplaySettings {
87
+ return {
88
+ showPriorityBadges: config.showPriorityBadges,
89
+ showAssignee: config.showAssignee,
90
+ showDueDate: config.showDueDate,
91
+ showLabels: config.showLabels,
92
+ showBuildWithAI: config.showBuildWithAI,
93
+ showFileName: config.showFileName,
94
+ compactMode: config.compactMode,
95
+ markdownEditorMode: config.markdownEditorMode,
96
+ defaultPriority: config.defaultPriority,
97
+ defaultStatus: config.defaultStatus
98
+ }
99
+ }
100
+
101
+ /** Merge CardDisplaySettings back into a KanbanConfig */
102
+ export function settingsToConfig(config: KanbanConfig, settings: CardDisplaySettings): KanbanConfig {
103
+ return {
104
+ ...config,
105
+ showPriorityBadges: settings.showPriorityBadges,
106
+ showAssignee: settings.showAssignee,
107
+ showDueDate: settings.showDueDate,
108
+ showLabels: settings.showLabels,
109
+ showFileName: settings.showFileName,
110
+ compactMode: settings.compactMode,
111
+ defaultPriority: settings.defaultPriority,
112
+ defaultStatus: settings.defaultStatus
113
+ }
114
+ }
115
+ ```
116
+
117
+ **Step 2: Commit**
118
+
119
+ ```bash
120
+ git add src/shared/config.ts
121
+ git commit -m "feat: add shared config module for .kanban.json"
122
+ ```
123
+
124
+ ---
125
+
126
+ ### Task 2: Update KanbanPanel.ts to use config module
127
+
128
+ **Files:**
129
+ - Modify: `src/extension/KanbanPanel.ts`
130
+
131
+ **Step 1: Add imports and workspace root helper**
132
+
133
+ At top of file, add:
134
+ ```typescript
135
+ import { readConfig, writeConfig, configToSettings, settingsToConfig, configPath, CONFIG_FILENAME } from '../shared/config'
136
+ import type { KanbanConfig } from '../shared/config'
137
+ ```
138
+
139
+ Add a private helper and field:
140
+ ```typescript
141
+ private _configWatcher: vscode.FileSystemWatcher | undefined
142
+ ```
143
+
144
+ Add helper method:
145
+ ```typescript
146
+ private _getWorkspaceRoot(): string | null {
147
+ const workspaceFolders = vscode.workspace.workspaceFolders
148
+ if (!workspaceFolders || workspaceFolders.length === 0) return null
149
+ return workspaceFolders[0].uri.fsPath
150
+ }
151
+ ```
152
+
153
+ **Step 2: Replace `_getWorkspaceFeaturesDir()`**
154
+
155
+ Replace body with:
156
+ ```typescript
157
+ private _getWorkspaceFeaturesDir(): string | null {
158
+ const root = this._getWorkspaceRoot()
159
+ if (!root) return null
160
+ const config = readConfig(root)
161
+ return path.join(root, config.featuresDirectory)
162
+ }
163
+ ```
164
+
165
+ **Step 3: Replace `_getColumns()`**
166
+
167
+ Replace body with:
168
+ ```typescript
169
+ private _getColumns(): KanbanColumn[] {
170
+ const root = this._getWorkspaceRoot()
171
+ if (!root) return [...DEFAULT_CONFIG.columns]
172
+ const config = readConfig(root)
173
+ return config.columns.map(c => ({ ...c }))
174
+ }
175
+ ```
176
+
177
+ Import `DEFAULT_CONFIG` from config module (already added in step 1).
178
+
179
+ **Step 4: Replace `_sendFeaturesToWebviewWithColumns()`**
180
+
181
+ Replace the method body to read from config instead of `vscode.workspace.getConfiguration`:
182
+ ```typescript
183
+ private _sendFeaturesToWebviewWithColumns(columns: KanbanColumn[]): void {
184
+ const root = this._getWorkspaceRoot()
185
+ const config = root ? readConfig(root) : { ...DEFAULT_CONFIG }
186
+ const settings = configToSettings(config)
187
+
188
+ this._panel.webview.postMessage({
189
+ type: 'init',
190
+ features: this._features,
191
+ columns,
192
+ settings
193
+ })
194
+ }
195
+ ```
196
+
197
+ **Step 5: Replace `_saveSettings()` and remove `_updateConfig()`**
198
+
199
+ Delete `_updateConfig()` entirely. Replace `_saveSettings()` with:
200
+ ```typescript
201
+ private _saveSettings(settings: CardDisplaySettings): void {
202
+ const root = this._getWorkspaceRoot()
203
+ if (!root) return
204
+ const config = readConfig(root)
205
+ const updated = settingsToConfig(config, settings)
206
+ writeConfig(root, updated)
207
+ this._sendFeaturesToWebview()
208
+ }
209
+ ```
210
+
211
+ **Step 6: Replace `_saveColumns()`**
212
+
213
+ Replace with:
214
+ ```typescript
215
+ private _saveColumns(columns: KanbanColumn[]): void {
216
+ const root = this._getWorkspaceRoot()
217
+ if (!root) return
218
+ const config = readConfig(root)
219
+ config.columns = columns
220
+ writeConfig(root, config)
221
+ this._sendFeaturesToWebviewWithColumns(columns)
222
+ }
223
+ ```
224
+
225
+ **Step 7: Replace `onDidChangeConfiguration` with config file watcher**
226
+
227
+ In the constructor, replace lines 186-199 (the `onDidChangeConfiguration` block) with:
228
+ ```typescript
229
+ // Watch .kanban.json for changes
230
+ this._setupConfigWatcher()
231
+ ```
232
+
233
+ Add method:
234
+ ```typescript
235
+ private _setupConfigWatcher(): void {
236
+ if (this._configWatcher) {
237
+ this._configWatcher.dispose()
238
+ }
239
+
240
+ const root = this._getWorkspaceRoot()
241
+ if (!root) return
242
+
243
+ const pattern = new vscode.RelativePattern(root, CONFIG_FILENAME)
244
+ this._configWatcher = vscode.workspace.createFileSystemWatcher(pattern)
245
+
246
+ const handleConfigChange = () => {
247
+ // Re-read config — featuresDirectory may have changed
248
+ const oldFeaturesDir = this._getWorkspaceFeaturesDir()
249
+ // Config is re-read on next call to _getWorkspaceFeaturesDir, _getColumns, etc.
250
+ const newFeaturesDir = this._getWorkspaceFeaturesDir()
251
+ if (oldFeaturesDir !== newFeaturesDir) {
252
+ this._setupFileWatcher()
253
+ this._loadFeatures().then(() => this._sendFeaturesToWebview())
254
+ } else {
255
+ this._sendFeaturesToWebview()
256
+ }
257
+ }
258
+
259
+ this._configWatcher.onDidChange(handleConfigChange, null, this._disposables)
260
+ this._configWatcher.onDidCreate(handleConfigChange, null, this._disposables)
261
+ this._configWatcher.onDidDelete(handleConfigChange, null, this._disposables)
262
+ this._disposables.push(this._configWatcher)
263
+ }
264
+ ```
265
+
266
+ **Step 8: Fix `markdownEditorMode` reads**
267
+
268
+ Replace all `vscode.workspace.getConfiguration('kanban-lite').get<boolean>('markdownEditorMode', false)` calls (in `createFeature`, `openFeature` cases, and `openFeature` method) with:
269
+ ```typescript
270
+ const root = this._getWorkspaceRoot()
271
+ const config = root ? readConfig(root) : DEFAULT_CONFIG
272
+ config.markdownEditorMode
273
+ ```
274
+
275
+ There are 3 spots:
276
+ 1. `case 'createFeature'` (line 102-103)
277
+ 2. `case 'openFeature'` (line 122-123)
278
+ 3. `public openFeature()` method (line 447-448)
279
+
280
+ **Step 9: Fix `aiAgent` read in `_startWithAI()`**
281
+
282
+ Replace line 775-776:
283
+ ```typescript
284
+ const config = vscode.workspace.getConfiguration('kanban-lite')
285
+ const selectedAgent = agent || config.get<string>('aiAgent') || 'claude'
286
+ ```
287
+ With:
288
+ ```typescript
289
+ const root = this._getWorkspaceRoot()
290
+ const kanbanConfig = root ? readConfig(root) : DEFAULT_CONFIG
291
+ const selectedAgent = agent || kanbanConfig.aiAgent || 'claude'
292
+ ```
293
+
294
+ **Step 10: Update `openSettings` case**
295
+
296
+ Replace the `openSettings` message handler (line 144-145). Instead of opening VSCode settings, send the current settings to the webview:
297
+ ```typescript
298
+ case 'openSettings': {
299
+ const root = this._getWorkspaceRoot()
300
+ const config = root ? readConfig(root) : { ...DEFAULT_CONFIG }
301
+ const settings = configToSettings(config)
302
+ this._panel.webview.postMessage({ type: 'showSettings', settings })
303
+ break
304
+ }
305
+ ```
306
+
307
+ **Step 11: Remove all remaining `vscode.workspace.getConfiguration('kanban-lite')` calls**
308
+
309
+ Search the file and verify no `getConfiguration('kanban-lite')` calls remain.
310
+
311
+ **Step 12: Commit**
312
+
313
+ ```bash
314
+ git add src/extension/KanbanPanel.ts
315
+ git commit -m "refactor: KanbanPanel reads config from .kanban.json"
316
+ ```
317
+
318
+ ---
319
+
320
+ ### Task 3: Update SidebarViewProvider.ts
321
+
322
+ **Files:**
323
+ - Modify: `src/extension/SidebarViewProvider.ts`
324
+
325
+ **Step 1: Add imports**
326
+
327
+ ```typescript
328
+ import { readConfig, CONFIG_FILENAME } from '../shared/config'
329
+ ```
330
+
331
+ **Step 2: Replace `_getFeaturesDir()`**
332
+
333
+ ```typescript
334
+ private _getFeaturesDir(): string | null {
335
+ const workspaceFolders = vscode.workspace.workspaceFolders
336
+ if (!workspaceFolders || workspaceFolders.length === 0) return null
337
+ const root = workspaceFolders[0].uri.fsPath
338
+ const config = readConfig(root)
339
+ return path.join(root, config.featuresDirectory)
340
+ }
341
+ ```
342
+
343
+ **Step 3: Replace `_getColumns()`**
344
+
345
+ ```typescript
346
+ private _getColumns(): KanbanColumn[] {
347
+ const workspaceFolders = vscode.workspace.workspaceFolders
348
+ if (!workspaceFolders || workspaceFolders.length === 0) return []
349
+ const root = workspaceFolders[0].uri.fsPath
350
+ const config = readConfig(root)
351
+ return config.columns
352
+ }
353
+ ```
354
+
355
+ **Step 4: Replace `onDidChangeConfiguration` with config file watcher**
356
+
357
+ In the constructor, replace the `onDidChangeConfiguration` block (lines 26-33) with:
358
+ ```typescript
359
+ // Watch .kanban.json for config changes
360
+ const workspaceFolders = vscode.workspace.workspaceFolders
361
+ if (workspaceFolders) {
362
+ const root = workspaceFolders[0].uri.fsPath
363
+ const configPattern = new vscode.RelativePattern(root, CONFIG_FILENAME)
364
+ const configWatcher = vscode.workspace.createFileSystemWatcher(configPattern)
365
+
366
+ const handleConfigChange = () => {
367
+ this._setupFileWatcher()
368
+ this._refresh()
369
+ }
370
+
371
+ configWatcher.onDidChange(handleConfigChange, null, this._disposables)
372
+ configWatcher.onDidCreate(handleConfigChange, null, this._disposables)
373
+ configWatcher.onDidDelete(handleConfigChange, null, this._disposables)
374
+ this._disposables.push(configWatcher)
375
+ }
376
+ ```
377
+
378
+ **Step 5: Commit**
379
+
380
+ ```bash
381
+ git add src/extension/SidebarViewProvider.ts
382
+ git commit -m "refactor: SidebarViewProvider reads config from .kanban.json"
383
+ ```
384
+
385
+ ---
386
+
387
+ ### Task 4: Update FeatureHeaderProvider.ts
388
+
389
+ **Files:**
390
+ - Modify: `src/extension/FeatureHeaderProvider.ts`
391
+
392
+ **Step 1: Add import**
393
+
394
+ ```typescript
395
+ import { readConfig, CONFIG_FILENAME } from '../shared/config'
396
+ ```
397
+
398
+ **Step 2: Replace config reads in `_onActiveEditorChanged()`**
399
+
400
+ Replace lines 182-185:
401
+ ```typescript
402
+ const config = vscode.workspace.getConfiguration('kanban-lite')
403
+ const featuresDirectory = config.get<string>('featuresDirectory') || '.kanban'
404
+ const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
405
+ const fullFeaturesDir = workspaceRoot ? path.join(workspaceRoot, featuresDirectory) : featuresDirectory
406
+ ```
407
+ With:
408
+ ```typescript
409
+ const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
410
+ if (!workspaceRoot) return
411
+ const kanbanConfig = readConfig(workspaceRoot)
412
+ const fullFeaturesDir = path.join(workspaceRoot, kanbanConfig.featuresDirectory)
413
+ ```
414
+
415
+ **Step 3: Replace `onDidChangeConfiguration` with config file watcher**
416
+
417
+ Replace the configuration listener in `register()` (lines 52-59) with a file watcher:
418
+ ```typescript
419
+ // Listen for .kanban.json changes
420
+ const workspaceFolders = vscode.workspace.workspaceFolders
421
+ if (workspaceFolders) {
422
+ const root = workspaceFolders[0].uri.fsPath
423
+ const configPattern = new vscode.RelativePattern(root, CONFIG_FILENAME)
424
+ const configWatcher = vscode.workspace.createFileSystemWatcher(configPattern)
425
+
426
+ const handleConfigChange = () => {
427
+ provider._onActiveEditorChanged(vscode.window.activeTextEditor)
428
+ }
429
+
430
+ configWatcher.onDidChange(handleConfigChange)
431
+ configWatcher.onDidCreate(handleConfigChange)
432
+ configWatcher.onDidDelete(handleConfigChange)
433
+ disposables.push(configWatcher)
434
+ }
435
+ ```
436
+
437
+ **Step 4: Commit**
438
+
439
+ ```bash
440
+ git add src/extension/FeatureHeaderProvider.ts
441
+ git commit -m "refactor: FeatureHeaderProvider reads config from .kanban.json"
442
+ ```
443
+
444
+ ---
445
+
446
+ ### Task 5: Update extension/index.ts
447
+
448
+ **Files:**
449
+ - Modify: `src/extension/index.ts`
450
+
451
+ **Step 1: Add import**
452
+
453
+ ```typescript
454
+ import { readConfig } from '../shared/config'
455
+ ```
456
+
457
+ **Step 2: Replace config read at activation**
458
+
459
+ Replace lines 157-159:
460
+ ```typescript
461
+ const config = vscode.workspace.getConfiguration('kanban-lite')
462
+ const featuresDirectory = config.get<string>('featuresDirectory') || '.kanban'
463
+ const featuresDir = path.join(workspaceFolders[0].uri.fsPath, featuresDirectory)
464
+ ```
465
+ With:
466
+ ```typescript
467
+ const root = workspaceFolders[0].uri.fsPath
468
+ const kanbanConfig = readConfig(root)
469
+ const featuresDir = path.join(root, kanbanConfig.featuresDirectory)
470
+ ```
471
+
472
+ **Step 3: Replace config read in `createFeatureFromPrompts()`**
473
+
474
+ Replace lines 71-73:
475
+ ```typescript
476
+ const config = vscode.workspace.getConfiguration('kanban-lite')
477
+ const featuresDirectory = config.get<string>('featuresDirectory') || '.kanban'
478
+ const featuresDir = path.join(workspaceFolders[0].uri.fsPath, featuresDirectory)
479
+ ```
480
+ With:
481
+ ```typescript
482
+ const root = workspaceFolders[0].uri.fsPath
483
+ const kanbanConfig = readConfig(root)
484
+ const featuresDir = path.join(root, kanbanConfig.featuresDirectory)
485
+ ```
486
+
487
+ **Step 4: Commit**
488
+
489
+ ```bash
490
+ git add src/extension/index.ts
491
+ git commit -m "refactor: extension index reads config from .kanban.json"
492
+ ```
493
+
494
+ ---
495
+
496
+ ### Task 6: Update standalone/server.ts
497
+
498
+ **Files:**
499
+ - Modify: `src/standalone/server.ts`
500
+
501
+ The standalone server currently uses `.kanban-settings.json` inside the features directory. Replace this with the shared config module, but note the standalone receives `featuresDir` as a parameter — we need to derive the workspace root (parent of featuresDir) for config reading.
502
+
503
+ **Step 1: Add imports**
504
+
505
+ ```typescript
506
+ import { readConfig, writeConfig, configToSettings, settingsToConfig, CONFIG_FILENAME } from '../shared/config'
507
+ import type { KanbanConfig } from '../shared/config'
508
+ ```
509
+
510
+ **Step 2: Replace settings persistence logic**
511
+
512
+ Remove:
513
+ - `DEFAULT_COLUMNS` constant (lines 32-38)
514
+ - `DEFAULT_SETTINGS` constant (lines 40-51)
515
+ - `settingsFilePath` variable (line 64)
516
+ - `currentSettings` and `currentColumns` variables (lines 65-66)
517
+ - `loadSettings()` function (lines 68-82)
518
+ - `saveSettingsToFile()` function (lines 84-94)
519
+ - `saveColumns()` function (lines 96-108)
520
+ - `loadSettings()` call (line 110)
521
+
522
+ Replace with:
523
+ ```typescript
524
+ // Derive workspace root from features directory
525
+ // The features dir is typically <root>/<featuresDirectory>, so go up one level
526
+ const workspaceRoot = path.dirname(absoluteFeaturesDir)
527
+
528
+ function getConfig(): KanbanConfig {
529
+ return readConfig(workspaceRoot)
530
+ }
531
+
532
+ function saveConfig(config: KanbanConfig): void {
533
+ writeConfig(workspaceRoot, config)
534
+ }
535
+ ```
536
+
537
+ **Step 3: Update `buildInitMessage()`**
538
+
539
+ ```typescript
540
+ function buildInitMessage(): unknown {
541
+ const config = getConfig()
542
+ const settings = configToSettings(config)
543
+ settings.showBuildWithAI = false
544
+ settings.markdownEditorMode = false
545
+ return {
546
+ type: 'init',
547
+ features,
548
+ columns: config.columns,
549
+ settings
550
+ }
551
+ }
552
+ ```
553
+
554
+ **Step 4: Update `loadFeatures()`**
555
+
556
+ Replace `currentColumns.map(c => c.id)` with `getConfig().columns.map(c => c.id)` (2 occurrences — lines 131 and 244).
557
+
558
+ **Step 5: Update `saveSettings` handler**
559
+
560
+ Replace the `saveSettings` case:
561
+ ```typescript
562
+ case 'saveSettings': {
563
+ const newSettings = msg.settings as CardDisplaySettings
564
+ const config = getConfig()
565
+ const updated = settingsToConfig(config, newSettings)
566
+ updated.showBuildWithAI = false
567
+ updated.markdownEditorMode = false
568
+ saveConfig(updated)
569
+ broadcast(buildInitMessage())
570
+ break
571
+ }
572
+ ```
573
+
574
+ **Step 6: Update column handlers**
575
+
576
+ Replace `addColumn`, `editColumn`, `removeColumn` cases to use `getConfig()` / `saveConfig()`:
577
+ ```typescript
578
+ case 'addColumn': {
579
+ const col = msg.column as { name: string; color: string }
580
+ const config = getConfig()
581
+ const id = col.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
582
+ let uniqueId = id
583
+ let counter = 1
584
+ while (config.columns.some(c => c.id === uniqueId)) {
585
+ uniqueId = `${id}-${counter++}`
586
+ }
587
+ config.columns.push({ id: uniqueId, name: col.name, color: col.color })
588
+ saveConfig(config)
589
+ broadcast(buildInitMessage())
590
+ break
591
+ }
592
+
593
+ case 'editColumn': {
594
+ const columnId = msg.columnId as string
595
+ const updates = msg.updates as { name: string; color: string }
596
+ const config = getConfig()
597
+ if (!config.columns.some(c => c.id === columnId)) break
598
+ config.columns = config.columns.map(c =>
599
+ c.id === columnId ? { ...c, name: updates.name, color: updates.color } : c
600
+ )
601
+ saveConfig(config)
602
+ broadcast(buildInitMessage())
603
+ break
604
+ }
605
+
606
+ case 'removeColumn': {
607
+ const columnId = msg.columnId as string
608
+ const hasFeatures = features.some(f => f.status === columnId)
609
+ if (hasFeatures) break
610
+ const config = getConfig()
611
+ const updated = config.columns.filter(c => c.id !== columnId)
612
+ if (updated.length === 0) break
613
+ config.columns = updated
614
+ saveConfig(config)
615
+ broadcast(buildInitMessage())
616
+ break
617
+ }
618
+ ```
619
+
620
+ **Step 7: Update `openSettings` handler**
621
+
622
+ ```typescript
623
+ case 'openSettings': {
624
+ const config = getConfig()
625
+ const settings = configToSettings(config)
626
+ settings.showBuildWithAI = false
627
+ settings.markdownEditorMode = false
628
+ ws.send(JSON.stringify({
629
+ type: 'showSettings',
630
+ settings
631
+ }))
632
+ break
633
+ }
634
+ ```
635
+
636
+ **Step 8: Commit**
637
+
638
+ ```bash
639
+ git add src/standalone/server.ts
640
+ git commit -m "refactor: standalone server uses shared .kanban.json config"
641
+ ```
642
+
643
+ ---
644
+
645
+ ### Task 7: Remove contributes.configuration from package.json
646
+
647
+ **Files:**
648
+ - Modify: `package.json`
649
+
650
+ **Step 1: Remove the configuration section**
651
+
652
+ Remove the entire `"configuration": { ... }` block from `contributes` (lines 75-227). Keep `viewsContainers`, `views`, `commands`, and `menus`.
653
+
654
+ **Step 2: Verify build**
655
+
656
+ Run: `npm run build:extension`
657
+ Expected: Build succeeds without errors
658
+
659
+ **Step 3: Commit**
660
+
661
+ ```bash
662
+ git add package.json
663
+ git commit -m "refactor: remove contributes.configuration, config now in .kanban.json"
664
+ ```
665
+
666
+ ---
667
+
668
+ ### Task 8: Build and verify
669
+
670
+ **Step 1: Full build**
671
+
672
+ Run: `npm run build`
673
+ Expected: All builds succeed
674
+
675
+ **Step 2: Run tests**
676
+
677
+ Run: `npm test`
678
+ Expected: All tests pass
679
+
680
+ **Step 3: Run type check**
681
+
682
+ Run: `npm run typecheck`
683
+ Expected: No type errors
684
+
685
+ **Step 4: Final commit if any fixes needed**
686
+
687
+ ```bash
688
+ git add -A
689
+ git commit -m "fix: resolve build/type issues from config migration"
690
+ ```
@@ -0,0 +1,31 @@
1
+ import js from '@eslint/js'
2
+ import tseslint from 'typescript-eslint'
3
+ import react from 'eslint-plugin-react'
4
+ import reactHooks from 'eslint-plugin-react-hooks'
5
+
6
+ export default tseslint.config(
7
+ { ignores: ['**/node_modules', '**/dist', '**/out', '**/releases'] },
8
+ js.configs.recommended,
9
+ ...tseslint.configs.recommended,
10
+ {
11
+ files: ['**/*.{ts,tsx}'],
12
+ plugins: {
13
+ react,
14
+ 'react-hooks': reactHooks
15
+ },
16
+ languageOptions: {
17
+ parserOptions: {
18
+ ecmaFeatures: { jsx: true }
19
+ }
20
+ },
21
+ settings: {
22
+ react: { version: 'detect' }
23
+ },
24
+ rules: {
25
+ ...react.configs.recommended.rules,
26
+ ...react.configs['jsx-runtime'].rules,
27
+ ...reactHooks.configs.recommended.rules,
28
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }]
29
+ }
30
+ }
31
+ )