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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import os from 'node:os'
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { spawn } from 'node:child_process'
|
|
7
|
+
|
|
8
|
+
import { parseArgs, emitFailure, emitSuccess, resolveClaudeSessionPath } from './claude_worker_common.mjs'
|
|
9
|
+
|
|
10
|
+
const argv = parseArgs(process.argv.slice(2))
|
|
11
|
+
|
|
12
|
+
async function pathExists(targetPath) {
|
|
13
|
+
try {
|
|
14
|
+
await fs.access(targetPath)
|
|
15
|
+
return true
|
|
16
|
+
} catch {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function run(command, args, cwd) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const child = spawn(command, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'], env: process.env })
|
|
24
|
+
let stdout = ''
|
|
25
|
+
let stderr = ''
|
|
26
|
+
child.stdout.on('data', (chunk) => { stdout += chunk.toString() })
|
|
27
|
+
child.stderr.on('data', (chunk) => { stderr += chunk.toString() })
|
|
28
|
+
child.on('error', reject)
|
|
29
|
+
child.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }))
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function createZipArchive(sourceDir, outputPath) {
|
|
34
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true })
|
|
35
|
+
|
|
36
|
+
if (process.platform === 'win32') {
|
|
37
|
+
const script = [
|
|
38
|
+
`$src = ${JSON.stringify(path.join(sourceDir, '*'))}`,
|
|
39
|
+
`$dest = ${JSON.stringify(outputPath)}`,
|
|
40
|
+
'if (Test-Path $dest) { Remove-Item -Force $dest }',
|
|
41
|
+
'Compress-Archive -Path $src -DestinationPath $dest -Force',
|
|
42
|
+
].join('; ')
|
|
43
|
+
|
|
44
|
+
let lastError = null
|
|
45
|
+
for (const shell of ['pwsh', 'powershell']) {
|
|
46
|
+
try {
|
|
47
|
+
const result = await run(shell, [
|
|
48
|
+
'-NoProfile',
|
|
49
|
+
'-ExecutionPolicy', 'Bypass',
|
|
50
|
+
'-Command',
|
|
51
|
+
script,
|
|
52
|
+
], sourceDir)
|
|
53
|
+
|
|
54
|
+
if (result.code === 0) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
lastError = new Error((result.stderr || result.stdout).trim() || `zip failed with exit ${result.code}`)
|
|
59
|
+
} catch (error) {
|
|
60
|
+
lastError = error
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
throw lastError || new Error('No usable PowerShell shell was found for zip creation')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const result = await run('zip', ['-9', '-q', '-r', outputPath, '.'], sourceDir)
|
|
69
|
+
if (result.code !== 0) {
|
|
70
|
+
throw new Error((result.stderr || result.stdout).trim() || `zip failed with exit ${result.code}`)
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new Error(error instanceof Error ? error.message : 'zip command is required to package Claude sessions')
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function normalizeClaudeJsonlFiles(projectDir) {
|
|
78
|
+
const normalizerScript = path.join(path.dirname(new URL(import.meta.url).pathname), 'normalize_claude_session.py')
|
|
79
|
+
const entries = await fs.readdir(projectDir, { withFileTypes: true }).catch(() => [])
|
|
80
|
+
const jsonlFiles = entries
|
|
81
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.jsonl'))
|
|
82
|
+
.map((entry) => path.join(projectDir, entry.name))
|
|
83
|
+
|
|
84
|
+
for (const filePath of jsonlFiles) {
|
|
85
|
+
const tempOutputPath = `${filePath}.normalized`
|
|
86
|
+
const result = await run('python3', [normalizerScript, filePath, '--output', tempOutputPath], projectDir)
|
|
87
|
+
if (result.code !== 0) {
|
|
88
|
+
throw new Error(`Failed to normalize Claude session file ${path.basename(filePath)}: ${(result.stderr || result.stdout).trim()}`)
|
|
89
|
+
}
|
|
90
|
+
await fs.rename(tempOutputPath, filePath)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const sessionId = argv['session-id']
|
|
96
|
+
const transcriptPath = await resolveClaudeSessionPath(sessionId, argv.cwd)
|
|
97
|
+
if (!(await pathExists(transcriptPath))) {
|
|
98
|
+
throw new Error(`Claude transcript not found for session ${sessionId}`)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const sourceProjectDir = path.dirname(transcriptPath)
|
|
102
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'slopmachine-claude-project-'))
|
|
103
|
+
const projectDir = path.join(tempRoot, path.basename(sourceProjectDir))
|
|
104
|
+
await fs.cp(sourceProjectDir, projectDir, { recursive: true })
|
|
105
|
+
await normalizeClaudeJsonlFiles(projectDir)
|
|
106
|
+
const included = (await fs.readdir(projectDir).catch(() => [])).sort((left, right) => left.localeCompare(right))
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await createZipArchive(projectDir, argv.output)
|
|
110
|
+
emitSuccess(sessionId, {
|
|
111
|
+
output: argv.output,
|
|
112
|
+
project_dir: sourceProjectDir,
|
|
113
|
+
label: argv.label || null,
|
|
114
|
+
included,
|
|
115
|
+
normalized: true,
|
|
116
|
+
})
|
|
117
|
+
} finally {
|
|
118
|
+
await fs.rm(tempRoot, { recursive: true, force: true }).catch(() => {})
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
emitFailure('package_claude_session_failed', error instanceof Error ? error.message : String(error))
|
|
122
|
+
process.exitCode = 1
|
|
123
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { emitFailure, emitSuccess, parseArgs } from './claude_worker_common.mjs'
|
|
7
|
+
|
|
8
|
+
const argv = parseArgs(process.argv.slice(2))
|
|
9
|
+
|
|
10
|
+
function resolveLinkType(targetPath) {
|
|
11
|
+
return process.platform === 'win32' ? 'junction' : 'dir'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function pathExists(targetPath) {
|
|
15
|
+
try {
|
|
16
|
+
await fs.access(targetPath)
|
|
17
|
+
return true
|
|
18
|
+
} catch {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function ensureLinkedDir(targetPath, linkPath) {
|
|
24
|
+
await fs.mkdir(path.dirname(linkPath), { recursive: true })
|
|
25
|
+
await fs.symlink(targetPath, linkPath, resolveLinkType(targetPath))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const workspaceRoot = argv['workspace-root'] ? path.resolve(argv['workspace-root']) : null
|
|
30
|
+
const name = argv.name || 'strict-audit'
|
|
31
|
+
const auditRoot = argv['output-dir']
|
|
32
|
+
? path.resolve(argv['output-dir'])
|
|
33
|
+
: path.join(workspaceRoot || '', '.ai', 'audit-workspaces', name)
|
|
34
|
+
|
|
35
|
+
if (!workspaceRoot) {
|
|
36
|
+
throw new Error('Missing --workspace-root')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const repoDir = path.join(workspaceRoot, 'repo')
|
|
40
|
+
const tmpDir = path.join(workspaceRoot, '.tmp')
|
|
41
|
+
if (!(await pathExists(repoDir))) {
|
|
42
|
+
throw new Error(`Missing repo directory at ${repoDir}`)
|
|
43
|
+
}
|
|
44
|
+
if (!(await pathExists(tmpDir))) {
|
|
45
|
+
throw new Error(`Missing .tmp directory at ${tmpDir}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await fs.rm(auditRoot, { recursive: true, force: true })
|
|
49
|
+
const runDir = path.join(auditRoot, 'run')
|
|
50
|
+
await fs.mkdir(runDir, { recursive: true })
|
|
51
|
+
|
|
52
|
+
await ensureLinkedDir(repoDir, path.join(runDir, 'repo'))
|
|
53
|
+
await ensureLinkedDir(tmpDir, path.join(auditRoot, '.tmp'))
|
|
54
|
+
|
|
55
|
+
emitSuccess(null, {
|
|
56
|
+
workspace_root: workspaceRoot,
|
|
57
|
+
audit_root: auditRoot,
|
|
58
|
+
run_dir: runDir,
|
|
59
|
+
repo_path: path.join(runDir, 'repo'),
|
|
60
|
+
tmp_path: path.join(auditRoot, '.tmp'),
|
|
61
|
+
})
|
|
62
|
+
} catch (error) {
|
|
63
|
+
emitFailure('prepare_strict_audit_workspace_failed', error instanceof Error ? error.message : String(error))
|
|
64
|
+
process.exitCode = 1
|
|
65
|
+
}
|
package/package.json
CHANGED
package/src/constants.js
CHANGED
|
@@ -62,17 +62,56 @@ export const CLAUDE_REQUIRED_AGENT_FILES = ["developer.md"];
|
|
|
62
62
|
export const REQUIRED_SLOPMACHINE_FILES = [
|
|
63
63
|
"backend-evaluation-prompt.md",
|
|
64
64
|
"frontend-evaluation-prompt.md",
|
|
65
|
+
"test-coverage-prompt.md",
|
|
65
66
|
"workflow-init.js",
|
|
66
67
|
"templates/AGENTS.md",
|
|
68
|
+
"templates/CLAUDE.md",
|
|
67
69
|
"utils/claude_worker_common.mjs",
|
|
70
|
+
"utils/claude_live_common.mjs",
|
|
71
|
+
"utils/claude_live_channel.mjs",
|
|
72
|
+
"utils/claude_live_hook.py",
|
|
73
|
+
"utils/claude_live_launch.mjs",
|
|
74
|
+
"utils/claude_live_turn.mjs",
|
|
75
|
+
"utils/claude_live_status.mjs",
|
|
76
|
+
"utils/claude_live_stop.mjs",
|
|
68
77
|
"utils/claude_create_session.mjs",
|
|
69
78
|
"utils/claude_resume_session.mjs",
|
|
70
79
|
"utils/claude_export_session.mjs",
|
|
71
80
|
"utils/export_ai_session.mjs",
|
|
72
|
-
"utils/
|
|
73
|
-
"utils/
|
|
81
|
+
"utils/package_claude_session.mjs",
|
|
82
|
+
"utils/prepare_strict_audit_workspace.mjs",
|
|
83
|
+
"utils/claude_wait_for_rate_limit_reset.mjs",
|
|
84
|
+
"utils/claude_wait_for_rate_limit_reset.sh",
|
|
85
|
+
"utils/normalize_claude_session.py",
|
|
86
|
+
"scaffold-playbooks/docker-shared-contract.md",
|
|
87
|
+
"scaffold-playbooks/generic-unknown-tech-guide.md",
|
|
88
|
+
"scaffold-playbooks/frontend-family-matrix.md",
|
|
89
|
+
"scaffold-playbooks/backend-family-matrix.md",
|
|
90
|
+
"scaffold-playbooks/database-module-matrix.md",
|
|
91
|
+
"scaffold-playbooks/platform-family-matrix.md",
|
|
92
|
+
"scaffold-playbooks/overlay-module-matrix.md",
|
|
93
|
+
"scaffold-playbooks/selection-matrix.md",
|
|
94
|
+
"scaffold-playbooks/docker-baseline.md",
|
|
95
|
+
"scaffold-playbooks/frontend-baseline.md",
|
|
96
|
+
"scaffold-playbooks/backend-baseline.md",
|
|
97
|
+
"scaffold-playbooks/vue-vite-default.md",
|
|
98
|
+
"scaffold-playbooks/angular-default.md",
|
|
99
|
+
"scaffold-playbooks/fastapi-default.md",
|
|
100
|
+
"scaffold-playbooks/spring-boot-default.md",
|
|
101
|
+
"scaffold-playbooks/django-default.md",
|
|
102
|
+
"scaffold-playbooks/laravel-default.md",
|
|
103
|
+
"scaffold-playbooks/livewire-default.md",
|
|
104
|
+
"scaffold-playbooks/go-chi-default.md",
|
|
105
|
+
"scaffold-playbooks/web-default.md",
|
|
106
|
+
"scaffold-playbooks/android-native-java.md",
|
|
107
|
+
"scaffold-playbooks/android-kotlin-compose.md",
|
|
108
|
+
"scaffold-playbooks/expo-react-native-default.md",
|
|
109
|
+
"scaffold-playbooks/electron-vite-default.md",
|
|
110
|
+
"scaffold-playbooks/tauri-default.md",
|
|
111
|
+
"scaffold-playbooks/ios-linux-portable.md",
|
|
112
|
+
"scaffold-playbooks/ios-native-swift.md",
|
|
113
|
+
"scaffold-playbooks/ios-native-objective-c.md",
|
|
74
114
|
"utils/strip_session_parent.py",
|
|
75
|
-
"utils/convert_ai_session.py",
|
|
76
115
|
"utils/cleanup_delivery_artifacts.py",
|
|
77
116
|
];
|
|
78
117
|
|
package/src/init.js
CHANGED
|
@@ -25,11 +25,12 @@ const GITIGNORE_ENTRIES = [
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
const ALLOWED_EXISTING_ENTRIES = new Set(['.DS_Store', '.git'])
|
|
28
|
-
const INITIAL_COMMIT_PATHS = ['.gitignore', 'docs', 'metadata.json', 'repo', '
|
|
28
|
+
const INITIAL_COMMIT_PATHS = ['.gitignore', '.tmp', 'docs', 'metadata.json', 'repo', 'sessions']
|
|
29
29
|
const ADOPTION_ROOT_KEEP = new Set([
|
|
30
30
|
'.DS_Store',
|
|
31
31
|
'.git',
|
|
32
32
|
'.gitignore',
|
|
33
|
+
'.tmp',
|
|
33
34
|
'.ai',
|
|
34
35
|
'.beads',
|
|
35
36
|
'docs',
|
|
@@ -37,7 +38,19 @@ const ADOPTION_ROOT_KEEP = new Set([
|
|
|
37
38
|
'metadata.json',
|
|
38
39
|
'repo',
|
|
39
40
|
])
|
|
40
|
-
const VALID_START_PHASES = new Set(['
|
|
41
|
+
const VALID_START_PHASES = new Set(['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8', 'P9', 'P10'])
|
|
42
|
+
const PHASE_LABELS = {
|
|
43
|
+
P1: 'P1 Clarification',
|
|
44
|
+
P2: 'P2 Planning',
|
|
45
|
+
P3: 'P3 Scaffold',
|
|
46
|
+
P4: 'P4 Development',
|
|
47
|
+
P5: 'P5 Integrated Verification',
|
|
48
|
+
P6: 'P6 Hardening',
|
|
49
|
+
P7: 'P7 Evaluation and Fix Verification',
|
|
50
|
+
P8: 'P8 Final Human Decision',
|
|
51
|
+
P9: 'P9 Submission Packaging',
|
|
52
|
+
P10: 'P10 Retrospective',
|
|
53
|
+
}
|
|
41
54
|
|
|
42
55
|
function getUnixBrCommandCandidates(paths) {
|
|
43
56
|
return [
|
|
@@ -281,7 +294,24 @@ async function createInitialPhaseArtifacts(targetPath, options) {
|
|
|
281
294
|
`Capture the planning-critical project shape here before real planning begins.\n\n` +
|
|
282
295
|
`## Bootstrap Notes\n\n` +
|
|
283
296
|
`${options.adoptExisting ? '- This workspace was adopted from an existing project. Inspect the current codebase state before assuming a clean-slate build.\n' : '- This workspace started from a clean slopmachine bootstrap.\n'}` +
|
|
284
|
-
`${options.requestedStartPhase ? `- Requested start phase: ${options.requestedStartPhase}.\n` : ''}`
|
|
297
|
+
`${options.requestedStartPhase ? `- Requested start phase: ${options.requestedStartPhase}.\n` : ''}` +
|
|
298
|
+
`\n## Intake Shape\n\n` +
|
|
299
|
+
`Fill this file with concise but concrete planning inputs:\n\n` +
|
|
300
|
+
`- prompt-critical requirements\n` +
|
|
301
|
+
`- actors and path-to-success summaries\n` +
|
|
302
|
+
`- required surfaces\n` +
|
|
303
|
+
`- explicit out-of-scope items\n` +
|
|
304
|
+
`- locked defaults\n` +
|
|
305
|
+
`- major risks\n` +
|
|
306
|
+
`- shared prerequisites and likely parallel work packages\n` +
|
|
307
|
+
`\n## Gap Sweep\n\n` +
|
|
308
|
+
`Before planning, note the important unresolved gaps in these categories when relevant:\n\n` +
|
|
309
|
+
`- business rules\n` +
|
|
310
|
+
`- workflows\n` +
|
|
311
|
+
`- data model\n` +
|
|
312
|
+
`- security and compliance\n` +
|
|
313
|
+
`- reliability / offline / retries\n` +
|
|
314
|
+
`- reporting / exports / analytics\n`
|
|
285
315
|
|
|
286
316
|
const clarificationOptionsContent = `# Clarification Options\n\n` +
|
|
287
317
|
`## Original Prompt\n\n` +
|
|
@@ -293,32 +323,128 @@ async function createInitialPhaseArtifacts(targetPath, options) {
|
|
|
293
323
|
`Build the developer-facing clarification prompt here once P1 resolves the material ambiguities.\n`
|
|
294
324
|
|
|
295
325
|
const designContent = `# Design\n\n` +
|
|
296
|
-
`This document is the owner-maintained external design reference for the project.\n\n` +
|
|
326
|
+
`This document is the owner-maintained external design reference for the project. Treat it as the authoritative detailed planning and design contract once planning begins.\n\n` +
|
|
297
327
|
`## Bootstrap Notes\n\n` +
|
|
298
328
|
`${options.adoptExisting ? '- This workspace was adopted from an existing project. Update this design reference to match the real codebase state before planning deeply.\n' : '- This workspace started from a clean slopmachine bootstrap. Update this file as clarification and planning progress.\n'}` +
|
|
299
|
-
`${options.requestedStartPhase ? `- Requested start phase: ${options.requestedStartPhase}.\n` : ''}`
|
|
329
|
+
`${options.requestedStartPhase ? `- Requested start phase: ${options.requestedStartPhase}.\n` : ''}` +
|
|
330
|
+
`\n## How To Use This File\n\n` +
|
|
331
|
+
`- Replace placeholders with concrete implementation decisions.\n` +
|
|
332
|
+
`- Do not leave major sections vague, empty, or deferred without an explicit reason.\n` +
|
|
333
|
+
`- Prefer explicit tables, bullet lists, and subsections over broad narrative.\n` +
|
|
334
|
+
`- Make this detailed enough that scaffold and development can mostly execute by following the accepted plan.\n` +
|
|
335
|
+
`\n## Tech Stack Summary\n\n` +
|
|
336
|
+
`State the concrete backend, frontend, persistence, auth/security, jobs/async, runtime, and other major stack choices here. If something is intentionally not fixed yet, say so explicitly and explain why.\n` +
|
|
337
|
+
`\n## Product Overview\n\n` +
|
|
338
|
+
`Describe what is being built, for whom, and the core business objective in implementation-relevant language.\n` +
|
|
339
|
+
`\n## System Overview\n\n` +
|
|
340
|
+
`- What is being built\n` +
|
|
341
|
+
`- Core business objective\n` +
|
|
342
|
+
`- Primary technical shape\n` +
|
|
343
|
+
`\n## In-Scope Domains\n\n` +
|
|
344
|
+
`List the major domains or modules that are part of delivery.\n` +
|
|
345
|
+
`\n## Explicit Out-Of-Scope\n\n` +
|
|
346
|
+
`List what is intentionally not being built so implementation does not overreach.\n` +
|
|
347
|
+
`\n## Actors And Roles\n\n` +
|
|
348
|
+
`Define the user types, admins, operators, and other actors.\n` +
|
|
349
|
+
`\n## Actor Success Paths\n\n` +
|
|
350
|
+
`For each important actor, define the concrete path to success for the prompt-critical workflows.\n` +
|
|
351
|
+
`\n## Architecture And Module Boundaries\n\n` +
|
|
352
|
+
`Describe the major components, boundaries, and why this structure was chosen.\n` +
|
|
353
|
+
`\n## Domain Model And Data Model\n\n` +
|
|
354
|
+
`List the important entities, relationships, supporting records, and persistence constraints.\n` +
|
|
355
|
+
`\n## Authoritative Business Rules\n\n` +
|
|
356
|
+
`Define defaults, limits, formulas, uniqueness rules, conflicts, retries, reversals, ownership rules, and other critical domain rules.\n` +
|
|
357
|
+
`\n## State Machines And Lifecycles\n\n` +
|
|
358
|
+
`Define the allowed states, transitions, triggers, and illegal transitions where workflow state matters.\n` +
|
|
359
|
+
`\n## Permissions And Authorization Model\n\n` +
|
|
360
|
+
`Define roles, scopes, route-level checks, object-level checks, and privileged actions.\n` +
|
|
361
|
+
`\n## Validation And Error Handling\n\n` +
|
|
362
|
+
`Define the important validations, normalized errors, and failure behavior.\n` +
|
|
363
|
+
`\n## Security, Compliance, And Data Governance\n\n` +
|
|
364
|
+
`Describe authentication, session or token rules, audit expectations, masking, retention, deletion, export restrictions, and other sensitive-data controls when relevant.\n` +
|
|
365
|
+
`\n## Offline, Queueing, Reliability, And Background Jobs\n\n` +
|
|
366
|
+
`When relevant, define queue behavior, retries, resumability, conflict handling, job states, observability, and maintenance behavior.\n` +
|
|
367
|
+
`\n## Reporting, Analytics, Search, And Exports\n\n` +
|
|
368
|
+
`When relevant, describe KPIs, source of truth, calculations, indexing or search behavior, export rules, and report job lifecycles.\n` +
|
|
369
|
+
`\n## Runtime, Config, And Ops Contract\n\n` +
|
|
370
|
+
`Define runtime entrypoints, config flow, environment handling, jobs, observability, and operational expectations.\n` +
|
|
371
|
+
`\n## Interface Contracts\n\n` +
|
|
372
|
+
`Describe important APIs, frontend/backend contracts, payload shapes, and user-visible interaction expectations.\n` +
|
|
373
|
+
`\n## Non-Functional Requirements\n\n` +
|
|
374
|
+
`Describe performance, deterministic behavior, restart recovery, availability, timezone, backup, and other cross-cutting quality requirements when relevant.\n` +
|
|
375
|
+
`\n## Verification Strategy\n\n` +
|
|
376
|
+
`Describe the local verification model, broad-gate verification, and the major risk-to-test expectations.\n` +
|
|
377
|
+
`\n## Dependency And Parallelism Plan\n\n` +
|
|
378
|
+
`Identify which work must stay serial because of shared foundations, and which 2 or 3 work packages can safely proceed in parallel once those foundations are settled.\n` +
|
|
379
|
+
`\n## Implementation Phases\n\n` +
|
|
380
|
+
`Break the work into realistic phases or major chunks with dependencies and usable outcomes.\n` +
|
|
381
|
+
`\n## Phase Checkpoints\n\n` +
|
|
382
|
+
`For each phase, define required artifacts, required working flows, required tests, exit criteria, and what is not allowed to defer past that phase.\n` +
|
|
383
|
+
`\n## Definition Of Done\n\n` +
|
|
384
|
+
`Define the hard completion standard that should block fake-complete or scaffold-only delivery.\n` +
|
|
385
|
+
`\n## Deliverables\n\n` +
|
|
386
|
+
`List the concrete outputs delivery must produce, such as working application, scripts, tests, docs, bootstrap assets, exports, or admin tools when relevant.\n` +
|
|
387
|
+
`\n## Assumptions, Dispositions, And Open Items\n\n` +
|
|
388
|
+
`Keep unresolved items rare. Each section should be either concrete or explicitly marked not applicable with a reason. If something is still open, name it explicitly, explain why it is still open, and state what evidence or decision is needed to close it.\n`
|
|
300
389
|
|
|
301
390
|
const apiSpecContent = `# API Spec\n\n` +
|
|
302
391
|
`Use this owner-maintained external document when API or interface contracts are material to the project.\n\n` +
|
|
303
392
|
`If the project has no meaningful API surface, mark this file as not applicable rather than deleting it silently.\n`
|
|
304
393
|
|
|
305
394
|
const testCoverageContent = `# Test Coverage\n\n` +
|
|
306
|
-
`Map prompt-critical requirements and
|
|
307
|
-
`##
|
|
395
|
+
`Map prompt-critical requirements, endpoint coverage, and audit-facing test evidence here as the project evolves.\n\n` +
|
|
396
|
+
`## Requirement And Risk Matrix\n\n` +
|
|
308
397
|
`- Requirement or risk\n` +
|
|
309
398
|
`- Test file or layer\n` +
|
|
310
399
|
`- Key assertion\n` +
|
|
311
400
|
`- Coverage status\n` +
|
|
401
|
+
`- Remaining gap\n\n` +
|
|
402
|
+
`## Backend Endpoint Inventory\n\n` +
|
|
403
|
+
`Use this when the project exposes backend or fullstack API endpoints.\n\n` +
|
|
404
|
+
`- Method\n` +
|
|
405
|
+
`- Resolved path\n` +
|
|
406
|
+
`- Notes\n\n` +
|
|
407
|
+
`## API Test Mapping Table\n\n` +
|
|
408
|
+
`- Endpoint\n` +
|
|
409
|
+
`- Covered: yes/no\n` +
|
|
410
|
+
`- Test type: true no-mock HTTP / HTTP with mocking / unit-only or indirect\n` +
|
|
411
|
+
`- Test file\n` +
|
|
412
|
+
`- Evidence\n\n` +
|
|
413
|
+
`## Unit Test Summary\n\n` +
|
|
414
|
+
`- Module family\n` +
|
|
415
|
+
`- Covered by\n` +
|
|
416
|
+
`- Important modules not tested\n\n` +
|
|
417
|
+
`## Coverage Summary\n\n` +
|
|
418
|
+
`- Total endpoints\n` +
|
|
419
|
+
`- Endpoints with HTTP tests\n` +
|
|
420
|
+
`- Endpoints with true no-mock HTTP tests\n` +
|
|
421
|
+
`- Coverage threshold target\n` +
|
|
312
422
|
`- Remaining gap\n`
|
|
313
423
|
|
|
314
424
|
const repoReadmeContent = `# Project\n\n` +
|
|
315
425
|
`This README is the primary in-repo documentation surface.\n\n` +
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
426
|
+
`## Project Type\n\n` +
|
|
427
|
+
`State one of: backend, fullstack, web, android, ios, desktop.\n\n` +
|
|
428
|
+
`## Overview\n\n` +
|
|
429
|
+
`Explain what the project does and who it is for.\n\n` +
|
|
430
|
+
`## Tech Stack\n\n` +
|
|
431
|
+
`List the main frameworks, languages, and runtime pieces.\n\n` +
|
|
432
|
+
`## Startup Instructions\n\n` +
|
|
433
|
+
`Document the canonical startup path and any audit-compatibility or platform-specific launch notes.\n\n` +
|
|
434
|
+
`## Access Method\n\n` +
|
|
435
|
+
`Explain how a reviewer reaches the system after startup.\n\n` +
|
|
436
|
+
`## Verification Method\n\n` +
|
|
437
|
+
`Explain how to confirm the main flows actually work.\n\n` +
|
|
438
|
+
`## Authentication\n\n` +
|
|
439
|
+
`Provide demo credentials for every role, or state exactly: No authentication required\n\n` +
|
|
440
|
+
`## Roles And Workflows\n\n` +
|
|
441
|
+
`Summarize the important roles, main workflows, and any security-relevant behavior a reviewer should know.\n\n` +
|
|
442
|
+
`## Main Repo Contents\n\n` +
|
|
443
|
+
`List the main directories, entry points, and scripts.\n\n` +
|
|
444
|
+
`## Architecture\n\n` +
|
|
445
|
+
`Summarize the delivered architecture and major implementation structure.\n\n` +
|
|
446
|
+
`## Important Notes\n\n` +
|
|
447
|
+
`Document feature flags, mock/default behavior, setup boundaries, and important new-developer information.\n`
|
|
322
448
|
|
|
323
449
|
const startupContextContent = `# Startup Context\n\n` +
|
|
324
450
|
`- Bootstrap mode: ${options.adoptExisting ? 'adopt-existing-project' : 'new-workspace'}\n` +
|
|
@@ -338,14 +464,17 @@ async function createInitialPhaseArtifacts(targetPath, options) {
|
|
|
338
464
|
}
|
|
339
465
|
|
|
340
466
|
async function createRepoStructure(targetPath, agentsTemplate, options) {
|
|
467
|
+
const initialPhase = options.requestedStartPhase ? PHASE_LABELS[options.requestedStartPhase] : 'P1 Clarification'
|
|
468
|
+
|
|
341
469
|
log('Creating parent workflow directories')
|
|
470
|
+
await ensureDir(path.join(targetPath, '.tmp'))
|
|
342
471
|
await ensureDir(path.join(targetPath, 'docs'))
|
|
343
472
|
await ensureDir(path.join(targetPath, 'sessions'))
|
|
344
|
-
await ensureDir(path.join(targetPath, '
|
|
345
|
-
await ensureDir(path.join(targetPath, '.ai', 'claude-traces'))
|
|
473
|
+
await ensureDir(path.join(targetPath, '.ai', 'claude-live'))
|
|
346
474
|
|
|
347
475
|
log('Creating repo/ working directory')
|
|
348
476
|
await ensureDir(path.join(targetPath, 'repo'))
|
|
477
|
+
await ensureDir(path.join(targetPath, 'repo', '.claude'))
|
|
349
478
|
|
|
350
479
|
log('Copying AGENTS template into repo/')
|
|
351
480
|
const repoAgentsPath = path.join(targetPath, 'repo', 'AGENTS.md')
|
|
@@ -354,6 +483,17 @@ async function createRepoStructure(targetPath, agentsTemplate, options) {
|
|
|
354
483
|
}
|
|
355
484
|
await fs.copyFile(agentsTemplate, repoAgentsPath)
|
|
356
485
|
|
|
486
|
+
await writeFileIfMissing(
|
|
487
|
+
path.join(targetPath, 'repo', '.claude', 'settings.json'),
|
|
488
|
+
`${JSON.stringify({
|
|
489
|
+
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
490
|
+
agent: 'developer',
|
|
491
|
+
env: {
|
|
492
|
+
CLAUDE_CODE_SUBAGENT_MODEL: 'sonnet',
|
|
493
|
+
},
|
|
494
|
+
}, null, 2)}\n`,
|
|
495
|
+
)
|
|
496
|
+
|
|
357
497
|
const projectMetadataPath = path.join(targetPath, 'metadata.json')
|
|
358
498
|
if (!(await pathExists(projectMetadataPath))) {
|
|
359
499
|
log('Creating parent metadata.json')
|
|
@@ -373,35 +513,40 @@ async function createRepoStructure(targetPath, agentsTemplate, options) {
|
|
|
373
513
|
|
|
374
514
|
const workflowMetadataPath = path.join(targetPath, '.ai', 'metadata.json')
|
|
375
515
|
if (!(await pathExists(workflowMetadataPath))) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
516
|
+
log('Creating .ai/metadata.json')
|
|
517
|
+
await fs.writeFile(workflowMetadataPath, `${JSON.stringify({
|
|
518
|
+
run_id: randomUUID(),
|
|
519
|
+
current_phase: initialPhase,
|
|
520
|
+
awaiting_human: false,
|
|
381
521
|
clarification_approved: false,
|
|
382
522
|
clarification_validator_session_id: null,
|
|
383
523
|
evaluation_prompt_kind: null,
|
|
384
524
|
active_evaluator_session_id: null,
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
525
|
+
evaluation_reports_root: '.tmp',
|
|
526
|
+
evaluation_audit_count: 0,
|
|
527
|
+
evaluation_runs: [],
|
|
528
|
+
completed_bugfix_session_count: 0,
|
|
529
|
+
required_bugfix_session_count: 2,
|
|
530
|
+
coverage_readme_audit_completed: false,
|
|
531
|
+
coverage_readme_audit_report_path: null,
|
|
390
532
|
developer_sessions: [],
|
|
533
|
+
developer_rulebook_file: null,
|
|
391
534
|
current_developer_lane: null,
|
|
392
535
|
active_developer_session_id: null,
|
|
536
|
+
primary_develop_session_id: null,
|
|
537
|
+
latest_develop_session_id: null,
|
|
393
538
|
next_develop_session_number: 1,
|
|
394
539
|
next_bugfix_session_number: 1,
|
|
395
540
|
packaging_completed: false,
|
|
396
|
-
|
|
541
|
+
claude_live_root: '.ai/claude-live',
|
|
397
542
|
bootstrap_mode: options.adoptExisting ? 'adopt' : 'new',
|
|
398
543
|
requested_start_phase: options.requestedStartPhase,
|
|
399
544
|
}, null, 2)}\n`, 'utf8')
|
|
400
545
|
}
|
|
401
546
|
|
|
402
547
|
await fs.writeFile(path.join(targetPath, '.ai', 'artifacts', '.gitkeep'), '', 'utf8')
|
|
403
|
-
await fs.writeFile(path.join(targetPath, '.ai', 'claude-
|
|
404
|
-
await fs.writeFile(path.join(targetPath, '
|
|
548
|
+
await fs.writeFile(path.join(targetPath, '.ai', 'claude-live', '.gitkeep'), '', 'utf8')
|
|
549
|
+
await fs.writeFile(path.join(targetPath, '.tmp', '.gitkeep'), '', 'utf8')
|
|
405
550
|
await fs.writeFile(path.join(targetPath, 'sessions', '.gitkeep'), '', 'utf8')
|
|
406
551
|
|
|
407
552
|
await createInitialPhaseArtifacts(targetPath, options)
|