@use-aistack/cli 0.1.0

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 ADDED
@@ -0,0 +1,61 @@
1
+ # @use-aistack/cli
2
+
3
+ Share and clone AI development configurations (prompts, rules, skills, MCP setups).
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npx @use-aistack/cli <command>
9
+ ```
10
+
11
+ Or install globally:
12
+
13
+ ```sh
14
+ npm i -g @use-aistack/cli
15
+ ```
16
+
17
+ ## Commands
18
+
19
+ ### `aistack login`
20
+
21
+ Authenticate with your AI Stack account via browser.
22
+
23
+ ```sh
24
+ aistack login
25
+ ```
26
+
27
+ ### `aistack collect`
28
+
29
+ Scan your project for AI config files and upload them.
30
+
31
+ ```sh
32
+ cd your-project
33
+ aistack collect
34
+ ```
35
+
36
+ Detects: `.cursorrules`, `CLAUDE.md`, `AGENTS.md`, `.cursor/rules/`, `mcp.json`, skill directories, prompts, and global configs (`~/.claude/`, `~/.cursor/`, etc).
37
+
38
+ ### `aistack create <slug>`
39
+
40
+ Clone a shared project's AI config files into your current directory.
41
+
42
+ ```sh
43
+ aistack create my-project-abc123
44
+ ```
45
+
46
+ ## Development
47
+
48
+ ```sh
49
+ pnpm --filter @use-aistack/cli build
50
+ pnpm --filter @use-aistack/cli dev # watch mode
51
+ ```
52
+
53
+ Test locally:
54
+
55
+ ```sh
56
+ node packages/cli/dist/index.js login
57
+ node packages/cli/dist/index.js collect
58
+ node packages/cli/dist/index.js create <slug>
59
+ ```
60
+
61
+ Set `AISTACK_URL=http://localhost:3019` to test against local dev server.
package/dist/index.js ADDED
@@ -0,0 +1,787 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/commands/login.ts
7
+ import * as p from "@clack/prompts";
8
+ import open from "open";
9
+
10
+ // src/api.ts
11
+ var BASE_URL = process.env.AISTACK_URL || "https://aistack.to";
12
+ async function request(path, options = {}) {
13
+ return fetch(`${BASE_URL}${path}`, {
14
+ ...options,
15
+ headers: {
16
+ "Content-Type": "application/json",
17
+ ...options.headers
18
+ }
19
+ });
20
+ }
21
+ function authHeaders(token) {
22
+ return { Authorization: `Bearer ${token}` };
23
+ }
24
+ async function authStart() {
25
+ const res = await request("/api/cli/auth/start", { method: "POST" });
26
+ if (!res.ok) throw new Error(`Auth start failed: ${res.status}`);
27
+ return res.json();
28
+ }
29
+ async function authPoll(secretId) {
30
+ const res = await request(
31
+ `/api/cli/auth/poll?secretId=${encodeURIComponent(secretId)}`
32
+ );
33
+ if (!res.ok) throw new Error(`Auth poll failed: ${res.status}`);
34
+ return res.json();
35
+ }
36
+ async function projectsCheck(token, name) {
37
+ const res = await request(
38
+ `/api/cli/projects/check?name=${encodeURIComponent(name)}`,
39
+ {
40
+ headers: authHeaders(token)
41
+ }
42
+ );
43
+ if (res.status === 401)
44
+ throw new Error("Authentication expired. Run `aistack login` again.");
45
+ if (!res.ok) throw new Error(`Project check failed: ${res.status}`);
46
+ return res.json();
47
+ }
48
+ async function projectsCollect(token, data) {
49
+ const res = await request("/api/cli/projects/collect", {
50
+ method: "POST",
51
+ headers: authHeaders(token),
52
+ body: JSON.stringify(data)
53
+ });
54
+ if (res.status === 401)
55
+ throw new Error("Authentication expired. Run `aistack login` again.");
56
+ if (!res.ok) {
57
+ const body = await res.json().catch(() => ({}));
58
+ throw new Error(
59
+ body.error || `Collect failed: ${res.status}`
60
+ );
61
+ }
62
+ return res.json();
63
+ }
64
+ async function projectGet(shortId) {
65
+ const res = await request(`/api/cli/projects/${encodeURIComponent(shortId)}`);
66
+ if (res.status === 404) return null;
67
+ if (!res.ok) throw new Error(`Project fetch failed: ${res.status}`);
68
+ return res.json();
69
+ }
70
+
71
+ // src/config.ts
72
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
73
+ import { homedir } from "os";
74
+ import { join } from "path";
75
+ var CONFIG_DIR = join(homedir(), ".config", "aistack");
76
+ var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
77
+ function getToken() {
78
+ if (!existsSync(CREDENTIALS_FILE)) return null;
79
+ try {
80
+ const data = JSON.parse(
81
+ readFileSync(CREDENTIALS_FILE, "utf-8")
82
+ );
83
+ return data.token ?? null;
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ function saveToken(token, userId) {
89
+ mkdirSync(CONFIG_DIR, { recursive: true });
90
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify({ token, userId }, null, 2));
91
+ }
92
+ var PROJECTS_FILE = join(CONFIG_DIR, "projects.json");
93
+ function readProjects() {
94
+ if (!existsSync(PROJECTS_FILE)) return {};
95
+ try {
96
+ const raw = JSON.parse(readFileSync(PROJECTS_FILE, "utf-8"));
97
+ const data = {};
98
+ for (const [key, value] of Object.entries(raw)) {
99
+ if (typeof value === "string") {
100
+ data[key] = { name: value };
101
+ } else {
102
+ data[key] = value;
103
+ }
104
+ }
105
+ return data;
106
+ } catch {
107
+ return {};
108
+ }
109
+ }
110
+ function writeProjects(data) {
111
+ mkdirSync(CONFIG_DIR, { recursive: true });
112
+ writeFileSync(PROJECTS_FILE, JSON.stringify(data, null, 2));
113
+ }
114
+ function getProjectName(directory) {
115
+ return readProjects()[directory]?.name ?? null;
116
+ }
117
+ function getExcludedPaths(directory) {
118
+ return readProjects()[directory]?.excluded ?? [];
119
+ }
120
+ function saveProjectSettings(directory, name, excluded) {
121
+ const data = readProjects();
122
+ data[directory] = {
123
+ name,
124
+ excluded: excluded.length > 0 ? excluded : void 0
125
+ };
126
+ writeProjects(data);
127
+ }
128
+
129
+ // src/theme.ts
130
+ var esc = (code) => `\x1B[${code}m`;
131
+ var reset = esc("0");
132
+ var LIME = "163;230;53";
133
+ var BLACK = "0;0;0";
134
+ var YELLOW = "250;204;21";
135
+ var RED = "248;113;113";
136
+ var MUTED = "120;120;120";
137
+ var lime = (s) => `${esc(`38;2;${LIME}`)}${s}${reset}`;
138
+ var limeBold = (s) => `${esc("1")}${esc(`38;2;${LIME}`)}${s}${reset}`;
139
+ var bgLime = (s) => `${esc(`48;2;${LIME}`)}${esc(`38;2;${BLACK}`)}${s}${reset}`;
140
+ var yellow = (s) => `${esc(`38;2;${YELLOW}`)}${s}${reset}`;
141
+ var red = (s) => `${esc(`38;2;${RED}`)}${s}${reset}`;
142
+ var dim = (s) => `${esc(`38;2;${MUTED}`)}${s}${reset}`;
143
+ var bold = (s) => `${esc("1")}${s}${reset}`;
144
+ var banner = (cmd) => `${lime("\u25A0")} ${bgLime(` AISTACK `)} ${bold(cmd.toUpperCase())}`;
145
+ var BAR = `${esc(`38;2;${MUTED}`)}\u2502${reset}`;
146
+ function lines(items) {
147
+ for (const item of items) {
148
+ console.log(`${BAR} ${item}`);
149
+ }
150
+ }
151
+ function section(label, count) {
152
+ console.log(`${BAR}`);
153
+ const countStr = count !== void 0 ? ` ${dim(String(count))}` : "";
154
+ console.log(`${BAR} ${bold(label.toUpperCase())}${countStr}`);
155
+ }
156
+ function divider() {
157
+ console.log(`${BAR} ${dim("\u2500".repeat(40))}`);
158
+ }
159
+
160
+ // src/commands/login.ts
161
+ async function loginCommand() {
162
+ p.intro(banner("login"));
163
+ const s = p.spinner();
164
+ s.start("Starting authentication...");
165
+ let session;
166
+ try {
167
+ session = await authStart();
168
+ s.stop("Session created");
169
+ } catch (err) {
170
+ s.stop("Failed to start authentication");
171
+ p.log.error(err instanceof Error ? err.message : String(err));
172
+ process.exit(1);
173
+ }
174
+ p.log.info(`${dim("CODE")} ${limeBold(session.userCode)}`);
175
+ p.log.info(`${dim("OPEN")} ${dim(session.authUrl)}`);
176
+ try {
177
+ await open(session.authUrl);
178
+ } catch {
179
+ p.log.warn(
180
+ "Could not open browser automatically. Please visit the URL above."
181
+ );
182
+ }
183
+ s.start("Waiting for approval...");
184
+ const maxAttempts = 36;
185
+ for (let i = 0; i < maxAttempts; i++) {
186
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
187
+ try {
188
+ const result = await authPoll(session.secretId);
189
+ if (result.status === "approved" && result.token) {
190
+ s.stop(lime("Authenticated"));
191
+ saveToken(result.token, result.userId);
192
+ p.log.success(
193
+ `Token saved. Run ${limeBold("aistack collect")} to get started.`
194
+ );
195
+ p.outro(lime("done"));
196
+ return;
197
+ }
198
+ if (result.status === "expired") {
199
+ s.stop("Session expired");
200
+ p.log.error("Authentication session expired. Please try again.");
201
+ process.exit(1);
202
+ }
203
+ } catch (err) {
204
+ s.stop("Error polling");
205
+ p.log.error(err instanceof Error ? err.message : String(err));
206
+ process.exit(1);
207
+ }
208
+ }
209
+ s.stop("Timed out");
210
+ p.log.error("Authentication timed out after 3 minutes. Please try again.");
211
+ process.exit(1);
212
+ }
213
+
214
+ // src/commands/collect.ts
215
+ import * as p2 from "@clack/prompts";
216
+ import { basename as basename2 } from "path";
217
+
218
+ // src/scanner.ts
219
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
220
+ import { join as join2, relative } from "path";
221
+ import { homedir as homedir2 } from "os";
222
+ import ignore from "ignore";
223
+ var MAX_FILE_SIZE = 100 * 1024;
224
+ var LOCAL_PATTERNS = [
225
+ // Rules
226
+ { path: "CLAUDE.md", type: "rule" },
227
+ { path: "AGENTS.md", type: "rule" },
228
+ { path: ".cursorrules", type: "rule" },
229
+ { path: ".windsurfrules", type: "rule" },
230
+ { path: ".clinerules", type: "rule" },
231
+ { path: ".github/copilot-instructions.md", type: "rule" },
232
+ // MCP
233
+ { path: "mcp.json", type: "mcp" },
234
+ { path: ".cursor/mcp.json", type: "mcp" },
235
+ { path: "claude_desktop_config.json", type: "mcp" },
236
+ // Config
237
+ { path: ".aider.conf.yml", type: "config" },
238
+ { path: ".continue/config.json", type: "config" },
239
+ // Config
240
+ { path: ".claude/settings.json", type: "config" },
241
+ { path: ".claude/settings.local.json", type: "config" },
242
+ // Prompts
243
+ { path: "system-prompt.md", type: "prompt" }
244
+ ];
245
+ var LOCAL_DIR_PATTERNS = [
246
+ { dir: ".cursor/rules", type: "rule" },
247
+ { dir: ".claude/commands", type: "command" },
248
+ { dir: ".claude/agents", type: "subagent" },
249
+ { dir: ".claude/hooks", type: "hook" },
250
+ { dir: "prompts", type: "prompt" },
251
+ { dir: ".ai", type: "custom" }
252
+ ];
253
+ function loadGitignore(cwd) {
254
+ const ig = ignore();
255
+ const gitignorePath = join2(cwd, ".gitignore");
256
+ if (existsSync2(gitignorePath)) {
257
+ ig.add(readFileSync2(gitignorePath, "utf-8"));
258
+ }
259
+ ig.add(["node_modules", ".git", "dist", "build", ".next", ".output"]);
260
+ return ig;
261
+ }
262
+ function readFileSafe(filePath) {
263
+ try {
264
+ const stat = statSync(filePath);
265
+ if (stat.size > MAX_FILE_SIZE) return null;
266
+ return readFileSync2(filePath, "utf-8");
267
+ } catch {
268
+ return null;
269
+ }
270
+ }
271
+ function walkDir(dir, maxDepth = 3, currentDepth = 0) {
272
+ if (currentDepth >= maxDepth || !existsSync2(dir)) return [];
273
+ const results = [];
274
+ try {
275
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
276
+ const fullPath = join2(dir, entry.name);
277
+ if (entry.isFile()) {
278
+ results.push(fullPath);
279
+ } else if (entry.isDirectory()) {
280
+ results.push(...walkDir(fullPath, maxDepth, currentDepth + 1));
281
+ }
282
+ }
283
+ } catch {
284
+ }
285
+ return results;
286
+ }
287
+ function scanLocal(cwd) {
288
+ const ig = loadGitignore(cwd);
289
+ const results = [];
290
+ for (const pattern of LOCAL_PATTERNS) {
291
+ const filePath = join2(cwd, pattern.path);
292
+ const content = readFileSafe(filePath);
293
+ if (content !== null) {
294
+ const rel = relative(cwd, filePath);
295
+ if (!ig.ignores(rel)) {
296
+ results.push({
297
+ path: filePath,
298
+ relativePath: rel,
299
+ content,
300
+ type: pattern.type,
301
+ source: "local"
302
+ });
303
+ }
304
+ }
305
+ }
306
+ for (const { dir, type } of LOCAL_DIR_PATTERNS) {
307
+ const dirPath = join2(cwd, dir);
308
+ const files = walkDir(dirPath);
309
+ for (const filePath of files) {
310
+ const rel = relative(cwd, filePath);
311
+ if (ig.ignores(rel)) continue;
312
+ const content = readFileSafe(filePath);
313
+ if (content !== null) {
314
+ results.push({
315
+ path: filePath,
316
+ relativePath: rel,
317
+ content,
318
+ type,
319
+ source: "local"
320
+ });
321
+ }
322
+ }
323
+ }
324
+ try {
325
+ for (const entry of readdirSync(cwd, { withFileTypes: true }).filter(
326
+ (e) => e.isDirectory()
327
+ )) {
328
+ if (ig.ignores(entry.name + "/")) continue;
329
+ scanSkillDirs(join2(cwd, entry.name), cwd, ig, results, 1);
330
+ }
331
+ } catch {
332
+ }
333
+ return results;
334
+ }
335
+ function scanSkillDirs(dir, cwd, ig, results, depth) {
336
+ if (depth > 3) return;
337
+ const skillMd = join2(dir, "SKILL.md");
338
+ if (existsSync2(skillMd)) {
339
+ const files = walkDir(dir, 1);
340
+ for (const filePath of files) {
341
+ const rel = relative(cwd, filePath);
342
+ if (ig.ignores(rel)) continue;
343
+ const content = readFileSafe(filePath);
344
+ if (content !== null) {
345
+ results.push({
346
+ path: filePath,
347
+ relativePath: rel,
348
+ content,
349
+ type: "skill",
350
+ source: "local"
351
+ });
352
+ }
353
+ }
354
+ return;
355
+ }
356
+ try {
357
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
358
+ if (entry.isDirectory()) {
359
+ const rel = relative(cwd, join2(dir, entry.name));
360
+ if (!ig.ignores(rel + "/")) {
361
+ scanSkillDirs(join2(dir, entry.name), cwd, ig, results, depth + 1);
362
+ }
363
+ }
364
+ }
365
+ } catch {
366
+ }
367
+ }
368
+ function scanGlobal() {
369
+ const home = homedir2();
370
+ const results = [];
371
+ const globalPatterns = [
372
+ { path: ".claude/CLAUDE.md", type: "rule" },
373
+ { path: ".claude/settings.json", type: "config" },
374
+ { path: ".cursor/mcp.json", type: "mcp" },
375
+ { path: ".continue/config.json", type: "config" },
376
+ { path: ".aider.conf.yml", type: "config" }
377
+ ];
378
+ for (const pattern of globalPatterns) {
379
+ const filePath = join2(home, pattern.path);
380
+ const content = readFileSafe(filePath);
381
+ if (content !== null) {
382
+ results.push({
383
+ path: filePath,
384
+ relativePath: `~/${pattern.path}`,
385
+ content,
386
+ type: pattern.type,
387
+ source: "global"
388
+ });
389
+ }
390
+ }
391
+ const globalDirs = [
392
+ { dir: ".claude/commands", type: "command" },
393
+ { dir: ".claude/agents", type: "subagent" },
394
+ { dir: ".claude/hooks", type: "hook" },
395
+ { dir: ".cursor/rules", type: "rule" }
396
+ ];
397
+ for (const { dir, type } of globalDirs) {
398
+ const dirPath = join2(home, dir);
399
+ const files = walkDir(dirPath, 2);
400
+ for (const filePath of files) {
401
+ const content = readFileSafe(filePath);
402
+ if (content !== null) {
403
+ results.push({
404
+ path: filePath,
405
+ relativePath: `~/${relative(home, filePath)}`,
406
+ content,
407
+ type,
408
+ source: "global"
409
+ });
410
+ }
411
+ }
412
+ }
413
+ return results;
414
+ }
415
+
416
+ // src/classifier.ts
417
+ import { basename, dirname } from "path";
418
+ function classify(files) {
419
+ const skillDirs = /* @__PURE__ */ new Map();
420
+ const nonSkillFiles = [];
421
+ for (const file of files) {
422
+ if (file.type === "skill") {
423
+ const dir = dirname(file.relativePath);
424
+ const existing = skillDirs.get(dir) ?? [];
425
+ existing.push(file);
426
+ skillDirs.set(dir, existing);
427
+ } else {
428
+ nonSkillFiles.push(file);
429
+ }
430
+ }
431
+ const items = [];
432
+ for (const file of nonSkillFiles) {
433
+ const tags = file.source === "global" ? ["global"] : void 0;
434
+ items.push({
435
+ type: file.type,
436
+ name: file.relativePath,
437
+ files: [
438
+ {
439
+ name: basename(file.relativePath),
440
+ content: file.content,
441
+ path: file.relativePath,
442
+ tags
443
+ }
444
+ ]
445
+ });
446
+ }
447
+ for (const [dir, dirFiles] of skillDirs) {
448
+ const isGlobal = dirFiles[0]?.source === "global";
449
+ items.push({
450
+ type: "skill",
451
+ name: dir,
452
+ files: dirFiles.map((f) => ({
453
+ name: basename(f.relativePath),
454
+ content: f.content,
455
+ path: f.relativePath,
456
+ tags: isGlobal ? ["global"] : void 0
457
+ }))
458
+ });
459
+ }
460
+ return items;
461
+ }
462
+
463
+ // src/commands/collect.ts
464
+ async function collectCommand(options) {
465
+ p2.intro(banner("collect"));
466
+ const token = getToken();
467
+ if (!token) {
468
+ p2.log.error(`Not authenticated. Run ${limeBold("aistack login")} first.`);
469
+ process.exit(1);
470
+ }
471
+ const cwd = process.cwd();
472
+ const savedName = getProjectName(cwd);
473
+ const savedExcluded = getExcludedPaths(cwd);
474
+ const s = p2.spinner();
475
+ s.start("Scanning...");
476
+ const localFiles = scanLocal(cwd);
477
+ const globalFiles = options.global ? scanGlobal() : [];
478
+ s.stop("Scan complete");
479
+ if (localFiles.length === 0 && globalFiles.length === 0) {
480
+ p2.log.warn("No AI configuration files found.");
481
+ p2.outro(dim("nothing to collect"));
482
+ return;
483
+ }
484
+ let projectName;
485
+ if (savedName) {
486
+ p2.log.info(`${dim("PROJECT")} ${limeBold(savedName)}`);
487
+ projectName = savedName;
488
+ } else {
489
+ const defaultName = basename2(cwd);
490
+ const name = await p2.text({
491
+ message: "Project name:",
492
+ defaultValue: defaultName,
493
+ placeholder: defaultName
494
+ });
495
+ if (p2.isCancel(name)) {
496
+ p2.cancel("Cancelled.");
497
+ process.exit(0);
498
+ }
499
+ projectName = name || defaultName;
500
+ }
501
+ const allFiles = [...localFiles, ...globalFiles];
502
+ let selectedFiles = allFiles.filter(
503
+ (f) => !savedExcluded.includes(f.relativePath)
504
+ );
505
+ let excluded = allFiles.filter((f) => savedExcluded.includes(f.relativePath));
506
+ p2.log.info(
507
+ `${lime(String(selectedFiles.length))} included${excluded.length > 0 ? ` \xB7 ${dim(String(excluded.length) + " excluded")}` : ""}`
508
+ );
509
+ let allInstructions = classify(selectedFiles);
510
+ let existingProject = null;
511
+ try {
512
+ const check = await projectsCheck(token, projectName);
513
+ if (check.exists && check.slug) {
514
+ const shortId = check.slug.includes("-") ? check.slug.slice(check.slug.lastIndexOf("-") + 1) : check.slug;
515
+ existingProject = await projectGet(shortId);
516
+ }
517
+ } catch (err) {
518
+ p2.log.error(err instanceof Error ? err.message : String(err));
519
+ process.exit(1);
520
+ }
521
+ if (existingProject) {
522
+ const diff = diffInstructions(
523
+ allInstructions,
524
+ existingProject.instructions
525
+ );
526
+ if (diff.changed === 0 && diff.added === 0 && diff.removed === 0) {
527
+ p2.log.info("No changes since last collect.");
528
+ p2.outro(dim("nothing to upload"));
529
+ return;
530
+ }
531
+ divider();
532
+ section("changes");
533
+ lines(
534
+ diff.details.map((f) => {
535
+ if (f.status === "added") return lime(`+ ${f.name}`);
536
+ if (f.status === "changed") return yellow(`~ ${f.name}`);
537
+ return red(`- ${f.name}`);
538
+ })
539
+ );
540
+ if (diff.unchanged > 0) {
541
+ lines([dim(`${diff.unchanged} unchanged`)]);
542
+ }
543
+ divider();
544
+ } else {
545
+ const local = selectedFiles.filter((f) => f.source === "local");
546
+ const global = selectedFiles.filter((f) => f.source === "global");
547
+ if (local.length > 0) {
548
+ p2.log.step(`${bold("LOCAL")} ${dim(String(local.length))}`);
549
+ divider();
550
+ for (const [type, files] of groupByType(local)) {
551
+ lines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);
552
+ lines(files.map((f) => dim(` ${f.relativePath}`)));
553
+ }
554
+ divider();
555
+ }
556
+ if (global.length > 0) {
557
+ p2.log.step(`${bold("GLOBAL")} ${dim(String(global.length))}`);
558
+ divider();
559
+ for (const [type, files] of groupByType(global)) {
560
+ lines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);
561
+ lines(files.map((f) => dim(` ${f.relativePath}`)));
562
+ }
563
+ divider();
564
+ }
565
+ }
566
+ const action = await p2.select({
567
+ message: existingProject ? "Upload changes?" : `Upload ${bold(String(selectedFiles.length))} files as ${limeBold(projectName)}?`,
568
+ options: [
569
+ { value: "upload", label: "Upload" },
570
+ { value: "customize", label: "Select files" },
571
+ { value: "cancel", label: "Cancel" }
572
+ ]
573
+ });
574
+ if (p2.isCancel(action) || action === "cancel") {
575
+ p2.cancel("Cancelled.");
576
+ process.exit(0);
577
+ }
578
+ if (action === "customize") {
579
+ const selected = await p2.multiselect({
580
+ message: "Select files to include:",
581
+ options: allFiles.map((f) => ({
582
+ value: f.relativePath,
583
+ label: f.relativePath,
584
+ hint: `${f.type}${f.source === "global" ? " \xB7 global" : ""}`
585
+ })),
586
+ initialValues: selectedFiles.map((f) => f.relativePath)
587
+ });
588
+ if (p2.isCancel(selected)) {
589
+ p2.cancel("Cancelled.");
590
+ process.exit(0);
591
+ }
592
+ const selectedSet = new Set(selected);
593
+ selectedFiles = allFiles.filter((f) => selectedSet.has(f.relativePath));
594
+ excluded = allFiles.filter((f) => !selectedSet.has(f.relativePath));
595
+ allInstructions = classify(selectedFiles);
596
+ if (selectedFiles.length === 0) {
597
+ p2.log.warn("No files selected.");
598
+ p2.outro(dim("nothing to collect"));
599
+ process.exit(0);
600
+ }
601
+ }
602
+ s.start("Uploading...");
603
+ try {
604
+ const result = await projectsCollect(token, {
605
+ name: projectName,
606
+ instructions: allInstructions
607
+ });
608
+ s.stop(lime("Uploaded"));
609
+ saveProjectSettings(
610
+ cwd,
611
+ projectName,
612
+ excluded.map((f) => f.relativePath)
613
+ );
614
+ p2.log.success(dim(result.url));
615
+ p2.outro(lime("done"));
616
+ } catch (err) {
617
+ s.stop("Upload failed");
618
+ p2.log.error(err instanceof Error ? err.message : String(err));
619
+ process.exit(1);
620
+ }
621
+ }
622
+ var TYPE_ORDER = [
623
+ "config",
624
+ "prompt",
625
+ "rule",
626
+ "command",
627
+ "skill",
628
+ "subagent",
629
+ "mcp",
630
+ "hook",
631
+ "custom"
632
+ ];
633
+ function groupByType(files) {
634
+ const map = /* @__PURE__ */ new Map();
635
+ for (const f of files) {
636
+ const existing = map.get(f.type) ?? [];
637
+ existing.push(f);
638
+ map.set(f.type, existing);
639
+ }
640
+ const sorted = /* @__PURE__ */ new Map();
641
+ for (const type of TYPE_ORDER) {
642
+ const group = map.get(type);
643
+ if (group) sorted.set(type, group);
644
+ }
645
+ for (const [type, group] of map) {
646
+ if (!sorted.has(type)) sorted.set(type, group);
647
+ }
648
+ return sorted;
649
+ }
650
+ function diffInstructions(current, existing) {
651
+ const existingMap = /* @__PURE__ */ new Map();
652
+ for (const item of existing) {
653
+ for (const file of item.files) {
654
+ existingMap.set(file.path ?? file.name, file.content);
655
+ }
656
+ }
657
+ const currentMap = /* @__PURE__ */ new Map();
658
+ for (const item of current) {
659
+ for (const file of item.files) {
660
+ currentMap.set(file.path ?? file.name, file.content);
661
+ }
662
+ }
663
+ const details = [];
664
+ let added = 0;
665
+ let changed = 0;
666
+ let unchanged = 0;
667
+ for (const [key, content] of currentMap) {
668
+ const prev = existingMap.get(key);
669
+ if (prev === void 0) {
670
+ added++;
671
+ details.push({ name: key, status: "added" });
672
+ } else if (prev !== content) {
673
+ changed++;
674
+ details.push({ name: key, status: "changed" });
675
+ } else {
676
+ unchanged++;
677
+ }
678
+ }
679
+ let removed = 0;
680
+ for (const key of existingMap.keys()) {
681
+ if (!currentMap.has(key)) {
682
+ removed++;
683
+ details.push({ name: key, status: "removed" });
684
+ }
685
+ }
686
+ return { added, changed, removed, unchanged, details };
687
+ }
688
+
689
+ // src/commands/create.ts
690
+ import * as p3 from "@clack/prompts";
691
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
692
+ import { dirname as dirname2, join as join3 } from "path";
693
+ async function createCommand(slugOrShortId) {
694
+ p3.intro(banner("create"));
695
+ const s = p3.spinner();
696
+ s.start("Fetching project...");
697
+ const shortId = slugOrShortId.includes("-") ? slugOrShortId.slice(slugOrShortId.lastIndexOf("-") + 1) : slugOrShortId;
698
+ let project;
699
+ try {
700
+ project = await projectGet(shortId);
701
+ if (!project) {
702
+ s.stop("Not found");
703
+ p3.log.error(`Project "${slugOrShortId}" not found.`);
704
+ process.exit(1);
705
+ }
706
+ s.stop(bold(project.name));
707
+ } catch (err) {
708
+ s.stop("Failed to fetch project");
709
+ p3.log.error(err instanceof Error ? err.message : String(err));
710
+ process.exit(1);
711
+ }
712
+ const localFiles = [];
713
+ const globalFiles = [];
714
+ for (const item of project.instructions) {
715
+ for (const file of item.files) {
716
+ const writePath = file.path ?? file.name;
717
+ const isGlobal = file.tags?.includes("global");
718
+ if (isGlobal) {
719
+ globalFiles.push({ path: writePath, content: file.content });
720
+ } else {
721
+ localFiles.push({ path: writePath, content: file.content });
722
+ }
723
+ }
724
+ }
725
+ if (globalFiles.length > 0) {
726
+ section("global config", globalFiles.length);
727
+ lines([dim("view only")]);
728
+ lines(globalFiles.map((f) => dim(f.path)));
729
+ }
730
+ if (localFiles.length === 0) {
731
+ p3.log.warn("No local files to write.");
732
+ p3.outro(dim("nothing to create"));
733
+ return;
734
+ }
735
+ const cwd = process.cwd();
736
+ const toWrite = [];
737
+ const skipped = [];
738
+ for (const f of localFiles) {
739
+ const fullPath = join3(cwd, f.path);
740
+ if (existsSync3(fullPath)) {
741
+ const existing = readFileSync3(fullPath, "utf-8");
742
+ skipped.push({ path: f.path, differs: existing !== f.content });
743
+ } else {
744
+ toWrite.push(f);
745
+ }
746
+ }
747
+ section("local files", localFiles.length);
748
+ lines(toWrite.map((f) => lime(`+ ${f.path}`)));
749
+ lines(
750
+ skipped.map(
751
+ (f) => f.differs ? `${yellow(`= ${f.path}`)} ${dim("(differs)")}` : dim(`= ${f.path} (identical)`)
752
+ )
753
+ );
754
+ if (toWrite.length === 0) {
755
+ divider();
756
+ p3.log.info("All local files already exist.");
757
+ p3.outro(dim("nothing to write"));
758
+ return;
759
+ }
760
+ divider();
761
+ const confirm2 = await p3.confirm({
762
+ message: `Write ${lime(String(toWrite.length))} new files? ${dim(`(${skipped.length} skipped)`)}`
763
+ });
764
+ if (p3.isCancel(confirm2) || !confirm2) {
765
+ p3.cancel("Cancelled.");
766
+ process.exit(0);
767
+ }
768
+ for (const f of toWrite) {
769
+ const fullPath = join3(cwd, f.path);
770
+ const dir = dirname2(fullPath);
771
+ mkdirSync2(dir, { recursive: true });
772
+ writeFileSync2(fullPath, f.content);
773
+ }
774
+ p3.log.success(
775
+ `${lime(String(toWrite.length))} written, ${dim(String(skipped.length) + " skipped")}`
776
+ );
777
+ p3.outro(lime("done"));
778
+ }
779
+
780
+ // src/index.ts
781
+ var program = new Command();
782
+ program.name("aistack").description("Share and clone AI development configurations").version("0.1.0");
783
+ program.command("login").description("Authenticate with AI Stack").action(loginCommand);
784
+ program.command("collect").description("Scan and upload AI config files from your project").option("--no-global", "Exclude global config files (~/.claude, etc.)").action((options) => collectCommand({ global: options.global ?? true }));
785
+ program.command("create").description("Download and write AI config files from a shared project").argument("<slug>", "Project slug or short ID").action(createCommand);
786
+ program.parse();
787
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/api.ts","../src/config.ts","../src/theme.ts","../src/commands/collect.ts","../src/scanner.ts","../src/classifier.ts","../src/commands/create.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { loginCommand } from \"./commands/login.js\";\nimport { collectCommand } from \"./commands/collect.js\";\nimport { createCommand } from \"./commands/create.js\";\n\nconst program = new Command();\n\nprogram\n\t.name(\"aistack\")\n\t.description(\"Share and clone AI development configurations\")\n\t.version(\"0.1.0\");\n\nprogram\n\t.command(\"login\")\n\t.description(\"Authenticate with AI Stack\")\n\t.action(loginCommand);\n\nprogram\n\t.command(\"collect\")\n\t.description(\"Scan and upload AI config files from your project\")\n\t.option(\"--no-global\", \"Exclude global config files (~/.claude, etc.)\")\n\t.action((options) => collectCommand({ global: options.global ?? true }));\n\nprogram\n\t.command(\"create\")\n\t.description(\"Download and write AI config files from a shared project\")\n\t.argument(\"<slug>\", \"Project slug or short ID\")\n\t.action(createCommand);\n\nprogram.parse();\n","import * as p from \"@clack/prompts\";\nimport open from \"open\";\nimport { authStart, authPoll } from \"../api.js\";\nimport { saveToken } from \"../config.js\";\nimport { banner, dim, lime, limeBold } from \"../theme.js\";\n\nexport async function loginCommand() {\n\tp.intro(banner(\"login\"));\n\n\tconst s = p.spinner();\n\ts.start(\"Starting authentication...\");\n\n\tlet session: Awaited<ReturnType<typeof authStart>>;\n\ttry {\n\t\tsession = await authStart();\n\t\ts.stop(\"Session created\");\n\t} catch (err) {\n\t\ts.stop(\"Failed to start authentication\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\tprocess.exit(1);\n\t}\n\n\tp.log.info(`${dim(\"CODE\")} ${limeBold(session.userCode)}`);\n\tp.log.info(`${dim(\"OPEN\")} ${dim(session.authUrl)}`);\n\n\ttry {\n\t\tawait open(session.authUrl);\n\t} catch {\n\t\tp.log.warn(\n\t\t\t\"Could not open browser automatically. Please visit the URL above.\",\n\t\t);\n\t}\n\n\ts.start(\"Waiting for approval...\");\n\n\tconst maxAttempts = 36;\n\tfor (let i = 0; i < maxAttempts; i++) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 5000));\n\n\t\ttry {\n\t\t\tconst result = await authPoll(session.secretId);\n\n\t\t\tif (result.status === \"approved\" && result.token) {\n\t\t\t\ts.stop(lime(\"Authenticated\"));\n\t\t\t\tsaveToken(result.token, result.userId);\n\t\t\t\tp.log.success(\n\t\t\t\t\t`Token saved. Run ${limeBold(\"aistack collect\")} to get started.`,\n\t\t\t\t);\n\t\t\t\tp.outro(lime(\"done\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (result.status === \"expired\") {\n\t\t\t\ts.stop(\"Session expired\");\n\t\t\t\tp.log.error(\"Authentication session expired. Please try again.\");\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\ts.stop(\"Error polling\");\n\t\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\ts.stop(\"Timed out\");\n\tp.log.error(\"Authentication timed out after 3 minutes. Please try again.\");\n\tprocess.exit(1);\n}\n","const BASE_URL = process.env.AISTACK_URL || \"https://aistack.to\";\n\nasync function request(\n\tpath: string,\n\toptions: RequestInit = {},\n): Promise<Response> {\n\treturn fetch(`${BASE_URL}${path}`, {\n\t\t...options,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t...options.headers,\n\t\t},\n\t});\n}\n\nfunction authHeaders(token: string): HeadersInit {\n\treturn { Authorization: `Bearer ${token}` };\n}\n\nexport async function authStart(): Promise<{\n\tsecretId: string;\n\tuserCode: string;\n\tauthUrl: string;\n}> {\n\tconst res = await request(\"/api/cli/auth/start\", { method: \"POST\" });\n\tif (!res.ok) throw new Error(`Auth start failed: ${res.status}`);\n\treturn res.json();\n}\n\nexport async function authPoll(\n\tsecretId: string,\n): Promise<{ status: string; token?: string; userId?: string }> {\n\tconst res = await request(\n\t\t`/api/cli/auth/poll?secretId=${encodeURIComponent(secretId)}`,\n\t);\n\tif (!res.ok) throw new Error(`Auth poll failed: ${res.status}`);\n\treturn res.json();\n}\n\nexport async function projectsCheck(\n\ttoken: string,\n\tname: string,\n): Promise<{ exists: boolean; slug?: string }> {\n\tconst res = await request(\n\t\t`/api/cli/projects/check?name=${encodeURIComponent(name)}`,\n\t\t{\n\t\t\theaders: authHeaders(token),\n\t\t},\n\t);\n\tif (res.status === 401)\n\t\tthrow new Error(\"Authentication expired. Run `aistack login` again.\");\n\tif (!res.ok) throw new Error(`Project check failed: ${res.status}`);\n\treturn res.json();\n}\n\nexport async function projectsCollect(\n\ttoken: string,\n\tdata: { name: string; instructions: InstructionItem[] },\n): Promise<{ slug: string; shortId: string; url: string }> {\n\tconst res = await request(\"/api/cli/projects/collect\", {\n\t\tmethod: \"POST\",\n\t\theaders: authHeaders(token),\n\t\tbody: JSON.stringify(data),\n\t});\n\tif (res.status === 401)\n\t\tthrow new Error(\"Authentication expired. Run `aistack login` again.\");\n\tif (!res.ok) {\n\t\tconst body = await res.json().catch(() => ({}));\n\t\tthrow new Error(\n\t\t\t(body as { error?: string }).error || `Collect failed: ${res.status}`,\n\t\t);\n\t}\n\treturn res.json();\n}\n\nexport async function projectGet(shortId: string): Promise<ProjectData | null> {\n\tconst res = await request(`/api/cli/projects/${encodeURIComponent(shortId)}`);\n\tif (res.status === 404) return null;\n\tif (!res.ok) throw new Error(`Project fetch failed: ${res.status}`);\n\treturn res.json();\n}\n\n// Types used across the CLI\nexport interface InstructionFile {\n\tname: string;\n\tcontent: string;\n\tpath?: string;\n\ttags?: string[];\n}\n\nexport interface InstructionItem {\n\ttype: string;\n\tname: string;\n\tdescription?: string;\n\tfiles: InstructionFile[];\n}\n\nexport interface ProjectData {\n\tname: string;\n\tslug: string;\n\tshortId: string;\n\tinstructions: InstructionItem[];\n\tcreator?: { name: string };\n\tstack?: { name: string; slug: string };\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nconst CONFIG_DIR = join(homedir(), \".config\", \"aistack\");\nconst CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n\ninterface Credentials {\n\ttoken: string;\n\tuserId?: string;\n}\n\nexport function getToken(): string | null {\n\tif (!existsSync(CREDENTIALS_FILE)) return null;\n\ttry {\n\t\tconst data = JSON.parse(\n\t\t\treadFileSync(CREDENTIALS_FILE, \"utf-8\"),\n\t\t) as Credentials;\n\t\treturn data.token ?? null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function saveToken(token: string, userId?: string): void {\n\tmkdirSync(CONFIG_DIR, { recursive: true });\n\twriteFileSync(CREDENTIALS_FILE, JSON.stringify({ token, userId }, null, 2));\n}\n\nexport function clearToken(): void {\n\tif (existsSync(CREDENTIALS_FILE)) {\n\t\twriteFileSync(CREDENTIALS_FILE, \"{}\");\n\t}\n}\n\nconst PROJECTS_FILE = join(CONFIG_DIR, \"projects.json\");\n\ninterface ProjectEntry {\n\tname: string;\n\texcluded?: string[];\n}\n\ninterface ProjectsData {\n\t[directory: string]: ProjectEntry;\n}\n\nfunction readProjects(): ProjectsData {\n\tif (!existsSync(PROJECTS_FILE)) return {};\n\ttry {\n\t\tconst raw = JSON.parse(readFileSync(PROJECTS_FILE, \"utf-8\"));\n\t\t// Migrate old format (string values) to new format\n\t\tconst data: ProjectsData = {};\n\t\tfor (const [key, value] of Object.entries(raw)) {\n\t\t\tif (typeof value === \"string\") {\n\t\t\t\tdata[key] = { name: value };\n\t\t\t} else {\n\t\t\t\tdata[key] = value as ProjectEntry;\n\t\t\t}\n\t\t}\n\t\treturn data;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nfunction writeProjects(data: ProjectsData): void {\n\tmkdirSync(CONFIG_DIR, { recursive: true });\n\twriteFileSync(PROJECTS_FILE, JSON.stringify(data, null, 2));\n}\n\nexport function getProjectName(directory: string): string | null {\n\treturn readProjects()[directory]?.name ?? null;\n}\n\nexport function getExcludedPaths(directory: string): string[] {\n\treturn readProjects()[directory]?.excluded ?? [];\n}\n\nexport function saveProjectSettings(\n\tdirectory: string,\n\tname: string,\n\texcluded: string[],\n): void {\n\tconst data = readProjects();\n\tdata[directory] = {\n\t\tname,\n\t\texcluded: excluded.length > 0 ? excluded : undefined,\n\t};\n\twriteProjects(data);\n}\n","const esc = (code: string) => `\\x1b[${code}m`;\nconst reset = esc(\"0\");\n\nconst LIME = \"163;230;53\";\nconst BLACK = \"0;0;0\";\nconst YELLOW = \"250;204;21\";\nconst RED = \"248;113;113\";\nconst MUTED = \"120;120;120\";\n\nexport const lime = (s: string) => `${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const limeBold = (s: string) =>\n\t`${esc(\"1\")}${esc(`38;2;${LIME}`)}${s}${reset}`;\nexport const bgLime = (s: string) =>\n\t`${esc(`48;2;${LIME}`)}${esc(`38;2;${BLACK}`)}${s}${reset}`;\nexport const yellow = (s: string) => `${esc(`38;2;${YELLOW}`)}${s}${reset}`;\nexport const red = (s: string) => `${esc(`38;2;${RED}`)}${s}${reset}`;\nexport const dim = (s: string) => `${esc(`38;2;${MUTED}`)}${s}${reset}`;\nexport const bold = (s: string) => `${esc(\"1\")}${s}${reset}`;\n\n// ■ logo square in lime + AISTACK in bold on lime bg\nexport const banner = (cmd: string) =>\n\t`${lime(\"■\")} ${bgLime(` AISTACK `)} ${bold(cmd.toUpperCase())}`;\n\n// Compact line with clack-style bar\nconst BAR = `${esc(`38;2;${MUTED}`)}│${reset}`;\n\nexport function lines(items: string[]) {\n\tfor (const item of items) {\n\t\tconsole.log(`${BAR} ${item}`);\n\t}\n}\n\nexport function section(label: string, count?: number) {\n\tconsole.log(`${BAR}`);\n\tconst countStr = count !== undefined ? ` ${dim(String(count))}` : \"\";\n\tconsole.log(`${BAR} ${bold(label.toUpperCase())}${countStr}`);\n}\n\nexport function divider() {\n\tconsole.log(`${BAR} ${dim(\"─\".repeat(40))}`);\n}\n","import * as p from \"@clack/prompts\";\nimport { basename } from \"node:path\";\nimport { scanLocal, scanGlobal, type ScannedFile } from \"../scanner.js\";\nimport { classify } from \"../classifier.js\";\nimport {\n\tprojectsCheck,\n\tprojectsCollect,\n\tprojectGet,\n\ttype InstructionItem,\n} from \"../api.js\";\nimport {\n\tgetToken,\n\tgetProjectName,\n\tgetExcludedPaths,\n\tsaveProjectSettings,\n} from \"../config.js\";\nimport {\n\tbanner,\n\tbold,\n\tdim,\n\tdivider,\n\tlime,\n\tlimeBold,\n\tlines,\n\tred,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\nexport async function collectCommand(options: { global: boolean }) {\n\tp.intro(banner(\"collect\"));\n\n\tconst token = getToken();\n\tif (!token) {\n\t\tp.log.error(`Not authenticated. Run ${limeBold(\"aistack login\")} first.`);\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst savedName = getProjectName(cwd);\n\tconst savedExcluded = getExcludedPaths(cwd);\n\n\tconst s = p.spinner();\n\ts.start(\"Scanning...\");\n\n\tconst localFiles = scanLocal(cwd);\n\tconst globalFiles = options.global ? scanGlobal() : [];\n\ts.stop(\"Scan complete\");\n\n\tif (localFiles.length === 0 && globalFiles.length === 0) {\n\t\tp.log.warn(\"No AI configuration files found.\");\n\t\tp.outro(dim(\"nothing to collect\"));\n\t\treturn;\n\t}\n\n\t// Project name\n\tlet projectName: string;\n\tif (savedName) {\n\t\tp.log.info(`${dim(\"PROJECT\")} ${limeBold(savedName)}`);\n\t\tprojectName = savedName;\n\t} else {\n\t\tconst defaultName = basename(cwd);\n\t\tconst name = await p.text({\n\t\t\tmessage: \"Project name:\",\n\t\t\tdefaultValue: defaultName,\n\t\t\tplaceholder: defaultName,\n\t\t});\n\t\tif (p.isCancel(name)) {\n\t\t\tp.cancel(\"Cancelled.\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t\tprojectName = (name as string) || defaultName;\n\t}\n\n\t// Apply saved exclusions\n\tconst allFiles = [...localFiles, ...globalFiles];\n\tlet selectedFiles = allFiles.filter(\n\t\t(f) => !savedExcluded.includes(f.relativePath),\n\t);\n\tlet excluded = allFiles.filter((f) => savedExcluded.includes(f.relativePath));\n\n\t// Show file counts\n\tp.log.info(\n\t\t`${lime(String(selectedFiles.length))} included${excluded.length > 0 ? ` · ${dim(String(excluded.length) + \" excluded\")}` : \"\"}`,\n\t);\n\n\t// Classify selected files\n\tlet allInstructions = classify(selectedFiles);\n\n\t// Fetch existing project and diff\n\tlet existingProject: Awaited<ReturnType<typeof projectGet>> = null;\n\ttry {\n\t\tconst check = await projectsCheck(token, projectName);\n\t\tif (check.exists && check.slug) {\n\t\t\tconst shortId = check.slug.includes(\"-\")\n\t\t\t\t? check.slug.slice(check.slug.lastIndexOf(\"-\") + 1)\n\t\t\t\t: check.slug;\n\t\t\texistingProject = await projectGet(shortId);\n\t\t}\n\t} catch (err) {\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\tprocess.exit(1);\n\t}\n\n\t// Show file list or diff\n\tif (existingProject) {\n\t\tconst diff = diffInstructions(\n\t\t\tallInstructions,\n\t\t\texistingProject.instructions,\n\t\t);\n\n\t\tif (diff.changed === 0 && diff.added === 0 && diff.removed === 0) {\n\t\t\tp.log.info(\"No changes since last collect.\");\n\t\t\tp.outro(dim(\"nothing to upload\"));\n\t\t\treturn;\n\t\t}\n\n\t\tdivider();\n\t\tsection(\"changes\");\n\t\tlines(\n\t\t\tdiff.details.map((f) => {\n\t\t\t\tif (f.status === \"added\") return lime(`+ ${f.name}`);\n\t\t\t\tif (f.status === \"changed\") return yellow(`~ ${f.name}`);\n\t\t\t\treturn red(`- ${f.name}`);\n\t\t\t}),\n\t\t);\n\t\tif (diff.unchanged > 0) {\n\t\t\tlines([dim(`${diff.unchanged} unchanged`)]);\n\t\t}\n\t\tdivider();\n\t} else {\n\t\tconst local = selectedFiles.filter((f) => f.source === \"local\");\n\t\tconst global = selectedFiles.filter((f) => f.source === \"global\");\n\n\t\tif (local.length > 0) {\n\t\t\tp.log.step(`${bold(\"LOCAL\")} ${dim(String(local.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(local)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t\tif (global.length > 0) {\n\t\t\tp.log.step(`${bold(\"GLOBAL\")} ${dim(String(global.length))}`);\n\t\t\tdivider();\n\t\t\tfor (const [type, files] of groupByType(global)) {\n\t\t\t\tlines([`${lime(type.toUpperCase())} ${dim(`${files.length}`)}`]);\n\t\t\t\tlines(files.map((f) => dim(` ${f.relativePath}`)));\n\t\t\t}\n\t\t\tdivider();\n\t\t}\n\t}\n\n\t// Action: upload, customize, or cancel\n\tconst action = await p.select({\n\t\tmessage: existingProject\n\t\t\t? \"Upload changes?\"\n\t\t\t: `Upload ${bold(String(selectedFiles.length))} files as ${limeBold(projectName)}?`,\n\t\toptions: [\n\t\t\t{ value: \"upload\", label: \"Upload\" },\n\t\t\t{ value: \"customize\", label: \"Select files\" },\n\t\t\t{ value: \"cancel\", label: \"Cancel\" },\n\t\t],\n\t});\n\n\tif (p.isCancel(action) || action === \"cancel\") {\n\t\tp.cancel(\"Cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tif (action === \"customize\") {\n\t\tconst selected = await p.multiselect({\n\t\t\tmessage: \"Select files to include:\",\n\t\t\toptions: allFiles.map((f) => ({\n\t\t\t\tvalue: f.relativePath,\n\t\t\t\tlabel: f.relativePath,\n\t\t\t\thint: `${f.type}${f.source === \"global\" ? \" · global\" : \"\"}`,\n\t\t\t})),\n\t\t\tinitialValues: selectedFiles.map((f) => f.relativePath),\n\t\t});\n\n\t\tif (p.isCancel(selected)) {\n\t\t\tp.cancel(\"Cancelled.\");\n\t\t\tprocess.exit(0);\n\t\t}\n\n\t\tconst selectedSet = new Set(selected as string[]);\n\t\tselectedFiles = allFiles.filter((f) => selectedSet.has(f.relativePath));\n\t\texcluded = allFiles.filter((f) => !selectedSet.has(f.relativePath));\n\t\tallInstructions = classify(selectedFiles);\n\n\t\tif (selectedFiles.length === 0) {\n\t\t\tp.log.warn(\"No files selected.\");\n\t\t\tp.outro(dim(\"nothing to collect\"));\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\ts.start(\"Uploading...\");\n\ttry {\n\t\tconst result = await projectsCollect(token, {\n\t\t\tname: projectName,\n\t\t\tinstructions: allInstructions,\n\t\t});\n\t\ts.stop(lime(\"Uploaded\"));\n\t\tsaveProjectSettings(\n\t\t\tcwd,\n\t\t\tprojectName,\n\t\t\texcluded.map((f) => f.relativePath),\n\t\t);\n\t\tp.log.success(dim(result.url));\n\t\tp.outro(lime(\"done\"));\n\t} catch (err) {\n\t\ts.stop(\"Upload failed\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\tprocess.exit(1);\n\t}\n}\n\nconst TYPE_ORDER = [\n\t\"config\",\n\t\"prompt\",\n\t\"rule\",\n\t\"command\",\n\t\"skill\",\n\t\"subagent\",\n\t\"mcp\",\n\t\"hook\",\n\t\"custom\",\n];\n\nfunction groupByType(files: ScannedFile[]): Map<string, ScannedFile[]> {\n\tconst map = new Map<string, ScannedFile[]>();\n\tfor (const f of files) {\n\t\tconst existing = map.get(f.type) ?? [];\n\t\texisting.push(f);\n\t\tmap.set(f.type, existing);\n\t}\n\tconst sorted = new Map<string, ScannedFile[]>();\n\tfor (const type of TYPE_ORDER) {\n\t\tconst group = map.get(type);\n\t\tif (group) sorted.set(type, group);\n\t}\n\tfor (const [type, group] of map) {\n\t\tif (!sorted.has(type)) sorted.set(type, group);\n\t}\n\treturn sorted;\n}\n\ninterface DiffResult {\n\tadded: number;\n\tchanged: number;\n\tremoved: number;\n\tunchanged: number;\n\tdetails: Array<{ name: string; status: \"added\" | \"changed\" | \"removed\" }>;\n}\n\nfunction diffInstructions(\n\tcurrent: InstructionItem[],\n\texisting: InstructionItem[],\n): DiffResult {\n\tconst existingMap = new Map<string, string>();\n\tfor (const item of existing) {\n\t\tfor (const file of item.files) {\n\t\t\texistingMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst currentMap = new Map<string, string>();\n\tfor (const item of current) {\n\t\tfor (const file of item.files) {\n\t\t\tcurrentMap.set(file.path ?? file.name, file.content);\n\t\t}\n\t}\n\n\tconst details: DiffResult[\"details\"] = [];\n\tlet added = 0;\n\tlet changed = 0;\n\tlet unchanged = 0;\n\n\tfor (const [key, content] of currentMap) {\n\t\tconst prev = existingMap.get(key);\n\t\tif (prev === undefined) {\n\t\t\tadded++;\n\t\t\tdetails.push({ name: key, status: \"added\" });\n\t\t} else if (prev !== content) {\n\t\t\tchanged++;\n\t\t\tdetails.push({ name: key, status: \"changed\" });\n\t\t} else {\n\t\t\tunchanged++;\n\t\t}\n\t}\n\n\tlet removed = 0;\n\tfor (const key of existingMap.keys()) {\n\t\tif (!currentMap.has(key)) {\n\t\t\tremoved++;\n\t\t\tdetails.push({ name: key, status: \"removed\" });\n\t\t}\n\t}\n\n\treturn { added, changed, removed, unchanged, details };\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport ignore from \"ignore\";\n\nexport type FileType =\n\t| \"rule\"\n\t| \"mcp\"\n\t| \"skill\"\n\t| \"command\"\n\t| \"prompt\"\n\t| \"hook\"\n\t| \"subagent\"\n\t| \"config\"\n\t| \"custom\";\n\nexport interface ScannedFile {\n\tpath: string;\n\trelativePath: string;\n\tcontent: string;\n\ttype: FileType;\n\tsource: \"local\" | \"global\";\n}\n\nconst MAX_FILE_SIZE = 100 * 1024; // 100KB\n\ninterface FilePattern {\n\tpath: string;\n\ttype: FileType;\n}\n\nconst LOCAL_PATTERNS: FilePattern[] = [\n\t// Rules\n\t{ path: \"CLAUDE.md\", type: \"rule\" },\n\t{ path: \"AGENTS.md\", type: \"rule\" },\n\t{ path: \".cursorrules\", type: \"rule\" },\n\t{ path: \".windsurfrules\", type: \"rule\" },\n\t{ path: \".clinerules\", type: \"rule\" },\n\t{ path: \".github/copilot-instructions.md\", type: \"rule\" },\n\t// MCP\n\t{ path: \"mcp.json\", type: \"mcp\" },\n\t{ path: \".cursor/mcp.json\", type: \"mcp\" },\n\t{ path: \"claude_desktop_config.json\", type: \"mcp\" },\n\t// Config\n\t{ path: \".aider.conf.yml\", type: \"config\" },\n\t{ path: \".continue/config.json\", type: \"config\" },\n\t// Config\n\t{ path: \".claude/settings.json\", type: \"config\" },\n\t{ path: \".claude/settings.local.json\", type: \"config\" },\n\t// Prompts\n\t{ path: \"system-prompt.md\", type: \"prompt\" },\n];\n\nconst LOCAL_DIR_PATTERNS: { dir: string; type: FileType }[] = [\n\t{ dir: \".cursor/rules\", type: \"rule\" },\n\t{ dir: \".claude/commands\", type: \"command\" },\n\t{ dir: \".claude/agents\", type: \"subagent\" },\n\t{ dir: \".claude/hooks\", type: \"hook\" },\n\t{ dir: \"prompts\", type: \"prompt\" },\n\t{ dir: \".ai\", type: \"custom\" },\n];\n\nfunction loadGitignore(cwd: string): ReturnType<typeof ignore> {\n\tconst ig = ignore();\n\tconst gitignorePath = join(cwd, \".gitignore\");\n\tif (existsSync(gitignorePath)) {\n\t\tig.add(readFileSync(gitignorePath, \"utf-8\"));\n\t}\n\tig.add([\"node_modules\", \".git\", \"dist\", \"build\", \".next\", \".output\"]);\n\treturn ig;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n\ttry {\n\t\tconst stat = statSync(filePath);\n\t\tif (stat.size > MAX_FILE_SIZE) return null;\n\t\treturn readFileSync(filePath, \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction walkDir(dir: string, maxDepth = 3, currentDepth = 0): string[] {\n\tif (currentDepth >= maxDepth || !existsSync(dir)) return [];\n\tconst results: string[] = [];\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tconst fullPath = join(dir, entry.name);\n\t\t\tif (entry.isFile()) {\n\t\t\t\tresults.push(fullPath);\n\t\t\t} else if (entry.isDirectory()) {\n\t\t\t\tresults.push(...walkDir(fullPath, maxDepth, currentDepth + 1));\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors, etc */\n\t}\n\treturn results;\n}\n\nexport function scanLocal(cwd: string): ScannedFile[] {\n\tconst ig = loadGitignore(cwd);\n\tconst results: ScannedFile[] = [];\n\n\tfor (const pattern of LOCAL_PATTERNS) {\n\t\tconst filePath = join(cwd, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (!ig.ignores(rel)) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: pattern.type,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const { dir, type } of LOCAL_DIR_PATTERNS) {\n\t\tconst dirPath = join(cwd, dir);\n\t\tconst files = walkDir(dirPath);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Scan for skill directories (dirs with SKILL.md, 3 levels deep)\n\ttry {\n\t\tfor (const entry of readdirSync(cwd, { withFileTypes: true }).filter((e) =>\n\t\t\te.isDirectory(),\n\t\t)) {\n\t\t\tif (ig.ignores(entry.name + \"/\")) continue;\n\t\t\tscanSkillDirs(join(cwd, entry.name), cwd, ig, results, 1);\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n\n\treturn results;\n}\n\nfunction scanSkillDirs(\n\tdir: string,\n\tcwd: string,\n\tig: ReturnType<typeof ignore>,\n\tresults: ScannedFile[],\n\tdepth: number,\n) {\n\tif (depth > 3) return;\n\tconst skillMd = join(dir, \"SKILL.md\");\n\tif (existsSync(skillMd)) {\n\t\tconst files = walkDir(dir, 1);\n\t\tfor (const filePath of files) {\n\t\t\tconst rel = relative(cwd, filePath);\n\t\t\tif (ig.ignores(rel)) continue;\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: rel,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype: \"skill\",\n\t\t\t\t\tsource: \"local\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn;\n\t}\n\ttry {\n\t\tfor (const entry of readdirSync(dir, { withFileTypes: true })) {\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tconst rel = relative(cwd, join(dir, entry.name));\n\t\t\t\tif (!ig.ignores(rel + \"/\")) {\n\t\t\t\t\tscanSkillDirs(join(dir, entry.name), cwd, ig, results, depth + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* permission errors */\n\t}\n}\n\nexport function scanGlobal(): ScannedFile[] {\n\tconst home = homedir();\n\tconst results: ScannedFile[] = [];\n\n\tconst globalPatterns: FilePattern[] = [\n\t\t{ path: \".claude/CLAUDE.md\", type: \"rule\" },\n\t\t{ path: \".claude/settings.json\", type: \"config\" },\n\t\t{ path: \".cursor/mcp.json\", type: \"mcp\" },\n\t\t{ path: \".continue/config.json\", type: \"config\" },\n\t\t{ path: \".aider.conf.yml\", type: \"config\" },\n\t];\n\n\tfor (const pattern of globalPatterns) {\n\t\tconst filePath = join(home, pattern.path);\n\t\tconst content = readFileSafe(filePath);\n\t\tif (content !== null) {\n\t\t\tresults.push({\n\t\t\t\tpath: filePath,\n\t\t\t\trelativePath: `~/${pattern.path}`,\n\t\t\t\tcontent,\n\t\t\t\ttype: pattern.type,\n\t\t\t\tsource: \"global\",\n\t\t\t});\n\t\t}\n\t}\n\n\tconst globalDirs: { dir: string; type: FileType }[] = [\n\t\t{ dir: \".claude/commands\", type: \"command\" },\n\t\t{ dir: \".claude/agents\", type: \"subagent\" },\n\t\t{ dir: \".claude/hooks\", type: \"hook\" },\n\t\t{ dir: \".cursor/rules\", type: \"rule\" },\n\t];\n\n\tfor (const { dir, type } of globalDirs) {\n\t\tconst dirPath = join(home, dir);\n\t\tconst files = walkDir(dirPath, 2);\n\t\tfor (const filePath of files) {\n\t\t\tconst content = readFileSafe(filePath);\n\t\t\tif (content !== null) {\n\t\t\t\tresults.push({\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\trelativePath: `~/${relative(home, filePath)}`,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttype,\n\t\t\t\t\tsource: \"global\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n","import type { ScannedFile } from \"./scanner.js\";\nimport type { InstructionItem } from \"./api.js\";\nimport { basename, dirname } from \"node:path\";\n\nexport function classify(files: ScannedFile[]): InstructionItem[] {\n\tconst skillDirs = new Map<string, ScannedFile[]>();\n\tconst nonSkillFiles: ScannedFile[] = [];\n\n\tfor (const file of files) {\n\t\tif (file.type === \"skill\") {\n\t\t\tconst dir = dirname(file.relativePath);\n\t\t\tconst existing = skillDirs.get(dir) ?? [];\n\t\t\texisting.push(file);\n\t\t\tskillDirs.set(dir, existing);\n\t\t} else {\n\t\t\tnonSkillFiles.push(file);\n\t\t}\n\t}\n\n\tconst items: InstructionItem[] = [];\n\n\tfor (const file of nonSkillFiles) {\n\t\tconst tags = file.source === \"global\" ? [\"global\"] : undefined;\n\t\titems.push({\n\t\t\ttype: file.type,\n\t\t\tname: file.relativePath,\n\t\t\tfiles: [\n\t\t\t\t{\n\t\t\t\t\tname: basename(file.relativePath),\n\t\t\t\t\tcontent: file.content,\n\t\t\t\t\tpath: file.relativePath,\n\t\t\t\t\ttags,\n\t\t\t\t},\n\t\t\t],\n\t\t});\n\t}\n\n\tfor (const [dir, dirFiles] of skillDirs) {\n\t\tconst isGlobal = dirFiles[0]?.source === \"global\";\n\t\titems.push({\n\t\t\ttype: \"skill\",\n\t\t\tname: dir,\n\t\t\tfiles: dirFiles.map((f) => ({\n\t\t\t\tname: basename(f.relativePath),\n\t\t\t\tcontent: f.content,\n\t\t\t\tpath: f.relativePath,\n\t\t\t\ttags: isGlobal ? [\"global\"] : undefined,\n\t\t\t})),\n\t\t});\n\t}\n\n\treturn items;\n}\n\nexport function isGlobalItem(item: InstructionItem): boolean {\n\treturn item.files.some((f) => f.tags?.includes(\"global\"));\n}\n","import * as p from \"@clack/prompts\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { projectGet } from \"../api.js\";\nimport {\n\tbanner,\n\tbold,\n\tdim,\n\tdivider,\n\tlime,\n\tlines,\n\tsection,\n\tyellow,\n} from \"../theme.js\";\n\nexport async function createCommand(slugOrShortId: string) {\n\tp.intro(banner(\"create\"));\n\n\tconst s = p.spinner();\n\ts.start(\"Fetching project...\");\n\n\tconst shortId = slugOrShortId.includes(\"-\")\n\t\t? slugOrShortId.slice(slugOrShortId.lastIndexOf(\"-\") + 1)\n\t\t: slugOrShortId;\n\n\tlet project: Awaited<ReturnType<typeof projectGet>>;\n\ttry {\n\t\tproject = await projectGet(shortId);\n\t\tif (!project) {\n\t\t\ts.stop(\"Not found\");\n\t\t\tp.log.error(`Project \"${slugOrShortId}\" not found.`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ts.stop(bold(project.name));\n\t} catch (err) {\n\t\ts.stop(\"Failed to fetch project\");\n\t\tp.log.error(err instanceof Error ? err.message : String(err));\n\t\tprocess.exit(1);\n\t}\n\n\tconst localFiles: FileToWrite[] = [];\n\tconst globalFiles: FileToWrite[] = [];\n\n\tfor (const item of project.instructions) {\n\t\tfor (const file of item.files) {\n\t\t\tconst writePath = file.path ?? file.name;\n\t\t\tconst isGlobal = file.tags?.includes(\"global\");\n\t\t\tif (isGlobal) {\n\t\t\t\tglobalFiles.push({ path: writePath, content: file.content });\n\t\t\t} else {\n\t\t\t\tlocalFiles.push({ path: writePath, content: file.content });\n\t\t\t}\n\t\t}\n\t}\n\n\tif (globalFiles.length > 0) {\n\t\tsection(\"global config\", globalFiles.length);\n\t\tlines([dim(\"view only\")]);\n\t\tlines(globalFiles.map((f) => dim(f.path)));\n\t}\n\n\tif (localFiles.length === 0) {\n\t\tp.log.warn(\"No local files to write.\");\n\t\tp.outro(dim(\"nothing to create\"));\n\t\treturn;\n\t}\n\n\tconst cwd = process.cwd();\n\tconst toWrite: FileToWrite[] = [];\n\tconst skipped: { path: string; differs: boolean }[] = [];\n\n\tfor (const f of localFiles) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tif (existsSync(fullPath)) {\n\t\t\tconst existing = readFileSync(fullPath, \"utf-8\");\n\t\t\tskipped.push({ path: f.path, differs: existing !== f.content });\n\t\t} else {\n\t\t\ttoWrite.push(f);\n\t\t}\n\t}\n\n\tsection(\"local files\", localFiles.length);\n\tlines(toWrite.map((f) => lime(`+ ${f.path}`)));\n\tlines(\n\t\tskipped.map((f) =>\n\t\t\tf.differs\n\t\t\t\t? `${yellow(`= ${f.path}`)} ${dim(\"(differs)\")}`\n\t\t\t\t: dim(`= ${f.path} (identical)`),\n\t\t),\n\t);\n\n\tif (toWrite.length === 0) {\n\t\tdivider();\n\t\tp.log.info(\"All local files already exist.\");\n\t\tp.outro(dim(\"nothing to write\"));\n\t\treturn;\n\t}\n\n\tdivider();\n\n\tconst confirm = await p.confirm({\n\t\tmessage: `Write ${lime(String(toWrite.length))} new files? ${dim(`(${skipped.length} skipped)`)}`,\n\t});\n\n\tif (p.isCancel(confirm) || !confirm) {\n\t\tp.cancel(\"Cancelled.\");\n\t\tprocess.exit(0);\n\t}\n\n\tfor (const f of toWrite) {\n\t\tconst fullPath = join(cwd, f.path);\n\t\tconst dir = dirname(fullPath);\n\t\tmkdirSync(dir, { recursive: true });\n\t\twriteFileSync(fullPath, f.content);\n\t}\n\n\tp.log.success(\n\t\t`${lime(String(toWrite.length))} written, ${dim(String(skipped.length) + \" skipped\")}`,\n\t);\n\tp.outro(lime(\"done\"));\n}\n\ninterface FileToWrite {\n\tpath: string;\n\tcontent: string;\n}\n"],"mappings":"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","names":["p","basename","existsSync","readFileSync","join","homedir","basename","p","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","join","join","existsSync","readFileSync","confirm","dirname","mkdirSync","writeFileSync"]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@use-aistack/cli",
3
+ "version": "0.1.0",
4
+ "description": "Share and clone AI development configurations",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/alp82/aistack.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "homepage": "https://github.com/alp82/aistack#readme",
13
+ "bin": { "aistack": "./dist/index.js" },
14
+ "files": ["dist", "README.md"],
15
+ "publishConfig": { "access": "public" },
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "postbuild": "chmod +x dist/index.js",
19
+ "dev": "tsup --watch",
20
+ "prepublishOnly": "pnpm build"
21
+ },
22
+ "dependencies": {
23
+ "commander": "^13.1.0",
24
+ "open": "^10.1.0",
25
+ "picocolors": "^1.1.0",
26
+ "@clack/prompts": "^0.10.0",
27
+ "ignore": "^7.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "tsup": "^8.4.0",
31
+ "typescript": "^5.7.2",
32
+ "@types/node": "^22.10.2"
33
+ },
34
+ "engines": { "node": ">=18" }
35
+ }