kantban-cli 0.1.36 → 0.1.37
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/{chunk-YFBFQAFI.js → chunk-AF72765A.js} +9 -5
- package/dist/{chunk-YFBFQAFI.js.map → chunk-AF72765A.js.map} +1 -1
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/{chunk-GCDCGOWL.js → chunk-MIL7SIPJ.js} +2 -2
- package/dist/{chunk-4VT3TGJ5.js → chunk-UNSNCYTR.js} +29 -1
- package/dist/chunk-UNSNCYTR.js.map +1 -0
- package/dist/{context-7YDNTI3P.js → context-IRTNYVN6.js} +3 -1
- package/dist/{context-7YDNTI3P.js.map → context-IRTNYVN6.js.map} +1 -1
- package/dist/{cron-HKGLRBVM.js → cron-PS2IEPH2.js} +4 -3
- package/dist/{cron-HKGLRBVM.js.map → cron-PS2IEPH2.js.map} +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/gate-proxy-server.js +3 -2
- package/dist/lib/gate-proxy-server.js.map +1 -1
- package/dist/{pipeline-N7Z43P6P.js → pipeline-76ZTI4IN.js} +16 -10
- package/dist/{pipeline-N7Z43P6P.js.map → pipeline-76ZTI4IN.js.map} +1 -1
- package/dist/{pipeline-init-IGZZOOLK.js → pipeline-init-QDHBJGWY.js} +3 -1
- package/dist/{pipeline-init-IGZZOOLK.js.map → pipeline-init-QDHBJGWY.js.map} +1 -1
- package/dist/{status-4GFXMVIM.js → status-3B3BSJBJ.js} +3 -1
- package/dist/{status-4GFXMVIM.js.map → status-3B3BSJBJ.js.map} +1 -1
- package/dist/{work-RECDDPXT.js → work-HZVJJG4A.js} +8 -5
- package/dist/work-HZVJJG4A.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-4VT3TGJ5.js.map +0 -1
- package/dist/work-RECDDPXT.js.map +0 -1
- /package/dist/{chunk-GCDCGOWL.js.map → chunk-MIL7SIPJ.js.map} +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
1
3
|
// src/commands/pipeline-init.ts
|
|
2
4
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
3
5
|
import { join } from "path";
|
|
@@ -100,4 +102,4 @@ Next steps:`);
|
|
|
100
102
|
export {
|
|
101
103
|
runPipelineInit
|
|
102
104
|
};
|
|
103
|
-
//# sourceMappingURL=pipeline-init-
|
|
105
|
+
//# sourceMappingURL=pipeline-init-QDHBJGWY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/pipeline-init.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst GATE_FILE = 'pipeline.gates.yaml';\n\ninterface ProjectDetection {\n type: string;\n testCommand: string;\n typecheckCommand: string;\n lintCommand: string;\n buildCommand: string;\n}\n\nfunction detectProjectType(cwd: string): ProjectDetection {\n if (existsSync(join(cwd, 'package.json'))) {\n const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8')) as {\n scripts?: Record<string, string>;\n };\n const scripts = pkg.scripts ?? {};\n const pm = existsSync(join(cwd, 'pnpm-lock.yaml')) ? 'pnpm'\n : existsSync(join(cwd, 'yarn.lock')) ? 'yarn' : 'npm';\n return {\n type: 'node',\n testCommand: scripts.test ? `${pm} test` : `${pm} vitest run`,\n typecheckCommand: scripts.typecheck ? `${pm} typecheck` : `${pm} tsc --noEmit`,\n lintCommand: scripts.lint ? `${pm} lint` : `${pm} eslint .`,\n buildCommand: scripts.build ? `${pm} build` : 'echo \"no build configured\"',\n };\n }\n if (existsSync(join(cwd, 'Cargo.toml'))) {\n return { type: 'rust', testCommand: 'cargo test', typecheckCommand: 'cargo check', lintCommand: 'cargo clippy', buildCommand: 'cargo build' };\n }\n if (existsSync(join(cwd, 'pyproject.toml')) || existsSync(join(cwd, 'setup.py'))) {\n return { type: 'python', testCommand: 'pytest', typecheckCommand: 'mypy .', lintCommand: 'ruff check .', buildCommand: 'echo \"no build configured\"' };\n }\n if (existsSync(join(cwd, 'go.mod'))) {\n return { type: 'go', testCommand: 'go test ./...', typecheckCommand: 'go vet ./...', lintCommand: 'golangci-lint run', buildCommand: 'go build ./...' };\n }\n return { type: 'unknown', testCommand: 'echo \"configure your test command\"', typecheckCommand: 'echo \"configure your typecheck command\"', lintCommand: 'echo \"configure your lint command\"', buildCommand: 'echo \"configure your build command\"' };\n}\n\n/** YAML-safe single-quoting: escapes internal single quotes by doubling them */\nfunction yamlQuote(s: string): string {\n return `'${s.replace(/'/g, \"''\")}'`;\n}\n\nfunction generateGateFile(detection: ProjectDetection): string {\n return `# pipeline.gates.yaml — Gate definitions for KantBan pipeline\n# Detected project type: ${detection.type}\n#\n# Gates are shell commands that produce pass/fail results.\n# Required gates block ticket movement. Advisory gates show output but don't block.\n\ndefault:\n - name: typecheck\n run: ${yamlQuote(detection.typecheckCommand)}\n required: true\n timeout: 60s\n\n - name: tests\n run: ${yamlQuote(detection.testCommand)}\n required: true\n timeout: 120s\n\n - name: lint\n run: ${yamlQuote(detection.lintCommand)}\n required: false # advisory — output shown but doesn't block\n\n# Column-specific overrides — matched by column name (case-insensitive)\n# columns:\n# implementation:\n# extend: true # inherit default gates, add more\n# gates:\n# - name: build\n# run: ${yamlQuote(detection.buildCommand)}\n# required: true\n# timeout: 120s\n#\n# qa:\n# extend: false # replace default gates entirely\n# gates:\n# - name: e2e\n# run: \"echo 'configure your e2e test command'\"\n# required: true\n# timeout: 300s\n\nsettings:\n # cwd: .\n env:\n CI: \"true\"\n total_timeout: 300s\n budget:\n max_input_tokens: 5000000\n max_output_tokens: 1500000\n warn_pct: 75\n`;\n}\n\nexport async function runPipelineInit(): Promise<void> {\n const cwd = process.cwd();\n const filePath = join(cwd, GATE_FILE);\n\n if (existsSync(filePath)) {\n console.error(`Error: ${GATE_FILE} already exists in ${cwd}`);\n console.error('Edit the existing file or delete it to regenerate.');\n process.exit(1);\n }\n\n const detection = detectProjectType(cwd);\n const content = generateGateFile(detection);\n writeFileSync(filePath, content);\n\n console.log(`Created ${GATE_FILE} (detected: ${detection.type} project)`);\n console.log(`\\nNext steps:`);\n console.log(` 1. Review and customize the gate file`);\n console.log(` 2. Run: kantban pipeline <board-id>`);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/commands/pipeline-init.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst GATE_FILE = 'pipeline.gates.yaml';\n\ninterface ProjectDetection {\n type: string;\n testCommand: string;\n typecheckCommand: string;\n lintCommand: string;\n buildCommand: string;\n}\n\nfunction detectProjectType(cwd: string): ProjectDetection {\n if (existsSync(join(cwd, 'package.json'))) {\n const pkg = JSON.parse(readFileSync(join(cwd, 'package.json'), 'utf-8')) as {\n scripts?: Record<string, string>;\n };\n const scripts = pkg.scripts ?? {};\n const pm = existsSync(join(cwd, 'pnpm-lock.yaml')) ? 'pnpm'\n : existsSync(join(cwd, 'yarn.lock')) ? 'yarn' : 'npm';\n return {\n type: 'node',\n testCommand: scripts.test ? `${pm} test` : `${pm} vitest run`,\n typecheckCommand: scripts.typecheck ? `${pm} typecheck` : `${pm} tsc --noEmit`,\n lintCommand: scripts.lint ? `${pm} lint` : `${pm} eslint .`,\n buildCommand: scripts.build ? `${pm} build` : 'echo \"no build configured\"',\n };\n }\n if (existsSync(join(cwd, 'Cargo.toml'))) {\n return { type: 'rust', testCommand: 'cargo test', typecheckCommand: 'cargo check', lintCommand: 'cargo clippy', buildCommand: 'cargo build' };\n }\n if (existsSync(join(cwd, 'pyproject.toml')) || existsSync(join(cwd, 'setup.py'))) {\n return { type: 'python', testCommand: 'pytest', typecheckCommand: 'mypy .', lintCommand: 'ruff check .', buildCommand: 'echo \"no build configured\"' };\n }\n if (existsSync(join(cwd, 'go.mod'))) {\n return { type: 'go', testCommand: 'go test ./...', typecheckCommand: 'go vet ./...', lintCommand: 'golangci-lint run', buildCommand: 'go build ./...' };\n }\n return { type: 'unknown', testCommand: 'echo \"configure your test command\"', typecheckCommand: 'echo \"configure your typecheck command\"', lintCommand: 'echo \"configure your lint command\"', buildCommand: 'echo \"configure your build command\"' };\n}\n\n/** YAML-safe single-quoting: escapes internal single quotes by doubling them */\nfunction yamlQuote(s: string): string {\n return `'${s.replace(/'/g, \"''\")}'`;\n}\n\nfunction generateGateFile(detection: ProjectDetection): string {\n return `# pipeline.gates.yaml — Gate definitions for KantBan pipeline\n# Detected project type: ${detection.type}\n#\n# Gates are shell commands that produce pass/fail results.\n# Required gates block ticket movement. Advisory gates show output but don't block.\n\ndefault:\n - name: typecheck\n run: ${yamlQuote(detection.typecheckCommand)}\n required: true\n timeout: 60s\n\n - name: tests\n run: ${yamlQuote(detection.testCommand)}\n required: true\n timeout: 120s\n\n - name: lint\n run: ${yamlQuote(detection.lintCommand)}\n required: false # advisory — output shown but doesn't block\n\n# Column-specific overrides — matched by column name (case-insensitive)\n# columns:\n# implementation:\n# extend: true # inherit default gates, add more\n# gates:\n# - name: build\n# run: ${yamlQuote(detection.buildCommand)}\n# required: true\n# timeout: 120s\n#\n# qa:\n# extend: false # replace default gates entirely\n# gates:\n# - name: e2e\n# run: \"echo 'configure your e2e test command'\"\n# required: true\n# timeout: 300s\n\nsettings:\n # cwd: .\n env:\n CI: \"true\"\n total_timeout: 300s\n budget:\n max_input_tokens: 5000000\n max_output_tokens: 1500000\n warn_pct: 75\n`;\n}\n\nexport async function runPipelineInit(): Promise<void> {\n const cwd = process.cwd();\n const filePath = join(cwd, GATE_FILE);\n\n if (existsSync(filePath)) {\n console.error(`Error: ${GATE_FILE} already exists in ${cwd}`);\n console.error('Edit the existing file or delete it to regenerate.');\n process.exit(1);\n }\n\n const detection = detectProjectType(cwd);\n const content = generateGateFile(detection);\n writeFileSync(filePath, content);\n\n console.log(`Created ${GATE_FILE} (detected: ${detection.type} project)`);\n console.log(`\\nNext steps:`);\n console.log(` 1. Review and customize the gate file`);\n console.log(` 2. Run: kantban pipeline <board-id>`);\n}\n"],"mappings":";;;AAAA,SAAS,YAAY,cAAc,qBAAqB;AACxD,SAAS,YAAY;AAErB,IAAM,YAAY;AAUlB,SAAS,kBAAkB,KAA+B;AACxD,MAAI,WAAW,KAAK,KAAK,cAAc,CAAC,GAAG;AACzC,UAAM,MAAM,KAAK,MAAM,aAAa,KAAK,KAAK,cAAc,GAAG,OAAO,CAAC;AAGvE,UAAM,UAAU,IAAI,WAAW,CAAC;AAChC,UAAM,KAAK,WAAW,KAAK,KAAK,gBAAgB,CAAC,IAAI,SACjD,WAAW,KAAK,KAAK,WAAW,CAAC,IAAI,SAAS;AAClD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,aAAa,QAAQ,OAAO,GAAG,EAAE,UAAU,GAAG,EAAE;AAAA,MAChD,kBAAkB,QAAQ,YAAY,GAAG,EAAE,eAAe,GAAG,EAAE;AAAA,MAC/D,aAAa,QAAQ,OAAO,GAAG,EAAE,UAAU,GAAG,EAAE;AAAA,MAChD,cAAc,QAAQ,QAAQ,GAAG,EAAE,WAAW;AAAA,IAChD;AAAA,EACF;AACA,MAAI,WAAW,KAAK,KAAK,YAAY,CAAC,GAAG;AACvC,WAAO,EAAE,MAAM,QAAQ,aAAa,cAAc,kBAAkB,eAAe,aAAa,gBAAgB,cAAc,cAAc;AAAA,EAC9I;AACA,MAAI,WAAW,KAAK,KAAK,gBAAgB,CAAC,KAAK,WAAW,KAAK,KAAK,UAAU,CAAC,GAAG;AAChF,WAAO,EAAE,MAAM,UAAU,aAAa,UAAU,kBAAkB,UAAU,aAAa,gBAAgB,cAAc,6BAA6B;AAAA,EACtJ;AACA,MAAI,WAAW,KAAK,KAAK,QAAQ,CAAC,GAAG;AACnC,WAAO,EAAE,MAAM,MAAM,aAAa,iBAAiB,kBAAkB,gBAAgB,aAAa,qBAAqB,cAAc,iBAAiB;AAAA,EACxJ;AACA,SAAO,EAAE,MAAM,WAAW,aAAa,sCAAsC,kBAAkB,2CAA2C,aAAa,sCAAsC,cAAc,sCAAsC;AACnP;AAGA,SAAS,UAAU,GAAmB;AACpC,SAAO,IAAI,EAAE,QAAQ,MAAM,IAAI,CAAC;AAClC;AAEA,SAAS,iBAAiB,WAAqC;AAC7D,SAAO;AAAA,2BACkB,UAAU,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAO9B,UAAU,UAAU,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,WAKrC,UAAU,UAAU,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,WAKhC,UAAU,UAAU,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAS1B,UAAU,UAAU,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBlD;AAEA,eAAsB,kBAAiC;AACrD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,WAAW,KAAK,KAAK,SAAS;AAEpC,MAAI,WAAW,QAAQ,GAAG;AACxB,YAAQ,MAAM,UAAU,SAAS,sBAAsB,GAAG,EAAE;AAC5D,YAAQ,MAAM,oDAAoD;AAClE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,YAAY,kBAAkB,GAAG;AACvC,QAAM,UAAU,iBAAiB,SAAS;AAC1C,gBAAc,UAAU,OAAO;AAE/B,UAAQ,IAAI,WAAW,SAAS,eAAe,UAAU,IAAI,WAAW;AACxE,UAAQ,IAAI;AAAA,YAAe;AAC3B,UAAQ,IAAI,yCAAyC;AACrD,UAAQ,IAAI,uCAAuC;AACrD;","names":[]}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import "./chunk-DGUM43GV.js";
|
|
2
|
+
|
|
1
3
|
// src/commands/status.ts
|
|
2
4
|
import { readFileSync, existsSync, statSync, readdirSync } from "fs";
|
|
3
5
|
import { homedir } from "os";
|
|
@@ -125,4 +127,4 @@ Warnings: ${stuckCount} stuck, ${wipCount} WIP violations`);
|
|
|
125
127
|
export {
|
|
126
128
|
runStatus
|
|
127
129
|
};
|
|
128
|
-
//# sourceMappingURL=status-
|
|
130
|
+
//# sourceMappingURL=status-3B3BSJBJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/status.ts"],"sourcesContent":["import { readFileSync, existsSync, statSync, readdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { KantBanCLIClient } from '../client.js';\n\n// ---------------------------------------------------------------------------\n// Pipeline runtime info helper\n// ---------------------------------------------------------------------------\n\nfunction formatUptime(ms: number): string {\n const totalMinutes = Math.floor(ms / 60_000);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (hours > 0) return `${String(hours)}h ${String(minutes)}m`;\n return `${String(minutes)}m`;\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction showPipelineInfo(boardId: string): void {\n const pipelineDir = join(homedir(), '.kantban', 'pipelines', boardId);\n const pidFile = join(pipelineDir, 'orchestrator.pid');\n\n if (!existsSync(pidFile)) {\n console.log('\\nPipeline: not running');\n return;\n }\n\n const pidContent = readFileSync(pidFile, 'utf8').trim();\n const pid = Number(pidContent);\n if (isNaN(pid) || pid <= 0) {\n console.log('\\nOrchestrator: not running (invalid PID file)');\n return;\n }\n\n if (!isProcessRunning(pid)) {\n console.log('\\nOrchestrator: not running (stale PID file)');\n return;\n }\n\n // Process is running — calculate uptime from PID file mtime\n const pidStat = statSync(pidFile);\n const uptimeMs = Date.now() - pidStat.mtimeMs;\n console.log(`\\nOrchestrator: running (PID: ${String(pid)}, uptime: ${formatUptime(uptimeMs)})`);\n\n // Scan for recent iteration logs to show active/recent loop info\n if (!existsSync(pipelineDir)) return;\n\n const entries = readdirSync(pipelineDir, { withFileTypes: true });\n const ticketDirs = entries.filter((e) => e.isDirectory());\n\n if (ticketDirs.length === 0) return;\n\n const recentLoops: Array<{ ticket: string; lastIteration: number; outcome: string; timestamp: string }> = [];\n\n for (const dir of ticketDirs) {\n const ticketDir = join(pipelineDir, dir.name);\n const files = readdirSync(ticketDir).filter((f) => f.startsWith('iteration-') && f.endsWith('.log'));\n if (files.length === 0) continue;\n\n // Get the latest iteration log\n files.sort();\n const latestFile = files[files.length - 1]!;\n const logPath = join(ticketDir, latestFile);\n\n try {\n const logContent = readFileSync(logPath, 'utf8');\n const logData = JSON.parse(logContent) as Record<string, unknown>;\n recentLoops.push({\n ticket: dir.name,\n lastIteration: Number(logData['iteration'] ?? 0),\n outcome: String(logData['outcome'] ?? 'unknown'),\n timestamp: String(logData['timestamp'] ?? ''),\n });\n } catch {\n // Skip malformed log files\n }\n }\n\n if (recentLoops.length > 0) {\n // Sort by timestamp descending to show most recent first\n recentLoops.sort((a, b) => b.timestamp.localeCompare(a.timestamp));\n console.log(` Active/recent loops (${String(recentLoops.length)}):`);\n for (const loop of recentLoops.slice(0, 10)) {\n console.log(` ${loop.ticket} iter:${String(loop.lastIteration)} ${loop.outcome}`);\n }\n if (recentLoops.length > 10) {\n console.log(` ... and ${String(recentLoops.length - 10)} more`);\n }\n }\n}\n\nexport async function runStatus(client: KantBanCLIClient, args: string[]): Promise<void> {\n const [boardId] = args;\n if (!boardId) {\n console.error('Usage: kantban status <board-id>');\n process.exit(1);\n }\n\n const projectId = process.env['KANTBAN_PROJECT_ID'];\n if (!projectId) {\n console.error('Error: KANTBAN_PROJECT_ID required');\n process.exit(1);\n }\n\n const data = await client.get<Record<string, unknown>>(\n `/projects/${projectId}/pipeline-context`,\n { boardId },\n );\n\n const columns = data['columns'] as Array<Record<string, unknown>> | undefined;\n const cb = data['circuit_breaker'] as Record<string, unknown> | undefined;\n const board = data['board'] as Record<string, unknown> | undefined;\n\n console.log(`Pipeline Status: ${board?.['name'] ?? 'Unknown Board'}`);\n console.log('\\u2500'.repeat(60));\n\n if (columns) {\n for (const col of columns) {\n const prompt = col['has_prompt'] ? '\\u{1F4C4}' : ' ';\n const name = String(col['name'] ?? '(unnamed)');\n const count = Number(col['ticket_count'] ?? 0);\n const goal = col['goal'] ? ` \\u2014 ${String(col['goal'])}` : '';\n console.log(`${prompt} ${name} (${count} tickets)${goal}`);\n }\n }\n\n if (cb?.['threshold']) {\n console.log(`\\nCircuit Breaker: threshold=${String(cb['threshold'])}`);\n }\n\n // Fetch bottleneck info\n const bottlenecks = await client.get<Record<string, unknown>>(\n `/projects/${projectId}/boards/${boardId}/bottlenecks`,\n );\n const stuckCount = (bottlenecks['stuck_tickets'] as unknown[] | undefined)?.length ?? 0;\n const wipCount = (bottlenecks['wip_violations'] as unknown[] | undefined)?.length ?? 0;\n\n if (stuckCount > 0 || wipCount > 0) {\n console.log(`\\nWarnings: ${stuckCount} stuck, ${wipCount} WIP violations`);\n } else {\n console.log('\\nAll clear.');\n }\n\n // Show pipeline orchestrator runtime info (non-fatal on error)\n try {\n showPipelineInfo(boardId);\n } catch {\n // Pipeline info is supplementary — don't break the status command\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/commands/status.ts"],"sourcesContent":["import { readFileSync, existsSync, statSync, readdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { KantBanCLIClient } from '../client.js';\n\n// ---------------------------------------------------------------------------\n// Pipeline runtime info helper\n// ---------------------------------------------------------------------------\n\nfunction formatUptime(ms: number): string {\n const totalMinutes = Math.floor(ms / 60_000);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (hours > 0) return `${String(hours)}h ${String(minutes)}m`;\n return `${String(minutes)}m`;\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction showPipelineInfo(boardId: string): void {\n const pipelineDir = join(homedir(), '.kantban', 'pipelines', boardId);\n const pidFile = join(pipelineDir, 'orchestrator.pid');\n\n if (!existsSync(pidFile)) {\n console.log('\\nPipeline: not running');\n return;\n }\n\n const pidContent = readFileSync(pidFile, 'utf8').trim();\n const pid = Number(pidContent);\n if (isNaN(pid) || pid <= 0) {\n console.log('\\nOrchestrator: not running (invalid PID file)');\n return;\n }\n\n if (!isProcessRunning(pid)) {\n console.log('\\nOrchestrator: not running (stale PID file)');\n return;\n }\n\n // Process is running — calculate uptime from PID file mtime\n const pidStat = statSync(pidFile);\n const uptimeMs = Date.now() - pidStat.mtimeMs;\n console.log(`\\nOrchestrator: running (PID: ${String(pid)}, uptime: ${formatUptime(uptimeMs)})`);\n\n // Scan for recent iteration logs to show active/recent loop info\n if (!existsSync(pipelineDir)) return;\n\n const entries = readdirSync(pipelineDir, { withFileTypes: true });\n const ticketDirs = entries.filter((e) => e.isDirectory());\n\n if (ticketDirs.length === 0) return;\n\n const recentLoops: Array<{ ticket: string; lastIteration: number; outcome: string; timestamp: string }> = [];\n\n for (const dir of ticketDirs) {\n const ticketDir = join(pipelineDir, dir.name);\n const files = readdirSync(ticketDir).filter((f) => f.startsWith('iteration-') && f.endsWith('.log'));\n if (files.length === 0) continue;\n\n // Get the latest iteration log\n files.sort();\n const latestFile = files[files.length - 1]!;\n const logPath = join(ticketDir, latestFile);\n\n try {\n const logContent = readFileSync(logPath, 'utf8');\n const logData = JSON.parse(logContent) as Record<string, unknown>;\n recentLoops.push({\n ticket: dir.name,\n lastIteration: Number(logData['iteration'] ?? 0),\n outcome: String(logData['outcome'] ?? 'unknown'),\n timestamp: String(logData['timestamp'] ?? ''),\n });\n } catch {\n // Skip malformed log files\n }\n }\n\n if (recentLoops.length > 0) {\n // Sort by timestamp descending to show most recent first\n recentLoops.sort((a, b) => b.timestamp.localeCompare(a.timestamp));\n console.log(` Active/recent loops (${String(recentLoops.length)}):`);\n for (const loop of recentLoops.slice(0, 10)) {\n console.log(` ${loop.ticket} iter:${String(loop.lastIteration)} ${loop.outcome}`);\n }\n if (recentLoops.length > 10) {\n console.log(` ... and ${String(recentLoops.length - 10)} more`);\n }\n }\n}\n\nexport async function runStatus(client: KantBanCLIClient, args: string[]): Promise<void> {\n const [boardId] = args;\n if (!boardId) {\n console.error('Usage: kantban status <board-id>');\n process.exit(1);\n }\n\n const projectId = process.env['KANTBAN_PROJECT_ID'];\n if (!projectId) {\n console.error('Error: KANTBAN_PROJECT_ID required');\n process.exit(1);\n }\n\n const data = await client.get<Record<string, unknown>>(\n `/projects/${projectId}/pipeline-context`,\n { boardId },\n );\n\n const columns = data['columns'] as Array<Record<string, unknown>> | undefined;\n const cb = data['circuit_breaker'] as Record<string, unknown> | undefined;\n const board = data['board'] as Record<string, unknown> | undefined;\n\n console.log(`Pipeline Status: ${board?.['name'] ?? 'Unknown Board'}`);\n console.log('\\u2500'.repeat(60));\n\n if (columns) {\n for (const col of columns) {\n const prompt = col['has_prompt'] ? '\\u{1F4C4}' : ' ';\n const name = String(col['name'] ?? '(unnamed)');\n const count = Number(col['ticket_count'] ?? 0);\n const goal = col['goal'] ? ` \\u2014 ${String(col['goal'])}` : '';\n console.log(`${prompt} ${name} (${count} tickets)${goal}`);\n }\n }\n\n if (cb?.['threshold']) {\n console.log(`\\nCircuit Breaker: threshold=${String(cb['threshold'])}`);\n }\n\n // Fetch bottleneck info\n const bottlenecks = await client.get<Record<string, unknown>>(\n `/projects/${projectId}/boards/${boardId}/bottlenecks`,\n );\n const stuckCount = (bottlenecks['stuck_tickets'] as unknown[] | undefined)?.length ?? 0;\n const wipCount = (bottlenecks['wip_violations'] as unknown[] | undefined)?.length ?? 0;\n\n if (stuckCount > 0 || wipCount > 0) {\n console.log(`\\nWarnings: ${stuckCount} stuck, ${wipCount} WIP violations`);\n } else {\n console.log('\\nAll clear.');\n }\n\n // Show pipeline orchestrator runtime info (non-fatal on error)\n try {\n showPipelineInfo(boardId);\n } catch {\n // Pipeline info is supplementary — don't break the status command\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
crossSpawnOptions
|
|
3
|
-
|
|
2
|
+
crossSpawnOptions,
|
|
3
|
+
resolveCommand
|
|
4
|
+
} from "./chunk-UNSNCYTR.js";
|
|
5
|
+
import "./chunk-DGUM43GV.js";
|
|
4
6
|
|
|
5
7
|
// src/commands/work.ts
|
|
6
8
|
import { spawn } from "child_process";
|
|
@@ -63,10 +65,11 @@ async function runWork(client, args) {
|
|
|
63
65
|
console.log(prompt);
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
66
|
-
const
|
|
68
|
+
const [cmd, prefixArgs] = resolveCommand("claude");
|
|
69
|
+
const child = spawn(cmd, [...prefixArgs, "-p", prompt], {
|
|
67
70
|
stdio: "inherit",
|
|
68
71
|
env: { ...process.env },
|
|
69
|
-
...crossSpawnOptions()
|
|
72
|
+
...prefixArgs.length > 0 ? {} : crossSpawnOptions()
|
|
70
73
|
});
|
|
71
74
|
await new Promise((resolve) => {
|
|
72
75
|
child.on("error", (err) => {
|
|
@@ -83,4 +86,4 @@ async function runWork(client, args) {
|
|
|
83
86
|
export {
|
|
84
87
|
runWork
|
|
85
88
|
};
|
|
86
|
-
//# sourceMappingURL=work-
|
|
89
|
+
//# sourceMappingURL=work-HZVJJG4A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/work.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport type { KantBanCLIClient } from '../client.js';\nimport { crossSpawnOptions, resolveCommand } from '../lib/platform.js';\n\nfunction asRecord(val: unknown): Record<string, unknown> | undefined {\n return typeof val === 'object' && val !== null && !Array.isArray(val)\n ? (val as Record<string, unknown>)\n : undefined;\n}\n\nfunction asStringArray(val: unknown): string[] {\n return Array.isArray(val) ? val.map((v) => String(v)) : [];\n}\n\nexport async function runWork(client: KantBanCLIClient, args: string[]): Promise<void> {\n const [ticketId] = args;\n if (!ticketId) {\n console.error('Usage: kantban work <ticket-id> [--dry-run]');\n process.exit(1);\n }\n\n const projectId = process.env['KANTBAN_PROJECT_ID'];\n if (!projectId) {\n console.error('Error: KANTBAN_PROJECT_ID required');\n process.exit(1);\n }\n\n // Fetch ticket-scope context\n const context = await client.get<Record<string, unknown>>(\n `/projects/${projectId}/pipeline-context`,\n { ticketId },\n );\n\n const ticket = asRecord(context['ticket']);\n const signals = asStringArray(context['signals']);\n const rules = context['transition_rules'] ? String(context['transition_rules']) : undefined;\n\n const title = String(ticket?.['title'] ?? 'Untitled');\n const description = ticket?.['description'] ? String(ticket['description']) : '';\n const ticketIdStr = String(ticket?.['id'] ?? ticketId);\n\n // Build prompt for Claude\n const promptLines = [\n `# Task: ${title}`,\n '',\n ];\n\n if (description) {\n promptLines.push(description, '');\n }\n\n if (signals.length > 0) {\n promptLines.push('## Signals');\n for (const s of signals) {\n promptLines.push(`- ${s}`);\n }\n promptLines.push('');\n }\n\n if (rules) {\n promptLines.push('## Transition Rules', rules, '');\n }\n\n const toolPrefix = context['tool_prefix'] ? String(context['tool_prefix']) : 'kantban_';\n promptLines.push(\n `## Tool Prefix: ${toolPrefix}`,\n `## Ticket ID: ${ticketIdStr}`,\n `## Project ID: ${projectId}`,\n );\n\n const prompt = promptLines.join('\\n');\n\n console.log(`Starting Claude session for: ${title}`);\n console.log(`Ticket: ${ticketIdStr}`);\n\n // Check for dry run\n const dryRun = args.includes('--dry-run');\n if (dryRun) {\n console.log('\\n--- Prompt (dry run) ---');\n console.log(prompt);\n return;\n }\n\n // Spawn Claude with the prompt — resolveCommand bypasses cmd.exe 8191-char limit\n const [cmd, prefixArgs] = resolveCommand('claude');\n const child = spawn(cmd, [...prefixArgs, '-p', prompt], {\n stdio: 'inherit',\n env: { ...process.env },\n ...(prefixArgs.length > 0 ? {} : crossSpawnOptions()),\n });\n\n await new Promise<void>((resolve) => {\n child.on('error', (err) => {\n console.error(`Failed to start claude: ${err.message}`);\n process.exitCode = 1;\n resolve();\n });\n child.on('exit', (code) => {\n process.exitCode = code ?? 0;\n resolve();\n });\n });\n}\n"],"mappings":";;;;;;;AAAA,SAAS,aAAa;AAItB,SAAS,SAAS,KAAmD;AACnE,SAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,CAAC,MAAM,QAAQ,GAAG,IAC/D,MACD;AACN;AAEA,SAAS,cAAc,KAAwB;AAC7C,SAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;AAC3D;AAEA,eAAsB,QAAQ,QAA0B,MAA+B;AACrF,QAAM,CAAC,QAAQ,IAAI;AACnB,MAAI,CAAC,UAAU;AACb,YAAQ,MAAM,6CAA6C;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,YAAY,QAAQ,IAAI,oBAAoB;AAClD,MAAI,CAAC,WAAW;AACd,YAAQ,MAAM,oCAAoC;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,UAAU,MAAM,OAAO;AAAA,IAC3B,aAAa,SAAS;AAAA,IACtB,EAAE,SAAS;AAAA,EACb;AAEA,QAAM,SAAS,SAAS,QAAQ,QAAQ,CAAC;AACzC,QAAM,UAAU,cAAc,QAAQ,SAAS,CAAC;AAChD,QAAM,QAAQ,QAAQ,kBAAkB,IAAI,OAAO,QAAQ,kBAAkB,CAAC,IAAI;AAElF,QAAM,QAAQ,OAAO,SAAS,OAAO,KAAK,UAAU;AACpD,QAAM,cAAc,SAAS,aAAa,IAAI,OAAO,OAAO,aAAa,CAAC,IAAI;AAC9E,QAAM,cAAc,OAAO,SAAS,IAAI,KAAK,QAAQ;AAGrD,QAAM,cAAc;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,aAAa;AACf,gBAAY,KAAK,aAAa,EAAE;AAAA,EAClC;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,gBAAY,KAAK,YAAY;AAC7B,eAAW,KAAK,SAAS;AACvB,kBAAY,KAAK,KAAK,CAAC,EAAE;AAAA,IAC3B;AACA,gBAAY,KAAK,EAAE;AAAA,EACrB;AAEA,MAAI,OAAO;AACT,gBAAY,KAAK,uBAAuB,OAAO,EAAE;AAAA,EACnD;AAEA,QAAM,aAAa,QAAQ,aAAa,IAAI,OAAO,QAAQ,aAAa,CAAC,IAAI;AAC7E,cAAY;AAAA,IACV,mBAAmB,UAAU;AAAA,IAC7B,iBAAiB,WAAW;AAAA,IAC5B,kBAAkB,SAAS;AAAA,EAC7B;AAEA,QAAM,SAAS,YAAY,KAAK,IAAI;AAEpC,UAAQ,IAAI,gCAAgC,KAAK,EAAE;AACnD,UAAQ,IAAI,WAAW,WAAW,EAAE;AAGpC,QAAM,SAAS,KAAK,SAAS,WAAW;AACxC,MAAI,QAAQ;AACV,YAAQ,IAAI,4BAA4B;AACxC,YAAQ,IAAI,MAAM;AAClB;AAAA,EACF;AAGA,QAAM,CAAC,KAAK,UAAU,IAAI,eAAe,QAAQ;AACjD,QAAM,QAAQ,MAAM,KAAK,CAAC,GAAG,YAAY,MAAM,MAAM,GAAG;AAAA,IACtD,OAAO;AAAA,IACP,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,IACtB,GAAI,WAAW,SAAS,IAAI,CAAC,IAAI,kBAAkB;AAAA,EACrD,CAAC;AAED,QAAM,IAAI,QAAc,CAAC,YAAY;AACnC,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,cAAQ,MAAM,2BAA2B,IAAI,OAAO,EAAE;AACtD,cAAQ,WAAW;AACnB,cAAQ;AAAA,IACV,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,cAAQ,WAAW,QAAQ;AAC3B,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/platform.ts"],"sourcesContent":["import { platform } from 'node:os';\nimport { execFileSync } from 'node:child_process';\n\nexport const IS_WINDOWS = platform() === 'win32';\n\n/** Normalize line endings to Unix-style (\\n). Strips \\r\\n and bare \\r. */\nexport function normalizeEol(text: string): string {\n return text.replace(/\\r\\n|\\r/g, '\\n');\n}\n\n/** Returns [shellExecutable, argsPrefix] for running shell commands.\n * Unix: ['/bin/sh', ['-c']] Windows: [cmd.exe /c] or [pwsh -NoProfile -Command] */\nexport function shellArgs(): [string, string[]] {\n if (IS_WINDOWS) {\n const comspec = process.env.COMSPEC || 'cmd.exe';\n // Detect PowerShell — uses -Command instead of /c\n if (/pwsh|powershell/i.test(comspec)) {\n return [comspec, ['-NoProfile', '-Command']];\n }\n return [comspec, ['/c']];\n }\n return ['/bin/sh', ['-c']];\n}\n\n/** Returns spawn options with shell:true on Windows for .cmd/.bat resolution. */\nexport function crossSpawnOptions<T extends Record<string, unknown>>(opts?: T): T & { shell: boolean } {\n return { ...(opts ?? {} as T), shell: IS_WINDOWS } as T & { shell: boolean };\n}\n\n/** Kill a process and its children. On Unix, tries process group first.\n * On Windows, uses `taskkill /T` for tree kill. Never throws. */\nexport function killProcessTree(pid: number, signal: NodeJS.Signals = 'SIGTERM'): void {\n if (IS_WINDOWS) {\n // taskkill /T kills the process tree; /F forces termination\n try { execFileSync('taskkill', ['/pid', String(pid), '/t', '/f'], { stdio: 'pipe' }); return; } catch { /* fall through */ }\n try { process.kill(pid); } catch { /* already dead */ }\n return;\n }\n // Unix: try process group kill first\n try { process.kill(-pid, signal); return; } catch { /* fall through */ }\n try { process.kill(pid, signal); } catch { /* already dead */ }\n}\n\n/** Returns platform-appropriate npx command ('npx' on Unix, 'npx.cmd' on Windows). */\nexport function npxCommand(): string {\n return IS_WINDOWS ? 'npx.cmd' : 'npx';\n}\n\n/** Returns platform-appropriate default PATH when process.env.PATH is undefined. */\nexport function defaultPath(): string {\n if (IS_WINDOWS) {\n return [\n process.env.SystemRoot ? `${process.env.SystemRoot}\\\\System32` : 'C:\\\\Windows\\\\System32',\n process.env.SystemRoot || 'C:\\\\Windows',\n 'C:\\\\Program Files\\\\nodejs',\n process.env.APPDATA ? `${process.env.APPDATA}\\\\npm` : '',\n ].filter(Boolean).join(';');\n }\n return '/usr/local/bin:/usr/bin:/bin';\n}\n"],"mappings":";AAAA,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAEtB,IAAM,aAAa,SAAS,MAAM;AAGlC,SAAS,aAAa,MAAsB;AACjD,SAAO,KAAK,QAAQ,YAAY,IAAI;AACtC;AAIO,SAAS,YAAgC;AAC9C,MAAI,YAAY;AACd,UAAM,UAAU,QAAQ,IAAI,WAAW;AAEvC,QAAI,mBAAmB,KAAK,OAAO,GAAG;AACpC,aAAO,CAAC,SAAS,CAAC,cAAc,UAAU,CAAC;AAAA,IAC7C;AACA,WAAO,CAAC,SAAS,CAAC,IAAI,CAAC;AAAA,EACzB;AACA,SAAO,CAAC,WAAW,CAAC,IAAI,CAAC;AAC3B;AAGO,SAAS,kBAAqD,MAAkC;AACrG,SAAO,EAAE,GAAI,QAAQ,CAAC,GAAS,OAAO,WAAW;AACnD;AAIO,SAAS,gBAAgB,KAAa,SAAyB,WAAiB;AACrF,MAAI,YAAY;AAEd,QAAI;AAAE,mBAAa,YAAY,CAAC,QAAQ,OAAO,GAAG,GAAG,MAAM,IAAI,GAAG,EAAE,OAAO,OAAO,CAAC;AAAG;AAAA,IAAQ,QAAQ;AAAA,IAAqB;AAC3H,QAAI;AAAE,cAAQ,KAAK,GAAG;AAAA,IAAG,QAAQ;AAAA,IAAqB;AACtD;AAAA,EACF;AAEA,MAAI;AAAE,YAAQ,KAAK,CAAC,KAAK,MAAM;AAAG;AAAA,EAAQ,QAAQ;AAAA,EAAqB;AACvE,MAAI;AAAE,YAAQ,KAAK,KAAK,MAAM;AAAA,EAAG,QAAQ;AAAA,EAAqB;AAChE;AAGO,SAAS,aAAqB;AACnC,SAAO,aAAa,YAAY;AAClC;AAGO,SAAS,cAAsB;AACpC,MAAI,YAAY;AACd,WAAO;AAAA,MACL,QAAQ,IAAI,aAAa,GAAG,QAAQ,IAAI,UAAU,eAAe;AAAA,MACjE,QAAQ,IAAI,cAAc;AAAA,MAC1B;AAAA,MACA,QAAQ,IAAI,UAAU,GAAG,QAAQ,IAAI,OAAO,UAAU;AAAA,IACxD,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,EAC5B;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/work.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport type { KantBanCLIClient } from '../client.js';\nimport { crossSpawnOptions } from '../lib/platform.js';\n\nfunction asRecord(val: unknown): Record<string, unknown> | undefined {\n return typeof val === 'object' && val !== null && !Array.isArray(val)\n ? (val as Record<string, unknown>)\n : undefined;\n}\n\nfunction asStringArray(val: unknown): string[] {\n return Array.isArray(val) ? val.map((v) => String(v)) : [];\n}\n\nexport async function runWork(client: KantBanCLIClient, args: string[]): Promise<void> {\n const [ticketId] = args;\n if (!ticketId) {\n console.error('Usage: kantban work <ticket-id> [--dry-run]');\n process.exit(1);\n }\n\n const projectId = process.env['KANTBAN_PROJECT_ID'];\n if (!projectId) {\n console.error('Error: KANTBAN_PROJECT_ID required');\n process.exit(1);\n }\n\n // Fetch ticket-scope context\n const context = await client.get<Record<string, unknown>>(\n `/projects/${projectId}/pipeline-context`,\n { ticketId },\n );\n\n const ticket = asRecord(context['ticket']);\n const signals = asStringArray(context['signals']);\n const rules = context['transition_rules'] ? String(context['transition_rules']) : undefined;\n\n const title = String(ticket?.['title'] ?? 'Untitled');\n const description = ticket?.['description'] ? String(ticket['description']) : '';\n const ticketIdStr = String(ticket?.['id'] ?? ticketId);\n\n // Build prompt for Claude\n const promptLines = [\n `# Task: ${title}`,\n '',\n ];\n\n if (description) {\n promptLines.push(description, '');\n }\n\n if (signals.length > 0) {\n promptLines.push('## Signals');\n for (const s of signals) {\n promptLines.push(`- ${s}`);\n }\n promptLines.push('');\n }\n\n if (rules) {\n promptLines.push('## Transition Rules', rules, '');\n }\n\n const toolPrefix = context['tool_prefix'] ? String(context['tool_prefix']) : 'kantban_';\n promptLines.push(\n `## Tool Prefix: ${toolPrefix}`,\n `## Ticket ID: ${ticketIdStr}`,\n `## Project ID: ${projectId}`,\n );\n\n const prompt = promptLines.join('\\n');\n\n console.log(`Starting Claude session for: ${title}`);\n console.log(`Ticket: ${ticketIdStr}`);\n\n // Check for dry run\n const dryRun = args.includes('--dry-run');\n if (dryRun) {\n console.log('\\n--- Prompt (dry run) ---');\n console.log(prompt);\n return;\n }\n\n // Spawn Claude with the prompt\n const child = spawn('claude', ['-p', prompt], {\n stdio: 'inherit',\n env: { ...process.env },\n ...crossSpawnOptions(),\n });\n\n await new Promise<void>((resolve) => {\n child.on('error', (err) => {\n console.error(`Failed to start claude: ${err.message}`);\n process.exitCode = 1;\n resolve();\n });\n child.on('exit', (code) => {\n process.exitCode = code ?? 0;\n resolve();\n });\n });\n}\n"],"mappings":";;;;;AAAA,SAAS,aAAa;AAItB,SAAS,SAAS,KAAmD;AACnE,SAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,CAAC,MAAM,QAAQ,GAAG,IAC/D,MACD;AACN;AAEA,SAAS,cAAc,KAAwB;AAC7C,SAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;AAC3D;AAEA,eAAsB,QAAQ,QAA0B,MAA+B;AACrF,QAAM,CAAC,QAAQ,IAAI;AACnB,MAAI,CAAC,UAAU;AACb,YAAQ,MAAM,6CAA6C;AAC3D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,YAAY,QAAQ,IAAI,oBAAoB;AAClD,MAAI,CAAC,WAAW;AACd,YAAQ,MAAM,oCAAoC;AAClD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,UAAU,MAAM,OAAO;AAAA,IAC3B,aAAa,SAAS;AAAA,IACtB,EAAE,SAAS;AAAA,EACb;AAEA,QAAM,SAAS,SAAS,QAAQ,QAAQ,CAAC;AACzC,QAAM,UAAU,cAAc,QAAQ,SAAS,CAAC;AAChD,QAAM,QAAQ,QAAQ,kBAAkB,IAAI,OAAO,QAAQ,kBAAkB,CAAC,IAAI;AAElF,QAAM,QAAQ,OAAO,SAAS,OAAO,KAAK,UAAU;AACpD,QAAM,cAAc,SAAS,aAAa,IAAI,OAAO,OAAO,aAAa,CAAC,IAAI;AAC9E,QAAM,cAAc,OAAO,SAAS,IAAI,KAAK,QAAQ;AAGrD,QAAM,cAAc;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB;AAAA,EACF;AAEA,MAAI,aAAa;AACf,gBAAY,KAAK,aAAa,EAAE;AAAA,EAClC;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,gBAAY,KAAK,YAAY;AAC7B,eAAW,KAAK,SAAS;AACvB,kBAAY,KAAK,KAAK,CAAC,EAAE;AAAA,IAC3B;AACA,gBAAY,KAAK,EAAE;AAAA,EACrB;AAEA,MAAI,OAAO;AACT,gBAAY,KAAK,uBAAuB,OAAO,EAAE;AAAA,EACnD;AAEA,QAAM,aAAa,QAAQ,aAAa,IAAI,OAAO,QAAQ,aAAa,CAAC,IAAI;AAC7E,cAAY;AAAA,IACV,mBAAmB,UAAU;AAAA,IAC7B,iBAAiB,WAAW;AAAA,IAC5B,kBAAkB,SAAS;AAAA,EAC7B;AAEA,QAAM,SAAS,YAAY,KAAK,IAAI;AAEpC,UAAQ,IAAI,gCAAgC,KAAK,EAAE;AACnD,UAAQ,IAAI,WAAW,WAAW,EAAE;AAGpC,QAAM,SAAS,KAAK,SAAS,WAAW;AACxC,MAAI,QAAQ;AACV,YAAQ,IAAI,4BAA4B;AACxC,YAAQ,IAAI,MAAM;AAClB;AAAA,EACF;AAGA,QAAM,QAAQ,MAAM,UAAU,CAAC,MAAM,MAAM,GAAG;AAAA,IAC5C,OAAO;AAAA,IACP,KAAK,EAAE,GAAG,QAAQ,IAAI;AAAA,IACtB,GAAG,kBAAkB;AAAA,EACvB,CAAC;AAED,QAAM,IAAI,QAAc,CAAC,YAAY;AACnC,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,cAAQ,MAAM,2BAA2B,IAAI,OAAO,EAAE;AACtD,cAAQ,WAAW;AACnB,cAAQ;AAAA,IACV,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,cAAQ,WAAW,QAAQ;AAC3B,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
|
File without changes
|