cskit-cli 1.0.38 → 1.0.41

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/commands/init.js +126 -214
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cskit-cli",
3
- "version": "1.0.38",
3
+ "version": "1.0.41",
4
4
  "description": "Content Suite Kit CLI - Download and manage CSK skills from private repository",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -45,129 +45,67 @@ const {
45
45
  // Timeline Display
46
46
  // =============================================================================
47
47
 
48
- const SYMBOLS = {
49
- pending: chalk.dim('◇'),
50
- active: chalk.cyan('◈'),
51
- done: chalk.green('◆'),
52
- error: chalk.red('◆'),
53
- skip: chalk.yellow('◇'),
54
- file: chalk.dim('│ '),
55
- fileNew: chalk.green('│ + '),
56
- fileUpdate: chalk.blue('│ ~ '),
57
- fileSkip: chalk.yellow('│ ! '),
58
- fileDel: chalk.red('│ - '),
59
- branch: chalk.dim('├──'),
60
- branchLast: chalk.dim('└──'),
61
- indent: ' ',
62
- };
63
-
48
+ /**
49
+ * Linear Timeline - shows progress step by step
50
+ * Each step prints its header, then children as they happen
51
+ */
64
52
  class Timeline {
65
53
  constructor() {
66
54
  this.steps = [];
67
55
  this.currentStep = -1;
68
- this.lastLineCount = 0;
69
- this.paused = false; // After prompt, stop full re-renders
70
56
  }
71
57
 
72
58
  addStep(name) {
73
59
  this.steps.push({ name, status: 'pending', children: [] });
74
60
  }
75
61
 
62
+ // Print step header when starting
76
63
  start(stepIndex) {
77
64
  this.currentStep = stepIndex;
78
65
  this.steps[stepIndex].status = 'active';
79
- this.render();
66
+ const num = chalk.dim(`${stepIndex + 1}.`);
67
+ const name = chalk.cyan(this.steps[stepIndex].name);
68
+ console.log(`\n ${num} ${name}`);
80
69
  }
81
70
 
71
+ // Print completion message
82
72
  complete(stepIndex, message = null) {
83
73
  this.steps[stepIndex].status = 'done';
84
- if (message) this.steps[stepIndex].message = message;
85
- this.render();
74
+ if (message) {
75
+ console.log(` ${chalk.dim('└──')} ${chalk.green('✓')} ${chalk.dim(message)}`);
76
+ } else {
77
+ console.log(` ${chalk.dim('└──')} ${chalk.green('✓')} Done`);
78
+ }
86
79
  }
87
80
 
81
+ // Print error message
88
82
  error(stepIndex, message) {
89
83
  this.steps[stepIndex].status = 'error';
90
- this.steps[stepIndex].message = message;
91
- this.render();
84
+ console.log(` ${chalk.dim('└──')} ${chalk.red('✗')} ${message}`);
92
85
  }
93
86
 
87
+ // Print skip message
94
88
  skip(stepIndex, message) {
95
89
  this.steps[stepIndex].status = 'skip';
96
- this.steps[stepIndex].message = message;
97
- this.render();
90
+ console.log(` ${chalk.dim('└──')} ${chalk.yellow('○')} ${message}`);
98
91
  }
99
92
 
93
+ // Print child item immediately
100
94
  addChild(stepIndex, text, type = 'info') {
101
95
  this.steps[stepIndex].children.push({ text, type });
102
- }
103
-
104
- // Call before any prompt - stops full re-renders
105
- pause() {
106
- this.paused = true;
107
- this.lastLineCount = 0;
108
- }
109
-
110
- // Resume after prompt - no separator needed, step headers are enough
111
- resume() {
112
- console.log('');
113
- }
114
-
115
- buildStatusLine(stepIndex) {
116
- const step = this.steps[stepIndex];
117
- const symbol = SYMBOLS[step.status];
118
- const num = chalk.dim(`${stepIndex + 1}.`);
119
- const name = step.status === 'active' ? chalk.cyan(step.name) : step.name;
120
- const msg = step.message ? chalk.dim(` (${step.message})`) : '';
121
- return ` ${symbol} ${num} ${name}${msg}`;
122
- }
123
-
124
- render() {
125
- // After pause, only print current step status (no full timeline)
126
- if (this.paused) {
127
- console.log(this.buildStatusLine(this.currentStep));
128
- return;
129
- }
130
-
131
- const lines = [];
132
- for (let i = 0; i < this.steps.length; i++) {
133
- lines.push(this.buildStatusLine(i));
134
-
135
- const step = this.steps[i];
136
- // Children for non-pending steps
137
- if (step.status !== 'pending') {
138
- for (let j = 0; j < step.children.length; j++) {
139
- const child = step.children[j];
140
- const isLast = j === step.children.length - 1;
141
- const branch = isLast ? SYMBOLS.branchLast : SYMBOLS.branch;
142
- const prefix = child.type === 'new' ? chalk.green('+') :
143
- child.type === 'update' ? chalk.blue('~') :
144
- child.type === 'skip' ? chalk.yellow('!') :
145
- child.type === 'delete' ? chalk.red('-') :
146
- child.type === 'info' ? chalk.cyan('i') :
147
- chalk.dim('•');
148
- lines.push(` ${SYMBOLS.indent}${branch} ${prefix} ${child.text}`);
149
- }
150
- }
151
- }
152
-
153
- // In-place update if have previous output
154
- if (this.lastLineCount > 0) {
155
- process.stdout.write(`\x1b[${this.lastLineCount}A`);
156
- for (let i = 0; i < this.lastLineCount; i++) {
157
- process.stdout.write('\x1b[2K\n');
158
- }
159
- process.stdout.write(`\x1b[${this.lastLineCount}A`);
160
- } else {
161
- console.log('');
162
- }
163
-
164
- for (const line of lines) {
165
- console.log(line);
166
- }
167
- console.log('');
168
-
169
- this.lastLineCount = lines.length + 1;
170
- }
96
+ const prefix = type === 'new' ? chalk.green('+') :
97
+ type === 'update' ? chalk.blue('~') :
98
+ type === 'skip' ? chalk.yellow('!') :
99
+ type === 'delete' ? chalk.red('-') :
100
+ type === 'info' ? chalk.cyan('i') :
101
+ chalk.dim('•');
102
+ console.log(` ${chalk.dim('├──')} ${prefix} ${text}`);
103
+ }
104
+
105
+ // No-op for compatibility
106
+ pause() {}
107
+ resume() { console.log(''); }
108
+ render() {}
171
109
  }
