theslopmachine 0.3.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 (31) hide show
  1. package/MANUAL.md +63 -0
  2. package/README.md +23 -0
  3. package/RELEASE.md +81 -0
  4. package/assets/agents/developer.md +294 -0
  5. package/assets/agents/slopmachine.md +510 -0
  6. package/assets/skills/beads-operations/SKILL.md +75 -0
  7. package/assets/skills/clarification-gate/SKILL.md +51 -0
  8. package/assets/skills/developer-session-lifecycle/SKILL.md +75 -0
  9. package/assets/skills/final-evaluation-orchestration/SKILL.md +75 -0
  10. package/assets/skills/frontend-design/SKILL.md +41 -0
  11. package/assets/skills/get-overlays/SKILL.md +157 -0
  12. package/assets/skills/planning-gate/SKILL.md +68 -0
  13. package/assets/skills/submission-packaging/SKILL.md +268 -0
  14. package/assets/skills/verification-gates/SKILL.md +106 -0
  15. package/assets/slopmachine/backend-evaluation-prompt.md +275 -0
  16. package/assets/slopmachine/beads-init.js +428 -0
  17. package/assets/slopmachine/document-completeness.md +45 -0
  18. package/assets/slopmachine/engineering-results.md +59 -0
  19. package/assets/slopmachine/frontend-evaluation-prompt.md +304 -0
  20. package/assets/slopmachine/implementation-comparison.md +36 -0
  21. package/assets/slopmachine/quality-document.md +108 -0
  22. package/assets/slopmachine/templates/AGENTS.md +114 -0
  23. package/assets/slopmachine/utils/convert_ai_session.py +1837 -0
  24. package/assets/slopmachine/utils/strip_session_parent.py +66 -0
  25. package/bin/slopmachine.js +9 -0
  26. package/package.json +25 -0
  27. package/src/cli.js +32 -0
  28. package/src/constants.js +77 -0
  29. package/src/init.js +179 -0
  30. package/src/install.js +330 -0
  31. package/src/utils.js +162 -0
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+
9
+ def strip_parent_id(data: Any) -> Any:
10
+ if not isinstance(data, dict):
11
+ raise ValueError("Expected top-level JSON object")
12
+
13
+ info = data.get("info")
14
+ if not isinstance(info, dict):
15
+ raise ValueError("Expected top-level 'info' object")
16
+
17
+ cleaned = dict(data)
18
+ cleaned_info = dict(info)
19
+
20
+ # Remove only the session-level parent marker. Message-level parentIDs
21
+ # are preserved so the conversation structure remains intact.
22
+ cleaned_info.pop("parentID", None)
23
+ cleaned["info"] = cleaned_info
24
+ return cleaned
25
+
26
+
27
+ def parse_args() -> argparse.Namespace:
28
+ parser = argparse.ArgumentParser(
29
+ description="Remove the top-level parentID from an OpenCode session export."
30
+ )
31
+ parser.add_argument("input", type=Path, help="Path to the source session JSON file")
32
+ parser.add_argument(
33
+ "-o",
34
+ "--output",
35
+ type=Path,
36
+ help="Write the cleaned session JSON here. Defaults to stdout.",
37
+ )
38
+ parser.add_argument(
39
+ "--in-place",
40
+ action="store_true",
41
+ help="Overwrite the input file instead of printing to stdout.",
42
+ )
43
+ return parser.parse_args()
44
+
45
+
46
+ def main() -> int:
47
+ args = parse_args()
48
+ if args.output and args.in_place:
49
+ raise SystemExit("Use either --output or --in-place, not both")
50
+
51
+ data = json.loads(args.input.read_text())
52
+ cleaned = strip_parent_id(data)
53
+ rendered = json.dumps(cleaned, indent=2, ensure_ascii=False) + "\n"
54
+
55
+ if args.in_place:
56
+ args.input.write_text(rendered)
57
+ elif args.output:
58
+ args.output.write_text(rendered)
59
+ else:
60
+ print(rendered, end="")
61
+
62
+ return 0
63
+
64
+
65
+ if __name__ == "__main__":
66
+ raise SystemExit(main())
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCli } from '../src/cli.js'
4
+
5
+ runCli(process.argv.slice(2)).catch((error) => {
6
+ const message = error instanceof Error ? error.message : String(error)
7
+ console.error(`[slopmachine] ERROR: ${message}`)
8
+ process.exit(1)
9
+ })
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "theslopmachine",
3
+ "version": "0.3.0",
4
+ "description": "SlopMachine installer and project bootstrap CLI",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "slopmachine": "bin/slopmachine.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node ./bin/slopmachine.js",
12
+ "check": "node ./bin/slopmachine.js --help"
13
+ },
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "files": [
18
+ "bin",
19
+ "src",
20
+ "assets",
21
+ "README.md",
22
+ "RELEASE.md",
23
+ "MANUAL.md"
24
+ ]
25
+ }
package/src/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ import { runInit } from './init.js'
2
+ import { runInstall } from './install.js'
3
+
4
+ function printHelp() {
5
+ console.log(`SlopMachine 0.3
6
+
7
+ Commands:
8
+ install Install SlopMachine into the local user environment
9
+ init Run the packaged init script in the current directory (-o opens OpenCode in repo/)
10
+ help Show this help text`)
11
+ }
12
+
13
+ export async function runCli(args) {
14
+ const [command] = args
15
+
16
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
17
+ printHelp()
18
+ return
19
+ }
20
+
21
+ if (command === 'install') {
22
+ await runInstall()
23
+ return
24
+ }
25
+
26
+ if (command === 'init') {
27
+ await runInit(args.slice(1))
28
+ return
29
+ }
30
+
31
+ throw new Error(`Unknown command: ${command}`)
32
+ }
@@ -0,0 +1,77 @@
1
+ import os from 'node:os'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ export const PACKAGE_VERSION = '0.3.0'
6
+ export const OPCODE_VERSION = '1.3.5'
7
+ export const BEADS_VERSION = '0.52.0'
8
+ export const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
9
+
10
+ export function resolveHomeDir() {
11
+ return process.env.SLOPMACHINE_HOME || os.homedir()
12
+ }
13
+
14
+ export function buildPaths() {
15
+ const home = resolveHomeDir()
16
+ return {
17
+ home,
18
+ slopmachineDir: path.join(home, 'slopmachine'),
19
+ opencodeDir: path.join(home, '.config', 'opencode'),
20
+ opencodeAgentsDir: path.join(home, '.config', 'opencode', 'agents'),
21
+ opencodeConfigPath: path.join(home, '.config', 'opencode', 'opencode.json'),
22
+ globalSkillsDir: path.join(home, '.agents', 'skills'),
23
+ }
24
+ }
25
+
26
+ export const REQUIRED_SKILL_DIRS = [
27
+ 'clarification-gate',
28
+ 'developer-session-lifecycle',
29
+ 'final-evaluation-orchestration',
30
+ 'beads-operations',
31
+ 'get-overlays',
32
+ 'planning-gate',
33
+ 'verification-gates',
34
+ 'submission-packaging',
35
+ 'frontend-design',
36
+ ]
37
+
38
+ export const REQUIRED_SLOPMACHINE_FILES = [
39
+ 'backend-evaluation-prompt.md',
40
+ 'frontend-evaluation-prompt.md',
41
+ 'document-completeness.md',
42
+ 'quality-document.md',
43
+ 'engineering-results.md',
44
+ 'implementation-comparison.md',
45
+ 'beads-init.js',
46
+ 'templates/AGENTS.md',
47
+ 'utils/strip_session_parent.py',
48
+ 'utils/convert_ai_session.py',
49
+ ]
50
+
51
+ export const MCP_ENTRIES = {
52
+ 'chrome-devtools': {
53
+ type: 'local',
54
+ command: ['npx', '-y', 'chrome-devtools-mcp@latest'],
55
+ },
56
+ context7: {
57
+ enabled: true,
58
+ type: 'remote',
59
+ url: 'https://mcp.context7.com/mcp',
60
+ headers: {
61
+ CONTEXT7_API_KEY: '',
62
+ },
63
+ },
64
+ exa: {
65
+ enabled: true,
66
+ type: 'remote',
67
+ url: 'https://mcp.exa.ai/mcp',
68
+ headers: {
69
+ EXA_API_KEY: '',
70
+ },
71
+ },
72
+ shadcn: {
73
+ command: ['npx', 'shadcn@latest', 'mcp'],
74
+ enabled: false,
75
+ type: 'local',
76
+ },
77
+ }
package/src/init.js ADDED
@@ -0,0 +1,179 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+
4
+ import { buildPaths } from './constants.js'
5
+ import { ensureDir, log, pathExists, runCommand } from './utils.js'
6
+
7
+ const GITIGNORE_ENTRIES = [
8
+ '.DS_Store',
9
+ '*.log',
10
+ '.env',
11
+ '.env.local',
12
+ '.env.*.local',
13
+ 'node_modules/',
14
+ 'dist/',
15
+ 'build/',
16
+ '.coverage',
17
+ '.pytest_cache/',
18
+ '__pycache__/',
19
+ 'logs/',
20
+ 'gh-multi_logs/',
21
+ 'antigravity-logs/',
22
+ ]
23
+
24
+ function parseInitArgs(args) {
25
+ let openAfterInit = false
26
+ let targetInput = '.'
27
+
28
+ for (const arg of args) {
29
+ if (arg === '-o') {
30
+ openAfterInit = true
31
+ continue
32
+ }
33
+
34
+ if (arg.startsWith('-')) {
35
+ throw new Error(`Unknown init option: ${arg}`)
36
+ }
37
+
38
+ targetInput = arg
39
+ }
40
+
41
+ return { openAfterInit, targetInput }
42
+ }
43
+
44
+ async function assertRequiredFiles(paths) {
45
+ const beadsScript = path.join(paths.slopmachineDir, 'beads-init.js')
46
+ const agentsTemplate = path.join(paths.slopmachineDir, 'templates', 'AGENTS.md')
47
+
48
+ if (!(await pathExists(beadsScript))) {
49
+ throw new Error(`Missing packaged Beads init script at ${beadsScript}. Run slopmachine install first.`)
50
+ }
51
+
52
+ if (!(await pathExists(agentsTemplate))) {
53
+ throw new Error(`Missing packaged AGENTS template at ${agentsTemplate}. Run slopmachine install first.`)
54
+ }
55
+
56
+ return { beadsScript, agentsTemplate }
57
+ }
58
+
59
+ async function resolveTarget(targetInput) {
60
+ const targetPath = path.resolve(process.cwd(), targetInput)
61
+ if (!(await pathExists(targetPath))) {
62
+ throw new Error(`Target directory '${targetInput}' does not exist or is not accessible.`)
63
+ }
64
+ return targetPath
65
+ }
66
+
67
+ async function ensureGitRepo(targetPath) {
68
+ const repoCheck = await runCommand('git', ['-C', targetPath, 'rev-parse', '--is-inside-work-tree'])
69
+ if (repoCheck.code === 0) {
70
+ log('Git repository already initialized')
71
+ return
72
+ }
73
+
74
+ log('Initializing git repository')
75
+ const initResult = await runCommand('git', ['-C', targetPath, 'init'], { stdio: 'inherit' })
76
+ if (initResult.code !== 0) {
77
+ throw new Error('Git repository initialization failed')
78
+ }
79
+ }
80
+
81
+ async function ensureGitignore(targetPath) {
82
+ const gitignorePath = path.join(targetPath, '.gitignore')
83
+ await fs.writeFile(gitignorePath, '', { flag: 'a' })
84
+ const existingContent = await fs.readFile(gitignorePath, 'utf8')
85
+ const existingLines = new Set(existingContent.split(/\r?\n/))
86
+
87
+ const linesToAppend = []
88
+ for (const entry of GITIGNORE_ENTRIES) {
89
+ if (!existingLines.has(entry)) {
90
+ linesToAppend.push(entry)
91
+ log(`Added .gitignore entry: ${entry}`)
92
+ }
93
+ }
94
+
95
+ if (linesToAppend.length > 0) {
96
+ const prefix = existingContent.endsWith('\n') || existingContent.length === 0 ? '' : '\n'
97
+ await fs.appendFile(gitignorePath, `${prefix}${linesToAppend.join('\n')}\n`, 'utf8')
98
+ }
99
+ }
100
+
101
+ async function runBeadsBootstrap(paths, targetPath, beadsScript) {
102
+ const nodeExecutable = process.execPath
103
+ if (!nodeExecutable) {
104
+ throw new Error('Unable to locate the current Node.js executable for Beads bootstrap.')
105
+ }
106
+
107
+ log('Running Beads setup')
108
+ const result = await runCommand(nodeExecutable, [beadsScript, targetPath], {
109
+ stdio: 'inherit',
110
+ env: {
111
+ ...process.env,
112
+ HOME: paths.home,
113
+ },
114
+ })
115
+ if (result.code !== 0) {
116
+ throw new Error('Beads initialization failed')
117
+ }
118
+ }
119
+
120
+ async function createRepoStructure(targetPath, agentsTemplate) {
121
+ log('Creating repo/ working directory')
122
+ await ensureDir(path.join(targetPath, 'repo'))
123
+
124
+ log('Copying AGENTS template into repo/')
125
+ await fs.copyFile(agentsTemplate, path.join(targetPath, 'repo', 'AGENTS.md'))
126
+
127
+ await fs.writeFile(path.join(targetPath, 'repo', '.gitkeep'), '', 'utf8')
128
+ }
129
+
130
+ async function createInitialCommit(targetPath) {
131
+ log('Creating initial git checkpoint')
132
+ const addResult = await runCommand('git', ['-C', targetPath, 'add', '.'], { stdio: 'inherit' })
133
+ if (addResult.code !== 0) {
134
+ throw new Error('Initial git add failed')
135
+ }
136
+
137
+ const commitResult = await runCommand('git', ['-C', targetPath, 'commit', '-m', 'init'], { stdio: 'inherit' })
138
+ if (commitResult.code !== 0) {
139
+ throw new Error('Initial git commit failed. Configure git user.name/user.email or inspect repository state and retry.')
140
+ }
141
+ }
142
+
143
+ async function maybeOpenOpencode(targetPath, openAfterInit) {
144
+ if (!openAfterInit) {
145
+ return
146
+ }
147
+
148
+ log('Opening OpenCode in repo/')
149
+ const result = await runCommand('opencode', [], {
150
+ stdio: 'inherit',
151
+ cwd: path.join(targetPath, 'repo'),
152
+ })
153
+ if (result.code !== 0) {
154
+ throw new Error('Failed to launch OpenCode')
155
+ }
156
+ }
157
+
158
+ export async function runInit(args = []) {
159
+ const paths = buildPaths()
160
+ const { openAfterInit, targetInput } = parseInitArgs(args)
161
+ const { beadsScript, agentsTemplate } = await assertRequiredFiles(paths)
162
+ const targetPath = await resolveTarget(targetInput)
163
+
164
+ log(`Target: ${targetPath}`)
165
+ log('Ensuring .ai/artifacts exists')
166
+ await ensureDir(path.join(targetPath, '.ai', 'artifacts'))
167
+
168
+ await ensureGitRepo(targetPath)
169
+ log('Ensuring .gitignore defaults')
170
+ await ensureGitignore(targetPath)
171
+ await runBeadsBootstrap(paths, targetPath, beadsScript)
172
+ await createRepoStructure(targetPath, agentsTemplate)
173
+ await createInitialCommit(targetPath)
174
+ await maybeOpenOpencode(targetPath, openAfterInit)
175
+
176
+ if (!openAfterInit) {
177
+ log('Done')
178
+ }
179
+ }