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.
- package/.editorconfig +9 -0
- package/.github/workflows/ci.yml +59 -0
- package/.github/workflows/release.yml +75 -0
- package/.prettierignore +6 -0
- package/.prettierrc.yaml +4 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/launch.json +17 -0
- package/.vscode/settings.json +21 -0
- package/.vscode/tasks.json +22 -0
- package/.vscodeignore +11 -0
- package/CHANGELOG.md +184 -0
- package/CLAUDE.md +58 -0
- package/CONTRIBUTING.md +114 -0
- package/LICENSE +22 -0
- package/README.md +482 -0
- package/SKILL.md +237 -0
- package/dist/cli.js +8716 -0
- package/dist/extension.js +8463 -0
- package/dist/mcp-server.js +1327 -0
- package/dist/standalone-webview/icons-Dx9MGYqN.js +180 -0
- package/dist/standalone-webview/icons-Dx9MGYqN.js.map +1 -0
- package/dist/standalone-webview/index.js +85 -0
- package/dist/standalone-webview/index.js.map +1 -0
- package/dist/standalone-webview/react-vendor-DkYdDBET.js +25 -0
- package/dist/standalone-webview/react-vendor-DkYdDBET.js.map +1 -0
- package/dist/standalone-webview/style.css +1 -0
- package/dist/standalone.js +7513 -0
- package/dist/webview/icons-Dx9MGYqN.js +180 -0
- package/dist/webview/icons-Dx9MGYqN.js.map +1 -0
- package/dist/webview/index.js +85 -0
- package/dist/webview/index.js.map +1 -0
- package/dist/webview/react-vendor-DkYdDBET.js +25 -0
- package/dist/webview/react-vendor-DkYdDBET.js.map +1 -0
- package/dist/webview/style.css +1 -0
- package/docs/images/board-overview.png +0 -0
- package/docs/images/editor-view.png +0 -0
- package/docs/plans/2026-02-20-kanban-json-config-design.md +74 -0
- package/docs/plans/2026-02-20-kanban-json-config.md +690 -0
- package/eslint.config.mjs +31 -0
- package/package.json +161 -0
- package/postcss.config.js +6 -0
- package/resources/icon-light.png +0 -0
- package/resources/icon-light.svg +105 -0
- package/resources/icon.png +0 -0
- package/resources/icon.svg +105 -0
- package/resources/kanban-dark.svg +21 -0
- package/resources/kanban-light.svg +21 -0
- package/resources/kanban.svg +21 -0
- package/src/cli/index.ts +846 -0
- package/src/extension/FeatureHeaderProvider.ts +370 -0
- package/src/extension/KanbanPanel.ts +973 -0
- package/src/extension/SidebarViewProvider.ts +507 -0
- package/src/extension/featureFileUtils.ts +82 -0
- package/src/extension/index.ts +234 -0
- package/src/mcp-server/index.ts +632 -0
- package/src/sdk/KanbanSDK.ts +349 -0
- package/src/sdk/__tests__/KanbanSDK.test.ts +468 -0
- package/src/sdk/__tests__/parser.test.ts +170 -0
- package/src/sdk/fileUtils.ts +76 -0
- package/src/sdk/index.ts +6 -0
- package/src/sdk/parser.ts +70 -0
- package/src/sdk/types.ts +15 -0
- package/src/shared/config.ts +113 -0
- package/src/shared/editorTypes.ts +14 -0
- package/src/shared/types.ts +120 -0
- package/src/standalone/__tests__/server.integration.test.ts +1916 -0
- package/src/standalone/__tests__/webhooks.test.ts +357 -0
- package/src/standalone/fileUtils.ts +70 -0
- package/src/standalone/index.ts +71 -0
- package/src/standalone/server.ts +1046 -0
- package/src/standalone/webhooks.ts +135 -0
- package/src/webview/App.tsx +469 -0
- package/src/webview/assets/main.css +329 -0
- package/src/webview/assets/standalone-theme.css +130 -0
- package/src/webview/components/ColumnDialog.tsx +119 -0
- package/src/webview/components/CreateFeatureDialog.tsx +524 -0
- package/src/webview/components/DatePicker.tsx +185 -0
- package/src/webview/components/FeatureCard.tsx +186 -0
- package/src/webview/components/FeatureEditor.tsx +623 -0
- package/src/webview/components/KanbanBoard.tsx +144 -0
- package/src/webview/components/KanbanColumn.tsx +159 -0
- package/src/webview/components/MarkdownEditor.tsx +291 -0
- package/src/webview/components/PrioritySelect.tsx +39 -0
- package/src/webview/components/QuickAddInput.tsx +72 -0
- package/src/webview/components/SettingsPanel.tsx +284 -0
- package/src/webview/components/Toolbar.tsx +175 -0
- package/src/webview/components/UndoToast.tsx +70 -0
- package/src/webview/index.html +12 -0
- package/src/webview/lib/utils.ts +6 -0
- package/src/webview/main.tsx +11 -0
- package/src/webview/standalone-main.tsx +13 -0
- package/src/webview/standalone-shim.ts +132 -0
- package/src/webview/standalone.html +12 -0
- package/src/webview/store/index.ts +241 -0
- package/tailwind.config.js +53 -0
- package/tsconfig.json +22 -0
- package/vite.config.ts +36 -0
- package/vite.standalone.config.ts +62 -0
- 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
|
+
)
|