@archetypeai/ds-cli 0.3.18 → 0.3.20

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/README.md CHANGED
@@ -66,7 +66,7 @@ npx @archetypeai/ds-cli init --pm npm --fonts /path/to/fonts --codeagent claude
66
66
 
67
67
  ---
68
68
 
69
- ### `add` → Add components or agent config
69
+ ### `add` → Add components, tokens, or agent config
70
70
 
71
71
  #### `add ds-ui-svelte`
72
72
 
@@ -74,7 +74,37 @@ npx @archetypeai/ds-cli init --pm npm --fonts /path/to/fonts --codeagent claude
74
74
  npx @archetypeai/ds-cli add ds-ui-svelte
75
75
  ```
76
76
 
77
- Installs all components from the registry. Requires shadcn-svelte (`components.json` must exist).
77
+ Installs design tokens, all components from the registry, and optionally local fonts. Requires shadcn-svelte (`components.json` must exist).
78
+
79
+ | Flag | Values | Default |
80
+ |------|--------|---------|
81
+ | `--fonts <path>` | path to PP Neue Montreal fonts folder | prompt |
82
+ | `--no-fonts` | skip font installation (use system fallback) | — |
83
+
84
+ ```bash
85
+ npx @archetypeai/ds-cli add ds-ui-svelte --fonts /path/to/fonts
86
+ npx @archetypeai/ds-cli add ds-ui-svelte --no-fonts
87
+ ```
88
+
89
+ #### `add ds-lib-tokens`
90
+
91
+ ```bash
92
+ npx @archetypeai/ds-cli add ds-lib-tokens
93
+ ```
94
+
95
+ Installs `@archetypeai/ds-lib-tokens` and optionally local fonts.
96
+
97
+ | Flag | Values | Default |
98
+ |------|--------|---------|
99
+ | `--fonts <path>` | path to PP Neue Montreal fonts folder | prompt |
100
+ | `--no-fonts` | skip font installation (use system fallback) | — |
101
+
102
+ ```bash
103
+ npx @archetypeai/ds-cli add ds-lib-tokens --fonts /path/to/fonts
104
+ npx @archetypeai/ds-cli add ds-lib-tokens --no-fonts
105
+ ```
106
+
107
+ **Non-interactive mode** (no TTY or `--defaults`): font flags must be specified. The CLI lists missing options and exits so agents can ask the user before proceeding.
78
108
 
79
109
  #### `add ds-config-codeagent`
80
110
 
package/commands/add.js CHANGED
@@ -1,16 +1,24 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import { addDsUiSvelte } from '../lib/add-ds-ui-svelte.js';
3
+ import { addDsLibTokens } from '../lib/add-ds-lib-tokens.js';
3
4
  import { addDsConfigCodeagent } from '../lib/add-ds-config-codeagent.js';
4
5
 
5
- const VALID_TARGETS = ['ds-ui-svelte', 'ds-config-codeagent'];
6
+ const VALID_TARGETS = ['ds-ui-svelte', 'ds-lib-tokens', 'ds-config-codeagent'];
6
7
 
7
8
  function printUsage() {
8
9
  console.log(`
9
10
  ds add - Add components or configurations
10
11
 
11
12
  Usage:
12
- npx @archetypeai/ds-cli add ds-ui-svelte Install all design system components
13
+ npx @archetypeai/ds-cli add ds-ui-svelte [flags] Install all design system components
14
+ npx @archetypeai/ds-cli add ds-lib-tokens [flags] Install design tokens
13
15
  npx @archetypeai/ds-cli add ds-config-codeagent [flags] Install agent configuration
16
+
17
+ Font flags (ds-ui-svelte, ds-lib-tokens):
18
+ --fonts <path> Install internal fonts from local path
19
+ --no-fonts Skip font installation
20
+
21
+ Agent flags (ds-config-codeagent):
14
22
  --cursor Setup for Cursor IDE
15
23
  --claude Setup for Claude Code
