@shirayner/ace 0.1.1-snapshot.2 → 0.1.1-snapshot.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shirayner/ace",
3
- "version": "0.1.1-snapshot.2",
3
+ "version": "0.1.1-snapshot.3",
4
4
  "description": "AI Coding Environment - One command to set up your Claude Code harness",
5
5
  "bin": {
6
6
  "ace": "./bin/ace.js"
@@ -1,10 +1,10 @@
1
- import chalk from 'chalk';
2
1
  import inquirer from 'inquirer';
2
+ import ora from 'ora';
3
3
  import { createRequire } from 'module';
4
4
  import { PRESETS, ROLES, COMPONENTS } from '../core/constants.js';
5
5
  import { Installer } from '../core/installer.js';
6
6
  import {
7
- printBanner, stepDone, stepMerge, stepSkip, stepFail,
7
+ printBanner, renderScreen,
8
8
  doneMessage, doneWithErrors,
9
9
  colors, icons, componentLabels,
10
10
  } from '../core/ui.js';
@@ -12,29 +12,35 @@ import {
12
12
  const require = createRequire(import.meta.url);
13
13
  const pkg = require('../../package.json');
14
14
 
15
- export async function initCommand(options) {
16
- printBanner(pkg.version);
15
+ function formatStep(label, value) {
16
+ return ` ${colors.success(icons.check)} ${colors.dim(label)} ${colors.dim('·')} ${colors.white(value)}`;
17
+ }
17
18
 
19
+ export async function initCommand(options) {
20
+ const version = pkg.version;
18
21
  let role = 'fullstack';
19
22
  let preset = options.preset;
23
+ const completedSteps = [];
20
24
 
21
- // ─── Interactive: ask role only ─────────────────────────────
25
+ // ─── Step 1: Role ──────────────────────────────────────────
22
26
  if (options.interaction !== false) {
23
- const answers = await inquirer.prompt([
24
- {
25
- type: 'list',
26
- name: 'role',
27
- message: 'Role',
28
- choices: Object.entries(ROLES).map(([key, val]) => ({
29
- name: `${colors.white(val.label)} ${colors.dim(val.description)}`,
30
- value: key,
31
- short: val.label,
32
- })),
33
- default: 'fullstack',
34
- prefix: colors.brand('?'),
35
- },
36
- ]);
37
- role = answers.role;
27
+ renderScreen(version);
28
+
29
+ const result = await inquirer.prompt([{
30
+ type: 'list',
31
+ name: 'role',
32
+ message: 'Role',
33
+ choices: Object.entries(ROLES).map(([key, val]) => ({
34
+ name: `${colors.white(val.label)} ${colors.dim(val.description)}`,
35
+ value: key,
36
+ short: val.label,
37
+ })),
38
+ default: 'fullstack',
39
+ prefix: colors.brand('?'),
40
+ }]);
41
+
42
+ role = result.role;
43
+ completedSteps.push(formatStep('Role', ROLES[role].label));
38
44
  }
39
45
 
40
46
  const components = PRESETS[preset];
@@ -43,7 +49,7 @@ export async function initCommand(options) {
43
49
  process.exit(1);
44
50
  }
45
51
 
46
- // ─── Conflict detection ─────────────────────────────────────
52
+ // ─── Step 2: Conflicts (conditional) ───────────────────────
47
53
  const installer = new Installer({
48
54
  force: options.force,
49
55
  dryRun: options.dryRun,
@@ -61,44 +67,53 @@ export async function initCommand(options) {
61
67
  if (conflictKeys.length > 0) {
62
68
  const totalFiles = conflictKeys.reduce((sum, k) => sum + conflicts[k].files.length, 0);
63
69
 
64
- console.log(` ${colors.warning(icons.warn)} ${totalFiles} existing file(s) found.`);
70
+ renderScreen(version, completedSteps);
71
+ console.log(` ${colors.warning(icons.warn)} ${totalFiles} existing file(s) found`);
65
72
  console.log();
66
73
 
67
- const { action } = await inquirer.prompt([
68
- {
69
- type: 'list',
70
- name: 'action',
71
- message: 'How to handle?',
72
- choices: [
73
- {
74
- name: `${colors.white('Keep existing')} ${colors.dim('merge compatible, skip rest')}`,
75
- value: 'skip',
76
- short: 'Keep',
77
- },
78
- {
79
- name: `${colors.warning('Overwrite all')} ${colors.dim('replace with latest')}`,
80
- value: 'overwrite',
81
- short: 'Overwrite',
82
- },
83
- ],
84
- default: 'skip',
85
- prefix: colors.brand('?'),
86
- },
87
- ]);
74
+ const { action } = await inquirer.prompt([{
75
+ type: 'list',
76
+ name: 'action',
77
+ message: 'How to handle?',
78
+ choices: [
79
+ {
80
+ name: `${colors.white('Keep existing')} ${colors.dim('merge compatible, skip rest')}`,
81
+ value: 'skip',
82
+ short: 'Keep',
83
+ },
84
+ {
85
+ name: `${colors.warning('Overwrite all')} ${colors.dim('replace with latest')}`,
86
+ value: 'overwrite',
87
+ short: 'Overwrite',
88
+ },
89
+ ],
90
+ default: 'skip',
91
+ prefix: colors.brand('?'),
92
+ }]);
88
93
 
89
94
  for (const key of conflictKeys) {
90
95
  resolutions[key] = action;
91
96
  }
97
+
98
+ completedSteps.push(
99
+ formatStep('Conflicts', action === 'skip' ? 'Keep existing' : 'Overwrite all')
100
+ );
92
101
  }
93
102
  }
94
103
 
95
104
  installer.resolutions = resolutions;
96
105
 
97
- // ─── Install ────────────────────────────────────────────────
106
+ // ─── Step 3: Install ───────────────────────────────────────
107
+ if (options.interaction !== false) {
108
+ renderScreen(version, completedSteps);
109
+ } else {
110
+ printBanner(version);
111
+ }
112
+
98
113
  if (options.dryRun) {
99
114
  console.log(` ${colors.dim('dry-run — no changes will be made')}`);
115
+ console.log();
100
116
  }
101
- console.log();
102
117
 
103
118
  for (const componentName of components) {
104
119
  const component = COMPONENTS[componentName];
@@ -109,6 +124,12 @@ export async function initCommand(options) {
109
124
  const beforeMerged = installer.results.merged.length;
110
125
  const beforeSkipped = installer.results.skipped.length;
111
126
 
127
+ const spinner = ora({
128
+ text: label,
129
+ indent: 2,
130
+ color: 'magenta',
131
+ }).start();
132
+
112
133
  try {
113
134
  await installer.installComponent(componentName, component);
114
135
 
@@ -117,16 +138,28 @@ export async function initCommand(options) {
117
138
  const newSkipped = installer.results.skipped.length - beforeSkipped;
118
139
 
119
140
  if (newMerged > 0 && newInstalled === 0 && newSkipped === 0) {
120
- stepMerge(label, 'merged');
141
+ spinner.stopAndPersist({
142
+ symbol: colors.blue(icons.merge),
143
+ text: `${label} ${colors.dim('merged')}`,
144
+ });
121
145
  } else if (newSkipped > 0 && newInstalled === 0 && newMerged === 0) {
122
- stepSkip(label, 'unchanged');
146
+ spinner.stopAndPersist({
147
+ symbol: colors.muted(icons.skip),
148
+ text: `${colors.muted(label)} ${colors.dim('unchanged')}`,
149
+ });
123
150
  } else {
124
151
  const count = newInstalled + newMerged;
125
- const detail = count > 0 ? `${count} file${count > 1 ? 's' : ''}` : '';
126
- stepDone(label, detail);
152
+ const detail = count > 0 ? ` ${count} file${count > 1 ? 's' : ''}` : '';
153
+ spinner.stopAndPersist({
154
+ symbol: colors.success(icons.check),
155
+ text: `${label}${colors.dim(detail)}`,
156
+ });
127
157
  }
128
158
  } catch (err) {
129
- stepFail(label, err.message);
159
+ spinner.stopAndPersist({
160
+ symbol: colors.error(icons.cross),
161
+ text: `${label} ${colors.dim(err.message)}`,
162
+ });
130
163
  installer.results.errors.push({ component: componentName, error: err.message });
131
164
  }
132
165
  }
package/src/core/ui.js CHANGED
@@ -23,6 +23,11 @@ export const colors = {
23
23
  blue: chalk.hex('#3B82F6'),
24
24
  };
25
25
 
26
+ // ─── Screen control ─────────────────────────────────────────
27
+ export function clearScreen() {
28
+ process.stdout.write('\x1B[2J\x1B[3J\x1B[H');
29
+ }
30
+
26
31
  // ─── Banner (single line) ───────────────────────────────────
27
32
  export function printBanner(version) {
28
33
  console.log();
@@ -30,6 +35,16 @@ export function printBanner(version) {
30
35
  console.log();
31
36
  }
32
37
 
38
+ // ─── Step-by-step screen (clear + banner + previous answers) ─
39
+ export function renderScreen(version, completedSteps = []) {
40
+ clearScreen();
41
+ printBanner(version);
42
+ for (const step of completedSteps) {
43
+ console.log(step);
44
+ }
45
+ if (completedSteps.length > 0) console.log();
46
+ }
47
+
33
48
  // ─── Step indicators ────────────────────────────────────────
34
49
  export function stepDone(label, detail) {
35
50
  const d = detail ? colors.dim(` ${detail}`) : '';