Repository navigation
Expand file tree
/
Copy pathcli.js
More file actions
114 lines (101 loc) · 3.76 KB
/
Copy pathcli.js
File metadata and controls
114 lines (101 loc) · 3.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { readFile } from 'node:fs/promises'
import path from 'node:path'
import { parseArgs } from 'node:util'
import {
InitConflictError,
initializeProject,
normalizeAgents,
normalizePacks,
SUPPORTED_AGENTS,
SUPPORTED_PACKS,
} from './init-project.js'
const usage = `Usage:
sparkwell init [directory] [options]
Options:
--agent <name> Coding-agent integration; repeat for multiple agents
Supported: ${SUPPORTED_AGENTS.join(', ')}
--pack <id> Optional implementation pack; repeat for multiple packs
Supported: ${SUPPORTED_PACKS.join(', ')}
--dry-run Show planned changes without writing files
--force Overwrite conflicting Sparkwell-managed files
--help, -h Show this help
--version, -v Show the CLI version`
export async function runCli(args, environment = {}) {
const cwd = environment.cwd ?? process.cwd()
const stdout = environment.stdout ?? process.stdout
const stderr = environment.stderr ?? process.stderr
try {
const parsed = parseArgs({
args,
allowPositionals: true,
strict: true,
options: {
agent: { type: 'string', multiple: true },
pack: { type: 'string', multiple: true },
'dry-run': { type: 'boolean', default: false },
force: { type: 'boolean', default: false },
help: { type: 'boolean', short: 'h', default: false },
version: { type: 'boolean', short: 'v', default: false },
},
})
if (parsed.values.version) {
const packageJson = JSON.parse(
await readFile(new URL('../package.json', import.meta.url), 'utf8'),
)
stdout.write(`${packageJson.version}\n`)
return 0
}
if (parsed.values.help) {
stdout.write(`${usage}\n`)
return 0
}
const [command, destinationArgument, ...extraPositionals] =
parsed.positionals
if (command !== 'init' || extraPositionals.length > 0) {
stderr.write(`${usage}\n`)
return 1
}
const agents = normalizeAgents(parsed.values.agent)
const packs = normalizePacks(parsed.values.pack)
const destination = path.resolve(cwd, destinationArgument ?? '.')
const result = await initializeProject({
destination,
agents,
packs,
dryRun: parsed.values['dry-run'],
force: parsed.values.force,
})
const action = result.dryRun ? 'Would initialize' : 'Initialized'
stdout.write(`${action} Sparkwell in ${destination}\n`)
const integrationLabel = agents.length === 1 ? 'integration' : 'integrations'
stdout.write(`Agent ${integrationLabel}: ${agents.join(', ')}\n`)
stdout.write(`Implementation packs: ${packs.length ? packs.join(', ') : 'none'}\n`)
stdout.write(
`Files: ${result.created} created, ${result.updated} updated, ${result.unchanged} unchanged\n`,
)
return 0
} catch (error) {
if (error instanceof InitConflictError) {
stderr.write('Sparkwell found conflicting managed paths:\n')
for (const conflict of error.conflicts) {
stderr.write(` - ${conflict}\n`)
}
stderr.write(
'No files were written. Resolve structural conflicts or re-run with --force to replace conflicting Sparkwell-managed content.\n',
)
return 2
}
if (error instanceof TypeError && error.message.includes('Unsupported agent')) {
stderr.write(`${error.message}\n`)
stderr.write(`Supported agents: ${SUPPORTED_AGENTS.join(', ')}\n`)
return 1
}
if (error instanceof TypeError && error.message.includes('Unsupported implementation pack')) {
stderr.write(`${error.message}\n`)
stderr.write(`Supported implementation packs: ${SUPPORTED_PACKS.join(', ')}\n`)
return 1
}
stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
return 1
}
}