productfoo 0.0.0-stage → 1.0.1
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/LICENSE +201 -0
- package/README.md +338 -2
- package/bin/foo +3 -0
- package/bin/foo.mjs +42 -0
- package/commands/bug.md +31 -0
- package/commands/discovery.md +28 -0
- package/commands/ideate.md +27 -0
- package/commands/init.md +28 -0
- package/commands/pitch.md +27 -0
- package/commands/preview.md +25 -0
- package/commands/research.md +31 -0
- package/commands/roast.md +27 -0
- package/commands/spec.md +30 -0
- package/config/pricing.yaml +198 -0
- package/config/routing-policy.yaml +171 -0
- package/dist-public/src/activeModel.d.ts +83 -0
- package/dist-public/src/activeModel.js +331 -0
- package/dist-public/src/agentAdapters.d.ts +122 -0
- package/dist-public/src/agentAdapters.js +178 -0
- package/dist-public/src/agentState.d.ts +43 -0
- package/dist-public/src/agentState.js +177 -0
- package/dist-public/src/bugCapsule.d.ts +94 -0
- package/dist-public/src/bugCapsule.js +227 -0
- package/dist-public/src/bugCapture.d.ts +26 -0
- package/dist-public/src/bugCapture.js +251 -0
- package/dist-public/src/bugOwnerLog.d.ts +93 -0
- package/dist-public/src/bugOwnerLog.js +304 -0
- package/dist-public/src/bugReportStore.d.ts +48 -0
- package/dist-public/src/bugReportStore.js +860 -0
- package/dist-public/src/bugSpecContext.d.ts +7 -0
- package/dist-public/src/bugSpecContext.js +129 -0
- package/dist-public/src/channelFreshness.d.ts +25 -0
- package/dist-public/src/channelFreshness.js +194 -0
- package/dist-public/src/cli.d.ts +30 -0
- package/dist-public/src/cli.js +1667 -0
- package/dist-public/src/cliArgs.d.ts +7 -0
- package/dist-public/src/cliArgs.js +44 -0
- package/dist-public/src/codeCatalog.d.ts +41 -0
- package/dist-public/src/codeCatalog.js +197 -0
- package/dist-public/src/commandContract.d.ts +84 -0
- package/dist-public/src/commandContract.js +390 -0
- package/dist-public/src/commandUsage.d.ts +3 -0
- package/dist-public/src/commandUsage.js +30 -0
- package/dist-public/src/commands/agent.d.ts +60 -0
- package/dist-public/src/commands/agent.js +1353 -0
- package/dist-public/src/commands/asp.d.ts +8 -0
- package/dist-public/src/commands/asp.js +669 -0
- package/dist-public/src/commands/evalCmd.d.ts +4 -0
- package/dist-public/src/commands/evalCmd.js +405 -0
- package/dist-public/src/commands/login.d.ts +25 -0
- package/dist-public/src/commands/login.js +94 -0
- package/dist-public/src/commands/loop.d.ts +16 -0
- package/dist-public/src/commands/loop.js +619 -0
- package/dist-public/src/commands/reconcile.d.ts +41 -0
- package/dist-public/src/commands/reconcile.js +292 -0
- package/dist-public/src/commands/runtime.d.ts +1 -0
- package/dist-public/src/commands/runtime.js +36 -0
- package/dist-public/src/commands/scanRedact.d.ts +8 -0
- package/dist-public/src/commands/scanRedact.js +266 -0
- package/dist-public/src/commands/setup.d.ts +28 -0
- package/dist-public/src/commands/setup.js +398 -0
- package/dist-public/src/commands/shared.d.ts +17 -0
- package/dist-public/src/commands/shared.js +147 -0
- package/dist-public/src/commands/source.d.ts +1 -0
- package/dist-public/src/commands/source.js +134 -0
- package/dist-public/src/commands/specCmd.d.ts +24 -0
- package/dist-public/src/commands/specCmd.js +794 -0
- package/dist-public/src/commands/update.d.ts +61 -0
- package/dist-public/src/commands/update.js +244 -0
- package/dist-public/src/comprehensionSignal.d.ts +20 -0
- package/dist-public/src/comprehensionSignal.js +166 -0
- package/dist-public/src/conductor/diversity.d.ts +48 -0
- package/dist-public/src/conductor/diversity.js +423 -0
- package/dist-public/src/conductor/engine.d.ts +7 -0
- package/dist-public/src/conductor/engine.js +295 -0
- package/dist-public/src/conductor/families/ideate.d.ts +2 -0
- package/dist-public/src/conductor/families/ideate.js +255 -0
- package/dist-public/src/conductor/families/interview.d.ts +2 -0
- package/dist-public/src/conductor/families/interview.js +39 -0
- package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
- package/dist-public/src/conductor/families/specSwarm.js +146 -0
- package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
- package/dist-public/src/conductor/familyDelivery.js +37 -0
- package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
- package/dist-public/src/conductor/familyPackArtifact.js +260 -0
- package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
- package/dist-public/src/conductor/ideateDecision.js +237 -0
- package/dist-public/src/conductor/pack.d.ts +26 -0
- package/dist-public/src/conductor/pack.js +75 -0
- package/dist-public/src/conductor/types.d.ts +111 -0
- package/dist-public/src/conductor/types.js +7 -0
- package/dist-public/src/configPath.d.ts +2 -0
- package/dist-public/src/configPath.js +7 -0
- package/dist-public/src/discoveryArtifacts.d.ts +54 -0
- package/dist-public/src/discoveryArtifacts.js +467 -0
- package/dist-public/src/discoveryCommand.d.ts +127 -0
- package/dist-public/src/discoveryCommand.js +2148 -0
- package/dist-public/src/discoveryConnections.d.ts +339 -0
- package/dist-public/src/discoveryConnections.js +613 -0
- package/dist-public/src/discoveryDocs.d.ts +129 -0
- package/dist-public/src/discoveryDocs.js +577 -0
- package/dist-public/src/discoveryEvidence.d.ts +59 -0
- package/dist-public/src/discoveryEvidence.js +198 -0
- package/dist-public/src/discoveryFireflies.d.ts +25 -0
- package/dist-public/src/discoveryFireflies.js +551 -0
- package/dist-public/src/discoveryGmail.d.ts +12 -0
- package/dist-public/src/discoveryGmail.js +466 -0
- package/dist-public/src/discoveryGong.d.ts +12 -0
- package/dist-public/src/discoveryGong.js +491 -0
- package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
- package/dist-public/src/discoveryLearningRouting.js +405 -0
- package/dist-public/src/discoveryMatching.d.ts +26 -0
- package/dist-public/src/discoveryMatching.js +73 -0
- package/dist-public/src/discoveryOutlook.d.ts +12 -0
- package/dist-public/src/discoveryOutlook.js +492 -0
- package/dist-public/src/discoveryProcess.d.ts +221 -0
- package/dist-public/src/discoveryProcess.js +703 -0
- package/dist-public/src/discoveryProof.d.ts +143 -0
- package/dist-public/src/discoveryProof.js +529 -0
- package/dist-public/src/discoveryProviders.d.ts +152 -0
- package/dist-public/src/discoveryProviders.js +756 -0
- package/dist-public/src/discoveryReadiness.d.ts +219 -0
- package/dist-public/src/discoveryReadiness.js +672 -0
- package/dist-public/src/discoveryReconcile.d.ts +57 -0
- package/dist-public/src/discoveryReconcile.js +424 -0
- package/dist-public/src/discoverySlack.d.ts +12 -0
- package/dist-public/src/discoverySlack.js +452 -0
- package/dist-public/src/discoverySources.d.ts +56 -0
- package/dist-public/src/discoverySources.js +322 -0
- package/dist-public/src/discoverySync.d.ts +52 -0
- package/dist-public/src/discoverySync.js +151 -0
- package/dist-public/src/discoverySynthesis.d.ts +57 -0
- package/dist-public/src/discoverySynthesis.js +215 -0
- package/dist-public/src/discoveryTeams.d.ts +12 -0
- package/dist-public/src/discoveryTeams.js +516 -0
- package/dist-public/src/discoveryZoom.d.ts +12 -0
- package/dist-public/src/discoveryZoom.js +523 -0
- package/dist-public/src/elicitationSurface.d.ts +13 -0
- package/dist-public/src/elicitationSurface.js +77 -0
- package/dist-public/src/errorReport.d.ts +99 -0
- package/dist-public/src/errorReport.js +443 -0
- package/dist-public/src/errorTaxonomy.d.ts +84 -0
- package/dist-public/src/errorTaxonomy.js +189 -0
- package/dist-public/src/evalCalibration.d.ts +82 -0
- package/dist-public/src/evalCalibration.js +169 -0
- package/dist-public/src/evalChecks.d.ts +21 -0
- package/dist-public/src/evalChecks.js +295 -0
- package/dist-public/src/evalCompile.d.ts +56 -0
- package/dist-public/src/evalCompile.js +58 -0
- package/dist-public/src/evalImprove.d.ts +60 -0
- package/dist-public/src/evalImprove.js +175 -0
- package/dist-public/src/evalJudge.d.ts +47 -0
- package/dist-public/src/evalJudge.js +143 -0
- package/dist-public/src/evalLabels.d.ts +100 -0
- package/dist-public/src/evalLabels.js +287 -0
- package/dist-public/src/evalRecord.d.ts +68 -0
- package/dist-public/src/evalRecord.js +165 -0
- package/dist-public/src/evalRoast.d.ts +88 -0
- package/dist-public/src/evalRoast.js +249 -0
- package/dist-public/src/evidenceIntake.d.ts +66 -0
- package/dist-public/src/evidenceIntake.js +356 -0
- package/dist-public/src/feedback.d.ts +78 -0
- package/dist-public/src/feedback.js +294 -0
- package/dist-public/src/firefliesClient.d.ts +106 -0
- package/dist-public/src/firefliesClient.js +258 -0
- package/dist-public/src/firstMoveTensions.d.ts +10 -0
- package/dist-public/src/firstMoveTensions.js +93 -0
- package/dist-public/src/gitRun.d.ts +21 -0
- package/dist-public/src/gitRun.js +50 -0
- package/dist-public/src/githubReconcile.d.ts +94 -0
- package/dist-public/src/githubReconcile.js +205 -0
- package/dist-public/src/gmailNango.d.ts +131 -0
- package/dist-public/src/gmailNango.js +329 -0
- package/dist-public/src/gongNango.d.ts +118 -0
- package/dist-public/src/gongNango.js +308 -0
- package/dist-public/src/grammarGate.d.ts +13 -0
- package/dist-public/src/grammarGate.js +72 -0
- package/dist-public/src/harnessGates.d.ts +65 -0
- package/dist-public/src/harnessGates.js +168 -0
- package/dist-public/src/humanSurfaceEval.d.ts +19 -0
- package/dist-public/src/humanSurfaceEval.js +156 -0
- package/dist-public/src/ideate.d.ts +43 -0
- package/dist-public/src/ideate.js +255 -0
- package/dist-public/src/ideateContext.d.ts +11 -0
- package/dist-public/src/ideateContext.js +83 -0
- package/dist-public/src/index.d.ts +2 -0
- package/dist-public/src/index.js +4 -0
- package/dist-public/src/initCommand.d.ts +25 -0
- package/dist-public/src/initCommand.js +558 -0
- package/dist-public/src/interviewGate.d.ts +25 -0
- package/dist-public/src/interviewGate.js +126 -0
- package/dist-public/src/jsonFile.d.ts +2 -0
- package/dist-public/src/jsonFile.js +22 -0
- package/dist-public/src/loopBudget.d.ts +182 -0
- package/dist-public/src/loopBudget.js +494 -0
- package/dist-public/src/loopContracts.d.ts +35 -0
- package/dist-public/src/loopContracts.js +364 -0
- package/dist-public/src/machineState.d.ts +1 -0
- package/dist-public/src/machineState.js +9 -0
- package/dist-public/src/notionNango.d.ts +93 -0
- package/dist-public/src/notionNango.js +279 -0
- package/dist-public/src/orchestrationResult.d.ts +11 -0
- package/dist-public/src/orchestrationResult.js +73 -0
- package/dist-public/src/outlookNango.d.ts +124 -0
- package/dist-public/src/outlookNango.js +327 -0
- package/dist-public/src/packetCorrections.d.ts +40 -0
- package/dist-public/src/packetCorrections.js +172 -0
- package/dist-public/src/packetGlossary.d.ts +6 -0
- package/dist-public/src/packetGlossary.js +42 -0
- package/dist-public/src/packetIndex.d.ts +54 -0
- package/dist-public/src/packetIndex.js +744 -0
- package/dist-public/src/packetIndexEntry.d.ts +3 -0
- package/dist-public/src/packetIndexEntry.js +128 -0
- package/dist-public/src/packetIndexServe.d.ts +13 -0
- package/dist-public/src/packetIndexServe.js +113 -0
- package/dist-public/src/packetIndexWorker.d.ts +7 -0
- package/dist-public/src/packetIndexWorker.js +45 -0
- package/dist-public/src/packetLifecycle.d.ts +30 -0
- package/dist-public/src/packetLifecycle.js +127 -0
- package/dist-public/src/packetReconcile.d.ts +73 -0
- package/dist-public/src/packetReconcile.js +386 -0
- package/dist-public/src/packetStatus.d.ts +125 -0
- package/dist-public/src/packetStatus.js +770 -0
- package/dist-public/src/packetValidation.d.ts +17 -0
- package/dist-public/src/packetValidation.js +333 -0
- package/dist-public/src/packetVerify.d.ts +73 -0
- package/dist-public/src/packetVerify.js +303 -0
- package/dist-public/src/packetWrite.d.ts +52 -0
- package/dist-public/src/packetWrite.js +448 -0
- package/dist-public/src/pitch.d.ts +38 -0
- package/dist-public/src/pitch.js +154 -0
- package/dist-public/src/processMemo.d.ts +8 -0
- package/dist-public/src/processMemo.js +23 -0
- package/dist-public/src/productDetect.d.ts +30 -0
- package/dist-public/src/productDetect.js +376 -0
- package/dist-public/src/profile.d.ts +62 -0
- package/dist-public/src/profile.js +334 -0
- package/dist-public/src/questionModel.d.ts +46 -0
- package/dist-public/src/questionModel.js +74 -0
- package/dist-public/src/questionText.d.ts +7 -0
- package/dist-public/src/questionText.js +38 -0
- package/dist-public/src/redactConfig.d.ts +2 -0
- package/dist-public/src/redactConfig.js +11 -0
- package/dist-public/src/redactEngine.d.ts +59 -0
- package/dist-public/src/redactEngine.js +509 -0
- package/dist-public/src/redactGate.d.ts +63 -0
- package/dist-public/src/redactGate.js +209 -0
- package/dist-public/src/repoRead.d.ts +29 -0
- package/dist-public/src/repoRead.js +160 -0
- package/dist-public/src/research.d.ts +242 -0
- package/dist-public/src/research.js +1314 -0
- package/dist-public/src/researchCompletion.d.ts +34 -0
- package/dist-public/src/researchCompletion.js +123 -0
- package/dist-public/src/researchExecution.d.ts +47 -0
- package/dist-public/src/researchExecution.js +309 -0
- package/dist-public/src/researchFormat.d.ts +21 -0
- package/dist-public/src/researchFormat.js +77 -0
- package/dist-public/src/researchGate.d.ts +86 -0
- package/dist-public/src/researchGate.js +1036 -0
- package/dist-public/src/researchReceipt.d.ts +76 -0
- package/dist-public/src/researchReceipt.js +148 -0
- package/dist-public/src/researchRender.d.ts +2 -0
- package/dist-public/src/researchRender.js +267 -0
- package/dist-public/src/researchSigningKey.d.ts +19 -0
- package/dist-public/src/researchSigningKey.js +95 -0
- package/dist-public/src/riskClassify.d.ts +42 -0
- package/dist-public/src/riskClassify.js +149 -0
- package/dist-public/src/routines.d.ts +42 -0
- package/dist-public/src/routines.js +269 -0
- package/dist-public/src/routingPolicy.d.ts +172 -0
- package/dist-public/src/routingPolicy.js +646 -0
- package/dist-public/src/secretInput.d.ts +8 -0
- package/dist-public/src/secretInput.js +89 -0
- package/dist-public/src/selfCheck.d.ts +7 -0
- package/dist-public/src/selfCheck.js +473 -0
- package/dist-public/src/selfCheckCache.d.ts +29 -0
- package/dist-public/src/selfCheckCache.js +147 -0
- package/dist-public/src/severityClass.d.ts +15 -0
- package/dist-public/src/severityClass.js +22 -0
- package/dist-public/src/skillClientPolicy.d.ts +8 -0
- package/dist-public/src/skillClientPolicy.js +16 -0
- package/dist-public/src/skillLocal.d.ts +98 -0
- package/dist-public/src/skillLocal.js +463 -0
- package/dist-public/src/skillTreeDigest.d.ts +9 -0
- package/dist-public/src/skillTreeDigest.js +64 -0
- package/dist-public/src/slackNango.d.ts +62 -0
- package/dist-public/src/slackNango.js +208 -0
- package/dist-public/src/social/arxiv.d.ts +19 -0
- package/dist-public/src/social/arxiv.js +96 -0
- package/dist-public/src/social/github.d.ts +2 -0
- package/dist-public/src/social/github.js +78 -0
- package/dist-public/src/social/hackernews.d.ts +2 -0
- package/dist-public/src/social/hackernews.js +67 -0
- package/dist-public/src/social/index.d.ts +10 -0
- package/dist-public/src/social/index.js +79 -0
- package/dist-public/src/social/reddit.d.ts +2 -0
- package/dist-public/src/social/reddit.js +125 -0
- package/dist-public/src/social/types.d.ts +73 -0
- package/dist-public/src/social/types.js +61 -0
- package/dist-public/src/social/xml.d.ts +10 -0
- package/dist-public/src/social/xml.js +49 -0
- package/dist-public/src/social/youtube.d.ts +7 -0
- package/dist-public/src/social/youtube.js +217 -0
- package/dist-public/src/sourceIngress.d.ts +488 -0
- package/dist-public/src/sourceIngress.js +1651 -0
- package/dist-public/src/specAfter.d.ts +187 -0
- package/dist-public/src/specAfter.js +814 -0
- package/dist-public/src/specBudget.d.ts +124 -0
- package/dist-public/src/specBudget.js +424 -0
- package/dist-public/src/specDecisionSignals.d.ts +38 -0
- package/dist-public/src/specDecisionSignals.js +121 -0
- package/dist-public/src/specLinkedCommits.d.ts +7 -0
- package/dist-public/src/specLinkedCommits.js +107 -0
- package/dist-public/src/specSections.d.ts +105 -0
- package/dist-public/src/specSections.js +718 -0
- package/dist-public/src/statusGlyphs.d.ts +18 -0
- package/dist-public/src/statusGlyphs.js +75 -0
- package/dist-public/src/swarmCommand.d.ts +10 -0
- package/dist-public/src/swarmCommand.js +96 -0
- package/dist-public/src/teamsNango.d.ts +114 -0
- package/dist-public/src/teamsNango.js +301 -0
- package/dist-public/src/tokenStore.d.ts +10 -0
- package/dist-public/src/tokenStore.js +77 -0
- package/dist-public/src/trustEnvelope.d.ts +78 -0
- package/dist-public/src/trustEnvelope.js +392 -0
- package/dist-public/src/untrustedText.d.ts +6 -0
- package/dist-public/src/untrustedText.js +20 -0
- package/dist-public/src/updateNotice.d.ts +41 -0
- package/dist-public/src/updateNotice.js +290 -0
- package/dist-public/src/vendorYaml.d.ts +1 -0
- package/dist-public/src/vendorYaml.js +7374 -0
- package/dist-public/src/workflowRunActuals.d.ts +67 -0
- package/dist-public/src/workflowRunActuals.js +184 -0
- package/dist-public/src/zendeskDiscovery.d.ts +12 -0
- package/dist-public/src/zendeskDiscovery.js +513 -0
- package/dist-public/src/zendeskNango.d.ts +131 -0
- package/dist-public/src/zendeskNango.js +344 -0
- package/dist-public/src/zoomNango.d.ts +106 -0
- package/dist-public/src/zoomNango.js +351 -0
- package/docs/CHANGELOG.md +32 -0
- package/docs/THIRD_PARTY_NOTICES.md +36 -0
- package/package.json +71 -4
- package/schemas/bug-capsule-receipt.schema.json +54 -0
- package/schemas/bug-capsule.schema.json +189 -0
- package/schemas/discovery-evidence-envelope.schema.json +79 -0
- package/schemas/discovery-processing-receipt.schema.json +71 -0
- package/schemas/discovery-service-contract.schema.json +189 -0
- package/skills/AGENTS.md +32 -0
- package/skills/README.md +287 -0
- package/skills/bug/SKILL.md +49 -0
- package/skills/build-preview-spec/SKILL.md +103 -0
- package/skills/capture-external-artifact/SKILL.md +23 -0
- package/skills/capture-external-artifact/agents/openai.yaml +4 -0
- package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
- package/skills/check-spec-health/SKILL.md +45 -0
- package/skills/check-spec-health/agents/openai.yaml +4 -0
- package/skills/competitive-positioning/SKILL.md +86 -0
- package/skills/competitive-positioning/agents/openai.yaml +4 -0
- package/skills/cpo-lens/SKILL.md +398 -0
- package/skills/cpo-lens/agents/openai.yaml +4 -0
- package/skills/create-prd/SKILL.md +26 -0
- package/skills/create-prd/agents/openai.yaml +4 -0
- package/skills/customer-discovery/SKILL.md +192 -0
- package/skills/design-system/SKILL.md +49 -0
- package/skills/design-system/agents/openai.yaml +4 -0
- package/skills/discussion-guide/SKILL.md +75 -0
- package/skills/discussion-guide/agents/openai.yaml +4 -0
- package/skills/firecrawl-research-papers/SKILL.md +96 -0
- package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
- package/skills/foo-roast/SKILL.md +325 -0
- package/skills/generate-gtm-brief/SKILL.md +41 -0
- package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
- package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
- package/skills/gong-quotes/SKILL.md +43 -0
- package/skills/gong-quotes/agents/openai.yaml +4 -0
- package/skills/grill-me/SKILL.md +27 -0
- package/skills/grill-me/agents/openai.yaml +4 -0
- package/skills/ideate/SKILL.md +180 -0
- package/skills/ideate/references/family-packs.md +5 -0
- package/skills/initiative-tracker/SKILL.md +23 -0
- package/skills/initiative-tracker/agents/openai.yaml +4 -0
- package/skills/interop-review/SKILL.md +25 -0
- package/skills/interop-review/agents/openai.yaml +4 -0
- package/skills/interview-me/SKILL.md +404 -0
- package/skills/interview-me/agents/openai.yaml +4 -0
- package/skills/loop-library-audit/SKILL.md +123 -0
- package/skills/pitch/SKILL.md +110 -0
- package/skills/pitch/references/opportunity-assessment.md +136 -0
- package/skills/pitch/references/prfaq-template.md +54 -0
- package/skills/pmm-release-briefing/SKILL.md +27 -0
- package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
- package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
- package/skills/positioning-messaging/SKILL.md +70 -0
- package/skills/positioning-messaging/agents/openai.yaml +4 -0
- package/skills/positioning-messaging/references/guest-insights.md +1400 -0
- package/skills/preview-swarm/SKILL.md +418 -0
- package/skills/preview-swarm/agents/openai.yaml +4 -0
- package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
- package/skills/product-copywriting/SKILL.md +127 -0
- package/skills/product-copywriting/agents/openai.yaml +4 -0
- package/skills/product-copywriting/references/foo-voice.md +26 -0
- package/skills/product-shape-interview/SKILL.md +219 -0
- package/skills/product-shape-interview/agents/openai.yaml +4 -0
- package/skills/ready-for-release/SKILL.md +39 -0
- package/skills/ready-for-release/agents/openai.yaml +4 -0
- package/skills/redact-transcripts/SKILL.md +23 -0
- package/skills/redact-transcripts/agents/openai.yaml +4 -0
- package/skills/research-brief/SKILL.md +48 -0
- package/skills/research-brief/agents/openai.yaml +4 -0
- package/skills/research-refresh/SKILL.md +63 -0
- package/skills/research-refresh/references/worked-cases.md +44 -0
- package/skills/review-loop/SKILL.md +200 -0
- package/skills/review-spec/SKILL.md +32 -0
- package/skills/review-spec/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/SKILL.md +42 -0
- package/skills/rfc-from-research/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
- package/skills/skill-registry.yaml +810 -0
- package/skills/spec-cleanup/SKILL.md +103 -0
- package/skills/spec-cleanup/agents/openai.yaml +4 -0
- package/skills/spec-swarm/SKILL.md +87 -0
- package/skills/spec-swarm/agents/openai.yaml +4 -0
- package/skills/spec-swarm/template/SKILL.template.md +94 -0
- package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
- package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
- package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
- package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
- package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
- package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
- package/skills/spec-swarm/template/personas/content-design.md +40 -0
- package/skills/spec-swarm/template/personas/cpo.md +42 -0
- package/skills/spec-swarm/template/personas/data-science.md +42 -0
- package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
- package/skills/spec-swarm/template/personas/dx-product.md +70 -0
- package/skills/spec-swarm/template/personas/field.md +43 -0
- package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
- package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
- package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
- package/skills/spec-swarm/template/personas/pmm.md +43 -0
- package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
- package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
- package/skills/spec-swarm/template/personas/qa-release.md +41 -0
- package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
- package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
- package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
- package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
- package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
- package/skills/spec-swarm/template/personas/user-research.md +41 -0
- package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
- package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
- package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
- package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
- package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
- package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
- package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
- package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
- package/skills/spec-swarm-content-design/SKILL.md +95 -0
- package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
- package/skills/spec-swarm-cpo/SKILL.md +94 -0
- package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-data-science/SKILL.md +95 -0
- package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
- package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
- package/skills/spec-swarm-dx-product/SKILL.md +126 -0
- package/skills/spec-swarm-field/SKILL.md +96 -0
- package/skills/spec-swarm-field/agents/openai.yaml +4 -0
- package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
- package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
- package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
- package/skills/spec-swarm-pmm/SKILL.md +96 -0
- package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
- package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
- package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
- package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
- package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
- package/skills/spec-swarm-qa-release/SKILL.md +96 -0
- package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
- package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
- package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
- package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
- package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
- package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
- package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
- package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
- package/skills/spec-swarm-user-research/SKILL.md +95 -0
- package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
- package/skills/strategy-check/SKILL.md +170 -0
- package/skills/strategy-check/agents/openai.yaml +4 -0
- package/skills/study-digest/SKILL.md +41 -0
- package/skills/synthetic-customer/SKILL.md +68 -0
- package/skills/update-cli-spec/SKILL.md +69 -0
- package/skills/update-cli-spec/agents/openai.yaml +4 -0
- package/skills/voc-researcher/SKILL.md +45 -0
- package/skills/voc-researcher/agents/openai.yaml +4 -0
- package/skills-lock.json +35 -0
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
// `foo asp <command>`: the packet-contract dispatch body — validate, reconcile
|
|
2
|
+
// (including --source github:), verify, status, index, init, event, task, and
|
|
3
|
+
// verdict — plus the per-packet compiled-module loading it prefers in
|
|
4
|
+
// transition. cli.ts parses the domain/command tokens and delegates here.
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { parse as parseYaml } from "../vendorYaml.js";
|
|
9
|
+
import { derivePacketStatus, renderAspStatus } from "../packetStatus.js";
|
|
10
|
+
import { costHeadline } from "./loop.js";
|
|
11
|
+
import { runAspIndex } from "../packetIndex.js";
|
|
12
|
+
import { serveIndex, DEFAULT_SERVE_PORT } from "../packetIndexServe.js";
|
|
13
|
+
import { isPacketValidationError, validatePacket as validatePacketGeneric } from "../packetValidation.js";
|
|
14
|
+
import { lintAnchors, migrateSpecText } from "../specSections.js";
|
|
15
|
+
import { parseReconcileArgs, reconcilePacket as reconcilePacketGeneric } from "../packetReconcile.js";
|
|
16
|
+
import { verifyPacket, renderVerifyReport, VERIFY_EVENT_TYPE } from "../packetVerify.js";
|
|
17
|
+
import { appendEvent, initPacket, updateTask } from "../packetWrite.js";
|
|
18
|
+
import { isTerminal, recordVerdict, VERDICT_STATES } from "../packetLifecycle.js";
|
|
19
|
+
import { glyphsEnabled } from "../statusGlyphs.js";
|
|
20
|
+
import { loadLoopContracts } from "../loopContracts.js";
|
|
21
|
+
import { estimateLoopBudget, handshakeFloorUsd, hasExternalSources, loadPricing } from "../loopBudget.js";
|
|
22
|
+
import { appendIntegrationRecord, approvalStates, githubSelectionReport, githubSourceId, parseGithubSource, sourceKey, } from "../sourceIngress.js";
|
|
23
|
+
import { reconcilePrProof, recordGithubConflicts, renderGithubReceipt, } from "../githubReconcile.js";
|
|
24
|
+
import { classifyThrown, markFailure, UsageError } from "../errorTaxonomy.js";
|
|
25
|
+
import { failThrown, failWith, flagValue, hasFlag, resolvePacketDir } from "./shared.js";
|
|
26
|
+
import { surplusArgs } from "../cliArgs.js";
|
|
27
|
+
import { readJson } from "../jsonFile.js";
|
|
28
|
+
import { ASP_USAGE_LINES } from "../commandUsage.js";
|
|
29
|
+
export function aspUsage() {
|
|
30
|
+
return [
|
|
31
|
+
"foo asp — the packet contract: scaffold it, check it, and record what happened.",
|
|
32
|
+
"",
|
|
33
|
+
...ASP_USAGE_LINES,
|
|
34
|
+
"",
|
|
35
|
+
"The local event log appends through `foo asp event` without rewriting prior lines.",
|
|
36
|
+
"It is not an independently append-only or tamper-proof guarantee; the command refuses unknown input rather than guess.",
|
|
37
|
+
"",
|
|
38
|
+
].join("\n");
|
|
39
|
+
}
|
|
40
|
+
// The flags `foo asp event` actually has. Anything else in its argv is surplus.
|
|
41
|
+
const EVENT_VALUE_FLAGS = ["--subject", "--data", "--summary"];
|
|
42
|
+
async function loadPacketCommand(root, packetArg, command) {
|
|
43
|
+
const modulePath = path.join(root, "dist", packetArg, `${command}.js`);
|
|
44
|
+
if (!existsSync(modulePath))
|
|
45
|
+
return null;
|
|
46
|
+
return import(pathToFileURL(modulePath).href);
|
|
47
|
+
}
|
|
48
|
+
function readPacketSpecId(packetDir) {
|
|
49
|
+
try {
|
|
50
|
+
const m = readFileSync(path.join(packetDir, "tasks.yaml"), "utf8").match(/^spec_id:\s*(\S+)\s*$/m);
|
|
51
|
+
if (m)
|
|
52
|
+
return m[1];
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// fall through to a slug-derived guess
|
|
56
|
+
}
|
|
57
|
+
return path.basename(packetDir).replace(/-/g, "_");
|
|
58
|
+
}
|
|
59
|
+
// Read the packet's done_when criteria (with any check predicates) from tasks.yaml
|
|
60
|
+
// (github-integration-v0 T-006). The agreed criteria are the verdict's left side;
|
|
61
|
+
// GitHub evidence is the right side.
|
|
62
|
+
function readDoneWhenCriteria(packetDir) {
|
|
63
|
+
try {
|
|
64
|
+
const doc = parseYaml(readFileSync(path.join(packetDir, "tasks.yaml"), "utf8"));
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const task of doc.tasks ?? []) {
|
|
67
|
+
for (const c of task.done_when ?? []) {
|
|
68
|
+
if (c.id)
|
|
69
|
+
out.push({ id: c.id, text: c.text, check: c.check });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// foo asp reconcile <packet> --source github:<url> (github-integration-v0 T-002):
|
|
79
|
+
// admit a per-PR/per-repo GitHub selection through the substrate gate and report
|
|
80
|
+
// its identity, approval, and packet-linkage. No new command (rides reconcile);
|
|
81
|
+
// the done_when verdict is T-003. The standalone CLI has no live MCP, so evidence
|
|
82
|
+
// arrives via --source-fixture (errors-as-DX hands off when it is missing).
|
|
83
|
+
function runGithubSourceReconcile(root, packetDir, args) {
|
|
84
|
+
const json = args.includes("--json");
|
|
85
|
+
const source = flagValue(args, "--source") ?? "";
|
|
86
|
+
if (!source.startsWith("github:")) {
|
|
87
|
+
return { code: 1, out: `--source '${source}' is not supported on asp reconcile; V0 admits only github:<pr-or-repo-url>\n` };
|
|
88
|
+
}
|
|
89
|
+
let selection;
|
|
90
|
+
try {
|
|
91
|
+
selection = parseGithubSource(source);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return { code: 1, out: `${error instanceof Error ? error.message : String(error)}\n` };
|
|
95
|
+
}
|
|
96
|
+
const id = githubSourceId(selection);
|
|
97
|
+
const rel = path.relative(root, packetDir) || packetDir;
|
|
98
|
+
const resume = `foo asp reconcile ${rel} --source ${source} --source-fixture <file.json>`;
|
|
99
|
+
const approvalState = approvalStates(packetDir).get(sourceKey(id))?.state ?? "excluded";
|
|
100
|
+
if (approvalState !== "approved") {
|
|
101
|
+
// Approval is blocking for use; never auto-approve (Contracts approval-state).
|
|
102
|
+
return {
|
|
103
|
+
code: 1,
|
|
104
|
+
out: `GitHub source ${selection.connection_id} (${selection.external_id}) is ${approvalState} for this packet — connection is not approval.\n` +
|
|
105
|
+
`Approve it, then re-run:\n` +
|
|
106
|
+
` foo source approve ${rel} --provider github --connection ${selection.connection_id} --external-id ${selection.external_id} --scope ${selection.external_id} --by <you>\n` +
|
|
107
|
+
` ${resume}\n`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const fixturePath = flagValue(args, "--source-fixture");
|
|
111
|
+
if (!fixturePath) {
|
|
112
|
+
return { code: 1, out: `no GitHub evidence for ${selection.url}. The CLI has no live GitHub MCP — supply it with --source-fixture <file.json>, then re-run:\n ${resume}\n` };
|
|
113
|
+
}
|
|
114
|
+
let fixture;
|
|
115
|
+
try {
|
|
116
|
+
fixture = readJson(path.resolve(root, fixturePath));
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return { code: 1, out: `--source-fixture is not readable JSON: ${fixturePath}\n` };
|
|
120
|
+
}
|
|
121
|
+
const at = new Date().toISOString();
|
|
122
|
+
const specId = readPacketSpecId(packetDir);
|
|
123
|
+
// Per-PR selection -> the full done_when verdict + dual-reader receipt (T-006).
|
|
124
|
+
if (selection.unit === "pr") {
|
|
125
|
+
// One shared engine for both reconcile entry points (reconcile-anywhere-v0
|
|
126
|
+
// T-001 / D-001): asp reconcile delegates the verdict + receipt here.
|
|
127
|
+
const { result, receipt } = reconcilePrProof({
|
|
128
|
+
packet: path.basename(packetDir),
|
|
129
|
+
sourceId: id,
|
|
130
|
+
fixture,
|
|
131
|
+
specId,
|
|
132
|
+
criteria: readDoneWhenCriteria(packetDir),
|
|
133
|
+
observedAt: at,
|
|
134
|
+
});
|
|
135
|
+
recordGithubConflicts(packetDir, id, result, at);
|
|
136
|
+
// Local-only, gitignored persistence; foo posts nothing to GitHub (R-007/R-008).
|
|
137
|
+
appendIntegrationRecord(packetDir, { kind: "github_receipt", ...receipt });
|
|
138
|
+
if (json)
|
|
139
|
+
return { code: 0, out: `${JSON.stringify(receipt, null, 2)}\n` };
|
|
140
|
+
return { code: 0, out: `${renderGithubReceipt(receipt)}\nReceipt is local-only — paste the block above into the PR yourself; foo posts nothing.\n` };
|
|
141
|
+
}
|
|
142
|
+
// Per-repo selection -> the packet-linkage selection report (T-002); run per-PR for a verdict.
|
|
143
|
+
const report = githubSelectionReport({ packet: path.basename(packetDir), selection, approvalState, specId, fixture });
|
|
144
|
+
if (json)
|
|
145
|
+
return { code: 0, out: `${JSON.stringify(report, null, 2)}\n` };
|
|
146
|
+
const linkedList = report.linked.length ? ` — ${report.linked.map((l) => l.external_id).join(", ")}` : "";
|
|
147
|
+
const out = [
|
|
148
|
+
`GitHub source reconcile · ${report.packet}`,
|
|
149
|
+
` selection: ${selection.unit} ${selection.connection_id} (${selection.external_id}) — approved`,
|
|
150
|
+
` packet-linked PRs: ${report.linked.length}${linkedList}`,
|
|
151
|
+
...(report.unlinked_excluded ? [` excluded (not packet-linked): ${report.unlinked_excluded}`] : []),
|
|
152
|
+
` ${report.note}`,
|
|
153
|
+
"",
|
|
154
|
+
].join("\n");
|
|
155
|
+
return { code: 0, out };
|
|
156
|
+
}
|
|
157
|
+
// Proofs-complete nudge (packet-lifecycle-truth-v0 R-003): when reconcile runs
|
|
158
|
+
// on a packet whose derived truth is done but whose header is still non-terminal,
|
|
159
|
+
// print the done-verdict suggestion. A printed signal on stderr, never an
|
|
160
|
+
// automatic restamp (signals-over-gates-v0) — and never on stdout, where it
|
|
161
|
+
// would corrupt the JSON report a pipe is parsing.
|
|
162
|
+
function suggestDoneVerdict(root, packetDir) {
|
|
163
|
+
try {
|
|
164
|
+
const status = derivePacketStatus(packetDir);
|
|
165
|
+
if (status.lifecycle_effective !== "done" || isTerminal(status.lifecycle_stored))
|
|
166
|
+
return;
|
|
167
|
+
const rel = path.relative(root, packetDir).split(path.sep).join("/");
|
|
168
|
+
process.stderr.write(`[signal] ${rel} is proofs-complete but lifecycle reads '${status.lifecycle_stored ?? "unset"}' — ` +
|
|
169
|
+
`record the completion: foo asp verdict ${rel} done --reason "<why it is done>" (a suggestion, never automatic)\n`);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Advisory only — a failed derivation must never break reconcile itself.
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export async function runAspCommand(root, command, packetArg, args, domainArgs) {
|
|
176
|
+
// Answer --help before anything else runs (alpha_tester_fixes_v0 R-001).
|
|
177
|
+
//
|
|
178
|
+
// asp used to take "whatever the first token is" as the subcommand and, inside
|
|
179
|
+
// `event`, as the event type — before any flag was parsed. So `foo asp --help`
|
|
180
|
+
// died with "packet-dir missing", and `foo asp event <packet> --help` appended
|
|
181
|
+
// a literal {"type":"--help"} to the local event log. The `self` domain got
|
|
182
|
+
// this branch (cli.ts); asp never did. Nothing may be written on the way to
|
|
183
|
+
// working out what was asked.
|
|
184
|
+
if (command === "help" || hasFlag(domainArgs, "--help") || hasFlag(domainArgs, "-h")) {
|
|
185
|
+
process.stdout.write(aspUsage());
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
// Cross-packet index (packet_index_v0): takes no packet-dir argument.
|
|
189
|
+
if (command === "index") {
|
|
190
|
+
try {
|
|
191
|
+
const indexArgs = domainArgs.slice(1);
|
|
192
|
+
// Live mode: serve the index over localhost, re-rendered per request, with
|
|
193
|
+
// reload-on-change. Long-running until ctrl-c (bin/foo sets exitCode, never
|
|
194
|
+
// process.exit, so the listening server keeps the process alive).
|
|
195
|
+
if (indexArgs.includes("--serve")) {
|
|
196
|
+
const portIdx = indexArgs.indexOf("--port");
|
|
197
|
+
const portRaw = portIdx !== -1 ? indexArgs[portIdx + 1] : undefined;
|
|
198
|
+
const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : DEFAULT_SERVE_PORT;
|
|
199
|
+
const server = serveIndex(root, { port });
|
|
200
|
+
await new Promise((resolve, reject) => {
|
|
201
|
+
server.once("error", reject);
|
|
202
|
+
server.once("listening", () => {
|
|
203
|
+
process.stdout.write(`foo live spec dashboard\n root: ${root}\n url: http://localhost:${port}/\n reloads on spec-state change · ctrl-c to stop\n`);
|
|
204
|
+
});
|
|
205
|
+
server.once("close", () => resolve());
|
|
206
|
+
});
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
const htmlIdx = indexArgs.indexOf("--html");
|
|
210
|
+
const html = htmlIdx !== -1;
|
|
211
|
+
const maybePath = html ? indexArgs[htmlIdx + 1] : undefined;
|
|
212
|
+
const htmlPath = maybePath && !maybePath.startsWith("--") ? maybePath : undefined;
|
|
213
|
+
// Drift glyphs on the text index ride the same human-only gate as status
|
|
214
|
+
// (statusGlyphs R-008): interactive TTY only, never --json or a pipe.
|
|
215
|
+
const glyphs = glyphsEnabled({ isTTY: Boolean(process.stdout.isTTY), env: process.env, args: indexArgs });
|
|
216
|
+
process.stdout.write(runAspIndex(root, { json: indexArgs.includes("--json"), html, htmlPath, glyphs }));
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
// A throw here is a real defect, not a typo — classify it so it pages and so the
|
|
221
|
+
// message + stack reach the local log instead of vanishing into a bare exit 1.
|
|
222
|
+
markFailure(classifyThrown(error));
|
|
223
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Scaffold (asp-cli-lifecycle-v0 T-003): the packet dir does not exist yet, so
|
|
228
|
+
// this runs before packet resolution.
|
|
229
|
+
if (command === "init") {
|
|
230
|
+
try {
|
|
231
|
+
if (!packetArg)
|
|
232
|
+
return failWith("packet name missing");
|
|
233
|
+
const result = initPacket({ root: root, name: packetArg });
|
|
234
|
+
const rel = path.relative(root, result.packetDir).split(path.sep).join("/");
|
|
235
|
+
process.stdout.write(`scaffolded ${rel} (spec_id ${result.specId})\n` +
|
|
236
|
+
`Next: foo asp validate ${rel}\n`);
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
return failThrown(error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const packet = (() => {
|
|
244
|
+
try {
|
|
245
|
+
return resolvePacketDir(root, packetArg);
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
})();
|
|
251
|
+
// Two different failures wear the same shape here. No arg is a typo (`foo asp status`
|
|
252
|
+
// — two of the four errors on the owner's dashboard were exactly this). A named packet
|
|
253
|
+
// that does not exist is a real not-found. Only the second is a fact about the repo.
|
|
254
|
+
if (!packet) {
|
|
255
|
+
return packetArg
|
|
256
|
+
? failWith(`packet directory not found: ${packetArg}`, { errorClass: "not_found", errorCode: "E_PACKET_NOT_FOUND" })
|
|
257
|
+
: failWith("packet-dir missing", { errorCode: "E_PACKET_DIR_MISSING" });
|
|
258
|
+
}
|
|
259
|
+
// Local event-log write (T-004): foo asp event <packet> <type> [flags].
|
|
260
|
+
if (command === "event") {
|
|
261
|
+
try {
|
|
262
|
+
const [type, ...eventArgs] = args;
|
|
263
|
+
if (!type)
|
|
264
|
+
return failWith("event type missing");
|
|
265
|
+
// A flag is not an event type. Without this, `--anything` became the type
|
|
266
|
+
// and went straight into the ledger (alpha_tester_fixes_v0 R-001).
|
|
267
|
+
if (type.startsWith("-")) {
|
|
268
|
+
return failWith(`event type missing — \`${type}\` is a flag, not an event type.\n` +
|
|
269
|
+
`Nothing was written. Try: foo asp event ${packetArg ?? "<packet-dir>"} <type> --summary "<text>"`);
|
|
270
|
+
}
|
|
271
|
+
// Refuse what we did not read. `foo asp event <packet> <type> <summary>`
|
|
272
|
+
// used to drop the bare summary on the floor and commit `data: {}`. For an
|
|
273
|
+
// local event log, refusing loudly is the only safe default — a wrong
|
|
274
|
+
// entry cannot be removed, so productfoo never guesses at one.
|
|
275
|
+
const surplus = surplusArgs(eventArgs, { value: EVENT_VALUE_FLAGS });
|
|
276
|
+
if (surplus.length > 0) {
|
|
277
|
+
const first = surplus[0];
|
|
278
|
+
const problem = first.startsWith("-")
|
|
279
|
+
? `unknown flag: ${first}`
|
|
280
|
+
: `unexpected argument: ${first} — did you mean --summary "${first}"?`;
|
|
281
|
+
return failWith(`${problem}\nNothing was written. The local event log is not rewritten, so productfoo refuses rather than guess.`);
|
|
282
|
+
}
|
|
283
|
+
const subject = flagValue(eventArgs, "--subject");
|
|
284
|
+
const dataRaw = flagValue(eventArgs, "--data");
|
|
285
|
+
const summary = flagValue(eventArgs, "--summary");
|
|
286
|
+
let data = {};
|
|
287
|
+
if (dataRaw) {
|
|
288
|
+
// Malformed --data is the caller's typo, not productfoo's bug: convert the raw
|
|
289
|
+
// SyntaxError to a UsageError so it exits as a usage error and files no
|
|
290
|
+
// Bug Capsule (#778).
|
|
291
|
+
let parsedData;
|
|
292
|
+
try {
|
|
293
|
+
parsedData = JSON.parse(dataRaw);
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
throw new UsageError(`--data is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, "E_BAD_FLAG");
|
|
297
|
+
}
|
|
298
|
+
if (!parsedData || typeof parsedData !== "object" || Array.isArray(parsedData)) {
|
|
299
|
+
return failWith("--data must be a JSON object");
|
|
300
|
+
}
|
|
301
|
+
data = parsedData;
|
|
302
|
+
}
|
|
303
|
+
if (summary && !("summary" in data))
|
|
304
|
+
data.summary = summary;
|
|
305
|
+
const event = appendEvent({ packetDir: packet.packetDir, type, subject, data });
|
|
306
|
+
process.stdout.write(`${JSON.stringify(event, null, 2)}\n`);
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
return failThrown(error);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Lifecycle verdict (packet-lifecycle-v0): foo asp verdict <packet> <state> --reason <text>.
|
|
314
|
+
// parked/killed retire the packet; done completes it (packet-lifecycle-truth-v0
|
|
315
|
+
// R-003); active reopens it. The reason is mandatory — the whole point is that
|
|
316
|
+
// every terminal transition gets a recorded rationale, not silence.
|
|
317
|
+
if (command === "verdict") {
|
|
318
|
+
try {
|
|
319
|
+
const [state, ...verdictArgs] = args;
|
|
320
|
+
if (!state)
|
|
321
|
+
return failWith(`verdict state missing (expected ${VERDICT_STATES.join(" | ")})`);
|
|
322
|
+
if (!VERDICT_STATES.includes(state)) {
|
|
323
|
+
return failWith(`invalid verdict state: ${state} (expected ${VERDICT_STATES.join(" | ")})`);
|
|
324
|
+
}
|
|
325
|
+
const reason = flagValue(verdictArgs, "--reason");
|
|
326
|
+
if (!reason)
|
|
327
|
+
return failWith("--reason is required: record why, not just that");
|
|
328
|
+
// A done verdict must be backed by the derived truth (R-003): every task
|
|
329
|
+
// done and every done_when check proven. A human can still overrule the
|
|
330
|
+
// derivation with --force — the event trail then records that they did.
|
|
331
|
+
if (state === "done" && !hasFlag(verdictArgs, "--force")) {
|
|
332
|
+
const status = derivePacketStatus(packet.packetDir);
|
|
333
|
+
if (status.lifecycle_effective !== "done") {
|
|
334
|
+
const open = status.tasks.total - status.tasks.done - status.tasks.dropped;
|
|
335
|
+
return failWith(`refusing 'done': packet is not effectively done — ${open} task(s) still open and ` +
|
|
336
|
+
`${status.proof.checks_done} of ${status.proof.checks_total} done_when checks are done (a done verdict needs every check done with a proof). ` +
|
|
337
|
+
`Finish and prove the work, or overrule the derivation with --force.`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const result = recordVerdict({ packetDir: packet.packetDir, state: state, reason });
|
|
341
|
+
const rel = path.relative(root, packet.packetDir).split(path.sep).join("/");
|
|
342
|
+
process.stdout.write(`${rel}: lifecycle ${result.from ?? "(unset)"} -> ${result.to} (event ${result.event.id})\n` +
|
|
343
|
+
`Next: foo asp status ${rel}\n`);
|
|
344
|
+
return 0;
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
return failThrown(error);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Task status write (T-004): foo asp task <packet> <id> --status <s> [flags].
|
|
351
|
+
if (command === "task") {
|
|
352
|
+
try {
|
|
353
|
+
const [taskId, ...taskArgs] = args;
|
|
354
|
+
if (!taskId)
|
|
355
|
+
return failWith("task id missing");
|
|
356
|
+
const status = flagValue(taskArgs, "--status");
|
|
357
|
+
if (!status)
|
|
358
|
+
return failWith("--status is required");
|
|
359
|
+
const needsRaw = flagValue(taskArgs, "--needs");
|
|
360
|
+
const result = updateTask({
|
|
361
|
+
packetDir: packet.packetDir,
|
|
362
|
+
taskId,
|
|
363
|
+
status,
|
|
364
|
+
proof: flagValue(taskArgs, "--proof"),
|
|
365
|
+
summary: flagValue(taskArgs, "--summary"),
|
|
366
|
+
reason: flagValue(taskArgs, "--reason"),
|
|
367
|
+
needs: needsRaw ? needsRaw.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
|
|
368
|
+
});
|
|
369
|
+
const rel = path.relative(root, packet.packetDir).split(path.sep).join("/");
|
|
370
|
+
process.stdout.write(`${taskId}: ${result.from} -> ${result.to} (event ${result.event.id})\n` +
|
|
371
|
+
`Next: foo asp status ${rel}\n`);
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
return failThrown(error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Derived status (R-019): generic over any packet, computed from tasks.yaml —
|
|
379
|
+
// not a per-packet module like validate/reconcile. Exits non-zero only on a
|
|
380
|
+
// packet error (parse/missing file), never on incomplete work.
|
|
381
|
+
if (command === "status") {
|
|
382
|
+
try {
|
|
383
|
+
// --html writes the self-contained single-packet progress card (the visual
|
|
384
|
+
// sibling of asp index --html). Like index, the only write is the artifact.
|
|
385
|
+
const htmlIdx = args.indexOf("--html");
|
|
386
|
+
if (htmlIdx !== -1) {
|
|
387
|
+
const maybePath = args[htmlIdx + 1];
|
|
388
|
+
const explicit = maybePath && !maybePath.startsWith("--") ? maybePath : undefined;
|
|
389
|
+
const outPath = path.resolve(root, explicit ?? `${path.basename(packet.packetDir)}-progress.html`);
|
|
390
|
+
writeFileSync(outPath, renderAspStatus(packet.packetDir, { html: true }));
|
|
391
|
+
process.stdout.write(`wrote progress card to ${outPath}\n`);
|
|
392
|
+
return 0;
|
|
393
|
+
}
|
|
394
|
+
// The rich card is the default in an interactive terminal; pipes, --json,
|
|
395
|
+
// and --plain keep the stable text view so scripts and tests never parse
|
|
396
|
+
// ANSI. --rich forces the layout for capture (without color unless a TTY).
|
|
397
|
+
const json = args.includes("--json");
|
|
398
|
+
const isTty = Boolean(process.stdout.isTTY);
|
|
399
|
+
const rich = !json && (args.includes("--rich") || (isTty && !args.includes("--plain")));
|
|
400
|
+
const color = rich && isTty && !process.env.NO_COLOR && process.env.TERM !== "dumb";
|
|
401
|
+
// Bars/truncation are visual-only and unhelpful (noisy) off a terminal, so
|
|
402
|
+
// they ride the real-TTY signal, not the --rich force used for capture.
|
|
403
|
+
const bars = rich && isTty;
|
|
404
|
+
// Status glyphs (foo-status-glyphs-v0) ride a human-only gate (R-008): off
|
|
405
|
+
// for --json, NO_COLOR, and piped/non-TTY. They decorate the text view
|
|
406
|
+
// (--plain / --deep); the rich card carries its own.
|
|
407
|
+
const glyphs = !json && glyphsEnabled({ isTTY: Boolean(process.stdout.isTTY), env: process.env, args });
|
|
408
|
+
process.stdout.write(renderAspStatus(packet.packetDir, { json, deep: args.includes("--deep"), rich, color, bars, glyphs }));
|
|
409
|
+
// Cost of what is LEFT, at the moment you choose the next task (spec-budget-v0
|
|
410
|
+
// R-010). A packet that is 80% done does not still cost its full build price,
|
|
411
|
+
// so this scales by the open task share rather than reprinting the packet
|
|
412
|
+
// total. Human surfaces only: the --json and --html contracts are untouched.
|
|
413
|
+
// Structural, never egresses, and silently absent when it cannot be honest.
|
|
414
|
+
if (!json) {
|
|
415
|
+
const cost = await costHeadline(root, packet.packetDir, { remainingOnly: true });
|
|
416
|
+
if (cost)
|
|
417
|
+
process.stdout.write(`\n Cost: ${cost}\n`);
|
|
418
|
+
}
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
catch (error) {
|
|
422
|
+
// A throw here is a real defect, not a typo — classify it so it pages and so the
|
|
423
|
+
// message + stack reach the local log instead of vanishing into a bare exit 1.
|
|
424
|
+
markFailure(classifyThrown(error));
|
|
425
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
426
|
+
return 1;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
// Intent verification (intent-verification-verdict-v0): the third packet read
|
|
430
|
+
// leg — did we ship what we intended, with proof? Advisory by default (a
|
|
431
|
+
// signal, not a boundary): it surfaces the verdict and exits 0. --strict
|
|
432
|
+
// escalates only `drift` (a false "done") to a nonzero exit, never merely
|
|
433
|
+
// unproven or unfinished work. --record appends the portable verdict event so
|
|
434
|
+
// the proof travels in the ledger any runtime reads.
|
|
435
|
+
if (command === "verify") {
|
|
436
|
+
try {
|
|
437
|
+
const json = args.includes("--json");
|
|
438
|
+
const report = verifyPacket({ packetDir: packet.packetDir, root: root });
|
|
439
|
+
if (args.includes("--record")) {
|
|
440
|
+
appendEvent({
|
|
441
|
+
packetDir: packet.packetDir,
|
|
442
|
+
type: VERIFY_EVENT_TYPE,
|
|
443
|
+
data: {
|
|
444
|
+
verdict: report.verdict,
|
|
445
|
+
summary: report.summary,
|
|
446
|
+
tasks: report.tasks,
|
|
447
|
+
checks: report.checks,
|
|
448
|
+
requirements: report.requirements,
|
|
449
|
+
findings: report.findings.length,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
if (json) {
|
|
454
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
const glyphs = glyphsEnabled({ isTTY: Boolean(process.stdout.isTTY), env: process.env, args });
|
|
458
|
+
process.stdout.write(renderVerifyReport(report, glyphs));
|
|
459
|
+
process.stdout.write(report.verdict === "drift"
|
|
460
|
+
? "\n[boundary-adjacent] drift is a false completion claim — run with --strict to make it block.\n"
|
|
461
|
+
: "\n[signal] advisory only — asp verify surfaces the verdict, it does not block.\n");
|
|
462
|
+
}
|
|
463
|
+
return hasFlag(args, "--strict") && report.verdict === "drift" ? 1 : 0;
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
// A throw here is a real defect, not a typo — classify it so it pages and so the
|
|
467
|
+
// message + stack reach the local log instead of vanishing into a bare exit 1.
|
|
468
|
+
markFailure(classifyThrown(error));
|
|
469
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
470
|
+
return 1;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// packet-grammar-v1 R-006: mint missing IDs in place, prove it, refuse
|
|
474
|
+
// anything that is not pure insertion. Existing IDs and prose are
|
|
475
|
+
// untouchable (R-003) — the migrator only ever ADDS a token to a line.
|
|
476
|
+
if (command === "migrate") {
|
|
477
|
+
const dryRun = hasFlag(args, "--dry-run");
|
|
478
|
+
const wantJson = hasFlag(args, "--json");
|
|
479
|
+
const specPath = path.join(packet.packetDir, "spec.md");
|
|
480
|
+
if (!existsSync(specPath))
|
|
481
|
+
return failWith(`spec.md missing in ${packet.packetDir}`);
|
|
482
|
+
const original = readFileSync(specPath, "utf8");
|
|
483
|
+
const result = migrateSpecText(original);
|
|
484
|
+
const label2 = path.basename(packet.packetDir);
|
|
485
|
+
if (!result.proofOk) {
|
|
486
|
+
// Defensive: a migrator that cannot strip its own insertions back to the
|
|
487
|
+
// original must not write (R-006 contract — exit 2, no files touched).
|
|
488
|
+
process.stderr.write(`foo asp migrate — REFUSED: insertion proof failed for ${label2}; no files written.\n`);
|
|
489
|
+
return 2;
|
|
490
|
+
}
|
|
491
|
+
if (!dryRun && result.insertions.length > 0)
|
|
492
|
+
writeFileSync(specPath, result.text);
|
|
493
|
+
if (wantJson) {
|
|
494
|
+
process.stdout.write(`${JSON.stringify({
|
|
495
|
+
schema_version: "0.1",
|
|
496
|
+
packet: label2,
|
|
497
|
+
written: !dryRun && result.insertions.length > 0,
|
|
498
|
+
proof: "insertion-only",
|
|
499
|
+
insertions: result.insertions.map((i) => ({ line: i.line, id: i.id })),
|
|
500
|
+
}, null, 2)}\n`);
|
|
501
|
+
return 0;
|
|
502
|
+
}
|
|
503
|
+
if (result.insertions.length === 0) {
|
|
504
|
+
process.stdout.write(`${label2} — every anchor already has an ID; nothing to insert.\n`);
|
|
505
|
+
return 0;
|
|
506
|
+
}
|
|
507
|
+
const verb = dryRun ? "would insert" : "inserted";
|
|
508
|
+
process.stdout.write(`${label2} — ${verb} ${result.insertions.length} ID(s), prose untouched (proof: insertion-only):\n`);
|
|
509
|
+
for (const i of result.insertions)
|
|
510
|
+
process.stdout.write(` spec.md:${i.line} ${i.id}\n`);
|
|
511
|
+
if (!dryRun)
|
|
512
|
+
process.stdout.write(`Next: foo asp validate ${packet.packetArg}\n`);
|
|
513
|
+
return 0;
|
|
514
|
+
}
|
|
515
|
+
if (command !== "validate" && command !== "reconcile") {
|
|
516
|
+
return failWith(`unknown command: ${command}`);
|
|
517
|
+
}
|
|
518
|
+
// A compiled per-packet module is preferred during transition, but no longer
|
|
519
|
+
// required: when a packet ships no validate/reconcile module, both commands
|
|
520
|
+
// fall back to the shared engines (asp-cli-lifecycle-v0 T-002), so any packet
|
|
521
|
+
// dir — including a freshly scaffolded one — validates and reconciles.
|
|
522
|
+
const module = await loadPacketCommand(root, packet.packetArg, command);
|
|
523
|
+
const context = { root: root, packetDir: packet.packetDir, packetArg: packet.packetArg };
|
|
524
|
+
const label = path.basename(packet.packetDir);
|
|
525
|
+
// asp validate is advisory by default (sog_v0 T-004 / R1): it surfaces findings
|
|
526
|
+
// and exits 0. --strict escalates to a nonzero exit only on a boundary-class
|
|
527
|
+
// violation (B2 spec identity); structural findings stay signals even then.
|
|
528
|
+
if (command === "validate") {
|
|
529
|
+
const strict = hasFlag(args, "--strict");
|
|
530
|
+
const wantJson = hasFlag(args, "--json");
|
|
531
|
+
// --fix applies the migrator before validating (R-006): complaining about
|
|
532
|
+
// a missing ID when one command inserts it is friction with no safety.
|
|
533
|
+
if (hasFlag(args, "--fix")) {
|
|
534
|
+
const specPath = path.join(packet.packetDir, "spec.md");
|
|
535
|
+
if (existsSync(specPath)) {
|
|
536
|
+
const fixed = migrateSpecText(readFileSync(specPath, "utf8"));
|
|
537
|
+
if (fixed.proofOk && fixed.insertions.length > 0) {
|
|
538
|
+
writeFileSync(specPath, fixed.text);
|
|
539
|
+
if (!wantJson) {
|
|
540
|
+
process.stdout.write(`--fix: inserted ${fixed.insertions.length} ID(s) via foo asp migrate.\n`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
try {
|
|
546
|
+
const message = module?.validatePacket
|
|
547
|
+
? await module.validatePacket(context)
|
|
548
|
+
: validatePacketGeneric({ packetDir: packet.packetDir, label, requireSpecSections: true });
|
|
549
|
+
// Grammar lint (packet-grammar-v1 R-004): advisory findings with stable
|
|
550
|
+
// codes, never touching the exit code — blocking is the commit gate's
|
|
551
|
+
// call (R-005), and only for the malformed/duplicate subset.
|
|
552
|
+
const grammar = lintAnchors(readFileSync(path.join(packet.packetDir, "spec.md"), "utf8")).map((f) => ({ ...f, file: "spec.md" }));
|
|
553
|
+
const decisions = derivePacketStatus(packet.packetDir).decision_findings.map((f) => ({ ...f, file: "spec.md" }));
|
|
554
|
+
const findings = [...grammar, ...decisions];
|
|
555
|
+
if (wantJson) {
|
|
556
|
+
process.stdout.write(`${JSON.stringify({ schema_version: "0.1", packet: label, ok: true, findings }, null, 2)}\n`);
|
|
557
|
+
return 0;
|
|
558
|
+
}
|
|
559
|
+
process.stdout.write(`${message}\n`);
|
|
560
|
+
for (const f of findings) {
|
|
561
|
+
process.stdout.write(`[signal] ${f.code} ${f.file}:${f.line} — ${f.detail}\n`);
|
|
562
|
+
}
|
|
563
|
+
// B4 at authoring time. `asp validate` is the reflex an agent runs right after
|
|
564
|
+
// editing a packet (AGENTS.md), and it used to say a plain "ok" for a packet
|
|
565
|
+
// carrying an unbudgeted, spending loop — it never looked at loops.yaml at all.
|
|
566
|
+
// So the one moment someone is actually holding the loop contract was the one
|
|
567
|
+
// moment nothing mentioned the budget. Surfaced here as a SIGNAL (foo blocks in
|
|
568
|
+
// `self check`/CI, per signals-over-gates); the point is to tell the author now,
|
|
569
|
+
// not to ambush them in CI.
|
|
570
|
+
const loopSignal = loopBudgetSignal(root, packet.packetDir);
|
|
571
|
+
if (loopSignal)
|
|
572
|
+
process.stdout.write(`\n${loopSignal}\n`);
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
catch (error) {
|
|
576
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
577
|
+
if (!isPacketValidationError(error))
|
|
578
|
+
markFailure(classifyThrown(error));
|
|
579
|
+
// B2 spec identity is the only boundary asp validate surfaces; every other
|
|
580
|
+
// structural failure is an advisory signal that never blocks.
|
|
581
|
+
const isBoundary = /spec_id must match/.test(message);
|
|
582
|
+
if (wantJson) {
|
|
583
|
+
process.stdout.write(`${JSON.stringify({ schema_version: "0.1", packet: label, ok: false, error: message, findings: [] }, null, 2)}\n`);
|
|
584
|
+
return strict && isBoundary ? 1 : 0;
|
|
585
|
+
}
|
|
586
|
+
process.stdout.write(`${message}\n`);
|
|
587
|
+
process.stdout.write(isBoundary
|
|
588
|
+
? "\n[boundary] B2 spec identity — run with --strict to make this block.\n"
|
|
589
|
+
: "\n[signal] advisory only — asp validate never blocks on structural findings.\n");
|
|
590
|
+
return strict && isBoundary ? 1 : 0;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
// GitHub source reconcile (github-integration-v0 T-002): `asp reconcile
|
|
595
|
+
// --source github:<url>` admits a PR/repo selection rather than diffing files.
|
|
596
|
+
if (flagValue(args, "--source")) {
|
|
597
|
+
const result = runGithubSourceReconcile(root, packet.packetDir, args);
|
|
598
|
+
process.stdout.write(result.out);
|
|
599
|
+
if (result.code === 0)
|
|
600
|
+
suggestDoneVerdict(root, packet.packetDir);
|
|
601
|
+
return result.code;
|
|
602
|
+
}
|
|
603
|
+
if (module?.reconcilePacket) {
|
|
604
|
+
const report = await module.reconcilePacket({ ...context, args });
|
|
605
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
606
|
+
suggestDoneVerdict(root, packet.packetDir);
|
|
607
|
+
return 0;
|
|
608
|
+
}
|
|
609
|
+
const report = reconcilePacketGeneric({
|
|
610
|
+
...parseReconcileArgs(args),
|
|
611
|
+
packetDir: packet.packetDir,
|
|
612
|
+
root: root,
|
|
613
|
+
});
|
|
614
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
615
|
+
suggestDoneVerdict(root, packet.packetDir);
|
|
616
|
+
return 0;
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
// Mark it, like every other catch in this file does. This one did not, so a real crash
|
|
620
|
+
// here reached the top with no class, no code and — worst — no STACK: it fingerprinted
|
|
621
|
+
// as the stackless E_UNKNOWN sentinel and fused with every other unmarked nonzero exit
|
|
622
|
+
// in foo. The bug's own trace was discarded at the only place that still had it.
|
|
623
|
+
markFailure(classifyThrown(error));
|
|
624
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
625
|
+
return 1;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// B4 authoring-time signal (see the call site in `asp validate`). Read-only and
|
|
629
|
+
// status-aware, mirroring `self check`: a draft contract cannot spend, so it is a
|
|
630
|
+
// nudge; an ARMED one that has not been budgeted is called out as the boundary
|
|
631
|
+
// violation it is. Returns null when there is nothing to say.
|
|
632
|
+
export function loopBudgetSignal(root, packetDir) {
|
|
633
|
+
if (!existsSync(path.join(packetDir, "loops.yaml")))
|
|
634
|
+
return null;
|
|
635
|
+
let loops;
|
|
636
|
+
try {
|
|
637
|
+
loops = loadLoopContracts(packetDir).loops;
|
|
638
|
+
}
|
|
639
|
+
catch (error) {
|
|
640
|
+
return `[signal] loops.yaml does not parse as a loop contract (${error instanceof Error ? error.message : String(error)}) — foo self check will block on this (B4).`;
|
|
641
|
+
}
|
|
642
|
+
const pricing = loadPricing(root);
|
|
643
|
+
const floor = handshakeFloorUsd();
|
|
644
|
+
const packetRel = path.relative(root, packetDir).split(path.sep).join("/") || packetDir;
|
|
645
|
+
const lines = [];
|
|
646
|
+
for (const loop of loops) {
|
|
647
|
+
if (loop.budget?.agreed)
|
|
648
|
+
continue;
|
|
649
|
+
const estimate = estimateLoopBudget({ packetDir, sources: loop.observe.sources, pricing, unit: "usd", budget: loop.budget });
|
|
650
|
+
const unmeasurable = hasExternalSources(estimate.measured);
|
|
651
|
+
// A loop that can prove it is cheap auto-approves on `loop check` — nothing to say.
|
|
652
|
+
if (estimate.priced && !unmeasurable && estimate.usd.expected < floor)
|
|
653
|
+
continue;
|
|
654
|
+
const armed = loop.status === "ready" || loop.status === "active";
|
|
655
|
+
// Never quote a dollar figure we cannot stand behind.
|
|
656
|
+
const cost = unmeasurable
|
|
657
|
+
? "its cost is unknown (it reads sources productfoo cannot size)"
|
|
658
|
+
: !estimate.priced
|
|
659
|
+
? "its cost could not be priced"
|
|
660
|
+
: `it prices at $${estimate.usd.expected.toFixed(2)} expected, at or above the $${floor.toFixed(2)} floor`;
|
|
661
|
+
const fix = unmeasurable
|
|
662
|
+
? `foo loop estimate ${packetRel} --agree --limit <usd> (you set the cap; productfoo will not invent one)`
|
|
663
|
+
: `foo loop estimate ${packetRel} --agree`;
|
|
664
|
+
lines.push(armed
|
|
665
|
+
? `[boundary] B4 — loop ${loop.id} is ${loop.status} (armed) with no agreed budget, and ${cost}. foo self check and CI will block this.\n Authorise it: ${fix}`
|
|
666
|
+
: `[signal] loop ${loop.id} (${loop.status}) has no agreed budget, and ${cost}.\n Budget it before arming it: ${fix}`);
|
|
667
|
+
}
|
|
668
|
+
return lines.length > 0 ? lines.join("\n") : null;
|
|
669
|
+
}
|