theslopmachine 1.0.7 → 1.0.9

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.
@@ -33,7 +33,7 @@ All communication, code comments, docs, tests, and user-facing strings you add m
33
33
  - Understand the user request, existing architecture, data flow, runtime path, and test conventions before editing.
34
34
  - Prefer the smallest correct change that fully solves the problem.
35
35
  - Implement real behavior, not placeholders, fake success paths, no-op jobs, disconnected forms, or route-only shells.
36
- - If subagents can safely parallelize independent investigation, implementation, or verification work, do not hesitate to use them to speed up delivery.
36
+ - Use subagents only for bounded investigation or verification that clearly supports your current assignment. Do not create separate implementation lanes, delegate broad product ownership, or use subagents to bypass the current task scope.
37
37
  - Keep frontend, backend, data, permissions, docs, and tests aligned when those surfaces exist.
38
38
  - Do not silently narrow scope for convenience.
39
39
  - If a requirement is ambiguous but a safe, request-faithful default exists, use it and proceed.
@@ -54,7 +54,8 @@ All communication, code comments, docs, tests, and user-facing strings you add m
54
54
  ## Testing And Coverage
55
55
 
56
56
  - Tests should prove behavior and side effects, not only existence or rendering.
57
- - Add or update tests for meaningful changes, especially business rules, authorization, validation, failure paths, persistence/state changes, and integration boundaries.
57
+ - Add or update tests for every implementation change. Target full meaningful coverage of delivered behavior, not just a smoke path.
58
+ - Cover implementation at the strongest relevant layers: unit tests for business logic, API/integration HTTP tests for every endpoint or interface, and E2E/platform tests for user-facing flows.
58
59
  - Include negative and boundary coverage when relevant: unauthenticated, unauthorized, not found, conflicts, invalid input, empty states, duplicate actions, object ownership, and sensitive data exposure.
59
60
  - For frontend work, test loading, empty, submitting, disabled, success, error, and re-entry states when those states are relevant.
60
61
  - For backend-backed frontend work, verify the frontend uses the real client/API path and the backend performs real handler/service/data work.
@@ -105,13 +105,31 @@ Good Claude-message style:
105
105
 
106
106
  - Use `read`, `glob`, and `grep` to build evidence before acting.
107
107
  - Use `bash` for the packaged Claude live scripts, tmux helper execution, git, package managers, tests, Docker, runtime checks, and artifact commands.
108
- - Use `task` for bounded non-Claude specialist work: clarification worker, planning worker, evaluator session, or focused research/review.
109
- - Do not use `task`, OpenCode subagents, helper agents, local edits, or ad hoc shell scripting for product development, product bugfixes, product test authoring, product docs authored by the implementation lane, or implementation verification guidance. Those must go through live Claude lanes using the packaged Claude utilities.
108
+ - Use `task` only for bounded non-Claude owner-side specialist work: `general` for clarification/planning/research, `evaluator` for audit/evaluation work, and `explore` for read-only discovery when useful.
109
+ - Never use `task` with `developer`, `implement`, `helper`, maintenance, or ad hoc coding subagents for product implementation, product bugfixes, product test authoring, product docs authored by the implementation lane, or implementation verification guidance. Those must go through live Claude lanes using the packaged Claude utilities.
110
+ - Do not use OpenCode subagents, local edits, raw `claude` commands, manual tmux typing, or untracked helper scripts as a substitute for Claude live-lane implementation. The only normal interaction path with Claude lanes is `claude_live_launch.mjs`, `claude_live_turn.mjs`, `claude_live_status.mjs`, and `claude_live_stop.mjs`.
110
111
  - Use `question` only for material user decisions that cannot be resolved by a prompt-faithful default.
111
112
  - Use `edit`/`write` only for owner-side workflow files, reports, packaged prompts/templates, and tiny safe owner fixes that do not substitute for Claude implementation work. If a tiny owner fix touches product code/docs, notify the active Claude lane and ask it to inspect/acknowledge before continuing.
112
113
  - Use `todowrite` for substantial multi-step owner work when tracking improves reliability.
113
114
  - Use Context7/Exa only when current documentation or external facts are needed.
114
115
 
116
+ ## Owner Subagent And Claude Allowlist
117
+
118
+ This owner may only launch these OpenCode subagent types:
119
+ - `general`: clarification worker, owner-private planning worker, focused owner-side research, or small non-implementation workflow analysis.
120
+ - `evaluator`: audit, evaluation, fix-check, and coverage/README review sessions only.
121
+ - `explore`: read-only discovery when targeted codebase inspection is useful.
122
+
123
+ All other OpenCode subagent types are forbidden for owner use unless the user explicitly overrides this rule. In particular, never use `developer`, `implement`, `helper`, `helper-new`, `librarian`, maintenance agents, or ad hoc coding agents for product implementation, bugfixes, tests, product docs, or implementation verification guidance.
124
+
125
+ Product implementation and remediation must use Claude through the packaged live scripts only:
126
+ - `claude_live_launch.mjs`
127
+ - `claude_live_turn.mjs`
128
+ - `claude_live_status.mjs`
129
+ - `claude_live_stop.mjs`
130
+
131
+ Do not interact with Claude through raw `claude` commands, manual tmux typing, untracked helper scripts, Claude tools, OpenCode developer subagents, or local owner edits as a substitute for live-lane implementation.
132
+
115
133
  ## Claude Lane Rules
116
134
 
