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.
Files changed (3) hide show
  1. package/bin/kandown.js +53 -43
  2. package/bin/tui.js +17247 -739
  3. 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
- function cmdInit(rawArgs) {
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
- log('');
184
- info(`Installing kandown in ${c.bold}${kandownPath}/${c.reset}`);
185
- log('');
197
+ if (existsSync(kandownDir)) return { kandownDir, alreadyExisted: true };
186
198
 
187
- if (existsSync(kandownDir) && !args.force) {
188
- err(`Directory ${c.bold}${kandownPath}/${c.reset} already exists.`);
189
- log(` Use ${c.cyan}--force${c.reset} to overwrite or ${c.cyan}--path <dir>${c.reset} for another location.`);
190
- process.exit(1);
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
- process.exit(1);
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 args = parseArgs(rawArgs);
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(args.port);
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
- const tuiArgs = kandownDir === explicitKandownDir ? rawArgs : ['--path', kandownDir, ...rawArgs];
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 args = parseArgs(rawArgs);
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
  }