theslopmachine 0.3.7 → 0.4.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/MANUAL.md +13 -9
- package/README.md +163 -3
- package/RELEASE.md +11 -3
- package/assets/agents/developer-v2.md +86 -0
- package/assets/agents/developer.md +21 -23
- package/assets/agents/slopmachine-v2.md +219 -0
- package/assets/agents/slopmachine.md +56 -38
- package/assets/skills/beads-operations/SKILL.md +32 -31
- package/assets/skills/beads-operations-v2/SKILL.md +82 -0
- package/assets/skills/clarification-gate/SKILL.md +8 -1
- package/assets/skills/clarification-gate-v2/SKILL.md +74 -0
- package/assets/skills/developer-session-lifecycle/SKILL.md +45 -14
- package/assets/skills/developer-session-lifecycle-v2/SKILL.md +148 -0
- package/assets/skills/development-guidance-v2/SKILL.md +60 -0
- package/assets/skills/evaluation-triage-v2/SKILL.md +38 -0
- package/assets/skills/final-evaluation-orchestration/SKILL.md +9 -11
- package/assets/skills/final-evaluation-orchestration-v2/SKILL.md +57 -0
- package/assets/skills/get-overlays/SKILL.md +77 -6
- package/assets/skills/hardening-gate-v2/SKILL.md +64 -0
- package/assets/skills/integrated-verification-v2/SKILL.md +47 -0
- package/assets/skills/owner-evidence-discipline-v2/SKILL.md +15 -0
- package/assets/skills/planning-gate/SKILL.md +6 -4
- package/assets/skills/planning-gate-v2/SKILL.md +91 -0
- package/assets/skills/planning-guidance-v2/SKILL.md +100 -0
- package/assets/skills/remediation-guidance-v2/SKILL.md +31 -0
- package/assets/skills/report-output-discipline-v2/SKILL.md +15 -0
- package/assets/skills/scaffold-guidance-v2/SKILL.md +57 -0
- package/assets/skills/session-rollover-v2/SKILL.md +41 -0
- package/assets/skills/submission-packaging/SKILL.md +147 -115
- package/assets/skills/submission-packaging-v2/SKILL.md +142 -0
- package/assets/skills/verification-gates/SKILL.md +44 -16
- package/assets/skills/verification-gates-v2/SKILL.md +102 -0
- package/assets/slopmachine/backend-evaluation-prompt.md +9 -2
- package/assets/slopmachine/frontend-evaluation-prompt.md +9 -2
- package/assets/slopmachine/templates/AGENTS-v2.md +55 -0
- package/assets/slopmachine/templates/AGENTS.md +20 -17
- package/assets/slopmachine/tracker-init.js +104 -0
- package/assets/slopmachine/workflow-init-v2.js +99 -0
- package/package.json +1 -1
- package/src/constants.js +22 -3
- package/src/init.js +33 -28
- package/src/install.js +186 -140
- package/src/utils.js +19 -0
- package/assets/slopmachine/beads-init.js +0 -439
package/src/install.js
CHANGED
|
@@ -3,7 +3,7 @@ import os from 'node:os'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
BR_VERSION,
|
|
7
7
|
buildPaths,
|
|
8
8
|
MCP_ENTRIES,
|
|
9
9
|
OPCODE_VERSION,
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
backupFile,
|
|
16
16
|
commandExists,
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
copyDirReplacing,
|
|
18
|
+
copyFileReplacing,
|
|
19
19
|
ensureDir,
|
|
20
20
|
findBashExecutable,
|
|
21
21
|
getGlobalNpmPackageVersion,
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
readJsonIfExists,
|
|
29
29
|
resolveCommand,
|
|
30
30
|
runCommand,
|
|
31
|
+
section,
|
|
31
32
|
warn,
|
|
32
33
|
writeJson,
|
|
33
34
|
} from './utils.js'
|
|
@@ -44,26 +45,21 @@ function getUnixBeadsBinDirs() {
|
|
|
44
45
|
const home = getHomeDir()
|
|
45
46
|
return [
|
|
46
47
|
path.join(home, '.local', 'bin'),
|
|
47
|
-
path.join(home, '
|
|
48
|
-
path.join(home, '.linuxbrew', 'bin'),
|
|
48
|
+
path.join(home, '.cargo', 'bin'),
|
|
49
49
|
'/opt/homebrew/bin',
|
|
50
50
|
'/usr/local/bin',
|
|
51
|
-
'/home/linuxbrew/.linuxbrew/bin',
|
|
52
51
|
]
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
function getLinuxBeadsBinDir() {
|
|
56
|
-
return path.join(getHomeDir(), '.local', 'bin')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
54
|
function getWindowsBeadsBinDirs() {
|
|
60
55
|
if (process.platform !== 'win32') {
|
|
61
56
|
return []
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
const dirs = [
|
|
65
|
-
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs', '
|
|
66
|
-
|
|
60
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs', 'br') : null,
|
|
61
|
+
path.join(getHomeDir(), '.local', 'bin'),
|
|
62
|
+
path.join(getHomeDir(), '.cargo', 'bin'),
|
|
67
63
|
].filter(Boolean)
|
|
68
64
|
|
|
69
65
|
return [...new Set(dirs)]
|
|
@@ -71,14 +67,14 @@ function getWindowsBeadsBinDirs() {
|
|
|
71
67
|
|
|
72
68
|
function getBeadsCommandCandidates() {
|
|
73
69
|
if (process.platform === 'win32') {
|
|
74
|
-
return getWindowsBeadsBinDirs().map((dir) => path.join(dir, '
|
|
70
|
+
return getWindowsBeadsBinDirs().map((dir) => path.join(dir, 'br.exe'))
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
return getUnixBeadsBinDirs().map((dir) => path.join(dir, '
|
|
73
|
+
return getUnixBeadsBinDirs().map((dir) => path.join(dir, 'br'))
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
async function resolveBeadsCommand() {
|
|
81
|
-
return resolveCommand('
|
|
77
|
+
return resolveCommand('br', { additionalCandidates: getBeadsCommandCandidates(), preferCandidates: true })
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
async function getBeadsVersion() {
|
|
@@ -99,14 +95,24 @@ async function probeBeadsRuntime(command) {
|
|
|
99
95
|
const probeDir = await fs.mkdtemp(path.join(os.tmpdir(), 'slopmachine-beads-probe-'))
|
|
100
96
|
|
|
101
97
|
try {
|
|
102
|
-
const result = await runCommand(command, ['init', '--quiet'
|
|
98
|
+
const result = await runCommand(command, ['init', '--quiet'], {
|
|
103
99
|
cwd: probeDir,
|
|
104
100
|
env: { ...process.env, CI: '1' },
|
|
105
101
|
})
|
|
106
102
|
|
|
107
|
-
|
|
103
|
+
if (result.code !== 0) {
|
|
104
|
+
const output = `${result.stdout}${result.stderr}`.trim()
|
|
105
|
+
return { ok: false, output }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const syncResult = await runCommand(command, ['sync', '--flush-only'], {
|
|
109
|
+
cwd: probeDir,
|
|
110
|
+
env: { ...process.env, CI: '1' },
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const output = `${syncResult.stdout}${syncResult.stderr}`.trim()
|
|
108
114
|
return {
|
|
109
|
-
ok:
|
|
115
|
+
ok: syncResult.code === 0,
|
|
110
116
|
output,
|
|
111
117
|
}
|
|
112
118
|
} finally {
|
|
@@ -167,7 +173,7 @@ async function ensureUnixBeadsPath() {
|
|
|
167
173
|
}
|
|
168
174
|
|
|
169
175
|
for (const dir of getUnixBeadsBinDirs()) {
|
|
170
|
-
if (!(await pathExists(path.join(dir, '
|
|
176
|
+
if (!(await pathExists(path.join(dir, 'br')))) {
|
|
171
177
|
continue
|
|
172
178
|
}
|
|
173
179
|
|
|
@@ -175,122 +181,120 @@ async function ensureUnixBeadsPath() {
|
|
|
175
181
|
}
|
|
176
182
|
}
|
|
177
183
|
|
|
178
|
-
async function installLinuxBeadsPrerequisites() {
|
|
179
|
-
const managers = await detectPackageManagers()
|
|
180
|
-
|
|
181
|
-
if (managers.apt) {
|
|
182
|
-
log('Installing Linux Beads build dependencies via apt')
|
|
183
|
-
const updateResult = await runCommand('sudo', ['apt-get', 'update'], { stdio: 'inherit' })
|
|
184
|
-
if (updateResult.code !== 0) {
|
|
185
|
-
return updateResult
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return runCommand('sudo', [
|
|
189
|
-
'apt-get',
|
|
190
|
-
'install',
|
|
191
|
-
'-y',
|
|
192
|
-
'build-essential',
|
|
193
|
-
'pkg-config',
|
|
194
|
-
'libicu-dev',
|
|
195
|
-
'libzstd-dev',
|
|
196
|
-
'golang-go',
|
|
197
|
-
], { stdio: 'inherit' })
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (managers.dnf) {
|
|
201
|
-
log('Installing Linux Beads build dependencies via dnf')
|
|
202
|
-
return runCommand('sudo', [
|
|
203
|
-
'dnf',
|
|
204
|
-
'install',
|
|
205
|
-
'-y',
|
|
206
|
-
'gcc',
|
|
207
|
-
'gcc-c++',
|
|
208
|
-
'make',
|
|
209
|
-
'pkgconf-pkg-config',
|
|
210
|
-
'libicu-devel',
|
|
211
|
-
'libzstd-devel',
|
|
212
|
-
'golang',
|
|
213
|
-
], { stdio: 'inherit' })
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return { code: 0, stdout: '', stderr: '' }
|
|
217
|
-
}
|
|
218
|
-
|
|
219
184
|
async function installBeadsOnWindows() {
|
|
220
185
|
const shell = await resolveCommand('pwsh') || await resolveCommand('powershell')
|
|
221
186
|
if (!shell) {
|
|
222
|
-
return { code: 1, stdout: '', stderr: 'PowerShell is required to install
|
|
187
|
+
return { code: 1, stdout: '', stderr: 'PowerShell is required to install beads_rust on Windows' }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (process.arch !== 'x64') {
|
|
191
|
+
return { code: 1, stdout: '', stderr: `Unsupported Windows architecture for beads_rust: ${process.arch}` }
|
|
223
192
|
}
|
|
224
193
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
'
|
|
228
|
-
'-ExecutionPolicy',
|
|
229
|
-
'Bypass',
|
|
230
|
-
'-Command',
|
|
231
|
-
'irm https://raw.githubusercontent.com/steveyegge/beads/main/install.ps1 | iex',
|
|
232
|
-
], { stdio: 'inherit' })
|
|
194
|
+
const installDir = process.env.LOCALAPPDATA
|
|
195
|
+
? path.join(process.env.LOCALAPPDATA, 'Programs', 'br')
|
|
196
|
+
: path.join(getHomeDir(), 'AppData', 'Local', 'Programs', 'br')
|
|
233
197
|
|
|
198
|
+
const zipName = `br-v${BR_VERSION}-windows_amd64.zip`
|
|
199
|
+
const downloadUrl = `https://github.com/Dicklesworthstone/beads_rust/releases/download/v${BR_VERSION}/${zipName}`
|
|
200
|
+
const tempZip = process.env.TEMP
|
|
201
|
+
? path.join(process.env.TEMP, zipName)
|
|
202
|
+
: path.join(getHomeDir(), 'AppData', 'Local', 'Temp', zipName)
|
|
203
|
+
|
|
204
|
+
const ps = [
|
|
205
|
+
'$ErrorActionPreference = "Stop"',
|
|
206
|
+
`$zipUrl = ${quotePowerShell(downloadUrl)}`,
|
|
207
|
+
`$zipPath = ${quotePowerShell(tempZip)}`,
|
|
208
|
+
`$installDir = ${quotePowerShell(installDir)}`,
|
|
209
|
+
'New-Item -ItemType Directory -Force -Path $installDir | Out-Null',
|
|
210
|
+
'Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath',
|
|
211
|
+
'Expand-Archive -Path $zipPath -DestinationPath $installDir -Force',
|
|
212
|
+
'Remove-Item -Path $zipPath -Force -ErrorAction SilentlyContinue',
|
|
213
|
+
].join('; ')
|
|
214
|
+
|
|
215
|
+
log(`Installing beads_rust ${BR_VERSION} on Windows from release zip`)
|
|
216
|
+
const result = await runCommand(shell, ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', ps], { stdio: 'inherit' })
|
|
234
217
|
await ensureWindowsBeadsPath()
|
|
235
218
|
return result
|
|
236
219
|
}
|
|
237
220
|
|
|
238
|
-
|
|
239
|
-
if (process.platform
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return depsResult
|
|
243
|
-
}
|
|
221
|
+
function getLinuxBrFallbackArchiveName() {
|
|
222
|
+
if (process.platform !== 'linux') {
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
244
225
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
226
|
+
if (process.arch === 'x64') {
|
|
227
|
+
return `br-v${BR_VERSION}-linux_musl_amd64.tar.gz`
|
|
228
|
+
}
|
|
248
229
|
|
|
249
|
-
|
|
250
|
-
|
|
230
|
+
if (process.arch === 'arm64') {
|
|
231
|
+
return `br-v${BR_VERSION}-linux_arm64.tar.gz`
|
|
232
|
+
}
|
|
251
233
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
stdio: 'inherit',
|
|
255
|
-
env: {
|
|
256
|
-
...process.env,
|
|
257
|
-
CGO_ENABLED: '1',
|
|
258
|
-
GOBIN: binDir,
|
|
259
|
-
},
|
|
260
|
-
})
|
|
234
|
+
return null
|
|
235
|
+
}
|
|
261
236
|
|
|
262
|
-
|
|
263
|
-
|
|
237
|
+
async function downloadFile(url, outputPath) {
|
|
238
|
+
const response = await fetch(url)
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
throw new Error(`Download failed with status ${response.status}`)
|
|
264
241
|
}
|
|
265
242
|
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
243
|
+
const buffer = Buffer.from(await response.arrayBuffer())
|
|
244
|
+
await fs.writeFile(outputPath, buffer)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function installLinuxBrFallbackBinary() {
|
|
248
|
+
const archiveName = getLinuxBrFallbackArchiveName()
|
|
249
|
+
if (!archiveName) {
|
|
250
|
+
return { code: 1, stdout: '', stderr: `Unsupported Linux architecture for beads_rust fallback: ${process.arch}` }
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const downloadUrl = `https://github.com/Dicklesworthstone/beads_rust/releases/download/v${BR_VERSION}/${archiveName}`
|
|
254
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'slopmachine-br-fallback-'))
|
|
255
|
+
const archivePath = path.join(tempDir, archiveName)
|
|
256
|
+
const binDir = path.join(getHomeDir(), '.local', 'bin')
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
await ensureDir(binDir)
|
|
260
|
+
log(`Installing beads_rust fallback binary ${archiveName}`)
|
|
261
|
+
await downloadFile(downloadUrl, archivePath)
|
|
262
|
+
|
|
263
|
+
const extractResult = await runCommand('tar', ['-xzf', archivePath, '-C', tempDir])
|
|
264
|
+
if (extractResult.code !== 0) {
|
|
265
|
+
return extractResult
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const sourceBinary = path.join(tempDir, 'br')
|
|
269
|
+
if (!(await pathExists(sourceBinary))) {
|
|
270
|
+
return { code: 1, stdout: '', stderr: 'Downloaded beads_rust archive did not contain br' }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
await fs.copyFile(sourceBinary, path.join(binDir, 'br'))
|
|
274
|
+
await fs.chmod(path.join(binDir, 'br'), 0o755)
|
|
270
275
|
await ensureUnixBeadsPath()
|
|
271
|
-
return
|
|
276
|
+
return { code: 0, stdout: '', stderr: '' }
|
|
277
|
+
} catch (error) {
|
|
278
|
+
return { code: 1, stdout: '', stderr: error instanceof Error ? error.message : String(error) }
|
|
279
|
+
} finally {
|
|
280
|
+
await fs.rm(tempDir, { recursive: true, force: true })
|
|
272
281
|
}
|
|
282
|
+
}
|
|
273
283
|
|
|
284
|
+
async function installBeadsOnUnix() {
|
|
274
285
|
const bashExecutable = await findBashExecutable()
|
|
275
286
|
if (!bashExecutable) {
|
|
276
|
-
return { code: 1, stdout: '', stderr: 'bash is required to run the
|
|
287
|
+
return { code: 1, stdout: '', stderr: 'bash is required to run the beads_rust installer on this platform' }
|
|
277
288
|
}
|
|
278
289
|
|
|
279
|
-
if (await commandExists('curl')) {
|
|
280
|
-
|
|
281
|
-
const result = await runCommand(bashExecutable, ['-lc', 'curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash'], { stdio: 'inherit' })
|
|
282
|
-
await ensureUnixBeadsPath()
|
|
283
|
-
return result
|
|
290
|
+
if (!(await commandExists('curl'))) {
|
|
291
|
+
return { code: 1, stdout: '', stderr: 'curl is required to install beads_rust automatically on this platform' }
|
|
284
292
|
}
|
|
285
293
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return result
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return { code: 1, stdout: '', stderr: 'curl or wget is required to install Beads on this platform' }
|
|
294
|
+
log('Installing beads_rust with the upstream Unix install script')
|
|
295
|
+
const result = await runCommand(bashExecutable, ['-lc', 'curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/beads_rust/main/install.sh?$(date +%s)" | bash'], { stdio: 'inherit' })
|
|
296
|
+
await ensureUnixBeadsPath()
|
|
297
|
+
return result
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
async function getCommandVersion(command, args = ['--version']) {
|
|
@@ -337,7 +341,7 @@ async function tryInstallCoreDependency(name) {
|
|
|
337
341
|
log('Installing opencode-ai@latest globally via npm')
|
|
338
342
|
return runCommand('npm', ['install', '-g', 'opencode-ai@latest'], { stdio: 'inherit' })
|
|
339
343
|
}
|
|
340
|
-
if (name === '
|
|
344
|
+
if (name === 'br') {
|
|
341
345
|
if (process.platform === 'win32') {
|
|
342
346
|
return installBeadsOnWindows()
|
|
343
347
|
}
|
|
@@ -419,7 +423,7 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
419
423
|
}
|
|
420
424
|
}
|
|
421
425
|
|
|
422
|
-
if (checkCommand === '
|
|
426
|
+
if (checkCommand === 'br') {
|
|
423
427
|
const beads = await getBeadsVersion()
|
|
424
428
|
if (beads) {
|
|
425
429
|
const probe = await probeBeadsRuntime(beads.command)
|
|
@@ -483,23 +487,59 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
483
487
|
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`.')
|
|
484
488
|
}
|
|
485
489
|
|
|
486
|
-
if (checkCommand === '
|
|
490
|
+
if (checkCommand === 'br') {
|
|
487
491
|
const beads = await getBeadsVersion()
|
|
488
492
|
if (beads) {
|
|
489
493
|
const probe = await probeBeadsRuntime(beads.command)
|
|
490
494
|
if (probe.ok) {
|
|
491
495
|
log(`Installed ${name} via ${beads.command}: ${beads.version}`)
|
|
492
|
-
if (process.platform === 'win32' && !(await commandExists('
|
|
493
|
-
warn('
|
|
496
|
+
if (process.platform === 'win32' && !(await commandExists('br'))) {
|
|
497
|
+
warn('beads_rust was installed, but `br` is not visible in this shell PATH yet. The installer added the common beads_rust directories to PATH for future shells.')
|
|
494
498
|
}
|
|
495
499
|
return
|
|
496
500
|
}
|
|
497
501
|
|
|
502
|
+
if (process.platform === 'linux') {
|
|
503
|
+
warn(`${name} was installed, but the detected binary failed runtime verification. Trying Linux fallback binary.`)
|
|
504
|
+
const fallbackResult = await installLinuxBrFallbackBinary()
|
|
505
|
+
if (fallbackResult.code === 0) {
|
|
506
|
+
const fallbackVersion = await getBeadsVersion()
|
|
507
|
+
if (fallbackVersion) {
|
|
508
|
+
const fallbackProbe = await probeBeadsRuntime(fallbackVersion.command)
|
|
509
|
+
if (fallbackProbe.ok) {
|
|
510
|
+
log(`Installed ${name} via fallback binary at ${fallbackVersion.command}: ${fallbackVersion.version}`)
|
|
511
|
+
return
|
|
512
|
+
}
|
|
513
|
+
warn(`${name} fallback binary still failed runtime verification.${fallbackProbe.output ? ` ${fallbackProbe.output}` : ''}`)
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
warn(`Linux fallback binary installation failed.${fallbackResult.stderr ? ` ${fallbackResult.stderr}` : ''}`)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
498
520
|
warn(`${name} was installed at ${beads.command}, but it still failed a runtime probe.${probe.output ? ` ${probe.output}` : ''}`)
|
|
499
521
|
return
|
|
500
522
|
}
|
|
501
523
|
|
|
502
|
-
|
|
524
|
+
if (process.platform === 'linux') {
|
|
525
|
+
warn(`${name} install completed, but no working br binary was detected. Trying Linux fallback binary.`)
|
|
526
|
+
const fallbackResult = await installLinuxBrFallbackBinary()
|
|
527
|
+
if (fallbackResult.code === 0) {
|
|
528
|
+
const fallbackVersion = await getBeadsVersion()
|
|
529
|
+
if (fallbackVersion) {
|
|
530
|
+
const fallbackProbe = await probeBeadsRuntime(fallbackVersion.command)
|
|
531
|
+
if (fallbackProbe.ok) {
|
|
532
|
+
log(`Installed ${name} via fallback binary at ${fallbackVersion.command}: ${fallbackVersion.version}`)
|
|
533
|
+
return
|
|
534
|
+
}
|
|
535
|
+
warn(`${name} fallback binary still failed runtime verification.${fallbackProbe.output ? ` ${fallbackProbe.output}` : ''}`)
|
|
536
|
+
}
|
|
537
|
+
} else {
|
|
538
|
+
warn(`Linux fallback binary installation failed.${fallbackResult.stderr ? ` ${fallbackResult.stderr}` : ''}`)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
warn('beads_rust installation completed, but the `br` command could not be verified. Open a new terminal and run `br version`.')
|
|
503
543
|
}
|
|
504
544
|
}
|
|
505
545
|
|
|
@@ -529,16 +569,16 @@ async function checkInitShellSupport() {
|
|
|
529
569
|
async function installAgents(paths) {
|
|
530
570
|
const sourceAgents = path.join(assetsRoot(), 'agents')
|
|
531
571
|
await ensureDir(paths.opencodeAgentsDir)
|
|
532
|
-
const summary = { installed: [],
|
|
572
|
+
const summary = { installed: [], refreshed: [] }
|
|
533
573
|
|
|
534
|
-
for (const fileName of ['slopmachine.md', 'developer.md']) {
|
|
535
|
-
const result = await
|
|
536
|
-
if (result.
|
|
574
|
+
for (const fileName of ['slopmachine.md', 'developer.md', 'slopmachine-v2.md', 'developer-v2.md']) {
|
|
575
|
+
const result = await copyFileReplacing(path.join(sourceAgents, fileName), path.join(paths.opencodeAgentsDir, fileName))
|
|
576
|
+
if (result.replaced) {
|
|
577
|
+
log(`Refreshed agent: ${fileName}`)
|
|
578
|
+
summary.refreshed.push(fileName)
|
|
579
|
+
} else {
|
|
537
580
|
log(`Installed agent: ${fileName}`)
|
|
538
581
|
summary.installed.push(fileName)
|
|
539
|
-
} else {
|
|
540
|
-
warn(`Skipped existing agent: ${fileName}`)
|
|
541
|
-
summary.skipped.push(fileName)
|
|
542
582
|
}
|
|
543
583
|
}
|
|
544
584
|
|
|
@@ -548,16 +588,16 @@ async function installAgents(paths) {
|
|
|
548
588
|
async function installSkills(paths) {
|
|
549
589
|
const sourceSkills = path.join(assetsRoot(), 'skills')
|
|
550
590
|
await ensureDir(paths.globalSkillsDir)
|
|
551
|
-
const summary = { installed: [],
|
|
591
|
+
const summary = { installed: [], refreshed: [] }
|
|
552
592
|
|
|
553
593
|
for (const dirName of REQUIRED_SKILL_DIRS) {
|
|
554
|
-
const result = await
|
|
555
|
-
if (result.
|
|
594
|
+
const result = await copyDirReplacing(path.join(sourceSkills, dirName), path.join(paths.globalSkillsDir, dirName))
|
|
595
|
+
if (result.replaced) {
|
|
596
|
+
log(`Refreshed skill: ${dirName}`)
|
|
597
|
+
summary.refreshed.push(dirName)
|
|
598
|
+
} else {
|
|
556
599
|
log(`Installed skill: ${dirName}`)
|
|
557
600
|
summary.installed.push(dirName)
|
|
558
|
-
} else {
|
|
559
|
-
warn(`Skipped existing skill: ${dirName}`)
|
|
560
|
-
summary.skipped.push(dirName)
|
|
561
601
|
}
|
|
562
602
|
}
|
|
563
603
|
|
|
@@ -567,17 +607,18 @@ async function installSkills(paths) {
|
|
|
567
607
|
async function installSlopmachineAssets(paths) {
|
|
568
608
|
const source = path.join(assetsRoot(), 'slopmachine')
|
|
569
609
|
await ensureDir(paths.slopmachineDir)
|
|
570
|
-
const summary = { installed: [],
|
|
610
|
+
const summary = { installed: [], refreshed: [] }
|
|
571
611
|
|
|
572
612
|
for (const relativePath of REQUIRED_SLOPMACHINE_FILES) {
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
613
|
+
const targetPath = path.join(paths.slopmachineDir, relativePath)
|
|
614
|
+
const result = await copyFileReplacing(path.join(source, relativePath), targetPath)
|
|
615
|
+
await makeExecutableIfShellScript(targetPath)
|
|
616
|
+
if (result.replaced) {
|
|
617
|
+
log(`Refreshed asset: ${relativePath}`)
|
|
618
|
+
summary.refreshed.push(relativePath)
|
|
619
|
+
} else {
|
|
576
620
|
log(`Installed asset: ${relativePath}`)
|
|
577
621
|
summary.installed.push(relativePath)
|
|
578
|
-
} else {
|
|
579
|
-
warn(`Skipped existing asset: ${relativePath}`)
|
|
580
|
-
summary.skipped.push(relativePath)
|
|
581
622
|
}
|
|
582
623
|
}
|
|
583
624
|
|
|
@@ -668,31 +709,36 @@ async function collectApiKeys() {
|
|
|
668
709
|
|
|
669
710
|
export async function runInstall() {
|
|
670
711
|
const paths = buildPaths()
|
|
712
|
+
section('Setup')
|
|
671
713
|
log(`Configuring theslopmachine in ${paths.home}`)
|
|
672
714
|
|
|
715
|
+
section('Dependencies')
|
|
673
716
|
await ensureDependency({ name: 'git', checkCommand: 'git', installable: true })
|
|
674
717
|
await ensureDependency({ name: 'python3', checkCommand: 'python3', installable: true })
|
|
675
718
|
await ensureDependency({ name: 'opencode', checkCommand: 'opencode', requiredVersion: OPCODE_VERSION, installable: true })
|
|
676
|
-
await ensureDependency({ name: '
|
|
719
|
+
await ensureDependency({ name: 'beads_rust (br)', checkCommand: 'br', requiredVersion: null, installable: true })
|
|
677
720
|
await checkDocker()
|
|
678
721
|
await checkInitShellSupport()
|
|
679
722
|
|
|
723
|
+
section('Package Assets')
|
|
680
724
|
const agentSummary = await installAgents(paths)
|
|
681
725
|
const skillSummary = await installSkills(paths)
|
|
682
726
|
const assetSummary = await installSlopmachineAssets(paths)
|
|
683
727
|
|
|
728
|
+
section('OpenCode Config')
|
|
684
729
|
await maybeInstallPluginBinary()
|
|
685
730
|
const keys = await collectApiKeys()
|
|
686
731
|
await mergeOpencodeConfig(paths, keys)
|
|
687
732
|
|
|
733
|
+
section('Summary')
|
|
688
734
|
log('Setup phase completed.')
|
|
689
735
|
console.log('\ntheslopmachine setup summary')
|
|
690
736
|
console.log(`- Agents installed: ${agentSummary.installed.length}`)
|
|
691
|
-
console.log(`- Agents
|
|
737
|
+
console.log(`- Agents refreshed: ${agentSummary.refreshed.length}`)
|
|
692
738
|
console.log(`- Skills installed: ${skillSummary.installed.length}`)
|
|
693
|
-
console.log(`- Skills
|
|
739
|
+
console.log(`- Skills refreshed: ${skillSummary.refreshed.length}`)
|
|
694
740
|
console.log(`- SlopMachine assets installed: ${assetSummary.installed.length}`)
|
|
695
|
-
console.log(`- SlopMachine assets
|
|
741
|
+
console.log(`- SlopMachine assets refreshed: ${assetSummary.refreshed.length}`)
|
|
696
742
|
console.log(`- Agents directory: ${paths.opencodeAgentsDir}`)
|
|
697
743
|
console.log(`- Skills directory: ${paths.globalSkillsDir}`)
|
|
698
744
|
console.log(`- SlopMachine home: ${paths.slopmachineDir}`)
|
package/src/utils.js
CHANGED
|
@@ -13,6 +13,10 @@ export function warn(message) {
|
|
|
13
13
|
console.warn(`[slopmachine] WARN: ${message}`)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
export function section(title) {
|
|
17
|
+
console.log(`\n[slopmachine] ${title}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
export async function pathExists(targetPath) {
|
|
17
21
|
try {
|
|
18
22
|
await fs.access(targetPath, fsConstants.F_OK)
|
|
@@ -35,6 +39,13 @@ export async function copyFileIfMissing(sourcePath, targetPath) {
|
|
|
35
39
|
return { copied: true, skipped: false }
|
|
36
40
|
}
|
|
37
41
|
|
|
42
|
+
export async function copyFileReplacing(sourcePath, targetPath) {
|
|
43
|
+
const existed = await pathExists(targetPath)
|
|
44
|
+
await ensureDir(path.dirname(targetPath))
|
|
45
|
+
await fs.copyFile(sourcePath, targetPath)
|
|
46
|
+
return { copied: true, replaced: existed, skipped: false }
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
export async function copyDirIfMissing(sourcePath, targetPath) {
|
|
39
50
|
if (await pathExists(targetPath)) {
|
|
40
51
|
return { copied: false, skipped: true }
|
|
@@ -44,6 +55,14 @@ export async function copyDirIfMissing(sourcePath, targetPath) {
|
|
|
44
55
|
return { copied: true, skipped: false }
|
|
45
56
|
}
|
|
46
57
|
|
|
58
|
+
export async function copyDirReplacing(sourcePath, targetPath) {
|
|
59
|
+
const existed = await pathExists(targetPath)
|
|
60
|
+
await ensureDir(path.dirname(targetPath))
|
|
61
|
+
await fs.rm(targetPath, { recursive: true, force: true })
|
|
62
|
+
await fs.cp(sourcePath, targetPath, { recursive: true })
|
|
63
|
+
return { copied: true, replaced: existed, skipped: false }
|
|
64
|
+
}
|
|
65
|
+
|
|
47
66
|
export async function makeExecutableIfShellScript(targetPath) {
|
|
48
67
|
if (targetPath.endsWith('.sh')) {
|
|
49
68
|
await fs.chmod(targetPath, 0o755)
|