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,12 @@
|
|
|
1
|
+
// Unity transport policy: direct CLI + com.unity.pipeline only. No Unity MCP servers,
|
|
2
|
+
// no Coplay/AnkleBreaker, no `unity mcp` mode, no registering them through Claude/Codex.
|
|
3
|
+
export const PROVIDER = /mcp-?for-?unity|unity[-_. ]?mcp|coplay|anklebreaker|unitymcp/i;
|
|
4
|
+
|
|
5
|
+
export function unityMcpViolation(cmd) {
|
|
6
|
+
if (!cmd) return null;
|
|
7
|
+
if (/\bunity(\.exe)?\s+mcp\b/i.test(cmd)) return "`unity mcp` mode is disabled; use `unity command` / Pipeline directly";
|
|
8
|
+
if (/\b(claude|codex)\s+mcp\s+add(-json)?\b/i.test(cmd) && PROVIDER.test(cmd)) return "registering a Unity MCP server is disabled";
|
|
9
|
+
if (/\b(uvx|uv|pipx?|pip3|npx|npm|pnpm|bunx)\b/i.test(cmd) && PROVIDER.test(cmd)) return "installing or launching a Unity MCP server is disabled";
|
|
10
|
+
if (/\bgit\s+clone\b/i.test(cmd) && PROVIDER.test(cmd)) return "cloning a Unity MCP server is disabled";
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"builtin": true,
|
|
4
|
+
"agents": ["coordinator", "implementer", "reviewer", "fast-fix", "scout", "playtester"],
|
|
5
|
+
"modes": {
|
|
6
|
+
"FAST": {
|
|
7
|
+
"description": "Cheapest. Sonnet everywhere, Haiku for scouting. Good for prototypes and small fixes.",
|
|
8
|
+
"coordinator": { "model": "sonnet", "effort": "medium" },
|
|
9
|
+
"implementer": { "model": "sonnet", "effort": "high" },
|
|
10
|
+
"reviewer": { "model": "sonnet", "effort": "high", "fallback": null },
|
|
11
|
+
"fast-fix": { "model": "sonnet", "effort": "medium" },
|
|
12
|
+
"scout": { "model": "haiku", "effort": null },
|
|
13
|
+
"playtester": { "model": "sonnet", "effort": "medium" }
|
|
14
|
+
},
|
|
15
|
+
"BALANCE": {
|
|
16
|
+
"description": "Default. Opus for the coordinator, implementer and reviewer; Sonnet for support agents.",
|
|
17
|
+
"coordinator": { "model": "opus", "effort": "high" },
|
|
18
|
+
"implementer": { "model": "opus", "effort": "high" },
|
|
19
|
+
"reviewer": { "model": "opus", "effort": "high", "fallback": null },
|
|
20
|
+
"fast-fix": { "model": "sonnet", "effort": "high" },
|
|
21
|
+
"scout": { "model": "sonnet", "effort": "medium" },
|
|
22
|
+
"playtester": { "model": "sonnet", "effort": "high" }
|
|
23
|
+
},
|
|
24
|
+
"PERFORMANCE": {
|
|
25
|
+
"description": "Strongest. Opus/xhigh for building, Fable/high for independent review (falls back to Opus/xhigh when Fable is unavailable).",
|
|
26
|
+
"coordinator": { "model": "opus", "effort": "xhigh" },
|
|
27
|
+
"implementer": { "model": "opus", "effort": "xhigh" },
|
|
28
|
+
"reviewer": { "model": "fable", "effort": "high", "fallback": { "model": "opus", "effort": "xhigh" } },
|
|
29
|
+
"fast-fix": { "model": "sonnet", "effort": "high" },
|
|
30
|
+
"scout": { "model": "sonnet", "effort": "medium" },
|
|
31
|
+
"playtester": { "model": "sonnet", "effort": "high" }
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"allowed": {
|
|
35
|
+
"models": ["haiku", "sonnet", "opus", "fable", "inherit"],
|
|
36
|
+
"efforts": ["low", "medium", "high", "xhigh", "max"],
|
|
37
|
+
"notes": "A full model ID (claude-...) is also accepted. Unusual model/effort combinations warn, they do not fail."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Token cost per task, read from the Claude Code transcripts the hooks recorded (session.start and
|
|
2
|
+
// agent.* events carry transcript paths). Every assistant message in [openedAt, closedAt] counts once
|
|
3
|
+
// (deduplicated by message id); subagent transcripts under <session>/subagents/ count too and are
|
|
4
|
+
// attributed to the agent name the SubagentStart event recorded for that agentId.
|
|
5
|
+
// Tokens are the fact. Dollars are an estimate from config.pricing (per million tokens) and only
|
|
6
|
+
// appear for models that have a price row.
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { at, loadConfig } from "../core/project.mjs";
|
|
11
|
+
import { exists, readText } from "../core/fs.mjs";
|
|
12
|
+
|
|
13
|
+
const USAGE_KEYS = ["input_tokens", "cache_creation_input_tokens", "cache_read_input_tokens", "output_tokens"];
|
|
14
|
+
|
|
15
|
+
export function readEvents(root) {
|
|
16
|
+
const text = readText(at(root, "events"), "");
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const line of text.split("\n")) { if (!line.trim()) continue; try { out.push(JSON.parse(line)); } catch {} }
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Transcript files that can hold messages for this project: the session transcripts named by hooks
|
|
23
|
+
// and every subagent transcript beside them.
|
|
24
|
+
// Sessions recorded before transcript paths were logged (alpha.6 and earlier) are found by session id
|
|
25
|
+
// under ~/.claude/projects/*/<sessionId>.jsonl.
|
|
26
|
+
function findBySessionId(sessionId) {
|
|
27
|
+
const base = path.join(process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude"), "projects");
|
|
28
|
+
if (!exists(base)) return null;
|
|
29
|
+
for (const dir of fs.readdirSync(base)) {
|
|
30
|
+
const f = path.join(base, dir, `${sessionId}.jsonl`);
|
|
31
|
+
if (exists(f)) return f;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function transcriptFiles(root, events = readEvents(root)) {
|
|
37
|
+
const files = new Set();
|
|
38
|
+
const sessions = new Set();
|
|
39
|
+
for (const e of events) {
|
|
40
|
+
for (const p of [e.transcriptPath, e.agentTranscriptPath]) if (p && exists(p)) files.add(path.resolve(p));
|
|
41
|
+
if (e.sessionId && !e.transcriptPath) sessions.add(e.sessionId);
|
|
42
|
+
}
|
|
43
|
+
for (const id of sessions) { const f = findBySessionId(id); if (f) files.add(f); }
|
|
44
|
+
for (const f of [...files]) {
|
|
45
|
+
const dir = path.join(path.dirname(f), path.basename(f, ".jsonl"), "subagents");
|
|
46
|
+
if (exists(dir)) for (const n of fs.readdirSync(dir)) if (n.endsWith(".jsonl")) files.add(path.join(dir, n));
|
|
47
|
+
}
|
|
48
|
+
return [...files];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function* assistantMessages(file) {
|
|
52
|
+
let text;
|
|
53
|
+
try { text = fs.readFileSync(file, "utf8"); } catch { return; }
|
|
54
|
+
for (const line of text.split("\n")) {
|
|
55
|
+
if (!line.includes('"assistant"')) continue;
|
|
56
|
+
let j; try { j = JSON.parse(line); } catch { continue; }
|
|
57
|
+
if (j.type !== "assistant" || !j.message?.usage) continue;
|
|
58
|
+
yield { id: j.message.id ?? j.uuid, at: Date.parse(j.timestamp), model: j.message.model ?? "unknown", agentId: j.agentId ?? null, usage: j.message.usage };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const empty = () => ({ input: 0, cacheWrite: 0, cacheRead: 0, output: 0, messages: 0 });
|
|
63
|
+
function add(acc, u) {
|
|
64
|
+
acc.input += u.input_tokens ?? 0; acc.cacheWrite += u.cache_creation_input_tokens ?? 0; acc.cacheRead += u.cache_read_input_tokens ?? 0; acc.output += u.output_tokens ?? 0; acc.messages += 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function taskCost(root, task, { events = readEvents(root), config = loadConfig(root) } = {}) {
|
|
68
|
+
const from = Date.parse(task.openedAt), to = task.closedAt ? Date.parse(task.closedAt) : Date.now();
|
|
69
|
+
const agentNames = new Map();
|
|
70
|
+
for (const e of events) if (e.type === "agent.start" && e.agentId && e.agent) agentNames.set(e.agentId, e.agent);
|
|
71
|
+
const files = transcriptFiles(root, events);
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
const byModel = {}, byAgent = {};
|
|
74
|
+
const total = empty();
|
|
75
|
+
for (const file of files) {
|
|
76
|
+
for (const m of assistantMessages(file)) {
|
|
77
|
+
if (!(m.at >= from && m.at <= to) || seen.has(m.id)) continue;
|
|
78
|
+
seen.add(m.id);
|
|
79
|
+
const agent = m.agentId ? (agentNames.get(m.agentId) ?? "subagent") : "coordinator";
|
|
80
|
+
byModel[m.model] ??= empty(); add(byModel[m.model], m.usage);
|
|
81
|
+
byAgent[agent] ??= empty(); add(byAgent[agent], m.usage);
|
|
82
|
+
add(total, m.usage);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const pricing = config.pricing ?? {};
|
|
86
|
+
let usd = 0, priced = 0;
|
|
87
|
+
for (const [model, t] of Object.entries(byModel)) {
|
|
88
|
+
const row = priceFor(model, pricing);
|
|
89
|
+
if (!row) continue;
|
|
90
|
+
priced++;
|
|
91
|
+
usd += (t.input * row.input + t.cacheWrite * row.cacheWrite + t.cacheRead * row.cacheRead + t.output * row.output) / 1e6;
|
|
92
|
+
}
|
|
93
|
+
return { total, byModel, byAgent, transcripts: files.length, estimatedUsd: priced ? Number(usd.toFixed(4)) : null, pricedModels: priced, unpricedModels: Object.keys(byModel).length - priced };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// pricing rows are keyed by a substring of the model id ("opus", "sonnet", "claude-haiku-5-5").
|
|
97
|
+
function priceFor(model, pricing) {
|
|
98
|
+
const keys = Object.keys(pricing).sort((a, b) => b.length - a.length);
|
|
99
|
+
const k = keys.find(k => model.includes(k));
|
|
100
|
+
return k ? pricing[k] : null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const fmtTokens = n => n >= 1e6 ? `${(n / 1e6).toFixed(2)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Acceptance criteria: each criterion owns assertions against evidence artifacts that
|
|
2
|
+
// Unity (through `task verify`) produced. Claude writes criteria; it never writes artifacts.
|
|
3
|
+
//
|
|
4
|
+
// Assertion: { artifact, pointer?, op, value?, min?, max? }
|
|
5
|
+
// op: eq | gte | lte | exists | contains | range | image
|
|
6
|
+
// artifact: path relative to the project (usually under .nccgs/evidence/)
|
|
7
|
+
// pointer: JSON pointer into a JSON artifact (eq/gte/lte/contains/range)
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { readJson, exists } from "../core/fs.mjs";
|
|
11
|
+
|
|
12
|
+
export const OPS = ["eq", "gte", "lte", "exists", "contains", "range", "image"];
|
|
13
|
+
|
|
14
|
+
export function validateCriteria(criteria) {
|
|
15
|
+
const problems = [];
|
|
16
|
+
if (!Array.isArray(criteria) || criteria.length === 0) problems.push("criteria must be a non-empty array");
|
|
17
|
+
const ids = new Set();
|
|
18
|
+
for (const [i, c] of (criteria ?? []).entries()) {
|
|
19
|
+
const where = `criteria[${i}]`;
|
|
20
|
+
if (!c?.id || typeof c.id !== "string") problems.push(`${where}.id is required`);
|
|
21
|
+
else if (ids.has(c.id)) problems.push(`${where}.id duplicates ${c.id}`); else ids.add(c.id);
|
|
22
|
+
if (!c?.description) problems.push(`${where}.description is required`);
|
|
23
|
+
if (c?.kind === "manual") continue; // decided by a person at close time
|
|
24
|
+
if (!Array.isArray(c?.assertions) || !c.assertions.length) { problems.push(`${where}.assertions must be a non-empty array (or kind: "manual")`); continue; }
|
|
25
|
+
for (const [j, a] of c.assertions.entries()) {
|
|
26
|
+
const w = `${where}.assertions[${j}]`;
|
|
27
|
+
if (!a?.artifact) problems.push(`${w}.artifact is required`);
|
|
28
|
+
if (!OPS.includes(a?.op)) problems.push(`${w}.op must be one of ${OPS.join("|")}`);
|
|
29
|
+
if (["eq", "gte", "lte", "contains"].includes(a?.op) && !("value" in (a ?? {}))) problems.push(`${w}.value is required for ${a?.op}`);
|
|
30
|
+
if (["eq", "gte", "lte", "contains", "range"].includes(a?.op) && typeof a?.pointer !== "string") problems.push(`${w}.pointer is required for ${a?.op}`);
|
|
31
|
+
if (a?.op === "range" && !(Number.isFinite(a.min) || Number.isFinite(a.max))) problems.push(`${w} range needs min and/or max`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return problems;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function pointer(value, ptr) {
|
|
38
|
+
if (!ptr || ptr === "/") return value;
|
|
39
|
+
let cur = value;
|
|
40
|
+
for (const raw of ptr.split("/").slice(1)) {
|
|
41
|
+
const key = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
42
|
+
if (cur === null || typeof cur !== "object") return undefined;
|
|
43
|
+
cur = Array.isArray(cur) ? cur[Number(key)] : cur[key];
|
|
44
|
+
}
|
|
45
|
+
return cur;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function pngSize(file) {
|
|
49
|
+
try {
|
|
50
|
+
const fd = fs.openSync(file, "r"); const head = Buffer.alloc(24); fs.readSync(fd, head, 0, 24, 0); fs.closeSync(fd);
|
|
51
|
+
if (head.toString("hex", 0, 8) !== "89504e470d0a1a0a") return null;
|
|
52
|
+
return { width: head.readUInt32BE(16), height: head.readUInt32BE(20) };
|
|
53
|
+
} catch { return null; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Evaluate one assertion against the project. Returns { status: PASS|FAIL|UNVERIFIED, actual, reason }.
|
|
57
|
+
export function evaluateAssertion(root, a) {
|
|
58
|
+
const file = path.resolve(root, a.artifact);
|
|
59
|
+
if (!exists(file)) return { status: "UNVERIFIED", reason: `artifact missing: ${a.artifact}` };
|
|
60
|
+
if (a.op === "exists") {
|
|
61
|
+
const size = fs.statSync(file).size;
|
|
62
|
+
return size > 0 ? { status: "PASS", actual: size } : { status: "FAIL", actual: 0, reason: "artifact is empty" };
|
|
63
|
+
}
|
|
64
|
+
if (a.op === "image") {
|
|
65
|
+
const size = pngSize(file);
|
|
66
|
+
if (!size) return { status: "FAIL", reason: "not a PNG" };
|
|
67
|
+
const min = a.minWidth ?? 64;
|
|
68
|
+
const ok = size.width >= min && size.height >= Math.min(min, 64) && fs.statSync(file).size > 200;
|
|
69
|
+
return ok ? { status: "PASS", actual: size } : { status: "FAIL", actual: size, reason: `image smaller than ${min}px` };
|
|
70
|
+
}
|
|
71
|
+
let json;
|
|
72
|
+
try { json = readJson(file); } catch (e) { return { status: "UNVERIFIED", reason: e.message }; }
|
|
73
|
+
const actual = pointer(json, a.pointer);
|
|
74
|
+
if (actual === undefined) return { status: "UNVERIFIED", reason: `pointer ${a.pointer} not found` };
|
|
75
|
+
switch (a.op) {
|
|
76
|
+
case "eq": return actual === a.value ? { status: "PASS", actual } : { status: "FAIL", actual, reason: `expected ${JSON.stringify(a.value)}` };
|
|
77
|
+
case "gte": return typeof actual === "number" && actual >= a.value ? { status: "PASS", actual } : { status: "FAIL", actual, reason: `expected >= ${a.value}` };
|
|
78
|
+
case "lte": return typeof actual === "number" && actual <= a.value ? { status: "PASS", actual } : { status: "FAIL", actual, reason: `expected <= ${a.value}` };
|
|
79
|
+
case "range": {
|
|
80
|
+
const ok = typeof actual === "number" && (!Number.isFinite(a.min) || actual >= a.min) && (!Number.isFinite(a.max) || actual <= a.max);
|
|
81
|
+
return ok ? { status: "PASS", actual } : { status: "FAIL", actual, reason: `expected within [${a.min ?? "-inf"}, ${a.max ?? "inf"}]` };
|
|
82
|
+
}
|
|
83
|
+
case "contains": {
|
|
84
|
+
const hay = Array.isArray(actual) ? actual : String(actual);
|
|
85
|
+
const ok = Array.isArray(hay) ? hay.includes(a.value) : hay.includes(String(a.value));
|
|
86
|
+
return ok ? { status: "PASS", actual } : { status: "FAIL", actual, reason: `expected to contain ${JSON.stringify(a.value)}` };
|
|
87
|
+
}
|
|
88
|
+
default: return { status: "UNVERIFIED", reason: `unknown op ${a.op}` };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Evaluate all criteria. A criterion passes only when every assertion passes.
|
|
93
|
+
// `task close --decide id=pass|accepted|yes|ok` all mean accepted; fail|rejected|no mean rejected.
|
|
94
|
+
export function normalizeDecision(d) {
|
|
95
|
+
const v = String(d ?? "").trim().toLowerCase();
|
|
96
|
+
if (["accepted", "accept", "pass", "passed", "yes", "ok", "true"].includes(v)) return "accepted";
|
|
97
|
+
if (["rejected", "reject", "fail", "failed", "no", "false"].includes(v)) return "rejected";
|
|
98
|
+
return v || null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function evaluate(root, criteria, { manualDecisions = {} } = {}) {
|
|
102
|
+
const rows = criteria.map(c => {
|
|
103
|
+
if (c.kind === "manual") {
|
|
104
|
+
const d = normalizeDecision(manualDecisions[c.id]);
|
|
105
|
+
return { id: c.id, description: c.description, kind: "manual", status: d === "accepted" ? "PASS" : d === "rejected" ? "FAIL" : "PENDING", assertions: [] };
|
|
106
|
+
}
|
|
107
|
+
const results = c.assertions.map(a => ({ ...a, ...evaluateAssertion(root, a) }));
|
|
108
|
+
const status = results.every(r => r.status === "PASS") ? "PASS" : results.some(r => r.status === "FAIL") ? "FAIL" : "UNVERIFIED";
|
|
109
|
+
return { id: c.id, description: c.description, status, assertions: results };
|
|
110
|
+
});
|
|
111
|
+
const summary = { total: rows.length, pass: rows.filter(r => r.status === "PASS").length, fail: rows.filter(r => r.status === "FAIL").length, unverified: rows.filter(r => r.status === "UNVERIFIED").length, pending: rows.filter(r => r.status === "PENDING").length };
|
|
112
|
+
return { rows, summary, allPass: summary.pass === summary.total };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Default observation criteria added when a task touches what the player sees.
|
|
116
|
+
export function defaultObserveCriteria(taskId) {
|
|
117
|
+
const m = `.nccgs/evidence/measurements/${taskId}-observe.json`;
|
|
118
|
+
return [{
|
|
119
|
+
id: "observe", description: "The game enters Play Mode without exceptions and a screenshot of the Game view is captured",
|
|
120
|
+
assertions: [
|
|
121
|
+
{ artifact: m, pointer: "/enteredPlayMode", op: "eq", value: true },
|
|
122
|
+
{ artifact: m, pointer: "/exceptionCount", op: "eq", value: 0 },
|
|
123
|
+
{ artifact: `.nccgs/evidence/screens/${taskId}/01.png`, op: "image" }
|
|
124
|
+
]
|
|
125
|
+
}];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const PLAYER_VISIBLE = /(^|\/)(Assets\/.*\.(unity|prefab|uxml|uss|shader|shadergraph|mat|anim|controller)|Assets\/(UI|Scenes|Prefabs)\/)/i;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// nccgs feedback: one bundle a person can send after days of use — what ran, what failed, what was
|
|
2
|
+
// blocked, how long and how many tokens each task took, the doctor's view and any note they add.
|
|
3
|
+
// Everything comes from local files (ledger, events, diagnostics, reports); no code, no screenshots,
|
|
4
|
+
// no transcripts, no secrets. Nothing is uploaded: the bundle is written under .nccgs/feedback/ and
|
|
5
|
+
// the person decides where it goes.
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import { writeJson, writeText, readText, exists, toPosix, appendLine } from "../core/fs.mjs";
|
|
9
|
+
import { at, loadConfig, editorVersion, pipelineVersion } from "../core/project.mjs";
|
|
10
|
+
import { listTasks } from "../task/ledger.mjs";
|
|
11
|
+
import { readEvents, taskCost, fmtTokens } from "../task/cost.mjs";
|
|
12
|
+
import { readDiag } from "../core/diag.mjs";
|
|
13
|
+
import { inScope } from "../core/snapshot.mjs";
|
|
14
|
+
import { readJson } from "../core/fs.mjs";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { evaluate, markSent } from "../core/feedback-policy.mjs";
|
|
17
|
+
|
|
18
|
+
// the runtime is installed into the project; its version is in install-state.json, falling back to the package
|
|
19
|
+
function nccgsVersion(root) { try { return readJson(at(root, "installState")).version; } catch { try { return readJson(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json")).version; } catch { return "unknown"; } } }
|
|
20
|
+
|
|
21
|
+
const DAY = 86400000;
|
|
22
|
+
const count = (arr, key) => arr.reduce((m, x) => { const k = key(x); m[k] = (m[k] ?? 0) + 1; return m; }, {});
|
|
23
|
+
|
|
24
|
+
export async function buildBundle(root, { sinceDays = 14, note = "", withReports = false, doctor = null, trigger = "manual", reasons = [] } = {}) {
|
|
25
|
+
const sinceMs = Date.now() - sinceDays * DAY;
|
|
26
|
+
const config = loadConfig(root);
|
|
27
|
+
const events = readEvents(root).filter(e => Date.parse(e.at) >= sinceMs);
|
|
28
|
+
const diags = readDiag(root, { sinceMs });
|
|
29
|
+
const tasks = listTasks(root).filter(t => Date.parse(t.updatedAt ?? t.openedAt) >= sinceMs);
|
|
30
|
+
const taskRows = tasks.map(t => {
|
|
31
|
+
let cost = null; try { cost = taskCost(root, t); } catch {}
|
|
32
|
+
const verifications = t.verifications ?? [];
|
|
33
|
+
return {
|
|
34
|
+
id: t.id, title: t.title, rigor: t.rigor, mode: t.mode, status: t.status, outcome: t.outcome ?? null, fromPlan: !!t.fromPlan,
|
|
35
|
+
openedAt: t.openedAt, closedAt: t.closedAt ?? null, minutes: t.closedAt ? Math.round((Date.parse(t.closedAt) - Date.parse(t.openedAt)) / 60000) : null,
|
|
36
|
+
criteria: (t.criteria ?? []).length, manualCriteria: (t.criteria ?? []).filter(c => c.kind === "manual").length,
|
|
37
|
+
verifications: { total: verifications.length, failed: verifications.filter(v => !v.ok).length, byKind: count(verifications, v => v.kind) },
|
|
38
|
+
reviews: (t.reviews ?? []).map(r => ({ verdict: r.verdict, model: r.model, findings: count(r.findings ?? [], f => (f.severity ?? "INFO").toUpperCase()), resolved: (r.findings ?? []).filter(f => f.resolved).length })),
|
|
39
|
+
fixRounds: `${t.fixRounds}/${t.maxFixRounds}`,
|
|
40
|
+
changedFiles: (t.changedFiles ?? []).length, outsideContract: (t.changedFiles ?? []).filter(f => !inScope(f, t.files)).length,
|
|
41
|
+
tokens: cost?.total?.messages ? { in: cost.total.input + cost.total.cacheWrite + cost.total.cacheRead, cached: cost.total.cacheRead, out: cost.total.output, messages: cost.total.messages, byAgent: Object.fromEntries(Object.entries(cost.byAgent).map(([k, v]) => [k, v.output])) } : null
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
let doc = null;
|
|
45
|
+
if (doctor) { try { doc = await doctor(root); doc = { summary: doc.summary, checks: doc.checks.map(c => ({ id: c.id, status: c.status, message: c.message })) }; } catch (e) { doc = { error: e.message }; } }
|
|
46
|
+
const bundle = {
|
|
47
|
+
schemaVersion: 1, generatedAt: new Date().toISOString(), sinceDays, trigger, reasons,
|
|
48
|
+
nccgs: nccgsVersion(root), node: process.version, platform: `${os.platform()} ${os.release()} ${os.arch()}`,
|
|
49
|
+
project: { editor: editorVersion(root), pipeline: pipelineVersion(root), mode: config.mode, rigor: config.rigor, autonomy: config.autonomy, reportLanguage: config.report?.language ?? "en" },
|
|
50
|
+
doctor: doc,
|
|
51
|
+
totals: {
|
|
52
|
+
tasks: taskRows.length, closed: taskRows.filter(t => t.status === "closed").length, byOutcome: count(taskRows.filter(t => t.status === "closed"), t => t.outcome),
|
|
53
|
+
verifications: taskRows.reduce((a, t) => a + t.verifications.total, 0), failedVerifications: taskRows.reduce((a, t) => a + t.verifications.failed, 0),
|
|
54
|
+
fixRounds: taskRows.reduce((a, t) => a + Number(t.fixRounds.split("/")[0]), 0),
|
|
55
|
+
tokensOut: taskRows.reduce((a, t) => a + (t.tokens?.out ?? 0), 0),
|
|
56
|
+
sessions: events.filter(e => e.type === "session.start").length,
|
|
57
|
+
agentRuns: count(events.filter(e => e.type === "agent.start"), e => e.agent || "unknown"),
|
|
58
|
+
diagnostics: count(diags, d => d.kind)
|
|
59
|
+
},
|
|
60
|
+
tasks: taskRows,
|
|
61
|
+
diagnostics: diags.slice(-300),
|
|
62
|
+
updateNotices: exists(at(root, "updateCheck")) ? JSON.parse(readText(at(root, "updateCheck"), "{}")).notices ?? [] : [],
|
|
63
|
+
state: readText(at(root, "state"), "").split("\n").filter(l => l.startsWith("- ")).slice(0, 30),
|
|
64
|
+
note: note || null,
|
|
65
|
+
reports: withReports ? Object.fromEntries(tasks.filter(t => t.report && exists(path.resolve(root, t.report))).map(t => [t.id, readText(path.resolve(root, t.report), "")])) : undefined
|
|
66
|
+
};
|
|
67
|
+
const stamp = new Date().toISOString().slice(0, 19).replace(/[:T]/g, "-").slice(0, 16);
|
|
68
|
+
const dir = path.join(at(root, "nccgs"), "feedback");
|
|
69
|
+
const jsonFile = path.join(dir, `nccgs-feedback-${stamp}.json`);
|
|
70
|
+
const mdFile = path.join(dir, `nccgs-feedback-${stamp}.md`);
|
|
71
|
+
writeJson(jsonFile, bundle);
|
|
72
|
+
writeText(mdFile, renderSummary(bundle));
|
|
73
|
+
return { jsonFile, mdFile, bundle };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const MAX_BYTES = 512 * 1024;
|
|
77
|
+
|
|
78
|
+
// POST the bundle to the configured endpoint. Never throws; the outcome is appended to
|
|
79
|
+
// .nccgs/feedback/sent.jsonl so a person can see what left the machine and when.
|
|
80
|
+
export async function sendBundle(root, { bundle, markdown, endpoint = loadConfig(root).feedback?.endpoint, timeoutMs = 10000 } = {}) {
|
|
81
|
+
const log = path.join(at(root, "nccgs"), "feedback", "sent.jsonl");
|
|
82
|
+
const record = async r => { try { appendLine(log, JSON.stringify({ at: new Date().toISOString(), endpoint, ...r })); } catch {} return r; };
|
|
83
|
+
if (!endpoint) return record({ ok: false, reason: "no endpoint configured" });
|
|
84
|
+
let body = JSON.stringify({ bundle, markdown });
|
|
85
|
+
if (body.length > MAX_BYTES) { body = JSON.stringify({ bundle: { ...bundle, diagnostics: bundle.diagnostics.slice(-50), reports: undefined, truncated: true }, markdown }); }
|
|
86
|
+
if (body.length > MAX_BYTES) return record({ ok: false, reason: `bundle too large (${body.length} bytes)` });
|
|
87
|
+
const ctl = new AbortController(); const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
88
|
+
try {
|
|
89
|
+
const r = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json", "user-agent": `nccgs/${bundle.nccgs}` }, body, signal: ctl.signal });
|
|
90
|
+
const text = await r.text().catch(() => "");
|
|
91
|
+
let id = null; try { id = JSON.parse(text).id ?? null; } catch {}
|
|
92
|
+
if (!r.ok) return record({ ok: false, status: r.status, reason: text.slice(0, 200) });
|
|
93
|
+
markSent(root);
|
|
94
|
+
return record({ ok: true, status: r.status, id, bytes: body.length });
|
|
95
|
+
} catch (e) { return record({ ok: false, reason: e.name === "AbortError" ? "timeout" : e.message }); }
|
|
96
|
+
finally { clearTimeout(t); }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Called by hooks and `task close`: decide, and either send (consent auto) or return a suggestion.
|
|
100
|
+
// Returns { action, reasons, sent? } and never throws.
|
|
101
|
+
export async function autoFeedback(root, { trigger = "hook" } = {}) {
|
|
102
|
+
try {
|
|
103
|
+
const d = evaluate(root);
|
|
104
|
+
if (d.action !== "send") return d;
|
|
105
|
+
const { bundle, mdFile } = await buildBundle(root, { sinceDays: 14, trigger, reasons: d.reasons });
|
|
106
|
+
const sent = await sendBundle(root, { bundle, markdown: readText(mdFile, "") });
|
|
107
|
+
return { ...d, sent };
|
|
108
|
+
} catch (e) { return { action: "none", reasons: [], error: e.message }; }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function renderSummary(b) {
|
|
112
|
+
const L = [];
|
|
113
|
+
L.push(`# NCCGS feedback · ${b.generatedAt.slice(0, 10)} · last ${b.sinceDays} days`, "");
|
|
114
|
+
L.push(`NCCGS ${b.nccgs} · Node ${b.node} · ${b.platform} · Unity ${b.project.editor ?? "?"} · Pipeline ${b.project.pipeline ?? "?"} · mode ${b.project.mode} · rigor ${b.project.rigor} · autonomy ${b.project.autonomy}`, "");
|
|
115
|
+
const t = b.totals;
|
|
116
|
+
L.push(`**Totals.** ${t.tasks} tasks (${t.closed} closed: ${Object.entries(t.byOutcome).map(([k, v]) => `${v} ${k}`).join(", ") || "none"}) · ${t.verifications} verifications (${t.failedVerifications} failed) · ${t.fixRounds} fix rounds · ${fmtTokens(t.tokensOut)} tokens out · ${t.sessions} sessions · agents: ${Object.entries(t.agentRuns).map(([k, v]) => `${k} ×${v}`).join(", ") || "none"}`, "");
|
|
117
|
+
if (b.doctor?.summary) L.push(`**Doctor.** ${b.doctor.summary.pass} pass · ${b.doctor.summary.warn} warn · ${b.doctor.summary.fail} fail${b.doctor.checks.filter(c => c.status !== "pass").map(c => `\n- ${c.status.toUpperCase()} ${c.id}: ${c.message}`).join("")}`, "");
|
|
118
|
+
L.push(`## Tasks`, "", `| Task | Rigor | Outcome | Verify (failed) | Review | Fix | Outside scope | Minutes | Tokens out |`, `| --- | --- | --- | --- | --- | --- | --- | --- | --- |`);
|
|
119
|
+
for (const x of b.tasks) L.push(`| ${x.id} ${x.title.slice(0, 40)} | ${x.rigor} | ${x.status === "closed" ? x.outcome : x.status} | ${x.verifications.total} (${x.verifications.failed}) | ${x.reviews.map(r => `${r.verdict}${Object.keys(r.findings).length ? ` ${Object.entries(r.findings).map(([k, v]) => `${k[0]}${v}`).join("/")}` : ""}`).join(" → ") || "–"} | ${x.fixRounds} | ${x.outsideContract} | ${x.minutes ?? "–"} | ${x.tokens ? fmtTokens(x.tokens.out) : "–"} |`);
|
|
120
|
+
L.push("");
|
|
121
|
+
const kinds = Object.entries(t.diagnostics);
|
|
122
|
+
L.push(`## Diagnostics (${b.diagnostics.length})`, "");
|
|
123
|
+
if (!kinds.length) L.push("Nothing recorded: no runtime errors, failed verifications, Unity failures or hook blocks in the window.", "");
|
|
124
|
+
else {
|
|
125
|
+
L.push(kinds.map(([k, v]) => `${k} ×${v}`).join(" · "), "");
|
|
126
|
+
const groups = {};
|
|
127
|
+
for (const d of b.diagnostics) { const key = `${d.kind}: ${(d.reason ?? d.message ?? d.error?.message ?? (d.errors?.[0]?.code ?? d.errors?.[0]?.message) ?? d.name ?? "").toString().slice(0, 100)}`; groups[key] = (groups[key] ?? 0) + 1; }
|
|
128
|
+
for (const [k, v] of Object.entries(groups).sort((a, b) => b[1] - a[1]).slice(0, 30)) L.push(`- ×${v} ${k}`);
|
|
129
|
+
L.push("");
|
|
130
|
+
}
|
|
131
|
+
if (b.updateNotices?.length) L.push(`## Updates pending`, "", ...b.updateNotices.map(n => `- ${n.component} ${n.current} → ${n.latest}`), "");
|
|
132
|
+
if (b.note) L.push(`## Note from the person`, "", b.note, "");
|
|
133
|
+
L.push(`## Recent closes`, "", ...(b.state.length ? b.state : ["(none)"]), "");
|
|
134
|
+
L.push("_Local bundle. Contains task metadata, counts, diagnostics and the note above; no source code, screenshots, transcripts or credentials. The JSON beside this file has the detail._");
|
|
135
|
+
return L.join("\n");
|
|
136
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Task records: .nccgs/tasks/<id>.json (schema 3). The ledger records; it does not block tool calls.
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readJson, writeJson, exists, nowIso, shortId, appendLine } from "../core/fs.mjs";
|
|
5
|
+
import { at, loadConfig, ensureLayout, editorVersion } from "../core/project.mjs";
|
|
6
|
+
import { snapshot } from "../core/snapshot.mjs";
|
|
7
|
+
import { validateCriteria, defaultObserveCriteria, PLAYER_VISIBLE, evaluate } from "./criteria.mjs";
|
|
8
|
+
|
|
9
|
+
export const RIGORS = ["prototype", "production", "release"];
|
|
10
|
+
export const REVIEW_REQUIRED = { prototype: 0, production: 1, release: 2 };
|
|
11
|
+
export const FIX_ROUNDS = { prototype: 6, production: 4, release: 2 };
|
|
12
|
+
|
|
13
|
+
export const taskFile = (root, id) => path.join(at(root, "tasks"), `${id}.json`);
|
|
14
|
+
|
|
15
|
+
export function loadTask(root, id) {
|
|
16
|
+
const file = taskFile(root, id);
|
|
17
|
+
if (!exists(file)) throw new Error(`Task ${id} not found (${file})`);
|
|
18
|
+
return readJson(file);
|
|
19
|
+
}
|
|
20
|
+
export function saveTask(root, task) { task.updatedAt = nowIso(); writeJson(taskFile(root, task.id), task); return task; }
|
|
21
|
+
export function listTasks(root) {
|
|
22
|
+
const dir = at(root, "tasks");
|
|
23
|
+
if (!exists(dir)) return [];
|
|
24
|
+
return fs.readdirSync(dir).filter(f => f.endsWith(".json")).map(f => readJson(path.join(dir, f))).sort((a, b) => (a.openedAt < b.openedAt ? 1 : -1));
|
|
25
|
+
}
|
|
26
|
+
export function openTasks(root) { return listTasks(root).filter(t => t.status !== "closed"); }
|
|
27
|
+
|
|
28
|
+
export function event(root, type, data = {}) {
|
|
29
|
+
appendLine(at(root, "events"), JSON.stringify({ at: nowIso(), type, ...data }));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function openTask(root, { id, title, rigor, files = [], criteria = [], mode, notes = "", plan = false }) {
|
|
33
|
+
ensureLayout(root);
|
|
34
|
+
const config = loadConfig(root);
|
|
35
|
+
rigor = rigor || config.rigor;
|
|
36
|
+
if (!RIGORS.includes(rigor)) throw new Error(`rigor must be one of ${RIGORS.join("|")}`);
|
|
37
|
+
id = id || `T-${new Date().toISOString().slice(0, 10).replace(/-/g, "")}-${shortId()}`;
|
|
38
|
+
if (exists(taskFile(root, id))) throw new Error(`Task ${id} already exists`);
|
|
39
|
+
if (!title) throw new Error("--title is required");
|
|
40
|
+
const touchesPlayerVisible = files.some(f => PLAYER_VISIBLE.test(f));
|
|
41
|
+
const all = [...criteria];
|
|
42
|
+
if (rigor !== "prototype" && touchesPlayerVisible && !all.some(c => c.id === "observe")) all.push(...defaultObserveCriteria(id));
|
|
43
|
+
const problems = validateCriteria(all);
|
|
44
|
+
if (problems.length) throw new Error(`Invalid criteria:\n- ${problems.join("\n- ")}`);
|
|
45
|
+
const task = {
|
|
46
|
+
schemaVersion: 3, id, title, rigor, mode: mode || config.mode, status: "open", notes,
|
|
47
|
+
unity: { editor: editorVersion(root) },
|
|
48
|
+
files, criteria: all, fromPlan: !!plan,
|
|
49
|
+
openedAt: nowIso(), updatedAt: nowIso(), closedAt: null,
|
|
50
|
+
snapshotAtOpen: brief(snapshot(root, { config })),
|
|
51
|
+
verifications: [], reviews: [], fixRounds: 0, maxFixRounds: FIX_ROUNDS[rigor],
|
|
52
|
+
manualDecisions: {}, outcome: null, report: null
|
|
53
|
+
};
|
|
54
|
+
saveTask(root, task);
|
|
55
|
+
event(root, "task.open", { id, rigor, title });
|
|
56
|
+
return task;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The task file keeps the sha only; the per-file map lives in .nccgs/cache/snapshots/<sha>.json.
|
|
60
|
+
const brief = s => ({ sha: s.sha, files: s.files, at: s.at });
|
|
61
|
+
|
|
62
|
+
export function assess(root, task) {
|
|
63
|
+
const evaluation = evaluate(root, task.criteria, { manualDecisions: task.manualDecisions });
|
|
64
|
+
const current = brief(snapshot(root));
|
|
65
|
+
const lastVerification = task.verifications.at(-1);
|
|
66
|
+
const stale = lastVerification ? lastVerification.snapshot !== current.sha : true;
|
|
67
|
+
return { evaluation, snapshot: current, stale, lastVerification };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function recordVerification(root, task, record) {
|
|
71
|
+
task.verifications.push({ ...record, at: nowIso(), snapshot: snapshot(root).sha });
|
|
72
|
+
task.status = task.status === "open" ? "verifying" : task.status;
|
|
73
|
+
saveTask(root, task);
|
|
74
|
+
event(root, "task.verify", { id: task.id, name: record.name, ok: record.ok });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function reviewsNeeded(task) { return REVIEW_REQUIRED[task.rigor] ?? 1; }
|
|
78
|
+
|
|
79
|
+
// Mark a reviewer finding resolved. MEDIUM/LOW may be resolved by the coordinator with a reason (the
|
|
80
|
+
// report shows who and why); HIGH/BLOCKER are resolved only by a new review verdict, never by hand.
|
|
81
|
+
export function resolveFinding(root, task, { match, reason, by = "coordinator" }) {
|
|
82
|
+
const review = task.reviews.at(-1);
|
|
83
|
+
if (!review) throw new Error("no review recorded for this task");
|
|
84
|
+
if (!reason) throw new Error("--reason is required: say what changed or why the finding does not apply");
|
|
85
|
+
const needle = String(match).toLowerCase();
|
|
86
|
+
const hits = (review.findings ?? []).map((f, i) => ({ f, i })).filter(({ f, i }) => String(i) === needle || (f.title ?? f.summary ?? "").toLowerCase().includes(needle));
|
|
87
|
+
if (hits.length !== 1) throw new Error(hits.length ? `"${match}" matches ${hits.length} findings; use the index` : `no finding matches "${match}"`);
|
|
88
|
+
const { f, i } = hits[0];
|
|
89
|
+
if (/HIGH|BLOCKER/i.test(f.severity ?? "")) throw new Error(`${f.severity} findings are resolved only by re-review (task review --id ${task.id}, then --import)`);
|
|
90
|
+
if (f.resolved) throw new Error(`finding ${i} is already resolved`);
|
|
91
|
+
Object.assign(f, { resolved: true, resolvedBy: by, resolvedAt: nowIso(), resolvedReason: reason, resolvedSnapshot: snapshot(root).sha });
|
|
92
|
+
saveTask(root, task);
|
|
93
|
+
event(root, "task.finding.resolve", { id: task.id, index: i, severity: f.severity, by, reason });
|
|
94
|
+
return { index: i, finding: f };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function readyToClose(root, task, outcome) {
|
|
98
|
+
const { evaluation, snapshot: current, stale } = assess(root, task);
|
|
99
|
+
const reasons = [];
|
|
100
|
+
if (outcome === "success") {
|
|
101
|
+
if (!evaluation.allPass) reasons.push(`criteria not all passing: ${evaluation.summary.fail} fail, ${evaluation.summary.unverified} unverified, ${evaluation.summary.pending} pending`);
|
|
102
|
+
if (stale) reasons.push("source changed after the last verification; run `task verify` again");
|
|
103
|
+
const latest = task.reviews.at(-1);
|
|
104
|
+
const passes = task.reviews.filter(r => r.verdict === "PASS" && r.snapshot === current.sha);
|
|
105
|
+
if (latest && latest.verdict !== "PASS" && latest.snapshot === current.sha) reasons.push(`latest review on this snapshot is ${latest.verdict}; fix and re-review`);
|
|
106
|
+
if (passes.length < reviewsNeeded(task)) reasons.push(`independent review PASS on the current snapshot required (${passes.length}/${reviewsNeeded(task)})`);
|
|
107
|
+
}
|
|
108
|
+
return { ok: reasons.length === 0, reasons, evaluation, snapshot: current, stale };
|
|
109
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// The slice plan: .nccgs/design/slice.json, written by /map-systems, consumed by /vertical-slice and
|
|
2
|
+
// `task open --plan <id>`. Each item is a full task contract (title, files, criteria, notes, rigor) plus
|
|
3
|
+
// `dependsOn`, so a task can only be opened when its dependencies closed SUCCESS.
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { readJson, writeJson, exists } from "../core/fs.mjs";
|
|
6
|
+
import { at } from "../core/project.mjs";
|
|
7
|
+
import { listTasks } from "./ledger.mjs";
|
|
8
|
+
import { validateCriteria } from "./criteria.mjs";
|
|
9
|
+
|
|
10
|
+
export const planFile = root => path.join(at(root, "nccgs"), "design", "slice.json");
|
|
11
|
+
|
|
12
|
+
export function loadPlan(root) {
|
|
13
|
+
const f = planFile(root);
|
|
14
|
+
if (!exists(f)) throw new Error("no slice plan: run /map-systems first (it writes .nccgs/design/slice.json)");
|
|
15
|
+
const plan = readJson(f);
|
|
16
|
+
const problems = validatePlan(plan);
|
|
17
|
+
if (problems.length) throw new Error(`slice.json invalid:\n- ${problems.join("\n- ")}`);
|
|
18
|
+
return plan;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function validatePlan(plan) {
|
|
22
|
+
const problems = [];
|
|
23
|
+
if (!plan || typeof plan !== "object") return ["not an object"];
|
|
24
|
+
if (plan.schemaVersion !== 1) problems.push("schemaVersion must be 1");
|
|
25
|
+
if (!Array.isArray(plan.tasks) || !plan.tasks.length) return [...problems, "tasks must be a non-empty array"];
|
|
26
|
+
const ids = new Set();
|
|
27
|
+
for (const [i, t] of plan.tasks.entries()) {
|
|
28
|
+
const w = `tasks[${i}]`;
|
|
29
|
+
if (!t.id || typeof t.id !== "string") problems.push(`${w}.id required`);
|
|
30
|
+
else if (ids.has(t.id)) problems.push(`${w}.id ${t.id} duplicated`); else ids.add(t.id);
|
|
31
|
+
if (!t.title) problems.push(`${w}.title required`);
|
|
32
|
+
if (!Array.isArray(t.files) || !t.files.length) problems.push(`${w}.files must list the files in scope`);
|
|
33
|
+
if (!Array.isArray(t.criteria) || !t.criteria.length) problems.push(`${w}.criteria must be a non-empty array`);
|
|
34
|
+
else problems.push(...validateCriteria(t.criteria).map(p => `${w}.criteria: ${p}`));
|
|
35
|
+
for (const d of t.dependsOn ?? []) if (!plan.tasks.some(x => x.id === d)) problems.push(`${w}.dependsOn ${d} is not in the plan`);
|
|
36
|
+
}
|
|
37
|
+
return problems;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Status of every planned task against the ledger: done | open | ready | blocked.
|
|
41
|
+
export function planStatus(root, plan = loadPlan(root)) {
|
|
42
|
+
const tasks = listTasks(root);
|
|
43
|
+
const byId = new Map(tasks.map(t => [t.id, t]));
|
|
44
|
+
return plan.tasks.map(p => {
|
|
45
|
+
const t = byId.get(p.id);
|
|
46
|
+
const state = t ? (t.status === "closed" ? (t.outcome === "success" ? "done" : `closed-${t.outcome}`) : "open") : null;
|
|
47
|
+
const waiting = (p.dependsOn ?? []).filter(d => byId.get(d)?.outcome !== "success");
|
|
48
|
+
return { id: p.id, title: p.title, state: state ?? (waiting.length ? "blocked" : "ready"), waiting, rigor: p.rigor ?? null };
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function nextPlanned(root, plan = loadPlan(root)) {
|
|
53
|
+
return planStatus(root, plan).find(s => s.state === "ready" || s.state === "open") ?? null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function planItem(root, id, plan = loadPlan(root)) {
|
|
57
|
+
const item = plan.tasks.find(t => t.id === id);
|
|
58
|
+
if (!item) throw new Error(`plan has no task ${id} (${plan.tasks.map(t => t.id).join(", ")})`);
|
|
59
|
+
const status = planStatus(root, plan).find(s => s.id === id);
|
|
60
|
+
if (status.state === "blocked") throw new Error(`${id} waits for ${status.waiting.join(", ")} to close SUCCESS`);
|
|
61
|
+
return item;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function savePlan(root, plan) {
|
|
65
|
+
const problems = validatePlan(plan);
|
|
66
|
+
if (problems.length) throw new Error(`slice.json invalid:\n- ${problems.join("\n- ")}`);
|
|
67
|
+
writeJson(planFile(root), plan);
|
|
68
|
+
return planFile(root);
|
|
69
|
+
}
|