theslopmachine 0.6.2 → 0.7.1
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 +21 -6
- package/README.md +55 -7
- package/RELEASE.md +16 -1
- package/assets/agents/developer.md +41 -1
- package/assets/agents/slopmachine-claude.md +101 -60
- package/assets/agents/slopmachine.md +40 -17
- package/assets/claude/agents/developer.md +42 -5
- package/assets/skills/clarification-gate/SKILL.md +25 -5
- package/assets/skills/claude-worker-management/SKILL.md +290 -57
- package/assets/skills/developer-session-lifecycle/SKILL.md +83 -38
- package/assets/skills/development-guidance/SKILL.md +21 -1
- package/assets/skills/evaluation-triage/SKILL.md +34 -23
- package/assets/skills/final-evaluation-orchestration/SKILL.md +88 -50
- package/assets/skills/hardening-gate/SKILL.md +17 -3
- package/assets/skills/integrated-verification/SKILL.md +3 -3
- package/assets/skills/planning-gate/SKILL.md +32 -3
- package/assets/skills/planning-guidance/SKILL.md +72 -13
- package/assets/skills/retrospective-analysis/SKILL.md +2 -2
- package/assets/skills/scaffold-guidance/SKILL.md +129 -124
- package/assets/skills/submission-packaging/SKILL.md +33 -27
- package/assets/skills/verification-gates/SKILL.md +44 -14
- package/assets/slopmachine/backend-evaluation-prompt.md +1 -1
- package/assets/slopmachine/frontend-evaluation-prompt.md +5 -5
- package/assets/slopmachine/scaffold-playbooks/android-kotlin-compose.md +81 -0
- package/assets/slopmachine/scaffold-playbooks/android-kotlin-views.md +191 -0
- package/assets/slopmachine/scaffold-playbooks/android-native-java.md +203 -0
- package/assets/slopmachine/scaffold-playbooks/angular-default.md +181 -0
- package/assets/slopmachine/scaffold-playbooks/backend-baseline.md +142 -0
- package/assets/slopmachine/scaffold-playbooks/backend-family-matrix.md +80 -0
- package/assets/slopmachine/scaffold-playbooks/database-module-matrix.md +80 -0
- package/assets/slopmachine/scaffold-playbooks/django-default.md +166 -0
- package/assets/slopmachine/scaffold-playbooks/docker-baseline.md +189 -0
- package/assets/slopmachine/scaffold-playbooks/docker-shared-contract.md +334 -0
- package/assets/slopmachine/scaffold-playbooks/electron-vite-default.md +124 -0
- package/assets/slopmachine/scaffold-playbooks/expo-react-native-default.md +73 -0
- package/assets/slopmachine/scaffold-playbooks/fastapi-default.md +134 -0
- package/assets/slopmachine/scaffold-playbooks/frontend-baseline.md +160 -0
- package/assets/slopmachine/scaffold-playbooks/frontend-family-matrix.md +134 -0
- package/assets/slopmachine/scaffold-playbooks/generic-unknown-tech-guide.md +136 -0
- package/assets/slopmachine/scaffold-playbooks/go-chi-default.md +160 -0
- package/assets/slopmachine/scaffold-playbooks/ios-linux-portable.md +93 -0
- package/assets/slopmachine/scaffold-playbooks/ios-native-objective-c.md +151 -0
- package/assets/slopmachine/scaffold-playbooks/ios-native-swift.md +188 -0
- package/assets/slopmachine/scaffold-playbooks/laravel-default.md +216 -0
- package/assets/slopmachine/scaffold-playbooks/livewire-default.md +265 -0
- package/assets/slopmachine/scaffold-playbooks/overlay-module-matrix.md +130 -0
- package/assets/slopmachine/scaffold-playbooks/platform-family-matrix.md +79 -0
- package/assets/slopmachine/scaffold-playbooks/selection-matrix.md +72 -0
- package/assets/slopmachine/scaffold-playbooks/spring-boot-default.md +182 -0
- package/assets/slopmachine/scaffold-playbooks/tauri-default.md +80 -0
- package/assets/slopmachine/scaffold-playbooks/vue-vite-default.md +162 -0
- package/assets/slopmachine/scaffold-playbooks/web-default.md +96 -0
- package/assets/slopmachine/templates/AGENTS.md +41 -3
- package/assets/slopmachine/templates/CLAUDE.md +111 -0
- package/assets/slopmachine/test-coverage-prompt.md +561 -0
- package/assets/slopmachine/utils/claude_create_session.mjs +3 -2
- package/assets/slopmachine/utils/claude_live_channel.mjs +188 -0
- package/assets/slopmachine/utils/claude_live_common.mjs +411 -0
- package/assets/slopmachine/utils/claude_live_hook.py +47 -0
- package/assets/slopmachine/utils/claude_live_launch.mjs +187 -0
- package/assets/slopmachine/utils/claude_live_status.mjs +25 -0
- package/assets/slopmachine/utils/claude_live_stop.mjs +46 -0
- package/assets/slopmachine/utils/claude_live_turn.mjs +277 -0
- package/assets/slopmachine/utils/claude_resume_session.mjs +3 -2
- package/assets/slopmachine/utils/claude_wait_for_rate_limit_reset.mjs +23 -0
- package/assets/slopmachine/utils/claude_wait_for_rate_limit_reset.sh +5 -0
- package/assets/slopmachine/utils/claude_worker_common.mjs +361 -4
- package/assets/slopmachine/utils/cleanup_delivery_artifacts.py +4 -0
- package/assets/slopmachine/utils/export_ai_session.mjs +1 -1
- package/assets/slopmachine/utils/normalize_claude_session.py +153 -0
- package/assets/slopmachine/utils/package_claude_session.mjs +123 -0
- package/assets/slopmachine/utils/prepare_strict_audit_workspace.mjs +65 -0
- package/package.json +1 -1
- package/src/constants.js +42 -3
- package/src/init.js +173 -28
- package/src/install.js +156 -8
- package/src/send-data.js +56 -57
package/src/install.js
CHANGED
|
@@ -62,6 +62,41 @@ function getUnixBeadsBinDirs() {
|
|
|
62
62
|
]
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function getUnixCommonBinDirs() {
|
|
66
|
+
const home = getHomeDir()
|
|
67
|
+
return [
|
|
68
|
+
path.join(home, '.local', 'bin'),
|
|
69
|
+
path.join(home, '.cargo', 'bin'),
|
|
70
|
+
'/opt/homebrew/bin',
|
|
71
|
+
'/usr/local/bin',
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getWindowsCommonBinDirs() {
|
|
76
|
+
if (process.platform !== 'win32') {
|
|
77
|
+
return []
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const home = getHomeDir()
|
|
81
|
+
return [
|
|
82
|
+
process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Programs') : null,
|
|
83
|
+
path.join(home, '.local', 'bin'),
|
|
84
|
+
path.join(home, '.cargo', 'bin'),
|
|
85
|
+
].filter(Boolean)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getCommonCommandCandidates(command) {
|
|
89
|
+
if (process.platform === 'win32') {
|
|
90
|
+
return getWindowsCommonBinDirs().flatMap((dir) => [
|
|
91
|
+
path.join(dir, `${command}.exe`),
|
|
92
|
+
path.join(dir, `${command}.cmd`),
|
|
93
|
+
path.join(dir, command),
|
|
94
|
+
])
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return getUnixCommonBinDirs().map((dir) => path.join(dir, command))
|
|
98
|
+
}
|
|
99
|
+
|
|
65
100
|
function getWindowsBeadsBinDirs() {
|
|
66
101
|
if (process.platform !== 'win32') {
|
|
67
102
|
return []
|
|
@@ -308,14 +343,57 @@ async function installBeadsOnUnix() {
|
|
|
308
343
|
return result
|
|
309
344
|
}
|
|
310
345
|
|
|
346
|
+
function getVersionArgs(command) {
|
|
347
|
+
if (command === 'tmux') {
|
|
348
|
+
return ['-V']
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (command === 'zip') {
|
|
352
|
+
return ['-v']
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return ['--version']
|
|
356
|
+
}
|
|
357
|
+
|
|
311
358
|
async function getCommandVersion(command, args = ['--version']) {
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
359
|
+
const resolved = await resolveCommand(command, {
|
|
360
|
+
additionalCandidates: getCommonCommandCandidates(command),
|
|
361
|
+
preferCandidates: true,
|
|
362
|
+
})
|
|
363
|
+
if (!resolved) return null
|
|
364
|
+
const versionArgs = args.length === 1 && args[0] === '--version' ? getVersionArgs(command) : args
|
|
365
|
+
const result = await runCommand(resolved, versionArgs)
|
|
315
366
|
if (result.code !== 0) return null
|
|
316
367
|
return (result.stdout || result.stderr).trim()
|
|
317
368
|
}
|
|
318
369
|
|
|
370
|
+
function extractSemver(value) {
|
|
371
|
+
const match = String(value || '').match(/\d+\.\d+\.\d+/)
|
|
372
|
+
return match ? match[0] : null
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function compareSemver(left, right) {
|
|
376
|
+
const leftParts = left.split('.').map((part) => Number.parseInt(part, 10))
|
|
377
|
+
const rightParts = right.split('.').map((part) => Number.parseInt(part, 10))
|
|
378
|
+
const length = Math.max(leftParts.length, rightParts.length)
|
|
379
|
+
for (let index = 0; index < length; index += 1) {
|
|
380
|
+
const a = leftParts[index] || 0
|
|
381
|
+
const b = rightParts[index] || 0
|
|
382
|
+
if (a > b) return 1
|
|
383
|
+
if (a < b) return -1
|
|
384
|
+
}
|
|
385
|
+
return 0
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function isVersionAtLeast(currentVersion, minimumVersion) {
|
|
389
|
+
const current = extractSemver(currentVersion)
|
|
390
|
+
const minimum = extractSemver(minimumVersion)
|
|
391
|
+
if (!current || !minimum) {
|
|
392
|
+
return false
|
|
393
|
+
}
|
|
394
|
+
return compareSemver(current, minimum) >= 0
|
|
395
|
+
}
|
|
396
|
+
|
|
319
397
|
async function getPythonVersion() {
|
|
320
398
|
const python3Version = await getCommandVersion('python3')
|
|
321
399
|
if (python3Version) {
|
|
@@ -376,6 +454,30 @@ async function tryInstallCoreDependency(name) {
|
|
|
376
454
|
winget: ['winget', ['install', '--id', 'Python.Python.3.11', '-e']],
|
|
377
455
|
choco: ['choco', ['install', 'python', '--version=3.11.7', '-y']],
|
|
378
456
|
},
|
|
457
|
+
bash: {
|
|
458
|
+
brew: ['brew', ['install', 'bash']],
|
|
459
|
+
apt: ['sudo', ['apt-get', 'install', '-y', 'bash']],
|
|
460
|
+
dnf: ['sudo', ['dnf', 'install', '-y', 'bash']],
|
|
461
|
+
},
|
|
462
|
+
curl: {
|
|
463
|
+
brew: ['brew', ['install', 'curl']],
|
|
464
|
+
apt: ['sudo', ['apt-get', 'install', '-y', 'curl']],
|
|
465
|
+
dnf: ['sudo', ['dnf', 'install', '-y', 'curl']],
|
|
466
|
+
},
|
|
467
|
+
tar: {
|
|
468
|
+
apt: ['sudo', ['apt-get', 'install', '-y', 'tar']],
|
|
469
|
+
dnf: ['sudo', ['dnf', 'install', '-y', 'tar']],
|
|
470
|
+
},
|
|
471
|
+
tmux: {
|
|
472
|
+
brew: ['brew', ['install', 'tmux']],
|
|
473
|
+
apt: ['sudo', ['apt-get', 'install', '-y', 'tmux']],
|
|
474
|
+
dnf: ['sudo', ['dnf', 'install', '-y', 'tmux']],
|
|
475
|
+
},
|
|
476
|
+
zip: {
|
|
477
|
+
brew: ['brew', ['install', 'zip']],
|
|
478
|
+
apt: ['sudo', ['apt-get', 'install', '-y', 'zip']],
|
|
479
|
+
dnf: ['sudo', ['dnf', 'install', '-y', 'zip']],
|
|
480
|
+
},
|
|
379
481
|
}
|
|
380
482
|
|
|
381
483
|
const platformKey = managers.brew
|
|
@@ -405,19 +507,27 @@ async function tryInstallCoreDependency(name) {
|
|
|
405
507
|
async function ensureLatestOpencode(requiredVersion) {
|
|
406
508
|
const npmVersion = await getGlobalNpmPackageVersion('opencode-ai')
|
|
407
509
|
const commandPath = await resolveCommand('opencode')
|
|
510
|
+
const commandVersion = await getCommandVersion('opencode')
|
|
408
511
|
|
|
409
512
|
if (npmVersion) {
|
|
410
513
|
log(`opencode detected via global npm package: opencode-ai@${npmVersion}`)
|
|
411
|
-
if (requiredVersion
|
|
412
|
-
|
|
514
|
+
if (!requiredVersion || isVersionAtLeast(npmVersion, requiredVersion)) {
|
|
515
|
+
log(`Using existing opencode-ai@${npmVersion}`)
|
|
516
|
+
return
|
|
413
517
|
}
|
|
414
|
-
|
|
518
|
+
warn(`opencode version ${npmVersion} is below minimum supported version ${requiredVersion}`)
|
|
519
|
+
log('Updating opencode-ai via npm to reach the minimum supported version')
|
|
415
520
|
} else if (commandPath) {
|
|
416
521
|
log(`opencode command detected at ${commandPath}`)
|
|
417
|
-
|
|
522
|
+
if (!requiredVersion || (commandVersion && isVersionAtLeast(commandVersion, requiredVersion))) {
|
|
523
|
+
log(`Using existing opencode command${commandVersion ? `: ${commandVersion}` : ''}`)
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
warn(`opencode command version${commandVersion ? ` ${commandVersion}` : ''} is below minimum supported version ${requiredVersion}`)
|
|
527
|
+
log('Installing opencode-ai globally via npm so theslopmachine can manage a supported opencode install')
|
|
418
528
|
} else {
|
|
419
529
|
warn('opencode is not installed or not available in PATH')
|
|
420
|
-
log('Installing opencode-ai
|
|
530
|
+
log('Installing opencode-ai via npm')
|
|
421
531
|
}
|
|
422
532
|
|
|
423
533
|
const result = await tryInstallCoreDependency('opencode')
|
|
@@ -587,6 +697,31 @@ async function checkInitShellSupport() {
|
|
|
587
697
|
}
|
|
588
698
|
}
|
|
589
699
|
|
|
700
|
+
async function checkClaudeCli() {
|
|
701
|
+
const claudeVersion = await getCommandVersion('claude')
|
|
702
|
+
if (!claudeVersion) {
|
|
703
|
+
warn('Claude CLI is required for the `slopmachine-claude` backend and was not found in PATH. Install Claude Code before using the Claude-backed workflow.')
|
|
704
|
+
return
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
log(`claude detected: ${claudeVersion}`)
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
async function checkArchiveSupport() {
|
|
711
|
+
if (process.platform === 'win32') {
|
|
712
|
+
const shell = await resolveCommand('pwsh') || await resolveCommand('powershell')
|
|
713
|
+
if (!shell) {
|
|
714
|
+
warn('PowerShell is required on Windows for archive packaging paths such as send-data and Claude session packaging.')
|
|
715
|
+
return
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
log(`archive shell detected: ${shell}`)
|
|
719
|
+
return
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
await ensureDependency({ name: 'zip', checkCommand: 'zip', requiredVersion: null, installable: true })
|
|
723
|
+
}
|
|
724
|
+
|
|
590
725
|
async function findLegacyInstallItems(paths) {
|
|
591
726
|
const legacyItems = []
|
|
592
727
|
const scanTargets = [
|
|
@@ -880,11 +1015,24 @@ export async function runInstall() {
|
|
|
880
1015
|
log(`Configuring theslopmachine in ${paths.home}`)
|
|
881
1016
|
|
|
882
1017
|
section('Dependencies')
|
|
1018
|
+
await ensureDependency({ name: 'npm', checkCommand: 'npm', requiredVersion: null, installable: false })
|
|
883
1019
|
await ensureDependency({ name: 'git', checkCommand: 'git', installable: true })
|
|
884
1020
|
await ensureDependency({ name: 'python3', checkCommand: 'python3', installable: true })
|
|
1021
|
+
if (process.platform !== 'win32') {
|
|
1022
|
+
await ensureDependency({ name: 'bash', checkCommand: 'bash', installable: true })
|
|
1023
|
+
}
|
|
1024
|
+
if (process.platform !== 'win32') {
|
|
1025
|
+
await ensureDependency({ name: 'curl', checkCommand: 'curl', installable: true })
|
|
1026
|
+
}
|
|
1027
|
+
if (process.platform === 'linux') {
|
|
1028
|
+
await ensureDependency({ name: 'tar', checkCommand: 'tar', installable: true })
|
|
1029
|
+
}
|
|
885
1030
|
await ensureDependency({ name: 'opencode', checkCommand: 'opencode', requiredVersion: OPCODE_VERSION, installable: true })
|
|
886
1031
|
await ensureDependency({ name: 'beads_rust (br)', checkCommand: 'br', requiredVersion: null, installable: true })
|
|
887
1032
|
await checkDocker()
|
|
1033
|
+
await ensureDependency({ name: 'tmux', checkCommand: 'tmux', requiredVersion: null, installable: process.platform !== 'win32' })
|
|
1034
|
+
await checkClaudeCli()
|
|
1035
|
+
await checkArchiveSupport()
|
|
888
1036
|
await checkInitShellSupport()
|
|
889
1037
|
|
|
890
1038
|
section('Legacy Cleanup')
|
package/src/send-data.js
CHANGED
|
@@ -23,7 +23,7 @@ const RETROSPECTIVE_FILENAMES = (runId) => [
|
|
|
23
23
|
`improvement-actions-${runId}.md`,
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const EVALUATION_REPORTS_DIR = '.tmp'
|
|
27
27
|
|
|
28
28
|
function sha256Hex(buffer) {
|
|
29
29
|
return createHash('sha256').update(buffer).digest('hex')
|
|
@@ -368,7 +368,7 @@ async function buildManifest(stagingDir, projectId, runId, label, workspaceRoot)
|
|
|
368
368
|
project_id: projectId,
|
|
369
369
|
run_id: runId,
|
|
370
370
|
label,
|
|
371
|
-
bundle_categories: ['sessions', '
|
|
371
|
+
bundle_categories: ['sessions', 'evaluation', 'retrospective', 'beads', 'metadata'],
|
|
372
372
|
files,
|
|
373
373
|
file_count: files.length,
|
|
374
374
|
total_bytes: totalBytes,
|
|
@@ -386,67 +386,33 @@ async function buildManifest(stagingDir, projectId, runId, label, workspaceRoot)
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
async function exportDeveloperSessionArtifacts(session, workspaceRoot, stagingDir) {
|
|
389
|
+
if (session.backend === 'claude' || session.backend === 'claude-live') {
|
|
390
|
+
throw new Error(`Claude-backed sessions should be exported through the shared project archive, not per-session packaging (${session.label})`)
|
|
391
|
+
}
|
|
392
|
+
|
|
389
393
|
const utilsDir = path.join(buildPaths().slopmachineDir, 'utils')
|
|
390
|
-
const exportScript = path.join(utilsDir, 'export_ai_session.mjs')
|
|
391
394
|
const stripScript = path.join(utilsDir, 'strip_session_parent.py')
|
|
392
|
-
const convertScript = path.join(utilsDir, 'convert_exported_ai_session.mjs')
|
|
393
|
-
const converterPythonScript = path.join(utilsDir, 'convert_ai_session.py')
|
|
394
395
|
|
|
395
|
-
const
|
|
396
|
-
const rootSessionFile = path.join(stagingDir, `session-${exportNumber}.json`)
|
|
397
|
-
const convertedSessionFile = path.join(stagingDir, 'sessions', `${session.label}.json`)
|
|
396
|
+
const cleanedSessionFile = path.join(stagingDir, 'sessions', `${session.label}.json`)
|
|
398
397
|
const rawSessionFile = path.join(stagingDir, `.session-export-${session.label}.raw`)
|
|
399
398
|
|
|
400
|
-
await
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const exportResult = await runCommand(process.execPath, [
|
|
404
|
-
exportScript,
|
|
405
|
-
'--backend',
|
|
406
|
-
'claude',
|
|
407
|
-
'--cwd',
|
|
408
|
-
workspaceRoot,
|
|
409
|
-
'--session-id',
|
|
410
|
-
session.sessionId,
|
|
411
|
-
'--output',
|
|
412
|
-
rootSessionFile,
|
|
413
|
-
])
|
|
414
|
-
|
|
415
|
-
if (exportResult.code !== 0) {
|
|
416
|
-
throw new Error(`Failed to export session ${session.label}: ${(exportResult.stderr || exportResult.stdout).trim()}`)
|
|
417
|
-
}
|
|
418
|
-
} else {
|
|
419
|
-
const opencodeCommand = await resolveCommand('opencode')
|
|
420
|
-
if (!opencodeCommand) {
|
|
421
|
-
throw new Error('Unable to find `opencode` for developer session export')
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
await exportOpenCodeSession(opencodeCommand, session.sessionId, rawSessionFile, workspaceRoot)
|
|
425
|
-
|
|
426
|
-
const stripResult = await runCommand('python3', [
|
|
427
|
-
stripScript,
|
|
428
|
-
rawSessionFile,
|
|
429
|
-
'--output',
|
|
430
|
-
rootSessionFile,
|
|
431
|
-
])
|
|
432
|
-
|
|
433
|
-
if (stripResult.code !== 0) {
|
|
434
|
-
throw new Error(`Failed to clean exported session ${session.label}: ${(stripResult.stderr || stripResult.stdout).trim()}`)
|
|
435
|
-
}
|
|
399
|
+
const opencodeCommand = await resolveCommand('opencode')
|
|
400
|
+
if (!opencodeCommand) {
|
|
401
|
+
throw new Error('Unable to find `opencode` for developer session export')
|
|
436
402
|
}
|
|
437
403
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
404
|
+
await ensureDir(path.dirname(cleanedSessionFile))
|
|
405
|
+
await exportOpenCodeSession(opencodeCommand, session.sessionId, rawSessionFile, workspaceRoot)
|
|
406
|
+
|
|
407
|
+
const stripResult = await runCommand('python3', [
|
|
408
|
+
stripScript,
|
|
409
|
+
rawSessionFile,
|
|
444
410
|
'--output',
|
|
445
|
-
|
|
411
|
+
cleanedSessionFile,
|
|
446
412
|
])
|
|
447
413
|
|
|
448
|
-
if (
|
|
449
|
-
throw new Error(`Failed to
|
|
414
|
+
if (stripResult.code !== 0) {
|
|
415
|
+
throw new Error(`Failed to clean exported session ${session.label}: ${(stripResult.stderr || stripResult.stdout).trim()}`)
|
|
450
416
|
}
|
|
451
417
|
|
|
452
418
|
if (await pathExists(rawSessionFile)) {
|
|
@@ -454,6 +420,33 @@ async function exportDeveloperSessionArtifacts(session, workspaceRoot, stagingDi
|
|
|
454
420
|
}
|
|
455
421
|
}
|
|
456
422
|
|
|
423
|
+
async function exportClaudeProjectArtifacts(claudeSessions, workspaceRoot, stagingDir) {
|
|
424
|
+
if (claudeSessions.length === 0) {
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const utilsDir = path.join(buildPaths().slopmachineDir, 'utils')
|
|
429
|
+
const packageClaudeSessionScript = path.join(utilsDir, 'package_claude_session.mjs')
|
|
430
|
+
const outputPath = path.join(stagingDir, 'claude-sessions.zip')
|
|
431
|
+
const anchorSession = claudeSessions[0]
|
|
432
|
+
|
|
433
|
+
const packageResult = await runCommand(process.execPath, [
|
|
434
|
+
packageClaudeSessionScript,
|
|
435
|
+
'--cwd',
|
|
436
|
+
workspaceRoot,
|
|
437
|
+
'--session-id',
|
|
438
|
+
anchorSession.sessionId,
|
|
439
|
+
'--label',
|
|
440
|
+
'claude-sessions',
|
|
441
|
+
'--output',
|
|
442
|
+
outputPath,
|
|
443
|
+
])
|
|
444
|
+
|
|
445
|
+
if (packageResult.code !== 0) {
|
|
446
|
+
throw new Error(`Failed to package Claude project sessions: ${(packageResult.stderr || packageResult.stdout).trim()}`)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
457
450
|
async function createZipArchive(stagingDir, outputPath) {
|
|
458
451
|
await ensureDir(path.dirname(outputPath))
|
|
459
452
|
|
|
@@ -550,15 +543,21 @@ async function stageSendDataBundle({ workspaceRoot, repoPath, ownerSessionId, de
|
|
|
550
543
|
|
|
551
544
|
await exportOpenCodeSession(opencodeCommand, ownerSessionId, path.join(stagingDir, 'owner-session.json'), workspaceRoot)
|
|
552
545
|
|
|
553
|
-
|
|
546
|
+
const sortedSessions = developerSessions.sort((left, right) => left.order - right.order)
|
|
547
|
+
const claudeSessions = sortedSessions.filter((session) => session.backend === 'claude' || session.backend === 'claude-live')
|
|
548
|
+
const nonClaudeSessions = sortedSessions.filter((session) => session.backend !== 'claude' && session.backend !== 'claude-live')
|
|
549
|
+
|
|
550
|
+
for (const session of nonClaudeSessions) {
|
|
554
551
|
await exportDeveloperSessionArtifacts(session, workspaceRoot, stagingDir)
|
|
555
552
|
}
|
|
556
553
|
|
|
554
|
+
await exportClaudeProjectArtifacts(claudeSessions, workspaceRoot, stagingDir)
|
|
555
|
+
|
|
557
556
|
await copyOptionalPath(
|
|
558
|
-
path.join(workspaceRoot,
|
|
559
|
-
path.join(stagingDir,
|
|
557
|
+
path.join(workspaceRoot, EVALUATION_REPORTS_DIR),
|
|
558
|
+
path.join(stagingDir, EVALUATION_REPORTS_DIR),
|
|
560
559
|
warnings,
|
|
561
|
-
|
|
560
|
+
EVALUATION_REPORTS_DIR,
|
|
562
561
|
)
|
|
563
562
|
|
|
564
563
|
const retrospectiveDir = path.join(buildPaths().slopmachineDir, 'retrospectives')
|