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.
- package/CHANGELOG.md +76 -235
- package/LICENSE +1 -1
- package/README.md +87 -206
- package/THIRD_PARTY_NOTICES.md +6 -34
- package/bin/nccgs.mjs +96 -0
- package/cli/doctor.mjs +82 -0
- package/cli/feedback.mjs +13 -0
- package/cli/install.mjs +111 -0
- package/cli/release.mjs +59 -0
- package/cli/run.mjs +35 -0
- package/cli/spawn.mjs +20 -0
- package/cli/uninstall.mjs +43 -0
- package/cli/update-check.mjs +66 -0
- package/docs/EVIDENCE-VERIFICATION.md +22 -0
- package/package.json +23 -30
- package/runtime/core/diag.mjs +43 -0
- package/runtime/core/feedback-policy.mjs +54 -0
- package/runtime/core/fs.mjs +80 -0
- package/runtime/core/job-object.mjs +73 -0
- package/runtime/core/modes.mjs +87 -0
- package/runtime/core/process-tree.mjs +109 -0
- package/runtime/core/project.mjs +113 -0
- package/runtime/core/snapshot.mjs +114 -0
- package/runtime/hooks/common.mjs +22 -0
- package/runtime/hooks/done-guard.mjs +15 -0
- package/runtime/hooks/guard-mcp.mjs +8 -0
- package/runtime/hooks/guard-shell.mjs +16 -0
- package/runtime/hooks/guard-write.mjs +25 -0
- package/runtime/hooks/session-log.mjs +7 -0
- package/runtime/hooks/session-start.mjs +40 -0
- package/runtime/hooks/unity-transport.mjs +12 -0
- package/runtime/modes.json +39 -0
- package/runtime/task/cost.mjs +103 -0
- package/runtime/task/criteria.mjs +128 -0
- package/runtime/task/feedback.mjs +136 -0
- package/runtime/task/ledger.mjs +109 -0
- package/runtime/task/plan.mjs +69 -0
- package/runtime/task/project.mjs +79 -0
- package/runtime/task/report.mjs +252 -0
- package/runtime/task/task.mjs +238 -0
- package/runtime/unity/assets.mjs +51 -0
- package/runtime/unity/playtest.mjs +145 -0
- package/runtime/unity/unity.mjs +350 -0
- package/scripts/check-job-object.mjs +18 -0
- package/scripts/sync-unity-skills.mjs +35 -119
- package/template/CLAUDE.md +39 -0
- package/template/agents/fast-fix.md +9 -0
- package/template/agents/implementer.md +19 -0
- package/template/agents/playtester.md +15 -0
- package/template/agents/reviewer.md +19 -0
- package/template/agents/scout.md +9 -0
- package/template/ponytail/README.md +8 -0
- package/{.claude/nccgs/packages → template}/ponytail/engineering.md +10 -0
- package/template/protocols/evidence.md +52 -0
- package/template/protocols/playtest.md +45 -0
- package/template/protocols/review.md +34 -0
- package/template/protocols/unity-cli-notes.md +56 -0
- package/template/settings.fragment.json +33 -0
- package/template/skills/art-bible/SKILL.md +19 -0
- package/template/skills/asset-spec/SKILL.md +19 -0
- package/template/skills/brainstorm/SKILL.md +17 -0
- package/template/skills/feedback/SKILL.md +16 -0
- package/template/skills/map-systems/SKILL.md +25 -0
- package/template/skills/playtest/SKILL.md +17 -0
- package/template/skills/status/SKILL.md +18 -0
- package/template/skills/test/SKILL.md +34 -0
- package/template/skills/unity-foundation/SKILL.md +17 -0
- package/template/skills/vertical-slice/SKILL.md +17 -0
- package/template/skills/work/SKILL.md +65 -0
- package/tools/feedback-worker/README.md +12 -0
- package/tools/feedback-worker/worker.js +39 -0
- package/tools/feedback-worker/wrangler.toml +12 -0
- package/unity-skills/2d-pixel-perfect/SKILL.md +208 -0
- package/unity-skills/2d-pixel-perfect/references/api-reference.md +81 -0
- package/unity-skills/2d-pixel-perfect/references/camera-setup-builtin.cs +40 -0
- package/unity-skills/2d-pixel-perfect/references/camera-setup-urp.cs +49 -0
- package/unity-skills/2d-pixel-perfect/references/pipeline-detection.cs +50 -0
- package/unity-skills/2d-pixel-perfect/references/sprite-settings.cs +37 -0
- package/unity-skills/asset-transformer-toolkit/SECURITY.md +15 -0
- package/unity-skills/asset-transformer-toolkit/SKILL.md +39 -0
- package/unity-skills/asset-transformer-toolkit/references/create-importer.md +121 -0
- package/unity-skills/asset-transformer-toolkit/references/lods.md +61 -0
- package/unity-skills/asset-transformer-toolkit/references/rule-api.md +154 -0
- package/unity-skills/asset-transformer-toolkit/references/ruleblock-api.md +37 -0
- package/unity-skills/asset-transformer-toolkit/references/ruleset-api.md +138 -0
- package/unity-skills/asset-transformer-toolkit/references/rulesets-and-actions.md +227 -0
- package/unity-skills/audio-setup-mixers/SECURITY.md +16 -0
- package/unity-skills/audio-setup-mixers/SKILL.md +127 -0
- package/unity-skills/audio-setup-mixers/references/api.md +162 -0
- package/unity-skills/build-gtk/SKILL.md +242 -0
- package/unity-skills/build-gtk/references/graph-api.md +324 -0
- package/unity-skills/build-gtk/references/pitfalls.md +86 -0
- package/unity-skills/build-gtk/references/runtime-and-visualization.md +196 -0
- package/unity-skills/build-gtk/references/state-machine-api.md +286 -0
- package/unity-skills/build-live-game/SKILL.md +255 -0
- package/{.claude/skills → unity-skills}/build-live-game/references/achievements.md +132 -154
- package/{.claude/skills → unity-skills}/build-live-game/references/apis.md +10 -19
- package/{.claude/skills → unity-skills}/build-live-game/references/authentication.md +14 -20
- package/{.claude/skills → unity-skills}/build-live-game/references/battlepass.md +269 -225
- package/unity-skills/build-live-game/references/blueprints-and-samples.md +75 -0
- package/unity-skills/build-live-game/references/cloud-code-cloudbehaviours.md +1111 -0
- package/unity-skills/build-live-game/references/cloud-code-modules.md +1276 -0
- package/unity-skills/build-live-game/references/cloud-code.md +58 -0
- package/{.claude/skills → unity-skills}/build-live-game/references/cloud-save.md +15 -25
- package/{.claude/skills → unity-skills}/build-live-game/references/deployment.md +33 -18
- package/{.claude/skills → unity-skills}/build-live-game/references/player-account.md +31 -23
- package/{.claude/skills → unity-skills}/build-live-game/references/tooling.md +0 -56
- package/unity-skills/build-live-game/references/ugs-cli.md +86 -0
- package/unity-skills/craft-game-floors/SECURITY.md +23 -0
- package/unity-skills/craft-game-floors/SKILL.md +134 -0
- package/unity-skills/craft-game-floors/references/art-direction.md +85 -0
- package/unity-skills/craft-game-floors/references/camera-rigs.md +51 -0
- package/unity-skills/craft-game-floors/references/character-motion.md +35 -0
- package/unity-skills/craft-game-floors/references/game-audio.md +47 -0
- package/unity-skills/craft-game-floors/references/game-feel.md +68 -0
- package/unity-skills/craft-game-floors/references/game-shell.md +45 -0
- package/unity-skills/craft-game-floors/references/live-floor.md +35 -0
- package/unity-skills/craft-game-floors/references/placeholder-assets.md +35 -0
- package/unity-skills/craft-game-floors/references/scene-lighting.md +52 -0
- package/unity-skills/craft-game-floors/references/toon-shading.md +34 -0
- package/unity-skills/craft-game-floors/references/vfx.md +40 -0
- package/unity-skills/craft-game-floors/references/visual-scorecard.md +43 -0
- package/unity-skills/create-2d-physics/SECURITY.md +16 -0
- package/unity-skills/create-2d-physics/SKILL.md +128 -0
- package/unity-skills/create-2d-physics/references/2d-physics-core.md +155 -0
- package/unity-skills/create-2d-physics/references/legacy-2d-physics.md +45 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/SKILL.md +12 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/references/hexagonalruletile.md +71 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/references/palette-create.md +38 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/references/ruletile-createempty.md +35 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/references/ruletile-createfromsegment.md +212 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/references/ruletile.md +90 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/scripts/CreatePaletteTemplate.cs +148 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/scripts/ManualWorkflowExample.cs +63 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/scripts/RuleTileGenerator.cs +61 -0
- package/unity-skills/create-tile-palette-or-rule-tiles/scripts/TilemapRuleTileCreateFromSegment.cs +225 -0
- package/unity-skills/generate-editor-search-query/SECURITY.md +17 -0
- package/unity-skills/generate-editor-search-query/SKILL.md +117 -0
- package/unity-skills/generate-editor-search-query/references/open-search-window.md +70 -0
- package/unity-skills/generate-editor-search-query/references/query-patterns.md +124 -0
- package/{.claude/skills → unity-skills}/implement-in-app-purchases/SKILL.md +1 -2
- package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/api-notes.md +25 -28
- package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-convert-native-google-billing.md +1 -1
- package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-implement-iap-d2c.md +40 -5
- package/{.claude/skills → unity-skills}/initialize-ai-navigation/SKILL.md +4 -4
- package/{.claude/skills → unity-skills}/initialize-ai-navigation/references/navigation-system.md +3 -3
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/CHANGELOG.md +12 -0
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/README.md +9 -5
- package/unity-skills/levelplay-unity-integration/SKILL.md +442 -0
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/banner-api.md +15 -8
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/best-practices.md +604 -536
- package/unity-skills/levelplay-unity-integration/references/dependency-resolution.md +94 -0
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/ilrd-api.md +2 -2
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/initialization-api.md +221 -0
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/interstitial-api.md +11 -3
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/ios-setup.md +6 -34
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/migration-sdk-9.md +1 -1
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/privacy-settings.md +14 -29
- package/{.claude/skills → unity-skills}/levelplay-unity-integration/references/rewarded-api.md +20 -4
- package/unity-skills/levelplay-unity-integration/references/testing-and-validation.md +222 -0
- package/unity-skills/levelplay-unity-integration/references/troubleshooting.md +99 -0
- package/unity-skills/localization/SKILL.md +347 -0
- package/{.claude/skills → unity-skills}/localization/references/api-notes.md +4 -0
- package/unity-skills/localization/resources/L10nBatchProcessor.cs +191 -0
- package/unity-skills/manage-sprite-atlas/SKILL.md +351 -0
- package/unity-skills/manage-sprite-atlas/references/addressables-delivery.md +58 -0
- package/unity-skills/manage-sprite-atlas/references/api.md +382 -0
- package/unity-skills/manage-sprite-atlas/references/best-practices.md +183 -0
- package/unity-skills/manage-sprite-atlas/references/common-errors.md +123 -0
- package/unity-skills/manage-sprite-atlas/references/custom-packing.md +147 -0
- package/unity-skills/manage-sprite-atlas/references/manual-authoring.md +39 -0
- package/unity-skills/manage-sprite-atlas/resources/authoringvsruntime.cs +176 -0
- package/unity-skills/manage-sprite-atlas/resources/buildaddressablespostprocess.cs +59 -0
- package/unity-skills/manage-sprite-atlas/resources/cacheloadedatlases.cs +145 -0
- package/unity-skills/manage-sprite-atlas/resources/cleanupbatchoperations.cs +119 -0
- package/unity-skills/manage-sprite-atlas/resources/createandconfiguremasteratlas.cs +57 -0
- package/unity-skills/manage-sprite-atlas/resources/custompackersetup.cs +88 -0
- package/unity-skills/manage-sprite-atlas/resources/deprecatedmethods.cs +142 -0
- package/unity-skills/manage-sprite-atlas/resources/dontmodifywithoutsaving.cs +120 -0
- package/unity-skills/manage-sprite-atlas/resources/dontpackinruntimebuilds.cs +190 -0
- package/unity-skills/manage-sprite-atlas/resources/dontscriptspriteatlasineditor.cs +114 -0
- package/unity-skills/manage-sprite-atlas/resources/editorconfiguration.cs +95 -0
- package/unity-skills/manage-sprite-atlas/resources/enablespritepacking.cs +172 -0
- package/unity-skills/manage-sprite-atlas/resources/gridspritepacker.cs +50 -0
- package/unity-skills/manage-sprite-atlas/resources/handlelatebinding.cs +147 -0
- package/unity-skills/manage-sprite-atlas/resources/handlenullcases.cs +141 -0
- package/unity-skills/manage-sprite-atlas/resources/migratev1tov2.cs +335 -0
- package/unity-skills/manage-sprite-atlas/resources/multipagepacker.cs +80 -0
- package/unity-skills/manage-sprite-atlas/resources/optimizetexturesettings.cs +191 -0
- package/unity-skills/manage-sprite-atlas/resources/packerdebugging.cs +158 -0
- package/unity-skills/manage-sprite-atlas/resources/packtransform.cs +51 -0
- package/unity-skills/manage-sprite-atlas/resources/platformnames.cs +127 -0
- package/unity-skills/manage-sprite-atlas/resources/queryspritesefficiently.cs +222 -0
- package/unity-skills/manage-sprite-atlas/resources/registercallbacks.cs +152 -0
- package/unity-skills/manage-sprite-atlas/resources/runtimeatlasloader.cs +15 -0
- package/unity-skills/manage-sprite-atlas/resources/runtimespriteaccess.cs +19 -0
- package/unity-skills/manage-sprite-atlas/resources/savespriteatlasasset.cs +107 -0
- package/unity-skills/manage-sprite-atlas/resources/scriptablepacker.cs +81 -0
- package/unity-skills/manage-sprite-atlas/resources/setincludeinbuild.cs +164 -0
- package/unity-skills/manage-sprite-atlas/resources/sizeoptimizedpacker.cs +88 -0
- package/unity-skills/manage-sprite-atlas/resources/spriteatlaslatebinding.cs +123 -0
- package/unity-skills/manage-sprite-atlas/resources/spriteatlasprebuildgenerator.cs +219 -0
- package/unity-skills/manage-sprite-atlas/resources/spritedata.cs +35 -0
- package/unity-skills/manage-sprite-atlas/resources/spritepack.cs +39 -0
- package/unity-skills/manage-sprite-atlas/resources/texturedata.cs +15 -0
- package/unity-skills/manage-sprite-atlas/resources/updatebuildscripts.cs +357 -0
- package/unity-skills/manage-sprite-atlas/resources/updatesettingsconfiguration.cs +135 -0
- package/unity-skills/manage-sprite-atlas/resources/usevariantswisely.cs +179 -0
- package/unity-skills/manage-sprite-atlas/resources/variantatlas.cs +39 -0
- package/unity-skills/manifest.json +1155 -0
- package/unity-skills/migrate-birp-to-urp/SECURITY.md +16 -0
- package/unity-skills/migrate-birp-to-urp/SKILL.md +543 -0
- package/unity-skills/migrate-birp-to-urp/references/capturing-the-editor.md +66 -0
- package/unity-skills/migrate-birp-to-urp/references/complex-shader-situations.md +272 -0
- package/unity-skills/migrate-birp-to-urp/references/custom-shader-triage.md +123 -0
- package/unity-skills/migrate-birp-to-urp/references/implementation-patterns.md +529 -0
- package/unity-skills/migrate-birp-to-urp/references/migration-workflow.md +265 -0
- package/unity-skills/migrate-birp-to-urp/references/quality-settings-map.md +69 -0
- package/unity-skills/new-unity-project/SECURITY.md +26 -0
- package/unity-skills/new-unity-project/SKILL.md +386 -0
- package/unity-skills/new-unity-project/references/skill-setup.md +66 -0
- package/unity-skills/optimize-audio/SECURITY.md +16 -0
- package/{.claude/skills → unity-skills}/optimize-audio/SKILL.md +17 -17
- package/{.claude/skills → unity-skills}/optimize-audio/resources/audio-import-api.md +8 -5
- package/{.claude/skills → unity-skills}/optimize-text-mesh-pro/SKILL.md +1 -11
- package/unity-skills/optimize-web/SECURITY.md +16 -0
- package/{.claude/skills → unity-skills}/optimize-web/SKILL.md +164 -27
- package/unity-skills/optimize-web/scripts/WebOptimizer.cs +46 -0
- package/unity-skills/optimize-web/scripts/toktx-examples.sh +11 -0
- package/{.claude/skills → unity-skills}/physics-3d-collision/SKILL.md +2 -2
- package/unity-skills/physics-3d-collision/references/troubleshooting.md +8 -0
- package/unity-skills/project-auditor-fixes/SKILL.md +64 -0
- package/{.claude/skills → unity-skills}/setup-multiplayer-services/SKILL.md +1 -4
- package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/SKILL.md +1 -1
- package/{.claude/skills → unity-skills}/shader-graph-create-custom-node/SKILL.md +1 -1
- package/unity-skills/sprite-editor/SECURITY.md +16 -0
- package/{.claude/skills → unity-skills}/sprite-editor/SKILL.md +9 -9
- package/{.claude/skills → unity-skills}/sprite-editor/references/templates.md +1 -1
- package/unity-skills/sprite-segment-3x3grid/SKILL.md +136 -0
- package/unity-skills/sprite-segment-3x3grid/scripts/SpriteGridAnalysis.cs +119 -0
- package/{.claude/skills → unity-skills}/ui/SKILL.md +1 -1
- package/{.claude/skills → unity-skills}/ui-imgui/SKILL.md +1 -7
- package/{.claude/skills → unity-skills}/ui-ugui/SKILL.md +3 -9
- package/{.claude/skills → unity-skills}/ui-uitk/SKILL.md +8 -14
- package/{.claude/skills → unity-skills}/ui-uitk/references/custom-elements.md +1 -1
- package/unity-skills/unity-cli/CHANGELOG.md +515 -0
- package/unity-skills/unity-cli/SECURITY.md +68 -0
- package/unity-skills/unity-cli/SKILL.md +723 -0
- package/unity-skills/unity-cli/references/auth-license-cloud.md +318 -0
- package/unity-skills/unity-cli/references/build-run-test.md +555 -0
- package/unity-skills/unity-cli/references/cloud-automation.md +736 -0
- package/{.claude/skills → unity-skills}/unity-cli/references/collaboration.md +166 -105
- package/unity-skills/unity-cli/references/config-hub.md +239 -0
- package/unity-skills/unity-cli/references/diagnostics-maintenance.md +720 -0
- package/unity-skills/unity-cli/references/docs-workflow.md +179 -0
- package/unity-skills/unity-cli/references/editors-install.md +476 -0
- package/unity-skills/unity-cli/references/integration-advanced.md +1130 -0
- package/unity-skills/unity-cli/references/playmode-verification-loop.md +279 -0
- package/unity-skills/unity-cli/references/projects-templates.md +996 -0
- package/unity-skills/unity-cli/references/version-control.md +612 -0
- package/{.claude/skills → unity-skills}/unity-package-management/SKILL.md +60 -12
- package/{.claude/skills → unity-skills}/unity-package-management/references/select-packages.md +39 -21
- package/unity-skills/unity-pipeline/SECURITY.md +17 -0
- package/unity-skills/unity-pipeline/SKILL.md +335 -0
- package/unity-skills/urp-postprocessing/SECURITY.md +16 -0
- package/{.claude/skills → unity-skills}/urp-postprocessing/SKILL.md +12 -12
- package/{.claude/skills → unity-skills}/urp-postprocessing/references/code-templates.md +39 -8
- package/{.claude/skills → unity-skills}/validate-urp-render-graph-renderer-feature/SKILL.md +1 -1
- package/.claude/agents/nccgs-adversarial-reviewer.md +0 -43
- package/.claude/agents/nccgs-engine-programmer.md +0 -27
- package/.claude/agents/nccgs-fast-implementer.md +0 -30
- package/.claude/agents/nccgs-game-designer.md +0 -26
- package/.claude/agents/nccgs-gameplay-programmer.md +0 -26
- package/.claude/agents/nccgs-qa-engineer.md +0 -27
- package/.claude/agents/nccgs-task-scout.md +0 -24
- package/.claude/agents/nccgs-ui-programmer.md +0 -27
- package/.claude/agents/nccgs-unity-build-specialist.md +0 -27
- package/.claude/agents/nccgs-unity-implementer.md +0 -28
- package/.claude/agents/nccgs-unity-systems-specialist.md +0 -27
- package/.claude/agents/nccgs-unity-ui-specialist.md +0 -27
- package/.claude/agents/nccgs-verification-engineer.md +0 -27
- package/.claude/nccgs/MEMORY.md +0 -66
- package/.claude/nccgs/THIRD_PARTY_NOTICES.md +0 -29
- package/.claude/nccgs/VERSION +0 -1
- package/.claude/nccgs/agent-library/nccgs-accessibility-specialist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-adversarial-reviewer.md +0 -41
- package/.claude/nccgs/agent-library/nccgs-ai-programmer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-analytics-engineer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-art-direction-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-audio-direction-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-creative-director.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-documentation-manager.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-economy-designer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-engine-programmer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-fast-implementer.md +0 -28
- package/.claude/nccgs/agent-library/nccgs-game-design-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-game-designer.md +0 -24
- package/.claude/nccgs/agent-library/nccgs-gameplay-programmer.md +0 -24
- package/.claude/nccgs/agent-library/nccgs-level-designer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-live-ops-designer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-localization-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-narrative-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-network-programmer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-performance-analyst.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-producer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-production-coordinator.md +0 -22
- package/.claude/nccgs/agent-library/nccgs-programming-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-prototyper.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-qa-engineer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-qa-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-release-engineer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-release-lead.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-security-engineer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-sound-designer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-systems-designer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-task-scout.md +0 -22
- package/.claude/nccgs/agent-library/nccgs-technical-architect.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-technical-artist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-technical-director.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-tools-programmer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-ui-programmer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-unity-build-specialist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-unity-content-specialist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-unity-implementer.md +0 -26
- package/.claude/nccgs/agent-library/nccgs-unity-rendering-specialist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-unity-systems-specialist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-unity-ui-specialist.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-ux-designer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-verification-engineer.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-world-builder.md +0 -25
- package/.claude/nccgs/agent-library/nccgs-writer.md +0 -25
- package/.claude/nccgs/constitution.md +0 -187
- package/.claude/nccgs/hooks/agent-audit.mjs +0 -16
- package/.claude/nccgs/hooks/closure-guard.mjs +0 -59
- package/.claude/nccgs/hooks/common.mjs +0 -42
- package/.claude/nccgs/hooks/post-compact.mjs +0 -1
- package/.claude/nccgs/hooks/pre-compact.mjs +0 -8
- package/.claude/nccgs/hooks/protect-git.mjs +0 -22
- package/.claude/nccgs/hooks/protect-write.mjs +0 -17
- package/.claude/nccgs/hooks/runtime-observation.mjs +0 -11
- package/.claude/nccgs/hooks/session-start.mjs +0 -29
- package/.claude/nccgs/hooks/session-stop.mjs +0 -7
- package/.claude/nccgs/hooks/unity-transport-guard.mjs +0 -11
- package/.claude/nccgs/packages/ponytail/README.md +0 -46
- package/.claude/nccgs/packages/ponytail/manifest.json +0 -24
- package/.claude/nccgs/pipeline.json +0 -260
- package/.claude/nccgs/protocols/agent-contract.md +0 -101
- package/.claude/nccgs/protocols/astra-claude.md +0 -111
- package/.claude/nccgs/protocols/bounded-repair.md +0 -246
- package/.claude/nccgs/protocols/context-packets.md +0 -23
- package/.claude/nccgs/protocols/delivery-acceptance.md +0 -105
- package/.claude/nccgs/protocols/design-lifecycle.md +0 -139
- package/.claude/nccgs/protocols/editor-session-evidence.md +0 -52
- package/.claude/nccgs/protocols/evidence.md +0 -31
- package/.claude/nccgs/protocols/model-routing.md +0 -46
- package/.claude/nccgs/protocols/orchestration.md +0 -74
- package/.claude/nccgs/protocols/review-handoff.md +0 -101
- package/.claude/nccgs/protocols/shared-workflow-context.md +0 -33
- package/.claude/nccgs/protocols/standalone-gates.md +0 -329
- package/.claude/nccgs/protocols/standalone-pilot.md +0 -144
- package/.claude/nccgs/protocols/standalone.md +0 -207
- package/.claude/nccgs/protocols/task-autonomy.md +0 -55
- package/.claude/nccgs/protocols/task-gates.md +0 -144
- package/.claude/nccgs/protocols/task-slices.md +0 -106
- package/.claude/nccgs/protocols/unity-boundary.md +0 -31
- package/.claude/nccgs/routing.generated.md +0 -49
- package/.claude/nccgs/settings.fragment.json +0 -213
- package/.claude/nccgs/studio.json +0 -472
- package/.claude/nccgs/tools/approve-overrides.mjs +0 -81
- package/.claude/nccgs/tools/autonomy.mjs +0 -40
- package/.claude/nccgs/tools/command-process.mjs +0 -50
- package/.claude/nccgs/tools/command-tokens.mjs +0 -22
- package/.claude/nccgs/tools/compile-policy.mjs +0 -121
- package/.claude/nccgs/tools/configure-models.mjs +0 -96
- package/.claude/nccgs/tools/criteria.mjs +0 -121
- package/.claude/nccgs/tools/delivery-budget.mjs +0 -52
- package/.claude/nccgs/tools/design-readiness.mjs +0 -158
- package/.claude/nccgs/tools/editor-session-evidence.mjs +0 -66
- package/.claude/nccgs/tools/evidence.mjs +0 -153
- package/.claude/nccgs/tools/gates.mjs +0 -297
- package/.claude/nccgs/tools/handoff.mjs +0 -51
- package/.claude/nccgs/tools/hook-transport.mjs +0 -34
- package/.claude/nccgs/tools/inputs.mjs +0 -108
- package/.claude/nccgs/tools/installation-guard.mjs +0 -55
- package/.claude/nccgs/tools/integrity.mjs +0 -69
- package/.claude/nccgs/tools/lifecycle.mjs +0 -542
- package/.claude/nccgs/tools/native-completion.mjs +0 -30
- package/.claude/nccgs/tools/orchestration.mjs +0 -680
- package/.claude/nccgs/tools/paths.mjs +0 -60
- package/.claude/nccgs/tools/policy.mjs +0 -107
- package/.claude/nccgs/tools/process-lock.mjs +0 -74
- package/.claude/nccgs/tools/repair-packet.mjs +0 -106
- package/.claude/nccgs/tools/returned-handoff.mjs +0 -47
- package/.claude/nccgs/tools/review-budget.mjs +0 -171
- package/.claude/nccgs/tools/review-handoff.mjs +0 -66
- package/.claude/nccgs/tools/runtime.mjs +0 -71
- package/.claude/nccgs/tools/session-budget.mjs +0 -67
- package/.claude/nccgs/tools/shared-context-guard.mjs +0 -60
- package/.claude/nccgs/tools/shared-context.mjs +0 -25
- package/.claude/nccgs/tools/task-slices.mjs +0 -147
- package/.claude/nccgs/tools/task.mjs +0 -145
- package/.claude/nccgs/tools/tool-guard.mjs +0 -120
- package/.claude/nccgs/tools/unity-repair.mjs +0 -128
- package/.claude/nccgs/tools/unity-transport.mjs +0 -170
- package/.claude/nccgs/tools/verification-jobs.mjs +0 -171
- package/.claude/nccgs/tools/verification-worker.mjs +0 -71
- package/.claude/nccgs/tools/windows-compiler-server.cs +0 -163
- package/.claude/nccgs/tools/windows-job.cs +0 -103
- package/.claude/nccgs/unity-skills-manifest.json +0 -727
- package/.claude/nccgs/workflow-catalog.json +0 -654
- package/.claude/rules/nccgs-canonical-docs.md +0 -19
- package/.claude/rules/nccgs-editor-tools.md +0 -12
- package/.claude/rules/nccgs-localization.md +0 -12
- package/.claude/rules/nccgs-networking.md +0 -13
- package/.claude/rules/nccgs-performance.md +0 -14
- package/.claude/rules/nccgs-rendering.md +0 -16
- package/.claude/rules/nccgs-security.md +0 -13
- package/.claude/rules/nccgs-tests.md +0 -18
- package/.claude/rules/nccgs-ui.md +0 -14
- package/.claude/rules/nccgs-unity-assets.md +0 -21
- package/.claude/rules/nccgs-unity-code.md +0 -22
- package/.claude/skills/accessibility-review/SKILL.md +0 -22
- package/.claude/skills/accessibility-review/references/astra.md +0 -17
- package/.claude/skills/accessibility-review/references/legacy.md +0 -7
- package/.claude/skills/architecture-decision/SKILL.md +0 -22
- package/.claude/skills/architecture-decision/references/astra.md +0 -12
- package/.claude/skills/architecture-decision/references/legacy.md +0 -7
- package/.claude/skills/asset-audit/SKILL.md +0 -22
- package/.claude/skills/asset-audit/references/astra.md +0 -17
- package/.claude/skills/asset-audit/references/legacy.md +0 -7
- package/.claude/skills/audit/SKILL.md +0 -22
- package/.claude/skills/audit/references/astra.md +0 -17
- package/.claude/skills/audit/references/dimensions.md +0 -46
- package/.claude/skills/audit/references/legacy.md +0 -9
- package/.claude/skills/balance-review/SKILL.md +0 -22
- package/.claude/skills/balance-review/references/astra.md +0 -17
- package/.claude/skills/balance-review/references/legacy.md +0 -7
- package/.claude/skills/bug-triage/SKILL.md +0 -22
- package/.claude/skills/bug-triage/references/astra.md +0 -27
- package/.claude/skills/bug-triage/references/legacy.md +0 -7
- package/.claude/skills/build-live-game/SKILL.md +0 -317
- package/.claude/skills/build-live-game/references/cloud-code.md +0 -563
- package/.claude/skills/closure/SKILL.md +0 -22
- package/.claude/skills/closure/references/astra.md +0 -15
- package/.claude/skills/closure/references/legacy.md +0 -7
- package/.claude/skills/compatibility-review/SKILL.md +0 -22
- package/.claude/skills/compatibility-review/references/astra.md +0 -17
- package/.claude/skills/compatibility-review/references/legacy.md +0 -7
- package/.claude/skills/context-pack/SKILL.md +0 -22
- package/.claude/skills/context-pack/references/astra.md +0 -12
- package/.claude/skills/context-pack/references/legacy.md +0 -7
- package/.claude/skills/dependency-review/SKILL.md +0 -22
- package/.claude/skills/dependency-review/references/astra.md +0 -17
- package/.claude/skills/dependency-review/references/legacy.md +0 -7
- package/.claude/skills/design/SKILL.md +0 -24
- package/.claude/skills/design/references/astra.md +0 -12
- package/.claude/skills/design/references/legacy.md +0 -13
- package/.claude/skills/design-review/SKILL.md +0 -24
- package/.claude/skills/design-review/references/astra.md +0 -17
- package/.claude/skills/design-review/references/legacy.md +0 -7
- package/.claude/skills/evidence-review/SKILL.md +0 -22
- package/.claude/skills/evidence-review/references/astra.md +0 -17
- package/.claude/skills/evidence-review/references/legacy.md +0 -7
- package/.claude/skills/hotfix/SKILL.md +0 -22
- package/.claude/skills/hotfix/references/astra.md +0 -30
- package/.claude/skills/hotfix/references/legacy.md +0 -7
- package/.claude/skills/incident-recovery/SKILL.md +0 -22
- package/.claude/skills/incident-recovery/references/astra.md +0 -27
- package/.claude/skills/incident-recovery/references/legacy.md +0 -7
- package/.claude/skills/levelplay-unity-integration/SKILL.md +0 -1126
- package/.claude/skills/localization/SKILL.md +0 -135
- package/.claude/skills/localization/resources/L10nBatchProcessor.cs +0 -69
- package/.claude/skills/localize-game/SKILL.md +0 -22
- package/.claude/skills/localize-game/references/astra.md +0 -27
- package/.claude/skills/localize-game/references/legacy.md +0 -7
- package/.claude/skills/migrate-project/SKILL.md +0 -22
- package/.claude/skills/migrate-project/references/astra.md +0 -30
- package/.claude/skills/migrate-project/references/legacy.md +0 -19
- package/.claude/skills/migrate-project/references/procedure.md +0 -96
- package/.claude/skills/milestone-review/SKILL.md +0 -22
- package/.claude/skills/milestone-review/references/astra.md +0 -12
- package/.claude/skills/milestone-review/references/legacy.md +0 -7
- package/.claude/skills/nccgs-code-review/SKILL.md +0 -22
- package/.claude/skills/nccgs-code-review/references/astra.md +0 -21
- package/.claude/skills/nccgs-code-review/references/legacy.md +0 -7
- package/.claude/skills/new-unity-project/SKILL.md +0 -179
- package/.claude/skills/performance-audit/SKILL.md +0 -22
- package/.claude/skills/performance-audit/references/astra.md +0 -17
- package/.claude/skills/performance-audit/references/legacy.md +0 -7
- package/.claude/skills/physics-3d-collision/references/troubleshooting.md +0 -41
- package/.claude/skills/plan-feature/SKILL.md +0 -22
- package/.claude/skills/plan-feature/references/astra.md +0 -12
- package/.claude/skills/plan-feature/references/legacy.md +0 -7
- package/.claude/skills/playtest/SKILL.md +0 -22
- package/.claude/skills/playtest/references/astra.md +0 -31
- package/.claude/skills/playtest/references/legacy.md +0 -7
- package/.claude/skills/project-stage/SKILL.md +0 -22
- package/.claude/skills/project-stage/references/astra.md +0 -12
- package/.claude/skills/project-stage/references/legacy.md +0 -7
- package/.claude/skills/prototype-feature/SKILL.md +0 -22
- package/.claude/skills/prototype-feature/references/astra.md +0 -31
- package/.claude/skills/prototype-feature/references/legacy.md +0 -7
- package/.claude/skills/qa-plan/SKILL.md +0 -22
- package/.claude/skills/qa-plan/references/astra.md +0 -12
- package/.claude/skills/qa-plan/references/legacy.md +0 -7
- package/.claude/skills/release/SKILL.md +0 -22
- package/.claude/skills/release/references/astra.md +0 -30
- package/.claude/skills/release/references/legacy.md +0 -11
- package/.claude/skills/release-readiness/SKILL.md +0 -22
- package/.claude/skills/release-readiness/references/astra.md +0 -17
- package/.claude/skills/release-readiness/references/legacy.md +0 -7
- package/.claude/skills/retrospective/SKILL.md +0 -22
- package/.claude/skills/retrospective/references/astra.md +0 -12
- package/.claude/skills/retrospective/references/legacy.md +0 -7
- package/.claude/skills/review/SKILL.md +0 -22
- package/.claude/skills/review/references/astra.md +0 -21
- package/.claude/skills/review/references/legacy.md +0 -9
- package/.claude/skills/security-audit/SKILL.md +0 -22
- package/.claude/skills/security-audit/references/astra.md +0 -17
- package/.claude/skills/security-audit/references/legacy.md +0 -7
- package/.claude/skills/sprint-plan/SKILL.md +0 -22
- package/.claude/skills/sprint-plan/references/astra.md +0 -12
- package/.claude/skills/sprint-plan/references/legacy.md +0 -7
- package/.claude/skills/status/SKILL.md +0 -22
- package/.claude/skills/status/references/astra.md +0 -19
- package/.claude/skills/status/references/legacy.md +0 -9
- package/.claude/skills/story-readiness/SKILL.md +0 -24
- package/.claude/skills/story-readiness/references/astra.md +0 -17
- package/.claude/skills/story-readiness/references/legacy.md +0 -7
- package/.claude/skills/test/SKILL.md +0 -22
- package/.claude/skills/test/references/astra.md +0 -27
- package/.claude/skills/test/references/legacy.md +0 -9
- package/.claude/skills/ui-review/SKILL.md +0 -22
- package/.claude/skills/ui-review/references/astra.md +0 -17
- package/.claude/skills/ui-review/references/legacy.md +0 -7
- package/.claude/skills/unity-cli/CHANGELOG.md +0 -233
- package/.claude/skills/unity-cli/SECURITY.md +0 -22
- package/.claude/skills/unity-cli/SKILL.md +0 -414
- package/.claude/skills/unity-cli/references/auth-license-cloud.md +0 -146
- package/.claude/skills/unity-cli/references/build-run-test.md +0 -349
- package/.claude/skills/unity-cli/references/config-hub.md +0 -103
- package/.claude/skills/unity-cli/references/diagnostics-maintenance.md +0 -326
- package/.claude/skills/unity-cli/references/editors-install.md +0 -327
- package/.claude/skills/unity-cli/references/integration-advanced.md +0 -472
- package/.claude/skills/unity-cli/references/projects-templates.md +0 -574
- package/.claude/skills/work/SKILL.md +0 -22
- package/.claude/skills/work/references/astra.md +0 -32
- package/.claude/skills/work/references/classification.md +0 -41
- package/.claude/skills/work/references/closure.md +0 -60
- package/.claude/skills/work/references/feature-contracts.md +0 -36
- package/.claude/skills/work/references/legacy.md +0 -29
- package/.claude/skills/work/references/verification.md +0 -26
- package/CLAUDE.md +0 -10
- package/MEMORY.md +0 -109
- package/UPGRADING.md +0 -130
- package/VERSION +0 -1
- package/docs/ARCHITECTURE.md +0 -131
- package/docs/ASSET-PRODUCTION-GRAPHIFY-PLAN.md +0 -230
- package/docs/BOUNDED-REPAIR.md +0 -80
- package/docs/EDITOR-SESSION-EVIDENCE.md +0 -52
- package/docs/HANDOFF-AUTOMATION.md +0 -144
- package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +0 -117
- package/docs/MIGRATION-MATRIX.md +0 -23
- package/docs/NCCGS-1.3.md +0 -13
- package/docs/NCCGS-1.4.1.md +0 -149
- package/docs/NCCGS-1.4.md +0 -184
- package/docs/NCCGS-1.5-IMPLEMENTATION-PLAN.md +0 -378
- package/docs/NCCGS-1.5-PILOT.md +0 -130
- package/docs/NCCGS-1.5.md +0 -237
- package/docs/PHASE-1-OPERATIONS.md +0 -278
- package/docs/PHASE-2-OPERATIONS.md +0 -183
- package/docs/PROJECT-POLICY.md +0 -94
- package/docs/RELEASE-STATUS.md +0 -150
- package/docs/TASK-SLICING.md +0 -57
- package/docs/UNINSTALL.md +0 -134
- package/docs/UPGRADE-QUALIFICATION.md +0 -45
- package/docs/WFK17-HOOK-REPAIR.md +0 -71
- package/docs/WORKFLOWS.md +0 -85
- package/package-assets/nccgs-project.gitignore +0 -2
- package/package-assets/setup-vivox-voice-chat-evals.gitignore +0 -7
- package/scaffold/.nccgs/autonomy.json +0 -4
- package/scaffold/.nccgs/bugs/.gitkeep +0 -1
- package/scaffold/.nccgs/closures/.gitkeep +0 -1
- package/scaffold/.nccgs/context/.gitkeep +0 -1
- package/scaffold/.nccgs/decisions/.gitkeep +0 -1
- package/scaffold/.nccgs/evidence/.gitkeep +0 -1
- package/scaffold/.nccgs/execution.json +0 -10
- package/scaffold/.nccgs/features/.gitkeep +0 -1
- package/scaffold/.nccgs/gates.json +0 -7
- package/scaffold/.nccgs/inputs.json +0 -6
- package/scaffold/.nccgs/migrations/.gitkeep +0 -1
- package/scaffold/.nccgs/playtests/.gitkeep +0 -1
- package/scaffold/.nccgs/project.yaml +0 -109
- package/scaffold/.nccgs/requirements.yaml +0 -10
- package/scaffold/.nccgs/reviews/.gitkeep +0 -1
- package/scaffold/.nccgs/state.md +0 -40
- package/scaffold/.nccgs/templates/agent-handoff.md +0 -37
- package/scaffold/.nccgs/templates/architecture-decision.md +0 -27
- package/scaffold/.nccgs/templates/closure-record.md +0 -62
- package/scaffold/.nccgs/templates/context-packet.yaml +0 -17
- package/scaffold/.nccgs/templates/design-contract.json +0 -34
- package/scaffold/.nccgs/templates/evidence-record.md +0 -23
- package/scaffold/.nccgs/templates/feature-contract.md +0 -60
- package/scaffold/.nccgs/templates/implementation-report.md +0 -52
- package/scaffold/.nccgs/templates/migration-plan.md +0 -40
- package/scaffold/.nccgs/templates/repair-packet.json +0 -14
- package/scaffold/.nccgs/templates/repair-unity-code.json +0 -18
- package/scaffold/.nccgs/templates/repair-unity-editor.json +0 -21
- package/scaffold/.nccgs/templates/task-slices.json +0 -28
- package/scaffold/.nccgs/templates/waiver.md +0 -11
- package/scripts/benchmark-snapshot.mjs +0 -205
- package/scripts/benchmark-v15.mjs +0 -173
- package/scripts/cli.mjs +0 -205
- package/scripts/doctor.mjs +0 -261
- package/scripts/install.mjs +0 -530
- package/scripts/recover-session.mjs +0 -89
- package/scripts/run.mjs +0 -86
- package/scripts/supervise.mjs +0 -152
- package/scripts/uninstall.mjs +0 -292
- package/scripts/validate.mjs +0 -285
- package/scripts/work-handoff.mjs +0 -38
- package/tests/autonomy.test.mjs +0 -126
- package/tests/benchmark-v15.test.mjs +0 -65
- package/tests/design-readiness.test.mjs +0 -200
- package/tests/editor-session-evidence.test.mjs +0 -45
- package/tests/evidence.test.mjs +0 -186
- package/tests/fixtures/compiler-shutdown.cs +0 -67
- package/tests/fixtures/native-provider.mjs +0 -61
- package/tests/framework.test.mjs +0 -740
- package/tests/gates.test.mjs +0 -479
- package/tests/hook-repair.test.mjs +0 -106
- package/tests/lifecycle-v15.test.mjs +0 -346
- package/tests/native-completion-notices.test.mjs +0 -181
- package/tests/orchestration-v15.test.mjs +0 -596
- package/tests/phase1.test.mjs +0 -427
- package/tests/phase2.test.mjs +0 -250
- package/tests/policy-v15.test.mjs +0 -234
- package/tests/ponytail-package.test.mjs +0 -75
- package/tests/returned-handoff-notices.test.mjs +0 -170
- package/tests/review-budget.test.mjs +0 -177
- package/tests/runtime-v15.test.mjs +0 -1198
- package/tests/session-budget.test.mjs +0 -34
- package/tests/task-slices.test.mjs +0 -285
- package/tests/uninstall.test.mjs +0 -477
- package/tests/unity-generated.test.mjs +0 -140
- package/tests/unity-transport.test.mjs +0 -348
- /package/{.claude/nccgs/packages → template}/ponytail/LICENSE +0 -0
- /package/{.claude/nccgs/packages → template}/ponytail/NOTICE.md +0 -0
- /package/{.claude/nccgs/packages → template}/ponytail/complexity-review.md +0 -0
- /package/{.claude/skills → unity-skills}/build-live-game/references/remote-config.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/README.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/codeless-catalog.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-adapty.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-essentialkit.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-revenuecat.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/convert-unipay.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/migration-v4-to-v5.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-add-iap-to-new-project.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/path-convert-native-storekit.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/platform-notes.md +0 -0
- /package/{.claude/skills → unity-skills}/implement-in-app-purchases/references/pre-check.md +0 -0
- /package/{.claude/skills → unity-skills}/localization/resources/LocalizedFontAsset.cs +0 -0
- /package/{.claude/skills → unity-skills}/optimize-audio/resources/platform-settings.md +0 -0
- /package/{.claude/skills → unity-skills}/optimize-web/resources/WebOptimizer.cs +0 -0
- /package/{.claude/skills → unity-skills}/optimize-web/resources/toktx-examples.sh +0 -0
- /package/{.claude/skills → unity-skills}/physics-3d-collision/resources/CollisionDebugger.cs +0 -0
- /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/dgs-entrypoint.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/entrypoints.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/examples.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/implementation-fit.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/underlying-services.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-multiplayer-services/references/workflows-prerequisites.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/.env.example +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/README.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/promptfooconfig.yaml +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/tests/init-and-login.yaml +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/tests/text-chat.yaml +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/evals/tests/voice-channels.yaml +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/events-and-participants.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/init-and-login.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/text-chat.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/troubleshooting.md +0 -0
- /package/{.claude/skills → unity-skills}/setup-vivox-voice-chat/references/voice-channels.md +0 -0
- /package/{.claude/skills → unity-skills}/shader-graph-create-custom-node/resources/all_hints.hlsl +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/references/api_reference.md +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/references/background.md +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/AutomaticSliceTexture.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GenerateNewSpriteRects.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GetTextureSourceImageSize.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GetTextureToSlice.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/GridSliceTexture.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/IsometricSliceTexture.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/README.md +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/SetPivotExample.cs +0 -0
- /package/{.claude/skills → unity-skills}/sprite-editor/scripts/SpriteToPng.cs +0 -0
- /package/{.claude/skills → unity-skills}/ui-imgui/references/gui-elements.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-imgui/references/templates.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-ugui/references/scrollview-setup.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-uitk/references/common-issues.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-uitk/references/painter2d.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-uitk/references/pointermanipulator-guide.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-uitk/references/svg-icons.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-uitk/references/ui-runtime-binding.md +0 -0
- /package/{.claude/skills → unity-skills}/ui-uitk/references/uss-guide.md +0 -0
- /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.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 => ({ "&": "&", "<": "<", ">": ">", '"': """ }[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
|
+
}
|