kanban-lite 1.2.3 → 1.2.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/dist/cli.js +89 -5
- package/dist/extension.js +88 -4
- package/dist/mcp-server.js +23 -0
- package/dist/sdk/index.cjs +23 -0
- package/dist/sdk/index.mjs +23 -0
- package/dist/sdk/sdk/KanbanSDK.d.ts +22 -1
- package/dist/sdk/sdk/plugins/index.d.ts +7 -0
- package/dist/sdk/sdk/types.d.ts +11 -26
- package/dist/standalone-webview/index.js +35 -35
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone.js +88 -4
- package/package.json +1 -1
- package/src/cli/index.test.ts +157 -0
- package/src/cli/index.ts +1 -1
- package/src/mcp-server/index.test.ts +76 -0
- package/src/sdk/KanbanSDK.d.ts +21 -1
- package/src/sdk/KanbanSDK.ts +30 -1
- package/src/sdk/__tests__/KanbanSDK.test.ts +79 -24
- package/src/sdk/plugins/index.d.ts +7 -0
- package/src/sdk/plugins/index.ts +7 -0
- package/src/sdk/types.d.ts +9 -25
- package/src/sdk/types.ts +10 -24
- package/src/shared/config.ts +6 -0
- package/src/standalone/__tests__/server.integration.test.ts +81 -2
- package/src/standalone/internal/websocket.ts +13 -3
- package/src/standalone/server.ts +46 -2
- package/tmp/screenshots-workspace/.kanban/.active-card.json +5 -0
- package/tmp/screenshots-workspace/.kanban/boards/default/deleted/1-dddd.md +17 -0
- package/tmp/screenshots-workspace/.kanban/boards/default/deleted/attachments/1.log +1 -0
- package/tmp/screenshots-workspace/.kanban.json +59 -0
package/src/sdk/types.d.ts
CHANGED
|
@@ -468,29 +468,11 @@ export declare class CardStateError extends Error {
|
|
|
468
468
|
constructor(code: CardStateErrorCode, message: string);
|
|
469
469
|
}
|
|
470
470
|
/**
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
* importing `KanbanSDK` directly so they remain decoupled from core internals.
|
|
471
|
+
* @deprecated Use {@link KanbanSDK}. CLI plugin hosts now advertise the full
|
|
472
|
+
* public SDK surface, including `getConfigSnapshot()`, instead of a narrowed
|
|
473
|
+
* webhook-only facade.
|
|
475
474
|
*/
|
|
476
|
-
export
|
|
477
|
-
/**
|
|
478
|
-
* Returns the SDK extension bag contributed by the plugin with the given id,
|
|
479
|
-
* when the host is backed by a full `KanbanSDK` instance.
|
|
480
|
-
*
|
|
481
|
-
* CLI plugins should prefer this extension path when available and fall back
|
|
482
|
-
* to compatibility methods only when running against older or mocked SDK facades.
|
|
483
|
-
*/
|
|
484
|
-
getExtension?<T extends Record<string, unknown> = Record<string, unknown>>(id: string): T | undefined;
|
|
485
|
-
listWebhooks(): Webhook[];
|
|
486
|
-
createWebhook(input: {
|
|
487
|
-
url: string;
|
|
488
|
-
events: string[];
|
|
489
|
-
secret?: string;
|
|
490
|
-
}): Promise<Webhook>;
|
|
491
|
-
updateWebhook(id: string, updates: Partial<Pick<Webhook, 'url' | 'events' | 'secret' | 'active'>>): Promise<Webhook | null>;
|
|
492
|
-
deleteWebhook(id: string): Promise<boolean>;
|
|
493
|
-
}
|
|
475
|
+
export type CliPluginSdk = KanbanSDK;
|
|
494
476
|
/**
|
|
495
477
|
* Runtime context supplied to a {@link KanbanCliPlugin} when it is invoked by
|
|
496
478
|
* the `kl` CLI.
|
|
@@ -503,10 +485,12 @@ export interface CliPluginContext {
|
|
|
503
485
|
*
|
|
504
486
|
* Present when the plugin is invoked through the core `kl` CLI.
|
|
505
487
|
* Absent in isolated unit tests or standalone invocations.
|
|
506
|
-
|
|
507
|
-
|
|
488
|
+
* Plugins may use the full public {@link KanbanSDK} contract here, including
|
|
489
|
+
* extension lookup and `getConfigSnapshot()`, instead of relying on older
|
|
490
|
+
* helper-only SDK facades. Plugins should prefer this over constructing their
|
|
491
|
+
* own SDK so that SDK-level auth policy is honoured.
|
|
508
492
|
*/
|
|
509
|
-
sdk?:
|
|
493
|
+
sdk?: KanbanSDK;
|
|
510
494
|
/**
|
|
511
495
|
* Core-owned CLI auth helper.
|
|
512
496
|
*
|
package/src/sdk/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Card, CardFormAttachment, CardFormDataMap, Priority, ResolvedFormDescriptor } from '../shared/types'
|
|
2
2
|
import type { CapabilitySelections, Webhook } from '../shared/config'
|
|
3
|
+
import type { KanbanSDK } from './KanbanSDK'
|
|
3
4
|
import type { StorageEngine, StorageEngineType } from './plugins/types'
|
|
4
5
|
import type { CardStateCursor } from './plugins'
|
|
5
6
|
|
|
@@ -615,28 +616,11 @@ export class CardStateError extends Error {
|
|
|
615
616
|
}
|
|
616
617
|
|
|
617
618
|
/**
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
* importing `KanbanSDK` directly so they remain decoupled from core internals.
|
|
619
|
+
* @deprecated Use {@link KanbanSDK}. CLI plugin hosts now advertise the full
|
|
620
|
+
* public SDK surface, including `getConfigSnapshot()`, instead of a narrowed
|
|
621
|
+
* webhook-only facade.
|
|
622
622
|
*/
|
|
623
|
-
export
|
|
624
|
-
/**
|
|
625
|
-
* Returns the SDK extension bag contributed by the plugin with the given id,
|
|
626
|
-
* when the host is backed by a full `KanbanSDK` instance.
|
|
627
|
-
*
|
|
628
|
-
* CLI plugins should prefer this extension path when available and fall back
|
|
629
|
-
* to compatibility methods only when running against older or mocked SDK facades.
|
|
630
|
-
*/
|
|
631
|
-
getExtension?<T extends Record<string, unknown> = Record<string, unknown>>(id: string): T | undefined
|
|
632
|
-
listWebhooks(): Webhook[]
|
|
633
|
-
createWebhook(input: { url: string; events: string[]; secret?: string }): Promise<Webhook>
|
|
634
|
-
updateWebhook(
|
|
635
|
-
id: string,
|
|
636
|
-
updates: Partial<Pick<Webhook, 'url' | 'events' | 'secret' | 'active'>>,
|
|
637
|
-
): Promise<Webhook | null>
|
|
638
|
-
deleteWebhook(id: string): Promise<boolean>
|
|
639
|
-
}
|
|
623
|
+
export type CliPluginSdk = KanbanSDK
|
|
640
624
|
|
|
641
625
|
/**
|
|
642
626
|
* Runtime context supplied to a {@link KanbanCliPlugin} when it is invoked by
|
|
@@ -650,10 +634,12 @@ export interface CliPluginContext {
|
|
|
650
634
|
*
|
|
651
635
|
* Present when the plugin is invoked through the core `kl` CLI.
|
|
652
636
|
* Absent in isolated unit tests or standalone invocations.
|
|
653
|
-
* Plugins
|
|
654
|
-
*
|
|
637
|
+
* Plugins may use the full public {@link KanbanSDK} contract here, including
|
|
638
|
+
* extension lookup and `getConfigSnapshot()`, instead of relying on older
|
|
639
|
+
* helper-only SDK facades. Plugins should prefer this over constructing their
|
|
640
|
+
* own SDK so that SDK-level auth policy is honoured.
|
|
655
641
|
*/
|
|
656
|
-
sdk?:
|
|
642
|
+
sdk?: KanbanSDK
|
|
657
643
|
/**
|
|
658
644
|
* Core-owned CLI auth helper.
|
|
659
645
|
*
|
package/src/shared/config.ts
CHANGED
|
@@ -482,6 +482,12 @@ function loadDotEnv(dir: string): void {
|
|
|
482
482
|
* @returns The processed node (same reference for objects/arrays; new primitive for strings).
|
|
483
483
|
*/
|
|
484
484
|
function resolveConfigEnvVars(node: unknown, configFileName: string, nodePath = ''): unknown {
|
|
485
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath)
|
|
486
|
+
|
|
487
|
+
if (isFormDefaultDataPath) {
|
|
488
|
+
return node
|
|
489
|
+
}
|
|
490
|
+
|
|
485
491
|
if (typeof node === 'string') {
|
|
486
492
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName: string) => {
|
|
487
493
|
const envValue = process.env[varName]
|
|
@@ -350,6 +350,86 @@ describe('Standalone Server Integration', () => {
|
|
|
350
350
|
}
|
|
351
351
|
})
|
|
352
352
|
|
|
353
|
+
it('passes the resolved public SDK to standalone plugin registration and request contexts', async () => {
|
|
354
|
+
const cleanup = installTempPackage(
|
|
355
|
+
'standalone-sdk-context-test-plugin',
|
|
356
|
+
`module.exports = {
|
|
357
|
+
authIdentityPlugin: {
|
|
358
|
+
manifest: { id: 'standalone-sdk-context-test-plugin', provides: ['auth.identity'] },
|
|
359
|
+
async resolveIdentity() {
|
|
360
|
+
return { subject: 'standalone-sdk-context-test-plugin' }
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
standaloneHttpPlugin: {
|
|
364
|
+
manifest: { id: 'standalone-sdk-context-test-plugin', provides: ['standalone.http'] },
|
|
365
|
+
registerMiddleware() {
|
|
366
|
+
return [async (request) => {
|
|
367
|
+
if (!request.route('GET', '/api/plugin-sdk-context')) return false
|
|
368
|
+
request.mergeAuthContext({ actorHint: 'middleware-auth-context' })
|
|
369
|
+
return false
|
|
370
|
+
}]
|
|
371
|
+
},
|
|
372
|
+
registerRoutes(options) {
|
|
373
|
+
return [async (request) => {
|
|
374
|
+
if (!request.route('GET', '/api/plugin-sdk-context')) return false
|
|
375
|
+
const registrationSnapshot = options.sdk ? options.sdk.getConfigSnapshot() : null
|
|
376
|
+
const requestSnapshot = request.sdk.getConfigSnapshot()
|
|
377
|
+
const registrationBoard = options.sdk ? options.sdk.getBoard('default') : null
|
|
378
|
+
const requestBoard = request.sdk.getBoard('default')
|
|
379
|
+
request.res.statusCode = 200
|
|
380
|
+
request.res.setHeader('Content-Type', 'application/json')
|
|
381
|
+
request.res.end(JSON.stringify({
|
|
382
|
+
ok: true,
|
|
383
|
+
registrationSdkAvailable: !!options.sdk,
|
|
384
|
+
registrationPort: registrationSnapshot ? registrationSnapshot.port ?? null : null,
|
|
385
|
+
registrationBoardName: registrationBoard ? registrationBoard.name : null,
|
|
386
|
+
requestPort: requestSnapshot.port ?? null,
|
|
387
|
+
requestBoardName: requestBoard.name,
|
|
388
|
+
workspaceRootMatches: options.workspaceRoot === request.workspaceRoot,
|
|
389
|
+
kanbanDirMatches: options.kanbanDir === request.kanbanDir,
|
|
390
|
+
actorHint: request.getAuthContext().actorHint ?? null,
|
|
391
|
+
}))
|
|
392
|
+
return true
|
|
393
|
+
}]
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
}
|
|
397
|
+
`,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
const workspaceRoot = path.dirname(tempDir)
|
|
401
|
+
const resolvedConfigPath = writeWorkspaceConfig(workspaceRoot, {
|
|
402
|
+
port,
|
|
403
|
+
auth: {
|
|
404
|
+
'auth.identity': { provider: 'standalone-sdk-context-test-plugin' },
|
|
405
|
+
'auth.policy': { provider: 'noop' },
|
|
406
|
+
},
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
const localPort = await getPort()
|
|
410
|
+
const localServer = startServer(tempDir, localPort, webviewDir, resolvedConfigPath)
|
|
411
|
+
await sleep(200)
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
const res = await httpGet(`http://localhost:${localPort}/api/plugin-sdk-context`)
|
|
415
|
+
expect(res.status).toBe(200)
|
|
416
|
+
expect(JSON.parse(res.body)).toEqual({
|
|
417
|
+
ok: true,
|
|
418
|
+
registrationSdkAvailable: true,
|
|
419
|
+
registrationPort: port,
|
|
420
|
+
registrationBoardName: 'Default',
|
|
421
|
+
requestPort: port,
|
|
422
|
+
requestBoardName: 'Default',
|
|
423
|
+
workspaceRootMatches: true,
|
|
424
|
+
kanbanDirMatches: true,
|
|
425
|
+
actorHint: 'middleware-auth-context',
|
|
426
|
+
})
|
|
427
|
+
} finally {
|
|
428
|
+
cleanup()
|
|
429
|
+
await new Promise<void>((resolve) => localServer.close(() => resolve()))
|
|
430
|
+
}
|
|
431
|
+
})
|
|
432
|
+
|
|
353
433
|
it('starts even when the Swagger UI package logo file is unavailable', async () => {
|
|
354
434
|
vi.resetModules()
|
|
355
435
|
|
|
@@ -2561,7 +2641,7 @@ describe('Standalone Server Integration', () => {
|
|
|
2561
2641
|
const kanbanJson = path.join(path.dirname(tempDir), '.kanban.json')
|
|
2562
2642
|
fs.writeFileSync(kanbanJson, JSON.stringify({
|
|
2563
2643
|
version: 2,
|
|
2564
|
-
boards: { default: { name: 'Default', columns: [], nextCardId: 1, defaultStatus: 'backlog', defaultPriority: 'medium' } },
|
|
2644
|
+
boards: { default: { name: 'Default', columns: [{ id: 'backlog', name: 'Backlog' }], nextCardId: 1, defaultStatus: 'backlog', defaultPriority: 'medium' } },
|
|
2565
2645
|
defaultBoard: 'default',
|
|
2566
2646
|
kanbanDirectory: '.kanban',
|
|
2567
2647
|
forms: {
|
|
@@ -2585,7 +2665,6 @@ describe('Standalone Server Integration', () => {
|
|
|
2585
2665
|
}
|
|
2586
2666
|
}, null, 2), 'utf-8')
|
|
2587
2667
|
|
|
2588
|
-
|
|
2589
2668
|
const createRes = await httpRequest('POST', `http://localhost:${port}/api/tasks`, {
|
|
2590
2669
|
content: '# Interpolation Regression Card',
|
|
2591
2670
|
status: 'backlog',
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
import * as http from 'http'
|
|
1
2
|
import { extractAuthContext, getAuthErrorLike } from '../authUtils'
|
|
3
|
+
import type { AuthContext } from '../../sdk/types'
|
|
2
4
|
import type { StandaloneContext } from '../context'
|
|
3
5
|
import { clearClientEditingCard, setClientEditingCard } from '../broadcastService'
|
|
4
6
|
import { handleMessage } from '../messageHandlers'
|
|
5
7
|
|
|
6
|
-
export function attachWebSocketHandlers(
|
|
7
|
-
ctx
|
|
8
|
-
|
|
8
|
+
export function attachWebSocketHandlers(
|
|
9
|
+
ctx: StandaloneContext,
|
|
10
|
+
resolveAuthContext?: (req: http.IncomingMessage) => Promise<AuthContext>,
|
|
11
|
+
): void {
|
|
12
|
+
ctx.wss.on('connection', async (ws, req) => {
|
|
13
|
+
let authContext: AuthContext
|
|
14
|
+
try {
|
|
15
|
+
authContext = resolveAuthContext ? await resolveAuthContext(req) : extractAuthContext(req)
|
|
16
|
+
} catch {
|
|
17
|
+
authContext = extractAuthContext(req)
|
|
18
|
+
}
|
|
9
19
|
setClientEditingCard(ctx, ws, null)
|
|
10
20
|
ws.on('message', (data) => {
|
|
11
21
|
let message: unknown
|
package/src/standalone/server.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { createStandaloneRuntime, getIndexHtml } from './internal/runtime'
|
|
|
13
13
|
import { handleBoardRoutes } from './internal/routes/boards'
|
|
14
14
|
import { handleSystemRoutes } from './internal/routes/system'
|
|
15
15
|
import { handleTaskRoutes } from './internal/routes/tasks'
|
|
16
|
-
import { getRequestAuthContext, mergeRequestAuthContext, setRequestAuthContext } from './authUtils'
|
|
16
|
+
import { extractAuthContext, getRequestAuthContext, mergeRequestAuthContext, setRequestAuthContext } from './authUtils'
|
|
17
17
|
import { attachWebSocketHandlers } from './internal/websocket'
|
|
18
18
|
import { matchRoute, type IncomingMessageWithRawBody } from './httpUtils'
|
|
19
19
|
|
|
@@ -176,6 +176,7 @@ function collectStandaloneHttpHandlers(
|
|
|
176
176
|
): StandaloneHttpHandler[] {
|
|
177
177
|
const plugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? []
|
|
178
178
|
const registrationOptions = {
|
|
179
|
+
sdk: ctx.sdk,
|
|
179
180
|
workspaceRoot: ctx.workspaceRoot,
|
|
180
181
|
kanbanDir: ctx.absoluteKanbanDir,
|
|
181
182
|
capabilities: ctx.sdk.capabilities?.providers ?? {
|
|
@@ -338,7 +339,50 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
338
339
|
reply.hijack()
|
|
339
340
|
})
|
|
340
341
|
|
|
341
|
-
|
|
342
|
+
// Resolve auth context for WebSocket upgrade requests by running the middleware
|
|
343
|
+
// pipeline so session cookies set by auth plugins (e.g. kl-auth-plugin) are honoured.
|
|
344
|
+
const resolveWsAuthContext = async (req: http.IncomingMessage) => {
|
|
345
|
+
const silentRes = (() => {
|
|
346
|
+
const r: Record<string, unknown> = {
|
|
347
|
+
writableEnded: false,
|
|
348
|
+
writeHead() { return r },
|
|
349
|
+
setHeader() { return r },
|
|
350
|
+
removeHeader() { /* no-op */ },
|
|
351
|
+
getHeader() { return undefined },
|
|
352
|
+
getHeaders() { return {} },
|
|
353
|
+
end(..._args: unknown[]) { (r as { writableEnded: boolean }).writableEnded = true; return r },
|
|
354
|
+
write() { return false },
|
|
355
|
+
}
|
|
356
|
+
return r as unknown as import('http').ServerResponse
|
|
357
|
+
})()
|
|
358
|
+
const reqWithBody = req as IncomingMessageWithRawBody
|
|
359
|
+
const wsUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`)
|
|
360
|
+
const requestContext: StandaloneRequestContext = {
|
|
361
|
+
ctx,
|
|
362
|
+
sdk: ctx.sdk,
|
|
363
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
364
|
+
kanbanDir: ctx.absoluteKanbanDir,
|
|
365
|
+
req: reqWithBody,
|
|
366
|
+
res: silentRes,
|
|
367
|
+
url: wsUrl,
|
|
368
|
+
pathname: wsUrl.pathname,
|
|
369
|
+
method: 'GET',
|
|
370
|
+
resolvedWebviewDir,
|
|
371
|
+
indexHtml: resolvedIndexHtml,
|
|
372
|
+
route: createRouteMatcher('GET', wsUrl.pathname, matchRoute),
|
|
373
|
+
isApiRequest: false,
|
|
374
|
+
isPageRequest: false,
|
|
375
|
+
getAuthContext: () => getRequestAuthContext(req),
|
|
376
|
+
setAuthContext: (auth) => setRequestAuthContext(req, auth),
|
|
377
|
+
mergeAuthContext: (auth) => mergeRequestAuthContext(req, auth),
|
|
378
|
+
}
|
|
379
|
+
for (const handler of middlewareHandlers) {
|
|
380
|
+
if (await handler(requestContext)) break
|
|
381
|
+
}
|
|
382
|
+
return extractAuthContext(req)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
attachWebSocketHandlers(ctx, resolveWsAuthContext)
|
|
342
386
|
setupStandaloneLifecycle(ctx, fastify.server)
|
|
343
387
|
|
|
344
388
|
const effectiveConfigPath = resolvedConfigPath ?? configPath(path.dirname(ctx.absoluteKanbanDir))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1
|
|
3
|
+
id: "1"
|
|
4
|
+
status: "deleted"
|
|
5
|
+
priority: "medium"
|
|
6
|
+
assignee: null
|
|
7
|
+
dueDate: null
|
|
8
|
+
created: "2026-03-26T00:54:50.866Z"
|
|
9
|
+
modified: "2026-03-26T00:55:04.504Z"
|
|
10
|
+
completedAt: null
|
|
11
|
+
labels: []
|
|
12
|
+
attachments:
|
|
13
|
+
- "1.log"
|
|
14
|
+
order: "a0"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# dddd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026-03-26T00:55:04.503Z [system] Status changed: `in-progress` → `deleted` {"previousStatus":"in-progress","status":"deleted","activity":{"type":"card.status.changed","qualifiesForUnread":true}}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 2,
|
|
3
|
+
"boards": {
|
|
4
|
+
"default": {
|
|
5
|
+
"name": "Default",
|
|
6
|
+
"columns": [
|
|
7
|
+
{
|
|
8
|
+
"id": "backlog",
|
|
9
|
+
"name": "Backlog",
|
|
10
|
+
"color": "#6b7280"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "todo",
|
|
14
|
+
"name": "To Do",
|
|
15
|
+
"color": "#3b82f6"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "in-progress",
|
|
19
|
+
"name": "In Progress",
|
|
20
|
+
"color": "#f59e0b"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "review",
|
|
24
|
+
"name": "Review",
|
|
25
|
+
"color": "#8b5cf6"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "done",
|
|
29
|
+
"name": "Done",
|
|
30
|
+
"color": "#22c55e"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"nextCardId": 1,
|
|
34
|
+
"defaultStatus": "backlog",
|
|
35
|
+
"defaultPriority": "medium"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"defaultBoard": "default",
|
|
39
|
+
"kanbanDirectory": ".kanban",
|
|
40
|
+
"aiAgent": "claude",
|
|
41
|
+
"defaultPriority": "medium",
|
|
42
|
+
"defaultStatus": "backlog",
|
|
43
|
+
"nextCardId": 2,
|
|
44
|
+
"showPriorityBadges": true,
|
|
45
|
+
"showAssignee": true,
|
|
46
|
+
"showDueDate": true,
|
|
47
|
+
"showLabels": true,
|
|
48
|
+
"showBuildWithAI": true,
|
|
49
|
+
"showFileName": false,
|
|
50
|
+
"compactMode": false,
|
|
51
|
+
"markdownEditorMode": false,
|
|
52
|
+
"showDeletedColumn": false,
|
|
53
|
+
"boardZoom": 100,
|
|
54
|
+
"cardZoom": 100,
|
|
55
|
+
"boardBackgroundMode": "fancy",
|
|
56
|
+
"boardBackgroundPreset": "aurora",
|
|
57
|
+
"port": 2954,
|
|
58
|
+
"labels": {}
|
|
59
|
+
}
|