@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 +32 -2
- package/commands/add.js +13 -2
- package/lib/add-ds-lib-tokens.js +137 -0
- package/lib/add-ds-ui-svelte.js +149 -7
- package/package.json +1 -1
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
|
|
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
|
+
}
|
package/lib/add-ds-ui-svelte.js
CHANGED
|
@@ -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
|
-
|
|
17
|
-
|
|
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
|
-
//
|
|
22
|
-
if (!
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|