nccgs 2.1.1 → 3.0.0-rc.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 (705) hide show
  1. package/CHANGELOG.md +76 -235
  2. package/LICENSE +1 -1
  3. package/README.md +87 -206
  4. package/THIRD_PARTY_NOTICES.md +6 -34
  5. package/bin/nccgs.mjs +96 -0
  6. package/cli/doctor.mjs +82 -0
  7. package/cli/feedback.mjs +13 -0
  8. package/cli/install.mjs +111 -0
  9. package/cli/release.mjs +59 -0
  10. package/cli/run.mjs +35 -0
  11. package/cli/spawn.mjs +20 -0
  12. package/cli/uninstall.mjs +43 -0
  13. package/cli/update-check.mjs +66 -0
  14. package/docs/EVIDENCE-VERIFICATION.md +22 -0
  15. package/package.json +23 -30
  16. package/runtime/core/diag.mjs +43 -0
  17. package/runtime/core/feedback-policy.mjs +54 -0
  18. package/runtime/core/fs.mjs +80 -0
  19. package/runtime/core/job-object.mjs +73 -0
  20. package/runtime/core/modes.mjs +87 -0
  21. package/runtime/core/process-tree.mjs +109 -0
  22. package/runtime/core/project.mjs +113 -0
  23. package/runtime/core/snapshot.mjs +114 -0
  24. package/runtime/hooks/common.mjs +22 -0
  25. package/runtime/hooks/done-guard.mjs +15 -0
  26. package/runtime/hooks/guard-mcp.mjs +8 -0
  27. package/runtime/hooks/guard-shell.mjs +16 -0
  28. package/runtime/hooks/guard-write.mjs +25 -0
  29. package/runtime/hooks/session-log.mjs +7 -0
  30. package/runtime/hooks/session-start.mjs +40 -0
  31. package/runtime/hooks/unity-transport.mjs +12 -0
  32. package/runtime/modes.json +39 -0
  33. package/runtime/task/cost.mjs +103 -0
  34. package/runtime/task/criteria.mjs +128 -0
  35. package/runtime/task/feedback.mjs +136 -0
  36. package/runtime/task/ledger.mjs +109 -0
  37. package/runtime/task/plan.mjs +69 -0
  38. package/runtime/task/project.mjs +79 -0
  39. package/runtime/task/report.mjs +252 -0
  40. package/runtime/task/task.mjs +238 -0
  41. package/runtime/unity/assets.mjs +51 -0
  42. package/runtime/unity/playtest.mjs +145 -0
  43. package/runtime/unity/unity.mjs +350 -0
  44. package/scripts/check-job-object.mjs +18 -0
  45. package/scripts/sync-unity-skills.mjs +35 -119
  46. package/template/CLAUDE.md +39 -0
  47. package/template/agents/fast-fix.md +9 -0
  48. package/template/agents/implementer.md +19 -0
  49. package/template/agents/playtester.md +15 -0
  50. package/template/agents/reviewer.md +19 -0
  51. package/template/agents/scout.md +9 -0
  52. package/template/ponytail/README.md +8 -0
  53. package/{.claude/nccgs/packages → template}/ponytail/engineering.md +10 -0
  54. package/template/protocols/evidence.md +52 -0
  55. package/template/protocols/playtest.md +45 -0
  56. package/template/protocols/review.md +34 -0
  57. package/template/protocols/unity-cli-notes.md +56 -0
  58. package/template/settings.fragment.json +33 -0
  59. package/template/skills/art-bible/SKILL.md +19 -0
  60. package/template/skills/asset-spec/SKILL.md +19 -0
  61. package/template/skills/brainstorm/SKILL.md +17 -0
  62. package/template/skills/feedback/SKILL.md +16 -0
  63. package/template/skills/map-systems/SKILL.md +25 -0
  64. package/template/skills/playtest/SKILL.md +17 -0
  65. package/template/skills/status/SKILL.md +18 -0
  66. package/template/skills/test/SKILL.md +34 -0
  67. package/template/skills/unity-foundation/SKILL.md +17 -0
  68. package/template/skills/vertical-slice/SKILL.md +17 -0
  69. package/template/skills/work/SKILL.md +65 -0
  70. package/tools/feedback-worker/README.md +12 -0
  71. package/tools/feedback-worker/worker.js +39 -0
  72. package/tools/feedback-worker/wrangler.toml +12 -0
  73. package/unity-skills/2d-pixel-perfect/SKILL.md +208 -0
  74. package/unity-skills/2d-pixel-perfect/references/api-reference.md +81 -0
  75. package/unity-skills/2d-pixel-perfect/references/camera-setup-builtin.cs +40 -0
  76. package/unity-skills/2d-pixel-perfect/references/camera-setup-urp.cs +49 -0
  77. package/unity-skills/2d-pixel-perfect/references/pipeline-detection.cs +50 -0
  78. package/unity-skills/2d-pixel-perfect/references/sprite-settings.cs +37 -0
  79. package/unity-skills/asset-transformer-toolkit/SECURITY.md +15 -0
  80. package/unity-skills/asset-transformer-toolkit/SKILL.md +39 -0
  81. package/unity-skills/asset-transformer-toolkit/references/create-importer.md +121 -0
  82. package/unity-skills/asset-transformer-toolkit/references/lods.md +61 -0
  83. package/unity-skills/asset-transformer-toolkit/references/rule-api.md +154 -0
  84. package/unity-skills/asset-transformer-toolkit/references/ruleblock-api.md +37 -0
  85. package/unity-skills/asset-transformer-toolkit/references/ruleset-api.md +138 -0
  86. package/unity-skills/asset-transformer-toolkit/references/rulesets-and-actions.md +227 -0
  87. package/unity-skills/audio-setup-mixers/SECURITY.md +16 -0
  88. package/unity-skills/audio-setup-mixers/SKILL.md +127 -0
  89. package/unity-skills/audio-setup-mixers/references/api.md +162 -0
  90. package/unity-skills/build-gtk/SKILL.md +242 -0
  91. package/unity-skills/build-gtk/references/graph-api.md +324 -0
  92. package/unity-skills/build-gtk/references/pitfalls.md +86 -0
  93. package/unity-skills/build-gtk/references/runtime-and-visualization.md +196 -0
  94. package/unity-skills/build-gtk/references/state-machine-api.md +286 -0
  95. package/unity-skills/build-live-game/SKILL.md +255 -0
  96. package/{.claude/skills → unity-skills}/build-live-game/references/achievements.md +132 -154
  97. package/{.claude/skills → unity-skills}/build-live-game/references/apis.md +10 -19
  98. package/{.claude/skills → unity-skills}/build-live-game/references/authentication.md +14 -20
  99. package/{.claude/skills → unity-skills}/build-live-game/references/battlepass.md +269 -225
  100. package/unity-skills/build-live-game/references/blueprints-and-samples.md +75 -0
  101. package/unity-skills/build-live-game/references/cloud-code-cloudbehaviours.md +1111 -0
  102. package/unity-skills/build-live-game/references/cloud-code-modules.md +1276 -0
  103. package/unity-skills/build-live-game/references/cloud-code.md +58 -0
  104. package/{.claude/skills → unity-skills}/build-live-game/references/cloud-save.md +15 -25
  105. package/{.claude/skills → unity-skills}/build-live-game/references/deployment.md +33 -18
  106. package/{.claude/skills → unity-skills}/build-live-game/references/player-account.md +31 -23
  107. package/{.claude/skills → unity-skills}/build-live-game/references/tooling.md +0 -56
  108. package/unity-skills/build-live-game/references/ugs-cli.md +86 -0
  109. package/unity-skills/craft-game-floors/SECURITY.md +23 -0
  110. package/unity-skills/craft-game-floors/SKILL.md +134 -0
  111. package/unity-skills/craft-game-floors/references/art-direction.md +85 -0
  112. package/unity-skills/craft-game-floors/references/camera-rigs.md +51 -0
  113. package/unity-skills/craft-game-floors/references/character-motion.md +35 -0
  114. package/unity-skills/craft-game-floors/references/game-audio.md +47 -0
  115. package/unity-skills/craft-game-floors/references/game-feel.md +68 -0
  116. package/unity-skills/craft-game-floors/references/game-shell.md +45 -0
  117. package/unity-skills/craft-game-floors/references/live-floor.md +35 -0
  118. package/unity-skills/craft-game-floors/references/placeholder-assets.md +35 -0
  119. package/unity-skills/craft-game-floors/references/scene-lighting.md +52 -0
  120. package/unity-skills/craft-game-floors/references/toon-shading.md +34 -0
  121. package/unity-skills/craft-game-floors/references/vfx.md +40 -0
  122. package/unity-skills/craft-game-floors/references/visual-scorecard.md +43 -0
  123. package/unity-skills/create-2d-physics/SECURITY.md +16 -0
  124. package/unity-skills/create-2d-physics/SKILL.md +128 -0
  125. package/unity-skills/create-2d-physics/references/2d-physics-core.md +155 -0
  126. package/unity-skills/create-2d-physics/references/legacy-2d-physics.md +45 -0
  127. package/unity-skills/create-tile-palette-or-rule-tiles/SKILL.md +12 -0
  128. package/unity-skills/create-tile-palette-or-rule-tiles/references/hexagonalruletile.md +71 -0
  129. package/unity-skills/create-tile-palette-or-rule-tiles/references/palette-create.md +38 -0
  130. package/unity-skills/create-tile-palette-or-rule-tiles/references/ruletile-createempty.md +35 -0
  131. package/unity-skills/create-tile-palette-or-rule-tiles/references/ruletile-createfromsegment.md +212 -0
  132. package/unity-skills/create-tile-palette-or-rule-tiles/references/ruletile.md +90 -0
  133. package/unity-skills/create-tile-palette-or-rule-tiles/scripts/CreatePaletteTemplate.cs +148 -0
  134. package/unity-skills/create-tile-palette-or-rule-tiles/scripts/ManualWorkflowExample.cs +63 -0
  135. package/unity-skills/create-tile-palette-or-rule-tiles/scripts/RuleTileGenerator.cs +61 -0
  136. package/unity-skills/create-tile-palette-or-rule-tiles/scripts/TilemapRuleTileCreateFromSegment.cs +225 -0
  137. package/unity-skills/generate-editor-search-query/SECURITY.md +17 -0
  138. package/unity-skills/generate-editor-search-query/SKILL.md +117 -0
  139. package/unity-skills/generate-editor-search-query/references/open-search-window.md +70 -0
  140. package/unity-skills/generate-editor-search-query/references/query-patterns.md +124 -0
  141. package/{.claude/skills → unity-skills}/implement-in-app-purchases/SKILL.md +1 -2
  142. package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/api-notes.md +25 -28
  143. package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-convert-native-google-billing.md +1 -1
  144. package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-implement-iap-d2c.md +40 -5
  145. package/{.claude/skills → unity-skills}/initialize-ai-navigation/SKILL.md +4 -4
  146. package/{.claude/skills → unity-skills}/initialize-ai-navigation/references/navigation-system.md +3 -3
  147. package/{.claude/skills → unity-skills}/levelplay-unity-integration/CHANGELOG.md +12 -0
  148. package/{.claude/skills → unity-skills}/levelplay-unity-integration/README.md +9 -5
  149. package/unity-skills/levelplay-unity-integration/SKILL.md +442 -0
  150. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/banner-api.md +15 -8
  151. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/best-practices.md +604 -536
  152. package/unity-skills/levelplay-unity-integration/references/dependency-resolution.md +94 -0
  153. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/ilrd-api.md +2 -2
  154. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/initialization-api.md +221 -0
  155. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/interstitial-api.md +11 -3
  156. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/ios-setup.md +6 -34
  157. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/migration-sdk-9.md +1 -1
  158. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/privacy-settings.md +14 -29
  159. package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/rewarded-api.md +20 -4
  160. package/unity-skills/levelplay-unity-integration/references/testing-and-validation.md +222 -0
  161. package/unity-skills/levelplay-unity-integration/references/troubleshooting.md +99 -0
  162. package/unity-skills/localization/SKILL.md +347 -0
  163. package/{.claude/skills → unity-skills}/localization/references/api-notes.md +4 -0
  164. package/unity-skills/localization/resources/L10nBatchProcessor.cs +191 -0
  165. package/unity-skills/manage-sprite-atlas/SKILL.md +351 -0
  166. package/unity-skills/manage-sprite-atlas/references/addressables-delivery.md +58 -0
  167. package/unity-skills/manage-sprite-atlas/references/api.md +382 -0
  168. package/unity-skills/manage-sprite-atlas/references/best-practices.md +183 -0
  169. package/unity-skills/manage-sprite-atlas/references/common-errors.md +123 -0
  170. package/unity-skills/manage-sprite-atlas/references/custom-packing.md +147 -0
  171. package/unity-skills/manage-sprite-atlas/references/manual-authoring.md +39 -0
  172. package/unity-skills/manage-sprite-atlas/resources/authoringvsruntime.cs +176 -0
  173. package/unity-skills/manage-sprite-atlas/resources/buildaddressablespostprocess.cs +59 -0
  174. package/unity-skills/manage-sprite-atlas/resources/cacheloadedatlases.cs +145 -0
  175. package/unity-skills/manage-sprite-atlas/resources/cleanupbatchoperations.cs +119 -0
  176. package/unity-skills/manage-sprite-atlas/resources/createandconfiguremasteratlas.cs +57 -0
  177. package/unity-skills/manage-sprite-atlas/resources/custompackersetup.cs +88 -0
  178. package/unity-skills/manage-sprite-atlas/resources/deprecatedmethods.cs +142 -0
  179. package/unity-skills/manage-sprite-atlas/resources/dontmodifywithoutsaving.cs +120 -0
  180. package/unity-skills/manage-sprite-atlas/resources/dontpackinruntimebuilds.cs +190 -0
  181. package/unity-skills/manage-sprite-atlas/resources/dontscriptspriteatlasineditor.cs +114 -0
  182. package/unity-skills/manage-sprite-atlas/resources/editorconfiguration.cs +95 -0
  183. package/unity-skills/manage-sprite-atlas/resources/enablespritepacking.cs +172 -0
  184. package/unity-skills/manage-sprite-atlas/resources/gridspritepacker.cs +50 -0
  185. package/unity-skills/manage-sprite-atlas/resources/handlelatebinding.cs +147 -0
  186. package/unity-skills/manage-sprite-atlas/resources/handlenullcases.cs +141 -0
  187. package/unity-skills/manage-sprite-atlas/resources/migratev1tov2.cs +335 -0
  188. package/unity-skills/manage-sprite-atlas/resources/multipagepacker.cs +80 -0
  189. package/unity-skills/manage-sprite-atlas/resources/optimizetexturesettings.cs +191 -0
  190. package/unity-skills/manage-sprite-atlas/resources/packerdebugging.cs +158 -0
  191. package/unity-skills/manage-sprite-atlas/resources/packtransform.cs +51 -0
  192. package/unity-skills/manage-sprite-atlas/resources/platformnames.cs +127 -0
  193. package/unity-skills/manage-sprite-atlas/resources/queryspritesefficiently.cs +222 -0
  194. package/unity-skills/manage-sprite-atlas/resources/registercallbacks.cs +152 -0
  195. package/unity-skills/manage-sprite-atlas/resources/runtimeatlasloader.cs +15 -0
  196. package/unity-skills/manage-sprite-atlas/resources/runtimespriteaccess.cs +19 -0
  197. package/unity-skills/manage-sprite-atlas/resources/savespriteatlasasset.cs +107 -0
  198. package/unity-skills/manage-sprite-atlas/resources/scriptablepacker.cs +81 -0
  199. package/unity-skills/manage-sprite-atlas/resources/setincludeinbuild.cs +164 -0
  200. package/unity-skills/manage-sprite-atlas/resources/sizeoptimizedpacker.cs +88 -0
  201. package/unity-skills/manage-sprite-atlas/resources/spriteatlaslatebinding.cs +123 -0
  202. package/unity-skills/manage-sprite-atlas/resources/spriteatlasprebuildgenerator.cs +219 -0
  203. package/unity-skills/manage-sprite-atlas/resources/spritedata.cs +35 -0
  204. package/unity-skills/manage-sprite-atlas/resources/spritepack.cs +39 -0
  205. package/unity-skills/manage-sprite-atlas/resources/texturedata.cs +15 -0
  206. package/unity-skills/manage-sprite-atlas/resources/updatebuildscripts.cs +357 -0
  207. package/unity-skills/manage-sprite-atlas/resources/updatesettingsconfiguration.cs +135 -0
  208. package/unity-skills/manage-sprite-atlas/resources/usevariantswisely.cs +179 -0
  209. package/unity-skills/manage-sprite-atlas/resources/variantatlas.cs +39 -0
  210. package/unity-skills/manifest.json +1155 -0
  211. package/unity-skills/migrate-birp-to-urp/SECURITY.md +16 -0
  212. package/unity-skills/migrate-birp-to-urp/SKILL.md +543 -0
  213. package/unity-skills/migrate-birp-to-urp/references/capturing-the-editor.md +66 -0
  214. package/unity-skills/migrate-birp-to-urp/references/complex-shader-situations.md +272 -0
  215. package/unity-skills/migrate-birp-to-urp/references/custom-shader-triage.md +123 -0
  216. package/unity-skills/migrate-birp-to-urp/references/implementation-patterns.md +529 -0
  217. package/unity-skills/migrate-birp-to-urp/references/migration-workflow.md +265 -0
  218. package/unity-skills/migrate-birp-to-urp/references/quality-settings-map.md +69 -0
  219. package/unity-skills/new-unity-project/SECURITY.md +26 -0
  220. package/unity-skills/new-unity-project/SKILL.md +386 -0
  221. package/unity-skills/new-unity-project/references/skill-setup.md +66 -0
  222. package/unity-skills/optimize-audio/SECURITY.md +16 -0
  223. package/{.claude/skills → unity-skills}/optimize-audio/SKILL.md +17 -17
  224. package/{.claude/skills → unity-skills}/optimize-audio/resources/audio-import-api.md +8 -5
  225. package/{.claude/skills → unity-skills}/optimize-text-mesh-pro/SKILL.md +1 -11
  226. package/unity-skills/optimize-web/SECURITY.md +16 -0
  227. package/{.claude/skills → unity-skills}/optimize-web/SKILL.md +164 -27
  228. package/unity-skills/optimize-web/scripts/WebOptimizer.cs +46 -0
  229. package/unity-skills/optimize-web/scripts/toktx-examples.sh +11 -0
  230. package/{.claude/skills → unity-skills}/physics-3d-collision/SKILL.md +2 -2
  231. package/unity-skills/physics-3d-collision/references/troubleshooting.md +8 -0
  232. package/unity-skills/project-auditor-fixes/SKILL.md +64 -0
  233. package/{.claude/skills → unity-skills}/setup-multiplayer-services/SKILL.md +1 -4
  234. package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/SKILL.md +1 -1
  235. package/{.claude/skills → unity-skills}/shader-graph-create-custom-node/SKILL.md +1 -1
  236. package/unity-skills/sprite-editor/SECURITY.md +16 -0
  237. package/{.claude/skills → unity-skills}/sprite-editor/SKILL.md +9 -9
  238. package/{.claude/skills → unity-skills}/sprite-editor/references/templates.md +1 -1
  239. package/unity-skills/sprite-segment-3x3grid/SKILL.md +136 -0
  240. package/unity-skills/sprite-segment-3x3grid/scripts/SpriteGridAnalysis.cs +119 -0
  241. package/{.claude/skills → unity-skills}/ui/SKILL.md +1 -1
  242. package/{.claude/skills → unity-skills}/ui-imgui/SKILL.md +1 -7
  243. package/{.claude/skills → unity-skills}/ui-ugui/SKILL.md +3 -9
  244. package/{.claude/skills → unity-skills}/ui-uitk/SKILL.md +8 -14
  245. package/{.claude/skills → unity-skills}/ui-uitk/references/custom-elements.md +1 -1
  246. package/unity-skills/unity-cli/CHANGELOG.md +515 -0
  247. package/unity-skills/unity-cli/SECURITY.md +68 -0
  248. package/unity-skills/unity-cli/SKILL.md +723 -0
  249. package/unity-skills/unity-cli/references/auth-license-cloud.md +318 -0
  250. package/unity-skills/unity-cli/references/build-run-test.md +555 -0
  251. package/unity-skills/unity-cli/references/cloud-automation.md +736 -0
  252. package/{.claude/skills → unity-skills}/unity-cli/references/collaboration.md +166 -105
  253. package/unity-skills/unity-cli/references/config-hub.md +239 -0
  254. package/unity-skills/unity-cli/references/diagnostics-maintenance.md +720 -0
  255. package/unity-skills/unity-cli/references/docs-workflow.md +179 -0
  256. package/unity-skills/unity-cli/references/editors-install.md +476 -0
  257. package/unity-skills/unity-cli/references/integration-advanced.md +1130 -0
  258. package/unity-skills/unity-cli/references/playmode-verification-loop.md +279 -0
  259. package/unity-skills/unity-cli/references/projects-templates.md +996 -0
  260. package/unity-skills/unity-cli/references/version-control.md +612 -0
  261. package/{.claude/skills → unity-skills}/unity-package-management/SKILL.md +60 -12
  262. package/{.claude/skills → unity-skills}/unity-package-management/references/select-packages.md +39 -21
  263. package/unity-skills/unity-pipeline/SECURITY.md +17 -0
  264. package/unity-skills/unity-pipeline/SKILL.md +335 -0
  265. package/unity-skills/urp-postprocessing/SECURITY.md +16 -0
  266. package/{.claude/skills → unity-skills}/urp-postprocessing/SKILL.md +12 -12
  267. package/{.claude/skills → unity-skills}/urp-postprocessing/references/code-templates.md +39 -8
  268. package/{.claude/skills → unity-skills}/validate-urp-render-graph-renderer-feature/SKILL.md +1 -1
  269. package/.claude/agents/nccgs-adversarial-reviewer.md +0 -43
  270. package/.claude/agents/nccgs-engine-programmer.md +0 -27
  271. package/.claude/agents/nccgs-fast-implementer.md +0 -30
  272. package/.claude/agents/nccgs-game-designer.md +0 -26
  273. package/.claude/agents/nccgs-gameplay-programmer.md +0 -26
  274. package/.claude/agents/nccgs-qa-engineer.md +0 -27
  275. package/.claude/agents/nccgs-task-scout.md +0 -24
  276. package/.claude/agents/nccgs-ui-programmer.md +0 -27
  277. package/.claude/agents/nccgs-unity-build-specialist.md +0 -27
  278. package/.claude/agents/nccgs-unity-implementer.md +0 -28
  279. package/.claude/agents/nccgs-unity-systems-specialist.md +0 -27
  280. package/.claude/agents/nccgs-unity-ui-specialist.md +0 -27
  281. package/.claude/agents/nccgs-verification-engineer.md +0 -27
  282. package/.claude/nccgs/MEMORY.md +0 -66
  283. package/.claude/nccgs/THIRD_PARTY_NOTICES.md +0 -29
  284. package/.claude/nccgs/VERSION +0 -1
  285. package/.claude/nccgs/agent-library/nccgs-accessibility-specialist.md +0 -25
  286. package/.claude/nccgs/agent-library/nccgs-adversarial-reviewer.md +0 -41
  287. package/.claude/nccgs/agent-library/nccgs-ai-programmer.md +0 -25
  288. package/.claude/nccgs/agent-library/nccgs-analytics-engineer.md +0 -25
  289. package/.claude/nccgs/agent-library/nccgs-art-direction-lead.md +0 -25
  290. package/.claude/nccgs/agent-library/nccgs-audio-direction-lead.md +0 -25
  291. package/.claude/nccgs/agent-library/nccgs-creative-director.md +0 -25
  292. package/.claude/nccgs/agent-library/nccgs-documentation-manager.md +0 -25
  293. package/.claude/nccgs/agent-library/nccgs-economy-designer.md +0 -25
  294. package/.claude/nccgs/agent-library/nccgs-engine-programmer.md +0 -25
  295. package/.claude/nccgs/agent-library/nccgs-fast-implementer.md +0 -28
  296. package/.claude/nccgs/agent-library/nccgs-game-design-lead.md +0 -25
  297. package/.claude/nccgs/agent-library/nccgs-game-designer.md +0 -24
  298. package/.claude/nccgs/agent-library/nccgs-gameplay-programmer.md +0 -24
  299. package/.claude/nccgs/agent-library/nccgs-level-designer.md +0 -25
  300. package/.claude/nccgs/agent-library/nccgs-live-ops-designer.md +0 -25
  301. package/.claude/nccgs/agent-library/nccgs-localization-lead.md +0 -25
  302. package/.claude/nccgs/agent-library/nccgs-narrative-lead.md +0 -25
  303. package/.claude/nccgs/agent-library/nccgs-network-programmer.md +0 -25
  304. package/.claude/nccgs/agent-library/nccgs-performance-analyst.md +0 -25
  305. package/.claude/nccgs/agent-library/nccgs-producer.md +0 -25
  306. package/.claude/nccgs/agent-library/nccgs-production-coordinator.md +0 -22
  307. package/.claude/nccgs/agent-library/nccgs-programming-lead.md +0 -25
  308. package/.claude/nccgs/agent-library/nccgs-prototyper.md +0 -25
  309. package/.claude/nccgs/agent-library/nccgs-qa-engineer.md +0 -25
  310. package/.claude/nccgs/agent-library/nccgs-qa-lead.md +0 -25
  311. package/.claude/nccgs/agent-library/nccgs-release-engineer.md +0 -25
  312. package/.claude/nccgs/agent-library/nccgs-release-lead.md +0 -25
  313. package/.claude/nccgs/agent-library/nccgs-security-engineer.md +0 -25
  314. package/.claude/nccgs/agent-library/nccgs-sound-designer.md +0 -25
  315. package/.claude/nccgs/agent-library/nccgs-systems-designer.md +0 -25
  316. package/.claude/nccgs/agent-library/nccgs-task-scout.md +0 -22
  317. package/.claude/nccgs/agent-library/nccgs-technical-architect.md +0 -25
  318. package/.claude/nccgs/agent-library/nccgs-technical-artist.md +0 -25
  319. package/.claude/nccgs/agent-library/nccgs-technical-director.md +0 -25
  320. package/.claude/nccgs/agent-library/nccgs-tools-programmer.md +0 -25
  321. package/.claude/nccgs/agent-library/nccgs-ui-programmer.md +0 -25
  322. package/.claude/nccgs/agent-library/nccgs-unity-build-specialist.md +0 -25
  323. package/.claude/nccgs/agent-library/nccgs-unity-content-specialist.md +0 -25
  324. package/.claude/nccgs/agent-library/nccgs-unity-implementer.md +0 -26
  325. package/.claude/nccgs/agent-library/nccgs-unity-rendering-specialist.md +0 -25
  326. package/.claude/nccgs/agent-library/nccgs-unity-systems-specialist.md +0 -25
  327. package/.claude/nccgs/agent-library/nccgs-unity-ui-specialist.md +0 -25
  328. package/.claude/nccgs/agent-library/nccgs-ux-designer.md +0 -25
  329. package/.claude/nccgs/agent-library/nccgs-verification-engineer.md +0 -25
  330. package/.claude/nccgs/agent-library/nccgs-world-builder.md +0 -25
  331. package/.claude/nccgs/agent-library/nccgs-writer.md +0 -25
  332. package/.claude/nccgs/constitution.md +0 -187
  333. package/.claude/nccgs/hooks/agent-audit.mjs +0 -16
  334. package/.claude/nccgs/hooks/closure-guard.mjs +0 -59
  335. package/.claude/nccgs/hooks/common.mjs +0 -42
  336. package/.claude/nccgs/hooks/post-compact.mjs +0 -1
  337. package/.claude/nccgs/hooks/pre-compact.mjs +0 -8
  338. package/.claude/nccgs/hooks/protect-git.mjs +0 -22
  339. package/.claude/nccgs/hooks/protect-write.mjs +0 -17
  340. package/.claude/nccgs/hooks/runtime-observation.mjs +0 -11
  341. package/.claude/nccgs/hooks/session-start.mjs +0 -29
  342. package/.claude/nccgs/hooks/session-stop.mjs +0 -7
  343. package/.claude/nccgs/hooks/unity-transport-guard.mjs +0 -11
  344. package/.claude/nccgs/packages/ponytail/README.md +0 -46
  345. package/.claude/nccgs/packages/ponytail/manifest.json +0 -24
  346. package/.claude/nccgs/pipeline.json +0 -260
  347. package/.claude/nccgs/protocols/agent-contract.md +0 -101
  348. package/.claude/nccgs/protocols/astra-claude.md +0 -111
  349. package/.claude/nccgs/protocols/bounded-repair.md +0 -246
  350. package/.claude/nccgs/protocols/context-packets.md +0 -23
  351. package/.claude/nccgs/protocols/delivery-acceptance.md +0 -105
  352. package/.claude/nccgs/protocols/design-lifecycle.md +0 -139
  353. package/.claude/nccgs/protocols/editor-session-evidence.md +0 -52
  354. package/.claude/nccgs/protocols/evidence.md +0 -31
  355. package/.claude/nccgs/protocols/model-routing.md +0 -46
  356. package/.claude/nccgs/protocols/orchestration.md +0 -74
  357. package/.claude/nccgs/protocols/review-handoff.md +0 -101
  358. package/.claude/nccgs/protocols/shared-workflow-context.md +0 -33
  359. package/.claude/nccgs/protocols/standalone-gates.md +0 -329
  360. package/.claude/nccgs/protocols/standalone-pilot.md +0 -144
  361. package/.claude/nccgs/protocols/standalone.md +0 -207
  362. package/.claude/nccgs/protocols/task-autonomy.md +0 -55
  363. package/.claude/nccgs/protocols/task-gates.md +0 -144
  364. package/.claude/nccgs/protocols/task-slices.md +0 -106
  365. package/.claude/nccgs/protocols/unity-boundary.md +0 -31
  366. package/.claude/nccgs/routing.generated.md +0 -49
  367. package/.claude/nccgs/settings.fragment.json +0 -213
  368. package/.claude/nccgs/studio.json +0 -472
  369. package/.claude/nccgs/tools/approve-overrides.mjs +0 -81
  370. package/.claude/nccgs/tools/autonomy.mjs +0 -40
  371. package/.claude/nccgs/tools/command-process.mjs +0 -50
  372. package/.claude/nccgs/tools/command-tokens.mjs +0 -22
  373. package/.claude/nccgs/tools/compile-policy.mjs +0 -121
  374. package/.claude/nccgs/tools/configure-models.mjs +0 -96
  375. package/.claude/nccgs/tools/criteria.mjs +0 -121
  376. package/.claude/nccgs/tools/delivery-budget.mjs +0 -52
  377. package/.claude/nccgs/tools/design-readiness.mjs +0 -158
  378. package/.claude/nccgs/tools/editor-session-evidence.mjs +0 -66
  379. package/.claude/nccgs/tools/evidence.mjs +0 -153
  380. package/.claude/nccgs/tools/gates.mjs +0 -297
  381. package/.claude/nccgs/tools/handoff.mjs +0 -51
  382. package/.claude/nccgs/tools/hook-transport.mjs +0 -34
  383. package/.claude/nccgs/tools/inputs.mjs +0 -108
  384. package/.claude/nccgs/tools/installation-guard.mjs +0 -55
  385. package/.claude/nccgs/tools/integrity.mjs +0 -69
  386. package/.claude/nccgs/tools/lifecycle.mjs +0 -542
  387. package/.claude/nccgs/tools/native-completion.mjs +0 -30
  388. package/.claude/nccgs/tools/orchestration.mjs +0 -680
  389. package/.claude/nccgs/tools/paths.mjs +0 -60
  390. package/.claude/nccgs/tools/policy.mjs +0 -107
  391. package/.claude/nccgs/tools/process-lock.mjs +0 -74
  392. package/.claude/nccgs/tools/repair-packet.mjs +0 -106
  393. package/.claude/nccgs/tools/returned-handoff.mjs +0 -47
  394. package/.claude/nccgs/tools/review-budget.mjs +0 -171
  395. package/.claude/nccgs/tools/review-handoff.mjs +0 -66
  396. package/.claude/nccgs/tools/runtime.mjs +0 -71
  397. package/.claude/nccgs/tools/session-budget.mjs +0 -67
  398. package/.claude/nccgs/tools/shared-context-guard.mjs +0 -60
  399. package/.claude/nccgs/tools/shared-context.mjs +0 -25
  400. package/.claude/nccgs/tools/task-slices.mjs +0 -147
  401. package/.claude/nccgs/tools/task.mjs +0 -145
  402. package/.claude/nccgs/tools/tool-guard.mjs +0 -120
  403. package/.claude/nccgs/tools/unity-repair.mjs +0 -128
  404. package/.claude/nccgs/tools/unity-transport.mjs +0 -170
  405. package/.claude/nccgs/tools/verification-jobs.mjs +0 -171
  406. package/.claude/nccgs/tools/verification-worker.mjs +0 -71
  407. package/.claude/nccgs/tools/windows-compiler-server.cs +0 -163
  408. package/.claude/nccgs/tools/windows-job.cs +0 -103
  409. package/.claude/nccgs/unity-skills-manifest.json +0 -727
  410. package/.claude/nccgs/workflow-catalog.json +0 -654
  411. package/.claude/rules/nccgs-canonical-docs.md +0 -19
  412. package/.claude/rules/nccgs-editor-tools.md +0 -12
  413. package/.claude/rules/nccgs-localization.md +0 -12
  414. package/.claude/rules/nccgs-networking.md +0 -13
  415. package/.claude/rules/nccgs-performance.md +0 -14
  416. package/.claude/rules/nccgs-rendering.md +0 -16
  417. package/.claude/rules/nccgs-security.md +0 -13
  418. package/.claude/rules/nccgs-tests.md +0 -18
  419. package/.claude/rules/nccgs-ui.md +0 -14
  420. package/.claude/rules/nccgs-unity-assets.md +0 -21
  421. package/.claude/rules/nccgs-unity-code.md +0 -22
  422. package/.claude/skills/accessibility-review/SKILL.md +0 -22
  423. package/.claude/skills/accessibility-review/references/astra.md +0 -17
  424. package/.claude/skills/accessibility-review/references/legacy.md +0 -7
  425. package/.claude/skills/architecture-decision/SKILL.md +0 -22
  426. package/.claude/skills/architecture-decision/references/astra.md +0 -12
  427. package/.claude/skills/architecture-decision/references/legacy.md +0 -7
  428. package/.claude/skills/asset-audit/SKILL.md +0 -22
  429. package/.claude/skills/asset-audit/references/astra.md +0 -17
  430. package/.claude/skills/asset-audit/references/legacy.md +0 -7
  431. package/.claude/skills/audit/SKILL.md +0 -22
  432. package/.claude/skills/audit/references/astra.md +0 -17
  433. package/.claude/skills/audit/references/dimensions.md +0 -46
  434. package/.claude/skills/audit/references/legacy.md +0 -9
  435. package/.claude/skills/balance-review/SKILL.md +0 -22
  436. package/.claude/skills/balance-review/references/astra.md +0 -17
  437. package/.claude/skills/balance-review/references/legacy.md +0 -7
  438. package/.claude/skills/bug-triage/SKILL.md +0 -22
  439. package/.claude/skills/bug-triage/references/astra.md +0 -27
  440. package/.claude/skills/bug-triage/references/legacy.md +0 -7
  441. package/.claude/skills/build-live-game/SKILL.md +0 -317
  442. package/.claude/skills/build-live-game/references/cloud-code.md +0 -563
  443. package/.claude/skills/closure/SKILL.md +0 -22
  444. package/.claude/skills/closure/references/astra.md +0 -15
  445. package/.claude/skills/closure/references/legacy.md +0 -7
  446. package/.claude/skills/compatibility-review/SKILL.md +0 -22
  447. package/.claude/skills/compatibility-review/references/astra.md +0 -17
  448. package/.claude/skills/compatibility-review/references/legacy.md +0 -7
  449. package/.claude/skills/context-pack/SKILL.md +0 -22
  450. package/.claude/skills/context-pack/references/astra.md +0 -12
  451. package/.claude/skills/context-pack/references/legacy.md +0 -7
  452. package/.claude/skills/dependency-review/SKILL.md +0 -22
  453. package/.claude/skills/dependency-review/references/astra.md +0 -17
  454. package/.claude/skills/dependency-review/references/legacy.md +0 -7
  455. package/.claude/skills/design/SKILL.md +0 -24
  456. package/.claude/skills/design/references/astra.md +0 -12
  457. package/.claude/skills/design/references/legacy.md +0 -13
  458. package/.claude/skills/design-review/SKILL.md +0 -24
  459. package/.claude/skills/design-review/references/astra.md +0 -17
  460. package/.claude/skills/design-review/references/legacy.md +0 -7
  461. package/.claude/skills/evidence-review/SKILL.md +0 -22
  462. package/.claude/skills/evidence-review/references/astra.md +0 -17
  463. package/.claude/skills/evidence-review/references/legacy.md +0 -7
  464. package/.claude/skills/hotfix/SKILL.md +0 -22
  465. package/.claude/skills/hotfix/references/astra.md +0 -30
  466. package/.claude/skills/hotfix/references/legacy.md +0 -7
  467. package/.claude/skills/incident-recovery/SKILL.md +0 -22
  468. package/.claude/skills/incident-recovery/references/astra.md +0 -27
  469. package/.claude/skills/incident-recovery/references/legacy.md +0 -7
  470. package/.claude/skills/levelplay-unity-integration/SKILL.md +0 -1126
  471. package/.claude/skills/localization/SKILL.md +0 -135
  472. package/.claude/skills/localization/resources/L10nBatchProcessor.cs +0 -69
  473. package/.claude/skills/localize-game/SKILL.md +0 -22
  474. package/.claude/skills/localize-game/references/astra.md +0 -27
  475. package/.claude/skills/localize-game/references/legacy.md +0 -7
  476. package/.claude/skills/migrate-project/SKILL.md +0 -22
  477. package/.claude/skills/migrate-project/references/astra.md +0 -30
  478. package/.claude/skills/migrate-project/references/legacy.md +0 -19
  479. package/.claude/skills/migrate-project/references/procedure.md +0 -96
  480. package/.claude/skills/milestone-review/SKILL.md +0 -22
  481. package/.claude/skills/milestone-review/references/astra.md +0 -12
  482. package/.claude/skills/milestone-review/references/legacy.md +0 -7
  483. package/.claude/skills/nccgs-code-review/SKILL.md +0 -22
  484. package/.claude/skills/nccgs-code-review/references/astra.md +0 -21
  485. package/.claude/skills/nccgs-code-review/references/legacy.md +0 -7
  486. package/.claude/skills/new-unity-project/SKILL.md +0 -179
  487. package/.claude/skills/performance-audit/SKILL.md +0 -22
  488. package/.claude/skills/performance-audit/references/astra.md +0 -17
  489. package/.claude/skills/performance-audit/references/legacy.md +0 -7
  490. package/.claude/skills/physics-3d-collision/references/troubleshooting.md +0 -41
  491. package/.claude/skills/plan-feature/SKILL.md +0 -22
  492. package/.claude/skills/plan-feature/references/astra.md +0 -12
  493. package/.claude/skills/plan-feature/references/legacy.md +0 -7
  494. package/.claude/skills/playtest/SKILL.md +0 -22
  495. package/.claude/skills/playtest/references/astra.md +0 -31
  496. package/.claude/skills/playtest/references/legacy.md +0 -7
  497. package/.claude/skills/project-stage/SKILL.md +0 -22
  498. package/.claude/skills/project-stage/references/astra.md +0 -12
  499. package/.claude/skills/project-stage/references/legacy.md +0 -7
  500. package/.claude/skills/prototype-feature/SKILL.md +0 -22
  501. package/.claude/skills/prototype-feature/references/astra.md +0 -31
  502. package/.claude/skills/prototype-feature/references/legacy.md +0 -7
  503. package/.claude/skills/qa-plan/SKILL.md +0 -22
  504. package/.claude/skills/qa-plan/references/astra.md +0 -12
  505. package/.claude/skills/qa-plan/references/legacy.md +0 -7
  506. package/.claude/skills/release/SKILL.md +0 -22
  507. package/.claude/skills/release/references/astra.md +0 -30
  508. package/.claude/skills/release/references/legacy.md +0 -11
  509. package/.claude/skills/release-readiness/SKILL.md +0 -22
  510. package/.claude/skills/release-readiness/references/astra.md +0 -17
  511. package/.claude/skills/release-readiness/references/legacy.md +0 -7
  512. package/.claude/skills/retrospective/SKILL.md +0 -22
  513. package/.claude/skills/retrospective/references/astra.md +0 -12
  514. package/.claude/skills/retrospective/references/legacy.md +0 -7
  515. package/.claude/skills/review/SKILL.md +0 -22
  516. package/.claude/skills/review/references/astra.md +0 -21
  517. package/.claude/skills/review/references/legacy.md +0 -9
  518. package/.claude/skills/security-audit/SKILL.md +0 -22
  519. package/.claude/skills/security-audit/references/astra.md +0 -17
  520. package/.claude/skills/security-audit/references/legacy.md +0 -7
  521. package/.claude/skills/sprint-plan/SKILL.md +0 -22
  522. package/.claude/skills/sprint-plan/references/astra.md +0 -12
  523. package/.claude/skills/sprint-plan/references/legacy.md +0 -7
  524. package/.claude/skills/status/SKILL.md +0 -22
  525. package/.claude/skills/status/references/astra.md +0 -19
  526. package/.claude/skills/status/references/legacy.md +0 -9
  527. package/.claude/skills/story-readiness/SKILL.md +0 -24
  528. package/.claude/skills/story-readiness/references/astra.md +0 -17
  529. package/.claude/skills/story-readiness/references/legacy.md +0 -7
  530. package/.claude/skills/test/SKILL.md +0 -22
  531. package/.claude/skills/test/references/astra.md +0 -27
  532. package/.claude/skills/test/references/legacy.md +0 -9
  533. package/.claude/skills/ui-review/SKILL.md +0 -22
  534. package/.claude/skills/ui-review/references/astra.md +0 -17
  535. package/.claude/skills/ui-review/references/legacy.md +0 -7
  536. package/.claude/skills/unity-cli/CHANGELOG.md +0 -233
  537. package/.claude/skills/unity-cli/SECURITY.md +0 -22
  538. package/.claude/skills/unity-cli/SKILL.md +0 -414
  539. package/.claude/skills/unity-cli/references/auth-license-cloud.md +0 -146
  540. package/.claude/skills/unity-cli/references/build-run-test.md +0 -349
  541. package/.claude/skills/unity-cli/references/config-hub.md +0 -103
  542. package/.claude/skills/unity-cli/references/diagnostics-maintenance.md +0 -326
  543. package/.claude/skills/unity-cli/references/editors-install.md +0 -327
  544. package/.claude/skills/unity-cli/references/integration-advanced.md +0 -472
  545. package/.claude/skills/unity-cli/references/projects-templates.md +0 -574
  546. package/.claude/skills/work/SKILL.md +0 -22
  547. package/.claude/skills/work/references/astra.md +0 -32
  548. package/.claude/skills/work/references/classification.md +0 -41
  549. package/.claude/skills/work/references/closure.md +0 -60
  550. package/.claude/skills/work/references/feature-contracts.md +0 -36
  551. package/.claude/skills/work/references/legacy.md +0 -29
  552. package/.claude/skills/work/references/verification.md +0 -26
  553. package/CLAUDE.md +0 -10
  554. package/MEMORY.md +0 -109
  555. package/UPGRADING.md +0 -130
  556. package/VERSION +0 -1
  557. package/docs/ARCHITECTURE.md +0 -131
  558. package/docs/ASSET-PRODUCTION-GRAPHIFY-PLAN.md +0 -230
  559. package/docs/BOUNDED-REPAIR.md +0 -80
  560. package/docs/EDITOR-SESSION-EVIDENCE.md +0 -52
  561. package/docs/HANDOFF-AUTOMATION.md +0 -144
  562. package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +0 -117
  563. package/docs/MIGRATION-MATRIX.md +0 -23
  564. package/docs/NCCGS-1.3.md +0 -13
  565. package/docs/NCCGS-1.4.1.md +0 -149
  566. package/docs/NCCGS-1.4.md +0 -184
  567. package/docs/NCCGS-1.5-IMPLEMENTATION-PLAN.md +0 -378
  568. package/docs/NCCGS-1.5-PILOT.md +0 -130
  569. package/docs/NCCGS-1.5.md +0 -237
  570. package/docs/PHASE-1-OPERATIONS.md +0 -278
  571. package/docs/PHASE-2-OPERATIONS.md +0 -183
  572. package/docs/PROJECT-POLICY.md +0 -94
  573. package/docs/RELEASE-STATUS.md +0 -150
  574. package/docs/TASK-SLICING.md +0 -57
  575. package/docs/UNINSTALL.md +0 -134
  576. package/docs/UPGRADE-QUALIFICATION.md +0 -45
  577. package/docs/WFK17-HOOK-REPAIR.md +0 -71
  578. package/docs/WORKFLOWS.md +0 -85
  579. package/package-assets/nccgs-project.gitignore +0 -2
  580. package/package-assets/setup-vivox-voice-chat-evals.gitignore +0 -7
  581. package/scaffold/.nccgs/autonomy.json +0 -4
  582. package/scaffold/.nccgs/bugs/.gitkeep +0 -1
  583. package/scaffold/.nccgs/closures/.gitkeep +0 -1
  584. package/scaffold/.nccgs/context/.gitkeep +0 -1
  585. package/scaffold/.nccgs/decisions/.gitkeep +0 -1
  586. package/scaffold/.nccgs/evidence/.gitkeep +0 -1
  587. package/scaffold/.nccgs/execution.json +0 -10
  588. package/scaffold/.nccgs/features/.gitkeep +0 -1
  589. package/scaffold/.nccgs/gates.json +0 -7
  590. package/scaffold/.nccgs/inputs.json +0 -6
  591. package/scaffold/.nccgs/migrations/.gitkeep +0 -1
  592. package/scaffold/.nccgs/playtests/.gitkeep +0 -1
  593. package/scaffold/.nccgs/project.yaml +0 -109
  594. package/scaffold/.nccgs/requirements.yaml +0 -10
  595. package/scaffold/.nccgs/reviews/.gitkeep +0 -1
  596. package/scaffold/.nccgs/state.md +0 -40
  597. package/scaffold/.nccgs/templates/agent-handoff.md +0 -37
  598. package/scaffold/.nccgs/templates/architecture-decision.md +0 -27
  599. package/scaffold/.nccgs/templates/closure-record.md +0 -62
  600. package/scaffold/.nccgs/templates/context-packet.yaml +0 -17
  601. package/scaffold/.nccgs/templates/design-contract.json +0 -34
  602. package/scaffold/.nccgs/templates/evidence-record.md +0 -23
  603. package/scaffold/.nccgs/templates/feature-contract.md +0 -60
  604. package/scaffold/.nccgs/templates/implementation-report.md +0 -52
  605. package/scaffold/.nccgs/templates/migration-plan.md +0 -40
  606. package/scaffold/.nccgs/templates/repair-packet.json +0 -14
  607. package/scaffold/.nccgs/templates/repair-unity-code.json +0 -18
  608. package/scaffold/.nccgs/templates/repair-unity-editor.json +0 -21
  609. package/scaffold/.nccgs/templates/task-slices.json +0 -28
  610. package/scaffold/.nccgs/templates/waiver.md +0 -11
  611. package/scripts/benchmark-snapshot.mjs +0 -205
  612. package/scripts/benchmark-v15.mjs +0 -173
  613. package/scripts/cli.mjs +0 -205
  614. package/scripts/doctor.mjs +0 -261
  615. package/scripts/install.mjs +0 -530
  616. package/scripts/recover-session.mjs +0 -89
  617. package/scripts/run.mjs +0 -86
  618. package/scripts/supervise.mjs +0 -152
  619. package/scripts/uninstall.mjs +0 -292
  620. package/scripts/validate.mjs +0 -285
  621. package/scripts/work-handoff.mjs +0 -38
  622. package/tests/autonomy.test.mjs +0 -126
  623. package/tests/benchmark-v15.test.mjs +0 -65
  624. package/tests/design-readiness.test.mjs +0 -200
  625. package/tests/editor-session-evidence.test.mjs +0 -45
  626. package/tests/evidence.test.mjs +0 -186
  627. package/tests/fixtures/compiler-shutdown.cs +0 -67
  628. package/tests/fixtures/native-provider.mjs +0 -61
  629. package/tests/framework.test.mjs +0 -740
  630. package/tests/gates.test.mjs +0 -479
  631. package/tests/hook-repair.test.mjs +0 -106
  632. package/tests/lifecycle-v15.test.mjs +0 -346
  633. package/tests/native-completion-notices.test.mjs +0 -181
  634. package/tests/orchestration-v15.test.mjs +0 -596
  635. package/tests/phase1.test.mjs +0 -427
  636. package/tests/phase2.test.mjs +0 -250
  637. package/tests/policy-v15.test.mjs +0 -234
  638. package/tests/ponytail-package.test.mjs +0 -75
  639. package/tests/returned-handoff-notices.test.mjs +0 -170
  640. package/tests/review-budget.test.mjs +0 -177
  641. package/tests/runtime-v15.test.mjs +0 -1198
  642. package/tests/session-budget.test.mjs +0 -34
  643. package/tests/task-slices.test.mjs +0 -285
  644. package/tests/uninstall.test.mjs +0 -477
  645. package/tests/unity-generated.test.mjs +0 -140
  646. package/tests/unity-transport.test.mjs +0 -348
  647. /package/{.claude/nccgs/packages → template}/ponytail/LICENSE +0 -0
  648. /package/{.claude/nccgs/packages → template}/ponytail/NOTICE.md +0 -0
  649. /package/{.claude/nccgs/packages → template}/ponytail/complexity-review.md +0 -0
  650. /package/{.claude/skills → unity-skills}/build-live-game/references/remote-config.md +0 -0
  651. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/README.md +0 -0
  652. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/codeless-catalog.md +0 -0
  653. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-adapty.md +0 -0
  654. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-essentialkit.md +0 -0
  655. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-revenuecat.md +0 -0
  656. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-unipay.md +0 -0
  657. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/migration-v4-to-v5.md +0 -0
  658. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-add-iap-to-new-project.md +0 -0
  659. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-convert-native-storekit.md +0 -0
  660. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/platform-notes.md +0 -0
  661. /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/pre-check.md +0 -0
  662. /package/{.claude/skills → unity-skills}/localization/resources/LocalizedFontAsset.cs +0 -0
  663. /package/{.claude/skills → unity-skills}/optimize-audio/resources/platform-settings.md +0 -0
  664. /package/{.claude/skills → unity-skills}/optimize-web/resources/WebOptimizer.cs +0 -0
  665. /package/{.claude/skills → unity-skills}/optimize-web/resources/toktx-examples.sh +0 -0
  666. /package/{.claude/skills → unity-skills}/physics-3d-collision/resources/CollisionDebugger.cs +0 -0
  667. /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/dgs-entrypoint.md +0 -0
  668. /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/entrypoints.md +0 -0
  669. /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/examples.md +0 -0
  670. /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/implementation-fit.md +0 -0
  671. /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/underlying-services.md +0 -0
  672. /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/workflows-prerequisites.md +0 -0
  673. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/.env.example +0 -0
  674. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/README.md +0 -0
  675. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/promptfooconfig.yaml +0 -0
  676. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/tests/init-and-login.yaml +0 -0
  677. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/tests/text-chat.yaml +0 -0
  678. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/tests/voice-channels.yaml +0 -0
  679. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/events-and-participants.md +0 -0
  680. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/init-and-login.md +0 -0
  681. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/text-chat.md +0 -0
  682. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/troubleshooting.md +0 -0
  683. /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/voice-channels.md +0 -0
  684. /package/{.claude/skills → unity-skills}/shader-graph-create-custom-node/resources/all_hints.hlsl +0 -0
  685. /package/{.claude/skills → unity-skills}/sprite-editor/references/api_reference.md +0 -0
  686. /package/{.claude/skills → unity-skills}/sprite-editor/references/background.md +0 -0
  687. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/AutomaticSliceTexture.cs +0 -0
  688. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GenerateNewSpriteRects.cs +0 -0
  689. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GetTextureSourceImageSize.cs +0 -0
  690. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GetTextureToSlice.cs +0 -0
  691. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GridSliceTexture.cs +0 -0
  692. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/IsometricSliceTexture.cs +0 -0
  693. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/README.md +0 -0
  694. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/SetPivotExample.cs +0 -0
  695. /package/{.claude/skills → unity-skills}/sprite-editor/scripts/SpriteToPng.cs +0 -0
  696. /package/{.claude/skills → unity-skills}/ui-imgui/references/gui-elements.md +0 -0
  697. /package/{.claude/skills → unity-skills}/ui-imgui/references/templates.md +0 -0
  698. /package/{.claude/skills → unity-skills}/ui-ugui/references/scrollview-setup.md +0 -0
  699. /package/{.claude/skills → unity-skills}/ui-uitk/references/common-issues.md +0 -0
  700. /package/{.claude/skills → unity-skills}/ui-uitk/references/painter2d.md +0 -0
  701. /package/{.claude/skills → unity-skills}/ui-uitk/references/pointermanipulator-guide.md +0 -0
  702. /package/{.claude/skills → unity-skills}/ui-uitk/references/svg-icons.md +0 -0
  703. /package/{.claude/skills → unity-skills}/ui-uitk/references/ui-runtime-binding.md +0 -0
  704. /package/{.claude/skills → unity-skills}/ui-uitk/references/uss-guide.md +0 -0
  705. /package/{.claude/skills → unity-skills}/urp-postprocessing/references/effect-reference.md +0 -0
