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.
Files changed (44) hide show
  1. package/MANUAL.md +13 -9
  2. package/README.md +163 -3
  3. package/RELEASE.md +11 -3
  4. package/assets/agents/developer-v2.md +86 -0
  5. package/assets/agents/developer.md +21 -23
  6. package/assets/agents/slopmachine-v2.md +219 -0
  7. package/assets/agents/slopmachine.md +56 -38
  8. package/assets/skills/beads-operations/SKILL.md +32 -31
  9. package/assets/skills/beads-operations-v2/SKILL.md +82 -0
  10. package/assets/skills/clarification-gate/SKILL.md +8 -1
  11. package/assets/skills/clarification-gate-v2/SKILL.md +74 -0
  12. package/assets/skills/developer-session-lifecycle/SKILL.md +45 -14
  13. package/assets/skills/developer-session-lifecycle-v2/SKILL.md +148 -0
  14. package/assets/skills/development-guidance-v2/SKILL.md +60 -0
  15. package/assets/skills/evaluation-triage-v2/SKILL.md +38 -0
  16. package/assets/skills/final-evaluation-orchestration/SKILL.md +9 -11
  17. package/assets/skills/final-evaluation-orchestration-v2/SKILL.md +57 -0
  18. package/assets/skills/get-overlays/SKILL.md +77 -6
  19. package/assets/skills/hardening-gate-v2/SKILL.md +64 -0
  20. package/assets/skills/integrated-verification-v2/SKILL.md +47 -0
  21. package/assets/skills/owner-evidence-discipline-v2/SKILL.md +15 -0
  22. package/assets/skills/planning-gate/SKILL.md +6 -4
  23. package/assets/skills/planning-gate-v2/SKILL.md +91 -0
  24. package/assets/skills/planning-guidance-v2/SKILL.md +100 -0
  25. package/assets/skills/remediation-guidance-v2/SKILL.md +31 -0
  26. package/assets/skills/report-output-discipline-v2/SKILL.md +15 -0
  27. package/assets/skills/scaffold-guidance-v2/SKILL.md +57 -0
  28. package/assets/skills/session-rollover-v2/SKILL.md +41 -0
  29. package/assets/skills/submission-packaging/SKILL.md +147 -115
  30. package/assets/skills/submission-packaging-v2/SKILL.md +142 -0
  31. package/assets/skills/verification-gates/SKILL.md +44 -16
  32. package/assets/skills/verification-gates-v2/SKILL.md +102 -0
  33. package/assets/slopmachine/backend-evaluation-prompt.md +9 -2
  34. package/assets/slopmachine/frontend-evaluation-prompt.md +9 -2
  35. package/assets/slopmachine/templates/AGENTS-v2.md +55 -0
  36. package/assets/slopmachine/templates/AGENTS.md +20 -17
  37. package/assets/slopmachine/tracker-init.js +104 -0
  38. package/assets/slopmachine/workflow-init-v2.js +99 -0
  39. package/package.json +1 -1
  40. package/src/constants.js +22 -3
  41. package/src/init.js +33 -28
  42. package/src/install.js +186 -140
  43. package/src/utils.js +19 -0
  44. package/assets/slopmachine/beads-init.js +0 -439