117
135
  - Required skill for all Claude lane activity: `claude-worker-management`. Load and follow it before launching, resuming, messaging, checking status, recovering, stopping, or recording any Claude lane.
@@ -130,7 +148,7 @@ Good Claude-message style:
130
148
  - Claude messages must read like a lead engineer talking to another engineer.
131
149
  - Use private planning only to decide the next normal Claude instruction; do not mention private planning or its existence.
132
150
  - Include what to build or fix, why it matters, the broad affected area, expected behavior, and useful verification.
133
- - For substantial Claude turns, include a normal human reminder that Claude should use subagents when they can safely parallelize independent investigation, implementation, or verification work.
151
+ - For substantial Claude turns, you may include a normal human reminder that Claude can use its own built-in subagents for bounded investigation, implementation support, or verification inside the same Claude lane. Do not frame Claude subagents as separate workflow lanes, and do not create OpenCode subagents to help Claude implement.
134
152
  - Keep ordinary issue prompts at module/product level. Avoid file/line details unless the user explicitly asks you to pass exact references.
135
153
  - Do not paste, summarize, cite, name, or mention hidden plans.
136
154
  - Do not combine original-prompt orientation, design, implementation, verification, and bugfix work into one large prompt.
@@ -104,13 +104,24 @@ Good worker-message style:
104
104
  ## Tool Responsibilities
105
105
 
106
106
  - Use `read`, `glob`, and `grep` to build evidence before acting.
107
- - Use `task` for bounded specialist work: clarification worker, planning worker, developer session, evaluator session, or focused research/review.
107
+ - Use `task` only for these bounded roles: `general` for clarification/planning/research, `developer` for the active implementation or bugfix session, `evaluator` for audit/evaluation work, and `explore` for read-only discovery when useful.
108
+ - Do not use `implement`, `helper`, maintenance, or extra ad hoc subagents for product implementation unless the user explicitly asks. Keep implementation in the tracked active developer session except for evaluator-isolated work or a recorded recovery/context reason.
108
109
  - Use `question` only for material user decisions that cannot be resolved by a prompt-faithful default.
109
110
  - Use `bash` for git, package managers, tests, Docker, CLIs, runtime checks, and artifact commands.
110
111
  - Use `edit`/`write` for owner-side workflow files, tiny safe fixes, reports, and packaged prompts/templates.
111
112
  - Use `todowrite` for substantial multi-step owner work when tracking improves reliability.
112
113
  - Use Context7/Exa only when current documentation or external facts are needed.
113
114
 
115
+ ## Owner Subagent Allowlist
116
+
117
+ This owner may only launch these OpenCode subagent types:
118
+ - `general`: clarification worker, owner-private planning worker, focused owner-side research, or small non-implementation workflow analysis.
119
+ - `developer`: the tracked active implementation/bugfix/readiness developer session only.
120
+ - `evaluator`: audit, evaluation, fix-check, and coverage/README review sessions only.
121
+ - `explore`: read-only discovery when targeted codebase inspection is useful.
122
+
123
+ All other subagent types are forbidden for owner use unless the user explicitly overrides this rule. Do not use `implement`, `helper`, `helper-new`, `librarian`, maintenance agents, or extra ad hoc coding agents for product implementation, bugfixes, tests, or product docs. If work is implementation-shaped, route it through the tracked `developer` session.
124
+
114
125
  ## Delegation Rules
115
126
 
116
127
  - Developer messages must read like a lead engineer talking to another engineer.
@@ -19,7 +19,7 @@ All communication, code comments, docs, tests, and user-facing strings you add m
19
19
  - Understand the request, architecture, data flow, runtime path, and test conventions before editing.
20
20
  - Prefer the smallest correct change that fully solves the problem.
21
21
  - Implement real behavior, not placeholders, fake success paths, no-op jobs, disconnected forms, or route-only shells.
22
- - If subagents can safely parallelize independent investigation, implementation, or verification work, do not hesitate to use them to speed up delivery.
22
+ - Use built-in subagents only for bounded investigation, implementation support, or verification inside the current session. Do not treat subagents as separate workflow lanes, delegate broad product ownership, or use them to bypass the current task scope.
23
23
  - Keep frontend, backend, data, permissions, docs, and tests aligned when those surfaces exist.
24
24
  - Do not silently narrow scope for convenience.
25
25
  - If a safe, request-faithful default exists, use it and proceed.
@@ -40,7 +40,8 @@ All communication, code comments, docs, tests, and user-facing strings you add m
40
40
  ## Testing And Coverage
41
41
 
42
42
  - Tests must prove behavior and side effects, not only existence or rendering.
43
- - Add or update tests for meaningful changes, especially business rules, authorization, validation, failure paths, persistence/state changes, and integration boundaries.
43
+ - Add or update tests for every implementation change. Target full meaningful coverage of delivered behavior, not just a smoke path.
44
+ - Cover implementation at the strongest relevant layers: unit tests for business logic, API/integration HTTP tests for every endpoint or interface, and E2E/platform tests for user-facing flows.
44
45
  - Cover negative and boundary paths when relevant: unauthenticated, unauthorized, not found, conflicts, invalid input, empty states, duplicate actions, object ownership, and sensitive data exposure.
45
46
  - For frontend work, test loading, empty, submitting, disabled, success, error, and re-entry states when those states are relevant.
46
47
  - For backend-backed frontend work, verify the frontend uses the real client/API path and the backend performs real handler/service/data work.
