@sniper.ai/cli 1.0.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,83 @@
1
+ # @sniper.ai/cli
2
+
3
+ CLI tool for scaffolding and managing SNIPER-enabled projects.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @sniper.ai/cli
9
+ ```
10
+
11
+ ## Commands
12
+
13
+ ### `sniper init`
14
+
15
+ Initialize SNIPER in the current project. Scaffolds the `.sniper/` config directory, installs framework files, and sets up Claude Code slash commands.
16
+
17
+ ```bash
18
+ sniper init
19
+ ```
20
+
21
+ ### `sniper status`
22
+
23
+ Show the current lifecycle phase, artifact state, and team status.
24
+
25
+ ```bash
26
+ sniper status
27
+ ```
28
+
29
+ ### `sniper update`
30
+
31
+ Update the scaffolded framework files to the latest version from `@sniper.ai/core`.
32
+
33
+ ```bash
34
+ sniper update
35
+ ```
36
+
37
+ ### `sniper add-pack <pack>`
38
+
39
+ Install a domain pack to inject project-specific context into agents.
40
+
41
+ ```bash
42
+ sniper add-pack @sniper.ai/pack-sales-dialer
43
+ ```
44
+
45
+ ### `sniper remove-pack <pack>`
46
+
47
+ Remove an installed domain pack.
48
+
49
+ ```bash
50
+ sniper remove-pack @sniper.ai/pack-sales-dialer
51
+ ```
52
+
53
+ ### `sniper list-packs`
54
+
55
+ List all installed domain packs.
56
+
57
+ ```bash
58
+ sniper list-packs
59
+ ```
60
+
61
+ ## How It Works
62
+
63
+ The CLI reads framework content from `@sniper.ai/core` and scaffolds it into your project's `.sniper/` directory. This gives Claude Code access to personas, team definitions, templates, and slash commands that drive the SNIPER lifecycle.
64
+
65
+ ## Tech Stack
66
+
67
+ - **Runtime:** Node.js >= 18
68
+ - **CLI framework:** [citty](https://github.com/unjs/citty)
69
+ - **Prompts:** [@clack/prompts](https://github.com/bombshell-dev/clack)
70
+ - **YAML parsing:** [yaml](https://github.com/eemeli/yaml)
71
+ - **Build:** [tsup](https://github.com/egoist/tsup)
72
+
73
+ ## Development
74
+
75
+ ```bash
76
+ # From the monorepo root
77
+ pnpm dev # Watch mode
78
+ pnpm build # Production build
79
+ ```
80
+
81
+ ## License
82
+
83
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,797 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { createRequire as createRequire2 } from "module";
5
+ import { defineCommand as defineCommand7, runMain } from "citty";
6
+
7
+ // src/commands/init.ts
8
+ import { defineCommand } from "citty";
9
+ import * as p from "@clack/prompts";
10
+
11
+ // src/config.ts
12
+ import { readFile, writeFile, access } from "fs/promises";
13
+ import { join, dirname } from "path";
14
+ import { createRequire } from "module";
15
+ import YAML from "yaml";
16
+ var CONFIG_PATH = ".sniper/config.yaml";
17
+ async function sniperConfigExists(cwd) {
18
+ try {
19
+ await access(join(cwd, CONFIG_PATH));
20
+ return true;
21
+ } catch {
22
+ return false;
23
+ }
24
+ }
25
+ function assertField(obj, section, field, type) {
26
+ const val = obj[field];
27
+ if (typeof val !== type) {
28
+ throw new Error(
29
+ `Invalid config.yaml: "${section}.${field}" must be a ${type}, got ${typeof val}`
30
+ );
31
+ }
32
+ }
33
+ function validateConfig(data) {
34
+ if (!data || typeof data !== "object") {
35
+ throw new Error("Invalid config.yaml: expected an object");
36
+ }
37
+ const cfg = data;
38
+ for (const key of [
39
+ "project",
40
+ "stack",
41
+ "state",
42
+ "review_gates",
43
+ "agent_teams"
44
+ ]) {
45
+ if (!cfg[key] || typeof cfg[key] !== "object") {
46
+ throw new Error(`Invalid config.yaml: missing "${key}" section`);
47
+ }
48
+ }
49
+ const project = cfg.project;
50
+ assertField(project, "project", "name", "string");
51
+ assertField(project, "project", "type", "string");
52
+ const stack = cfg.stack;
53
+ assertField(stack, "stack", "language", "string");
54
+ const agentTeams = cfg.agent_teams;
55
+ assertField(agentTeams, "agent_teams", "max_teammates", "number");
56
+ const state = cfg.state;
57
+ if (state.artifacts !== void 0 && typeof state.artifacts !== "object") {
58
+ throw new Error(
59
+ 'Invalid config.yaml: "state.artifacts" must be an object'
60
+ );
61
+ }
62
+ if (!Array.isArray(cfg.domain_packs)) {
63
+ cfg.domain_packs = [];
64
+ }
65
+ return data;
66
+ }
67
+ async function readConfig(cwd) {
68
+ const raw = await readFile(join(cwd, CONFIG_PATH), "utf-8");
69
+ return validateConfig(YAML.parse(raw));
70
+ }
71
+ async function writeConfig(cwd, config) {
72
+ const content = YAML.stringify(config, { lineWidth: 0 });
73
+ await writeFile(join(cwd, CONFIG_PATH), content, "utf-8");
74
+ }
75
+ function getCorePath() {
76
+ const require3 = createRequire(import.meta.url);
77
+ try {
78
+ const corePkgPath = require3.resolve("@sniper.ai/core/package.json");
79
+ return join(dirname(corePkgPath), "framework");
80
+ } catch {
81
+ throw new Error(
82
+ '@sniper.ai/core is not installed. Run "pnpm add -D @sniper.ai/core" first.'
83
+ );
84
+ }
85
+ }
86
+
87
+ // src/scaffolder.ts
88
+ import {
89
+ mkdir,
90
+ readdir,
91
+ readFile as readFile2,
92
+ writeFile as writeFile2,
93
+ access as access2,
94
+ cp
95
+ } from "fs/promises";
96
+ import { join as join2 } from "path";
97
+ import YAML2 from "yaml";
98
+ var FRAMEWORK_DIRS = [
99
+ "personas",
100
+ "teams",
101
+ "templates",
102
+ "checklists",
103
+ "workflows",
104
+ "spawn-prompts"
105
+ ];
106
+ async function ensureDir(dir) {
107
+ await mkdir(dir, { recursive: true });
108
+ }
109
+ async function fileExists(p7) {
110
+ try {
111
+ await access2(p7);
112
+ return true;
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+ async function scaffoldProject(cwd, config, options = {}) {
118
+ const corePath = getCorePath();
119
+ const sniperDir = join2(cwd, ".sniper");
120
+ const log7 = [];
121
+ const isUpdate = options.update === true;
122
+ await ensureDir(sniperDir);
123
+ for (const dir of FRAMEWORK_DIRS) {
124
+ const src = join2(corePath, dir);
125
+ const dest = join2(sniperDir, dir);
126
+ await cp(src, dest, { recursive: true, force: true });
127
+ log7.push(`Copied ${dir}/`);
128
+ }
129
+ await ensureDir(join2(sniperDir, "domain-packs"));
130
+ if (!isUpdate) {
131
+ const configContent = YAML2.stringify(config, { lineWidth: 0 });
132
+ await writeFile2(join2(sniperDir, "config.yaml"), configContent, "utf-8");
133
+ log7.push("Created config.yaml");
134
+ }
135
+ if (!isUpdate || !await fileExists(join2(cwd, "CLAUDE.md"))) {
136
+ const claudeTemplate = await readFile2(
137
+ join2(corePath, "claude-md.template"),
138
+ "utf-8"
139
+ );
140
+ await writeFile2(join2(cwd, "CLAUDE.md"), claudeTemplate, "utf-8");
141
+ log7.push("Created CLAUDE.md");
142
+ } else {
143
+ log7.push("Skipped CLAUDE.md (preserved user customizations)");
144
+ }
145
+ const settingsDir = join2(cwd, ".claude");
146
+ await ensureDir(settingsDir);
147
+ if (!isUpdate || !await fileExists(join2(settingsDir, "settings.json"))) {
148
+ const settingsTemplate = await readFile2(
149
+ join2(corePath, "settings.template.json"),
150
+ "utf-8"
151
+ );
152
+ await writeFile2(
153
+ join2(settingsDir, "settings.json"),
154
+ settingsTemplate,
155
+ "utf-8"
156
+ );
157
+ log7.push("Created .claude/settings.json");
158
+ } else {
159
+ log7.push("Skipped .claude/settings.json (preserved user customizations)");
160
+ }
161
+ const commandsSrc = join2(corePath, "commands");
162
+ const commandsDest = join2(settingsDir, "commands");
163
+ await cp(commandsSrc, commandsDest, { recursive: true, force: true });
164
+ log7.push("Copied skills to .claude/commands/");
165
+ if (!isUpdate) {
166
+ for (const sub of ["epics", "stories", "reviews"]) {
167
+ const dir = join2(cwd, "docs", sub);
168
+ await ensureDir(dir);
169
+ try {
170
+ const entries = await readdir(dir);
171
+ if (entries.length === 0) {
172
+ await writeFile2(join2(dir, ".gitkeep"), "", "utf-8");
173
+ }
174
+ } catch {
175
+ await writeFile2(join2(dir, ".gitkeep"), "", "utf-8");
176
+ }
177
+ }
178
+ log7.push("Created docs/ directory");
179
+ }
180
+ return log7;
181
+ }
182
+
183
+ // src/commands/init.ts
184
+ var initCommand = defineCommand({
185
+ meta: {
186
+ name: "init",
187
+ description: "Initialize a new SNIPER-enabled project"
188
+ },
189
+ run: async () => {
190
+ const cwd = process.cwd();
191
+ p.intro("SNIPER \u2014 Project Initialization");
192
+ if (await sniperConfigExists(cwd)) {
193
+ const overwrite = await p.confirm({
194
+ message: "SNIPER is already initialized in this directory. Reinitialize?",
195
+ initialValue: false
196
+ });
197
+ if (p.isCancel(overwrite) || !overwrite) {
198
+ p.cancel("Aborted.");
199
+ process.exit(0);
200
+ }
201
+ }
202
+ const projectName = await p.text({
203
+ message: "Project name:",
204
+ placeholder: "my-app",
205
+ validate: (v) => v.length === 0 ? "Project name is required" : void 0
206
+ });
207
+ if (p.isCancel(projectName)) {
208
+ p.cancel("Aborted.");
209
+ process.exit(0);
210
+ }
211
+ const projectType = await p.select({
212
+ message: "Project type:",
213
+ options: [
214
+ { value: "saas", label: "SaaS" },
215
+ { value: "api", label: "API" },
216
+ { value: "mobile", label: "Mobile" },
217
+ { value: "cli", label: "CLI" },
218
+ { value: "library", label: "Library" },
219
+ { value: "monorepo", label: "Monorepo" }
220
+ ]
221
+ });
222
+ if (p.isCancel(projectType)) {
223
+ p.cancel("Aborted.");
224
+ process.exit(0);
225
+ }
226
+ const description = await p.text({
227
+ message: "One-line project description:",
228
+ placeholder: "A brief description of your project"
229
+ });
230
+ if (p.isCancel(description)) {
231
+ p.cancel("Aborted.");
232
+ process.exit(0);
233
+ }
234
+ const language = await p.select({
235
+ message: "Primary language:",
236
+ options: [
237
+ { value: "typescript", label: "TypeScript" },
238
+ { value: "python", label: "Python" },
239
+ { value: "go", label: "Go" },
240
+ { value: "rust", label: "Rust" },
241
+ { value: "java", label: "Java" }
242
+ ]
243
+ });
244
+ if (p.isCancel(language)) {
245
+ p.cancel("Aborted.");
246
+ process.exit(0);
247
+ }
248
+ const frontend = await p.select({
249
+ message: "Frontend framework:",
250
+ options: [
251
+ { value: "react", label: "React" },
252
+ { value: "nextjs", label: "Next.js" },
253
+ { value: "vue", label: "Vue" },
254
+ { value: "svelte", label: "Svelte" },
255
+ { value: "none", label: "None" }
256
+ ]
257
+ });
258
+ if (p.isCancel(frontend)) {
259
+ p.cancel("Aborted.");
260
+ process.exit(0);
261
+ }
262
+ const backend = await p.select({
263
+ message: "Backend framework:",
264
+ options: [
265
+ { value: "node-express", label: "Node + Express" },
266
+ { value: "node-fastify", label: "Node + Fastify" },
267
+ { value: "django", label: "Django" },
268
+ { value: "fastapi", label: "FastAPI" },
269
+ { value: "gin", label: "Go Gin" },
270
+ { value: "none", label: "None" }
271
+ ]
272
+ });
273
+ if (p.isCancel(backend)) {
274
+ p.cancel("Aborted.");
275
+ process.exit(0);
276
+ }
277
+ const database = await p.select({
278
+ message: "Primary database:",
279
+ options: [
280
+ { value: "postgresql", label: "PostgreSQL" },
281
+ { value: "mysql", label: "MySQL" },
282
+ { value: "mongodb", label: "MongoDB" },
283
+ { value: "sqlite", label: "SQLite" },
284
+ { value: "none", label: "None" }
285
+ ]
286
+ });
287
+ if (p.isCancel(database)) {
288
+ p.cancel("Aborted.");
289
+ process.exit(0);
290
+ }
291
+ const infrastructure = await p.select({
292
+ message: "Cloud infrastructure:",
293
+ options: [
294
+ { value: "aws", label: "AWS" },
295
+ { value: "gcp", label: "Google Cloud" },
296
+ { value: "azure", label: "Azure" },
297
+ { value: "vercel", label: "Vercel" },
298
+ { value: "none", label: "None / Self-hosted" }
299
+ ]
300
+ });
301
+ if (p.isCancel(infrastructure)) {
302
+ p.cancel("Aborted.");
303
+ process.exit(0);
304
+ }
305
+ const maxTeammates = await p.text({
306
+ message: "Max concurrent agent teammates:",
307
+ placeholder: "5",
308
+ initialValue: "5",
309
+ validate: (v) => {
310
+ const n = parseInt(v, 10);
311
+ if (isNaN(n) || n < 1 || n > 10) return "Must be 1-10";
312
+ return void 0;
313
+ }
314
+ });
315
+ if (p.isCancel(maxTeammates)) {
316
+ p.cancel("Aborted.");
317
+ process.exit(0);
318
+ }
319
+ const config = {
320
+ project: {
321
+ name: projectName,
322
+ type: projectType,
323
+ description: description || ""
324
+ },
325
+ stack: {
326
+ language,
327
+ frontend: frontend === "none" ? null : frontend,
328
+ backend: backend === "none" ? null : backend,
329
+ database: database === "none" ? null : database,
330
+ cache: null,
331
+ infrastructure: infrastructure === "none" ? null : infrastructure,
332
+ test_runner: null,
333
+ package_manager: "pnpm"
334
+ },
335
+ review_gates: {
336
+ after_discover: "flexible",
337
+ after_plan: "strict",
338
+ after_solve: "flexible",
339
+ after_sprint: "strict"
340
+ },
341
+ agent_teams: {
342
+ max_teammates: parseInt(maxTeammates, 10),
343
+ default_model: "sonnet",
344
+ planning_model: "opus",
345
+ delegate_mode: true,
346
+ plan_approval: true,
347
+ coordination_timeout: 30
348
+ },
349
+ domain_packs: [],
350
+ ownership: {
351
+ backend: [
352
+ "src/backend/",
353
+ "src/api/",
354
+ "src/services/",
355
+ "src/db/",
356
+ "src/workers/"
357
+ ],
358
+ frontend: [
359
+ "src/frontend/",
360
+ "src/components/",
361
+ "src/hooks/",
362
+ "src/styles/",
363
+ "src/pages/"
364
+ ],
365
+ infrastructure: [
366
+ "docker/",
367
+ ".github/",
368
+ "infra/",
369
+ "terraform/",
370
+ "scripts/"
371
+ ],
372
+ tests: ["tests/", "__tests__/", "*.test.*", "*.spec.*"],
373
+ ai: ["src/ai/", "src/ml/", "src/pipeline/"],
374
+ docs: ["docs/"]
375
+ },
376
+ state: {
377
+ current_phase: null,
378
+ phase_history: [],
379
+ current_sprint: 0,
380
+ artifacts: {
381
+ brief: null,
382
+ prd: null,
383
+ architecture: null,
384
+ ux_spec: null,
385
+ security: null,
386
+ epics: null,
387
+ stories: null
388
+ }
389
+ }
390
+ };
391
+ const s = p.spinner();
392
+ s.start("Scaffolding SNIPER project...");
393
+ try {
394
+ const log7 = await scaffoldProject(cwd, config);
395
+ s.stop("Done!");
396
+ for (const entry of log7) {
397
+ p.log.success(entry);
398
+ }
399
+ p.outro(
400
+ 'SNIPER initialized. Run "sniper add-pack <name>" to add domain packs.'
401
+ );
402
+ } catch (err) {
403
+ s.stop("Failed!");
404
+ p.log.error(`Scaffolding failed: ${err}`);
405
+ process.exit(1);
406
+ }
407
+ }
408
+ });
409
+
410
+ // src/commands/status.ts
411
+ import { defineCommand as defineCommand2 } from "citty";
412
+ import * as p2 from "@clack/prompts";
413
+ var ARTIFACT_ICONS = {
414
+ approved: "\u2713",
415
+ draft: "\u25D0"
416
+ };
417
+ var statusCommand = defineCommand2({
418
+ meta: {
419
+ name: "status",
420
+ description: "Show SNIPER lifecycle status and artifact state"
421
+ },
422
+ run: async () => {
423
+ const cwd = process.cwd();
424
+ if (!await sniperConfigExists(cwd)) {
425
+ p2.log.error(
426
+ 'SNIPER is not initialized in this directory. Run "sniper init" first.'
427
+ );
428
+ process.exit(1);
429
+ }
430
+ const config = await readConfig(cwd);
431
+ p2.intro("SNIPER Status");
432
+ p2.log.info(
433
+ `Project: ${config.project.name || "(unnamed)"} (${config.project.type})`
434
+ );
435
+ p2.log.info(
436
+ `Phase: ${config.state.current_phase || "not started"}`
437
+ );
438
+ if (config.state.current_sprint > 0) {
439
+ p2.log.info(`Sprint: ${config.state.current_sprint}`);
440
+ }
441
+ p2.log.step("Artifacts:");
442
+ const artifacts = config.state.artifacts;
443
+ for (const [name, status] of Object.entries(artifacts)) {
444
+ const icon = status ? ARTIFACT_ICONS[status] || "?" : "\u25CB";
445
+ const label = status || "\u2014";
446
+ console.log(` ${icon} ${name.padEnd(16)} ${label}`);
447
+ }
448
+ if (config.domain_packs && config.domain_packs.length > 0) {
449
+ const packNames = config.domain_packs.map((pk) => pk.name).join(", ");
450
+ p2.log.info(`
451
+ Packs: ${packNames}`);
452
+ }
453
+ const stack = config.stack;
454
+ const stackParts = [
455
+ stack.language,
456
+ stack.frontend,
457
+ stack.backend,
458
+ stack.database,
459
+ stack.infrastructure
460
+ ].filter(Boolean);
461
+ p2.log.info(`Stack: ${stackParts.join(", ")}`);
462
+ p2.outro("");
463
+ }
464
+ });
465
+
466
+ // src/commands/add-pack.ts
467
+ import { defineCommand as defineCommand3 } from "citty";
468
+ import * as p3 from "@clack/prompts";
469
+
470
+ // src/pack-manager.ts
471
+ import {
472
+ cp as cp2,
473
+ rm,
474
+ readdir as readdir2,
475
+ readFile as readFile3,
476
+ stat,
477
+ access as access3,
478
+ mkdir as mkdir2
479
+ } from "fs/promises";
480
+ import { join as join3, resolve, sep } from "path";
481
+ import { execFileSync } from "child_process";
482
+ import YAML3 from "yaml";
483
+ function assertSafePath(base, untrusted) {
484
+ const full = resolve(base, untrusted);
485
+ const safeBase = resolve(base) + sep;
486
+ if (!full.startsWith(safeBase) && full !== resolve(base)) {
487
+ throw new Error(
488
+ `Invalid name: path traversal detected in "${untrusted}"`
489
+ );
490
+ }
491
+ return full;
492
+ }
493
+ async function pathExists(p7) {
494
+ try {
495
+ await access3(p7);
496
+ return true;
497
+ } catch {
498
+ return false;
499
+ }
500
+ }
501
+ async function readJson(p7) {
502
+ const raw = await readFile3(p7, "utf-8");
503
+ return JSON.parse(raw);
504
+ }
505
+ function getPackDir(pkgName, cwd) {
506
+ const nmPath = join3(cwd, "node_modules", ...pkgName.split("/"));
507
+ return nmPath;
508
+ }
509
+ async function installPack(packageName, cwd) {
510
+ execFileSync("pnpm", ["add", "-D", packageName], { cwd, stdio: "pipe" });
511
+ const pkgDir = getPackDir(packageName, cwd);
512
+ const pkgJson = await readJson(join3(pkgDir, "package.json"));
513
+ if (!pkgJson.sniper || pkgJson.sniper.type !== "domain-pack") {
514
+ execFileSync("pnpm", ["remove", packageName], { cwd, stdio: "pipe" });
515
+ throw new Error(
516
+ `${packageName} is not a valid SNIPER domain pack (missing sniper.type: "domain-pack")`
517
+ );
518
+ }
519
+ const shortName = packageName.replace(/^@[^/]+\/pack-/, "");
520
+ const domainPacksDir = join3(cwd, ".sniper", "domain-packs");
521
+ const packDest = assertSafePath(domainPacksDir, shortName);
522
+ const packSrc = assertSafePath(pkgDir, pkgJson.sniper.packDir);
523
+ await mkdir2(packDest, { recursive: true });
524
+ await cp2(packSrc, packDest, { recursive: true, force: true });
525
+ const contextDir = join3(packDest, "context");
526
+ let contextCount = 0;
527
+ if (await pathExists(contextDir)) {
528
+ const files = await readdir2(contextDir);
529
+ contextCount = files.filter((f) => f.endsWith(".md")).length;
530
+ }
531
+ const config = await readConfig(cwd);
532
+ if (!config.domain_packs) config.domain_packs = [];
533
+ if (!config.domain_packs.some((p7) => p7.name === shortName)) {
534
+ config.domain_packs.push({ name: shortName, package: packageName });
535
+ }
536
+ await writeConfig(cwd, config);
537
+ return {
538
+ name: shortName,
539
+ package: packageName,
540
+ version: pkgJson.version,
541
+ contextCount
542
+ };
543
+ }
544
+ async function removePack(packName, cwd) {
545
+ const config = await readConfig(cwd);
546
+ const packEntry = (config.domain_packs || []).find(
547
+ (p7) => p7.name === packName
548
+ );
549
+ const packageName = packEntry?.package || `@sniper.ai/pack-${packName}`;
550
+ const domainPacksDir = join3(cwd, ".sniper", "domain-packs");
551
+ const packDir = assertSafePath(domainPacksDir, packName);
552
+ if (await pathExists(packDir)) {
553
+ await rm(packDir, { recursive: true, force: true });
554
+ }
555
+ try {
556
+ execFileSync("pnpm", ["remove", packageName], { cwd, stdio: "pipe" });
557
+ } catch {
558
+ }
559
+ config.domain_packs = (config.domain_packs || []).filter(
560
+ (p7) => p7.name !== packName
561
+ );
562
+ await writeConfig(cwd, config);
563
+ }
564
+ async function listInstalledPacks(cwd) {
565
+ const packsDir = join3(cwd, ".sniper", "domain-packs");
566
+ if (!await pathExists(packsDir)) return [];
567
+ const entries = await readdir2(packsDir);
568
+ const packs = [];
569
+ for (const entry of entries) {
570
+ const entryPath = join3(packsDir, entry);
571
+ const s = await stat(entryPath);
572
+ if (!s.isDirectory()) continue;
573
+ const packYaml = join3(entryPath, "pack.yaml");
574
+ if (await pathExists(packYaml)) {
575
+ const raw = await readFile3(packYaml, "utf-8");
576
+ const parsed = YAML3.parse(raw);
577
+ packs.push({ name: entry, version: parsed.version || "unknown" });
578
+ } else {
579
+ packs.push({ name: entry, version: "unknown" });
580
+ }
581
+ }
582
+ return packs;
583
+ }
584
+ async function searchRegistryPacks() {
585
+ try {
586
+ const result = execFileSync(
587
+ "npm",
588
+ ["search", "@sniper.ai/pack-", "--json"],
589
+ { encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
590
+ ).toString();
591
+ const packages = JSON.parse(result);
592
+ return packages.map(
593
+ (pkg) => ({
594
+ name: pkg.name,
595
+ version: pkg.version,
596
+ description: pkg.description || ""
597
+ })
598
+ );
599
+ } catch {
600
+ return [];
601
+ }
602
+ }
603
+
604
+ // src/commands/add-pack.ts
605
+ var addPackCommand = defineCommand3({
606
+ meta: {
607
+ name: "add-pack",
608
+ description: "Add a domain pack to the current project"
609
+ },
610
+ args: {
611
+ name: {
612
+ type: "positional",
613
+ description: "Pack name or full npm package name (e.g., sales-dialer or @sniper.ai/pack-sales-dialer)",
614
+ required: true
615
+ }
616
+ },
617
+ run: async ({ args }) => {
618
+ const cwd = process.cwd();
619
+ if (!await sniperConfigExists(cwd)) {
620
+ p3.log.error(
621
+ 'SNIPER is not initialized in this directory. Run "sniper init" first.'
622
+ );
623
+ process.exit(1);
624
+ }
625
+ let packageName = args.name;
626
+ if (!packageName.startsWith("@") && !packageName.includes("/")) {
627
+ packageName = `@sniper.ai/pack-${packageName}`;
628
+ }
629
+ const s = p3.spinner();
630
+ s.start(`Installing ${packageName}...`);
631
+ try {
632
+ const result = await installPack(packageName, cwd);
633
+ s.stop("Done!");
634
+ p3.log.success(`Installed ${result.package}@${result.version}`);
635
+ p3.log.success(
636
+ `Copied pack to .sniper/domain-packs/${result.name}/`
637
+ );
638
+ p3.log.success("Updated config.yaml with pack reference");
639
+ p3.log.info(
640
+ `
641
+ Pack "${result.name}" added. ${result.contextCount} context files available.`
642
+ );
643
+ } catch (err) {
644
+ s.stop("Failed!");
645
+ p3.log.error(`${err}`);
646
+ process.exit(1);
647
+ }
648
+ }
649
+ });
650
+
651
+ // src/commands/remove-pack.ts
652
+ import { defineCommand as defineCommand4 } from "citty";
653
+ import * as p4 from "@clack/prompts";
654
+ var removePackCommand = defineCommand4({
655
+ meta: {
656
+ name: "remove-pack",
657
+ description: "Remove a domain pack from the current project"
658
+ },
659
+ args: {
660
+ name: {
661
+ type: "positional",
662
+ description: "Pack name to remove (e.g., sales-dialer)",
663
+ required: true
664
+ }
665
+ },
666
+ run: async ({ args }) => {
667
+ const cwd = process.cwd();
668
+ if (!await sniperConfigExists(cwd)) {
669
+ p4.log.error(
670
+ 'SNIPER is not initialized in this directory. Run "sniper init" first.'
671
+ );
672
+ process.exit(1);
673
+ }
674
+ const confirm4 = await p4.confirm({
675
+ message: `Remove pack "${args.name}" and all its files?`
676
+ });
677
+ if (p4.isCancel(confirm4) || !confirm4) {
678
+ p4.cancel("Aborted.");
679
+ process.exit(0);
680
+ }
681
+ const s = p4.spinner();
682
+ s.start(`Removing ${args.name}...`);
683
+ try {
684
+ await removePack(args.name, cwd);
685
+ s.stop("Done!");
686
+ p4.log.success(`Removed pack "${args.name}"`);
687
+ p4.log.success("Updated config.yaml");
688
+ } catch (err) {
689
+ s.stop("Failed!");
690
+ p4.log.error(`${err}`);
691
+ process.exit(1);
692
+ }
693
+ }
694
+ });
695
+
696
+ // src/commands/list-packs.ts
697
+ import { defineCommand as defineCommand5 } from "citty";
698
+ import * as p5 from "@clack/prompts";
699
+ var listPacksCommand = defineCommand5({
700
+ meta: {
701
+ name: "list-packs",
702
+ description: "List available and installed domain packs"
703
+ },
704
+ run: async () => {
705
+ const cwd = process.cwd();
706
+ p5.intro("SNIPER Domain Packs");
707
+ const s = p5.spinner();
708
+ s.start("Searching npm registry for @sniper.ai/pack-*...");
709
+ const available = await searchRegistryPacks();
710
+ s.stop(
711
+ available.length > 0 ? `Found ${available.length} pack(s) on npm` : "No packs found on npm registry (packages may not be published yet)"
712
+ );
713
+ if (available.length > 0) {
714
+ p5.log.step("Available packs:");
715
+ for (const pkg of available) {
716
+ console.log(
717
+ ` ${pkg.name.padEnd(40)} v${pkg.version.padEnd(10)} ${pkg.description}`
718
+ );
719
+ }
720
+ }
721
+ if (await sniperConfigExists(cwd)) {
722
+ const installed = await listInstalledPacks(cwd);
723
+ if (installed.length > 0) {
724
+ p5.log.step("\nInstalled:");
725
+ for (const pack of installed) {
726
+ console.log(` ${pack.name.padEnd(20)} v${pack.version}`);
727
+ }
728
+ } else {
729
+ p5.log.info("\nNo packs installed.");
730
+ }
731
+ }
732
+ p5.outro("");
733
+ }
734
+ });
735
+
736
+ // src/commands/update.ts
737
+ import { defineCommand as defineCommand6 } from "citty";
738
+ import * as p6 from "@clack/prompts";
739
+ var updateCommand = defineCommand6({
740
+ meta: {
741
+ name: "update",
742
+ description: "Update SNIPER core and installed packs"
743
+ },
744
+ run: async () => {
745
+ const cwd = process.cwd();
746
+ if (!await sniperConfigExists(cwd)) {
747
+ p6.log.error(
748
+ 'SNIPER is not initialized in this directory. Run "sniper init" first.'
749
+ );
750
+ process.exit(1);
751
+ }
752
+ p6.intro("SNIPER Update");
753
+ const currentConfig = await readConfig(cwd);
754
+ const confirm4 = await p6.confirm({
755
+ message: "This will update framework files (personas, teams, templates, etc.) while preserving your config.yaml customizations. Continue?"
756
+ });
757
+ if (p6.isCancel(confirm4) || !confirm4) {
758
+ p6.cancel("Aborted.");
759
+ process.exit(0);
760
+ }
761
+ const s = p6.spinner();
762
+ s.start("Updating framework files...");
763
+ try {
764
+ const log7 = await scaffoldProject(cwd, currentConfig, { update: true });
765
+ s.stop("Done!");
766
+ for (const entry of log7) {
767
+ p6.log.success(entry);
768
+ }
769
+ p6.outro("SNIPER updated successfully.");
770
+ } catch (err) {
771
+ s.stop("Failed!");
772
+ p6.log.error(`Update failed: ${err}`);
773
+ process.exit(1);
774
+ }
775
+ }
776
+ });
777
+
778
+ // src/index.ts
779
+ var require2 = createRequire2(import.meta.url);
780
+ var { version } = require2("../package.json");
781
+ var main = defineCommand7({
782
+ meta: {
783
+ name: "sniper",
784
+ version,
785
+ description: "SNIPER \u2014 Spawn, Navigate, Implement, Parallelize, Evaluate, Release"
786
+ },
787
+ subCommands: {
788
+ init: initCommand,
789
+ status: statusCommand,
790
+ "add-pack": addPackCommand,
791
+ "remove-pack": removePackCommand,
792
+ "list-packs": listPacksCommand,
793
+ update: updateCommand
794
+ }
795
+ });
796
+ runMain(main);
797
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/config.ts","../src/scaffolder.ts","../src/commands/status.ts","../src/commands/add-pack.ts","../src/pack-manager.ts","../src/commands/remove-pack.ts","../src/commands/list-packs.ts","../src/commands/update.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { defineCommand, runMain } from \"citty\";\nimport { initCommand } from \"./commands/init.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { addPackCommand } from \"./commands/add-pack.js\";\nimport { removePackCommand } from \"./commands/remove-pack.js\";\nimport { listPacksCommand } from \"./commands/list-packs.js\";\nimport { updateCommand } from \"./commands/update.js\";\n\nconst require = createRequire(import.meta.url);\nconst { version } = require(\"../package.json\");\n\nconst main = defineCommand({\n meta: {\n name: \"sniper\",\n version,\n description: \"SNIPER — Spawn, Navigate, Implement, Parallelize, Evaluate, Release\",\n },\n subCommands: {\n init: initCommand,\n status: statusCommand,\n \"add-pack\": addPackCommand,\n \"remove-pack\": removePackCommand,\n \"list-packs\": listPacksCommand,\n update: updateCommand,\n },\n});\n\nrunMain(main);\n","import { defineCommand } from \"citty\";\nimport * as p from \"@clack/prompts\";\nimport { sniperConfigExists, type SniperConfig } from \"../config.js\";\nimport { scaffoldProject } from \"../scaffolder.js\";\n\nexport const initCommand = defineCommand({\n meta: {\n name: \"init\",\n description: \"Initialize a new SNIPER-enabled project\",\n },\n run: async () => {\n const cwd = process.cwd();\n\n p.intro(\"SNIPER — Project Initialization\");\n\n // Check if already initialized\n if (await sniperConfigExists(cwd)) {\n const overwrite = await p.confirm({\n message:\n \"SNIPER is already initialized in this directory. Reinitialize?\",\n initialValue: false,\n });\n if (p.isCancel(overwrite) || !overwrite) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n }\n\n const projectName = await p.text({\n message: \"Project name:\",\n placeholder: \"my-app\",\n validate: (v) => (v.length === 0 ? \"Project name is required\" : undefined),\n });\n if (p.isCancel(projectName)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const projectType = await p.select({\n message: \"Project type:\",\n options: [\n { value: \"saas\", label: \"SaaS\" },\n { value: \"api\", label: \"API\" },\n { value: \"mobile\", label: \"Mobile\" },\n { value: \"cli\", label: \"CLI\" },\n { value: \"library\", label: \"Library\" },\n { value: \"monorepo\", label: \"Monorepo\" },\n ],\n });\n if (p.isCancel(projectType)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const description = await p.text({\n message: \"One-line project description:\",\n placeholder: \"A brief description of your project\",\n });\n if (p.isCancel(description)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const language = await p.select({\n message: \"Primary language:\",\n options: [\n { value: \"typescript\", label: \"TypeScript\" },\n { value: \"python\", label: \"Python\" },\n { value: \"go\", label: \"Go\" },\n { value: \"rust\", label: \"Rust\" },\n { value: \"java\", label: \"Java\" },\n ],\n });\n if (p.isCancel(language)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const frontend = await p.select({\n message: \"Frontend framework:\",\n options: [\n { value: \"react\", label: \"React\" },\n { value: \"nextjs\", label: \"Next.js\" },\n { value: \"vue\", label: \"Vue\" },\n { value: \"svelte\", label: \"Svelte\" },\n { value: \"none\", label: \"None\" },\n ],\n });\n if (p.isCancel(frontend)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const backend = await p.select({\n message: \"Backend framework:\",\n options: [\n { value: \"node-express\", label: \"Node + Express\" },\n { value: \"node-fastify\", label: \"Node + Fastify\" },\n { value: \"django\", label: \"Django\" },\n { value: \"fastapi\", label: \"FastAPI\" },\n { value: \"gin\", label: \"Go Gin\" },\n { value: \"none\", label: \"None\" },\n ],\n });\n if (p.isCancel(backend)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const database = await p.select({\n message: \"Primary database:\",\n options: [\n { value: \"postgresql\", label: \"PostgreSQL\" },\n { value: \"mysql\", label: \"MySQL\" },\n { value: \"mongodb\", label: \"MongoDB\" },\n { value: \"sqlite\", label: \"SQLite\" },\n { value: \"none\", label: \"None\" },\n ],\n });\n if (p.isCancel(database)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const infrastructure = await p.select({\n message: \"Cloud infrastructure:\",\n options: [\n { value: \"aws\", label: \"AWS\" },\n { value: \"gcp\", label: \"Google Cloud\" },\n { value: \"azure\", label: \"Azure\" },\n { value: \"vercel\", label: \"Vercel\" },\n { value: \"none\", label: \"None / Self-hosted\" },\n ],\n });\n if (p.isCancel(infrastructure)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const maxTeammates = await p.text({\n message: \"Max concurrent agent teammates:\",\n placeholder: \"5\",\n initialValue: \"5\",\n validate: (v) => {\n const n = parseInt(v, 10);\n if (isNaN(n) || n < 1 || n > 10) return \"Must be 1-10\";\n return undefined;\n },\n });\n if (p.isCancel(maxTeammates)) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n // Build config\n const config: SniperConfig = {\n project: {\n name: projectName as string,\n type: projectType as string,\n description: (description as string) || \"\",\n },\n stack: {\n language: language as string,\n frontend: frontend === \"none\" ? null : (frontend as string),\n backend: backend === \"none\" ? null : (backend as string),\n database: database === \"none\" ? null : (database as string),\n cache: null,\n infrastructure:\n infrastructure === \"none\" ? null : (infrastructure as string),\n test_runner: null,\n package_manager: \"pnpm\",\n },\n review_gates: {\n after_discover: \"flexible\",\n after_plan: \"strict\",\n after_solve: \"flexible\",\n after_sprint: \"strict\",\n },\n agent_teams: {\n max_teammates: parseInt(maxTeammates as string, 10),\n default_model: \"sonnet\",\n planning_model: \"opus\",\n delegate_mode: true,\n plan_approval: true,\n coordination_timeout: 30,\n },\n domain_packs: [],\n ownership: {\n backend: [\n \"src/backend/\",\n \"src/api/\",\n \"src/services/\",\n \"src/db/\",\n \"src/workers/\",\n ],\n frontend: [\n \"src/frontend/\",\n \"src/components/\",\n \"src/hooks/\",\n \"src/styles/\",\n \"src/pages/\",\n ],\n infrastructure: [\n \"docker/\",\n \".github/\",\n \"infra/\",\n \"terraform/\",\n \"scripts/\",\n ],\n tests: [\"tests/\", \"__tests__/\", \"*.test.*\", \"*.spec.*\"],\n ai: [\"src/ai/\", \"src/ml/\", \"src/pipeline/\"],\n docs: [\"docs/\"],\n },\n state: {\n current_phase: null,\n phase_history: [],\n current_sprint: 0,\n artifacts: {\n brief: null,\n prd: null,\n architecture: null,\n ux_spec: null,\n security: null,\n epics: null,\n stories: null,\n },\n },\n };\n\n // Scaffold\n const s = p.spinner();\n s.start(\"Scaffolding SNIPER project...\");\n\n try {\n const log = await scaffoldProject(cwd, config);\n s.stop(\"Done!\");\n\n for (const entry of log) {\n p.log.success(entry);\n }\n\n p.outro(\n 'SNIPER initialized. Run \"sniper add-pack <name>\" to add domain packs.',\n );\n } catch (err) {\n s.stop(\"Failed!\");\n p.log.error(`Scaffolding failed: ${err}`);\n process.exit(1);\n }\n },\n});\n","import { readFile, writeFile, access } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport YAML from \"yaml\";\n\nexport interface SniperConfig {\n project: {\n name: string;\n type: string;\n description: string;\n };\n stack: {\n language: string;\n frontend: string | null;\n backend: string | null;\n database: string | null;\n cache: string | null;\n infrastructure: string | null;\n test_runner: string | null;\n package_manager: string;\n };\n review_gates: {\n after_discover: string;\n after_plan: string;\n after_solve: string;\n after_sprint: string;\n };\n agent_teams: {\n max_teammates: number;\n default_model: string;\n planning_model: string;\n delegate_mode: boolean;\n plan_approval: boolean;\n coordination_timeout: number;\n };\n domain_packs: Array<{ name: string; package: string }>;\n ownership: Record<string, string[]>;\n state: {\n current_phase: string | null;\n phase_history: Array<{\n phase: string;\n started_at: string;\n completed_at?: string;\n approved_by?: string;\n }>;\n current_sprint: number;\n artifacts: Record<string, string | null>;\n };\n}\n\nconst CONFIG_PATH = \".sniper/config.yaml\";\n\nexport async function sniperConfigExists(cwd: string): Promise<boolean> {\n try {\n await access(join(cwd, CONFIG_PATH));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction assertField(\n obj: Record<string, unknown>,\n section: string,\n field: string,\n type: string,\n): void {\n const val = obj[field];\n if (typeof val !== type) {\n throw new Error(\n `Invalid config.yaml: \"${section}.${field}\" must be a ${type}, got ${typeof val}`,\n );\n }\n}\n\nfunction validateConfig(data: unknown): SniperConfig {\n if (!data || typeof data !== \"object\") {\n throw new Error(\"Invalid config.yaml: expected an object\");\n }\n const cfg = data as Record<string, unknown>;\n for (const key of [\n \"project\",\n \"stack\",\n \"state\",\n \"review_gates\",\n \"agent_teams\",\n ]) {\n if (!cfg[key] || typeof cfg[key] !== \"object\") {\n throw new Error(`Invalid config.yaml: missing \"${key}\" section`);\n }\n }\n\n // Validate nested fields\n const project = cfg.project as Record<string, unknown>;\n assertField(project, \"project\", \"name\", \"string\");\n assertField(project, \"project\", \"type\", \"string\");\n\n const stack = cfg.stack as Record<string, unknown>;\n assertField(stack, \"stack\", \"language\", \"string\");\n\n const agentTeams = cfg.agent_teams as Record<string, unknown>;\n assertField(agentTeams, \"agent_teams\", \"max_teammates\", \"number\");\n\n const state = cfg.state as Record<string, unknown>;\n if (state.artifacts !== undefined && typeof state.artifacts !== \"object\") {\n throw new Error(\n 'Invalid config.yaml: \"state.artifacts\" must be an object',\n );\n }\n\n // Normalize: ensure domain_packs is always an array\n if (!Array.isArray(cfg.domain_packs)) {\n cfg.domain_packs = [];\n }\n return data as SniperConfig;\n}\n\nexport async function readConfig(cwd: string): Promise<SniperConfig> {\n const raw = await readFile(join(cwd, CONFIG_PATH), \"utf-8\");\n return validateConfig(YAML.parse(raw));\n}\n\nexport async function writeConfig(\n cwd: string,\n config: SniperConfig,\n): Promise<void> {\n const content = YAML.stringify(config, { lineWidth: 0 });\n await writeFile(join(cwd, CONFIG_PATH), content, \"utf-8\");\n}\n\nexport function getCorePath(): string {\n // Resolve the path to @sniper.ai/core's framework directory\n // Works in both monorepo (workspace link) and published (node_modules)\n const require = createRequire(import.meta.url);\n try {\n const corePkgPath = require.resolve(\"@sniper.ai/core/package.json\");\n return join(dirname(corePkgPath), \"framework\");\n } catch {\n throw new Error(\n '@sniper.ai/core is not installed. Run \"pnpm add -D @sniper.ai/core\" first.',\n );\n }\n}\n","import {\n mkdir,\n readdir,\n readFile,\n writeFile,\n access,\n cp,\n} from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport YAML from \"yaml\";\nimport { getCorePath } from \"./config.js\";\nimport type { SniperConfig } from \"./config.js\";\n\nconst FRAMEWORK_DIRS = [\n \"personas\",\n \"teams\",\n \"templates\",\n \"checklists\",\n \"workflows\",\n \"spawn-prompts\",\n];\n\nasync function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nexport interface ScaffoldOptions {\n /** When true, skips overwriting user-customizable files (CLAUDE.md, settings.json, commands) */\n update?: boolean;\n}\n\nexport async function scaffoldProject(\n cwd: string,\n config: SniperConfig,\n options: ScaffoldOptions = {},\n): Promise<string[]> {\n const corePath = getCorePath();\n const sniperDir = join(cwd, \".sniper\");\n const log: string[] = [];\n const isUpdate = options.update === true;\n\n // Create .sniper/ directory\n await ensureDir(sniperDir);\n\n // Copy framework directories\n for (const dir of FRAMEWORK_DIRS) {\n const src = join(corePath, dir);\n const dest = join(sniperDir, dir);\n await cp(src, dest, { recursive: true, force: true });\n log.push(`Copied ${dir}/`);\n }\n\n // Create domain-packs directory\n await ensureDir(join(sniperDir, \"domain-packs\"));\n\n // Generate config.yaml (skipped during update — caller preserves config separately)\n if (!isUpdate) {\n const configContent = YAML.stringify(config, { lineWidth: 0 });\n await writeFile(join(sniperDir, \"config.yaml\"), configContent, \"utf-8\");\n log.push(\"Created config.yaml\");\n }\n\n // Generate CLAUDE.md from template (skip during update if user has customized it)\n if (!isUpdate || !(await fileExists(join(cwd, \"CLAUDE.md\")))) {\n const claudeTemplate = await readFile(\n join(corePath, \"claude-md.template\"),\n \"utf-8\",\n );\n await writeFile(join(cwd, \"CLAUDE.md\"), claudeTemplate, \"utf-8\");\n log.push(\"Created CLAUDE.md\");\n } else {\n log.push(\"Skipped CLAUDE.md (preserved user customizations)\");\n }\n\n // Generate .claude/settings.json from template (skip during update if exists)\n const settingsDir = join(cwd, \".claude\");\n await ensureDir(settingsDir);\n if (!isUpdate || !(await fileExists(join(settingsDir, \"settings.json\")))) {\n const settingsTemplate = await readFile(\n join(corePath, \"settings.template.json\"),\n \"utf-8\",\n );\n await writeFile(\n join(settingsDir, \"settings.json\"),\n settingsTemplate,\n \"utf-8\",\n );\n log.push(\"Created .claude/settings.json\");\n } else {\n log.push(\"Skipped .claude/settings.json (preserved user customizations)\");\n }\n\n // Copy skills/commands into .claude/commands/\n const commandsSrc = join(corePath, \"commands\");\n const commandsDest = join(settingsDir, \"commands\");\n await cp(commandsSrc, commandsDest, { recursive: true, force: true });\n log.push(\"Copied skills to .claude/commands/\");\n\n // Create docs/ directory structure\n if (!isUpdate) {\n for (const sub of [\"epics\", \"stories\", \"reviews\"]) {\n const dir = join(cwd, \"docs\", sub);\n await ensureDir(dir);\n // Only write .gitkeep if directory is empty\n try {\n const entries = await readdir(dir);\n if (entries.length === 0) {\n await writeFile(join(dir, \".gitkeep\"), \"\", \"utf-8\");\n }\n } catch {\n await writeFile(join(dir, \".gitkeep\"), \"\", \"utf-8\");\n }\n }\n log.push(\"Created docs/ directory\");\n }\n\n return log;\n}\n","import { defineCommand } from \"citty\";\nimport * as p from \"@clack/prompts\";\nimport { readConfig, sniperConfigExists } from \"../config.js\";\n\nconst ARTIFACT_ICONS: Record<string, string> = {\n approved: \"\\u2713\",\n draft: \"\\u25D0\",\n};\n\nexport const statusCommand = defineCommand({\n meta: {\n name: \"status\",\n description: \"Show SNIPER lifecycle status and artifact state\",\n },\n run: async () => {\n const cwd = process.cwd();\n\n if (!(await sniperConfigExists(cwd))) {\n p.log.error(\n 'SNIPER is not initialized in this directory. Run \"sniper init\" first.',\n );\n process.exit(1);\n }\n\n const config = await readConfig(cwd);\n\n p.intro(\"SNIPER Status\");\n\n // Project info\n p.log.info(\n `Project: ${config.project.name || \"(unnamed)\"} (${config.project.type})`,\n );\n p.log.info(\n `Phase: ${config.state.current_phase || \"not started\"}`,\n );\n if (config.state.current_sprint > 0) {\n p.log.info(`Sprint: ${config.state.current_sprint}`);\n }\n\n // Artifacts\n p.log.step(\"Artifacts:\");\n const artifacts = config.state.artifacts;\n for (const [name, status] of Object.entries(artifacts)) {\n const icon = status ? (ARTIFACT_ICONS[status] || \"?\") : \"\\u25CB\";\n const label = status || \"\\u2014\";\n console.log(` ${icon} ${name.padEnd(16)} ${label}`);\n }\n\n // Domain packs\n if (config.domain_packs && config.domain_packs.length > 0) {\n const packNames = config.domain_packs.map((pk) => pk.name).join(\", \");\n p.log.info(`\\nPacks: ${packNames}`);\n }\n\n // Stack summary\n const stack = config.stack;\n const stackParts = [\n stack.language,\n stack.frontend,\n stack.backend,\n stack.database,\n stack.infrastructure,\n ].filter(Boolean);\n p.log.info(`Stack: ${stackParts.join(\", \")}`);\n\n p.outro(\"\");\n },\n});\n","import { defineCommand } from \"citty\";\nimport * as p from \"@clack/prompts\";\nimport { sniperConfigExists } from \"../config.js\";\nimport { installPack } from \"../pack-manager.js\";\n\nexport const addPackCommand = defineCommand({\n meta: {\n name: \"add-pack\",\n description: \"Add a domain pack to the current project\",\n },\n args: {\n name: {\n type: \"positional\",\n description:\n \"Pack name or full npm package name (e.g., sales-dialer or @sniper.ai/pack-sales-dialer)\",\n required: true,\n },\n },\n run: async ({ args }) => {\n const cwd = process.cwd();\n\n if (!(await sniperConfigExists(cwd))) {\n p.log.error(\n 'SNIPER is not initialized in this directory. Run \"sniper init\" first.',\n );\n process.exit(1);\n }\n\n // Resolve package name\n let packageName = args.name;\n if (!packageName.startsWith(\"@\") && !packageName.includes(\"/\")) {\n packageName = `@sniper.ai/pack-${packageName}`;\n }\n\n const s = p.spinner();\n s.start(`Installing ${packageName}...`);\n\n try {\n const result = await installPack(packageName, cwd);\n s.stop(\"Done!\");\n\n p.log.success(`Installed ${result.package}@${result.version}`);\n p.log.success(\n `Copied pack to .sniper/domain-packs/${result.name}/`,\n );\n p.log.success(\"Updated config.yaml with pack reference\");\n p.log.info(\n `\\nPack \"${result.name}\" added. ${result.contextCount} context files available.`,\n );\n } catch (err) {\n s.stop(\"Failed!\");\n p.log.error(`${err}`);\n process.exit(1);\n }\n },\n});\n","import {\n cp,\n rm,\n readdir,\n readFile,\n stat,\n access,\n mkdir,\n} from \"node:fs/promises\";\nimport { join, resolve, sep } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\n\n/**\n * Validates that a resolved path stays within the expected base directory.\n * Prevents path traversal attacks via user-supplied names containing \"..\".\n */\nfunction assertSafePath(base: string, untrusted: string): string {\n const full = resolve(base, untrusted);\n const safeBase = resolve(base) + sep;\n if (!full.startsWith(safeBase) && full !== resolve(base)) {\n throw new Error(\n `Invalid name: path traversal detected in \"${untrusted}\"`,\n );\n }\n return full;\n}\nimport YAML from \"yaml\";\nimport { readConfig, writeConfig } from \"./config.js\";\n\ninterface PackMetadata {\n name: string;\n version: string;\n sniper?: {\n type: string;\n packDir: string;\n };\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readJson<T>(p: string): Promise<T> {\n const raw = await readFile(p, \"utf-8\");\n return JSON.parse(raw) as T;\n}\n\nfunction getPackDir(pkgName: string, cwd: string): string {\n const nmPath = join(cwd, \"node_modules\", ...pkgName.split(\"/\"));\n return nmPath;\n}\n\nexport async function installPack(\n packageName: string,\n cwd: string,\n): Promise<{\n name: string;\n package: string;\n version: string;\n contextCount: number;\n}> {\n // Install the npm package\n execFileSync(\"pnpm\", [\"add\", \"-D\", packageName], { cwd, stdio: \"pipe\" });\n\n // Read the installed package.json\n const pkgDir = getPackDir(packageName, cwd);\n const pkgJson = await readJson<PackMetadata>(join(pkgDir, \"package.json\"));\n\n if (!pkgJson.sniper || pkgJson.sniper.type !== \"domain-pack\") {\n execFileSync(\"pnpm\", [\"remove\", packageName], { cwd, stdio: \"pipe\" });\n throw new Error(\n `${packageName} is not a valid SNIPER domain pack (missing sniper.type: \"domain-pack\")`,\n );\n }\n\n // Determine pack name from npm package name\n const shortName = packageName.replace(/^@[^/]+\\/pack-/, \"\");\n const domainPacksDir = join(cwd, \".sniper\", \"domain-packs\");\n const packDest = assertSafePath(domainPacksDir, shortName);\n const packSrc = assertSafePath(pkgDir, pkgJson.sniper.packDir);\n\n await mkdir(packDest, { recursive: true });\n await cp(packSrc, packDest, { recursive: true, force: true });\n\n // Count context files\n const contextDir = join(packDest, \"context\");\n let contextCount = 0;\n if (await pathExists(contextDir)) {\n const files = await readdir(contextDir);\n contextCount = files.filter((f) => f.endsWith(\".md\")).length;\n }\n\n // Update config.yaml — append to domain_packs (avoid duplicates)\n const config = await readConfig(cwd);\n if (!config.domain_packs) config.domain_packs = [];\n if (!config.domain_packs.some((p) => p.name === shortName)) {\n config.domain_packs.push({ name: shortName, package: packageName });\n }\n await writeConfig(cwd, config);\n\n return {\n name: shortName,\n package: packageName,\n version: pkgJson.version,\n contextCount,\n };\n}\n\nexport async function removePack(\n packName: string,\n cwd: string,\n): Promise<void> {\n // Read config to find the full package name for this pack\n const config = await readConfig(cwd);\n const packEntry = (config.domain_packs || []).find(\n (p) => p.name === packName,\n );\n const packageName = packEntry?.package || `@sniper.ai/pack-${packName}`;\n\n const domainPacksDir = join(cwd, \".sniper\", \"domain-packs\");\n const packDir = assertSafePath(domainPacksDir, packName);\n if (await pathExists(packDir)) {\n await rm(packDir, { recursive: true, force: true });\n }\n\n try {\n execFileSync(\"pnpm\", [\"remove\", packageName], { cwd, stdio: \"pipe\" });\n } catch {\n // Package may not be installed via npm\n }\n\n config.domain_packs = (config.domain_packs || []).filter(\n (p) => p.name !== packName,\n );\n await writeConfig(cwd, config);\n}\n\nexport async function listInstalledPacks(\n cwd: string,\n): Promise<Array<{ name: string; version: string }>> {\n const packsDir = join(cwd, \".sniper\", \"domain-packs\");\n if (!(await pathExists(packsDir))) return [];\n\n const entries = await readdir(packsDir);\n const packs: Array<{ name: string; version: string }> = [];\n\n for (const entry of entries) {\n const entryPath = join(packsDir, entry);\n const s = await stat(entryPath);\n if (!s.isDirectory()) continue;\n\n const packYaml = join(entryPath, \"pack.yaml\");\n if (await pathExists(packYaml)) {\n const raw = await readFile(packYaml, \"utf-8\");\n const parsed = YAML.parse(raw);\n packs.push({ name: entry, version: parsed.version || \"unknown\" });\n } else {\n packs.push({ name: entry, version: \"unknown\" });\n }\n }\n return packs;\n}\n\nexport async function searchRegistryPacks(): Promise<\n Array<{ name: string; version: string; description: string }>\n> {\n try {\n const result = execFileSync(\n \"npm\",\n [\"search\", \"@sniper.ai/pack-\", \"--json\"],\n { encoding: \"utf-8\", timeout: 10000, stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n ).toString();\n const packages = JSON.parse(result);\n return packages.map(\n (pkg: { name: string; version: string; description: string }) => ({\n name: pkg.name,\n version: pkg.version,\n description: pkg.description || \"\",\n }),\n );\n } catch {\n return [];\n }\n}\n","import { defineCommand } from \"citty\";\nimport * as p from \"@clack/prompts\";\nimport { sniperConfigExists } from \"../config.js\";\nimport { removePack } from \"../pack-manager.js\";\n\nexport const removePackCommand = defineCommand({\n meta: {\n name: \"remove-pack\",\n description: \"Remove a domain pack from the current project\",\n },\n args: {\n name: {\n type: \"positional\",\n description: \"Pack name to remove (e.g., sales-dialer)\",\n required: true,\n },\n },\n run: async ({ args }) => {\n const cwd = process.cwd();\n\n if (!(await sniperConfigExists(cwd))) {\n p.log.error(\n 'SNIPER is not initialized in this directory. Run \"sniper init\" first.',\n );\n process.exit(1);\n }\n\n const confirm = await p.confirm({\n message: `Remove pack \"${args.name}\" and all its files?`,\n });\n if (p.isCancel(confirm) || !confirm) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const s = p.spinner();\n s.start(`Removing ${args.name}...`);\n\n try {\n await removePack(args.name, cwd);\n s.stop(\"Done!\");\n p.log.success(`Removed pack \"${args.name}\"`);\n p.log.success(\"Updated config.yaml\");\n } catch (err) {\n s.stop(\"Failed!\");\n p.log.error(`${err}`);\n process.exit(1);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport * as p from \"@clack/prompts\";\nimport { sniperConfigExists } from \"../config.js\";\nimport { listInstalledPacks, searchRegistryPacks } from \"../pack-manager.js\";\n\nexport const listPacksCommand = defineCommand({\n meta: {\n name: \"list-packs\",\n description: \"List available and installed domain packs\",\n },\n run: async () => {\n const cwd = process.cwd();\n\n p.intro(\"SNIPER Domain Packs\");\n\n // Show available packs from registry\n const s = p.spinner();\n s.start(\"Searching npm registry for @sniper.ai/pack-*...\");\n const available = await searchRegistryPacks();\n s.stop(\n available.length > 0\n ? `Found ${available.length} pack(s) on npm`\n : \"No packs found on npm registry (packages may not be published yet)\",\n );\n\n if (available.length > 0) {\n p.log.step(\"Available packs:\");\n for (const pkg of available) {\n console.log(\n ` ${pkg.name.padEnd(40)} v${pkg.version.padEnd(10)} ${pkg.description}`,\n );\n }\n }\n\n // Show installed packs (if SNIPER is initialized)\n if (await sniperConfigExists(cwd)) {\n const installed = await listInstalledPacks(cwd);\n if (installed.length > 0) {\n p.log.step(\"\\nInstalled:\");\n for (const pack of installed) {\n console.log(` ${pack.name.padEnd(20)} v${pack.version}`);\n }\n } else {\n p.log.info(\"\\nNo packs installed.\");\n }\n }\n\n p.outro(\"\");\n },\n});\n","import { defineCommand } from \"citty\";\nimport * as p from \"@clack/prompts\";\nimport { sniperConfigExists, readConfig } from \"../config.js\";\nimport { scaffoldProject } from \"../scaffolder.js\";\n\nexport const updateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: \"Update SNIPER core and installed packs\",\n },\n run: async () => {\n const cwd = process.cwd();\n\n if (!(await sniperConfigExists(cwd))) {\n p.log.error(\n 'SNIPER is not initialized in this directory. Run \"sniper init\" first.',\n );\n process.exit(1);\n }\n\n p.intro(\"SNIPER Update\");\n\n // Preserve user's current config (especially state + config customizations)\n const currentConfig = await readConfig(cwd);\n\n const confirm = await p.confirm({\n message:\n \"This will update framework files (personas, teams, templates, etc.) while preserving your config.yaml customizations. Continue?\",\n });\n if (p.isCancel(confirm) || !confirm) {\n p.cancel(\"Aborted.\");\n process.exit(0);\n }\n\n const s = p.spinner();\n s.start(\"Updating framework files...\");\n\n try {\n // Re-scaffold in update mode: overwrites framework files but preserves\n // user-customizable files (CLAUDE.md, settings.json, config.yaml)\n const log = await scaffoldProject(cwd, currentConfig, { update: true });\n\n s.stop(\"Done!\");\n for (const entry of log) {\n p.log.success(entry);\n }\n p.outro(\"SNIPER updated successfully.\");\n } catch (err) {\n s.stop(\"Failed!\");\n p.log.error(`Update failed: ${err}`);\n process.exit(1);\n }\n },\n});\n"],"mappings":"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","names":["createRequire","defineCommand","require","readFile","writeFile","access","join","YAML","p","access","join","log","YAML","writeFile","readFile","log","defineCommand","p","defineCommand","defineCommand","p","cp","readdir","readFile","access","mkdir","join","YAML","p","access","readFile","join","mkdir","cp","readdir","YAML","defineCommand","defineCommand","p","defineCommand","confirm","defineCommand","p","defineCommand","defineCommand","p","defineCommand","confirm","log","require","createRequire","defineCommand"]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@sniper.ai/cli",
3
+ "version": "1.0.0",
4
+ "description": "SNIPER CLI — scaffold and manage SNIPER-enabled projects",
5
+ "type": "module",
6
+ "bin": {
7
+ "sniper": "dist/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "dependencies": {
14
+ "citty": "^0.1.6",
15
+ "@clack/prompts": "^0.9.1",
16
+ "yaml": "^2.7.0",
17
+ "@sniper.ai/core": "1.0.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^22.0.0",
21
+ "tsup": "^8.3.0",
22
+ "typescript": "^5.7.0"
23
+ },
24
+ "keywords": [
25
+ "sniper",
26
+ "cli",
27
+ "scaffold",
28
+ "ai",
29
+ "claude"
30
+ ],
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/sniperai/sniper.git",
35
+ "directory": "packages/cli"
36
+ },
37
+ "engines": {
38
+ "node": ">=18"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "dev": "tsup --watch",
46
+ "clean": "rm -rf dist"
47
+ }
48
+ }