@@ -1,439 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'node:fs/promises'
4
- import path from 'node:path'
5
- import { spawn } from 'node:child_process'
6
-
7
- const targetInput = process.argv[2] || '.'
8
- const target = path.resolve(process.cwd(), targetInput)
9
- const bdCommand = process.env.BD_COMMAND || 'bd'
10
-
11
- function log(message) {
12
- console.log(`[beads-init] ${message}`)
13
- }
14
-
15
- function die(message) {
16
- console.error(`[beads-init] ERROR: ${message}`)
17
- process.exit(1)
18
- }
19
-
20
- function quoteShellArg(value) {
21
- if (/^[A-Za-z0-9_./:-]+$/.test(value)) {
22
- return value
23
- }
24
-
25
- return `'${value.replaceAll(`'`, `'\\''`)}'`
26
- }
27
-
28
- function run(command, args, options = {}) {
29
- return new Promise((resolve, reject) => {
30
- const child = spawn(command, args, {
31
- cwd: options.cwd,
32
- env: options.env || process.env,
33
- stdio: options.stdio || 'pipe',
34
- shell: false,
35
- })
36
-
37
- let stdout = ''
38
- let stderr = ''
39
-
40
- if (child.stdout) child.stdout.on('data', (chunk) => { stdout += chunk.toString() })
41
- if (child.stderr) child.stderr.on('data', (chunk) => { stderr += chunk.toString() })
42
-
43
- if (options.input !== undefined && child.stdin) {
44
- child.stdin.write(options.input)
45
- child.stdin.end()
46
- }
47
-
48
- child.on('error', reject)
49
- child.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }))
50
- })
51
- }
52
-
53
- async function commandExists(command) {
54
- const checker = process.platform === 'win32' ? 'where' : 'which'
55
- const result = await run(checker, [command])
56
- return result.code === 0
57
- }
58
-
59
- async function pathExists(targetPath) {
60
- try {
61
- await fs.access(targetPath)
62
- return true
63
- } catch {
64
- return false
65
- }
66
- }
67
-
68
- async function runBd(args, options = {}) {
69
- return run(bdCommand, args, { cwd: target, ...options })
70
- }
71
-
72
- async function runBdNoninteractive(args) {
73
- return run(bdCommand, args, {
74
- cwd: target,
75
- env: { ...process.env, CI: '1' },
76
- input: '',
77
- })
78
- }
79
-
80
- async function supportsInitFlag(flag) {
81
- const help = await run(bdCommand, ['init', '--help'])
82
- return `${help.stdout}${help.stderr}`.includes(flag)
83
- }
84
-
85
- async function chooseNoninteractiveInitModeFlag(isGitRepo) {
86
- if (!isGitRepo) return null
87
- const help = await run(bdCommand, ['init', '--help'])
88
- const text = `${help.stdout}${help.stderr}`
89
- if (text.includes('--setup-exclude')) return '--setup-exclude'
90
- if (text.includes('--stealth')) return '--stealth'
91
- return null
92
- }
93
-
94
- async function configureGitMergeDriver() {
95
- const attributesFile = path.join(target, '.git', 'info', 'attributes')
96
- const attributesLine = '.beads/*.jsonl merge=beads'
97
- const mergeDriverCommand = `${quoteShellArg(bdCommand)} merge %A %O %A %B`
98
-
99
- log('Configuring local git merge driver for Beads')
100
- let result = await run('git', ['-C', target, 'config', 'merge.beads.driver', mergeDriverCommand])
101
- if (result.code !== 0) die(result.stderr || 'Failed configuring git merge driver')
102
-
103
- await fs.mkdir(path.dirname(attributesFile), { recursive: true })
104
- if (!(await pathExists(attributesFile))) {
105
- await fs.writeFile(attributesFile, '', 'utf8')
106
- }
107
- const content = await fs.readFile(attributesFile, 'utf8')
108
- const lines = new Set(content.split(/\r?\n/))
109
- if (!lines.has(attributesLine)) {
110
- const prefix = content.endsWith('\n') || content.length === 0 ? '' : '\n'
111
- await fs.appendFile(attributesFile, `${prefix}${attributesLine}\n`, 'utf8')
112
- }
113
- }
114
-
115
- async function configureGitRoleMaintainer() {
116
- log('Setting local Beads role to maintainer')
117
- const result = await run('git', ['-C', target, 'config', 'beads.role', 'maintainer'])
118
- if (result.code !== 0) die(result.stderr || 'Failed setting beads.role maintainer')
119
- }
120
-
121
- function parseDoctorJson(raw) {
122
- if (!raw.trim()) {
123
- return { errors: -1, warnings: 0, known: 0, warnMissingJsonl: 0, warnDoltDirty: 0, warnSyncBranch: 0 }
124
- }
125
- let data
126
- try {
127
- data = JSON.parse(raw)
128
- } catch {
129
- return { errors: -1, warnings: 0, known: 0, warnMissingJsonl: 0, warnDoltDirty: 0, warnSyncBranch: 0 }
130
- }
131
-
132
- const checks = Array.isArray(data.checks) ? data.checks : []
133
- let errors = 0
134
- let warnings = 0
135
- let known = 0
136
- let warnMissingJsonl = 0
137
- let warnDoltDirty = 0
138
- let warnSyncBranch = 0
139
-
140
- for (const check of checks) {
141
- const name = `${check.name || ''}`.trim()
142
- const status = check.status
143
- const text = `${check.message || ''} ${check.detail || ''}`.toLowerCase()
144
-
145
- if (status === 'error') {
146
- errors += 1
147
- if (text.includes('database not found: beads')) known = 1
148
- if (name === 'Dolt Schema' && ['issues', 'dependencies', 'config', 'labels', 'events'].every((term) => text.includes(term))) {
149
- known = 1
150
- }
151
- }
152
-
153
- if (status === 'warning') {
154
- warnings += 1
155
- if (name === 'Dolt-JSONL Sync' && (text.includes('could not read jsonl file') || text.includes('no such file or directory'))) {
156
- warnMissingJsonl = 1
157
- }
158
- if (name === 'Dolt Status' || name === 'Dolt Locks') {
159
- warnDoltDirty = 1
160
- }
161
- if (name === 'Sync Branch Config' && text.includes('not configured')) {
162
- warnSyncBranch = 1
163
- }
164
- }
165
- }
166
-
167
- return { errors, warnings, known, warnMissingJsonl, warnDoltDirty, warnSyncBranch }
168
- }
169
-
170
- async function getActiveDbName() {
171
- const metadataPath = path.join(target, '.beads', 'metadata.json')
172
- if (await pathExists(metadataPath)) {
173
- try {
174
- const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf8'))
175
- if (metadata.dolt_database) {
176
- return metadata.dolt_database
177
- }
178
- } catch {
179
- // ignore and fall through
180
- }
181
- }
182
-
183
- const showDatabases = await runBd(['sql', '-q', 'SHOW DATABASES;'])
184
- const names = `${showDatabases.stdout}${showDatabases.stderr}`
185
- .split(/\r?\n/)
186
- .map((line) => line.trim())
187
- .filter((line) => line && !/^[-]+$/.test(line))
188
- .filter((line) => !['database', 'information_schema', '(2 rows)', '(1 row)'].includes(line.toLowerCase()))
189
- .filter((line) => !(line.startsWith('(') && line.endsWith('rows)')))
190
-
191
- return names.find((name) => !['beads', 'information_schema'].includes(name)) || ''
192
- }
193
-
194
- async function knownDoltFix(sourceDb) {
195
- if (!sourceDb) return false
196
- log(`Applying known Dolt doctor fix using source DB '${sourceDb}'`)
197
- let result = await runBd(['sql', '-q', 'CREATE DATABASE IF NOT EXISTS beads;'])
198
- if (result.code !== 0) return false
199
- for (const table of ['issues', 'dependencies', 'config', 'labels', 'events']) {
200
- result = await runBd(['sql', '-q', `CREATE TABLE IF NOT EXISTS beads.${table} LIKE \`${sourceDb}\`.${table};`])
201
- if (result.code !== 0) return false
202
- }
203
- return true
204
- }
205
-
206
- async function ensureBeadsJsonl() {
207
- const jsonlPath = path.join(target, '.beads', 'beads.jsonl')
208
- if (await pathExists(jsonlPath)) return true
209
- log('Ensuring .beads/beads.jsonl exists')
210
- const exportResult = await runBd(['export', '--force', '-o', '.beads/beads.jsonl'])
211
- if (exportResult.code === 0) return true
212
- await fs.writeFile(jsonlPath, '', 'utf8')
213
- return true
214
- }
215
-
216
- async function getPendingDoltSchemaCount() {
217
- const result = await runBd(['--json', 'sql', 'SELECT COUNT(*) AS pending FROM beads.dolt_status;'])
218
- if (result.code !== 0 || !result.stdout.trim()) return 0
219
- try {
220
- const data = JSON.parse(result.stdout)
221
- if (Array.isArray(data) && data[0] && typeof data[0] === 'object') {
222
- return Number(data[0].pending || 0)
223
- }
224
- } catch {
225
- return 0
226
- }
227
- return 0
228
- }
229
-
230
- async function commitPendingDoltSchema(activeDb) {
231
- const pending = await getPendingDoltSchemaCount()
232
- if (pending <= 0) return true
233
- log("Committing pending Dolt schema changes in fallback 'beads' DB")
234
-
235
- let switched = false
236
- if (activeDb && activeDb !== 'beads') {
237
- const setResult = await runBd(['dolt', 'set', 'database', 'beads'])
238
- if (setResult.code !== 0) return false
239
- switched = true
240
- }
241
-
242
- const commitResult = await runBd(['vc', 'commit', '-m', 'Auto-commit initial fallback schema tables for bd doctor'])
243
-
244
- if (switched) {
245
- await runBd(['dolt', 'set', 'database', activeDb])
246
- }
247
-
248
- if (commitResult.code === 0) return true
249
- const lower = `${commitResult.stdout}${commitResult.stderr}`.toLowerCase()
250
- if (lower.includes('nothing to commit') || lower.includes('no changes') || lower.includes('clean working set')) {
251
- return true
252
- }
253
- console.error(commitResult.stdout || commitResult.stderr)
254
- return false
255
- }
256
-
257
- async function configureSyncBranchReconciled() {
258
- const syncBranchName = 'beads-sync'
259
- log(`Reconciling sync.branch (${syncBranchName})`)
260
-
261
- let result = await runBd(['config', 'set', 'sync.branch', syncBranchName])
262
- if (result.code !== 0) return false
263
-
264
- result = await runBdNoninteractive(['migrate', 'sync', syncBranchName, '--yes'])
265
- if (result.code !== 0) {
266
- result = await runBdNoninteractive(['migrate', 'sync', syncBranchName])
267
- if (result.code !== 0) {
268
- if (`${result.stdout}${result.stderr}`.trim()) {
269
- console.error(`${result.stdout}${result.stderr}`.trim())
270
- }
271
- log("WARNING: 'bd migrate sync' failed; validating config directly")
272
- }
273
- }
274
-
275
- const configResult = await runBd(['config', 'get', 'sync.branch'])
276
- const combined = `${configResult.stdout}${configResult.stderr}`
277
- if (!combined.includes(syncBranchName)) {
278
- if (combined.trim()) console.error(combined.trim())
279
- return false
280
- }
281
-
282
- log(`Verified sync.branch: ${combined.trim()}`)
283
- return true
284
- }
285
-
286
- async function installGitHooksNonfatal() {
287
- log('Installing Beads git hooks')
288
- const result = await runBd(['hooks', 'install'])
289
- if (result.code === 0) {
290
- if (`${result.stdout}`.trim()) {
291
- console.log(result.stdout.trim())
292
- }
293
- return true
294
- }
295
- if (`${result.stdout}${result.stderr}`.trim()) {
296
- console.error(`${result.stdout}${result.stderr}`.trim())
297
- }
298
- return false
299
- }
300
-
301
- async function main() {
302
- const bdAvailable = bdCommand !== 'bd' ? await pathExists(bdCommand) : await commandExists('bd')
303
- if (!bdAvailable) {
304
- die(`'${bdCommand}' is not available. Install Beads first.`)
305
- }
306
-
307
- if (!(await pathExists(target))) {
308
- die(`Target directory '${targetInput}' does not exist or is not accessible.`)
309
- }
310
-
311
- const gitCheck = await run('git', ['-C', target, 'rev-parse', '--is-inside-work-tree'])
312
- const isGitRepo = gitCheck.code === 0
313
-
314
- log(`Target: ${target}`)
315
- log(`Mode: ${isGitRepo ? 'git repository' : 'non-git directory'}`)
316
-
317
- const beadsDir = path.join(target, '.beads')
318
- if (!(await pathExists(beadsDir))) {
319
- if (isGitRepo) {
320
- await configureGitRoleMaintainer()
321
- }
322
-
323
- const initArgs = ['init', '--quiet']
324
- const initModeFlag = await chooseNoninteractiveInitModeFlag(isGitRepo)
325
- if (initModeFlag) initArgs.push(initModeFlag)
326
- if (await supportsInitFlag('--skip-hooks')) initArgs.push('--skip-hooks')
327
-
328
- log(`Running non-interactive 'bd ${initArgs.join(' ')}' (CI=1, stdin=/dev/null)`)
329
- const initResult = await runBdNoninteractive(initArgs)
330
- if (initResult.code !== 0) {
331
- console.error(`${initResult.stdout}${initResult.stderr}`.trim())
332
- die("'bd init' failed. Review output above.")
333
- }
334
-
335
- if (isGitRepo) {
336
- await configureGitMergeDriver()
337
- }
338
-
339
- if (initResult.stdout.trim()) {
340
- console.log(initResult.stdout.trim())
341
- }
342
- } else {
343
- log('Found existing .beads; skipping init to avoid destructive re-initialization')
344
- if (isGitRepo) {
345
- await configureGitMergeDriver()
346
- }
347
- }
348
-
349
- let hooksInstallStatus = 'not-applicable'
350
- if (isGitRepo) {
351
- if (await installGitHooksNonfatal()) {
352
- hooksInstallStatus = 'installed'
353
- } else {
354
- hooksInstallStatus = 'failed'
355
- log("WARNING: 'bd hooks install' failed; continuing to doctor")
356
- }
357
-
358
- if (!(await configureSyncBranchReconciled())) {
359
- die('Failed to reconcile sync.branch to beads-sync')
360
- }
361
- }
362
-
363
- const doctorBefore = await runBd(['doctor'])
364
- const doctorJson = await runBd(['doctor', '--json'])
365
- const parsedBefore = parseDoctorJson(doctorJson.stdout)
366
- if (parsedBefore.errors === -1) {
367
- console.log(doctorBefore.stdout)
368
- die("Unable to parse 'bd doctor --json'. Run 'bd doctor --verbose' manually.")
369
- }
370
-
371
- if (parsedBefore.errors > 0 && parsedBefore.known === 1) {
372
- const sourceDb = await getActiveDbName()
373
- if (!sourceDb) {
374
- console.log(doctorBefore.stdout)
375
- die('Detected known Dolt issue but could not determine active DB name from .beads/metadata.json or SHOW DATABASES.')
376
- }
377
- if (!(await knownDoltFix(sourceDb))) {
378
- console.log(doctorBefore.stdout)
379
- die('Detected known Dolt issue, but auto-fix failed while creating fallback schema.')
380
- }
381
- }
382
-
383
- const doctorPostFix = await runBd(['doctor', '--json'])
384
- const parsedPostFix = parseDoctorJson(doctorPostFix.stdout)
385
- if (parsedPostFix.warnings === -1 || parsedPostFix.known === -1) {
386
- console.log(doctorBefore.stdout)
387
- die("Unable to parse post-fix 'bd doctor --json'.")
388
- }
389
-
390
- if (parsedPostFix.warnMissingJsonl === 1) {
391
- if (!(await ensureBeadsJsonl())) {
392
- die('Failed to ensure .beads/beads.jsonl exists')
393
- }
394
- }
395
-
396
- if (parsedPostFix.warnDoltDirty === 1) {
397
- const activeDb = await getActiveDbName()
398
- if (!(await commitPendingDoltSchema(activeDb))) {
399
- die('Failed to auto-commit pending Dolt schema changes')
400
- }
401
- }
402
-
403
- if (parsedPostFix.warnSyncBranch === 1) {
404
- if (!(await configureSyncBranchReconciled())) {
405
- die('Failed to configure sync.branch')
406
- }
407
- }
408
-
409
- const doctorAfter = await runBd(['doctor'])
410
- const doctorAfterJson = await runBd(['doctor', '--json'])
411
- const parsedAfter = parseDoctorJson(doctorAfterJson.stdout)
412
-
413
- if (doctorAfter.stdout.trim()) {
414
- console.log(doctorAfter.stdout.trim())
415
- }
416
- if (parsedAfter.errors === -1) {
417
- die("Initialization finished, but doctor JSON parse failed. Run: bd doctor --verbose")
418
- }
419
- if (parsedAfter.warnings === -1 || parsedAfter.known === -1) {
420
- die("Initialization finished, but post-init warning parse failed. Run: bd doctor --verbose")
421
- }
422
- if (parsedAfter.errors > 0) {
423
- die(`'bd doctor' still reports ${parsedAfter.errors} error(s). Run 'bd doctor --verbose' and inspect .beads/metadata.json.`)
424
- }
425
- if (isGitRepo && parsedAfter.warnSyncBranch === 1) {
426
- die("'bd doctor' still reports Sync Branch Config warning: sync-branch not configured.")
427
- }
428
-
429
- log('Success: bd initialized and doctor reports zero errors')
430
- if (hooksInstallStatus === 'installed') {
431
- log('Hooks: Beads git hooks installed')
432
- } else if (hooksInstallStatus === 'failed') {
433
- log('Hooks: WARNING - installation failed (non-fatal)')
434
- }
435
- }
436
-
437
- main().catch((error) => {
438
- die(error instanceof Error ? error.message : String(error))
439
- })