16
24
  `);
@@ -24,6 +32,9 @@ export async function add(args) {
24
32
  case 'ds-ui-svelte':
25
33
  await addDsUiSvelte(flags);
26
34
  break;
35
+ case 'ds-lib-tokens':
36
+ await addDsLibTokens(flags);
37
+ break;
27
38
  case 'ds-config-codeagent':
28
39
  await addDsConfigCodeagent(flags);
29
40
  break;
@@ -0,0 +1,129 @@
1
+ import { execFile } from 'child_process';
2
+ import { promisify } from 'util';
3
+ import * as p from '@clack/prompts';
4
+ import { detectPm, getPm } from './use-package-manager.js';
5
+ import { validateFontsPath, installLocalFonts } from './install-fonts-local.js';
6
+ import { isInteractive } from './is-interactive.js';
7
+
8
+ const execFileAsync = promisify(execFile);
9
+
10
+ function parseFlags(args) {
11
+ const flags = { fonts: null, defaults: false };
12
+ for (let i = 0; i < args.length; i++) {
13
+ const arg = args[i];
14
+ if (arg === '--fonts' && args[i + 1]) {
15
+ flags.fonts = args[++i];
16
+ } else if (arg === '--no-fonts') {
17
+ flags.fonts = false;
18
+ } else if (arg === '--defaults') {
19
+ flags.defaults = true;
20
+ }
21
+ }
22
+ return flags;
23
+ }
24
+
25
+ export async function addDsLibTokens(args) {
26
+ const flags = parseFlags(args);
27
+ const interactive = isInteractive(flags);
28
+ const projectDir = process.cwd();
29
+
30
+ // preflight: in non-interactive mode, list unspecified options and exit
31
+ if (!interactive && flags.fonts === null) {
32
+ console.log(
33
+ '[CONFIGURATION REQUIRED] Do not assume defaults. Ask the user to choose each option:\n\n' +
34
+ ' --fonts <path> | --no-fonts Internal fonts (PP Neue Montreal)\n\n' +
35
+ 'Once the user has answered, re-run with their choices:\n' +
36
+ ' ds add ds-lib-tokens --fonts <path>\n' +
37
+ ' ds add ds-lib-tokens --no-fonts\n'
38
+ );
39
+ process.exit(1);
40
+ }
41
+
42
+ p.intro('Install design tokens from Archetype AI Design System.');
43
+
44
+ // detect package manager from lockfile, fall back to npm
45
+ const pmName = detectPm(projectDir) || 'npm';
46
+ const pm = getPm(pmName);
47
+
48
+ // install tokens
49
+ const s = p.spinner();
50
+ s.start('Installing design tokens');
51
+ try {
52
+ const [cmd, ...baseArgs] = pm.install.split(/\s+/);
53
+ await execFileAsync(cmd, [...baseArgs, '@archetypeai/ds-lib-tokens'], {
54
+ stdio: 'pipe',
55
+ maxBuffer: 10 * 1024 * 1024,
56
+ cwd: projectDir
57
+ });
58
+ s.stop('Design tokens installed');
59
+ } catch (error) {
60
+ s.stop('Failed');
61
+ p.log.error(error.stderr?.toString() || error.message);
62
+ process.exit(1);
63
+ }
64
+
65
+ // local fonts step
66
+ let fontsPath = null;
67
+ let includeFonts = false;
68
+ if (flags.fonts === false) {
69
+ // --no-fonts: skip
70
+ } else if (typeof flags.fonts === 'string') {
71
+ fontsPath = flags.fonts;
72
+ const result = validateFontsPath(fontsPath);
73
+ if (!result.valid) {
74
+ if (!interactive) {
75
+ p.log.error(result.error);
76
+ process.exit(1);
77
+ }
78
+ p.log.warn(result.error);
79
+ p.log.warn('Continuing without fonts.');
80
+ fontsPath = null;
81
+ } else {
82
+ includeFonts = true;
83
+ }
84
+ } else {
85
+ const useFonts = await p.confirm({
86
+ message: 'Do you want to use internal fonts?',
87
+ initialValue: false
88
+ });
89
+ if (p.isCancel(useFonts)) {
90
+ p.cancel('Setup cancelled.');
91
+ process.exit(0);
92
+ }
93
+ if (useFonts) {
94
+ const fontInput = await p.text({
95
+ message: 'Drop your fonts folder here:',
96
+ placeholder: '/path/to/fonts'
97
+ });
98
+ if (p.isCancel(fontInput)) {
99
+ p.cancel('Setup cancelled.');
100
+ process.exit(0);
101
+ }
102
+ if (fontInput && fontInput.trim()) {
103
+ fontsPath = fontInput.trim();
104
+ const result = validateFontsPath(fontsPath);
105
+ if (!result.valid) {
106
+ p.log.warn(result.error);
107
+ p.log.warn('Continuing without fonts.');
108
+ fontsPath = null;
109
+ } else {
110
+ includeFonts = true;
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ if (includeFonts && fontsPath) {
117
+ const fontSpinner = p.spinner();
118
+ fontSpinner.start('Installing fonts');
119
+ const fontResult = installLocalFonts(fontsPath, projectDir);
120
+ if (fontResult.success) {
121
+ fontSpinner.stop(`${fontResult.fileCount} font files installed`);
122
+ } else {
123
+ fontSpinner.stop('Failed to install fonts');
124
+ p.log.warn(fontResult.error);
125
+ }
126
+ }
127
+
128
+ p.outro('Design tokens installed successfully.');
129
+ }
@@ -1,11 +1,13 @@
1
1
  import { execFile } from 'child_process';
2
2
  import { promisify } from 'util';
3
- import { existsSync, readFileSync, writeFileSync } from 'fs';
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
4
4
  import { join, dirname } from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import * as p from '@clack/prompts';
7
7
  import { validateRegistryUrl } from './validate-url.js';
8
8
  import { detectPm, getPm } from './use-package-manager.js';
9
+ import { validateFontsPath, installLocalFonts } from './install-fonts-local.js';
10
+ import { isInteractive } from './is-interactive.js';
9
11
 
10
12
  const execFileAsync = promisify(execFile);
11
13
 
@@ -13,22 +15,84 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
13
15
  const REGISTRY_URL = process.env.REGISTRY_URL || 'https://design-system.archetypeai.workers.dev';
14
16
  const ALL_COMPONENTS_URL = `${REGISTRY_URL}/r/all.json`;
15
17
 
16
- export async function addDsUiSvelte() {
17
- p.intro('Install all components from Archetype AI Design System.');
18
+ function parseFlags(args) {
19
+ const flags = { fonts: null, defaults: false };
20
+ for (let i = 0; i < args.length; i++) {
21
+ const arg = args[i];
22
+ if (arg === '--fonts' && args[i + 1]) {
23
+ flags.fonts = args[++i];
24
+ } else if (arg === '--no-fonts') {
25
+ flags.fonts = false;
26
+ } else if (arg === '--defaults') {
27
+ flags.defaults = true;
28
+ }
29
+ }
30
+ return flags;
31
+ }
18
32
 
33
+ export async function addDsUiSvelte(args) {
34
+ const flags = parseFlags(args);
35
+ const interactive = isInteractive(flags);
19
36
  const projectDir = process.cwd();
20
37
 
21
- // check if shadcn-svelte is initialized
22
- if (!existsSync(join(projectDir, 'components.json'))) {
23
- p.log.error('shadcn-svelte is not initialized in this project.');
24
- p.log.info('Please run: npx shadcn-svelte@latest init');
38
+ // preflight: in non-interactive mode, list unspecified options and exit
39
+ if (!interactive && flags.fonts === null) {
40
+ console.log(
41
+ '[CONFIGURATION REQUIRED] Do not assume defaults. Ask the user to choose each option:\n\n' +
42
+ ' --fonts <path> | --no-fonts Internal fonts (PP Neue Montreal)\n\n' +
43
+ 'Once the user has answered, re-run with their choices:\n' +
44
+ ' ds add ds-ui-svelte --fonts <path>\n' +
45
+ ' ds add ds-ui-svelte --no-fonts\n'
46
+ );
25
47
  process.exit(1);
26
48
  }
27
49
 
50
+ p.intro('Install all components from Archetype AI Design System.');
51
+
28
52
  // detect package manager from lockfile, fall back to npm
29
53
  const pmName = detectPm(projectDir) || 'npm';
30
54
  const pm = getPm(pmName);
31
55
 
56
+ // initialize shadcn-svelte if not already set up
57
+ const componentsJsonPath = join(projectDir, 'components.json');
58
+ if (!existsSync(componentsJsonPath)) {
59
+ p.log.info('shadcn-svelte not initialized, setting up...');
60
+ const componentsJson = {
61
+ $schema: 'https://shadcn-svelte.com/schema.json',
62
+ tailwind: {
63
+ css: 'src/routes/layout.css',
64
+ baseColor: 'slate'
65
+ },
66
+ aliases: {
67
+ utils: '$lib/utils',
68
+ ui: '$lib/components/ui',
69
+ components: '$lib/components',
70
+ hooks: '$lib/hooks',
71
+ lib: '$lib'
72
+ },
73
+ typescript: false
74
+ };
75
+ writeFileSync(componentsJsonPath, JSON.stringify(componentsJson, null, 2));
76
+
77
+ const utilsPath = join(projectDir, 'src', 'lib', 'utils.js');
78
+ if (!existsSync(utilsPath)) {
79
+ const utilsDir = dirname(utilsPath);
80
+ if (!existsSync(utilsDir)) mkdirSync(utilsDir, { recursive: true });
81
+ writeFileSync(
82
+ utilsPath,
83
+ `import { clsx } from "clsx";\nimport { twMerge } from "tailwind-merge";\n\nexport function cn(...inputs) {\n\treturn twMerge(clsx(inputs));\n}\n`
84
+ );
85
+ }
86
+
87
+ const [setupCmd, ...setupArgs] = pm.install.split(/\s+/);
88
+ await execFileAsync(setupCmd, [...setupArgs, 'clsx', 'tailwind-merge', 'tw-animate-css'], {
89
+ stdio: 'pipe',
90
+ maxBuffer: 10 * 1024 * 1024,
91
+ cwd: projectDir
92
+ });
93
+ p.log.success('shadcn-svelte initialized');
94
+ }
95
+
32
96
  const s = p.spinner();
33
97
  s.start('Fetching component list from registry');
34
98
 
@@ -52,6 +116,80 @@ export async function addDsUiSvelte() {
52
116
 
53
117
  s.stop(`Found ${componentUrls.length} components`);
54
118
 
119
+ // install design tokens
120
+ const tokenSpinner = p.spinner();
121
+ tokenSpinner.start('Installing design tokens');
122
+ const [installCmd, ...installArgs] = pm.install.split(/\s+/);
123
+ await execFileAsync(installCmd, [...installArgs, '@archetypeai/ds-lib-tokens'], {
124
+ stdio: 'pipe',
125
+ maxBuffer: 10 * 1024 * 1024,
126
+ cwd: projectDir
127
+ });
128
+ tokenSpinner.stop('Design tokens installed');
129
+
130
+ // local fonts step
131
+ let fontsPath = null;
132
+ let includeFonts = false;
133
+ if (flags.fonts === false) {
134
+ // --no-fonts: skip
135
+ } else if (typeof flags.fonts === 'string') {
136
+ fontsPath = flags.fonts;
137
+ const result = validateFontsPath(fontsPath);
138
+ if (!result.valid) {
139
+ if (!interactive) {
140
+ p.log.error(result.error);
141
+ process.exit(1);
142
+ }
143
+ p.log.warn(result.error);
144
+ p.log.warn('Continuing without fonts.');
145
+ fontsPath = null;
146
+ } else {
147
+ includeFonts = true;
148
+ }
149
+ } else {
150
+ const useFonts = await p.confirm({
151
+ message: 'Do you want to use internal fonts?',
152
+ initialValue: false
153
+ });
154
+ if (p.isCancel(useFonts)) {
155
+ p.cancel('Setup cancelled.');
156
+ process.exit(0);
157
+ }
158
+ if (useFonts) {
159
+ const fontInput = await p.text({
160
+ message: 'Drop your fonts folder here:',
161
+ placeholder: '/path/to/fonts'
162
+ });
163
+ if (p.isCancel(fontInput)) {
164
+ p.cancel('Setup cancelled.');
165
+ process.exit(0);
166
+ }
167
+ if (fontInput && fontInput.trim()) {
168
+ fontsPath = fontInput.trim();
169
+ const result = validateFontsPath(fontsPath);
170
+ if (!result.valid) {
171
+ p.log.warn(result.error);
172
+ p.log.warn('Continuing without fonts.');
173
+ fontsPath = null;
174
+ } else {
175
+ includeFonts = true;
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ if (includeFonts && fontsPath) {
182
+ const fontSpinner = p.spinner();
183
+ fontSpinner.start('Installing fonts');
184
+ const fontResult = installLocalFonts(fontsPath, projectDir);
185
+ if (fontResult.success) {
186
+ fontSpinner.stop(`${fontResult.fileCount} font files installed`);
187
+ } else {
188
+ fontSpinner.stop('Failed to install fonts');
189
+ p.log.warn(fontResult.error);
190
+ }
191
+ }
192
+
55
193
  const installSpinner = p.spinner();
56
194
  installSpinner.start('Installing components');
57
195
 
@@ -59,7 +197,7 @@ export async function addDsUiSvelte() {
59
197
  await execFileAsync(
60
198
  dlxCmd,
61
199
  [...dlxArgs, 'shadcn-svelte@latest', 'add', ...componentUrls, '--yes', '--overwrite'],
62
- { stdio: 'pipe', cwd: projectDir }
200
+ { stdio: 'pipe', maxBuffer: 10 * 1024 * 1024, cwd: projectDir }
63
201
  );
64
202
 
65
203
  installSpinner.stop('All components installed');
@@ -9,7 +9,7 @@ const execFileAsync = promisify(execFile);
9
9
 
10
10
  // run a command without a shell to prevent injection
11
11
  async function run(command, args, opts) {
12
- await execFileAsync(command, args, { stdio: 'pipe', ...opts });
12
+ await execFileAsync(command, args, { stdio: 'pipe', maxBuffer: 10 * 1024 * 1024, ...opts });
13
13
  }
14
14
 
15
15
  // write a file
@@ -95,19 +95,16 @@ export async function initShadcn(pm, projectPath) {
95
95
  try {
96
96
  const componentsJson = {
97
97
  $schema: 'https://shadcn-svelte.com/schema.json',
98
- style: 'default',
99
98
  tailwind: {
100
- config: '',
101
99
  css: 'src/routes/layout.css',
102
- baseColor: 'slate',
103
- cssVariables: true,
104
- prefix: ''
100
+ baseColor: 'slate'
105
101
  },
106
102
  aliases: {
107
103
  utils: '$lib/utils',
108
- primitives: '$lib/components/primitives',
109
- lib: '$lib',
110
- components: '$lib/components'
104
+ ui: '$lib/components/ui',
105
+ components: '$lib/components',
106
+ hooks: '$lib/hooks',
107
+ lib: '$lib'
111
108
  },
112
109
  typescript: false
113
110
  };
@@ -218,9 +215,7 @@ export async function installComponents(pm, projectPath, componentUrls) {
218
215
  await run(
219
216
  dlxCmd,
220
217
  [...dlxArgs, 'shadcn-svelte@latest', 'add', ...urls, '--yes', '--overwrite'],
221
- {
222
- cwd: projectPath
223
- }
218
+ { cwd: projectPath }
224
219
  );
225
220
 
226
221
  const [installCmd, ...installArgs] = splitCmd(pm.install);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archetypeai/ds-cli",
3
- "version": "0.3.18",
3
+ "version": "0.3.20",
4
4
  "description": "Archetype AI Design System CLI Tool",
5
5
  "type": "module",
6
6
  "bin": {