theslopmachine 0.3.2 → 0.3.3
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 +3 -7
- package/package.json +1 -1
- package/src/init.js +5 -4
- package/src/install.js +37 -2
- package/src/utils.js +74 -1
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Installer package for the theslopmachine workflow owner, developer agent, required skills, templates, and local support files.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Commands
|
|
6
6
|
|
|
7
|
-
- `slopmachine setup`
|
|
8
|
-
- `slopmachine init`
|
|
7
|
+
- `slopmachine setup` - configures agents, files and scripts
|
|
8
|
+
- `slopmachine init` - sets up a project
|
|
9
9
|
- `slopmachine init -o` to bootstrap the project and immediately open OpenCode inside `repo/`
|
|
10
10
|
|
|
11
11
|
See `MANUAL.md` for a short usage guide and workflow summary.
|
|
@@ -17,7 +17,3 @@ See `MANUAL.md` for a short usage guide and workflow summary.
|
|
|
17
17
|
- `assets/slopmachine/`
|
|
18
18
|
- `bin/`
|
|
19
19
|
- `src/`
|
|
20
|
-
|
|
21
|
-
## Status
|
|
22
|
-
|
|
23
|
-
This package workspace is being built from the current local theslopmachine setup without modifying the live installation on this machine.
|
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { buildPaths } from './constants.js'
|
|
5
|
-
import {
|
|
5
|
+
import { ensureDir, log, pathExists, resolveCommand, runCommand, warn } from './utils.js'
|
|
6
6
|
|
|
7
7
|
const GITIGNORE_ENTRIES = [
|
|
8
8
|
'.DS_Store',
|
|
@@ -145,18 +145,19 @@ async function maybeOpenOpencode(targetPath, openAfterInit) {
|
|
|
145
145
|
return
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
const opencodeCommand = await resolveCommand('opencode')
|
|
149
|
+
if (!opencodeCommand) {
|
|
149
150
|
warn('OpenCode is not available in PATH, so the project was initialized but could not be opened automatically. Launch OpenCode manually inside repo/.')
|
|
150
151
|
return
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
log('Opening OpenCode in repo/')
|
|
154
|
-
const result = await runCommand(
|
|
155
|
+
const result = await runCommand(opencodeCommand, [], {
|
|
155
156
|
stdio: 'inherit',
|
|
156
157
|
cwd: path.join(targetPath, 'repo'),
|
|
157
158
|
})
|
|
158
159
|
if (result.code !== 0) {
|
|
159
|
-
|
|
160
|
+
warn(`Failed to launch OpenCode automatically (${result.stderr || `exit code ${result.code}`}). Launch it manually inside repo/.`)
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
|
package/src/install.js
CHANGED
|
@@ -16,12 +16,14 @@ import {
|
|
|
16
16
|
copyDirIfMissing,
|
|
17
17
|
copyFileIfMissing,
|
|
18
18
|
ensureDir,
|
|
19
|
+
getGlobalNpmPackageVersion,
|
|
19
20
|
log,
|
|
20
21
|
makeExecutableIfShellScript,
|
|
21
22
|
pathExists,
|
|
22
23
|
promptText,
|
|
23
24
|
promptYesNo,
|
|
24
25
|
readJsonIfExists,
|
|
26
|
+
resolveCommand,
|
|
25
27
|
runCommand,
|
|
26
28
|
warn,
|
|
27
29
|
writeJson,
|
|
@@ -72,8 +74,8 @@ async function detectPackageManagers() {
|
|
|
72
74
|
|
|
73
75
|
async function tryInstallCoreDependency(name) {
|
|
74
76
|
if (name === 'opencode') {
|
|
75
|
-
log(
|
|
76
|
-
return runCommand('npm', ['install', '-g',
|
|
77
|
+
log('Installing opencode-ai@latest globally via npm')
|
|
78
|
+
return runCommand('npm', ['install', '-g', 'opencode-ai@latest'], { stdio: 'inherit' })
|
|
77
79
|
}
|
|
78
80
|
if (name === 'bd') {
|
|
79
81
|
log(`Installing @beads/bd@${BEADS_VERSION} globally via npm`)
|
|
@@ -134,6 +136,24 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
|
|
139
|
+
if (name === 'opencode') {
|
|
140
|
+
const npmVersion = await getGlobalNpmPackageVersion('opencode-ai')
|
|
141
|
+
if (npmVersion) {
|
|
142
|
+
log(`opencode detected via global npm package: opencode-ai@${npmVersion}`)
|
|
143
|
+
if (requiredVersion && !npmVersion.includes(requiredVersion)) {
|
|
144
|
+
warn(`${name} version differs from tested reference ${requiredVersion}`)
|
|
145
|
+
}
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const commandPath = await resolveCommand('opencode')
|
|
150
|
+
if (commandPath) {
|
|
151
|
+
log(`opencode command detected at ${commandPath}`)
|
|
152
|
+
warn(`Unable to verify ${name} version without executing the binary during setup.`)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
137
157
|
const version = await getCommandVersion(checkCommand)
|
|
138
158
|
if (version) {
|
|
139
159
|
log(`${name} detected: ${version}`)
|
|
@@ -158,6 +178,21 @@ async function ensureDependency({ name, checkCommand, requiredVersion, installab
|
|
|
158
178
|
const result = await tryInstallCoreDependency(checkCommand)
|
|
159
179
|
if (result.code !== 0) {
|
|
160
180
|
warn(`Automatic installation for ${name} failed. Please install it manually.`)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (name === 'opencode') {
|
|
185
|
+
const installedVersion = await getGlobalNpmPackageVersion('opencode-ai')
|
|
186
|
+
if (installedVersion) {
|
|
187
|
+
log(`Installed opencode-ai@${installedVersion}`)
|
|
188
|
+
const commandPath = await resolveCommand('opencode')
|
|
189
|
+
if (!commandPath) {
|
|
190
|
+
warn('opencode-ai was installed, but the `opencode` command is not visible in this shell PATH yet. Open a new terminal before using `slopmachine init -o`.')
|
|
191
|
+
}
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
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`.')
|
|
161
196
|
}
|
|
162
197
|
}
|
|
163
198
|
|
package/src/utils.js
CHANGED
|
@@ -76,6 +76,7 @@ export async function backupFile(filePath) {
|
|
|
76
76
|
|
|
77
77
|
export async function runCommand(command, args, options = {}) {
|
|
78
78
|
return new Promise((resolve, reject) => {
|
|
79
|
+
let settled = false
|
|
79
80
|
const child = spawn(command, args, {
|
|
80
81
|
stdio: options.stdio || 'pipe',
|
|
81
82
|
cwd: options.cwd,
|
|
@@ -98,8 +99,24 @@ export async function runCommand(command, args, options = {}) {
|
|
|
98
99
|
})
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
child.on('error',
|
|
102
|
+
child.on('error', (error) => {
|
|
103
|
+
if (settled) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
settled = true
|
|
107
|
+
|
|
108
|
+
if (error && error.code === 'ENOENT') {
|
|
109
|
+
resolve({ code: 127, stdout: stdoutText, stderr: error.message })
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
reject(error)
|
|
114
|
+
})
|
|
102
115
|
child.on('close', (code) => {
|
|
116
|
+
if (settled) {
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
settled = true
|
|
103
120
|
resolve({ code: code ?? 1, stdout: stdoutText, stderr: stderrText })
|
|
104
121
|
})
|
|
105
122
|
})
|
|
@@ -111,6 +128,62 @@ export async function commandExists(command) {
|
|
|
111
128
|
return result.code === 0
|
|
112
129
|
}
|
|
113
130
|
|
|
131
|
+
export async function getGlobalNpmPackageVersion(packageName) {
|
|
132
|
+
const result = await runCommand('npm', ['list', '-g', packageName, '--depth=0', '--json'])
|
|
133
|
+
const raw = result.stdout.trim()
|
|
134
|
+
|
|
135
|
+
if (!raw) {
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const data = JSON.parse(raw)
|
|
141
|
+
return data.dependencies?.[packageName]?.version || null
|
|
142
|
+
} catch {
|
|
143
|
+
return null
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function getGlobalNpmPrefix() {
|
|
148
|
+
const result = await runCommand('npm', ['prefix', '-g'])
|
|
149
|
+
if (result.code !== 0) {
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const prefix = result.stdout.trim()
|
|
154
|
+
return prefix || null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function resolveCommand(command) {
|
|
158
|
+
if (await commandExists(command)) {
|
|
159
|
+
return command
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const prefix = await getGlobalNpmPrefix()
|
|
163
|
+
if (!prefix) {
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const candidates = process.platform === 'win32'
|
|
168
|
+
? [
|
|
169
|
+
path.join(prefix, `${command}.cmd`),
|
|
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
|
+
]
|
|
177
|
+
|
|
178
|
+
for (const candidate of candidates) {
|
|
179
|
+
if (await pathExists(candidate)) {
|
|
180
|
+
return candidate
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return null
|
|
185
|
+
}
|
|
186
|
+
|
|
114
187
|
export async function findBashExecutable() {
|
|
115
188
|
if (await commandExists('bash')) {
|
|
116
189
|
return 'bash'
|