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,124 @@
|
|
|
1
|
+
import { loadPricing, type MeasureMethod, type PricingTable } from "./loopBudget.js";
|
|
2
|
+
import { type ActiveModel } from "./activeModel.js";
|
|
3
|
+
export type Priority = "must" | "should" | "may" | "must-not";
|
|
4
|
+
export type Risk = "low" | "medium" | "high";
|
|
5
|
+
export type Confidence = "low" | "medium" | "high";
|
|
6
|
+
export type Tier = "core" | "next" | "deferred";
|
|
7
|
+
export type TierSource = "decisions" | "priority" | "single";
|
|
8
|
+
export type ParsedRequirement = {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
priority: Priority;
|
|
12
|
+
risk: Risk;
|
|
13
|
+
confidence: Confidence;
|
|
14
|
+
text: string;
|
|
15
|
+
};
|
|
16
|
+
export type ParseResult = {
|
|
17
|
+
requirements: ParsedRequirement[];
|
|
18
|
+
excluded: string[];
|
|
19
|
+
};
|
|
20
|
+
export declare const DEFAULT_BUILD_PASSES = 3;
|
|
21
|
+
export declare function buildPassesDefault(): number;
|
|
22
|
+
export declare function parseRequirements(specText: string): ParseResult;
|
|
23
|
+
export declare function parseTierMap(specText: string): Map<string, Tier>;
|
|
24
|
+
export declare function assignTier(req: ParsedRequirement, tierMap: Map<string, Tier>): Tier | "excluded";
|
|
25
|
+
export type RequirementEstimate = {
|
|
26
|
+
id: string;
|
|
27
|
+
priority: Priority;
|
|
28
|
+
risk: Risk;
|
|
29
|
+
confidence: Confidence;
|
|
30
|
+
tokens: number;
|
|
31
|
+
measured_method: MeasureMethod;
|
|
32
|
+
expected_tokens: number;
|
|
33
|
+
ceiling_tokens: number;
|
|
34
|
+
/** null when unpriced — NEVER 0. `0` reads as "free"; null reads as "unknown" (D-014). */
|
|
35
|
+
expected_usd: number | null;
|
|
36
|
+
ceiling_usd: number | null;
|
|
37
|
+
/** Fraction (0..1) of the packet's expected tokens. The relative signal — the honest one. */
|
|
38
|
+
share_of_packet: number;
|
|
39
|
+
};
|
|
40
|
+
export type TierEstimate = {
|
|
41
|
+
tier: Tier;
|
|
42
|
+
expected_usd: number | null;
|
|
43
|
+
ceiling_usd: number | null;
|
|
44
|
+
expected_tokens: number;
|
|
45
|
+
ceiling_tokens: number;
|
|
46
|
+
/** Agent runtime, minutes. null when the worker has no declared tokens_per_sec. */
|
|
47
|
+
expected_minutes: number | null;
|
|
48
|
+
ceiling_minutes: number | null;
|
|
49
|
+
requirements: RequirementEstimate[];
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* An alternative the user could actually take, with the honest price of taking
|
|
53
|
+
* it. A trade-off that only lists what you SAVE is an advertisement, not a
|
|
54
|
+
* trade-off — `costs_you` is mandatory (D-015).
|
|
55
|
+
*/
|
|
56
|
+
export type TradeOff = {
|
|
57
|
+
id: string;
|
|
58
|
+
label: string;
|
|
59
|
+
expected_usd: number | null;
|
|
60
|
+
expected_minutes: number | null;
|
|
61
|
+
expected_tokens: number;
|
|
62
|
+
/** Negative = cheaper/faster than the baseline. null when the baseline is unpriced. */
|
|
63
|
+
delta_usd_pct: number | null;
|
|
64
|
+
delta_minutes_pct: number | null;
|
|
65
|
+
costs_you: string;
|
|
66
|
+
};
|
|
67
|
+
export type SpecEstimate = {
|
|
68
|
+
/** 0.2: usd fields are nullable, runtime + trade-offs added (spec-budget-v0 D-013..D-015). */
|
|
69
|
+
schema_version: "0.2";
|
|
70
|
+
packet: string;
|
|
71
|
+
basis: "heuristic";
|
|
72
|
+
confidence: "low";
|
|
73
|
+
unit: "usd" | "tokens";
|
|
74
|
+
tier_source: TierSource;
|
|
75
|
+
build_passes: number;
|
|
76
|
+
worker_model: string;
|
|
77
|
+
/** Assumed agent throughput for the worker; null => no time is reported at all. */
|
|
78
|
+
worker_tokens_per_sec: number | null;
|
|
79
|
+
priced_with: string;
|
|
80
|
+
stale: boolean;
|
|
81
|
+
stale_age_days: number;
|
|
82
|
+
/**
|
|
83
|
+
* False when dollars could not be priced (missing pricing data or an unknown
|
|
84
|
+
* worker model): every *_usd field is null — never 0 — and the token and time
|
|
85
|
+
* ranges are the estimate (model-roster-data-v0 R-002/R-003, D-014).
|
|
86
|
+
*/
|
|
87
|
+
priced: boolean;
|
|
88
|
+
/** Worded degradation/unknown-model findings — always worded; a field in --json. */
|
|
89
|
+
findings: string[];
|
|
90
|
+
/** What this estimate does NOT price. Agent runtime is priced; human time is not. */
|
|
91
|
+
excludes: string[];
|
|
92
|
+
excluded_requirements: string[];
|
|
93
|
+
measured_method: MeasureMethod;
|
|
94
|
+
tiers: TierEstimate[];
|
|
95
|
+
total: {
|
|
96
|
+
expected_usd: number | null;
|
|
97
|
+
ceiling_usd: number | null;
|
|
98
|
+
expected_tokens: number;
|
|
99
|
+
ceiling_tokens: number;
|
|
100
|
+
expected_minutes: number | null;
|
|
101
|
+
ceiling_minutes: number | null;
|
|
102
|
+
};
|
|
103
|
+
/** Ranked alternatives. Empty only when there is genuinely nothing to trade. */
|
|
104
|
+
tradeoffs: TradeOff[];
|
|
105
|
+
};
|
|
106
|
+
export declare function estimateSpecBudget(args: {
|
|
107
|
+
packetDir: string;
|
|
108
|
+
packetLabel: string;
|
|
109
|
+
pricing: PricingTable;
|
|
110
|
+
unit?: "usd" | "tokens";
|
|
111
|
+
buildPasses?: number;
|
|
112
|
+
workerModel?: string;
|
|
113
|
+
apiKey?: string;
|
|
114
|
+
preferApi?: boolean;
|
|
115
|
+
now?: Date;
|
|
116
|
+
/**
|
|
117
|
+
* Repo root, used to ask git whether the spec is gitignored/local-only before
|
|
118
|
+
* any count_tokens egress (D-016). Omitted => treated as not-local-only; the
|
|
119
|
+
* secret scan in guardSourceForEgress still guards.
|
|
120
|
+
*/
|
|
121
|
+
repoRoot?: string;
|
|
122
|
+
active?: ActiveModel;
|
|
123
|
+
}): Promise<SpecEstimate>;
|
|
124
|
+
export { loadPricing };
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { countTokensViaApi, guardSourceForEgress, isLocalOnly, loadPricing, pricedWith, pricingAgeDays, pricingIsStale, structuralTokens, } from "./loopBudget.js";
|
|
4
|
+
import { autonomyDenied, resolveActiveModel } from "./activeModel.js";
|
|
5
|
+
// --- Heuristic factors (R-002 / R-006 defaults) ------------------------------
|
|
6
|
+
const PRIORITY_WEIGHT = { must: 1.5, should: 1.0, may: 0.6, "must-not": 0 };
|
|
7
|
+
const RISK_SURCHARGE = { high: 0.5, medium: 0.25, low: 0 };
|
|
8
|
+
const CONFIDENCE_SURCHARGE = { low: 0.25, medium: 0.1, high: 0 };
|
|
9
|
+
export const DEFAULT_BUILD_PASSES = 3;
|
|
10
|
+
export function buildPassesDefault() {
|
|
11
|
+
const raw = process.env.FOO_SPEC_BUILD_PASSES;
|
|
12
|
+
if (raw === undefined)
|
|
13
|
+
return DEFAULT_BUILD_PASSES;
|
|
14
|
+
const parsed = Number(raw);
|
|
15
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_BUILD_PASSES;
|
|
16
|
+
}
|
|
17
|
+
// --- Parsing -----------------------------------------------------------------
|
|
18
|
+
function normalizePriority(value) {
|
|
19
|
+
const v = value.trim().toLowerCase();
|
|
20
|
+
if (v === "must" || v === "should" || v === "may" || v === "must-not")
|
|
21
|
+
return v;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function normalizeLevel(value) {
|
|
25
|
+
const v = value.trim().toLowerCase();
|
|
26
|
+
if (v === "low" || v === "medium" || v === "high")
|
|
27
|
+
return v;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
// Parse requirement-heading blocks with Priority / Risk / Confidence metadata.
|
|
31
|
+
// The reader stays input-liberal across the catalog's legacy separators, while
|
|
32
|
+
// metadata may use the Release Channel V0 bullet layout or the older bare lines.
|
|
33
|
+
// A block missing or mis-formatting any of the three is reported as excluded so
|
|
34
|
+
// an undercount is visible rather than silent (R-002).
|
|
35
|
+
export function parseRequirements(specText) {
|
|
36
|
+
const lines = specText.split(/\r?\n/);
|
|
37
|
+
const headingRe = /^###\s+(R-?\d+)\b\s*[:—–-]?\s*(.+?)\s*$/;
|
|
38
|
+
const blocks = [];
|
|
39
|
+
let current = null;
|
|
40
|
+
for (const line of lines) {
|
|
41
|
+
const match = headingRe.exec(line);
|
|
42
|
+
if (match) {
|
|
43
|
+
if (current)
|
|
44
|
+
blocks.push(current);
|
|
45
|
+
current = { id: match[1], title: match[2], body: [line] };
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (current) {
|
|
49
|
+
if (/^##\s+/.test(line)) {
|
|
50
|
+
// next top-level section ends the requirements run
|
|
51
|
+
blocks.push(current);
|
|
52
|
+
current = null;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
current.body.push(line);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (current)
|
|
59
|
+
blocks.push(current);
|
|
60
|
+
const requirements = [];
|
|
61
|
+
const excluded = [];
|
|
62
|
+
for (const block of blocks) {
|
|
63
|
+
const body = block.body.join("\n");
|
|
64
|
+
const priorityMatch = /^-?\s*Priority:\s*(.+)$/m.exec(body);
|
|
65
|
+
const riskMatch = /^-?\s*Risk:\s*(.+)$/m.exec(body);
|
|
66
|
+
const confidenceMatch = /^-?\s*Confidence:\s*(.+)$/m.exec(body);
|
|
67
|
+
const priority = priorityMatch ? normalizePriority(priorityMatch[1]) : null;
|
|
68
|
+
const risk = riskMatch ? normalizeLevel(riskMatch[1]) : null;
|
|
69
|
+
const confidence = confidenceMatch ? normalizeLevel(confidenceMatch[1]) : null;
|
|
70
|
+
if (!priority || !risk || !confidence) {
|
|
71
|
+
excluded.push(block.id);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
requirements.push({ id: block.id, title: block.title, priority, risk, confidence, text: body });
|
|
75
|
+
}
|
|
76
|
+
return { requirements, excluded };
|
|
77
|
+
}
|
|
78
|
+
// Parse an explicit release-tier mapping from the `## Decisions` section, e.g.
|
|
79
|
+
// "core = R-001, R-002 ; next increment = R-006 ; deferred = ...". Returns a
|
|
80
|
+
// requirement-id → tier map (empty when no mapping is present).
|
|
81
|
+
export function parseTierMap(specText) {
|
|
82
|
+
const map = new Map();
|
|
83
|
+
// Anchor on the heading line so an inline `## Decisions` mention in prose does
|
|
84
|
+
// not get picked up as the section start.
|
|
85
|
+
const headingRe = /^##\s+Decisions\s*$/m;
|
|
86
|
+
const heading = headingRe.exec(specText);
|
|
87
|
+
let scope = specText;
|
|
88
|
+
if (heading) {
|
|
89
|
+
const rest = specText.slice(heading.index + heading[0].length);
|
|
90
|
+
const nextSection = rest.search(/\n##\s/);
|
|
91
|
+
scope = nextSection === -1 ? rest : rest.slice(0, nextSection);
|
|
92
|
+
}
|
|
93
|
+
const tierAliases = [
|
|
94
|
+
{ tier: "core", re: /\bcore\b[^.]*?=\s*([^;.\n]*)/i },
|
|
95
|
+
{ tier: "next", re: /\bnext(?:\s+increment)?\b[^.]*?=\s*([^;.\n]*)/i },
|
|
96
|
+
{ tier: "deferred", re: /\b(?:deferred|later)\b[^.]*?=\s*([^;.\n]*)/i },
|
|
97
|
+
];
|
|
98
|
+
for (const { tier, re } of tierAliases) {
|
|
99
|
+
const match = re.exec(scope);
|
|
100
|
+
if (!match)
|
|
101
|
+
continue;
|
|
102
|
+
for (const id of match[1].match(/R-\d+/g) ?? []) {
|
|
103
|
+
if (!map.has(id))
|
|
104
|
+
map.set(id, tier);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return map;
|
|
108
|
+
}
|
|
109
|
+
// Assign a tier to each requirement: explicit Decisions mapping wins; otherwise
|
|
110
|
+
// group by Priority (must→core, should→next, may→deferred). must-not is excluded
|
|
111
|
+
// from costing (weight 0).
|
|
112
|
+
export function assignTier(req, tierMap) {
|
|
113
|
+
if (req.priority === "must-not")
|
|
114
|
+
return "excluded";
|
|
115
|
+
const explicit = tierMap.get(req.id);
|
|
116
|
+
if (explicit)
|
|
117
|
+
return explicit;
|
|
118
|
+
if (req.priority === "must")
|
|
119
|
+
return "core";
|
|
120
|
+
if (req.priority === "should")
|
|
121
|
+
return "next";
|
|
122
|
+
return "deferred";
|
|
123
|
+
}
|
|
124
|
+
function round(value, places) {
|
|
125
|
+
const factor = 10 ** places;
|
|
126
|
+
return Math.round(value * factor) / factor;
|
|
127
|
+
}
|
|
128
|
+
// Blended per-token price: requirement build cost mixes reading (input) and
|
|
129
|
+
// generating (output), approximated 50/50 on the worker model. Documented
|
|
130
|
+
// assumption, single price source (pricing.yaml). Returns null when the model
|
|
131
|
+
// has no price — the caller degrades to token-only with a worded finding
|
|
132
|
+
// (model-roster-data-v0 R-002/R-003), never a throw and never another model's rates.
|
|
133
|
+
function blendedUsdPerToken(table, model) {
|
|
134
|
+
const price = table.models[model];
|
|
135
|
+
if (!price)
|
|
136
|
+
return null;
|
|
137
|
+
return ((price.input_per_mtok + price.output_per_mtok) / 2) / 1_000_000;
|
|
138
|
+
}
|
|
139
|
+
async function measureRequirementTokens(text, opts) {
|
|
140
|
+
// D-010 egress gate: never egress requirement text to a routed third-party endpoint.
|
|
141
|
+
if (opts.routedNonNative && opts.preferApi && opts.apiKey) {
|
|
142
|
+
return { tokens: structuralTokens(text), method: "structural_egress_gated" };
|
|
143
|
+
}
|
|
144
|
+
if (opts.preferApi && opts.apiKey) {
|
|
145
|
+
// CONTENT gate (D-016). The loop path has always guarded this same endpoint
|
|
146
|
+
// with guardSourceForEgress; the spec path shipped without it and sent raw
|
|
147
|
+
// spec.md text to count_tokens unredacted and unscanned. Two commands hitting
|
|
148
|
+
// one endpoint under different rules is how a boundary rots — so the spec
|
|
149
|
+
// path now runs the identical guard: a local-only source or a high-confidence
|
|
150
|
+
// secret withholds egress entirely; otherwise only the REDACTED copy is sent.
|
|
151
|
+
const decision = guardSourceForEgress(text, opts.localOnly === true);
|
|
152
|
+
if (!decision.egress) {
|
|
153
|
+
return { tokens: structuralTokens(text), method: "structural_egress_gated" };
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return {
|
|
157
|
+
tokens: await countTokensViaApi(opts.model, decision.text, opts.apiKey, opts.countTokensUrl),
|
|
158
|
+
method: "count_tokens",
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// graceful fallback — never block an estimate
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { tokens: structuralTokens(text), method: "structural" };
|
|
166
|
+
}
|
|
167
|
+
export async function estimateSpecBudget(args) {
|
|
168
|
+
const specPath = path.join(args.packetDir, "spec.md");
|
|
169
|
+
if (!existsSync(specPath))
|
|
170
|
+
throw new Error("spec.md missing");
|
|
171
|
+
const specText = readFileSync(specPath, "utf8");
|
|
172
|
+
const { requirements, excluded } = parseRequirements(specText);
|
|
173
|
+
const tierMap = parseTierMap(specText);
|
|
174
|
+
const tierSource = tierMap.size > 0 ? "decisions" : requirements.length > 0 ? "priority" : "single";
|
|
175
|
+
const buildPasses = args.buildPasses ?? buildPassesDefault();
|
|
176
|
+
// Price the resolved active model unless the caller pins one (R-002): a GLM-backed
|
|
177
|
+
// session is priced at GLM rates, not hardcoded Opus.
|
|
178
|
+
const active = args.active ?? resolveActiveModel();
|
|
179
|
+
const worker = args.workerModel ?? active.worker;
|
|
180
|
+
// Degraded pricing (model-roster-data-v0 R-002/R-003): a missing table or an
|
|
181
|
+
// unknown worker downgrades dollar fields to token-only with a worded finding.
|
|
182
|
+
const findings = [...(args.pricing.findings ?? [])];
|
|
183
|
+
const blended = blendedUsdPerToken(args.pricing, worker);
|
|
184
|
+
const priced = args.pricing.available !== false && blended !== null;
|
|
185
|
+
if (args.pricing.available !== false && blended === null) {
|
|
186
|
+
findings.push(`unknown model: ${worker} has no price in ${args.pricing.source} — dollar estimate degraded to token-only, no default-model rates assumed`);
|
|
187
|
+
}
|
|
188
|
+
// Unpriced => null, never 0. `0` is the one value that reads as a VALID and
|
|
189
|
+
// attractive answer ("this tier is free"); null reads as "unknown" (D-014).
|
|
190
|
+
const usdPerToken = priced && blended !== null ? blended : null;
|
|
191
|
+
const money = (tokens) => (usdPerToken === null ? null : round(tokens * usdPerToken, 4));
|
|
192
|
+
const workerTps = args.pricing.models[worker]?.tokens_per_sec ?? null;
|
|
193
|
+
if (priced && workerTps === null) {
|
|
194
|
+
findings.push(`no throughput for ${worker} in ${args.pricing.source} — runtime estimate withheld (no made-up minutes)`);
|
|
195
|
+
}
|
|
196
|
+
// Agent runtime, minutes. Null-safe: a model with no declared throughput
|
|
197
|
+
// reports NO time rather than an invented one (R-008).
|
|
198
|
+
const minutes = (tokens, tps) => tps === null || tps <= 0 ? null : round(tokens / tps / 60, 1);
|
|
199
|
+
const preferApi = args.preferApi ?? true;
|
|
200
|
+
// Mirror the loop path: a gitignored/local-only spec never egresses (D-016).
|
|
201
|
+
const localOnly = args.repoRoot ? isLocalOnly(args.repoRoot, specPath, specPath) : false;
|
|
202
|
+
const order = ["core", "next", "deferred"];
|
|
203
|
+
const buckets = new Map(order.map((tier) => [tier, []]));
|
|
204
|
+
let anyApi = false;
|
|
205
|
+
let anyStructural = false;
|
|
206
|
+
let anyGated = false;
|
|
207
|
+
for (const req of requirements) {
|
|
208
|
+
const tier = assignTier(req, tierMap);
|
|
209
|
+
if (tier === "excluded") {
|
|
210
|
+
excluded.push(req.id);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const { tokens, method } = await measureRequirementTokens(req.text, {
|
|
214
|
+
model: worker,
|
|
215
|
+
apiKey: args.apiKey,
|
|
216
|
+
preferApi,
|
|
217
|
+
countTokensUrl: active.countTokensUrl,
|
|
218
|
+
routedNonNative: active.routed,
|
|
219
|
+
localOnly,
|
|
220
|
+
});
|
|
221
|
+
if (method === "count_tokens")
|
|
222
|
+
anyApi = true;
|
|
223
|
+
else if (method === "structural_egress_gated")
|
|
224
|
+
anyGated = true;
|
|
225
|
+
else
|
|
226
|
+
anyStructural = true;
|
|
227
|
+
const expectedMult = PRIORITY_WEIGHT[req.priority];
|
|
228
|
+
const ceilingMult = expectedMult * (1 + RISK_SURCHARGE[req.risk] + CONFIDENCE_SURCHARGE[req.confidence]);
|
|
229
|
+
const expectedTokens = Math.round(tokens * expectedMult * buildPasses);
|
|
230
|
+
const ceilingTokens = Math.round(tokens * ceilingMult * buildPasses);
|
|
231
|
+
buckets.get(tier).push({
|
|
232
|
+
id: req.id,
|
|
233
|
+
priority: req.priority,
|
|
234
|
+
risk: req.risk,
|
|
235
|
+
confidence: req.confidence,
|
|
236
|
+
tokens,
|
|
237
|
+
measured_method: method,
|
|
238
|
+
expected_tokens: expectedTokens,
|
|
239
|
+
ceiling_tokens: ceilingTokens,
|
|
240
|
+
expected_usd: money(expectedTokens),
|
|
241
|
+
ceiling_usd: money(ceilingTokens),
|
|
242
|
+
share_of_packet: 0, // filled once the packet total is known
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
const tiers = order
|
|
246
|
+
.map((tier) => {
|
|
247
|
+
const reqs = buckets.get(tier);
|
|
248
|
+
const expectedTokens = reqs.reduce((s, r) => s + r.expected_tokens, 0);
|
|
249
|
+
const ceilingTokens = reqs.reduce((s, r) => s + r.ceiling_tokens, 0);
|
|
250
|
+
return {
|
|
251
|
+
tier,
|
|
252
|
+
requirements: reqs,
|
|
253
|
+
expected_tokens: expectedTokens,
|
|
254
|
+
ceiling_tokens: ceilingTokens,
|
|
255
|
+
expected_usd: money(expectedTokens),
|
|
256
|
+
ceiling_usd: money(ceilingTokens),
|
|
257
|
+
expected_minutes: minutes(expectedTokens, workerTps),
|
|
258
|
+
ceiling_minutes: minutes(ceilingTokens, workerTps),
|
|
259
|
+
};
|
|
260
|
+
})
|
|
261
|
+
.filter((tier) => tier.requirements.length > 0);
|
|
262
|
+
const totalExpectedTokens = tiers.reduce((s, t) => s + t.expected_tokens, 0);
|
|
263
|
+
const totalCeilingTokens = tiers.reduce((s, t) => s + t.ceiling_tokens, 0);
|
|
264
|
+
// The relative signal. This is the number that survives even when the dollars
|
|
265
|
+
// are junk: "R-002 is a third of this build" is decision-grade; "$0.0714" is not.
|
|
266
|
+
for (const tier of tiers) {
|
|
267
|
+
for (const req of tier.requirements) {
|
|
268
|
+
req.share_of_packet = totalExpectedTokens > 0 ? round(req.expected_tokens / totalExpectedTokens, 3) : 0;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const total = {
|
|
272
|
+
expected_tokens: totalExpectedTokens,
|
|
273
|
+
ceiling_tokens: totalCeilingTokens,
|
|
274
|
+
expected_usd: money(totalExpectedTokens),
|
|
275
|
+
ceiling_usd: money(totalCeilingTokens),
|
|
276
|
+
expected_minutes: minutes(totalExpectedTokens, workerTps),
|
|
277
|
+
ceiling_minutes: minutes(totalCeilingTokens, workerTps),
|
|
278
|
+
};
|
|
279
|
+
const tradeoffs = buildTradeOffs({
|
|
280
|
+
pricing: args.pricing,
|
|
281
|
+
worker,
|
|
282
|
+
workerTps,
|
|
283
|
+
usdPerToken,
|
|
284
|
+
buildPasses,
|
|
285
|
+
tiers,
|
|
286
|
+
totalExpectedTokens,
|
|
287
|
+
baselineUsd: total.expected_usd,
|
|
288
|
+
baselineMinutes: total.expected_minutes,
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
schema_version: "0.2",
|
|
292
|
+
packet: args.packetLabel,
|
|
293
|
+
basis: "heuristic",
|
|
294
|
+
confidence: "low",
|
|
295
|
+
unit: args.unit ?? "usd",
|
|
296
|
+
tier_source: tierSource,
|
|
297
|
+
build_passes: buildPasses,
|
|
298
|
+
worker_model: worker,
|
|
299
|
+
worker_tokens_per_sec: workerTps,
|
|
300
|
+
priced_with: priced ? pricedWith(args.pricing) : args.pricing.available === false ? pricedWith(args.pricing) : "unpriced (unknown model)",
|
|
301
|
+
stale: priced && pricingIsStale(args.pricing, args.now),
|
|
302
|
+
stale_age_days: priced ? pricingAgeDays(args.pricing, args.now) : 0,
|
|
303
|
+
priced,
|
|
304
|
+
findings,
|
|
305
|
+
// What this number does NOT cover. The dominant real cost of an agent building
|
|
306
|
+
// a requirement is its EXECUTION — subagents, tool calls, re-reads, retries,
|
|
307
|
+
// failed attempts — none of which this models. It sizes the requirement text and
|
|
308
|
+
// a build-passes multiplier, nothing more. Measured against a real run this can
|
|
309
|
+
// be low by an order of magnitude or worse, so it is a FLOOR, not a forecast.
|
|
310
|
+
// Naming that here is the difference between a useful lower bound and a lie.
|
|
311
|
+
//
|
|
312
|
+
// This is also precisely WHY the estimate is a RELATIVE instrument (D-013): a
|
|
313
|
+
// floor that omits the dominant cost term can never honestly answer "what will
|
|
314
|
+
// this cost" — but it CAN answer "which parts are heavy, and what do my options
|
|
315
|
+
// cost me," because the omitted overhead scales with all of them alike.
|
|
316
|
+
//
|
|
317
|
+
// R-007 (amended, D-013): AGENT RUNTIME is priced above and is deliberately NOT
|
|
318
|
+
// in this list. HUMAN time — engineer-effort, calendar-time — is, and always
|
|
319
|
+
// will be. The output must never conflate the two.
|
|
320
|
+
excludes: [
|
|
321
|
+
"engineer-effort",
|
|
322
|
+
"calendar-time",
|
|
323
|
+
"agent-execution-overhead (subagents, tool calls, retries — usually the dominant cost)",
|
|
324
|
+
],
|
|
325
|
+
excluded_requirements: excluded,
|
|
326
|
+
// count_tokens if any source used the API; else egress-gated if that's why we fell
|
|
327
|
+
// back; else plain structural. The gated signal must survive to the surface (D-002).
|
|
328
|
+
measured_method: anyApi ? "count_tokens" : anyGated && !anyStructural ? "structural_egress_gated" : "structural",
|
|
329
|
+
tiers,
|
|
330
|
+
total,
|
|
331
|
+
tradeoffs,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
// --- Trade-offs (R-009 / D-015) ----------------------------------------------
|
|
335
|
+
//
|
|
336
|
+
// The point of a relative estimate is that the alternatives are comparable. Each
|
|
337
|
+
// option must state what it COSTS you, not only what it saves — an option list
|
|
338
|
+
// that only shows savings is an advertisement.
|
|
339
|
+
function buildTradeOffs(args) {
|
|
340
|
+
const { pricing, worker, buildPasses, tiers, totalExpectedTokens, baselineUsd, baselineMinutes } = args;
|
|
341
|
+
const out = [];
|
|
342
|
+
if (totalExpectedTokens === 0)
|
|
343
|
+
return out;
|
|
344
|
+
const pct = (next, base) => next === null || base === null || base === 0 ? null : Math.round(((next - base) / base) * 100);
|
|
345
|
+
// 1. Swap the model. Token volume is text-derived and does not change with the
|
|
346
|
+
// model — only price and speed do. That is exactly why the honest caveat
|
|
347
|
+
// below matters: a weaker model plausibly needs MORE passes, which this
|
|
348
|
+
// arithmetic does NOT model. Never sell a model swap as free savings.
|
|
349
|
+
const candidates = Object.entries(pricing.models)
|
|
350
|
+
.filter(([name]) => name !== worker)
|
|
351
|
+
.map(([name, price]) => {
|
|
352
|
+
const perToken = (price.input_per_mtok + price.output_per_mtok) / 2 / 1_000_000;
|
|
353
|
+
return {
|
|
354
|
+
name,
|
|
355
|
+
usd: round(totalExpectedTokens * perToken, 4),
|
|
356
|
+
minutes: price.tokens_per_sec ? round(totalExpectedTokens / price.tokens_per_sec / 60, 1) : null,
|
|
357
|
+
// foo default-denies autonomy on some families. Offering such a model as a
|
|
358
|
+
// cheap win while `foo loop estimate --agree` would REFUSE it is foo
|
|
359
|
+
// contradicting itself — so the deny rides along in the trade-off (D-017).
|
|
360
|
+
autonomyDenied: autonomyDenied(price.family),
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
const cheapest = [...candidates].sort((a, b) => a.usd - b.usd)[0];
|
|
364
|
+
const fastest = [...candidates]
|
|
365
|
+
.filter((c) => c.minutes !== null)
|
|
366
|
+
.sort((a, b) => a.minutes - b.minutes)[0];
|
|
367
|
+
for (const pick of [cheapest, fastest]) {
|
|
368
|
+
if (!pick)
|
|
369
|
+
continue;
|
|
370
|
+
if (out.some((o) => o.id === `model:${pick.name}`))
|
|
371
|
+
continue; // cheapest may also be fastest
|
|
372
|
+
if (baselineUsd !== null && pick.usd >= baselineUsd && (pick.minutes === null || baselineMinutes === null || pick.minutes >= baselineMinutes)) {
|
|
373
|
+
continue; // neither cheaper nor faster — not a trade-off worth offering
|
|
374
|
+
}
|
|
375
|
+
const samePassesCaveat = "This assumes the SAME number of build passes — a weaker model plausibly needs more, which would eat the saving. Unmeasured (role-expertise, not repo-evidence).";
|
|
376
|
+
out.push({
|
|
377
|
+
id: `model:${pick.name}`,
|
|
378
|
+
label: `Build with ${pick.name} instead of ${worker}`,
|
|
379
|
+
expected_usd: args.usdPerToken === null ? null : pick.usd,
|
|
380
|
+
expected_minutes: pick.minutes,
|
|
381
|
+
expected_tokens: totalExpectedTokens,
|
|
382
|
+
delta_usd_pct: pct(pick.usd, baselineUsd),
|
|
383
|
+
delta_minutes_pct: pct(pick.minutes, baselineMinutes),
|
|
384
|
+
costs_you: pick.autonomyDenied
|
|
385
|
+
? `more than money: foo DEFAULT-DENIES autonomous work on this model (weaker self-correction makes it a degraded verifier of its own loop), so \`foo loop estimate --agree\` will refuse it unless you override with FOO_ALLOW_GLM_AUTONOMY=1. Plus capability on the hard requirements. ${samePassesCaveat}`
|
|
386
|
+
: `capability on the hard requirements. ${samePassesCaveat}`,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
// 2. Spend fewer build passes. Linear and honest: passes is the biggest single
|
|
390
|
+
// assumption in the whole estimate.
|
|
391
|
+
if (buildPasses > 1) {
|
|
392
|
+
const scale = (buildPasses - 1) / buildPasses;
|
|
393
|
+
const tokens = Math.round(totalExpectedTokens * scale);
|
|
394
|
+
const usd = args.usdPerToken === null ? null : round(tokens * args.usdPerToken, 4);
|
|
395
|
+
out.push({
|
|
396
|
+
id: `passes:${buildPasses - 1}`,
|
|
397
|
+
label: `Budget ${buildPasses - 1} build passes instead of ${buildPasses}`,
|
|
398
|
+
expected_usd: usd,
|
|
399
|
+
expected_minutes: args.workerTps === null ? null : round(tokens / args.workerTps / 60, 1),
|
|
400
|
+
expected_tokens: tokens,
|
|
401
|
+
delta_usd_pct: pct(usd, baselineUsd),
|
|
402
|
+
delta_minutes_pct: pct(args.workerTps === null ? null : round(tokens / args.workerTps / 60, 1), baselineMinutes),
|
|
403
|
+
costs_you: "rework headroom. If the build actually needs the last pass you pay for it anyway — this saves nothing, it only hides the cost until later.",
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
// 3. Cut scope to core. The only lever that changes what you actually GET.
|
|
407
|
+
const core = tiers.find((t) => t.tier === "core");
|
|
408
|
+
const deferred = tiers.filter((t) => t.tier !== "core");
|
|
409
|
+
if (core && deferred.length > 0 && core.expected_tokens < totalExpectedTokens) {
|
|
410
|
+
const deferredIds = deferred.flatMap((t) => t.requirements.map((r) => r.id));
|
|
411
|
+
out.push({
|
|
412
|
+
id: "tier:core",
|
|
413
|
+
label: "Build the core tier only",
|
|
414
|
+
expected_usd: core.expected_usd,
|
|
415
|
+
expected_minutes: core.expected_minutes,
|
|
416
|
+
expected_tokens: core.expected_tokens,
|
|
417
|
+
delta_usd_pct: pct(core.expected_usd, baselineUsd),
|
|
418
|
+
delta_minutes_pct: pct(core.expected_minutes, baselineMinutes),
|
|
419
|
+
costs_you: `you defer ${deferredIds.join(", ")}. This is the only option here that changes what you actually ship — the others just change what it costs to ship the same thing.`,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
return out;
|
|
423
|
+
}
|
|
424
|
+
export { loadPricing };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type HypothesisProof = "untested" | "supported" | "falsified" | "not_assessable";
|
|
2
|
+
export interface DecisionSignalProvenance {
|
|
3
|
+
source: string;
|
|
4
|
+
section: "Expected Impact" | "Hypotheses";
|
|
5
|
+
line: number;
|
|
6
|
+
}
|
|
7
|
+
export type ExpectedImpactSignal = {
|
|
8
|
+
status: "recorded";
|
|
9
|
+
outcome: string;
|
|
10
|
+
target: string | null;
|
|
11
|
+
provenance: DecisionSignalProvenance;
|
|
12
|
+
} | {
|
|
13
|
+
status: "missing";
|
|
14
|
+
label: "Impact not stated";
|
|
15
|
+
};
|
|
16
|
+
export interface RecordedHypothesisSignal {
|
|
17
|
+
status: "recorded";
|
|
18
|
+
id: string;
|
|
19
|
+
statement: string;
|
|
20
|
+
authoredState: string | null;
|
|
21
|
+
validationRefs: string[];
|
|
22
|
+
evidence: string | null;
|
|
23
|
+
proof: HypothesisProof;
|
|
24
|
+
proofDetail: string;
|
|
25
|
+
provenance: DecisionSignalProvenance;
|
|
26
|
+
}
|
|
27
|
+
export type PrimaryHypothesisSignal = RecordedHypothesisSignal | {
|
|
28
|
+
status: "missing";
|
|
29
|
+
label: "Hypothesis missing";
|
|
30
|
+
proof: "not_assessable";
|
|
31
|
+
proofDetail: "No hypothesis is recorded in the spec.";
|
|
32
|
+
};
|
|
33
|
+
export interface SpecDecisionSignals {
|
|
34
|
+
expectedImpact: ExpectedImpactSignal;
|
|
35
|
+
primaryHypothesis: PrimaryHypothesisSignal;
|
|
36
|
+
hypotheses: RecordedHypothesisSignal[];
|
|
37
|
+
}
|
|
38
|
+
export declare function deriveSpecDecisionSignals(specText: string, source?: string): SpecDecisionSignals;
|