kandown 0.1.2 → 0.1.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/bin/kandown.js +53 -43
- package/bin/tui.js +17247 -739
- package/package.json +5 -1
package/bin/kandown.js
CHANGED
|
@@ -25,6 +25,16 @@
|
|
|
25
25
|
* @exports none
|
|
26
26
|
*/
|
|
27
27
|
/* eslint-disable no-console */
|
|
28
|
+
// 📖 DEV=false prevents Ink from loading react-devtools-core (CJS-only, breaks ESM).
|
|
29
|
+
// Must be set BEFORE any imports because ESM hoists all import statements.
|
|
30
|
+
process.env.DEV = 'false';
|
|
31
|
+
// 📖 Polyfill browser globals that some bundled modules expect.
|
|
32
|
+
if (typeof globalThis.self === 'undefined') Object.defineProperty(globalThis, 'self', { value: globalThis });
|
|
33
|
+
if (typeof globalThis.window === 'undefined') Object.defineProperty(globalThis, 'window', { value: globalThis });
|
|
34
|
+
// 📖 Make require() available in this ESM module so bundled __require() shims work.
|
|
35
|
+
// tsup's __require checks `typeof require !== "undefined"` — this makes it truthy.
|
|
36
|
+
import { createRequire } from 'node:module';
|
|
37
|
+
globalThis.require = createRequire(import.meta.url);
|
|
28
38
|
|
|
29
39
|
import { fileURLToPath } from 'node:url';
|
|
30
40
|
import { createServer } from 'node:http';
|
|
@@ -174,35 +184,40 @@ function parseArgs(argv) {
|
|
|
174
184
|
return args;
|
|
175
185
|
}
|
|
176
186
|
|
|
177
|
-
|
|
187
|
+
/**
|
|
188
|
+
* @returns {{ kandownDir: string, alreadyExisted: boolean }} — resolves the
|
|
189
|
+
* kandown directory and auto-inits it if it doesn't exist (no prompt, silent init).
|
|
190
|
+
*/
|
|
191
|
+
function ensureKandownDir(rawArgs) {
|
|
178
192
|
const args = parseArgs(rawArgs);
|
|
179
193
|
const cwd = process.cwd();
|
|
194
|
+
const explicitPath = rawArgs.includes('--path') || rawArgs.includes('-p');
|
|
180
195
|
const kandownDir = resolve(cwd, args.path);
|
|
181
|
-
const kandownPath = args.path;
|
|
182
196
|
|
|
183
|
-
|
|
184
|
-
info(`Installing kandown in ${c.bold}${kandownPath}/${c.reset}`);
|
|
185
|
-
log('');
|
|
197
|
+
if (existsSync(kandownDir)) return { kandownDir, alreadyExisted: true };
|
|
186
198
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
199
|
+
log('');
|
|
200
|
+
info(`No .kandown/ found — auto-installing...`);
|
|
201
|
+
doInit(args, cwd, args.path, kandownDir);
|
|
202
|
+
return { kandownDir, alreadyExisted: false };
|
|
203
|
+
}
|
|
192
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Performs the actual init work. Returns on error (does not exit).
|
|
207
|
+
* @returns {boolean} true if init succeeded, false otherwise.
|
|
208
|
+
*/
|
|
209
|
+
function doInit(args, cwd, kandownPath, kandownDir) {
|
|
193
210
|
mkdirSync(kandownDir, { recursive: true });
|
|
194
211
|
|
|
195
|
-
// Copy kandown.html from dist
|
|
196
212
|
const htmlSrc = join(PKG_ROOT, 'dist', 'index.html');
|
|
197
213
|
const htmlDest = join(kandownDir, 'kandown.html');
|
|
198
214
|
if (!existsSync(htmlSrc)) {
|
|
199
215
|
err(`Missing build output at ${htmlSrc}. Did you run 'npm run build'?`);
|
|
200
|
-
|
|
216
|
+
return false;
|
|
201
217
|
}
|
|
202
218
|
copyFileSync(htmlSrc, htmlDest);
|
|
203
219
|
success('kandown.html');
|
|
204
220
|
|
|
205
|
-
// Copy templates
|
|
206
221
|
const templatesDir = join(PKG_ROOT, 'templates');
|
|
207
222
|
if (!existsSync(join(kandownDir, 'AGENT.md'))) {
|
|
208
223
|
copyFileSync(join(templatesDir, 'AGENT.md'), join(kandownDir, 'AGENT.md'));
|
|
@@ -222,7 +237,6 @@ function cmdInit(rawArgs) {
|
|
|
222
237
|
info('tasks/ already exists (kept)');
|
|
223
238
|
}
|
|
224
239
|
|
|
225
|
-
// 📖 Copy kandown.json config file (project preferences for UI, agent, fields)
|
|
226
240
|
if (!existsSync(join(kandownDir, 'kandown.json'))) {
|
|
227
241
|
copyFileSync(join(templatesDir, 'kandown.json'), join(kandownDir, 'kandown.json'));
|
|
228
242
|
success('kandown.json');
|
|
@@ -230,7 +244,6 @@ function cmdInit(rawArgs) {
|
|
|
230
244
|
info('kandown.json already exists (kept)');
|
|
231
245
|
}
|
|
232
246
|
|
|
233
|
-
// Copy AGENT_KANDOWN.md to project root (not inside .kandown/)
|
|
234
247
|
const agentKandownSrc = join(templatesDir, 'AGENT_KANDOWN.md');
|
|
235
248
|
const agentKandownDest = join(cwd, 'AGENT_KANDOWN.md');
|
|
236
249
|
if (!existsSync(agentKandownDest)) {
|
|
@@ -240,7 +253,6 @@ function cmdInit(rawArgs) {
|
|
|
240
253
|
info('AGENT_KANDOWN.md already exists at project root (kept)');
|
|
241
254
|
}
|
|
242
255
|
|
|
243
|
-
// 📖 Copy the compact agent doc — used by the CLI board launcher for system prompt injection
|
|
244
256
|
const compactSrc = join(templatesDir, 'AGENT_KANDOWN_COMPACT.md');
|
|
245
257
|
const compactDest = join(cwd, 'AGENT_KANDOWN_COMPACT.md');
|
|
246
258
|
if (existsSync(compactSrc) && !existsSync(compactDest)) {
|
|
@@ -248,7 +260,6 @@ function cmdInit(rawArgs) {
|
|
|
248
260
|
success('AGENT_KANDOWN_COMPACT.md (at project root)');
|
|
249
261
|
}
|
|
250
262
|
|
|
251
|
-
// Integrate with AGENTS.md / CLAUDE.md
|
|
252
263
|
if (!args.noAgents) {
|
|
253
264
|
log('');
|
|
254
265
|
const existingAgents = findAgentsFile(cwd);
|
|
@@ -261,6 +272,27 @@ function cmdInit(rawArgs) {
|
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
274
|
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function cmdInit(rawArgs) {
|
|
279
|
+
const args = parseArgs(rawArgs);
|
|
280
|
+
const cwd = process.cwd();
|
|
281
|
+
const kandownPath = args.path;
|
|
282
|
+
const kandownDir = resolve(cwd, kandownPath);
|
|
283
|
+
|
|
284
|
+
log('');
|
|
285
|
+
info(`Installing kandown in ${c.bold}${kandownPath}/${c.reset}`);
|
|
286
|
+
log('');
|
|
287
|
+
|
|
288
|
+
if (existsSync(kandownDir) && !args.force) {
|
|
289
|
+
err(`Directory ${c.bold}${kandownPath}/${c.reset} already exists.`);
|
|
290
|
+
log(` Use ${c.cyan}--force${c.reset} to overwrite or ${c.cyan}--path <dir>${c.reset} for another location.`);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!doInit(args, cwd, kandownPath, kandownDir)) process.exit(1);
|
|
295
|
+
|
|
264
296
|
log('');
|
|
265
297
|
log(`${c.green}${c.bold}Done.${c.reset}`);
|
|
266
298
|
log('');
|
|
@@ -410,21 +442,9 @@ async function listenOnAvailablePort(kandownDir, preferredPort) {
|
|
|
410
442
|
* the browser can keep talking to localhost while the terminal board is active.
|
|
411
443
|
*/
|
|
412
444
|
async function cmdServe(rawArgs) {
|
|
413
|
-
const
|
|
414
|
-
const cwd = process.cwd();
|
|
415
|
-
const hasExplicitPath = rawArgs.includes('--path') || rawArgs.includes('-p');
|
|
416
|
-
const explicitKandownDir = resolve(cwd, args.path);
|
|
417
|
-
const kandownDir = hasExplicitPath || existsSync(explicitKandownDir)
|
|
418
|
-
? explicitKandownDir
|
|
419
|
-
: findKandownDir(cwd);
|
|
420
|
-
if (!kandownDir || !existsSync(kandownDir)) {
|
|
421
|
-
const missingPath = hasExplicitPath ? args.path : '.kandown/';
|
|
422
|
-
err(`No ${c.bold}${missingPath}${c.reset} directory found.`);
|
|
423
|
-
log(` Run ${c.cyan}npx kandown init${c.reset} to set up kandown in this project.`);
|
|
424
|
-
process.exit(1);
|
|
425
|
-
}
|
|
445
|
+
const { kandownDir } = ensureKandownDir(rawArgs);
|
|
426
446
|
|
|
427
|
-
const preferredPort = parsePort(
|
|
447
|
+
const preferredPort = parsePort(parseArgs(rawArgs).port);
|
|
428
448
|
const { server, port } = await listenOnAvailablePort(kandownDir, preferredPort);
|
|
429
449
|
const url = `http://localhost:${port}`;
|
|
430
450
|
|
|
@@ -438,8 +458,7 @@ async function cmdServe(rawArgs) {
|
|
|
438
458
|
info(`Project: ${kandownDir}`);
|
|
439
459
|
openInBrowser(url);
|
|
440
460
|
try {
|
|
441
|
-
|
|
442
|
-
await cmdTui('board', tuiArgs);
|
|
461
|
+
await cmdTui('board', rawArgs);
|
|
443
462
|
} finally {
|
|
444
463
|
server.close();
|
|
445
464
|
}
|
|
@@ -477,22 +496,13 @@ function findKandownDir(cwd) {
|
|
|
477
496
|
|
|
478
497
|
// 📖 Launches the fullscreen TUI for a given screen (settings, board, etc.)
|
|
479
498
|
async function cmdTui(screen, rawArgs) {
|
|
480
|
-
const
|
|
481
|
-
const cwd = process.cwd();
|
|
482
|
-
const kandownDir = resolve(cwd, args.path);
|
|
483
|
-
|
|
484
|
-
if (!existsSync(kandownDir)) {
|
|
485
|
-
err(`No ${c.bold}${args.path}/${c.reset} directory found.`);
|
|
486
|
-
log(` Run ${c.cyan}npx kandown init${c.reset} first.`);
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
499
|
+
const { kandownDir } = ensureKandownDir(rawArgs);
|
|
489
500
|
|
|
490
501
|
try {
|
|
491
502
|
const { run } = await import(new URL('./tui.js', import.meta.url).href);
|
|
492
503
|
await run(screen, kandownDir);
|
|
493
504
|
} catch (e) {
|
|
494
505
|
err(`Failed to launch TUI: ${e.message}`);
|
|
495
|
-
log(` Make sure the CLI is built: ${c.cyan}pnpm build:cli${c.reset}`);
|
|
496
506
|
process.exit(1);
|
|
497
507
|
}
|
|
498
508
|
}
|