172
110
 
173
111
  // =============================================================================
@@ -439,7 +377,8 @@ async function initCommand(options) {
439
377
  timeline.addStep('Scan changes');
440
378
  timeline.addStep('Confirm changes');
441
379
  timeline.addStep('Apply changes');
442
- timeline.addStep('Setup dependencies');
380
+ timeline.addStep('Check dependencies');
381
+ timeline.addStep('Install packages');
443
382
 
444
383
  // Step 1: Authenticate
445
384
  timeline.start(0);
@@ -608,6 +547,7 @@ async function initCommand(options) {
608
547
  timeline.skip(4, 'User cancelled');
609
548
  timeline.skip(5, 'Skipped');
610
549
  timeline.skip(6, 'Skipped');
550
+ timeline.skip(7, 'Skipped');
611
551
 
612
552
  // Cleanup
613
553
  fs.rmSync(tempDir, { recursive: true, force: true });
@@ -669,111 +609,43 @@ async function initCommand(options) {
669
609
 
670
610
  timeline.complete(5, `${applied} files`);
671
611
 
672
- // Step 7: Setup dependencies
612
+ // Step 7: Check dependencies
673
613
  timeline.start(6);
674
614
 
675
615
  const libPythonDir = path.join(projectDir, 'lib', 'python');
676
616
 
677
- // Helper function to install Python packages
678
- async function handlePythonPackages(pythonCmd) {
679
- if (!fs.existsSync(libPythonDir)) {
680
- timeline.skip(6, 'No lib/python folder');
681
- return;
682
- }
683
-
684
- timeline.pause();
685
- const { installAdvanced } = await inquirer.prompt([{
686
- type: 'confirm',
687
- name: 'installAdvanced',
688
- message: 'Install Python dependencies?',
689
- default: true
690
- }]);
691
-
692
- if (!installAdvanced) {
693
- timeline.resume();
694
- timeline.skip(6, 'Skipped by user');
695
- return;
696
- }
697
- timeline.resume();
698
-
699
- const pkgStatus = checkPythonPackages(libPythonDir);
700
-
701
- // Show Python version in timeline
702
- timeline.addChild(6, `Python ${pythonCmd === 'python3' ? pythonInfo.version : '3.x'} (${pythonCmd})`, 'update');
703
-
704
- // Show already installed count
705
- if (pkgStatus.installed.length > 0) {
706
- timeline.addChild(6, `${pkgStatus.installed.length} packages already installed`, 'info');
707
- }
708
-
709
- const allToInstall = [...pkgStatus.toInstall, ...pkgStatus.toUpdate];
710
-
711
- if (allToInstall.length === 0) {
712
- timeline.complete(6, `${pkgStatus.installed.length} packages ready`);
713
- return;
714
- }
715
-
716
- // Show what needs to be installed/updated with tree connector
717
- console.log(`\n ${pipe}`);
718
- console.log(` ${branch} ${chalk.cyan('Packages to install/update:')}`);
719
-
720
- const showPkgs = allToInstall.slice(0, 10);
721
- const hasMore = allToInstall.length > 10;
722
-
723
- for (let i = 0; i < showPkgs.length; i++) {
724
- const pkg = showPkgs[i];
725
- const isLast = i === showPkgs.length - 1 && !hasMore;
726
- const prefix = isLast ? corner : branch;
727
- if (pkg.current) {
728
- console.log(` ${pipe} ${prefix} ${chalk.blue('~')} ${pkg.name}: ${pkg.current} → ${pkg.required}`);
729
- } else {
730
- console.log(` ${pipe} ${prefix} ${chalk.green('+')} ${pkg.name}: ${pkg.required || 'latest'}`);
731
- }
732
- }
733
- if (hasMore) {
734
- console.log(` ${pipe} ${corner} ${chalk.dim(`... and ${allToInstall.length - 10} more`)}`);
735
- }
736
- console.log(` ${pipe}`);
737
-
738
- timeline.pause();
739
- const { confirmPkgs } = await inquirer.prompt([{
740
- type: 'confirm',
741
- name: 'confirmPkgs',
742
- message: `Install/update ${allToInstall.length} packages?`,
743
- default: true
744
- }]);
745
- timeline.resume();
746
-
747
- if (confirmPkgs) {
748
- await installPackages(libPythonDir, allToInstall, timeline, 6, pythonCmd);
749
- timeline.complete(6, `${pkgStatus.installed.length + allToInstall.length} packages`);
750
- } else {
751
- timeline.skip(6, 'User skipped');
752
- }
753
- }
754
-
755
- // Check Python availability first
617
+ // Check if lib/python exists
618
+ if (!fs.existsSync(libPythonDir)) {
619
+ timeline.addChild(6, 'No lib/python folder', 'skip');
620
+ timeline.complete(6, 'Skipped');
621
+ timeline.skip(7, 'No dependencies');
622
+
623
+ // Cleanup and finish
624
+ try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch {}
625
+ const ver = selectedVersion.replace(/^v/, '');
626
+ console.log(chalk.green(`\n ✓ CSK v${ver} ${isUpdate ? 'updated' : 'installed'} successfully!\n`));
627
+ console.log(chalk.cyan(' Quick Start\n'));
628
+ console.log(chalk.dim(' 1. Open Claude Code:'));
629
+ console.log(` ${chalk.white('claude')}\n`);
630
+ console.log(chalk.dim(' 2. Start with CSK:'));
631
+ console.log(` ${chalk.white('/csk')}\n`);
632
+ return;
633
+ }
634
+
635
+ // Check Python availability
756
636
  let pythonInfo = detectPython();
757
637
  let pythonReady = false;
758
638
 
759
639
  if (!pythonInfo.found) {
760
- console.log('');
761
- console.log(chalk.yellow(' Python not found on this system.'));
762
- console.log(chalk.dim(' Python is required for:'));
763
- console.log(chalk.dim(' - Data fetching and analysis'));
764
- console.log(chalk.dim(' - Chart generation'));
765
- console.log(chalk.dim(' - MCP server tools'));
766
- console.log('');
640
+ timeline.addChild(6, 'Python not found', 'skip');
767
641
 
768
642
  // Check if we can auto-install
769
643
  const autoInstall = canAutoInstall();
770
644
 
771
645
  if (autoInstall.canInstall) {
646
+ console.log('');
647
+ console.log(chalk.yellow(' Python not found on this system.'));
772
648
  console.log(chalk.cyan(` Auto-install available via ${autoInstall.manager}`));
773
- if (autoInstall.needsSudo) {
774
- console.log(chalk.dim(' (requires sudo password)'));
775
- }
776
- console.log(chalk.dim(` Command: ${autoInstall.command}`));
777
649
  console.log('');
778
650
 
779
651
  timeline.pause();
@@ -786,11 +658,9 @@ async function initCommand(options) {
786
658
  timeline.resume();
787
659
 
788
660
  if (doInstall) {
789
- console.log('');
790
661
  timeline.addChild(6, `Installing via ${autoInstall.manager}...`, 'update');
791
-
792
662
  const result = await installPython((msg) => {
793
- console.log(chalk.dim(` ${msg}`));
663
+ console.log(chalk.dim(` ${msg}`));
794
664
  });
795
665
 
796
666
  if (result.success) {
@@ -798,43 +668,85 @@ async function initCommand(options) {
798
668
  timeline.addChild(6, `Python ${result.version} installed`, 'new');
799
669
  pythonReady = true;
800
670
  } else {
801
- console.log('');
802
- console.log(chalk.red(` ${result.error}`));
803
- console.log(chalk.dim(' You may need to restart your terminal after installation.'));
804
- console.log('');
805
- timeline.skip(6, 'Installation failed');
671
+ timeline.addChild(6, 'Installation failed', 'skip');
672
+ timeline.complete(6, 'Failed');
673
+ timeline.skip(7, 'No Python');
806
674
  }
807
675
  } else {
808
- timeline.skip(6, 'User declined install');
676
+ timeline.complete(6, 'Skipped');
677
+ timeline.skip(7, 'No Python');
809
678
  }
810
679
  } else {
811
- // No package manager - show manual instructions
680
+ // Show manual instructions
812
681
  const instructions = getInstallInstructions();
813
- console.log(chalk.bold(` Install via ${instructions.method}:\n`));
814
- for (const cmd of instructions.commands.slice(0, 4)) {
815
- if (cmd.startsWith('#')) {
816
- console.log(chalk.dim(` ${cmd}`));
817
- } else if (cmd !== '') {
818
- console.log(chalk.cyan(` ${cmd}`));
819
- }
820
- }
821
682
  console.log('');
822
- timeline.skip(6, 'Python not installed');
683
+ console.log(chalk.yellow(' Python not found. Install via:'));
684
+ console.log(chalk.cyan(` ${instructions.commands[0]}`));
685
+ console.log('');
686
+ timeline.complete(6, 'Python needed');
687
+ timeline.skip(7, 'No Python');
823
688
  }
824
689
  } else if (!pythonInfo.meetsMinimum) {
825
- console.log('');
826
- console.log(chalk.yellow(` Python ${pythonInfo.version} found, but 3.8+ required.`));
827
- console.log(chalk.dim(' Please upgrade Python to use advanced features.'));
828
- console.log('');
829
- timeline.skip(6, `Python ${pythonInfo.version} too old`);
690
+ timeline.addChild(6, `Python ${pythonInfo.version} (need 3.8+)`, 'skip');
691
+ timeline.complete(6, 'Upgrade needed');
692
+ timeline.skip(7, 'Python too old');
830
693
  } else {
831
694
  pythonReady = true;
695
+ timeline.addChild(6, `Python ${pythonInfo.version}`, 'info');
696
+ }
697
+
698
+ // If Python not ready, exit early
699
+ if (!pythonReady) {
700
+ try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch {}
701
+ const ver = selectedVersion.replace(/^v/, '');
702
+ console.log(chalk.green(`\n ✓ CSK v${ver} ${isUpdate ? 'updated' : 'installed'} successfully!\n`));
703
+ console.log(chalk.dim(' Note: Python dependencies not installed.\n'));
704
+ return;
832
705
  }
833
706
 
834
- // Install packages if Python is ready
835
- if (pythonReady && pythonInfo.meetsMinimum) {
836
- timeline.addChild(6, `Python ${pythonInfo.version} (${pythonInfo.command})`, 'update');
837
- await handlePythonPackages(pythonInfo.command);
707
+ // Check packages
708
+ const pkgStatus = checkPythonPackages(libPythonDir);
709
+ const allToInstall = [...pkgStatus.toInstall, ...pkgStatus.toUpdate];
710
+
711
+ if (pkgStatus.installed.length > 0) {
712
+ timeline.addChild(6, `${pkgStatus.installed.length} packages OK`, 'info');
713
+ }
714
+
715
+ if (allToInstall.length === 0) {
716
+ timeline.complete(6, 'All ready');
717
+ timeline.skip(7, 'Nothing to install');
718
+ } else {
719
+ // Show packages to install/update
720
+ for (const pkg of allToInstall.slice(0, 5)) {
721
+ if (pkg.current) {
722
+ timeline.addChild(6, `${pkg.name}: ${pkg.current} → ${pkg.required}`, 'update');
723
+ } else {
724
+ timeline.addChild(6, `${pkg.name}: ${pkg.required || 'latest'}`, 'new');
725
+ }
726
+ }
727
+ if (allToInstall.length > 5) {
728
+ timeline.addChild(6, `... and ${allToInstall.length - 5} more`, 'info');
729
+ }
730
+ timeline.complete(6, `${allToInstall.length} to install`);
731
+
732
+ // Step 8: Install packages
733
+ timeline.start(7);
734
+
735
+ timeline.pause();
736
+ const { confirmPkgs } = await inquirer.prompt([{
737
+ type: 'confirm',
738
+ name: 'confirmPkgs',
739
+ message: `Install ${allToInstall.length} packages?`,
740
+ default: true
741
+ }]);
742
+ timeline.resume();
743
+
744
+ if (confirmPkgs) {
745
+ await installPackages(libPythonDir, allToInstall, timeline, 7, pythonInfo.command);
746
+ timeline.complete(7, `${pkgStatus.installed.length + allToInstall.length} packages`);
747
+ } else {
748
+ timeline.skip(7, 'Skipped');
749
+ }
838
750
  }
839
751
 
840
752
  // Cleanup temp files