@punal_manalan/blender-project-harness 0.0.0-stage → 1.0.2

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 (91) hide show
  1. package/.kilo/agent/blender-grader.md +197 -0
  2. package/.kilo/agent/blender-worker.md +127 -0
  3. package/.kilo/blender-project-harness/config.example.json +71 -0
  4. package/.kilo/blender-project-harness/tools/harness/EXPECTED_FLOW.md +656 -0
  5. package/.kilo/blender-project-harness/tools/harness/blender-operations.mjs +882 -0
  6. package/.kilo/blender-project-harness/tools/harness/blender-operations.test.mjs +550 -0
  7. package/.kilo/blender-project-harness/tools/harness/blender.mjs +301 -0
  8. package/.kilo/blender-project-harness/tools/harness/blender.test.mjs +87 -0
  9. package/.kilo/blender-project-harness/tools/harness/cli.mjs +127 -0
  10. package/.kilo/blender-project-harness/tools/harness/documents.mjs +201 -0
  11. package/.kilo/blender-project-harness/tools/harness/download.mjs +94 -0
  12. package/.kilo/blender-project-harness/tools/harness/download.test.mjs +63 -0
  13. package/.kilo/blender-project-harness/tools/harness/engine.mjs +1866 -0
  14. package/.kilo/blender-project-harness/tools/harness/engine.test.mjs +2219 -0
  15. package/.kilo/blender-project-harness/tools/harness/intake.mjs +571 -0
  16. package/.kilo/blender-project-harness/tools/harness/intake.test.mjs +181 -0
  17. package/.kilo/blender-project-harness/tools/harness/package-lock.json +1227 -0
  18. package/.kilo/blender-project-harness/tools/harness/package.json +15 -0
  19. package/.kilo/blender-project-harness/tools/harness/plugin.mjs +550 -0
  20. package/.kilo/blender-project-harness/tools/harness/plugin.test.mjs +526 -0
  21. package/.kilo/blender-project-harness/tools/harness/proxy.mjs +222 -0
  22. package/.kilo/blender-project-harness/tools/harness/proxy.test.mjs +217 -0
  23. package/.kilo/blender-project-harness/tools/harness/scaffold.mjs +83 -0
  24. package/.kilo/blender-project-harness/tools/harness/scaffold.test.mjs +74 -0
  25. package/.kilo/blender-project-harness/tools/harness/server.mjs +24 -0
  26. package/.kilo/blender-project-harness/tools/harness/shell-policy.mjs +196 -0
  27. package/.kilo/blender-project-harness/tools/harness/shell-policy.test.mjs +68 -0
  28. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/ASSETS.md +29 -0
  29. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/DECISIONS.md +8 -0
  30. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/Goals/01-project-brief.md +71 -0
  31. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/Goals/README.md +17 -0
  32. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/INTAKE.md +8 -0
  33. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/README.md +57 -0
  34. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/REQUIREMENTS.md +64 -0
  35. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/REVIEW.md +29 -0
  36. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/References/README.md +18 -0
  37. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/Reviews/README.md +48 -0
  38. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/TASKSTRACKER.md +10 -0
  39. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/Tasks/README.md +4 -0
  40. package/.kilo/blender-project-harness/tools/harness/templates/project/Documents/WORKFLOW.md +36 -0
  41. package/.kilo/blender-project-harness/tools/harness/templates/project/README.md +130 -0
  42. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/README.md +31 -0
  43. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/audio/.gitkeep +0 -0
  44. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/external/README.md +43 -0
  45. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/external/SOURCE_TEMPLATE.md +18 -0
  46. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/hdri/.gitkeep +0 -0
  47. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/models/.gitkeep +0 -0
  48. package/.kilo/blender-project-harness/tools/harness/templates/project/assets/textures/.gitkeep +0 -0
  49. package/.kilo/blender-project-harness/tools/harness/templates/project/backups/.gitkeep +0 -0
  50. package/.kilo/blender-project-harness/tools/harness/templates/project/backups/README.md +5 -0
  51. package/.kilo/blender-project-harness/tools/harness/templates/project/exports/.gitkeep +0 -0
  52. package/.kilo/blender-project-harness/tools/harness/templates/project/exports/README.md +11 -0
  53. package/.kilo/blender-project-harness/tools/harness/templates/project/renders/README.md +7 -0
  54. package/.kilo/blender-project-harness/tools/harness/templates/project/renders/final/.gitkeep +0 -0
  55. package/.kilo/blender-project-harness/tools/harness/templates/project/renders/wip/.gitkeep +0 -0
  56. package/.kilo/blender-project-harness/tools/harness/templates/project/scenes/README.md +10 -0
  57. package/.kilo/blender-project-harness/tools/harness/templates/project/scripts/README.md +16 -0
  58. package/.kilo/blender-project-harness/tools/harness/test-fixture.mjs +91 -0
  59. package/.kilo/blender-project-harness/tools/harness/workspace.mjs +30 -0
  60. package/.kilo/blender-project-harness/tools/harness/workspace.test.mjs +31 -0
  61. package/.kilo/blender-project-harness/tools/test_blender_tools.py +427 -0
  62. package/.kilo/plugins/blender-workflow.js +3 -0
  63. package/.kilo/skills/blender-assets/SKILL.md +99 -0
  64. package/.kilo/skills/blender-mcp/SKILL.md +147 -0
  65. package/.kilo/skills/blender-mcp/references/README.md +22 -0
  66. package/.kilo/skills/blender-mcp/references/animation.md +83 -0
  67. package/.kilo/skills/blender-mcp/references/bpy.md +97 -0
  68. package/.kilo/skills/blender-mcp/references/level-design.md +53 -0
  69. package/.kilo/skills/blender-mcp/references/materials.md +80 -0
  70. package/.kilo/skills/blender-mcp/references/retopology.md +104 -0
  71. package/.kilo/skills/blender-mcp/references/rigging.md +123 -0
  72. package/.kilo/skills/blender-mcp/references/scene.md +72 -0
  73. package/.kilo/skills/blender-project-goal/SKILL.md +115 -0
  74. package/.kilo/skills/blender-project-grading/SKILL.md +184 -0
  75. package/.kilo/skills/blender-project-readme/SKILL.md +46 -0
  76. package/.kilo/skills/blender-project-requirements/SKILL.md +84 -0
  77. package/.kilo/skills/blender-project-scaffold/SKILL.md +59 -0
  78. package/.kilo/skills/blender-project-workflow/SKILL.md +96 -0
  79. package/.kilo/skills/blender-subagents/SKILL.md +204 -0
  80. package/.kilo/skills/blender-workflow-harness/SKILL.md +187 -0
  81. package/AGENTS.md +234 -0
  82. package/Blender/README.md +30 -0
  83. package/CHANGELOG.md +22 -0
  84. package/LICENSE +21 -0
  85. package/README.md +368 -2
  86. package/THIRD_PARTY_NOTICES.md +51 -0
  87. package/bin/blender-project-harness.mjs +4 -0
  88. package/installer/cli.mjs +116 -0
  89. package/installer/install.mjs +325 -0
  90. package/installer/install.test.mjs +134 -0
  91. package/package.json +58 -6
