kandown 0.1.4 β†’ 0.2.1

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 CHANGED
@@ -57,6 +57,51 @@ const PKG_ROOT = resolve(__dirname, '..');
57
57
  const DEFAULT_SERVE_PORT = 2048;
58
58
  const MAX_SERVE_PORT = 2060;
59
59
 
60
+ // πŸ“– Get current CLI version from package.json at PKG_ROOT
61
+ function getCurrentVersion() {
62
+ try {
63
+ const pkg = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'));
64
+ return pkg.version;
65
+ } catch { return null; }
66
+ }
67
+
68
+ // πŸ“– Check npm for a newer version and auto-update if outdated.
69
+ // Runs in background β€” does not block startup. Only activates when running from
70
+ // an installed npm package (not local dev source, where src/ exists).
71
+ // πŸ“– Uses npm install -g to self-upgrade, then re-spawns with the same arguments.
72
+ async function checkForUpdate(argv = process.argv) {
73
+ if (existsSync(join(PKG_ROOT, 'src'))) return; // local dev β€” skip
74
+ const current = getCurrentVersion();
75
+ if (!current) return;
76
+ try {
77
+ const { execSync } = await import('node:child_process');
78
+ const latest = String(execSync('npm view kandown version --json 2>/dev/null', {
79
+ timeout: 8000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
80
+ })).trim().replace(/^"|"$/g, '');
81
+ if (!latest || latest === current) return;
82
+
83
+ log(`${c.yellow}⚑ Auto-updating kandown ${c.reset}${c.dim}${current}${c.reset} ${c.yellow}β†’${c.reset} ${c.green}${latest}${c.reset}…`);
84
+ try {
85
+ execSync('npm install -g kandown 2>/dev/null', {
86
+ timeout: 30000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
87
+ });
88
+ const newVersion = String(execSync('npm view kandown version 2>/dev/null', {
89
+ timeout: 5000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
90
+ })).trim().replace(/^"|"$/g, '');
91
+ if (newVersion === latest) {
92
+ log(`${c.green}βœ“ Updated to v${newVersion}${c.reset} β€” restarting…`);
93
+ const child = spawn(process.argv[0], ['--experimental-vm-modules', ...argv.slice(1)], {
94
+ detached: true, stdio: 'ignore', env: { ...process.env } });
95
+ child.unref();
96
+ process.exit(0);
97
+ }
98
+ } catch {
99
+ log(`${c.yellow}⚠ Auto-update failed β€” will retry on next run${c.reset}`);
100
+ log(` Run ${c.cyan}npm install -g kandown${c.reset} to upgrade manually`);
101
+ }
102
+ } catch { /* offline or npm slow β€” silently skip */ }
103
+ }
104
+
60
105
  const c = {
61
106
  reset: '\x1b[0m',
62
107
  bold: '\x1b[1m',
@@ -75,8 +120,10 @@ const warn = (msg) => log(`${c.yellow}⚠${c.reset} ${msg}`);
75
120
  const err = (msg) => log(`${c.red}βœ—${c.reset} ${msg}`);
76
121
 
77
122
  function help() {
123
+ const v = getCurrentVersion() ?? '?';
78
124
  log(`
79
125
  ${c.bold}kandown${c.reset} ${c.dim}Β· file-based kanban backed by markdown${c.reset}
126
+ ${c.dim}v${v}${c.reset}
80
127
 
81
128
  ${c.bold}Usage:${c.reset}
82
129
  npx kandown [command]
@@ -140,7 +187,7 @@ function appendAgentReference(cwd, agentsFile, kandownPath) {
140
187
  ${marker}
141
188
  ## Task management
142
189
 
143
- **IMPORTANT:** Before touching any task files, you MUST read \`AGENT_KANDOWN.md\`.
190
+ **IMPORTANT:** Before touching any task files, you MUST read \`${kandownPath}/AGENT_KANDOWN.md\`.
144
191
 
145
192
  This project uses a file-based kanban:
146
193
  - **Tasks live in \`${kandownPath}/tasks/t-xxx.md\`** β€” each task file owns its status
@@ -245,19 +292,12 @@ function doInit(args, cwd, kandownPath, kandownDir) {
245
292
  }
246
293
 
247
294
  const agentKandownSrc = join(templatesDir, 'AGENT_KANDOWN.md');
248
- const agentKandownDest = join(cwd, 'AGENT_KANDOWN.md');
295
+ const agentKandownDest = join(kandownDir, 'AGENT_KANDOWN.md');
249
296
  if (!existsSync(agentKandownDest)) {
250
297
  copyFileSync(agentKandownSrc, agentKandownDest);
251
- success('AGENT_KANDOWN.md (at project root)');
298
+ success('AGENT_KANDOWN.md');
252
299
  } else {
253
- info('AGENT_KANDOWN.md already exists at project root (kept)');
254
- }
255
-
256
- const compactSrc = join(templatesDir, 'AGENT_KANDOWN_COMPACT.md');
257
- const compactDest = join(cwd, 'AGENT_KANDOWN_COMPACT.md');
258
- if (existsSync(compactSrc) && !existsSync(compactDest)) {
259
- copyFileSync(compactSrc, compactDest);
260
- success('AGENT_KANDOWN_COMPACT.md (at project root)');
300
+ info('AGENT_KANDOWN.md already exists (kept)');
261
301
  }
262
302
 
263
303
  if (!args.noAgents) {
@@ -497,10 +537,11 @@ function findKandownDir(cwd) {
497
537
  // πŸ“– Launches the fullscreen TUI for a given screen (settings, board, etc.)
498
538
  async function cmdTui(screen, rawArgs) {
499
539
  const { kandownDir } = ensureKandownDir(rawArgs);
540
+ const version = getCurrentVersion();
500
541
 
501
542
  try {
502
543
  const { run } = await import(new URL('./tui.js', import.meta.url).href);
503
- await run(screen, kandownDir);
544
+ await run(screen, kandownDir, version);
504
545
  } catch (e) {
505
546
  err(`Failed to launch TUI: ${e.message}`);
506
547
  process.exit(1);
@@ -509,6 +550,17 @@ async function cmdTui(screen, rawArgs) {
509
550
 
510
551
  const [cmd, ...rest] = process.argv.slice(2);
511
552
 
553
+ // πŸ“– Handle --version / -v before any command logic
554
+ if (cmd === '--version' || cmd === '-v') {
555
+ const v = getCurrentVersion() ?? 'unknown';
556
+ log(`kandown v${v}`);
557
+ process.exit(0);
558
+ }
559
+
560
+ // πŸ“– Auto-update check runs before EVERY command (except --version).
561
+ // Uses a short timeout so startup is not noticeably slower.
562
+ await checkForUpdate(rest);
563
+
512
564
  switch (cmd) {
513
565
  case 'init':
514
566
  cmdInit(rest);