theslopmachine 0.3.3 → 0.3.4
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 +1 -1
- package/assets/slopmachine/beads-init.js +8 -6
- package/package.json +1 -1
- package/src/init.js +18 -0
- package/src/install.js +130 -0
- package/src/utils.js +31 -13
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ import { spawn } from 'node:child_process'
|
|
|
6
6
|
|
|
7
7
|
const targetInput = process.argv[2] || '.'
|
|
8
8
|
const target = path.resolve(process.cwd(), targetInput)
|
|
9
|
+
const bdCommand = process.env.BD_COMMAND || 'bd'
|
|
9
10
|
|
|
10
11
|
function log(message) {
|
|
11
12
|
console.log(`[beads-init] ${message}`)
|
|
@@ -57,11 +58,11 @@ async function pathExists(targetPath) {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
async function runBd(args, options = {}) {
|
|
60
|
-
return run(
|
|
61
|
+
return run(bdCommand, args, { cwd: target, ...options })
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
async function runBdNoninteractive(args) {
|
|
64
|
-
return run(
|
|
65
|
+
return run(bdCommand, args, {
|
|
65
66
|
cwd: target,
|
|
66
67
|
env: { ...process.env, CI: '1' },
|
|
67
68
|
input: '',
|
|
@@ -69,13 +70,13 @@ async function runBdNoninteractive(args) {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
async function supportsInitFlag(flag) {
|
|
72
|
-
const help = await run(
|
|
73
|
+
const help = await run(bdCommand, ['init', '--help'])
|
|
73
74
|
return `${help.stdout}${help.stderr}`.includes(flag)
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
async function chooseNoninteractiveInitModeFlag(isGitRepo) {
|
|
77
78
|
if (!isGitRepo) return null
|
|
78
|
-
const help = await run(
|
|
79
|
+
const help = await run(bdCommand, ['init', '--help'])
|
|
79
80
|
const text = `${help.stdout}${help.stderr}`
|
|
80
81
|
if (text.includes('--setup-exclude')) return '--setup-exclude'
|
|
81
82
|
if (text.includes('--stealth')) return '--stealth'
|
|
@@ -289,8 +290,9 @@ async function installGitHooksNonfatal() {
|
|
|
289
290
|
}
|
|
290
291
|
|
|
291
292
|
async function main() {
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
const bdAvailable = bdCommand !== 'bd' ? await pathExists(bdCommand) : await commandExists('bd')
|
|
294
|
+
if (!bdAvailable) {
|
|
295
|
+
die(`'${bdCommand}' is not available. Install Beads first.`)
|
|
294
296
|
}
|
|
295
297
|
|
|
296
298
|
if (!(await pathExists(target))) {
|
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -21,6 +21,21 @@ const GITIGNORE_ENTRIES = [
|
|
|
21
21
|
'antigravity-logs/',
|
|
22
22
|
]
|
|
23
23
|
|
|
24
|
+
function getWindowsBeadsCommandCandidates() {
|
|
25
|
+
if (process.platform !== 'win32') {
|
|
26
|
+
return []
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs', 'bd', 'bd.exe') : null,
|
|
31
|
+
process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'go', 'bin', 'bd.exe') : null,
|
|
32
|
+
].filter(Boolean)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function resolveBeadsCommand() {
|
|
36
|
+
return resolveCommand('bd', { additionalCandidates: getWindowsBeadsCommandCandidates() })
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
function parseInitArgs(args) {
|
|
25
40
|
let openAfterInit = false
|
|
26
41
|
let targetInput = '.'
|
|
@@ -104,11 +119,14 @@ async function runBeadsBootstrap(paths, targetPath, beadsScript) {
|
|
|
104
119
|
throw new Error('Unable to locate the current Node.js executable for Beads bootstrap.')
|
|
105
120
|
}
|
|
106
121
|
|
|
122
|
+
const beadsCommand = await resolveBeadsCommand()
|
|
123
|
+
|
|
107
124
|
log('Running Beads setup')
|
|
108
125
|
const result = await runCommand(nodeExecutable, [beadsScript, targetPath], {
|
|
109
126
|
stdio: 'inherit',
|
|
110
127
|
env: {
|
|
111
128
|
...process.env,
|
|
129
|
+
BD_COMMAND: beadsCommand || '',
|
|
112
130
|
HOME: paths.home,
|
|
113
131
|
},
|
|
114
132
|
})
|
package/src/install.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
log,
|
|
21
21
|
makeExecutableIfShellScript,
|
|
22
22
|
pathExists,
|
|
23
|
+
prependToProcessPath,
|
|
23
24
|
promptText,
|
|
24
25
|
promptYesNo,
|
|
25
26
|
readJsonIfExists,
|
|
@@ -33,6 +34,107 @@ function assetsRoot() {
|
|
|
33
34
|
return path.join(PACKAGE_ROOT, 'assets')
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
function getWindowsBeadsBinDirs() {
|
|
38
|
+
if (process.platform !== 'win32') {
|
|
39
|
+
return []
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const dirs = [
|
|
43
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs', 'bd') : null,
|
|
44
|
+
process.env.USERPROFILE ? path.join(process.env.USERPROFILE, 'go', 'bin') : null,
|
|
45
|
+
].filter(Boolean)
|
|
46
|
+
|
|
47
|
+
return [...new Set(dirs)]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getWindowsBeadsCommandCandidates() {
|
|
51
|
+
return getWindowsBeadsBinDirs().map((dir) => path.join(dir, 'bd.exe'))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function resolveBeadsCommand() {
|
|
55
|
+
return resolveCommand('bd', { additionalCandidates: getWindowsBeadsCommandCandidates() })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function getBeadsVersion() {
|
|
59
|
+
const command = await resolveBeadsCommand()
|
|
60
|
+
if (!command) {
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const result = await runCommand(command, ['version'])
|
|
65
|
+
if (result.code !== 0) {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { command, version: (result.stdout || result.stderr).trim() }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function quotePowerShell(value) {
|
|
73
|
+
return `'${String(value).replaceAll(`'`, `''`)}'`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function updateWindowsUserPath(entry) {
|
|
77
|
+
const shell = await resolveCommand('pwsh') || await resolveCommand('powershell')
|
|
78
|
+
if (!shell) {
|
|
79
|
+
warn(`Unable to persist ${entry} into the Windows user PATH automatically.`)
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const quotedEntry = quotePowerShell(entry)
|
|
84
|
+
const script = [
|
|
85
|
+
`$entry = ${quotedEntry}`,
|
|
86
|
+
`$existing = [Environment]::GetEnvironmentVariable('Path', 'User')`,
|
|
87
|
+
`$parts = @()`,
|
|
88
|
+
`if ($existing) { $parts = $existing -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ } }`,
|
|
89
|
+
`if ($parts -notcontains $entry) {`,
|
|
90
|
+
` $newValue = if ($existing -and $existing.Trim()) { "$existing;$entry" } else { $entry }`,
|
|
91
|
+
` [Environment]::SetEnvironmentVariable('Path', $newValue, 'User')`,
|
|
92
|
+
`}`,
|
|
93
|
+
].join('; ')
|
|
94
|
+
|
|
95
|
+
const result = await runCommand(shell, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script])
|
|
96
|
+
if (result.code !== 0) {
|
|
97
|
+
warn(`Unable to persist ${entry} into the Windows user PATH automatically.`)
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function ensureWindowsBeadsPath() {
|
|
105
|
+
if (process.platform !== 'win32') {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const dir of getWindowsBeadsBinDirs()) {
|
|
110
|
+
if (!(await pathExists(dir))) {
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
prependToProcessPath(dir)
|
|
115
|
+
await updateWindowsUserPath(dir)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function installBeadsOnWindows() {
|
|
120
|
+
const shell = await resolveCommand('pwsh') || await resolveCommand('powershell')
|
|
121
|
+
if (!shell) {
|
|
122
|
+
return { code: 1, stdout: '', stderr: 'PowerShell is required to install Beads on Windows' }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
log('Installing Beads with the upstream Windows PowerShell installer')
|
|
126
|
+
const result = await runCommand(shell, [
|
|
127
|
+
'-NoProfile',
|
|
128
|
+
'-ExecutionPolicy',
|
|
129
|
+
'Bypass',
|
|
130
|
+
'-Command',
|
|
131
|
+
'irm https://raw.githubusercontent.com/steveyegge/beads/main/install.ps1 | iex',
|
|
132
|
+
], { stdio: 'inherit' })
|
|
133
|
+
|
|
134
|
+
await ensureWindowsBeadsPath()
|
|
135
|
+
return result
|
|
136
|
+
}
|
|
137
|
+
|
|
36
138
|
async function getCommandVersion(command, args = ['--version']) {
|
|
37
139
|
const exists = await commandExists(command)
|
|
38
140
|
if (!exists) return null
|
|
@@ -78,6 +180,10 @@ async function tryInstallCoreDependency(name) {
|
|
|
78
180
|
return runCommand('npm', ['install', '-g', 'opencode-ai@latest'], { stdio: 'inherit' })
|
|
79
181
|
}
|
|
80
182
|
if (name === 'bd') {
|
|
183
|
+
if (process.platform === 'win32') {
|
|
184
|
+
return installBeadsOnWindows()
|
|
185
|
+
}
|
|
186
|
+
|
|
81
187
|
log(`Installing @beads/bd@${BEADS_VERSION} globally via npm`)
|
|
82
188
|
return runCommand('npm', ['install', '-g', `@beads/bd@${BEADS_VERSION}`], { stdio: 'inherit' })
|
|
83
189
|
}
|
|
@@ -154,6 +260,17 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
154
260
|
}
|
|
155
261
|
}
|
|
156
262
|
|
|
263
|
+
if (checkCommand === 'bd') {
|
|
264
|
+
const beads = await getBeadsVersion()
|
|
265
|
+
if (beads) {
|
|
266
|
+
log(`${name} detected via ${beads.command}: ${beads.version}`)
|
|
267
|
+
if (requiredVersion && !beads.version.includes(requiredVersion)) {
|
|
268
|
+
warn(`${name} version differs from tested reference ${requiredVersion}`)
|
|
269
|
+
}
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
157
274
|
const version = await getCommandVersion(checkCommand)
|
|
158
275
|
if (version) {
|
|
159
276
|
log(`${name} detected: ${version}`)
|
|
@@ -194,6 +311,19 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
194
311
|
|
|
195
312
|
warn('opencode-ai install completed, but the package could not be verified. Open a new terminal and run `opencode --version` before using `slopmachine init -o`.')
|
|
196
313
|
}
|
|
314
|
+
|
|
315
|
+
if (checkCommand === 'bd') {
|
|
316
|
+
const beads = await getBeadsVersion()
|
|
317
|
+
if (beads) {
|
|
318
|
+
log(`Installed ${name} via ${beads.command}: ${beads.version}`)
|
|
319
|
+
if (process.platform === 'win32' && !(await commandExists('bd'))) {
|
|
320
|
+
warn('Beads was installed, but `bd` is not visible in this shell PATH yet. The installer added the common Windows Beads directories to PATH for future shells.')
|
|
321
|
+
}
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
warn('Beads installation completed, but the `bd` command could not be verified. Re-open PowerShell or Command Prompt and run `bd version`.')
|
|
326
|
+
}
|
|
197
327
|
}
|
|
198
328
|
|
|
199
329
|
async function checkDocker() {
|
package/src/utils.js
CHANGED
|
@@ -154,26 +154,30 @@ async function getGlobalNpmPrefix() {
|
|
|
154
154
|
return prefix || null
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
export async function resolveCommand(command) {
|
|
157
|
+
export async function resolveCommand(command, options = {}) {
|
|
158
158
|
if (await commandExists(command)) {
|
|
159
159
|
return command
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
const prefix = await getGlobalNpmPrefix()
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
const candidates = []
|
|
164
|
+
|
|
165
|
+
if (prefix) {
|
|
166
|
+
candidates.push(...(process.platform === 'win32'
|
|
167
|
+
? [
|
|
168
|
+
path.join(prefix, `${command}.cmd`),
|
|
169
|
+
path.join(prefix, `${command}.exe`),
|
|
170
|
+
path.join(prefix, command),
|
|
171
|
+
]
|
|
172
|
+
: [
|
|
173
|
+
path.join(prefix, 'bin', command),
|
|
174
|
+
path.join(prefix, command),
|
|
175
|
+
]))
|
|
165
176
|
}
|
|
166
177
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
path.join(prefix, `${command}.exe`),
|
|
171
|
-
path.join(prefix, command),
|
|
172
|
-
]
|
|
173
|
-
: [
|
|
174
|
-
path.join(prefix, 'bin', command),
|
|
175
|
-
path.join(prefix, command),
|
|
176
|
-
]
|
|
178
|
+
if (Array.isArray(options.additionalCandidates)) {
|
|
179
|
+
candidates.push(...options.additionalCandidates)
|
|
180
|
+
}
|
|
177
181
|
|
|
178
182
|
for (const candidate of candidates) {
|
|
179
183
|
if (await pathExists(candidate)) {
|
|
@@ -184,6 +188,20 @@ export async function resolveCommand(command) {
|
|
|
184
188
|
return null
|
|
185
189
|
}
|
|
186
190
|
|
|
191
|
+
export function prependToProcessPath(entry) {
|
|
192
|
+
if (!entry) {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const currentPath = process.env.PATH || ''
|
|
197
|
+
const entries = currentPath.split(path.delimiter).filter(Boolean)
|
|
198
|
+
if (entries.includes(entry)) {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
process.env.PATH = currentPath ? `${entry}${path.delimiter}${currentPath}` : entry
|
|
203
|
+
}
|
|
204
|
+
|
|
187
205
|
export async function findBashExecutable() {
|
|
188
206
|
if (await commandExists('bash')) {
|
|
189
207
|
return 'bash'
|