@@ -0,0 +1,79 @@
1
+ // Project page: .nccgs/project.html, one self-contained page a producer can read without Unity.
2
+ // Everything on it comes from the ledger, the reports, state.md and the transcripts; nothing is written by hand.
3
+ import path from "node:path";
4
+ import { writeText, readText, exists, toPosix } from "../core/fs.mjs";
5
+ import { at, loadConfig, editorVersion, pipelineVersion } from "../core/project.mjs";
6
+ import { listTasks } from "./ledger.mjs";
7
+ import { reportModel, esc, dataUri, CSS, tile, EN, VI } from "./report.mjs";
8
+ import { fmtTokens } from "./cost.mjs";
9
+
10
+ export function projectModel(root) {
11
+ const config = loadConfig(root);
12
+ const tasks = listTasks(root);
13
+ const models = tasks.map(t => { try { return reportModel(root, t); } catch { return null; } }).filter(Boolean);
14
+ const closed = models.filter(m => m.task.status === "closed");
15
+ const success = closed.filter(m => m.task.outcome === "success");
16
+ const open = models.filter(m => m.task.status !== "closed");
17
+ const findings = [];
18
+ for (const m of models) for (const f of m.findings) if (!f.resolved) findings.push({ task: m.task.id, title: m.task.title, ...f });
19
+ const bySeverity = { BLOCKER: 0, HIGH: 0, MEDIUM: 0, LOW: 0, INFO: 0 };
20
+ for (const f of findings) bySeverity[f.severity] = (bySeverity[f.severity] ?? 0) + 1;
21
+ const tokens = models.reduce((a, m) => ({ in: a.in + (m.cost?.total ? m.cost.total.input + m.cost.total.cacheWrite + m.cost.total.cacheRead : 0), out: a.out + (m.cost?.total?.output ?? 0), usd: a.usd + (m.cost?.estimatedUsd ?? 0) }), { in: 0, out: 0, usd: 0 });
22
+ const lastTests = models.find(m => m.tests)?.tests ?? null;
23
+ const state = readText(at(root, "state"), "");
24
+ const decisions = state.split("\n").filter(l => l.startsWith("- ")).slice(0, 12);
25
+ const stateFile = exists(at(root, "installState")) ? JSON.parse(readText(at(root, "installState"), "{}")) : {};
26
+ return {
27
+ lang: config.report?.language === "vi" ? "vi" : "en",
28
+ project: { name: config.name || path.basename(root), editor: editorVersion(root), pipeline: pipelineVersion(root), nccgs: stateFile.version ?? null, mode: config.mode, rigor: config.rigor },
29
+ models, closed, success, open, findings, bySeverity, tokens, lastTests, decisions, generatedAt: new Date().toISOString()
30
+ };
31
+ }
32
+
33
+ export function renderProject(root) {
34
+ const m = projectModel(root);
35
+ const t = { ...(m.lang === "vi" ? VI : EN), ...(m.lang === "vi" ? PVI : PEN) };
36
+ const h = [];
37
+ h.push(`<main><h1>${esc(m.project.name)} <span class="sub">· ${t.projectPage}</span></h1>`);
38
+ h.push(`<div class="sub">Unity ${esc(m.project.editor ?? "?")} · Pipeline ${esc(m.project.pipeline ?? "?")} · NCCGS ${esc(m.project.nccgs ?? "?")} · ${t.mode} ${esc(m.project.mode)} · ${t.rigor} ${esc(m.project.rigor)} · ${esc(m.generatedAt.slice(0, 16).replace("T", " "))} UTC</div>`);
39
+ h.push(`<div class="tiles">`);
40
+ h.push(tile(`${m.success.length}/${m.closed.length}`, t.tasksSuccess));
41
+ h.push(tile(String(m.open.length), t.tasksOpen));
42
+ h.push(tile(m.lastTests ? `${m.lastTests.passed}/${m.lastTests.total}` : "–", t.latestTests));
43
+ h.push(tile(`${m.bySeverity.BLOCKER + m.bySeverity.HIGH} / ${m.bySeverity.MEDIUM} / ${m.bySeverity.LOW}`, t.findingsOpen));
44
+ h.push(tile(m.tokens.out ? fmtTokens(m.tokens.out) : "–", t.tokensOut));
45
+ h.push(tile(m.tokens.usd ? `$${m.tokens.usd.toFixed(2)}` : "–", t.estimatedUsd));
46
+ h.push(`</div>`);
47
+ h.push(`<h2>${t.tasks}</h2><table><tr><th>${t.task}</th><th>${t.outcome}</th><th>${t.criteria}</th><th>${t.tests}</th><th>${t.review}</th><th>${t.fixRounds}</th><th>${t.tokensOut}</th><th>${t.closedAt}</th><th>${t.report}</th></tr>`);
48
+ for (const x of m.models) {
49
+ const task = x.task;
50
+ const outcome = task.status === "closed" ? task.outcome : task.status;
51
+ const html = path.join(at(root, "reports"), `${task.id}.html`);
52
+ h.push(`<tr><td><b>${esc(task.id)}</b><div class="sub">${esc(task.title)}</div></td><td><span class="chip ${esc(outcome)}">${esc(String(outcome).toUpperCase())}</span></td><td>${x.evaluation.summary.pass}/${x.evaluation.summary.total}</td><td>${x.tests ? `${x.tests.passed}/${x.tests.total}` : "–"}</td><td>${x.review ? `${esc(x.review.verdict)}<div class="sub">${esc(x.review.model ?? "")}</div>` : "–"}</td><td>${task.fixRounds}/${task.maxFixRounds}</td><td>${x.cost?.total?.messages ? fmtTokens(x.cost.total.output) : "–"}</td><td>${esc((task.closedAt ?? task.openedAt).slice(0, 10))}</td><td>${exists(html) ? `<a href="${esc(toPosix(path.relative(at(root, "nccgs"), html)))}">html</a>` : ""} <a href="${esc(toPosix(path.relative(at(root, "nccgs"), path.join(at(root, "reports"), `${task.id}.md`))))}">md</a></td></tr>`);
53
+ }
54
+ h.push(`</table>`);
55
+ const shots = m.models.map(x => ({ id: x.task.id, s: x.screens.at(-1) })).filter(x => x.s).slice(0, 8);
56
+ if (shots.length) {
57
+ h.push(`<h2>${t.latestScreens}</h2><div class="shots">`);
58
+ for (const { id, s } of shots) { const uri = dataUri(path.resolve(root, s.path)); if (uri) h.push(`<figure><img src="${uri}" alt="${esc(id)}"><figcaption>${esc(id)} · ${esc(path.basename(s.path))}${s.gameTimeS != null ? ` · t = ${s.gameTimeS.toFixed(2)} s` : ""}</figcaption></figure>`); }
59
+ h.push(`</div>`);
60
+ }
61
+ h.push(`<h2>${t.findingsOpenTitle}</h2>`);
62
+ if (m.findings.length) {
63
+ h.push(`<table><tr><th>${t.severity}</th><th>${t.task}</th><th>${t.finding}</th></tr>`);
64
+ const order = { BLOCKER: 0, HIGH: 1, MEDIUM: 2, LOW: 3, INFO: 4 };
65
+ for (const f of [...m.findings].sort((a, b) => (order[a.severity] ?? 9) - (order[b.severity] ?? 9))) h.push(`<tr><td><span class="sev ${esc(f.severity)}">${esc(f.severity)}</span></td><td>${esc(f.task)}</td><td>${esc(f.title ?? f.summary ?? "")}${f.detail ? `<div class="sub">${esc(String(f.detail).length > 300 ? String(f.detail).slice(0, 300).replace(/\s+\S*$/, "") + " …" : f.detail)}</div>` : ""}</td></tr>`);
66
+ h.push(`</table>`);
67
+ } else h.push(`<p class="sub">${t.noFindings}</p>`);
68
+ h.push(`<h2>${t.decisionsTitle}</h2>`);
69
+ if (m.decisions.length) { h.push(`<ul>`); for (const d of m.decisions) h.push(`<li>${esc(d.slice(2).replace(/\s·\s\[report\]\([^)]*\)/, ""))}</li>`); h.push(`</ul>`); }
70
+ else h.push(`<p class="sub">${t.noDecisions}</p>`);
71
+ h.push(`<p class="sub">${t.generatedBy}</p></main>`);
72
+ const html = `<!doctype html><html lang="${m.lang}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${esc(m.project.name)} · NCCGS</title><style>${CSS}</style></head><body>${h.join("\n")}</body></html>`;
73
+ const file = path.join(at(root, "nccgs"), "project.html");
74
+ writeText(file, html);
75
+ return { file, model: m };
76
+ }
77
+
78
+ const PEN = { projectPage: "project page", tasksSuccess: "tasks closed SUCCESS", tasksOpen: "tasks open", latestTests: "tests (latest task)", findingsOpen: "open findings H / M / L", estimatedUsd: "est. cost", tasks: "Tasks", task: "Task", outcome: "Outcome", closedAt: "Closed", report: "Report", latestScreens: "Latest frame per task", findingsOpenTitle: "Open review findings", severity: "Severity", finding: "Finding", noFindings: "No open findings.", decisionsTitle: "Recent closes (state.md)", noDecisions: "Nothing recorded yet.", generatedBy: "Generated by NCCGS from the task ledger, reports and transcripts. Nothing on this page was written by hand." };
79
+ const PVI = { projectPage: "trang dự án", tasksSuccess: "task đóng SUCCESS", tasksOpen: "task đang mở", latestTests: "test (task mới nhất)", findingsOpen: "finding mở H / M / L", estimatedUsd: "chi phí ước tính", tasks: "Task", task: "Task", outcome: "Kết quả", closedAt: "Đóng", report: "Report", latestScreens: "Frame mới nhất mỗi task", findingsOpenTitle: "Finding review còn mở", severity: "Mức", finding: "Finding", noFindings: "Không còn finding mở.", decisionsTitle: "Các lần đóng gần đây (state.md)", noDecisions: "Chưa ghi gì.", generatedBy: "NCCGS sinh từ ledger, report và transcript. Không dòng nào viết tay." };
@@ -0,0 +1,252 @@
1
+ // Task report: one page for a person, generated from the ledger (never free-written).
2
+ // `reportModel` gathers the facts once; `renderReport` writes <id>.md and <id>.html from it.
3
+ // The HTML embeds the screenshots, so the file can be mailed or dropped in a chat on its own.
4
+ import path from "node:path";
5
+ import fs from "node:fs";
6
+ import { writeText, toPosix, readText, exists } from "../core/fs.mjs";
7
+ import { at, loadConfig, editorVersion } from "../core/project.mjs";
8
+ import { evaluate } from "./criteria.mjs";
9
+ import { inScope } from "../core/snapshot.mjs";
10
+ import { taskCost, fmtTokens } from "./cost.mjs";
11
+
12
+ const STATUS_WORD = { PASS: "pass", FAIL: "FAIL", UNVERIFIED: "unverified", PENDING: "pending" };
13
+
14
+ export function reportModel(root, task, { outcome = task.outcome ?? "open", changedFiles = task.changedFiles ?? [] } = {}) {
15
+ const config = loadConfig(root);
16
+ const evaluation = evaluate(root, task.criteria, { manualDecisions: task.manualDecisions });
17
+ const review = task.reviews.at(-1);
18
+ const screens = [...new Map(task.verifications.flatMap(v => v.screens ?? []).map(s => [s.path, s])).values()].filter(s => s.path);
19
+ // Test runs count only on the task's final snapshot: exploratory runs on earlier snapshots (a probe with
20
+ // --name, a red run before the fix) are history, not the result.
21
+ const finalSnapshot = task.snapshotAtClose?.sha ?? task.verifications.at(-1)?.snapshot ?? null;
22
+ const testRuns = [...new Map(task.verifications.filter(v => v.kind === "tests" && (!finalSnapshot || v.snapshot === finalSnapshot)).map(v => [v.name, v])).values()];
23
+ const tests = testRuns.length ? testRuns.reduce((acc, v) => ({ passed: acc.passed + (v.summary.passed ?? 0), failed: acc.failed + (v.summary.failed ?? 0), skipped: acc.skipped + (v.summary.skipped ?? 0), total: acc.total + (v.summary.total ?? 0), failures: [...acc.failures, ...(v.summary.failures ?? [])] }), { passed: 0, failed: 0, skipped: 0, total: 0, failures: [] }) : null;
24
+ const observe = [...task.verifications].reverse().find(v => v.kind === "observe");
25
+ const observeJson = observe?.artifact && exists(path.resolve(root, observe.artifact)) ? JSON.parse(readText(path.resolve(root, observe.artifact), "{}")) : null;
26
+ const metas = changedFiles.filter(f => f.endsWith(".meta"));
27
+ const sources = changedFiles.filter(f => !f.endsWith(".meta"));
28
+ let cost = null;
29
+ try { cost = taskCost(root, task, { config }); } catch { cost = null; }
30
+ const findings = (review?.findings ?? []).map(f => ({ ...f, severity: (f.severity ?? "INFO").toUpperCase() }));
31
+ return {
32
+ lang: config.report?.language === "vi" ? "vi" : "en",
33
+ project: { name: config.name || path.basename(root), editor: editorVersion(root), mode: task.mode, rigor: task.rigor },
34
+ task, outcome, evaluation, review, findings, screens, testRuns, tests, observe: observeJson,
35
+ changes: { sources: sources.map(f => ({ path: f, inScope: inScope(f, task.files) })), metas, strayMetas: metas.filter(f => !inScope(f, task.files)) },
36
+ cost,
37
+ decisions: { pending: evaluation.rows.filter(r => r.status === "PENDING"), openFindings: findings.filter(f => !f.resolved && /HIGH|BLOCKER/.test(f.severity)) },
38
+ generatedAt: new Date().toISOString()
39
+ };
40
+ }
41
+
42
+ export function renderReport(root, task, options = {}) {
43
+ const m = reportModel(root, task, options);
44
+ const dir = at(root, "reports");
45
+ const md = renderMarkdown(root, m);
46
+ const html = renderHtml(root, m);
47
+ const file = path.join(dir, `${task.id}.md`);
48
+ const htmlFile = path.join(dir, `${task.id}.html`);
49
+ writeText(file, md);
50
+ writeText(htmlFile, html);
51
+ return { file, htmlFile, markdown: md, model: m };
52
+ }
53
+
54
+ function renderMarkdown(root, m) {
55
+ const t = m.lang === "vi" ? VI : EN;
56
+ const { task, evaluation, review, tests, testRuns, screens } = m;
57
+ const lines = [];
58
+ lines.push(`# ${task.id} · ${task.title} · ${m.outcome.toUpperCase()} · ${m.generatedAt.slice(0, 10)}`, "");
59
+ lines.push(`**${t.summary}.** ${summaryLine(t, m)}`, "");
60
+ lines.push(`## ${t.criteria}`, "", `| ID | ${t.description} | ${t.result} | ${t.evidence} |`, "| --- | --- | --- | --- |");
61
+ for (const row of evaluation.rows) {
62
+ const ev = row.assertions.map(a => a.artifact.split("/").pop() + (a.pointer ? `#${a.pointer}` : "")).join(", ") || (row.kind === "manual" ? t.manual : "");
63
+ lines.push(`| ${row.id} | ${row.description} | ${STATUS_WORD[row.status]} | ${ev} |`);
64
+ }
65
+ lines.push("");
66
+ if (screens.length) {
67
+ lines.push(`## ${t.screens}`, "");
68
+ for (const s of screens.slice(-4)) lines.push(`![${path.basename(s.path)}${s.gameTimeS != null ? ` · t=${s.gameTimeS.toFixed(2)}s` : ""}](${toPosix(path.relative(at(root, "reports"), path.resolve(root, s.path)))})`);
69
+ lines.push("");
70
+ }
71
+ if (tests) {
72
+ lines.push(`## ${t.tests}`, "");
73
+ for (const v of testRuns) lines.push(`- ${v.name} (${v.summary.mode ?? "?"}): ${t.testsLine(v.summary)}`);
74
+ lines.push("");
75
+ for (const f of tests.failures.slice(0, 10)) lines.push(`- ${f.name}: ${f.message || ""}`);
76
+ if (tests.failures.length) lines.push("");
77
+ }
78
+ lines.push(`## ${t.changes}`, "");
79
+ if (m.changes.sources.length) for (const f of m.changes.sources.slice(0, 60)) lines.push(`- ${f.path}${f.inScope ? "" : ` ${t.outsideContract}`}`);
80
+ else lines.push(t.noChanges);
81
+ if (m.changes.metas.length) lines.push(`- ${t.metaLine(m.changes.metas.length)}${m.changes.strayMetas.length ? ` · ${t.outsideContract}: ${m.changes.strayMetas.join(", ")}` : ""}`);
82
+ lines.push("");
83
+ lines.push(`## ${t.review}`, "");
84
+ if (review) {
85
+ lines.push(`${review.verdict} (${review.model ?? "model unknown"}${review.effort ? "/" + review.effort : ""}, snapshot ${review.snapshot?.slice(0, 8) ?? "?"})`, "");
86
+ for (const f of m.findings) lines.push(`- ${f.severity}: ${f.title ?? f.summary ?? ""}${findingState(t, f)}`);
87
+ if (review.screensViewed?.length) lines.push("", `${t.screensViewed}: ${[...new Set(review.screensViewed)].join(", ")}`);
88
+ } else lines.push(task.rigor === "prototype" ? t.noReviewPrototype : t.noReview);
89
+ lines.push("");
90
+ lines.push(`## ${t.cost}`, "", costLine(t, m), "");
91
+ if (m.cost?.total?.messages) {
92
+ for (const [agent, v] of Object.entries(m.cost.byAgent)) lines.push(`- ${agent}: ${fmtTokens(v.input + v.cacheWrite + v.cacheRead)} in (${fmtTokens(v.cacheRead)} cached) · ${fmtTokens(v.output)} out · ${v.messages} ${t.messages}`);
93
+ for (const [model, v] of Object.entries(m.cost.byModel)) lines.push(`- ${model}: ${fmtTokens(v.input + v.cacheWrite + v.cacheRead)} in · ${fmtTokens(v.output)} out`);
94
+ lines.push("");
95
+ }
96
+ if (task.notes) lines.push(`## ${t.howToPlay}`, "", task.notes, "");
97
+ if (m.decisions.pending.length || m.decisions.openFindings.length) {
98
+ lines.push(`## ${t.decisions}`, "");
99
+ for (const r of m.decisions.pending) lines.push(`- [ ] ${r.description}`);
100
+ for (const f of m.decisions.openFindings) lines.push(`- [ ] ${f.title ?? f.summary}`);
101
+ lines.push("");
102
+ }
103
+ return lines.join("\n");
104
+ }
105
+
106
+ function findingState(t, f) {
107
+ return f.resolved ? (f.resolvedBy ? ` (${t.resolvedBy(f.resolvedBy)}${f.resolvedReason ? `: ${f.resolvedReason}` : ""})` : ` (${t.resolved})`) : ` (${t.open})`;
108
+ }
109
+
110
+ function summaryLine(t, m) {
111
+ const parts = [`${t.criteriaShort} ${m.evaluation.summary.pass}/${m.evaluation.summary.total}`];
112
+ if (m.tests) parts.push(`${t.testsShort} ${m.tests.passed}/${m.tests.total}`);
113
+ if (m.review) parts.push(`${t.reviewShort} ${m.review.verdict}`);
114
+ parts.push(`${t.fixRounds} ${m.task.fixRounds}/${m.task.maxFixRounds}`);
115
+ if (m.cost?.total?.messages) parts.push(`${fmtTokens(m.cost.total.output)} ${t.tokensOut}`);
116
+ return parts.join(" · ") + ".";
117
+ }
118
+
119
+ function costLine(t, m) {
120
+ const base = `${m.task.verifications.length} ${t.verifyRuns} · ${m.task.fixRounds} ${t.fixRounds}`;
121
+ if (!m.cost?.total?.messages) return `${base} · ${t.noTranscript}`;
122
+ const c = m.cost.total;
123
+ const usd = m.cost.estimatedUsd != null ? ` · ≈ $${m.cost.estimatedUsd}${m.cost.unpricedModels ? ` (${m.cost.unpricedModels} ${t.unpriced})` : ""}` : "";
124
+ return `${base} · ${c.messages} ${t.messages} · ${fmtTokens(c.input + c.cacheWrite + c.cacheRead)} ${t.tokensIn} (${fmtTokens(c.cacheRead)} ${t.cached}) · ${fmtTokens(c.output)} ${t.tokensOut}${usd}`;
125
+ }
126
+
127
+ // ---- HTML --------------------------------------------------------------------------------------
128
+
129
+ export const esc = s => String(s ?? "").replace(/[&<>"]/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]));
130
+ export function dataUri(file) {
131
+ try { return `data:image/png;base64,${fs.readFileSync(file).toString("base64")}`; } catch { return null; }
132
+ }
133
+ export const CSS = `
134
+ :root{--bg:#fff;--fg:#1b1b1b;--muted:#666;--line:#e3e3e3;--card:#f7f7f5;--ok:#1e8e3e;--bad:#c5221f;--warn:#b26a00;--info:#1a73e8;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
135
+ @media(prefers-color-scheme:dark){:root{--bg:#141414;--fg:#e8e8e8;--muted:#9a9a9a;--line:#2c2c2c;--card:#1d1d1d;--ok:#5bbf72;--bad:#f28b82;--warn:#f0b35a;--info:#8ab4f8}}
136
+ body{margin:0;background:var(--bg);color:var(--fg);line-height:1.45}
137
+ main{max-width:1100px;margin:0 auto;padding:24px 16px 48px}
138
+ h1{font-size:1.5rem;margin:0 0 4px}h2{font-size:1.05rem;margin:28px 0 8px;border-bottom:1px solid var(--line);padding-bottom:4px}
139
+ .sub{color:var(--muted);font-size:.9rem}
140
+ .chip{display:inline-block;padding:2px 10px;border-radius:999px;font-size:.8rem;font-weight:600;color:#fff;vertical-align:middle}
141
+ .chip.success,.chip.pass{background:var(--ok)}.chip.failed,.chip.fail,.chip.blocked{background:var(--bad)}.chip.partial,.chip.pending,.chip.unverified{background:var(--warn)}.chip.open{background:var(--info)}
142
+ .tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin:16px 0}
143
+ .tile{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:10px 12px}.tile b{display:block;font-size:1.3rem}.tile span{color:var(--muted);font-size:.8rem}
144
+ table{border-collapse:collapse;width:100%;font-size:.9rem}th,td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--line);vertical-align:top}th{color:var(--muted);font-weight:600}
145
+ .shots{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.shots figure{margin:0}.shots img{width:100%;border:1px solid var(--line);border-radius:8px;background:#000}.shots figcaption{font-size:.8rem;color:var(--muted);margin-top:4px}
146
+ .sev{font-weight:700}.sev.BLOCKER,.sev.HIGH{color:var(--bad)}.sev.MEDIUM{color:var(--warn)}.sev.LOW,.sev.INFO{color:var(--muted)}
147
+ .res{color:var(--muted);font-size:.85rem}
148
+ ul{padding-left:20px}code{background:var(--card);padding:1px 4px;border-radius:4px;font-size:.85em}
149
+ .outside{color:var(--bad)}
150
+ a{color:var(--info)}
151
+ `;
152
+
153
+ function renderHtml(root, m) {
154
+ const t = m.lang === "vi" ? VI : EN;
155
+ const { task, evaluation, review, tests, testRuns, screens } = m;
156
+ const h = [];
157
+ h.push(`<main>`);
158
+ h.push(`<h1>${esc(task.id)} · ${esc(task.title)} <span class="chip ${esc(m.outcome)}">${esc(m.outcome.toUpperCase())}</span></h1>`);
159
+ h.push(`<div class="sub">${esc(m.project.name)} · Unity ${esc(m.project.editor ?? "?")} · ${t.rigor} ${esc(task.rigor)} · ${t.mode} ${esc(task.mode)} · ${esc(m.generatedAt.slice(0, 16).replace("T", " "))} UTC</div>`);
160
+ const c = m.cost?.total;
161
+ h.push(`<div class="tiles">`);
162
+ h.push(tile(`${evaluation.summary.pass}/${evaluation.summary.total}`, t.criteria));
163
+ h.push(tile(tests ? `${tests.passed}/${tests.total}` : "–", t.tests));
164
+ h.push(tile(review ? review.verdict : "–", t.review));
165
+ h.push(tile(`${task.fixRounds}/${task.maxFixRounds}`, t.fixRounds));
166
+ h.push(tile(c?.messages ? fmtTokens(c.output) : "–", t.tokensOut));
167
+ h.push(tile(c?.messages ? fmtTokens(c.input + c.cacheWrite + c.cacheRead) : "–", t.tokensIn));
168
+ h.push(`</div>`);
169
+ h.push(`<h2>${t.criteria}</h2><table><tr><th>ID</th><th>${t.description}</th><th>${t.result}</th><th>${t.evidence}</th></tr>`);
170
+ for (const row of evaluation.rows) {
171
+ const ev = row.assertions.map(a => esc(a.artifact.split("/").pop() + (a.pointer ? `#${a.pointer}` : ""))).join(", ") || (row.kind === "manual" ? t.manual : "");
172
+ h.push(`<tr><td><code>${esc(row.id)}</code></td><td>${esc(row.description)}</td><td><span class="chip ${esc(STATUS_WORD[row.status]).toLowerCase()}">${esc(STATUS_WORD[row.status])}</span></td><td>${ev}</td></tr>`);
173
+ }
174
+ h.push(`</table>`);
175
+ if (screens.length) {
176
+ h.push(`<h2>${t.screens}</h2><div class="shots">`);
177
+ for (const s of screens.slice(-6)) {
178
+ const uri = dataUri(path.resolve(root, s.path));
179
+ const cap = `${esc(path.basename(s.path))}${s.gameTimeS != null ? ` · t = ${s.gameTimeS.toFixed(2)} s` : s.atMs != null ? ` · +${s.atMs} ms` : ""}`;
180
+ h.push(`<figure>${uri ? `<img src="${uri}" alt="${esc(s.path)}">` : `<div class="sub">${esc(s.path)} (missing)</div>`}<figcaption>${cap}</figcaption></figure>`);
181
+ }
182
+ h.push(`</div>`);
183
+ }
184
+ if (tests) {
185
+ h.push(`<h2>${t.tests}</h2><ul>`);
186
+ for (const v of testRuns) h.push(`<li><b>${esc(v.name)}</b> (${esc(v.summary.mode ?? "?")}): ${esc(t.testsLine(v.summary))}</li>`);
187
+ for (const f of tests.failures.slice(0, 10)) h.push(`<li class="outside">${esc(f.name)}: ${esc(f.message || "")}</li>`);
188
+ h.push(`</ul>`);
189
+ }
190
+ h.push(`<h2>${t.changes}</h2><ul>`);
191
+ if (m.changes.sources.length) for (const f of m.changes.sources.slice(0, 60)) h.push(`<li><code>${esc(f.path)}</code>${f.inScope ? "" : ` <span class="outside">${t.outsideContract}</span>`}</li>`);
192
+ else h.push(`<li>${t.noChanges}</li>`);
193
+ if (m.changes.metas.length) h.push(`<li class="sub">${esc(t.metaLine(m.changes.metas.length))}${m.changes.strayMetas.length ? ` · <span class="outside">${t.outsideContract}: ${esc(m.changes.strayMetas.join(", "))}</span>` : ""}</li>`);
194
+ h.push(`</ul>`);
195
+ h.push(`<h2>${t.review}</h2>`);
196
+ if (review) {
197
+ h.push(`<p><span class="chip ${review.verdict === "PASS" ? "pass" : "fail"}">${esc(review.verdict)}</span> <span class="sub">${esc(review.model ?? "model unknown")}${review.effort ? "/" + esc(review.effort) : ""} · snapshot ${esc(review.snapshot?.slice(0, 8) ?? "?")}</span></p><ul>`);
198
+ for (const f of m.findings) h.push(`<li><span class="sev ${esc(f.severity)}">${esc(f.severity)}</span> ${esc(f.title ?? f.summary ?? "")} <span class="res">${esc(findingState(t, f).trim())}</span>${f.detail ? `<div class="sub">${esc(f.detail)}</div>` : ""}</li>`);
199
+ h.push(`</ul>`);
200
+ if (review.screensViewed?.length) h.push(`<p class="sub">${t.screensViewed}: ${esc([...new Set(review.screensViewed)].join(", "))}</p>`);
201
+ } else h.push(`<p class="sub">${task.rigor === "prototype" ? t.noReviewPrototype : t.noReview}</p>`);
202
+ h.push(`<h2>${t.cost}</h2><p>${esc(costLine(t, m))}</p>`);
203
+ if (c?.messages) {
204
+ h.push(`<table><tr><th>${t.who}</th><th>${t.tokensIn}</th><th>${t.cached}</th><th>${t.tokensOut}</th><th>${t.messages}</th></tr>`);
205
+ for (const [agent, v] of Object.entries(m.cost.byAgent)) h.push(`<tr><td>${esc(agent)}</td><td>${fmtTokens(v.input + v.cacheWrite + v.cacheRead)}</td><td>${fmtTokens(v.cacheRead)}</td><td>${fmtTokens(v.output)}</td><td>${v.messages}</td></tr>`);
206
+ for (const [model, v] of Object.entries(m.cost.byModel)) h.push(`<tr><td><code>${esc(model)}</code></td><td>${fmtTokens(v.input + v.cacheWrite + v.cacheRead)}</td><td>${fmtTokens(v.cacheRead)}</td><td>${fmtTokens(v.output)}</td><td>${v.messages}</td></tr>`);
207
+ h.push(`</table>`);
208
+ }
209
+ if (task.notes) h.push(`<h2>${t.howToPlay}</h2><p>${esc(task.notes)}</p>`);
210
+ if (m.decisions.pending.length || m.decisions.openFindings.length) {
211
+ h.push(`<h2>${t.decisions}</h2><ul>`);
212
+ for (const r of m.decisions.pending) h.push(`<li>☐ ${esc(r.description)}</li>`);
213
+ for (const f of m.decisions.openFindings) h.push(`<li>☐ ${esc(f.title ?? f.summary)}</li>`);
214
+ h.push(`</ul>`);
215
+ }
216
+ h.push(`<p class="sub">${t.generatedBy}</p></main>`);
217
+ return `<!doctype html><html lang="${m.lang}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${esc(task.id)} · ${esc(task.title)}</title><style>${CSS}</style></head><body>${h.join("\n")}</body></html>`;
218
+ }
219
+
220
+ export const tile = (value, label) => `<div class="tile"><b>${esc(value)}</b><span>${esc(label)}</span></div>`;
221
+
222
+ // Append a short entry to .nccgs/state.md so the next session starts from facts.
223
+ export function appendState(root, task, outcome, reportFile) {
224
+ const file = at(root, "state");
225
+ const existing = readText(file, "# Project state\n\nMaintained by `task close`. Newest first.\n\n");
226
+ const entry = `- ${new Date().toISOString().slice(0, 10)} · ${task.id} · ${task.title} · ${outcome.toUpperCase()} · [report](${toPosix(path.relative(root, reportFile))})\n`;
227
+ const marker = "Newest first.\n\n";
228
+ const idx = existing.indexOf(marker);
229
+ const next = idx >= 0 ? existing.slice(0, idx + marker.length) + entry + existing.slice(idx + marker.length) : existing + entry;
230
+ writeText(file, next);
231
+ }
232
+
233
+ export const EN = {
234
+ summary: "Summary", criteria: "Criteria", description: "Description", result: "Result", evidence: "Evidence", manual: "person decides",
235
+ screens: "Screens", tests: "Tests", changes: "Changes", review: "Review", cost: "Cost", howToPlay: "How to try it", decisions: "Needs your decision",
236
+ outsideContract: "(outside the contract)", noChanges: "No source changes recorded.", resolved: "resolved", open: "open", resolvedBy: by => `resolved by ${by}`, metaLine: n => `${n} .meta file${n === 1 ? "" : "s"} for the files above`, screensViewed: "Screens viewed by the reviewer",
237
+ noReview: "No independent review recorded.", noReviewPrototype: "No review (prototype rigor).",
238
+ criteriaShort: "criteria", testsShort: "tests", reviewShort: "review", fixRounds: "fix rounds", verifyRuns: "verification runs", rigor: "rigor", mode: "mode",
239
+ tokensIn: "tokens in", tokensOut: "tokens out", cached: "cached", messages: "messages", who: "Who", unpriced: "models without a price row", noTranscript: "token usage unavailable (no Claude Code transcript recorded for this window)",
240
+ generatedBy: "Generated by NCCGS from the task ledger. Nothing on this page was written by hand.",
241
+ testsLine: s => `${s.passed} passed, ${s.failed} failed, ${s.skipped} skipped of ${s.total} (${s.source}${s.durationSec ? `, ${s.durationSec}s` : ""}).`
242
+ };
243
+ export const VI = {
244
+ summary: "Tóm tắt", criteria: "Tiêu chí", description: "Mô tả", result: "Kết quả", evidence: "Bằng chứng", manual: "người quyết",
245
+ screens: "Ảnh", tests: "Test", changes: "Thay đổi", review: "Review", cost: "Chi phí", howToPlay: "Cách chơi thử", decisions: "Cần bạn quyết",
246
+ outsideContract: "(ngoài hợp đồng)", noChanges: "Không ghi nhận thay đổi source.", resolved: "đã xử lý", open: "còn mở", resolvedBy: by => `${by} đã xử lý`, metaLine: n => `${n} file .meta của các file trên`, screensViewed: "Ảnh reviewer đã xem",
247
+ noReview: "Chưa có review độc lập.", noReviewPrototype: "Không review (rigor prototype).",
248
+ criteriaShort: "tiêu chí", testsShort: "test", reviewShort: "review", fixRounds: "vòng sửa", verifyRuns: "lần verify", rigor: "rigor", mode: "mode",
249
+ tokensIn: "token vào", tokensOut: "token ra", cached: "cache", messages: "lượt gọi", who: "Ai", unpriced: "model chưa có giá", noTranscript: "chưa có số token (không ghi được transcript Claude Code cho khoảng này)",
250
+ generatedBy: "NCCGS sinh từ ledger của task. Không dòng nào viết tay.",
251
+ testsLine: s => `${s.passed} đạt, ${s.failed} lỗi, ${s.skipped} bỏ qua trên ${s.total} (${s.source}${s.durationSec ? `, ${s.durationSec}s` : ""}).`
252
+ };
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/env node
2
+ // `node .claude/nccgs/runtime/task/task.mjs <command> ...`
3
+ // Commands: open | verify | review | close | inspect | list | fix
4
+ // Output is JSON on stdout so Claude can read it; errors go to stderr with exit code 1.
5
+ import path from "node:path";
6
+ import fs from "node:fs";
7
+ import { projectRoot, at, loadConfig, isUnityProject } from "../core/project.mjs";
8
+ import { readJson, writeJson, writeText, exists, nowIso, toPosix } from "../core/fs.mjs";
9
+ import { snapshot, changedFiles, inScope } from "../core/snapshot.mjs";
10
+ import { run } from "../core/process-tree.mjs";
11
+ import * as unity from "../unity/unity.mjs";
12
+ import { playtest } from "../unity/playtest.mjs";
13
+ import { checkAssets } from "../unity/assets.mjs";
14
+ import { openTask, loadTask, saveTask, listTasks, assess, recordVerification, readyToClose, event, reviewsNeeded, resolveFinding } from "./ledger.mjs";
15
+ import { renderReport, appendState } from "./report.mjs";
16
+ import { renderProject } from "./project.mjs";
17
+ import { diag } from "../core/diag.mjs";
18
+ import { autoFeedback } from "./feedback.mjs";
19
+ import { loadPlan, planStatus, nextPlanned, planItem, savePlan } from "./plan.mjs";
20
+
21
+ const argv = process.argv.slice(2);
22
+ const command = argv[0];
23
+ const sep = argv.indexOf("--");
24
+ const own = sep < 0 ? argv.slice(1) : argv.slice(1, sep);
25
+ const forwarded = sep < 0 ? [] : argv.slice(sep + 1);
26
+ const opt = {};
27
+ for (let i = 0; i < own.length; i++) {
28
+ const a = own[i];
29
+ if (!a.startsWith("--")) continue;
30
+ const key = a.slice(2);
31
+ const next = own[i + 1];
32
+ if (next === undefined || next.startsWith("--")) opt[key] = true; else { opt[key] = next; i++; }
33
+ }
34
+ const root = projectRoot(opt.project);
35
+ const out = value => process.stdout.write(JSON.stringify(value, null, 2) + "\n");
36
+ const fail = message => { try { diag(root, "task.error", { command, args: own.slice(0, 20), message }); } catch {} process.stderr.write(`task ${command}: ${message}\n`); process.exit(1); };
37
+ const list = v => (typeof v === "string" ? v.split(",").map(s => s.trim()).filter(Boolean) : []);
38
+
39
+ try {
40
+ if (!command || command === "help") {
41
+ out({ commands: ["open [--plan <id>]", "verify", "review [--resolve <index|title> --reason ...]", "close", "inspect", "list", "fix", "report --id", "project", "plan [--status|--next|--show <id>|--validate <file>]"], hint: "node .claude/nccgs/runtime/task/task.mjs <command> --id T ..." });
42
+ } else if (!isUnityProject(root)) {
43
+ fail(`${root} is not a Unity project (needs Assets/ and ProjectSettings/ProjectVersion.txt)`);
44
+ } else if (command === "open") await cmdOpen();
45
+ else if (command === "verify") await cmdVerify();
46
+ else if (command === "review") await cmdReview();
47
+ else if (command === "close") await cmdClose();
48
+ else if (command === "inspect") out(withAssessment(loadTask(root, need("id"))));
49
+ else if (command === "plan") {
50
+ const sub = opt.show ? "show" : opt.next ? "next" : opt.validate ? "validate" : "status";
51
+ if (sub === "validate") { const f = opt.validate === true ? undefined : path.resolve(root, opt.validate); const plan = f ? readJson(f) : loadPlan(root); out({ file: f ?? null, ok: true, tasks: plan.tasks.map(t => t.id), saved: f ? toPosix(path.relative(root, savePlan(root, plan))) : null }); }
52
+ else if (sub === "show") out(planItem(root, opt.show === true ? (nextPlanned(root)?.id ?? fail("no ready task")) : opt.show));
53
+ else if (sub === "next") out(nextPlanned(root) ?? { done: true });
54
+ else out({ plan: loadPlan(root).name ?? null, tasks: planStatus(root) });
55
+ }
56
+ else if (command === "report") { const task = loadTask(root, need("id")); const r = renderReport(root, task, { outcome: task.outcome ?? task.status, changedFiles: task.changedFiles ?? changesSinceOpen(task) }); out({ id: task.id, file: toPosix(path.relative(root, r.file)), html: toPosix(path.relative(root, r.htmlFile)), tokensOut: r.model.cost?.total?.output ?? null }); }
57
+ else if (command === "project") { const r = renderProject(root); out({ file: toPosix(path.relative(root, r.file)), closed: r.model.closed.length, success: r.model.success.length, open: r.model.open.map(x => x.task.id), findings: r.model.bySeverity }); }
58
+ else if (command === "list") out(listTasks(root).map(t => ({ id: t.id, title: t.title, rigor: t.rigor, status: t.status, outcome: t.outcome, openedAt: t.openedAt })));
59
+ else if (command === "fix") {
60
+ const task = loadTask(root, need("id"));
61
+ task.fixRounds += 1;
62
+ saveTask(root, task);
63
+ event(root, "task.fix", { id: task.id, round: task.fixRounds });
64
+ out({ id: task.id, fixRounds: task.fixRounds, maxFixRounds: task.maxFixRounds, exhausted: task.fixRounds >= task.maxFixRounds });
65
+ } else fail(`unknown command ${command}`);
66
+ } catch (error) { try { diag(root, "task.exception", { command, args: own.slice(0, 20), error }); } catch {} fail(error.message); }
67
+
68
+ // Content diff against the snapshot taken at open; mtime fallback for tasks opened before alpha.3.
69
+ function changesSinceOpen(task) { return changedFiles(root, task.snapshotAtOpen?.sha, { sinceMs: Date.parse(task.openedAt) }); }
70
+ function outside(task, changed) { return changed.filter(f => !inScope(f, task.files)); }
71
+
72
+ function need(key) { if (!opt[key] || opt[key] === true) fail(`--${key} is required`); return opt[key]; }
73
+
74
+ function withAssessment(task) {
75
+ const a = assess(root, task);
76
+ return { ...task, assessment: { criteria: a.evaluation.summary, rows: a.evaluation.rows.map(r => ({ id: r.id, status: r.status, reasons: r.assertions.filter(x => x.status !== "PASS").map(x => x.reason).filter(Boolean) })), stale: a.stale, currentSnapshot: a.snapshot.sha, reviewsNeeded: reviewsNeeded(task), reviewsPassed: task.reviews.filter(r => r.verdict === "PASS" && r.snapshot === a.snapshot.sha).length } };
77
+ }
78
+
79
+ async function cmdOpen() {
80
+ let criteria = [];
81
+ if (opt.criteria && opt.criteria !== true) criteria = readJson(path.resolve(root, opt.criteria));
82
+ if (opt["criteria-json"]) criteria = JSON.parse(opt["criteria-json"]);
83
+ if (criteria && !Array.isArray(criteria) && Array.isArray(criteria.criteria)) criteria = criteria.criteria;
84
+ let spec = { id: opt.id === true ? undefined : opt.id, title: opt.title === true ? "" : opt.title, rigor: opt.rigor === true ? undefined : opt.rigor, files: list(opt.files), criteria, notes: opt.notes === true ? "" : opt.notes ?? "" };
85
+ if (opt.plan) {
86
+ // --plan <id>: the contract comes from .nccgs/design/slice.json; flags on the command line override it.
87
+ const item = planItem(root, opt.plan === true ? (nextPlanned(root)?.id ?? fail("plan has no ready task")) : opt.plan);
88
+ spec = { id: item.id, title: spec.title || item.title, rigor: spec.rigor ?? item.rigor, files: spec.files.length ? spec.files : item.files, criteria: criteria.length ? criteria : item.criteria, notes: spec.notes || item.notes || "", plan: true };
89
+ }
90
+ const task = openTask(root, spec);
91
+ out({ id: task.id, rigor: task.rigor, criteria: task.criteria.map(c => c.id), maxFixRounds: task.maxFixRounds, reviewsNeeded: reviewsNeeded(task), unity: task.unity, next: `node .claude/nccgs/runtime/task/task.mjs verify --id ${task.id} --kind tests|observe|console|command` });
92
+ }
93
+
94
+ async function cmdVerify() {
95
+ const task = loadTask(root, need("id"));
96
+ if (task.status === "closed") fail("task is closed");
97
+ const kind = opt.kind === true ? "tests" : (opt.kind ?? (forwarded.length ? "command" : "tests")); // tests|observe|console|playtest|assets|command
98
+ const measurements = at(root, "measurements");
99
+ fs.mkdirSync(measurements, { recursive: true });
100
+ let record;
101
+ if (kind === "tests") {
102
+ const mode = opt.mode === true ? undefined : opt.mode;
103
+ const result = await unity.testRun(root, { mode, filter: opt.filter === true ? undefined : opt.filter, timeout: Number(opt.timeout) || 600 });
104
+ const name = opt.name === true || !opt.name ? "tests" : opt.name; // --name playmode -> <id>-playmode.json
105
+ const file = path.join(measurements, `${task.id}-${name}.json`);
106
+ const summary = { ok: result.ok, source: result.source, mode: result.mode, total: result.total ?? 0, passed: result.passed ?? 0, failed: result.failed ?? 0, skipped: result.skipped ?? 0, failures: result.failures ?? [], errors: result.errors ?? [], durationSec: result.durationSec ?? null, at: nowIso() };
107
+ writeJson(file, summary);
108
+ record = { kind, name, ok: result.ok && (result.failed ?? 0) === 0, artifact: toPosix(path.relative(root, file)), summary };
109
+ } else if (kind === "observe") {
110
+ const screensDir = path.join(at(root, "screens"), task.id);
111
+ const o = await unity.observe(root, { screensDir, frames: Number(opt.frames) || 2, keys: list(opt.keys), holdMs: Number(opt.hold) || 1500, keysEveryFrame: opt["keys-every-frame"] === true, matchLog: typeof opt["match-log"] === "string" ? opt["match-log"] : null, shotsMs: typeof opt.shots === "string" ? list(opt.shots).map(Number).filter(n => Number.isFinite(n)) : null, holdKeyMs: Number(opt["hold-key"]) || 60 });
112
+ const exceptionCount = (o.console?.groundTruth?.consoleErrors ?? 0) + (o.console?.entries ?? []).filter(e => /exception/i.test(e.message ?? e.text ?? "")).length;
113
+ const file = path.join(measurements, `${task.id}-observe.json`);
114
+ const summary = { enteredPlayMode: o.enteredPlayMode, exitedPlayMode: o.exitedPlayMode, exceptionCount, compilationFailed: o.console?.groundTruth?.compilationFailed ?? null, consoleCounts: o.console?.counts ?? null, screens: o.screens.map(s => ({ path: s.path ? toPosix(path.relative(root, s.path)) : null, bytes: s.bytes, ...(s.atMs != null ? { atMs: s.atMs, gameTimeS: s.gameTimeS ?? null } : {}) })), keys: o.keys, keyPresses: o.keys.length, logMatches: o.logMatches, matchLog: typeof opt["match-log"] === "string" ? opt["match-log"] : null, timed: o.timed, runInBackground: o.runInBackground ?? false, scheduleDelayMs: o.scheduleDelayMs ?? null, scheduledAtGameTime: o.scheduledAtGameTime ?? null, performance: o.performance, errors: o.errors, at: nowIso() };
115
+ writeJson(file, summary);
116
+ record = { kind, name: opt.name === true ? "observe" : (opt.name ?? "observe"), ok: o.enteredPlayMode && o.exitedPlayMode && o.errors.length === 0 && exceptionCount === 0, artifact: toPosix(path.relative(root, file)), screens: summary.screens.filter(s => s.path), summary: { exceptionCount, screens: summary.screens.length } };
117
+ } else if (kind === "console") {
118
+ const r = await unity.consoleGet(root, { tail: Number(opt.tail) || 100, level: opt.level === true ? "warn" : (opt.level ?? "warn") });
119
+ const file = path.join(measurements, `${task.id}-console.json`);
120
+ const summary = { ok: r.ok, counts: r.result?.counts ?? null, groundTruth: r.result?.groundTruth ?? null, entries: (r.result?.entries ?? []).slice(-50), errors: r.errors, at: nowIso() };
121
+ writeJson(file, summary);
122
+ const why = r.ok ? null : (r.errors?.[0]?.code === "EDITOR_NOT_RUNNING" || !(await unity.instanceFor(root)) ? "no Editor open for this project: open it (unity open --project-path .) and run again; console needs a live Editor" : `${r.errors?.[0]?.code ?? "UNITY_ERROR"}: ${r.errors?.[0]?.message ?? r.stderr?.slice(0, 200) ?? ""}`);
123
+ record = { kind, name: "console", ok: r.ok && (summary.groundTruth?.compilationFailed !== true), artifact: toPosix(path.relative(root, file)), summary: { errors: summary.counts?.error ?? null, compilationFailed: summary.groundTruth?.compilationFailed ?? null, ...(why ? { reason: why } : {}) } };
124
+ } else if (kind === "playtest") {
125
+ const specFile = opt.spec && opt.spec !== true ? path.resolve(root, opt.spec) : fail("playtest needs --spec <file.json> (see protocols/playtest.md)");
126
+ const spec = readJson(specFile);
127
+ const name = opt.name === true || !opt.name ? (spec.name ?? "playtest") : opt.name;
128
+ const o = await playtest(root, { ...spec, name }, { outDir: path.join(at(root, "measurements"), "playtest", task.id) });
129
+ const file = path.join(measurements, `${task.id}-playtest-${name}.json`);
130
+ const summary = { name, spec: toPosix(path.relative(root, specFile)), enteredPlayMode: o.enteredPlayMode, exitedPlayMode: o.exitedPlayMode, presses: o.presses ?? null, plannedPresses: o.keys, ranS: o.ranS ?? null, stoppedEarly: o.stoppedEarly ?? null, stoppedAtS: o.stoppedAtS ?? null, rulePresses: o.rulePresses ?? [], summary: o.summary, samples: o.samples, console: o.console, errors: o.errors, csv: o.csv ? toPosix(path.relative(root, o.csv)) : null, at: nowIso() };
131
+ writeJson(file, summary);
132
+ record = { kind, name: `playtest-${name}`, ok: o.enteredPlayMode && o.exitedPlayMode && o.errors.length === 0, artifact: toPosix(path.relative(root, file)), summary: { presses: summary.presses, ranS: summary.ranS, stoppedEarly: summary.stoppedEarly, ...Object.fromEntries(Object.entries(o.summary ?? {}).filter(([k]) => !["samples"].includes(k)).map(([k, v]) => [k, v && typeof v === "object" ? v.final : v])) } };
133
+ } else if (kind === "assets") {
134
+ const specFile = opt.spec && opt.spec !== true ? path.resolve(root, opt.spec) : fail("assets needs --spec <file.json> (see skills/asset-spec)");
135
+ const spec = readJson(specFile);
136
+ const a = await checkAssets(root, spec);
137
+ const file = path.join(measurements, `${task.id}-assets.json`);
138
+ const summary = { spec: toPosix(path.relative(root, specFile)), checked: a.assets.length, mismatchCount: a.mismatches.length, mismatches: a.mismatches, assets: a.assets, errors: a.errors, at: nowIso() };
139
+ writeJson(file, summary);
140
+ record = { kind, name: "assets", ok: a.ok && a.mismatches.length === 0, artifact: toPosix(path.relative(root, file)), summary: { checked: summary.checked, mismatchCount: summary.mismatchCount, ...(a.errors.length ? { reason: a.errors[0].message } : {}) } };
141
+ } else if (kind === "command") {
142
+ if (!forwarded.length) fail("command kind needs `-- <executable> [args]`");
143
+ const name = opt.name === true || !opt.name ? "command" : opt.name;
144
+ const r = await run(forwarded[0], forwarded.slice(1), { cwd: root, timeoutMs: (Number(opt.timeout) || 600) * 1000 });
145
+ const file = path.join(measurements, `${task.id}-${name}.json`);
146
+ const summary = { command: forwarded, exitCode: r.code, timedOut: r.timedOut, stdout: r.stdout.slice(-4000), stderr: r.stderr.slice(-4000), at: nowIso() };
147
+ writeJson(file, summary);
148
+ record = { kind, name, ok: r.code === 0 && !r.timedOut, artifact: toPosix(path.relative(root, file)), summary: { exitCode: r.code, timedOut: r.timedOut } };
149
+ } else fail(`unknown --kind ${kind}`);
150
+ recordVerification(root, task, record);
151
+ if (!record.ok) diag(root, "verify.failed", { id: task.id, verifyKind: kind, name: record.name, summary: record.summary });
152
+ const a = assess(root, task);
153
+ out({ id: task.id, verification: record, criteria: a.evaluation.summary, rows: a.evaluation.rows.map(r => ({ id: r.id, status: r.status, reasons: r.assertions.filter(x => x.status !== "PASS").map(x => `${x.artifact}${x.pointer ?? ""}: ${x.reason}`) })) });
154
+ }
155
+
156
+ async function cmdReview() {
157
+ const task = loadTask(root, need("id"));
158
+ const dir = at(root, "review");
159
+ fs.mkdirSync(dir, { recursive: true });
160
+ if (opt.resolve) {
161
+ if (task.status === "closed") fail("task is closed");
162
+ const r = resolveFinding(root, task, { match: opt.resolve === true ? "" : opt.resolve, reason: opt.reason === true ? "" : opt.reason, by: opt.by === true ? "coordinator" : (opt.by ?? "coordinator") });
163
+ const open = (task.reviews.at(-1).findings ?? []).filter(f => !f.resolved).map(f => `${f.severity}: ${f.title ?? f.summary ?? ""}`);
164
+ out({ id: task.id, resolved: { index: r.index, severity: r.finding.severity, title: r.finding.title ?? r.finding.summary ?? "" }, stillOpen: open });
165
+ return;
166
+ }
167
+ if (opt.import) {
168
+ const file = path.resolve(root, opt.import === true ? path.join(dir, `${task.id}-verdict.json`) : opt.import);
169
+ const v = readJson(file);
170
+ const problems = [];
171
+ if (!["PASS", "CHANGES_REQUIRED", "BLOCKED"].includes(v.verdict)) problems.push("verdict must be PASS | CHANGES_REQUIRED | BLOCKED");
172
+ if (!Array.isArray(v.findings)) problems.push("findings must be an array (may be empty)");
173
+ const screens = task.verifications.flatMap(x => x.screens ?? []);
174
+ if (screens.length && !(Array.isArray(v.screensViewed) && v.screensViewed.length)) problems.push("screensViewed must list the screenshot files the reviewer looked at");
175
+ if (!v.snapshot) problems.push("snapshot (from the review prompt) is required");
176
+ if (v.verdict === "PASS" && (v.findings ?? []).some(f => /HIGH|BLOCKER/i.test(f.severity ?? "") && !f.resolved)) problems.push("PASS cannot carry unresolved HIGH/BLOCKER findings");
177
+ if (problems.length) fail(`verdict rejected:\n- ${problems.join("\n- ")}`);
178
+ const current = snapshot(root).sha;
179
+ const review = { verdict: v.verdict, findings: v.findings, screensViewed: v.screensViewed ?? [], summary: v.summary ?? "", model: opt.model === true ? null : (opt.model ?? v.model ?? null), effort: opt.effort === true ? null : (opt.effort ?? v.effort ?? null), snapshot: v.snapshot, snapshotCurrent: v.snapshot === current, at: nowIso(), file: toPosix(path.relative(root, file)) };
180
+ task.reviews.push(review);
181
+ task.status = review.verdict === "PASS" ? "reviewed" : "verifying";
182
+ saveTask(root, task);
183
+ event(root, "task.review", { id: task.id, verdict: review.verdict, model: review.model });
184
+ out({ id: task.id, review: { verdict: review.verdict, findings: review.findings.length, snapshotCurrent: review.snapshotCurrent }, reviewsPassed: task.reviews.filter(r => r.verdict === "PASS" && r.snapshot === current).length, reviewsNeeded: reviewsNeeded(task) });
185
+ return;
186
+ }
187
+ // --prepare (default): write the reviewer prompt from the ledger.
188
+ const a = assess(root, task);
189
+ const config = loadConfig(root);
190
+ const changed = changesSinceOpen(task);
191
+ const screens = task.verifications.flatMap(x => x.screens ?? []).map(s => s.path);
192
+ const tests = [...task.verifications].reverse().find(v => v.kind === "tests")?.summary;
193
+ const prompt = [
194
+ `# Independent review · ${task.id} · ${task.title}`, "",
195
+ `You are the independent reviewer. You did not write this change and you have not seen the conversation that produced it.`,
196
+ `Review the current source snapshot \`${a.snapshot.sha}\` against the contract below. Read the files, look at every screenshot, and return a verdict file.`, "",
197
+ `## Contract`, "", `Rigor: ${task.rigor}. Files in scope: ${task.files.length ? task.files.join(", ") : "(not declared)"}.`, "",
198
+ ...task.criteria.map(c => `- **${c.id}** — ${c.description} — current status: ${a.evaluation.rows.find(r => r.id === c.id)?.status}`), "",
199
+ `## Evidence`, "",
200
+ `- Changed files since open: ${changed.length ? changed.join(", ") : "none detected"}`,
201
+ `- Tests: ${tests ? `${tests.passed}/${tests.total} passed, ${tests.failed} failed (${tests.source})` : "no test run recorded"}`,
202
+ `- Screenshots (you must open each one and describe what you see): ${screens.length ? screens.join(", ") : "none"}`,
203
+ `- Measurements: ${task.verifications.map(v => v.artifact).join(", ") || "none"}`, "",
204
+ `## What to check`, "",
205
+ `1. Does the change satisfy each criterion, and is each piece of evidence real (produced by Unity, matching the snapshot)?`,
206
+ `2. Correctness and Unity lifecycle risks: null references, serialization, domain reload, physics timing, input handling.`,
207
+ `3. Scope: anything changed outside the files in scope, or any requirement quietly dropped?`,
208
+ `4. Simplicity (Ponytail complexity pass: see .claude/nccgs/ponytail/complexity-review.md) — but never ask for less feedback to the player.`, "",
209
+ `## Verdict file`, "",
210
+ `Write JSON to \`.nccgs/evidence/review/${task.id}-verdict.json\` with exactly these fields:`, "",
211
+ "```json", JSON.stringify({ verdict: "PASS | CHANGES_REQUIRED | BLOCKED", snapshot: a.snapshot.sha, summary: "two sentences", screensViewed: screens, findings: [{ severity: "BLOCKER | HIGH | MEDIUM | LOW", title: "", file: "", detail: "", resolved: false }], model: config.review?.model ?? "(the model you are running as, if known)" }, null, 2), "```", "",
212
+ `PASS only when every criterion passes on this snapshot and no HIGH/BLOCKER finding is open. Do not fix anything yourself.`
213
+ ].join("\n");
214
+ const file = path.join(dir, `${task.id}-prompt.md`);
215
+ writeText(file, prompt);
216
+ out({ id: task.id, promptFile: toPosix(path.relative(root, file)), verdictFile: toPosix(path.relative(root, path.join(dir, `${task.id}-verdict.json`))), snapshot: a.snapshot.sha, screens, next: `invoke the reviewer subagent with the prompt file, then: task review --id ${task.id} --import` });
217
+ }
218
+
219
+ async function cmdClose() {
220
+ const task = loadTask(root, need("id"));
221
+ const outcome = opt.outcome === true ? "success" : (opt.outcome ?? "success");
222
+ if (!["success", "partial", "blocked", "failed"].includes(outcome)) fail("outcome must be success | partial | blocked | failed");
223
+ for (const pair of list(opt.decide)) { const [id, decision] = pair.split("="); if (id && decision) task.manualDecisions[id] = decision; }
224
+ const ready = readyToClose(root, task, outcome);
225
+ if (!ready.ok && !opt.force) fail(`cannot close as ${outcome}:\n- ${ready.reasons.join("\n- ")}`);
226
+ const changed = changesSinceOpen(task);
227
+ const report = renderReport(root, task, { outcome, changedFiles: changed });
228
+ task.status = "closed"; task.outcome = outcome; task.closedAt = nowIso(); task.report = toPosix(path.relative(root, report.file)); task.reportHtml = toPosix(path.relative(root, report.htmlFile)); task.changedFiles = changed; task.snapshotAtClose = ready.snapshot;
229
+ saveTask(root, task);
230
+ appendState(root, task, outcome, report.file);
231
+ event(root, "task.close", { id: task.id, outcome });
232
+ let project = null;
233
+ try { project = toPosix(path.relative(root, renderProject(root).file)); } catch {}
234
+ // consent auto: a non-success close or accumulated diagnostics send a bundle now (best effort, 10 s cap)
235
+ let feedback = null;
236
+ try { const fb = await autoFeedback(root, { trigger: `task.close:${outcome}` }); feedback = fb.action === "send" ? (fb.sent?.ok ? `sent (${fb.reasons.join("; ")})` : `not sent: ${fb.sent?.reason ?? "?"}`) : fb.action === "none" ? null : `${fb.action}: ${fb.reasons.join("; ")}`; } catch {}
237
+ out({ id: task.id, outcome, report: task.report, reportHtml: task.reportHtml, project, feedback, tokensOut: report.model.cost?.total?.output ?? null, criteria: ready.evaluation.summary, changedFiles: changed.length, outsideContract: outside(task, changed) });
238
+ }