@@ -11,6 +11,8 @@ Use this skill whenever the `slopmachine-claude` owner launches, resumes, messag
11
11
 
12
12
  Claude lane management is owner-private. Claude must receive normal human engineering messages only. Never mention the bridge, tmux, runtime directory, metadata, Beads, phase names, workflow state, hidden files, or session bookkeeping in Claude-facing prompts.
13
13
 
14
+ The owner must use Claude only through the packaged live scripts for product implementation and remediation. Do not use OpenCode developer/implement/helper subagents, raw `claude` commands, manual tmux typing, Claude tools, or untracked scripts as an alternate implementation path.
15
+
14
16
  ## Lane Policy
15
17
 
16
18
  - Exactly one Claude implementation lane is active at a time. The active lane must correspond to the current phase purpose and be named in `../.ai/metadata.json` before any launch, resume, status check, or turn.
@@ -51,7 +53,7 @@ Use the accepted clarifications below to create docs/design.md from the design t
51
53
  When the work has independent parts, include a natural reminder such as:
52
54
 
53
55
  ```text
54
- If subagents can help you parallelize independent investigation, implementation, or verification work safely, use them.
56
+ If your built-in subagents can safely help with bounded investigation, implementation support, or verification inside this same session, use them.
55
57
  ```
56
58
 
57
59
  Do not send bare prompts such as `continue`, `next`, or `fix it` when the acceptance target changed. Send the current task, desired output, visible files, and verification or review expectation.
@@ -15,6 +15,8 @@ Use this skill for startup preflight, session policy, metadata consistency, lane
15
15
  - Confirm task docs are limited to `./docs/questions.md`, `./docs/design.md`, and `./docs/api-spec.md` when applicable.
16
16
  - Before any developer or Claude design session begins, confirm Phase 1 clarification and faithfulness review have been accepted.
17
17
  - The first implementation-session message should be plain product orientation and should say not to build yet because design/planning comes first.
18
+ - Owner-side subagent use is allowlisted only. In `slopmachine`, use only `general`, `developer`, `evaluator`, and `explore` for their named workflow roles. In `slopmachine-claude`, use only `general`, `evaluator`, and `explore`; product implementation must go through the packaged Claude live scripts.
19
+ - Do not use helper, implement, maintenance, librarian, or ad hoc coding subagents unless the user explicitly overrides the owner allowlist.
18
20
 
19
21
  ## Session Policy
20
22
 
@@ -22,11 +22,27 @@ Prompt like a human developer working with an AI coding assistant.
22
22
 
23
23
  Use direct wording such as:
24
24
  - `I checked the user module and found a missing authorization test. Please add that and rerun the relevant tests.`
25
- - `Continue with the invoice module from docs/design.md. Implement the create/list/detail flow and include the tests for the main success and validation paths.`
25
+ - `Continue with the invoice module. Build the create/list/detail flow against the existing product contract and cover the main success and validation paths.`
26
26
  - `The scaffold looks mostly right, but the README still describes a command that does not exist. Fix the README and rerun the local smoke check.`
27
27
 
28
28
  Do not send robotic process language. Do not require a specific response format. Do not repeat standing instructions every turn. Do not dump, name, summarize, or mention the private plan. Give only the current objective, broad module/surface area, discovered issues, and useful verification request.
29
29
 
30
+ Do not keep restating visible doc paths in routine follow-up prompts when the same session already knows the project contract. It is fine to say `existing product contract`, `accepted docs`, or simply name the module. Mention exact doc paths only when orienting a new session, resolving confusion, or asking for a final contract check.
31
+
32
+ For larger module slices, group expectations by user/business behavior instead of turning every endpoint, field, and negative case into a long checklist. Ask for real backend-backed behavior, visible UI states, and meaningful success/failure tests, but keep the wording natural.
33
+
34
+ Example of a good larger module prompt:
35
+
36
+ ```text
37
+ Continue with inventory parcel intake and production planning materials.
38
+
39
+ Build these as real backend-backed workflows, not static screens. Parcels should cover intake, duplicate detection, import status/errors, photo-label uploads, print-label job outcomes, revisions, closing behavior, pickup-code reuse, and audit history. Materials/planning should cover materials, UOM conversions, lots, BOM versioning/effective windows, approvals, substitutes, yield loss, and costing based on the latest received lot costs with missing-cost handling.
40
+
41
+ On the Angular side, make the inventory and materials workspaces feel complete: loading, empty, validation, submitting, duplicate, printing, missing-cost, success, and error states should all be visible where they matter.
42
+
43
+ Add focused tests for the important happy paths and failure paths, especially duplicate parcels, pickup-code collisions/reuse, invalid phone/tracking/import rows, printer unavailable, invalid UOM conversions, BOM effective-window conflicts, approval audit trails, latest-lot costing, and missing-cost behavior. Run the targeted checks when you're done.
44
+ ```
45
+
30
46
  When sending issues back, do not pass file names, line numbers, report snippets, or exact internal evidence unless the user explicitly asks for that. Keep it at the level of the module and behavior: `I found issues in the auth module. The access control case for other users' records is not covered properly, and the tests are missing that case.`
31
47
 
32
48
  Do not say `the review found`, `the evaluation found`, or `the audit found`. The owner should speak naturally: `I checked this and found...`.
@@ -23,10 +23,11 @@ The design must:
23
23
  - preserve the original business goal and required user outcomes
