@archetypeai/ds-cli 0.3.19 → 0.3.21

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,137 @@
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
+ import { prependCss } from './scaffold-ds-svelte-project.js';
8
+
9
+ const execFileAsync = promisify(execFile);
10
+
11
+ function parseFlags(args) {
12
+ const flags = { fonts: null, defaults: false };
13
+ for (let i = 0; i < args.length; i++) {
14
+ const arg = args[i];
15
+ if (arg === '--fonts' && args[i + 1]) {
16
+ flags.fonts = args[++i];
17
+ } else if (arg === '--no-fonts') {
18
+ flags.fonts = false;
19
+ } else if (arg === '--defaults') {
20
+ flags.defaults = true;
21
+ }
22
+ }
23
+ return flags;
24
+ }
25
+
26
+ export async function addDsLibTokens(args) {
27
+ const flags = parseFlags(args);
28
+ const interactive = isInteractive(flags);
29
+ const projectDir = process.cwd();
30
+
31
+ // preflight: in non-interactive mode, list unspecified options and exit
32
+ if (!interactive && flags.fonts === null) {
33
+ console.log(
34
+ '[CONFIGURATION REQUIRED] Do not assume defaults. Ask the user to choose each option:\n\n' +
35
+ ' --fonts <path> | --no-fonts Internal fonts (PP Neue Montreal)\n\n' +
36
+ 'Once the user has answered, re-run with their choices:\n' +
37
+ ' ds add ds-lib-tokens --fonts <path>\n' +
38
+ ' ds add ds-lib-tokens --no-fonts\n'
39
+ );
40
+ process.exit(1);
41
+ }
42
+
43
+ p.intro('Install design tokens from Archetype AI Design System.');
44
+
45
+ // detect package manager from lockfile, fall back to npm
46
+ const pmName = detectPm(projectDir) || 'npm';
47
+ const pm = getPm(pmName);
48
+
49
+ // install tokens
50
+ const s = p.spinner();
51
+ s.start('Installing design tokens');
52
+ try {
53
+ const [cmd, ...baseArgs] = pm.install.split(/\s+/);
54
+ await execFileAsync(
55
+ cmd,
56
+ [...baseArgs, '@archetypeai/ds-lib-tokens', 'tailwindcss', 'tw-animate-css'],
57
+ {
58
+ stdio: 'pipe',
59
+ maxBuffer: 10 * 1024 * 1024,
60
+ cwd: projectDir
61
+ }
62
+ );
63
+ s.stop('Design tokens installed');
64
+ } catch (error) {
65
+ s.stop('Failed');
66
+ p.log.error(error.stderr?.toString() || error.message);
67
+ process.exit(1);
68
+ }
69
+
70
+ // local fonts step
71
+ let fontsPath = null;
72
+ let includeFonts = false;
73
+ if (flags.fonts === false) {
74
+ // --no-fonts: skip
75
+ } else if (typeof flags.fonts === 'string') {
76
+ fontsPath = flags.fonts;
77
+ const result = validateFontsPath(fontsPath);
78
+ if (!result.valid) {
79
+ if (!interactive) {
80
+ p.log.error(result.error);
81
+ process.exit(1);
82
+ }
83
+ p.log.warn(result.error);
84
+ p.log.warn('Continuing without fonts.');
85
+ fontsPath = null;
86
+ } else {
87
+ includeFonts = true;
88
+ }
89
+ } else {
90
+ const useFonts = await p.confirm({
91
+ message: 'Do you want to use internal fonts?',
92
+ initialValue: false
93
+ });
94
+ if (p.isCancel(useFonts)) {
95
+ p.cancel('Setup cancelled.');
96
+ process.exit(0);
97
+ }
98
+ if (useFonts) {
99
+ const fontInput = await p.text({
100
+ message: 'Drop your fonts folder here:',
101
+ placeholder: '/path/to/fonts'
102
+ });
103
+ if (p.isCancel(fontInput)) {
104
+ p.cancel('Setup cancelled.');
105
+ process.exit(0);
106
+ }
107
+ if (fontInput && fontInput.trim()) {
108
+ fontsPath = fontInput.trim();
109
+ const result = validateFontsPath(fontsPath);
110
+ if (!result.valid) {
111
+ p.log.warn(result.error);
112
+ p.log.warn('Continuing without fonts.');
113
+ fontsPath = null;
114
+ } else {
115
+ includeFonts = true;
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ if (includeFonts && fontsPath) {
122
+ const fontSpinner = p.spinner();
123
+ fontSpinner.start('Installing fonts');
124
+ const fontResult = installLocalFonts(fontsPath, projectDir);
125
+ if (fontResult.success) {
126
+ fontSpinner.stop(`${fontResult.fileCount} font files installed`);
127
+ } else {
128
+ fontSpinner.stop('Failed to install fonts');
129
+ p.log.warn(fontResult.error);
130
+ }
131
+ }
132
+
133
+ // configure CSS imports in layout.css
134
+ prependCss(projectDir, includeFonts);
135
+
136
+ p.outro('Design tokens installed successfully.');
137
+ }
@@ -1,11 +1,14 @@
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';
11
+ import { prependCss } from './scaffold-ds-svelte-project.js';
9
12
 
10
13
  const execFileAsync = promisify(execFile);
11
14
 
@@ -13,22 +16,84 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
13
16
  const REGISTRY_URL = process.env.REGISTRY_URL || 'https://design-system.archetypeai.workers.dev';
14
17
  const ALL_COMPONENTS_URL = `${REGISTRY_URL}/r/all.json`;
15
18
 
16
- export async function addDsUiSvelte() {
17
- p.intro('Install all components from Archetype AI Design System.');
19
+ function parseFlags(args) {
20
+ const flags = { fonts: null, defaults: false };
21
+ for (let i = 0; i < args.length; i++) {
22
+ const arg = args[i];
23
+ if (arg === '--fonts' && args[i + 1]) {
24
+ flags.fonts = args[++i];
25
+ } else if (arg === '--no-fonts') {
26
+ flags.fonts = false;
27
+ } else if (arg === '--defaults') {
28
+ flags.defaults = true;
29
+ }
30
+ }
31
+ return flags;
32
+ }
18
33
 
34
+ export async function addDsUiSvelte(args) {
35
+ const flags = parseFlags(args);
36
+ const interactive = isInteractive(flags);
19
37
  const projectDir = process.cwd();
20
38
 
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');
39
+ // preflight: in non-interactive mode, list unspecified options and exit
40
+ if (!interactive && flags.fonts === null) {
41
+ console.log(
42
+ '[CONFIGURATION REQUIRED] Do not assume defaults. Ask the user to choose each option:\n\n' +
43
+ ' --fonts <path> | --no-fonts Internal fonts (PP Neue Montreal)\n\n' +
44
+ 'Once the user has answered, re-run with their choices:\n' +
45
+ ' ds add ds-ui-svelte --fonts <path>\n' +
46
+ ' ds add ds-ui-svelte --no-fonts\n'
47
+ );
25
48
  process.exit(1);
26
49
  }
27
50
 
51
+ p.intro('Install all components from Archetype AI Design System.');
52
+
28
53
  // detect package manager from lockfile, fall back to npm
29
54
  const pmName = detectPm(projectDir) || 'npm';
30
55
  const pm = getPm(pmName);
31
56
 
57
+ // initialize shadcn-svelte if not already set up
58
+ const componentsJsonPath = join(projectDir, 'components.json');
59
+ if (!existsSync(componentsJsonPath)) {
60
+ p.log.info('shadcn-svelte not initialized, setting up...');
61
+ const componentsJson = {
62
+ $schema: 'https://shadcn-svelte.com/schema.json',
63
+ tailwind: {
64
+ css: 'src/routes/layout.css',
65
+ baseColor: 'slate'
66
+ },
67
+ aliases: {
68
+ utils: '$lib/utils',
69
+ ui: '$lib/components/ui',
70
+ components: '$lib/components',
71
+ hooks: '$lib/hooks',
72
+ lib: '$lib'
73
+ },
74
+ typescript: false
75
+ };
76
+ writeFileSync(componentsJsonPath, JSON.stringify(componentsJson, null, 2));
77
+
78
+ const utilsPath = join(projectDir, 'src', 'lib', 'utils.js');
79
+ if (!existsSync(utilsPath)) {
80
+ const utilsDir = dirname(utilsPath);
81
+ if (!existsSync(utilsDir)) mkdirSync(utilsDir, { recursive: true });
82
+ writeFileSync(
83
+ utilsPath,
84
+ `import { clsx } from "clsx";\nimport { twMerge } from "tailwind-merge";\n\nexport function cn(...inputs) {\n\treturn twMerge(clsx(inputs));\n}\n`
85
+ );
86
+ }
87
+
88
+ const [setupCmd, ...setupArgs] = pm.install.split(/\s+/);
89
+ await execFileAsync(setupCmd, [...setupArgs, 'clsx', 'tailwind-merge', 'tw-animate-css'], {
90
+ stdio: 'pipe',
91
+ maxBuffer: 10 * 1024 * 1024,
92
+ cwd: projectDir
93
+ });
94
+ p.log.success('shadcn-svelte initialized');
95
+ }
96
+
32
97
  const s = p.spinner();
33
98
  s.start('Fetching component list from registry');
34
99
 
@@ -52,6 +117,83 @@ export async function addDsUiSvelte() {
52
117
 
53
118
  s.stop(`Found ${componentUrls.length} components`);
54
119
 
120
+ // install design tokens
121
+ const tokenSpinner = p.spinner();
122
+ tokenSpinner.start('Installing design tokens');
123
+ const [installCmd, ...installArgs] = pm.install.split(/\s+/);
124
+ await execFileAsync(installCmd, [...installArgs, '@archetypeai/ds-lib-tokens'], {
125
+ stdio: 'pipe',
126
+ maxBuffer: 10 * 1024 * 1024,
127
+ cwd: projectDir
128
+ });
129
+ tokenSpinner.stop('Design tokens installed');
130
+
131
+ // local fonts step
132
+ let fontsPath = null;
133
+ let includeFonts = false;
134
+ if (flags.fonts === false) {
135
+ // --no-fonts: skip
136
+ } else if (typeof flags.fonts === 'string') {
137
+ fontsPath = flags.fonts;
138
+ const result = validateFontsPath(fontsPath);
139
+ if (!result.valid) {
140
+ if (!interactive) {
141
+ p.log.error(result.error);
142
+ process.exit(1);
143
+ }
144
+ p.log.warn(result.error);
145
+ p.log.warn('Continuing without fonts.');
146
+ fontsPath = null;
147
+ } else {
148
+ includeFonts = true;
149
+ }
150
+ } else {
151
+ const useFonts = await p.confirm({
152
+ message: 'Do you want to use internal fonts?',
153
+ initialValue: false
154
+ });
155
+ if (p.isCancel(useFonts)) {
156
+ p.cancel('Setup cancelled.');
157
+ process.exit(0);
158
+ }
159
+ if (useFonts) {
160
+ const fontInput = await p.text({
161
+ message: 'Drop your fonts folder here:',
162
+ placeholder: '/path/to/fonts'
163
+ });
164
+ if (p.isCancel(fontInput)) {
165
+ p.cancel('Setup cancelled.');
166
+ process.exit(0);
167
+ }
168
+ if (fontInput && fontInput.trim()) {
169
+ fontsPath = fontInput.trim();
170
+ const result = validateFontsPath(fontsPath);
171
+ if (!result.valid) {
172
+ p.log.warn(result.error);
173
+ p.log.warn('Continuing without fonts.');
174
+ fontsPath = null;
175
+ } else {
176
+ includeFonts = true;
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ if (includeFonts && fontsPath) {
183
+ const fontSpinner = p.spinner();
184
+ fontSpinner.start('Installing fonts');
185
+ const fontResult = installLocalFonts(fontsPath, projectDir);
186
+ if (fontResult.success) {
187
+ fontSpinner.stop(`${fontResult.fileCount} font files installed`);
188
+ } else {
189
+ fontSpinner.stop('Failed to install fonts');
190
+ p.log.warn(fontResult.error);
191
+ }
192
+ }
193
+
194
+ // configure CSS imports in layout.css
195
+ prependCss(projectDir, includeFonts);
196
+
55
197
  const installSpinner = p.spinner();
56
198
  installSpinner.start('Installing components');
57
199
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archetypeai/ds-cli",
3
- "version": "0.3.19",
3
+ "version": "0.3.21",
4
4
  "description": "Archetype AI Design System CLI Tool",
5
5
  "type": "module",
6
6
  "bin": {