virastack 1.0.5 → 1.1.0-beta.1
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 +29 -17
- package/package.json +5 -4
- package/src/commands/add.js +12 -5
- package/src/commands/init.js +68 -32
- package/src/i18n.js +36 -20
- package/src/index.js +15 -1
- package/src/utils/agents.js +85 -0
- package/src/utils/args.js +1 -1
- package/src/utils/git.js +18 -4
- package/src/utils/next-agents-md.js +40 -0
- package/src/utils/package-manager.js +5 -2
- package/src/utils/tools.js +1 -1
- package/templates/nextjs/.github/workflows/ci.yml +3 -0
- package/templates/nextjs/.prettierignore +9 -0
- package/templates/nextjs/.prettierrc +0 -1
- package/templates/nextjs/README.md +21 -15
- package/templates/nextjs/eslint.config.mjs +41 -0
- package/templates/nextjs/knip.config.js +2 -0
- package/templates/nextjs/package.json +14 -3
- package/templates/nextjs/pnpm-workspace.yaml +1 -0
- package/templates/nextjs/public/llms.txt +15 -11
- package/templates/nextjs/src/config/site.config.ts +1 -1
- package/templates/nextjs/src/features/landing/api/get-users.api.test.ts +34 -0
- package/templates/nextjs/src/features/landing/api/index.ts +1 -0
- package/templates/nextjs/src/features/landing/api/user.queries.ts +10 -0
- package/templates/nextjs/src/features/landing/components/CartDemo.test.tsx +22 -0
- package/templates/nextjs/src/features/landing/components/Features.tsx +1 -1
- package/templates/nextjs/src/features/landing/components/Hero.tsx +2 -3
- package/templates/nextjs/src/features/landing/components/Reveal.tsx +1 -1
- package/templates/nextjs/src/features/landing/constants/index.ts +1 -0
- package/templates/nextjs/src/features/landing/helpers/get-initials.test.ts +17 -0
- package/templates/nextjs/src/features/landing/hooks/use-users.ts +2 -5
- package/templates/nextjs/src/features/landing/icons/tanstack.tsx +2 -314
- package/templates/nextjs/src/test/render.tsx +19 -0
- package/templates/nextjs/src/test/server.ts +7 -0
- package/templates/nextjs/src/test/setup.ts +15 -0
- package/templates/nextjs/tsconfig.json +0 -2
- package/templates/nextjs/vitest.config.ts +13 -0
- package/templates/tanstack/.github/workflows/ci.yml +3 -0
- package/templates/tanstack/.prettierignore +9 -0
- package/templates/tanstack/.prettierrc +0 -1
- package/templates/tanstack/README.md +21 -15
- package/templates/tanstack/eslint.config.js +47 -0
- package/templates/tanstack/package.json +12 -3
- package/templates/tanstack/pnpm-workspace.yaml +1 -0
- package/templates/tanstack/public/llms.txt +15 -11
- package/templates/tanstack/src/config/site.config.ts +1 -1
- package/templates/tanstack/src/features/landing/api/get-users.api.test.ts +34 -0
- package/templates/tanstack/src/features/landing/api/index.ts +1 -0
- package/templates/tanstack/src/features/landing/api/user.queries.ts +10 -0
- package/templates/tanstack/src/features/landing/components/CartDemo.test.tsx +22 -0
- package/templates/tanstack/src/features/landing/components/Features.tsx +1 -1
- package/templates/tanstack/src/features/landing/components/Hero.tsx +2 -3
- package/templates/tanstack/src/features/landing/components/Reveal.tsx +1 -1
- package/templates/tanstack/src/features/landing/components/UsersDemo.tsx +1 -1
- package/templates/tanstack/src/features/landing/constants/index.ts +1 -0
- package/templates/tanstack/src/features/landing/helpers/get-initials.test.ts +17 -0
- package/templates/tanstack/src/features/landing/hooks/use-users.ts +3 -6
- package/templates/tanstack/src/features/landing/icons/tanstack.tsx +2 -314
- package/templates/tanstack/src/test/render.tsx +19 -0
- package/templates/tanstack/src/test/server.ts +7 -0
- package/templates/tanstack/src/test/setup.ts +15 -0
- package/templates/tanstack/vitest.config.ts +13 -0
- package/templates-i18n/nextjs/.github/workflows/ci.yml +3 -0
- package/templates-i18n/nextjs/.prettierignore +9 -0
- package/templates-i18n/nextjs/.prettierrc +0 -1
- package/templates-i18n/nextjs/README.md +21 -15
- package/templates-i18n/nextjs/eslint.config.mjs +41 -0
- package/templates-i18n/nextjs/knip.config.js +4 -2
- package/templates-i18n/nextjs/{src/messages → messages}/en.json +1 -1
- package/templates-i18n/nextjs/{src/messages → messages}/tr.json +1 -1
- package/templates-i18n/nextjs/package.json +14 -3
- package/templates-i18n/nextjs/pnpm-workspace.yaml +1 -0
- package/templates-i18n/nextjs/public/llms.txt +48 -75
- package/templates-i18n/nextjs/src/components/shared/LanguageSwitcher.tsx +2 -2
- package/templates-i18n/nextjs/src/config/site.config.ts +1 -1
- package/templates-i18n/nextjs/src/features/landing/api/get-users.api.test.ts +34 -0
- package/templates-i18n/nextjs/src/features/landing/api/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/api/user.queries.ts +10 -0
- package/templates-i18n/nextjs/src/features/landing/components/CartDemo.test.tsx +22 -0
- package/templates-i18n/nextjs/src/features/landing/components/Hero.tsx +1 -2
- package/templates-i18n/nextjs/src/features/landing/components/Reveal.tsx +1 -1
- package/templates-i18n/nextjs/src/features/landing/constants/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/get-initials.test.ts +17 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/use-users.ts +2 -5
- package/templates-i18n/nextjs/src/features/landing/icons/tanstack.tsx +2 -314
- package/templates-i18n/nextjs/src/i18n/request.ts +1 -1
- package/templates-i18n/nextjs/src/test/render.tsx +26 -0
- package/templates-i18n/nextjs/src/test/server.ts +7 -0
- package/templates-i18n/nextjs/src/test/setup.ts +15 -0
- package/templates-i18n/nextjs/tsconfig.json +1 -2
- package/templates-i18n/nextjs/vitest.config.ts +13 -0
- package/templates-i18n/tanstack/.github/workflows/ci.yml +3 -0
- package/templates-i18n/tanstack/.prettierignore +12 -0
- package/templates-i18n/tanstack/.prettierrc +0 -1
- package/templates-i18n/tanstack/README.md +21 -15
- package/templates-i18n/tanstack/_gitignore +3 -0
- package/templates-i18n/tanstack/eslint.config.js +47 -0
- package/templates-i18n/tanstack/knip.config.js +2 -2
- package/templates-i18n/tanstack/messages/en.json +1 -1
- package/templates-i18n/tanstack/messages/tr.json +1 -1
- package/templates-i18n/tanstack/package.json +14 -4
- package/templates-i18n/tanstack/pnpm-workspace.yaml +1 -0
- package/templates-i18n/tanstack/public/llms.txt +44 -65
- package/templates-i18n/tanstack/src/components/shared/LanguageSwitcher.tsx +2 -2
- package/templates-i18n/tanstack/src/config/site.config.ts +1 -1
- package/templates-i18n/tanstack/src/features/landing/api/get-users.api.test.ts +34 -0
- package/templates-i18n/tanstack/src/features/landing/api/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/api/user.queries.ts +10 -0
- package/templates-i18n/tanstack/src/features/landing/components/CartDemo.test.tsx +22 -0
- package/templates-i18n/tanstack/src/features/landing/components/Hero.tsx +1 -2
- package/templates-i18n/tanstack/src/features/landing/components/Reveal.tsx +1 -1
- package/templates-i18n/tanstack/src/features/landing/constants/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/get-initials.test.ts +17 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/use-users.ts +3 -6
- package/templates-i18n/tanstack/src/features/landing/icons/tanstack.tsx +2 -314
- package/templates-i18n/tanstack/src/test/render.tsx +19 -0
- package/templates-i18n/tanstack/src/test/server.ts +7 -0
- package/templates-i18n/tanstack/src/test/setup.ts +15 -0
- package/templates-i18n/tanstack/vitest.config.ts +13 -0
- package/templates/nextjs/.eslintcache +0 -1
- package/templates/nextjs/src/features/landing/data/index.ts +0 -1
- package/templates/tanstack/.eslintcache +0 -1
- package/templates/tanstack/src/features/landing/data/index.ts +0 -1
- package/templates-i18n/nextjs/.eslintcache +0 -1
- package/templates-i18n/nextjs/src/features/landing/data/index.ts +0 -1
- package/templates-i18n/tanstack/.eslintcache +0 -1
- package/templates-i18n/tanstack/src/features/landing/data/index.ts +0 -1
- /package/templates/nextjs/src/features/landing/{data/features.data.tsx → constants/features.constants.tsx} +0 -0
- /package/templates/tanstack/src/features/landing/{data/features.data.tsx → constants/features.constants.tsx} +0 -0
- /package/templates-i18n/nextjs/src/features/landing/{data/features.data.tsx → constants/features.constants.tsx} +0 -0
- /package/templates-i18n/tanstack/src/features/landing/{data/features.data.tsx → constants/features.constants.tsx} +0 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</picture>
|
|
9
9
|
</a>
|
|
10
10
|
|
|
11
|
-
*
|
|
11
|
+
*Scaffolding CLI for Next.js and TanStack Start: ViraStack tools and AI rules in one command*
|
|
12
12
|
|
|
13
13
|
[](https://virastack.com/start/)
|
|
14
14
|
[](https://www.npmjs.com/package/virastack)
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
### [Read the full documentation →](https://virastack.com/start/)
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Features
|
|
27
27
|
|
|
28
28
|
- **Interactive CLI:** Interactive prompts for template selection, project naming, and ecosystem tool integration.
|
|
29
29
|
- **Production-Ready Foundations:** Scaffolds Next.js or TanStack Start projects with pre-configured feature-sliced architecture.
|
|
30
|
-
- **Built-in AI Protocols:**
|
|
30
|
+
- **Built-in AI Protocols:** Asks which AI coding tools you use (Claude Code, Cursor) and sets up [**ViraStack AI**](https://github.com/virastack/ai) for them — architecture rules in each tool's native format, `AGENTS.md`, and design skills from [**Emil Kowalski**](https://github.com/emilkowalski/skills) and [**Jakub Krehel**](https://github.com/jakubkrehel/skills) — giving your coding agents a strict "constitution" from day one.
|
|
31
31
|
- **End-to-End Type Safety:** Strict TypeScript rules, Zod validation, and native package manager alignment (pnpm, npm, yarn, bun).
|
|
32
32
|
- **Ecosystem Integration:** Add optional ViraStack tools (`@virastack/mask`, `@virastack/password`) seamlessly.
|
|
33
33
|
|
|
@@ -57,6 +57,10 @@ Which template would you like to use?
|
|
|
57
57
|
› Next.js App Router
|
|
58
58
|
TanStack Start
|
|
59
59
|
|
|
60
|
+
Which AI coding tools do you use?
|
|
61
|
+
[x] Claude Code
|
|
62
|
+
[x] Cursor
|
|
63
|
+
|
|
60
64
|
Would you like multi-language (i18n) support?
|
|
61
65
|
› Yes
|
|
62
66
|
No
|
|
@@ -66,6 +70,19 @@ Which ViraStack tools would you like to include?
|
|
|
66
70
|
[ ] @virastack/password
|
|
67
71
|
```
|
|
68
72
|
|
|
73
|
+
Select none to get plain rules in `.agents/rules/` that any agent can read through `AGENTS.md`.
|
|
74
|
+
|
|
75
|
+
## What it sets up
|
|
76
|
+
|
|
77
|
+
1. Copies the template and sets the project name.
|
|
78
|
+
2. Installs dependencies with the package manager you ran it with.
|
|
79
|
+
3. Next.js only: adds the Next.js block to `AGENTS.md`, pointing agents at the docs bundled in `node_modules/next/dist/docs/`.
|
|
80
|
+
4. Runs `@virastack/ai init --agent <your choice>`. It never overwrites files you changed.
|
|
81
|
+
5. Installs three design skills: `emil-design-eng` ([Emil Kowalski](https://github.com/emilkowalski/skills)), `better-ui` and `better-accessibility` ([Jakub Krehel](https://github.com/jakubkrehel/skills)). They go to `.agents/skills/` and, with Claude Code, to `.claude/skills/`. With Claude Code as the only tool, they go only to `.claude/skills/`.
|
|
82
|
+
6. Creates the first git commit, so `git status` is clean.
|
|
83
|
+
|
|
84
|
+
`--skip-install` skips step 2. Offline, steps 2, 4 and 5 are skipped and the commands to run later are printed.
|
|
85
|
+
|
|
69
86
|
## Tools
|
|
70
87
|
|
|
71
88
|
| Tool | Description |
|
|
@@ -83,9 +100,10 @@ Which ViraStack tools would you like to include?
|
|
|
83
100
|
| `--name <name>` | Project name (or pass as positional / `.`) |
|
|
84
101
|
| `--template <name>` | `nextjs` or `tanstack` |
|
|
85
102
|
| `--tools <list>` | Comma-separated tools (`mask,password`) |
|
|
103
|
+
| `--agent <list>` | AI coding tools: `claude`, `cursor` (comma separated) or `none`. Default with `--yes`: both |
|
|
86
104
|
| `--i18n` / `--no-i18n` | Enable or disable the i18n template |
|
|
87
105
|
| `--yes`, `-y` | Non-interactive mode (flags + defaults) |
|
|
88
|
-
| `--skip-install` |
|
|
106
|
+
| `--skip-install` | Skip dependency install (AI rules and skills are still added) |
|
|
89
107
|
| `--tr` | Turkish prompts |
|
|
90
108
|
| `--telemetry-disable` | Permanently disable anonymous usage tracking |
|
|
91
109
|
| `-v`, `--version` | Print CLI version |
|
|
@@ -95,6 +113,7 @@ Which ViraStack tools would you like to include?
|
|
|
95
113
|
pnpm dlx virastack add mask
|
|
96
114
|
pnpm dlx virastack add password
|
|
97
115
|
pnpm dlx virastack add ai
|
|
116
|
+
pnpm dlx virastack add ai --agent cursor
|
|
98
117
|
```
|
|
99
118
|
|
|
100
119
|
## Included by default
|
|
@@ -106,30 +125,23 @@ Every template ships with this stack already installed:
|
|
|
106
125
|
| **State** | TanStack Query, Zustand, nuqs |
|
|
107
126
|
| **Form** | React Hook Form, Zod |
|
|
108
127
|
| **Hooks** | `usehooks-ts` |
|
|
109
|
-
| **UI** | Base UI,
|
|
128
|
+
| **UI** | Base UI, Motion, Lucide React |
|
|
129
|
+
| **Test** | Vitest, Testing Library, MSW |
|
|
110
130
|
|
|
111
131
|
[ViraStack AI](https://github.com/virastack/ai) encodes these choices as agent rules so assistants follow the same stack from day one.
|
|
112
132
|
|
|
113
133
|
## Telemetry
|
|
114
134
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
Opt out anytime: `pnpm dlx virastack --telemetry-disable`
|
|
135
|
+
The CLI collects anonymous usage data only: template, options, tools, package manager and CLI version. Opt out: `pnpm dlx virastack --telemetry-disable`
|
|
118
136
|
|
|
119
137
|
## Contributing
|
|
120
138
|
|
|
121
139
|
Ideas and bug reports are welcome — open an [issue](https://github.com/virastack/start/issues).
|
|
122
140
|
|
|
123
|
-
##
|
|
141
|
+
## Ecosystem
|
|
124
142
|
|
|
125
|
-
|
|
143
|
+
Part of the [ViraStack](https://virastack.com) ecosystem: [Academy](https://github.com/virastack/academy) · [AI](https://github.com/virastack/ai) · [Mask](https://github.com/virastack/mask) · [Password](https://github.com/virastack/password) · [Guide](https://github.com/virastack/guide)
|
|
126
144
|
|
|
127
145
|
## License
|
|
128
146
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
## Maintainer
|
|
132
|
-
|
|
133
|
-
A project by [**Ömer Gülçiçek**](https://omergulcicek.com)
|
|
134
|
-
|
|
135
|
-
[](https://github.com/omergulcicek)
|
|
147
|
+
[MIT](LICENSE) © [Ömer Gülçiçek](https://omergulcicek.com)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "virastack",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.0-beta.1",
|
|
4
|
+
"description": "Scaffolding CLI for Next.js and TanStack Start: ViraStack tools and AI rules in one command",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Ömer Gülçiçek (ViraStack)",
|
|
7
7
|
"email": "iletisim@omergulcicek.com",
|
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
"!src/**/*.test.js"
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
|
-
"prepublishOnly": "find templates templates-i18n -type d \\( -name 'node_modules' -o -name '.next' -o -name 'dist' -o -name '.vinxi' -o -name '.output' -o -name '.turbo' \\) -prune -exec rm -rf '{}' +",
|
|
37
|
+
"prepublishOnly": "find templates templates-i18n -type d \\( -name 'node_modules' -o -name '.next' -o -name 'dist' -o -name '.vinxi' -o -name '.output' -o -name '.turbo' \\) -prune -exec rm -rf '{}' + && find templates templates-i18n -name '.eslintcache' -type f -delete",
|
|
38
38
|
"start": "node ./bin/virastack.js",
|
|
39
39
|
"test": "vitest run",
|
|
40
|
-
"test:watch": "vitest"
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"test:e2e": "node scripts/e2e.js"
|
|
41
42
|
},
|
|
42
43
|
"keywords": [
|
|
43
44
|
"virastack",
|
package/src/commands/add.js
CHANGED
|
@@ -8,6 +8,7 @@ import { runCommand } from "../utils/exec.js";
|
|
|
8
8
|
import { detectPackageManager, getAddArgs } from "../utils/package-manager.js";
|
|
9
9
|
import { trackEvent } from "../utils/telemetry.js";
|
|
10
10
|
import { findTool, TOOLS } from "../utils/tools.js";
|
|
11
|
+
import { getAiInitArgs } from "../utils/agents.js";
|
|
11
12
|
|
|
12
13
|
async function fileExists(filePath) {
|
|
13
14
|
try {
|
|
@@ -18,7 +19,11 @@ async function fileExists(filePath) {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
/**
|
|
23
|
+
* @param {string | undefined} toolId
|
|
24
|
+
* @param {{ agents?: string[] }} [options] `agents` applies to `add ai` only.
|
|
25
|
+
*/
|
|
26
|
+
export async function runAdd(toolId, options = {}) {
|
|
22
27
|
console.log();
|
|
23
28
|
p.intro(pc.bgMagenta(pc.black(" ViraStack ")));
|
|
24
29
|
|
|
@@ -49,9 +54,7 @@ export async function runAdd(toolId) {
|
|
|
49
54
|
if (tool.kind === "dependency") {
|
|
50
55
|
await runCommand(packageManager, getAddArgs(packageManager, [tool.package]), { cwd });
|
|
51
56
|
} else {
|
|
52
|
-
|
|
53
|
-
if (getLocale() === "tr") args.push("--tr");
|
|
54
|
-
await runCommand("npx", args, { cwd });
|
|
57
|
+
await runCommand("npx", getAiInitArgs(options.agents, { tr: getLocale() === "tr" }), { cwd });
|
|
55
58
|
}
|
|
56
59
|
spinner.stop(t("add.done", { pkg: tool.package }));
|
|
57
60
|
} catch (error) {
|
|
@@ -60,5 +63,9 @@ export async function runAdd(toolId) {
|
|
|
60
63
|
process.exit(1);
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
await trackEvent("add", {
|
|
66
|
+
await trackEvent("add", {
|
|
67
|
+
tool: tool.id,
|
|
68
|
+
...(tool.id === "ai" && options.agents ? { agents: options.agents } : {}),
|
|
69
|
+
packageManager,
|
|
70
|
+
});
|
|
64
71
|
}
|
package/src/commands/init.js
CHANGED
|
@@ -6,7 +6,9 @@ import pc from "picocolors";
|
|
|
6
6
|
import { t, getLocale } from "../i18n.js";
|
|
7
7
|
import { copyTemplate, dirHasFiles, isDirEmpty } from "../utils/copy-template.js";
|
|
8
8
|
import { runCommand } from "../utils/exec.js";
|
|
9
|
-
import { initGitRepo } from "../utils/git.js";
|
|
9
|
+
import { commitInitial, initGitRepo } from "../utils/git.js";
|
|
10
|
+
import { AGENTS, ALL_AGENTS, SKILLS, getAiInitArgs, getSkillArgs, getSkillsCommand } from "../utils/agents.js";
|
|
11
|
+
import { writeNextAgentsBlock } from "../utils/next-agents-md.js";
|
|
10
12
|
import { isOnline } from "../utils/is-online.js";
|
|
11
13
|
import {
|
|
12
14
|
detectPackageManager,
|
|
@@ -43,6 +45,7 @@ function failAndExit(message) {
|
|
|
43
45
|
* template?: string,
|
|
44
46
|
* i18n?: boolean,
|
|
45
47
|
* tools?: string[] | string | true,
|
|
48
|
+
* agents?: string[],
|
|
46
49
|
* yes?: boolean,
|
|
47
50
|
* skipInstall?: boolean,
|
|
48
51
|
* }} [options]
|
|
@@ -139,6 +142,25 @@ export async function runInit(options = {}) {
|
|
|
139
142
|
template = "nextjs";
|
|
140
143
|
}
|
|
141
144
|
|
|
145
|
+
let agents = options.agents;
|
|
146
|
+
if (agents == null) {
|
|
147
|
+
if (yes) {
|
|
148
|
+
agents = ALL_AGENTS;
|
|
149
|
+
} else {
|
|
150
|
+
const selected = await p.multiselect({
|
|
151
|
+
message: t("agents.message"),
|
|
152
|
+
required: false,
|
|
153
|
+
initialValues: ALL_AGENTS,
|
|
154
|
+
options: AGENTS.map((agent) => ({
|
|
155
|
+
value: agent.id,
|
|
156
|
+
label: t(`agents.${agent.id}.label`),
|
|
157
|
+
})),
|
|
158
|
+
});
|
|
159
|
+
if (p.isCancel(selected)) return cancelAndExit();
|
|
160
|
+
agents = ALL_AGENTS.filter((id) => selected.includes(id));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
142
164
|
let wantsI18n = options.i18n;
|
|
143
165
|
if (wantsI18n == null) {
|
|
144
166
|
if (yes) {
|
|
@@ -193,22 +215,15 @@ export async function runInit(options = {}) {
|
|
|
193
215
|
await setupEnvFile(targetDir);
|
|
194
216
|
scaffoldSpinner.stop(t("scaffold.copyDone"));
|
|
195
217
|
|
|
196
|
-
const gitSpinner = p.spinner();
|
|
197
|
-
gitSpinner.start(t("scaffold.gitInit"));
|
|
198
218
|
const gitInitialized = await initGitRepo(targetDir);
|
|
199
|
-
gitSpinner.stop(
|
|
200
|
-
gitInitialized ? t("scaffold.gitInitDone") : t("scaffold.gitInitSkipped"),
|
|
201
|
-
);
|
|
202
219
|
|
|
203
220
|
const online = await isOnline();
|
|
204
221
|
let installOk = false;
|
|
205
|
-
|
|
206
|
-
let offline = false;
|
|
222
|
+
const offline = !online;
|
|
207
223
|
|
|
208
224
|
if (skipInstall) {
|
|
209
225
|
p.log.info(t("scaffold.skipInstall"));
|
|
210
|
-
} else if (
|
|
211
|
-
offline = true;
|
|
226
|
+
} else if (offline) {
|
|
212
227
|
p.log.warn(t("scaffold.offline"));
|
|
213
228
|
} else {
|
|
214
229
|
const installSpinner = p.spinner();
|
|
@@ -221,37 +236,55 @@ export async function runInit(options = {}) {
|
|
|
221
236
|
installSpinner.stop(t("scaffold.installFailed"));
|
|
222
237
|
p.log.error(String(error?.message ?? error));
|
|
223
238
|
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (template === "nextjs") {
|
|
242
|
+
await writeNextAgentsBlock(targetDir);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ViraStack AI only writes files, so it runs even with --skip-install.
|
|
246
|
+
let aiOk = false;
|
|
247
|
+
if (!offline) {
|
|
248
|
+
const aiSpinner = p.spinner();
|
|
249
|
+
aiSpinner.start(t("scaffold.aiInit"));
|
|
250
|
+
try {
|
|
251
|
+
await runCommand("npx", getAiInitArgs(agents, { tr: getLocale() === "tr" }), { cwd: targetDir });
|
|
252
|
+
aiSpinner.stop(t("scaffold.aiInitDone"));
|
|
253
|
+
aiOk = true;
|
|
254
|
+
} catch {
|
|
255
|
+
aiSpinner.stop(t("scaffold.aiInitFailed"));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
224
258
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
259
|
+
// Each skill installs on its own: one failing must not stop the others.
|
|
260
|
+
// Like ViraStack AI, they only write files, so only offline skips them.
|
|
261
|
+
let skillsOk = false;
|
|
262
|
+
if (!offline) {
|
|
263
|
+
const skillsSpinner = p.spinner();
|
|
264
|
+
skillsSpinner.start(t("scaffold.skillsInit"));
|
|
265
|
+
let failed = 0;
|
|
266
|
+
for (const entry of SKILLS) {
|
|
228
267
|
try {
|
|
229
|
-
|
|
230
|
-
if (getLocale() === "tr") args.push("--tr");
|
|
231
|
-
await runCommand("npx", args, { cwd: targetDir });
|
|
232
|
-
aiSpinner.stop(t("scaffold.aiInitDone"));
|
|
233
|
-
aiOk = true;
|
|
268
|
+
await runCommand("npx", getSkillArgs(entry, agents), { cwd: targetDir });
|
|
234
269
|
} catch {
|
|
235
|
-
|
|
270
|
+
failed += 1;
|
|
236
271
|
}
|
|
237
|
-
|
|
238
|
-
if (aiOk) {
|
|
239
|
-
try {
|
|
240
|
-
await runCommand("npx", ["--yes", "skills@latest", "add", "emilkowalski/skills", "--skill", "emil-design-eng", "-y"], { cwd: targetDir });
|
|
241
|
-
await runCommand("npx", ["--yes", "skills@latest", "add", "jakubkrehel/make-interfaces-feel-better", "-y"], { cwd: targetDir });
|
|
242
|
-
} catch {
|
|
243
|
-
p.log.warn(t("scaffold.skillsInitFailed"));
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
p.log.warn(t("scaffold.aiInitSkipped"));
|
|
248
272
|
}
|
|
273
|
+
skillsOk = failed === 0;
|
|
274
|
+
skillsSpinner.stop(skillsOk ? t("scaffold.skillsInitDone") : t("scaffold.skillsInitFailed"));
|
|
249
275
|
}
|
|
250
276
|
|
|
277
|
+
// Last, so the first commit contains every generated file.
|
|
278
|
+
const gitSpinner = p.spinner();
|
|
279
|
+
gitSpinner.start(t("scaffold.gitInit"));
|
|
280
|
+
const committed = gitInitialized && (await commitInitial(targetDir));
|
|
281
|
+
gitSpinner.stop(committed ? t("scaffold.gitInitDone") : t("scaffold.gitInitSkipped"));
|
|
282
|
+
|
|
251
283
|
await trackEvent("create", {
|
|
252
284
|
template,
|
|
253
285
|
i18n: wantsI18n,
|
|
254
286
|
tools,
|
|
287
|
+
agents,
|
|
255
288
|
packageManager,
|
|
256
289
|
installOk,
|
|
257
290
|
skipInstall,
|
|
@@ -259,14 +292,16 @@ export async function runInit(options = {}) {
|
|
|
259
292
|
|
|
260
293
|
const cdHint = useCurrentDir ? null : t("done.cd", { dir: projectName });
|
|
261
294
|
const installCmd = getInstallCommand(packageManager);
|
|
262
|
-
const aiCmd = getAiInitCommand();
|
|
295
|
+
const aiCmd = getAiInitCommand(agents);
|
|
296
|
+
const skillsCmd = getSkillsCommand();
|
|
263
297
|
const devCmd = getRunDevCommand(packageManager);
|
|
264
298
|
|
|
265
299
|
if (skipInstall || offline || !installOk) {
|
|
266
300
|
const outroLines = [t("done.partialTitle")];
|
|
267
301
|
if (cdHint) outroLines.push(cdHint);
|
|
268
302
|
outroLines.push(t("done.manualInstall", { cmd: installCmd }));
|
|
269
|
-
outroLines.push(t("done.manualAi", { cmd: aiCmd }));
|
|
303
|
+
if (!aiOk) outroLines.push(t("done.manualAi", { cmd: aiCmd }));
|
|
304
|
+
if (!skillsOk) outroLines.push(t("done.manualSkills", { cmd: skillsCmd }));
|
|
270
305
|
outroLines.push(t("done.dev", { cmd: devCmd }));
|
|
271
306
|
if (!installOk && !offline && !skipInstall) {
|
|
272
307
|
p.log.error(t("done.installFailedNote"));
|
|
@@ -281,6 +316,7 @@ export async function runInit(options = {}) {
|
|
|
281
316
|
const outroLines = [t("done.title")];
|
|
282
317
|
if (cdHint) outroLines.push(cdHint);
|
|
283
318
|
if (!aiOk) outroLines.push(t("done.manualAi", { cmd: aiCmd }));
|
|
319
|
+
if (!skillsOk) outroLines.push(t("done.manualSkills", { cmd: skillsCmd }));
|
|
284
320
|
outroLines.push(t("done.dev", { cmd: devCmd }));
|
|
285
321
|
p.outro(outroLines.join("\n"));
|
|
286
322
|
}
|
package/src/i18n.js
CHANGED
|
@@ -7,7 +7,7 @@ const dictionaries = {
|
|
|
7
7
|
en: {
|
|
8
8
|
cancel: "Operation cancelled.",
|
|
9
9
|
"telemetry.notice":
|
|
10
|
-
"ViraStack collects anonymous usage data (template, i18n choice, selected tools) to improve the CLI. No personal data is ever collected. Disable anytime with --telemetry-disable.",
|
|
10
|
+
"ViraStack collects anonymous usage data (template, i18n choice, selected tools, AI coding tools) to improve the CLI. No personal data is ever collected. Disable anytime with --telemetry-disable.",
|
|
11
11
|
|
|
12
12
|
"projectName.message": "What is your project named?",
|
|
13
13
|
"projectName.placeholder": "virastack-app",
|
|
@@ -30,6 +30,12 @@ const dictionaries = {
|
|
|
30
30
|
"template.errorInvalid":
|
|
31
31
|
'Unknown template "{template}". Available templates: nextjs, tanstack.',
|
|
32
32
|
|
|
33
|
+
"agents.message": "Which AI coding tools do you use?",
|
|
34
|
+
"agents.claude.label": "Claude Code",
|
|
35
|
+
"agents.cursor.label": "Cursor",
|
|
36
|
+
"agent.errorInvalid":
|
|
37
|
+
'Invalid --agent value "{value}". Use claude, cursor (comma separated) or none.',
|
|
38
|
+
|
|
33
39
|
"i18n.message": "Would you like multi-language (i18n) support?",
|
|
34
40
|
"i18n.active": "Yes",
|
|
35
41
|
"i18n.inactive": "No",
|
|
@@ -46,9 +52,9 @@ const dictionaries = {
|
|
|
46
52
|
|
|
47
53
|
"scaffold.copying": "Creating project files...",
|
|
48
54
|
"scaffold.copyDone": "Project files created.",
|
|
49
|
-
"scaffold.gitInit": "
|
|
50
|
-
"scaffold.gitInitDone": "
|
|
51
|
-
"scaffold.gitInitSkipped": "Skipped git
|
|
55
|
+
"scaffold.gitInit": "Creating the initial git commit...",
|
|
56
|
+
"scaffold.gitInitDone": "Initial git commit created.",
|
|
57
|
+
"scaffold.gitInitSkipped": "Skipped the initial git commit.",
|
|
52
58
|
"scaffold.installing": "Installing dependencies with {pm}...",
|
|
53
59
|
"scaffold.installDone": "Dependencies installed.",
|
|
54
60
|
"scaffold.installFailed":
|
|
@@ -61,10 +67,9 @@ const dictionaries = {
|
|
|
61
67
|
"scaffold.aiInitDone": "🤖 ViraStack AI agent rules successfully integrated.",
|
|
62
68
|
"scaffold.aiInitFailed":
|
|
63
69
|
"Failed to configure ViraStack AI rules.",
|
|
64
|
-
"scaffold.
|
|
65
|
-
|
|
66
|
-
"scaffold.skillsInitFailed":
|
|
67
|
-
"Design skills could not be installed. Run manually: npx skills@latest add emilkowalski/skills && npx skills@latest add jakubkrehel/make-interfaces-feel-better",
|
|
70
|
+
"scaffold.skillsInit": "Installing design skills...",
|
|
71
|
+
"scaffold.skillsInitDone": "Design skills installed.",
|
|
72
|
+
"scaffold.skillsInitFailed": "Some design skills could not be installed.",
|
|
68
73
|
|
|
69
74
|
"done.title": "You're all set!",
|
|
70
75
|
"done.partialTitle": "Project files are ready — finish setup manually:",
|
|
@@ -73,6 +78,7 @@ const dictionaries = {
|
|
|
73
78
|
"done.cd": " cd {dir}",
|
|
74
79
|
"done.manualInstall": " {cmd}",
|
|
75
80
|
"done.manualAi": " {cmd}",
|
|
81
|
+
"done.manualSkills": " {cmd}",
|
|
76
82
|
"done.dev": " {cmd}",
|
|
77
83
|
|
|
78
84
|
"add.toolMissing": "Please specify a tool to add. Available tools: {list}.",
|
|
@@ -85,6 +91,7 @@ const dictionaries = {
|
|
|
85
91
|
help: `Usage:
|
|
86
92
|
pnpm dlx virastack [init] [name] Scaffold a new project
|
|
87
93
|
pnpm dlx virastack add <tool> Add a ViraStack tool to an existing project
|
|
94
|
+
pnpm dlx virastack add ai --agent <list> Add AI rules for the given tools
|
|
88
95
|
|
|
89
96
|
Tools:
|
|
90
97
|
mask, password, ai
|
|
@@ -93,9 +100,10 @@ Options:
|
|
|
93
100
|
--name <name> Project name (or pass as positional arg / ".")
|
|
94
101
|
--template <name> Template: nextjs | tanstack
|
|
95
102
|
--tools <list> Comma-separated tools (e.g. mask,password)
|
|
103
|
+
--agent <list> AI coding tools: claude, cursor (comma separated) or none
|
|
96
104
|
--i18n / --no-i18n Enable or disable the i18n template
|
|
97
105
|
--yes, -y Non-interactive mode (use flags/defaults)
|
|
98
|
-
--skip-install
|
|
106
|
+
--skip-install Skip dependency install (AI rules and skills are still added)
|
|
99
107
|
--tr Ask questions in Turkish
|
|
100
108
|
--telemetry-disable Disable anonymous usage tracking
|
|
101
109
|
-v, --version Print the version
|
|
@@ -104,13 +112,13 @@ Options:
|
|
|
104
112
|
Examples:
|
|
105
113
|
pnpm dlx virastack
|
|
106
114
|
pnpm dlx virastack my-app --yes
|
|
107
|
-
pnpm dlx virastack init my-app --template nextjs --tools mask --yes`,
|
|
115
|
+
pnpm dlx virastack init my-app --template nextjs --tools mask --agent claude --yes`,
|
|
108
116
|
},
|
|
109
117
|
|
|
110
118
|
tr: {
|
|
111
119
|
cancel: "İşlem iptal edildi.",
|
|
112
120
|
"telemetry.notice":
|
|
113
|
-
"ViraStack, CLI'ı geliştirmek için anonim kullanım verisi toplar (şablon, i18n tercihi, seçilen araçlar). Hiçbir kişisel veri toplanmaz. --telemetry-disable ile her zaman kapatabilirsiniz.",
|
|
121
|
+
"ViraStack, CLI'ı geliştirmek için anonim kullanım verisi toplar (şablon, i18n tercihi, seçilen araçlar, AI kodlama araçları). Hiçbir kişisel veri toplanmaz. --telemetry-disable ile her zaman kapatabilirsiniz.",
|
|
114
122
|
|
|
115
123
|
"projectName.message": "Projenizin adı ne olsun?",
|
|
116
124
|
"projectName.placeholder": "virastack-app",
|
|
@@ -133,6 +141,12 @@ Examples:
|
|
|
133
141
|
"template.errorInvalid":
|
|
134
142
|
'Bilinmeyen şablon "{template}". Kullanılabilir şablonlar: nextjs, tanstack.',
|
|
135
143
|
|
|
144
|
+
"agents.message": "Hangi AI kodlama araçlarını kullanıyorsunuz?",
|
|
145
|
+
"agents.claude.label": "Claude Code",
|
|
146
|
+
"agents.cursor.label": "Cursor",
|
|
147
|
+
"agent.errorInvalid":
|
|
148
|
+
'Geçersiz --agent değeri "{value}". claude, cursor (virgülle) veya none kullanın.',
|
|
149
|
+
|
|
136
150
|
"i18n.message": "Çoklu dil (i18n) desteği ister misiniz?",
|
|
137
151
|
"i18n.active": "Evet",
|
|
138
152
|
"i18n.inactive": "Hayır",
|
|
@@ -149,9 +163,9 @@ Examples:
|
|
|
149
163
|
|
|
150
164
|
"scaffold.copying": "Proje dosyaları oluşturuluyor...",
|
|
151
165
|
"scaffold.copyDone": "Proje dosyaları oluşturuldu.",
|
|
152
|
-
"scaffold.gitInit": "
|
|
153
|
-
"scaffold.gitInitDone": "
|
|
154
|
-
"scaffold.gitInitSkipped": "
|
|
166
|
+
"scaffold.gitInit": "İlk git commit'i oluşturuluyor...",
|
|
167
|
+
"scaffold.gitInitDone": "İlk git commit'i oluşturuldu.",
|
|
168
|
+
"scaffold.gitInitSkipped": "İlk git commit'i atlandı.",
|
|
155
169
|
"scaffold.installing": "Bağımlılıklar {pm} ile kuruluyor...",
|
|
156
170
|
"scaffold.installDone": "Bağımlılıklar kuruldu.",
|
|
157
171
|
"scaffold.installFailed":
|
|
@@ -164,10 +178,9 @@ Examples:
|
|
|
164
178
|
"scaffold.aiInitDone": "🤖 ViraStack AI ajan kuralları projeye başarıyla entegre edildi.",
|
|
165
179
|
"scaffold.aiInitFailed":
|
|
166
180
|
"ViraStack AI kuralları yapılandırılamadı.",
|
|
167
|
-
"scaffold.
|
|
168
|
-
|
|
169
|
-
"scaffold.skillsInitFailed":
|
|
170
|
-
"Tasarım skill'leri kurulamadı. Manuel çalıştırın: npx skills@latest add emilkowalski/skills && npx skills@latest add jakubkrehel/make-interfaces-feel-better",
|
|
181
|
+
"scaffold.skillsInit": "Tasarım skill'leri kuruluyor...",
|
|
182
|
+
"scaffold.skillsInitDone": "Tasarım skill'leri kuruldu.",
|
|
183
|
+
"scaffold.skillsInitFailed": "Bazı tasarım skill'leri kurulamadı.",
|
|
171
184
|
|
|
172
185
|
"done.title": "Her şey hazır!",
|
|
173
186
|
"done.partialTitle": "Proje dosyaları hazır — kurulumu manuel tamamlayın:",
|
|
@@ -176,6 +189,7 @@ Examples:
|
|
|
176
189
|
"done.cd": " cd {dir}",
|
|
177
190
|
"done.manualInstall": " {cmd}",
|
|
178
191
|
"done.manualAi": " {cmd}",
|
|
192
|
+
"done.manualSkills": " {cmd}",
|
|
179
193
|
"done.dev": " {cmd}",
|
|
180
194
|
|
|
181
195
|
"add.toolMissing":
|
|
@@ -189,6 +203,7 @@ Examples:
|
|
|
189
203
|
help: `Kullanım:
|
|
190
204
|
pnpm dlx virastack [init] [ad] Yeni bir proje oluştur
|
|
191
205
|
pnpm dlx virastack add <araç> Mevcut projeye bir ViraStack aracı ekle
|
|
206
|
+
pnpm dlx virastack add ai --agent <liste> Seçilen araçlar için AI kurallarını ekle
|
|
192
207
|
|
|
193
208
|
Araçlar:
|
|
194
209
|
mask, password, ai
|
|
@@ -197,9 +212,10 @@ Seçenekler:
|
|
|
197
212
|
--name <ad> Proje adı (veya positional arg / ".")
|
|
198
213
|
--template <ad> Şablon: nextjs | tanstack
|
|
199
214
|
--tools <liste> Virgülle ayrılmış araçlar (örn. mask,password)
|
|
215
|
+
--agent <liste> AI kodlama araçları: claude, cursor (virgülle) veya none
|
|
200
216
|
--i18n / --no-i18n i18n şablonunu aç / kapat
|
|
201
217
|
--yes, -y İnteraktif olmayan mod (flag/varsayılanlar)
|
|
202
|
-
--skip-install
|
|
218
|
+
--skip-install Bağımlılık kurulumunu atla (AI kuralları ve skill'ler yine eklenir)
|
|
203
219
|
--tr Soruları Türkçe sor
|
|
204
220
|
--telemetry-disable Anonim kullanım verisi toplamayı kapat
|
|
205
221
|
-v, --version Sürümü göster
|
|
@@ -208,7 +224,7 @@ Seçenekler:
|
|
|
208
224
|
Örnekler:
|
|
209
225
|
pnpm dlx virastack
|
|
210
226
|
pnpm dlx virastack my-app --yes
|
|
211
|
-
pnpm dlx virastack init my-app --template nextjs --tools mask --yes`,
|
|
227
|
+
pnpm dlx virastack init my-app --template nextjs --tools mask --agent claude --yes`,
|
|
212
228
|
},
|
|
213
229
|
};
|
|
214
230
|
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { setLocale, t } from "./i18n.js";
|
|
|
5
5
|
import { runInit } from "./commands/init.js";
|
|
6
6
|
import { runAdd } from "./commands/add.js";
|
|
7
7
|
import { parseToolsFlag } from "./utils/tools.js";
|
|
8
|
+
import { parseAgentFlag } from "./utils/agents.js";
|
|
8
9
|
|
|
9
10
|
function resolveI18nFlag(flags) {
|
|
10
11
|
if (hasFlag(flags, "no-i18n")) return false;
|
|
@@ -12,6 +13,18 @@ function resolveI18nFlag(flags) {
|
|
|
12
13
|
return undefined;
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
/** `--agent` as a list of ids, undefined when absent. Exits on an invalid value. */
|
|
17
|
+
function resolveAgentFlag(flags) {
|
|
18
|
+
const value = getFlag(flags, "agent");
|
|
19
|
+
if (value === undefined) return undefined;
|
|
20
|
+
const agents = parseAgentFlag(value);
|
|
21
|
+
if (agents === null) {
|
|
22
|
+
console.error(t("agent.errorInvalid", { value: value === true ? "" : value }));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
return agents;
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
function buildInitOptions(flags, projectName) {
|
|
16
29
|
const nameFlag = getFlag(flags, "name");
|
|
17
30
|
const template = getFlag(flags, "template");
|
|
@@ -22,6 +35,7 @@ function buildInitOptions(flags, projectName) {
|
|
|
22
35
|
template: typeof template === "string" ? template : undefined,
|
|
23
36
|
i18n: resolveI18nFlag(flags),
|
|
24
37
|
tools: tools === undefined ? undefined : parseToolsFlag(tools),
|
|
38
|
+
agents: resolveAgentFlag(flags),
|
|
25
39
|
yes: hasFlag(flags, "yes", "y"),
|
|
26
40
|
skipInstall: hasFlag(flags, "skip-install"),
|
|
27
41
|
};
|
|
@@ -55,7 +69,7 @@ export async function main() {
|
|
|
55
69
|
await runInit(buildInitOptions(flags, rest[0]));
|
|
56
70
|
break;
|
|
57
71
|
case "add":
|
|
58
|
-
await runAdd(rest[0]);
|
|
72
|
+
await runAdd(rest[0], { agents: resolveAgentFlag(flags) });
|
|
59
73
|
break;
|
|
60
74
|
default:
|
|
61
75
|
// `pnpm dlx virastack my-app --yes` — first token is the project name.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { getCliVersion } from "./package-info.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AI coding tools the CLI can set up. `skillsAgent` is the matching
|
|
5
|
+
* `skills` CLI agent id (https://github.com/vercel-labs/skills).
|
|
6
|
+
*/
|
|
7
|
+
export const AGENTS = [
|
|
8
|
+
{ id: "claude", skillsAgent: "claude-code" },
|
|
9
|
+
{ id: "cursor", skillsAgent: "cursor" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export const ALL_AGENTS = AGENTS.map((agent) => agent.id);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parses `--agent claude,cursor` / `--agent none`.
|
|
16
|
+
* Returns the selected ids in canonical order, or null when the value is
|
|
17
|
+
* missing or names an unknown tool.
|
|
18
|
+
*/
|
|
19
|
+
export function parseAgentFlag(value) {
|
|
20
|
+
if (typeof value !== "string" || value.trim() === "") return null;
|
|
21
|
+
const parts = value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
22
|
+
if (parts.length === 1 && parts[0] === "none") return [];
|
|
23
|
+
if (parts.length === 0 || parts.some((part) => !ALL_AGENTS.includes(part))) return null;
|
|
24
|
+
return ALL_AGENTS.filter((id) => parts.includes(id));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** `--agent` value for @virastack/ai: "claude,cursor", "cursor" or "none". */
|
|
28
|
+
export function formatAgents(agents) {
|
|
29
|
+
return agents.length === 0 ? "none" : agents.join(",");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* npm spec for @virastack/ai. A prerelease of this CLI pairs with the ai
|
|
34
|
+
* prerelease channel (`next`), a stable release with ai v2.
|
|
35
|
+
* VIRASTACK_AI_SPEC overrides it (e.g. `file:` + a local tarball in end-to-end tests).
|
|
36
|
+
*/
|
|
37
|
+
export function getAiPackageSpec(cliVersion = getCliVersion()) {
|
|
38
|
+
if (process.env.VIRASTACK_AI_SPEC) return process.env.VIRASTACK_AI_SPEC;
|
|
39
|
+
return `@virastack/ai@${cliVersion.includes("-") ? "next" : "2"}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Arguments for `npx`. Never `--force`, so files the user changed are kept.
|
|
44
|
+
* Without `agents`, ViraStack AI reuses the saved choice (or installs both).
|
|
45
|
+
*/
|
|
46
|
+
export function getAiInitArgs(agents, { tr = false } = {}) {
|
|
47
|
+
const args = ["--yes", getAiPackageSpec(), "init"];
|
|
48
|
+
if (agents) args.push("--agent", formatAgents(agents));
|
|
49
|
+
args.push("--yes");
|
|
50
|
+
if (tr) args.push("--tr");
|
|
51
|
+
return args;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Design skills installed in every project, one `skills add` call each. */
|
|
55
|
+
export const SKILLS = [
|
|
56
|
+
{ source: "emilkowalski/skills", skill: "emil-design-eng" },
|
|
57
|
+
{ source: "jakubkrehel/skills", skill: "better-ui" },
|
|
58
|
+
{ source: "jakubkrehel/skills", skill: "better-accessibility" },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Arguments for `npx skills@latest add`. Skills land in `.agents/skills/`
|
|
63
|
+
* (the `universal` target, read by Cursor and other agents) and are also
|
|
64
|
+
* copied for each selected tool that needs its own folder. With Claude Code
|
|
65
|
+
* as the only tool, `.agents/skills/` would go unused, so it is skipped.
|
|
66
|
+
* `--copy` writes real files instead of symlinks so the first commit works
|
|
67
|
+
* the same on Windows.
|
|
68
|
+
*/
|
|
69
|
+
export function getSkillArgs({ source, skill }, agents) {
|
|
70
|
+
const tools = agents.map((id) => AGENTS.find((agent) => agent.id === id).skillsAgent);
|
|
71
|
+
const claudeOnly = agents.length === 1 && agents[0] === "claude";
|
|
72
|
+
const targets = claudeOnly ? tools : ["universal", ...tools];
|
|
73
|
+
const args = ["--yes", "skills@latest", "add", source];
|
|
74
|
+
if (skill) args.push("--skill", skill);
|
|
75
|
+
for (const target of targets) args.push("-a", target);
|
|
76
|
+
args.push("--copy", "-y");
|
|
77
|
+
return args;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Manual command for the skills (interactive), shown when they were not installed. */
|
|
81
|
+
export function getSkillsCommand() {
|
|
82
|
+
return SKILLS.map(({ source, skill }) =>
|
|
83
|
+
["npx skills@latest add", source, ...(skill ? ["--skill", skill] : [])].join(" "),
|
|
84
|
+
).join(" && ");
|
|
85
|
+
}
|
package/src/utils/args.js
CHANGED