24
24
  - incorporate accepted clarifications and requirements without narrowing them
25
25
  - identify the project type, stack, actors, roles, main flows, modules, data, UI/API surfaces, security boundaries, assumptions, and verification strategy
26
+ - define the testing contract as part of the visible design: every API/interface endpoint must have positive and negative tests, unit coverage must target 90%+ for meaningful business logic, and user-facing applications must include full E2E/platform coverage for the main user journeys unless a surface is genuinely not applicable
26
27
  - make meaningful assumptions explicit
27
28
  - mark unresolved items only when a real decision is still needed
28
29
  - identify API/interface surfaces that should be captured in `./docs/api-spec.md`
29
- - avoid vague placeholders such as `TBD`, `later`, `standard CRUD`, `normal auth`, or `basic tests` for important behavior
30
+ - avoid vague placeholders such as `TBD`, `later`, `standard CRUD`, `normal auth`, `basic tests`, or `test where appropriate` for important behavior
30
31
 
31
32
  ## Boundary
32
33
 
@@ -95,14 +95,21 @@ Cover where relevant: authentication, route authorization, object authorization,
95
95
 
96
96
  This is a design-level strategy, not an execution checklist.
97
97
 
98
+ Required testing contract:
99
+ - All API/interface endpoints must have test coverage for successful behavior and important negative/error cases. If there is no API/interface surface, state `Not Applicable` with the reason.
100
+ - Meaningful business logic must target 90%+ unit coverage. If a component cannot be unit-tested meaningfully, state the exception and the replacement proof layer.
101
+ - User-facing applications must have full E2E/platform coverage for the main user journeys, including success, validation/failure, and recovery states. If E2E/platform testing is not applicable, state why and what proof replaces it.
102
+
98
103
  | Surface / risk | Expected proof layer | Notes |
99
104
  |---|---|---|
100
105
  | core happy paths | | |
101
106
  | key failure paths | | |
102
107
  | security boundaries | | |
103
- | API/interface behavior | | |
108
+ | API/interface behavior | endpoint tests for every endpoint, including positive and negative cases | |
104
109
  | UI states / interactions | | |
105
110
  | integration paths | | |
111
+ | unit coverage | 90%+ meaningful business-logic coverage | |
112
+ | E2E/platform journeys | full main-journey coverage for user-facing apps | |
106
113
 
107
114
  ## 12. API Spec Handoff
108
115
 
@@ -16,7 +16,7 @@ This file contains product engineering rules for the current project.
16
16
  - Use English for development communication, code comments, docs, tests, reports, and user-facing strings unless the original prompt explicitly requires another language.
17
17
  - Read the existing code before making assumptions.
18
18
  - Follow the prompts properly and carefully.
19
- - If subagents can safely parallelize independent investigation, implementation, or verification work, do not hesitate to use them to speed up delivery.
19
+ - Use subagents only for bounded investigation or verification that clearly supports the current assignment. Do not create separate implementation lanes, delegate broad product ownership, or use subagents to bypass the current task scope.
20
20
  - Work in coherent vertical slices from user/operator surface through logic, persistence/state, validation, failure states, docs, and tests where applicable.
21
21
  - Once given a bounded objective, keep going autonomously until it is complete or genuinely blocked.
22
22
  - If the request conflicts with `./docs/design.md`, `./docs/api-spec.md`, or `./docs/questions.md`, stop and ask for the narrow correction needed.
@@ -40,6 +40,8 @@ This file contains product engineering rules for the current project.
40
40
  - Do not run Docker or `run_tests.sh` unless asked.
41
41
  - For Android and iOS projects, document native build/run/debug/verification paths; do not force Docker as the primary runtime when platform tooling is inherently native.
42
42
  - Use `unit_tests/` for unit tests and `API_tests/` for API/integration HTTP tests when those surfaces exist.
43
+ - Every implementation change should include tests for the behavior it owns. Target full meaningful coverage across unit, API/integration, and E2E/platform layers where those surfaces exist.
44
+ - API/interface endpoints should have real positive and negative tests for exact behavior. User-facing flows should have E2E/platform coverage for the main journeys and important failure/recovery states.
43
45
  - Prefer the fastest meaningful targeted checks during ordinary implementation.
44
46
  - Never claim a command passed unless you actually ran it and saw the result.
45
47
  - If required verification cannot run in the current environment, report it as unverified with the exact risk.
@@ -16,7 +16,7 @@ This file contains product engineering rules for the current project.
16
16
  - Use English for development communication, code comments, docs, tests, reports, and user-facing strings unless the original prompt explicitly requires another language.
17
17
  - Read the existing code before making assumptions.
18
18
  - Follow the prompts properly and carefully.
19
- - If subagents can safely parallelize independent investigation, implementation, or verification work, do not hesitate to use them to speed up delivery.
19
+ - Use built-in subagents only for bounded investigation, implementation support, or verification inside the current session. Do not treat subagents as separate workflow lanes, delegate broad product ownership, or use them to bypass the current task scope.
20
20
  - Work in coherent vertical slices from user/operator surface through logic, persistence/state, validation, failure states, docs, and tests where applicable.
21
21
  - Once given a bounded objective, keep going autonomously until it is complete or genuinely blocked.
22
22
  - If the request conflicts with `./docs/design.md`, `./docs/api-spec.md`, or `./docs/questions.md`, stop and ask for the narrow correction needed.