@@ -0,0 +1,36 @@
1
+ # Workflow — {{PROJECT_NAME}}
2
+
3
+ Mode: Unset
4
+ Selected: —
5
+ Last changed: {{DATE}}
6
+
7
+ ## Current mode
8
+
9
+ - [ ] Fully Autonomous
10
+ - [ ] Partially Autonomous
11
+
12
+ ## Rules
13
+
14
+ - Fully Autonomous: proceed within confirmed requirements. Stop for blockers, destructive choices, or scope changes.
15
+ - Partially Autonomous: after every reviewable change (a bounded change with at least one apply),
16
+ seal, inspect with `look`, complete required grading and dispositions, then ask for approval or
17
+ revision direction before commit. Grader findings do not authorize corrections without that
18
+ direction.
19
+ - Kilo removes `question` during an active goal in either mode. The orchestrator follows
20
+ `blender-project-goal` for required-input handoffs, questions, objective updates, and resume; never
21
+ delegate goal commands to the user or repeat the stop procedure for already stopped work. Wait for
22
+ every required answer. Pause, cancellation, no answer, and manual editing keep work stopped.
23
+ - Commit through the harness `commit` action after each reviewed change: one scoped change per
24
+ commit, never one end-of-task dump. Manual project Git commits are blocked.
25
+ - Collect workflow and review answers after requirements confirmation and before scaffolding, then
26
+ persist their exact records before filling and starting the goal. Ask both modes before resuming a
27
+ genuine later return. Routine approval, clarification, and manual-edit pauses within the same task,
28
+ automatic goal turns, and compaction retain modes without reasking. The user may switch at any time.
29
+ - Require the final applicable `PASS` when grading is enabled and no genuine unresolved blocker or
30
+ major finding before completion. User approval does not waive those findings.
31
+
32
+ ## Change log
33
+
34
+ | Date | Mode | Reason | Confirmed by |
35
+ |---|---|---|---|
36
+ | {{DATE}} | Unset | Project scaffolded. | Pending |
@@ -0,0 +1,130 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ Status: Draft
4
+ Last updated: {{DATE}}
5
+
6
+ ## Purpose
7
+
8
+ {{DESCRIPTION}}
9
+
10
+ ## What this project is
11
+
12
+ Describe the subject, the intended audience, and the visual result this project must deliver. Keep
13
+ this section short and concrete.
14
+
15
+ ## Requirements
16
+
17
+ Confirmed scope and acceptance criteria: [`Documents/REQUIREMENTS.md`](Documents/REQUIREMENTS.md).
18
+
19
+ - [ ] Summarize the highest-priority confirmed requirements.
20
+
21
+ ## Visual direction
22
+
23
+ - Style:
24
+ - Mood / references:
25
+ - Colour and lighting:
26
+ - Camera / framing:
27
+ - Scale and units:
28
+
29
+ ## Deliverables
30
+
31
+ - Main Blender file: `{{PROJECT_SLUG}}.blend` (planned at project root until first save)
32
+ - Final renders: `renders/final/`
33
+ - Exports: `exports/`
34
+ - Documentation: `Documents/`
35
+ - External assets: `assets/external/`
36
+
37
+ ## Project structure
38
+
39
+ ```text
40
+ {{PROJECT_NAME}}/
41
+ README.md
42
+ {{PROJECT_SLUG}}.blend
43
+ .gitignore
44
+ Documents/
45
+ README.md
46
+ REQUIREMENTS.md
47
+ WORKFLOW.md
48
+ REVIEW.md
49
+ ASSETS.md
50
+ DECISIONS.md
51
+ TASKSTRACKER.md
52
+ Tasks/
53
+ Goals/
54
+ Reviews/
55
+ References/
56
+ assets/
57
+ external/
58
+ textures/
59
+ hdri/
60
+ models/
61
+ audio/
62
+ renders/
63
+ final/
64
+ wip/
65
+ exports/
66
+ scenes/
67
+ scripts/
68
+ logs/
69
+ backups/
70
+ ```
71
+
72
+ ## Blender
73
+
74
+ - MCP port: owned by the workspace root `.kilo/kilo.jsonc`.
75
+ - Launch and stop with `blender_workflow` `launch`/`stop`; never invoke Blender from the shell.
76
+ - Before launch, require confirmed requirements, selected workflow and review records, and an active
77
+ filled goal. The harness validates project records, keeps the `.blend` inside this project, reads
78
+ the workspace MCP port, records the instance outside the repository, and rejects conflicting
79
+ workspace instances.
80
+ - Each launch writes `logs/blender-<timestamp>.log`; `logs/` is ignored.
81
+ - Main file: save the first scene as `{{PROJECT_SLUG}}.blend` in the project root. Before production
82
+ edits, verify MCP scene settings report that exact path; then replace the planned status above.
83
+ - Keep external paths relative (`//assets/...`); do not use absolute paths.
84
+ - Only one Blender project spawned by this workspace may be open at a time. Close the current one
85
+ before opening another.
86
+
87
+ ## Workflow
88
+
89
+ - Record the selected autonomy mode in [`Documents/WORKFLOW.md`](Documents/WORKFLOW.md).
90
+ - Record review mode `Off`, `All`, or `Single` in [`Documents/REVIEW.md`](Documents/REVIEW.md);
91
+ rounds live in [`Documents/Reviews/`](Documents/Reviews/README.md).
92
+ - Collect both answers after requirements confirmation and before scaffolding; persist their exact
93
+ records after scaffolding and before filling and starting the goal. Launch only after that.
94
+ - After registration, workflow and review modes are frozen. A requested mode change stops production
95
+ for explicit re-registration; routine approval, clarification, or manual-edit pauses retain them.
96
+ - In Partially Autonomous mode, the agent seals, inspects, completes required grading, and obtains
97
+ approval or revision direction before the next reviewable change commits. In either mode, the agent
98
+ manages goal handoffs and continuation; users provide inputs and decisions.
99
+ - Require the saved inspected review render, current documents, and final applicable `PASS` when
100
+ grading is enabled before completion; no genuine unresolved blocker or major finding permits it.
101
+
102
+ ## External assets
103
+
104
+ See [`Documents/ASSETS.md`](Documents/ASSETS.md) and [`assets/external/README.md`](assets/external/README.md).
105
+
106
+ - User files are brought in through `blender_workflow {action:"intake"}`; originals are never moved.
107
+ - No external assets yet. Replace this line with a summary when assets are added.
108
+
109
+ ## Goals
110
+
111
+ Goal prompts live in [`Documents/Goals/`](Documents/Goals/). Multiple goals are allowed.
112
+ Identify the active goal's actual path; use it for grading rather than assuming the first brief.
113
+
114
+ - [`01-project-brief.md`](Documents/Goals/01-project-brief.md)
115
+
116
+ ## References
117
+
118
+ - [`Documents/References/README.md`](Documents/References/README.md)
119
+ - Add links, moodboards, and source references here.
120
+
121
+ ## Open questions
122
+
123
+ - [ ] Replace with unknown requirements or decisions.
124
+
125
+ ## Next steps
126
+
127
+ - [ ] Persist the exact confirmed requirements and workflow/review answers collected before scaffold.
128
+ - [ ] Fill this README and the active goal, then start the goal before launch.
129
+ - [ ] Collect references and external assets.
130
+ - [ ] Build the scene, save and inspect a review render, and complete applicable grading and approvals.
@@ -0,0 +1,31 @@
1
+ # Assets — {{PROJECT_NAME}}
2
+
3
+ | Folder | Use |
4
+ |---|---|
5
+ | `external/` | Third-party models, textures, HDRIs, audio, and libraries. Register in `Documents/ASSETS.md`. |
6
+ | `textures/` | Project-owned textures only. |
7
+ | `hdri/` | Project-owned environment maps only. |
8
+ | `models/` | Project-owned meshes and linked libraries only. |
9
+ | `audio/` | Project-owned sound effects, music, and voice assets only. |
10
+
11
+ Use relative paths from the `.blend` file (`//assets/...`). Do not embed absolute paths.
12
+ Store licensed third-party assets and their derivatives under `external/<source>/<asset>/`; modifying
13
+ an asset does not remove its source, licence, attribution, registration, or redistribution duties.
14
+ Keep `SOURCE.md` and verbatim `LICENSE.txt` beside each external asset and register every derivative.
15
+
16
+ Bring user files in with `blender_workflow {action:"intake"}`. Scan first, then copy inside an open
17
+ change; it hashes, classifies, deduplicates, writes source/licence sidecars, and updates the registers.
18
+ Use `download` only to fetch a public HTTPS asset into ignored staging. Do not ask the user to
19
+ create folders or copy files by hand. Browser/provider and user-assisted files return through
20
+ `intake` with their actual local path; never bypass authentication, CAPTCHA, or provider
21
+ restrictions.
22
+
23
+ External binaries under `assets/external/` are not committed unless `SOURCE.md` says
24
+ `Redistribution allowed: yes`. Sidecars, hashes, and metadata are committed. Media generation is
25
+ agent-owned and requires explicit permission before spending credits; pass the actual generated
26
+ file to `intake` with generated metadata.
27
+
28
+ Supported reviewed operations: `import_asset` for local GLB/glTF/FBX/OBJ/USD/`.blend`, `link_library`
29
+ for `.blend` link/append, `import_audio`, `image_texture`, `world_hdri`, `check_assets`,
30
+ `pack_assets`, and `export_scene`. Remote asset-library import and 3D generation remain blocked;
31
+ `download` and `intake` are the supported local staging path.
@@ -0,0 +1,43 @@
1
+ # External Assets — {{PROJECT_NAME}}
2
+
3
+ This folder holds third-party assets. The default `.gitignore` excludes asset binaries so a project
4
+ does not accidentally commit large or non-redistributable files. Metadata stays tracked.
5
+
6
+ ## Layout
7
+
8
+ ```text
9
+ assets/external/
10
+ <source-or-vendor>/
11
+ <asset-name>/
12
+ SOURCE.md
13
+ LICENSE.txt # mandatory verbatim licence terms; preserve originals too
14
+ <asset files>
15
+ ```
16
+
17
+ ## Rules
18
+
19
+ - Use `intake` for user files. It copies accepted files here and writes `SOURCE.md`,
20
+ `LICENSE.txt`, and the `ASSETS.md` row. Use `download` only to stage an HTTPS file in `.temp`;
21
+ then pass it to `intake` with source and licence details.
22
+ - Never ask the user to create this folder tree or copy by hand. Original files are never moved.
23
+ - Copy applicable licence terms verbatim into `LICENSE.txt`; preserve original licence files too.
24
+ Record the licence source URL and required attribution exactly. Stop if terms are unavailable or
25
+ do not permit the intended use; do not invent or summarize them. Verify `SOURCE.md` and
26
+ `LICENSE.txt` are tracked before committing the project.
27
+ - Keep third-party originals and derivatives in this folder, including licensed assets. Register
28
+ derivatives and identify source and modifications; project modification does not remove licence duties.
29
+ - Record the asset in `../../Documents/ASSETS.md`.
30
+ - Do not commit an asset unless its licence allows redistribution. If it does, remove the ignore
31
+ rule for that exact path so the harness commit can stage it, and keep the licence beside it.
32
+ Force-add is unsupported; request reviewed tooling instead of using manual Git.
33
+ - For large redistributable assets, use Git LFS rather than committing raw binaries.
34
+ - Link or import with relative paths. Use the harness `import_asset` operation for local model files,
35
+ `link_library` for `.blend` libraries, `import_audio` for audio, and `image_texture`/`world_hdri`
36
+ for image assets. Check missing files with `check_assets` before delivery.
37
+ - Search tools can find assets, but remote asset-library import is not implemented. Use `download`
38
+ for an exact HTTPS URL, then `intake`; or supply the local file path directly to `intake`.
39
+ - Packing or embedding external data into a tracked or delivered `.blend` redistributes those bytes.
40
+ Verify the licence, attribution, and redistribution terms first, and record the packaging mode in
41
+ `../../Documents/ASSETS.md` and `SOURCE.md`.
42
+ - Pack external data only for delivery copies when permitted; keep an unpacked working copy when the
43
+ assets need editing.
@@ -0,0 +1,18 @@
1
+ # Asset Source
2
+
3
+ - Asset:
4
+ - Source URL:
5
+ - Author / vendor:
6
+ - Licence:
7
+ - Licence source URL:
8
+ - Verbatim licence copy: LICENSE.txt (required; preserve original licence files too)
9
+ - Attribution required:
10
+ - Redistribution allowed:
11
+ - Packaging in .blend: linked / packed / embedded / none
12
+ - Local path:
13
+ - Imported into:
14
+ - Modifications:
15
+ - Original asset / derivative relationship:
16
+ - Acquisition instructions and file hashes:
17
+ - Date:
18
+ - Notes:
@@ -0,0 +1,5 @@
1
+ # Local Backups — {{PROJECT_NAME}}
2
+
3
+ Manual snapshots of the `.blend` and important assets can go here. This folder is ignored by git;
4
+ it is not the project history. Use git commits for tracked history and off-machine backups for
5
+ disaster recovery.
@@ -0,0 +1,11 @@
1
+ # Exports — {{PROJECT_NAME}}
2
+
3
+ Final interchange files (GLB, glTF, FBX, USD, OBJ) and other delivery outputs live here. Create them
4
+ with the reviewed `export_scene` operation inside a declared scene change, with the output path under
5
+ `exports/`. Record the export preset, scale, axis convention, and any known limitations in the project
6
+ README. Sidecar files such as OBJ `.mtl` or separate glTF buffers follow the same ownership and
7
+ licence rules as the main export.
8
+
9
+ Every export produces a harness receipt with format, project-relative path, bytes, SHA-256, source
10
+ scene, and signature check. Completion is denied for a missing or changed export. This validation is
11
+ separate from `check_assets`.
@@ -0,0 +1,7 @@
1
+ # Renders — {{PROJECT_NAME}}
2
+
3
+ - `final/` — reviewed renders that belong with the deliverable. Tracked.
4
+ - `wip/` — iteration renders and test frames. Ignored by git.
5
+
6
+ Use `//renders/...` paths relative to the `.blend` file. Record the render settings and the final
7
+ render names in the project README.
@@ -0,0 +1,10 @@
1
+ # Scenes — {{PROJECT_NAME}}
2
+
3
+ Save the main `.blend` at the project root as `{{PROJECT_SLUG}}.blend`. Multiple authored scenes
4
+ are supported inside that file. Use `create_scene`, `switch_scene`, `duplicate_scene`, and
5
+ `delete_scene`; `create_scene` supports `NEW`, `EMPTY` (Copy Settings), `LINK_COPY`, and
6
+ `FULL_COPY`. Existing operations accept an optional `scene` field. Declare exact scene names in
7
+ `begin`/`extend` or add them through scene operations, and give each shot scene its own camera.
8
+ A master Video Sequencer scene can use `scene_strip`. The harness seals and grades every declared
9
+ scene; a single-scene project may omit scene names and keeps the existing behavior. Linked `.blend`
10
+ libraries still use `link_library`. Use relative paths and document the primary scene.
@@ -0,0 +1,16 @@
1
+ # Scripts — {{PROJECT_NAME}}
2
+
3
+ Project-specific Blender Python source may live here. It is source-only: the harness never executes
4
+ it. Use relative paths from the project root or the `.blend` file and never hardcode machine-specific
5
+ paths.
6
+
7
+ Use `blender_workflow {action:"intake"}` with `kind:"script"` to copy `.py`/`.pyw` source here. The
8
+ harness accepts these files only under `scripts/` and records them in `Documents/INTAKE.md`.
9
+
10
+ Workspace automation scripts are a separate category. They may run only through the existing shell
11
+ command approval path, with the exact command, working directory, script SHA-256, reason, approval,
12
+ and outcome recorded. They are not a sandbox.
13
+
14
+ Any Blender Python execution requires a separately reviewed OS/VM isolation design. Do not use raw
15
+ Python, `--python`, `exec()`, or a hidden runner. Document each script's purpose and any approved
16
+ future invocation in this README.
@@ -0,0 +1,91 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { Harness, git } from './engine.mjs';
5
+
6
+ export function fixture(t, { partial = false, review = 'Off' } = {}) {
7
+ const tempBase = process.platform === 'win32' ? path.join(os.tmpdir(), 'kilo') : os.tmpdir();
8
+ const temp = fs.mkdtempSync(path.join(tempBase, 'workflow-integration-'));
9
+ t.after(() => fs.rmSync(temp, { force: true, recursive: true }));
10
+ const root = path.join(temp, 'workspace');
11
+ fs.mkdirSync(root);
12
+ const project = 'Blender/Test Project', scene = `${project}/scene.blend`, goal = 'Documents/Goals/01-test.md';
13
+ const write = (name, text) => {
14
+ const full = path.join(root, name);
15
+ fs.mkdirSync(path.dirname(full), { recursive: true });
16
+ fs.writeFileSync(full, text);
17
+ return full;
18
+ };
19
+ const objectiveText = '# Test Goal\n' + 'Confirmed fixture requirements and observable acceptance checks. '.repeat(4);
20
+ write(`${project}/README.md`, [
21
+ '# Fixture project',
22
+ 'Status: Confirmed',
23
+ 'Last updated: 2026-10-31',
24
+ '',
25
+ ...['Purpose', 'What this project is', 'Requirements', 'Visual direction', 'Deliverables', 'Project structure', 'Blender', 'Workflow', 'External assets', 'Goals', 'References', 'Open questions', 'Next steps'].flatMap((name) => [`## ${name}`, `Fixture ${name.toLowerCase()}.`, '']),
26
+ ].join('\n'));
27
+ write(`${project}/Documents/REQUIREMENTS.md`, [
28
+ '# Requirements',
29
+ 'Status: Confirmed',
30
+ 'Defined: 2026-10-31',
31
+ 'Last updated: 2026-10-31',
32
+ 'Confirmed: 2026-10-31',
33
+ 'Confirmation: Confirm fixture requirements.',
34
+ '',
35
+ ...['Core value', 'Confirmed requirements', 'Constraints', 'Deferred', 'Out of scope', 'Open questions', 'Research notes', 'Change log'].flatMap((name) => [`## ${name}`, `Fixture ${name.toLowerCase()}.`, '']),
36
+ ].join('\n'));
37
+ write(`${project}/Documents/WORKFLOW.md`, [
38
+ '# Workflow',
39
+ `Mode: ${partial ? 'Partially' : 'Fully'} Autonomous`,
40
+ 'Selected: 2026-10-31',
41
+ 'Last changed: 2026-10-31',
42
+ '',
43
+ ...['Current mode', 'Rules', 'Change log'].flatMap((name) => [`## ${name}`, `Fixture ${name.toLowerCase()}.`, '']),
44
+ ].join('\n'));
45
+ write(`${project}/Documents/REVIEW.md`, [
46
+ '# Review Mode',
47
+ `Mode: ${review}`,
48
+ 'Selected: 2026-10-31',
49
+ 'Last changed: 2026-10-31',
50
+ 'Single grader: visual',
51
+ '',
52
+ ...['Modes', 'Change log'].flatMap((name) => [`## ${name}`, `Fixture ${name.toLowerCase()}.`, '']),
53
+ ].join('\n'));
54
+ write(`${project}/Documents/ASSETS.md`, '# No external assets\n\n## Rules\n\nFixture asset rules.\n');
55
+ write(`${project}/Documents/DECISIONS.md`, '# Fixture decisions\n');
56
+ write(`${project}/Documents/TASKSTRACKER.md`, '# Task Tracker — Test Project\n\nStatus: Active\nLast updated: 2026-10-31\n\n## Tasks\n\n| Task | Kind | Change | Operation | Objects | Assigned | Child | State | Created | Finished | Result |\n|---|---|---|---|---|---|---|---|---|---|---|\n| — | — | — | — | — | — | — | — | — | — | — |\n');
57
+ const goalBody = objectiveText.replace(/^#\s+.*\r?\n/, '');
58
+ const objective = [
59
+ '# Goal: Fixture',
60
+ ...['Mission', 'Project', 'Visual outcome', 'Scope', 'Deliverables', 'Acceptance', 'Workflow', 'Done'].flatMap((name) => [`## ${name}`, goalBody, '']),
61
+ ].join('\n');
62
+ write(`${project}/${goal}`, objective);
63
+ write(scene, 'fixture blend bytes\n');
64
+ write(`${project}/Documents/Reviews/README.md`, '# Review Index\n\n## Index\n\nFixture index.\n\n## Rules\n\nFixture rules.\n');
65
+ write('.gitignore', '**/_evidence/**\n**/renders/wip/**\n/.kilo/blender-project-harness/config.json\n');
66
+ write(`${project}/.gitignore`, 'renders/wip/**\n');
67
+ const config = { blender_subagents: { workers: {
68
+ agent: 'blender-worker', provider: 'fixture-provider', model: 'fixture-model', variant: 'fixture-variant',
69
+ backup: { provider: 'backup-provider', model: 'backup-model', variant: 'backup-variant' },
70
+ max_minutes: 10, max_steps: 18, max_failed_attempts: 2, max_parallel: 1,
71
+ } } };
72
+ if (review !== 'Off') config.blender_subagents.graders = {
73
+ enabled: true, agent: 'blender-grader', max_parallel: 1, max_rounds: 2,
74
+ graders: [{ name: 'visual', focus: 'composition', provider: 'test', model: 'test-model', variant: 'test-variant', backup: { provider: 'backup-test', model: 'backup-test-model', variant: 'backup-test-variant' }, max_steps: 12, max_minutes: 5 }],
75
+ };
76
+ write('.kilo/blender-project-harness/config.json', JSON.stringify(config));
77
+ git(root, ['init', '--quiet']);
78
+ git(root, ['add', '--', '.']);
79
+ const commit = (message = 'test: fixture checkpoint') => git(root, ['-c', 'user.name=Harness Test', '-c', 'user.email=harness-test@example.invalid', 'commit', '--quiet', '-m', message]);
80
+ commit('test: fixture scaffold');
81
+ const harness = new Harness(root, { stateRoot: path.join(temp, 'state') });
82
+ const session = 'ses_orchestrator';
83
+ const questionArgs = (question, labels) => ({ questions: [{ question, custom: false, multiple: false, options: labels.map((label) => ({ label, description: label })) }] });
84
+ const registration = harness.prepare(session, { project, scene: 'scene.blend', goal });
85
+ harness.question(session, questionArgs(registration.question, registration.labels), [['Confirm registration']]);
86
+ const begin = () => {
87
+ harness.command(session, 'goal-active', { objective });
88
+ return harness.command(session, 'begin', { outcome: 'Create fixture composition', objects: ['Cube'], paths: ['notes.txt', 'Documents/DECISIONS.md'], final: true });
89
+ };
90
+ return { root, project, scene, goal, objective, write, commit, harness, session, questionArgs, begin };
91
+ }
@@ -0,0 +1,30 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export const INSTALL_RELATIVE = path.join('.kilo', 'blender-project-harness');
5
+ export const CONFIG_RELATIVE = path.join(INSTALL_RELATIVE, 'config.json');
6
+
7
+ export function workspaceRoot(start) {
8
+ const explicit = process.env.BLENDER_WORKSPACE_ROOT;
9
+ if (explicit) {
10
+ const resolved = path.resolve(explicit);
11
+ if (fs.existsSync(resolved)) return resolved;
12
+ }
13
+ let current = path.resolve(start);
14
+ while (true) {
15
+ if (fs.existsSync(path.join(current, '.kilo'))
16
+ && (fs.existsSync(path.join(current, 'Blender')) || fs.existsSync(path.join(current, 'AGENTS.md')))) {
17
+ return current;
18
+ }
19
+ const parent = path.dirname(current);
20
+ if (parent === current) break;
21
+ current = parent;
22
+ }
23
+ const cwd = path.resolve(process.cwd());
24
+ if (fs.existsSync(path.join(cwd, '.kilo'))) return cwd;
25
+ return path.resolve(start, '..', '..');
26
+ }
27
+
28
+ export function configPath(root) {
29
+ return path.join(root, CONFIG_RELATIVE);
30
+ }
@@ -0,0 +1,31 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import test from 'node:test';
6
+ import { configPath, workspaceRoot } from './workspace.mjs';
7
+
8
+ test('workspace root resolves from the installed harness path', (t) => {
9
+ const previous = process.env.BLENDER_WORKSPACE_ROOT;
10
+ delete process.env.BLENDER_WORKSPACE_ROOT;
11
+ t.after(() => {
12
+ if (previous === undefined) delete process.env.BLENDER_WORKSPACE_ROOT;
13
+ else process.env.BLENDER_WORKSPACE_ROOT = previous;
14
+ });
15
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'blender-workspace-root-'));
16
+ t.after(() => fs.rmSync(temp, { recursive: true, force: true }));
17
+ const root = path.join(temp, 'workspace');
18
+ fs.mkdirSync(path.join(root, '.kilo'), { recursive: true });
19
+ fs.mkdirSync(path.join(root, 'Blender'), { recursive: true });
20
+ const harness = path.join(root, '.kilo', 'blender-project-harness', 'tools', 'harness');
21
+ fs.mkdirSync(harness, { recursive: true });
22
+ assert.equal(workspaceRoot(harness), root);
23
+ });
24
+
25
+ test('config path is always the installed harness config', (t) => {
26
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'blender-workspace-config-'));
27
+ t.after(() => fs.rmSync(temp, { recursive: true, force: true }));
28
+ const root = path.join(temp, 'workspace');
29
+ fs.mkdirSync(root, { recursive: true });
30
+ assert.equal(configPath(root), path.join(root, '.kilo', 'blender-project-harness', 'config.json'));
31
+ });