@@ -40,6 +40,8 @@ This file contains product engineering rules for the current project.
40
40
  - Do not run Docker or `run_tests.sh` unless asked.
41
41
  - For Android and iOS projects, document native build/run/debug/verification paths; do not force Docker as the primary runtime when platform tooling is inherently native.
42
42
  - Use `unit_tests/` for unit tests and `API_tests/` for API/integration HTTP tests when those surfaces exist.
43
+ - Every implementation change should include tests for the behavior it owns. Target full meaningful coverage across unit, API/integration, and E2E/platform layers where those surfaces exist.
44
+ - API/interface endpoints should have real positive and negative tests for exact behavior. User-facing flows should have E2E/platform coverage for the main journeys and important failure/recovery states.
43
45
  - Prefer the fastest meaningful targeted checks during ordinary implementation.
44
46
  - Never claim a command passed unless you actually ran it and saw the result.
45
47
  - If required verification cannot run in the current environment, report it as unverified with the exact risk.
@@ -113,14 +113,12 @@ Packages the Claude project directory associated with a task root.
113
113
 
114
114
  Required:
115
115
  - `--task-root <task-root>`
116
- - `--output <zip-path>`
117
116
 
118
117
  Important options:
119
- - `--session-ids <comma-separated-session-ids>` narrows the expected tracked sessions
120
- - `--metadata-file <path>` overrides the default `<task-root>/metadata.json`
118
+ - `--output <zip-path>` writes the zip to a custom path; default is `<task-root>/claude-sessions.zip`
121
119
  - `--label <text>` adds reporting context
122
120
 
123
- The helper normalizes top-level JSONL transcripts, preserves the raw project directory structure, and writes a single zip.
121
+ The helper copies the Claude project/session directory as-is, removes `.DS_Store` files and top-level `.jsonl` transcripts under 25KB from the staged copy, zips the folder contents directly, and writes a single zip. It does not normalize or rewrite transcript files.
124
122
 
125
123
  ### `analyze_claude_project_dir.mjs`
126
124
 
@@ -184,10 +182,6 @@ Common usage:
184
182
  - `python3 convert_ai_session.py -i session.jsonl -o converted.json`
185
183
  - `python3 convert_ai_session.py -i session.jsonl --format claude`
186
184
 
187
- ### `normalize_claude_session.py`
188
-
189
- Normalizes Claude JSONL transcript structure for packaging and review.
190
-
191
185
  ### `strip_session_parent.py`
192
186
 
193
187
  Removes parent/session ancestry fields from exported session artifacts when needed for sanitized review.
@@ -8,25 +8,21 @@ import { spawn } from 'node:child_process'
8
8
  import { parseArgs, emitFailure, emitSuccess, printUsageAndExit } from './claude_worker_common.mjs'
9
9
 
10
10
  const argv = parseArgs(process.argv.slice(2))
11
+ const TINY_ROOT_TRANSCRIPT_MAX_BYTES = 25 * 1024
11
12
 
12
13
  if (argv.help === '1') {
13
14
  printUsageAndExit(`Usage:
14
- node ~/slopmachine/utils/package_claude_session.mjs --task-root <task-root> --output <zip-path> [options]
15
+ node ~/slopmachine/utils/package_claude_session.mjs --task-root <task-root> [options]
15
16
 
16
17
  Required:
17
18
  --task-root <task-root>
18
- --output <zip-path>
19
19
 
20
20
  Options:
21
+ --output <zip-path> Zip output path (default: <task-root>/claude-sessions.zip)
21
22
  --label <text> Optional package label for reporting
22
- --metadata-file <path> Metadata JSON containing the original prompt (default: ./metadata.json from task root)
23
23
  `)
24
24
  }
25
25
 
26
- const SMALL_PRE_ANCHOR_TRANSCRIPT_MAX_BYTES = 50 * 1024
27
- const TINY_TRANSCRIPT_PRUNE_MAX_BYTES = 25 * 1024
28
- const EARLY_USER_MESSAGE_LIMIT = 8
29
-
30
26
  async function pathExists(targetPath) {
31
27
  try {
32
28
  await fs.access(targetPath)
@@ -82,215 +78,48 @@ async function createZipArchive(sourceDir, outputPath) {
82
78
  throw lastError || new Error('No usable PowerShell shell was found for zip creation')
83
79
  }
84
80
 
85
- try {
86
- const result = await run('zip', ['-9', '-q', '-r', outputPath, '.'], sourceDir)
87
- if (result.code !== 0) {
88
- throw new Error((result.stderr || result.stdout).trim() || `zip failed with exit ${result.code}`)
89
- }
90
- } catch (error) {
91
- throw new Error(error instanceof Error ? error.message : 'zip command is required to package Claude sessions')
92
- }
93
- }
94
-
95
- async function normalizeClaudeJsonlFile(inputPath) {
96
- const normalizerScript = path.join(path.dirname(new URL(import.meta.url).pathname), 'normalize_claude_session.py')
97
- const outputPath = `${inputPath}.normalized`
98
- await fs.rm(outputPath, { force: true }).catch(() => {})
99
- const result = await run('python3', [normalizerScript, inputPath, '--output', outputPath], path.dirname(inputPath))
81
+ const result = await run('zip', ['-9', '-q', '-r', outputPath, '.'], sourceDir)
100
82
  if (result.code !== 0) {
101
- throw new Error(`Failed to normalize Claude session file ${path.basename(inputPath)}: ${(result.stderr || result.stdout).trim()}`)
102
- }
103
- await fs.rm(inputPath, { force: true })
104
- await fs.rename(outputPath, inputPath)
105
- }
106
-
107
- function normalizeForPromptMatch(value) {
108
- return String(value || '')
109
- .replace(/\r\n/g, '\n')
110
- .replace(/[ \t]+/g, ' ')
111
- .replace(/\n{3,}/g, '\n\n')
112
- .trim()
113
- }
114
-
115
- function buildPromptNeedles(prompt) {
116
- const normalized = normalizeForPromptMatch(prompt)
117
- if (!normalized) return []
118
- const needles = [normalized]
119
- const paragraphs = normalized
120
- .split(/\n\s*\n/)
121
- .map((part) => part.trim())
122
- .filter((part) => part.length >= 120)
123
-
124
- for (const paragraph of paragraphs.slice(0, 5)) {
125
- needles.push(paragraph)
83
+ throw new Error((result.stderr || result.stdout).trim() || `zip failed with exit ${result.code}`)
126
84
  }
127
-
128
- if (normalized.length > 2000) {
129
- needles.push(normalized.slice(0, 2000).trim())
130
- }
131
-
132
- return [...new Set(needles.filter((needle) => needle.length >= 80))]
133
- }
134
-
135
- function extractTextFromClaudeContent(content) {
136
- if (typeof content === 'string') return content
137
- if (Array.isArray(content)) {
138
- return content.map((item) => {
139
- if (typeof item === 'string') return item
140
- if (item && typeof item === 'object') {
141
- if (typeof item.text === 'string') return item.text
142
- if (typeof item.content === 'string') return item.content
143
- }
144
- return ''
145
- }).join('\n')
146
- }
147
- if (content && typeof content === 'object') {
148
- if (typeof content.text === 'string') return content.text
149
- if (typeof content.content === 'string') return content.content
150
- }
151
- return ''
152
- }
153
-
154
- function extractClaudeUserText(record) {
155
- if (!record || typeof record !== 'object') return ''
156
- const role = record.message?.role || record.role || record.type
157
- if (role !== 'user' && record.type !== 'user') return ''
158
- return extractTextFromClaudeContent(record.message?.content ?? record.content)
159
85
  }
160
86
 
161
- function extractClaudeTimestamp(record) {
162
- const candidates = [
163
- record?.timestamp,
164
- record?.created_at,
165
- record?.message?.timestamp,
166
- record?.message?.created_at,
167
- ]
168
- for (const candidate of candidates) {
169
- if (typeof candidate !== 'string' && typeof candidate !== 'number') continue
170
- const value = typeof candidate === 'number' ? candidate : Date.parse(candidate)
171
- if (Number.isFinite(value)) return value
172
- }
173
- return null
174
- }
175
-
176
- async function readOriginalPrompt({ cwd, metadataFile }) {
177
- const resolvedMetadataFile = metadataFile
178
- ? path.resolve(metadataFile)
179
- : path.resolve(cwd, 'metadata.json')
180
- if (!await pathExists(resolvedMetadataFile)) {
181
- return { prompt: '', metadataFile: resolvedMetadataFile, missing: true }
182
- }
183
- const metadata = JSON.parse(await fs.readFile(resolvedMetadataFile, 'utf8'))
184
- const prompt = typeof metadata?.prompt === 'string' ? metadata.prompt : ''
185
- return { prompt, metadataFile: resolvedMetadataFile, missing: false }
186
- }
87
+ async function removeDsStoreFiles(rootDir) {
88
+ const entries = await fs.readdir(rootDir, { withFileTypes: true })
187
89
 
188
- async function inspectTranscriptForPromptAnchor(transcriptPath, promptNeedles) {
189
- const stat = await fs.stat(transcriptPath)
190
- const text = await fs.readFile(transcriptPath, 'utf8')
191
- const lines = text.split('\n')
192
- let firstTimestamp = null
193
- let userMessagesSeen = 0
194
- let promptMatched = false
195
-
196
- for (const line of lines) {
197
- if (!line.trim()) continue
198
- let record = null
199
- try {
200
- record = JSON.parse(line)
201
- } catch {
90
+ for (const entry of entries) {
91
+ const absolutePath = path.join(rootDir, entry.name)
92
+ if (entry.isDirectory()) {
93
+ await removeDsStoreFiles(absolutePath)
202
94
  continue
203
95
  }
204
- if (firstTimestamp == null) {
205
- firstTimestamp = extractClaudeTimestamp(record)
206
- }
207
- const userText = extractClaudeUserText(record)
208
- if (!userText) continue
209
- userMessagesSeen += 1
210
- const normalizedUserText = normalizeForPromptMatch(userText)
211
- if (promptNeedles.some((needle) => normalizedUserText.includes(needle))) {
212
- promptMatched = true
213
- break
214
- }
215
- if (userMessagesSeen >= EARLY_USER_MESSAGE_LIMIT) break
216
- }
217
-
218
- return {
219
- path: transcriptPath,
220
- size: stat.size,
221
- mtimeMs: stat.mtimeMs,
222
- firstTimestamp: firstTimestamp ?? stat.mtimeMs,
223
- promptMatched,
224
- userMessagesSeen,
225
- }
226
- }
227
96
 
228
- async function filterPrePromptTinyTranscripts(transcriptTargets, { cwd, metadataFile }) {
229
- const { prompt, metadataFile: resolvedMetadataFile, missing } = await readOriginalPrompt({ cwd, metadataFile })
230
- const promptNeedles = buildPromptNeedles(prompt)
231
- if (missing || promptNeedles.length === 0) {
232
- return {
233
- anchor: null,
234
- removed: [],
235
- inspected: [],
236
- filter_applied: false,
237
- filter_reason: missing ? `metadata prompt file missing: ${resolvedMetadataFile}` : 'metadata prompt is empty or too short for safe matching',
238
- metadata_file: resolvedMetadataFile,
97
+ if (entry.isFile() && entry.name === '.DS_Store') {
98
+ await fs.rm(absolutePath, { force: true })
239
99
  }
240
100
  }
101
+ }
241
102
 
242
- const inspected = []
243
- for (const transcriptTarget of transcriptTargets) {
244
- inspected.push(await inspectTranscriptForPromptAnchor(transcriptTarget, promptNeedles))
245
- }
103
+ async function removeTinyRootTranscripts(rootDir) {
104
+ const entries = await fs.readdir(rootDir, { withFileTypes: true })
105
+ const removed = []
246
106
 
247
- inspected.sort((left, right) => left.firstTimestamp - right.firstTimestamp || left.path.localeCompare(right.path))
248
- const anchor = inspected.find((entry) => entry.promptMatched) || null
249
- if (!anchor) {
250
- return {
251
- anchor: null,
252
- removed: [],
253
- inspected,
254
- filter_applied: false,
255
- filter_reason: 'no top-level Claude transcript matched the original prompt in early user messages',
256
- metadata_file: resolvedMetadataFile,
107
+ for (const entry of entries) {
108
+ if (!entry.isFile() || !entry.name.endsWith('.jsonl')) {
109
+ continue
257
110
  }
258
- }
259
111
 
260
- const removed = []
261
- for (const entry of inspected) {
262
- if (entry.path === anchor.path) continue
263
- const tinyNoise = entry.size < TINY_TRANSCRIPT_PRUNE_MAX_BYTES
264
- const smallPreAnchor = entry.firstTimestamp < anchor.firstTimestamp && entry.size < SMALL_PRE_ANCHOR_TRANSCRIPT_MAX_BYTES
265
- if (!tinyNoise && !smallPreAnchor) continue
266
- await fs.rm(entry.path, { force: true })
267
- removed.push({
268
- ...entry,
269
- removalReason: tinyNoise
270
- ? `root-level transcript below ${TINY_TRANSCRIPT_PRUNE_MAX_BYTES} bytes`
271
- : `pre-anchor transcript below ${SMALL_PRE_ANCHOR_TRANSCRIPT_MAX_BYTES} bytes`,
272
- })
273
- }
112
+ const absolutePath = path.join(rootDir, entry.name)
113
+ const stat = await fs.stat(absolutePath)
114
+ if (stat.size >= TINY_ROOT_TRANSCRIPT_MAX_BYTES) {
115
+ continue
116
+ }
274
117
 
275
- return {
276
- anchor,
277
- removed,
278
- inspected,
279
- filter_applied: true,
280
- filter_reason: 'removed root-level transcripts under 25KB except the prompt anchor, and pre-anchor transcripts under 50KB',
281
- metadata_file: resolvedMetadataFile,
118
+ await fs.rm(absolutePath, { force: true })
119
+ removed.push({ file: entry.name, size: stat.size })
282
120
  }
283
- }
284
121
 
285
- async function preserveTranscriptTimes(transcriptTargets, inspected) {
286
- const inspectedByPath = new Map(inspected.map((entry) => [entry.path, entry]))
287
- for (const transcriptTarget of transcriptTargets) {
288
- if (!await pathExists(transcriptTarget)) continue
289
- const entry = inspectedByPath.get(transcriptTarget)
290
- if (!entry?.firstTimestamp || !Number.isFinite(entry.firstTimestamp)) continue
291
- const timestamp = new Date(entry.firstTimestamp)
292
- await fs.utimes(transcriptTarget, timestamp, timestamp).catch(() => {})
293
- }
122
+ return removed
294
123
  }
295
124
 
296
125
  async function listFilesRecursive(rootDir, relativePrefix = '') {
@@ -314,22 +143,6 @@ async function listFilesRecursive(rootDir, relativePrefix = '') {
314
143
  return files
315
144
  }
316
145
 
317
- async function removeDsStoreFiles(rootDir) {
318
- const entries = await fs.readdir(rootDir, { withFileTypes: true })
319
-
320
- for (const entry of entries) {
321
- const absolutePath = path.join(rootDir, entry.name)
322
- if (entry.isDirectory()) {
323
- await removeDsStoreFiles(absolutePath)
324
- continue
325
- }
326
-
327
- if (entry.isFile() && entry.name === '.DS_Store') {
328
- await fs.rm(absolutePath, { force: true })
329
- }
330
- }
331
- }
332
-
333
146
  async function resolveClaudeProjectDir(taskRoot) {
334
147
  const projectsRoot = path.join(os.homedir(), '.claude', 'projects')
335
148
  const resolvedTaskRoot = await fs.realpath(taskRoot).catch(() => path.resolve(taskRoot))
@@ -348,72 +161,31 @@ async function resolveClaudeProjectDir(taskRoot) {
348
161
  throw new Error(`Claude project directory not found for task root: ${resolvedTaskRoot}`)
349
162
  }
350
163
 
351
- async function stageClaudeProjectDir(sourceProjectDir, tempRoot) {
352
- const packageRoot = path.join(tempRoot, path.basename(sourceProjectDir))
353
- await fs.mkdir(tempRoot, { recursive: true })
354
- await fs.cp(sourceProjectDir, packageRoot, { recursive: true })
355
-
356
- const allFiles = await listFilesRecursive(packageRoot)
357
- const transcriptTargets = allFiles
358
- .filter((relativePath) => relativePath.endsWith('.jsonl') && !relativePath.includes(path.sep))
359
- .map((relativePath) => path.join(packageRoot, relativePath))
360
-
361
- return {
362
- packageRoot,
363
- transcriptTargets,
364
- }
365
- }
366
-
367
164
  try {
368
165
  const taskRoot = argv['task-root'] ? path.resolve(argv['task-root']) : null
369
166
  if (!taskRoot) {
370
167
  throw new Error('Missing --task-root')
371
168
  }
169
+
372
170
  const sourceProjectDir = await resolveClaudeProjectDir(taskRoot)
171
+ const outputPath = argv.output ? path.resolve(argv.output) : path.join(taskRoot, 'claude-sessions.zip')
373
172
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'slopmachine-claude-project-'))
374
- const { packageRoot, transcriptTargets } = await stageClaudeProjectDir(sourceProjectDir, tempRoot)
375
- await removeDsStoreFiles(packageRoot)
376
- const filterResult = await filterPrePromptTinyTranscripts(transcriptTargets, {
377
- cwd: taskRoot,
378
- metadataFile: argv['metadata-file'] || null,
379
- })
380
- for (const transcriptTargetPath of transcriptTargets) {
381
- if (!await pathExists(transcriptTargetPath)) continue
382
- await normalizeClaudeJsonlFile(transcriptTargetPath)
383
- }
384
- await preserveTranscriptTimes(transcriptTargets, filterResult.inspected)
385
- const included = (await listFilesRecursive(packageRoot)).sort((left, right) => left.localeCompare(right))
173
+ const packageRoot = path.join(tempRoot, path.basename(sourceProjectDir))
386
174
 
387
175
  try {
388
- await createZipArchive(packageRoot, argv.output)
176
+ await fs.cp(sourceProjectDir, packageRoot, { recursive: true })
177
+ await removeDsStoreFiles(packageRoot)
178
+ const tinyRootTranscriptsRemoved = await removeTinyRootTranscripts(packageRoot)
179
+ const included = (await listFilesRecursive(packageRoot)).sort((left, right) => left.localeCompare(right))
180
+ await createZipArchive(packageRoot, outputPath)
181
+
389
182
  emitSuccess(path.basename(sourceProjectDir), {
390
- output: argv.output,
183
+ output: outputPath,
391
184
  project_dir: sourceProjectDir,
392
185
  label: argv.label || null,
393
186
  included,
394
- packaging_mode: 'prompt_anchored_project_dir',
395
- normalized_transcripts_only: true,
396
- prompt_anchor: filterResult.anchor
397
- ? path.basename(filterResult.anchor.path)
398
- : null,
399
- pre_anchor_removed: filterResult.removed.map((entry) => ({
400
- file: path.basename(entry.path),
401
- size: entry.size,
402
- first_timestamp: new Date(entry.firstTimestamp).toISOString(),
403
- reason: entry.removalReason || null,
404
- })),
405
- prompt_anchor_filter_applied: filterResult.filter_applied,
406
- prompt_anchor_filter_reason: filterResult.filter_reason,
407
- prompt_anchor_metadata_file: filterResult.metadata_file,
408
- transcript_chronology: filterResult.inspected.map((entry) => ({
409
- file: path.basename(entry.path),
410
- size: entry.size,
411
- first_timestamp: new Date(entry.firstTimestamp).toISOString(),
412
- prompt_matched: entry.promptMatched,
413
- user_messages_scanned: entry.userMessagesSeen,
414
- included: !filterResult.removed.some((removed) => removed.path === entry.path),
415
- tiny_under_25kb: entry.size < TINY_TRANSCRIPT_PRUNE_MAX_BYTES,
416
- })),
187
+ tiny_root_transcripts_removed: tinyRootTranscriptsRemoved,
188
+ packaging_mode: 'raw_claude_project_dir',
417
189
  })
418
190
  } finally {
419
191
  await fs.rm(tempRoot, { recursive: true, force: true }).catch(() => {})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "theslopmachine",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "SlopMachine installer and project bootstrap CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/send-data.js CHANGED
@@ -430,14 +430,10 @@ async function exportClaudeProjectArtifacts(claudeSessions, taskRoot, stagingDir
430
430
  const utilsDir = path.join(buildPaths().slopmachineDir, 'utils')
431
431
  const packageClaudeSessionScript = path.join(utilsDir, 'package_claude_session.mjs')
432
432
  const outputPath = path.join(stagingDir, 'claude-sessions.zip')
433
- const trackedSessionIds = [...new Set(claudeSessions.map((session) => session.sessionId).filter(Boolean))]
434
-
435
433
  const packageResult = await runCommand(process.execPath, [
436
434
  packageClaudeSessionScript,
437
435
  '--task-root',
438
436
  taskRoot,
439
- '--session-ids',
440
- trackedSessionIds.join(','),
441
437
  '--label',
442
438
  'claude-sessions',
443
439
  '--output',