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,364 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { GateBlock, NotFoundError } from "./errorTaxonomy.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "./vendorYaml.js";
|
|
5
|
+
import { buildAgreedBudget, estimateLoopBudget, handshakeFloorUsd, hasExternalSources, loadPricing, parseBudget, } from "./loopBudget.js";
|
|
6
|
+
const LOOP_STATES = [
|
|
7
|
+
"draft",
|
|
8
|
+
"ready",
|
|
9
|
+
"active",
|
|
10
|
+
"succeeded",
|
|
11
|
+
"clean_no_op",
|
|
12
|
+
"blocked",
|
|
13
|
+
"approval_required",
|
|
14
|
+
"exhausted",
|
|
15
|
+
"stagnated",
|
|
16
|
+
"retired",
|
|
17
|
+
];
|
|
18
|
+
function asObject(value, label) {
|
|
19
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
20
|
+
throw new Error(`${label} must be an object`);
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function asArray(value, label) {
|
|
24
|
+
if (!Array.isArray(value))
|
|
25
|
+
throw new Error(`${label} must be an array`);
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function requireString(value, label) {
|
|
29
|
+
if (typeof value !== "string" || value.length === 0)
|
|
30
|
+
throw new Error(`${label} must be a nonempty string`);
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
function stringArray(value, label) {
|
|
34
|
+
return asArray(value, label).map((item, index) => requireString(item, `${label}[${index}]`));
|
|
35
|
+
}
|
|
36
|
+
function requireKeys(value, keys, label) {
|
|
37
|
+
for (const key of keys) {
|
|
38
|
+
if (!(key in value))
|
|
39
|
+
throw new Error(`${label} missing ${key}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function loadTasksDoc(packetDir) {
|
|
43
|
+
const tasksPath = path.join(packetDir, "tasks.yaml");
|
|
44
|
+
if (!existsSync(tasksPath))
|
|
45
|
+
throw new Error("tasks.yaml missing");
|
|
46
|
+
return asObject(parseYaml(readFileSync(tasksPath, "utf8")), "tasks.yaml");
|
|
47
|
+
}
|
|
48
|
+
function taskIds(tasksDoc) {
|
|
49
|
+
return new Set(asArray(tasksDoc.tasks, "tasks.yaml.tasks").map((task, index) => requireString(asObject(task, `tasks.yaml.tasks[${index}]`).id, `tasks.yaml.tasks[${index}].id`)));
|
|
50
|
+
}
|
|
51
|
+
function defaultLoopTask(tasksDoc) {
|
|
52
|
+
const tasks = asArray(tasksDoc.tasks, "tasks.yaml.tasks").map((task, index) => asObject(task, `tasks.yaml.tasks[${index}]`));
|
|
53
|
+
const reconciliationTask = tasks.find((task) => stringArray(task.refs, `task ${String(task.id)} refs`).includes("R-010"));
|
|
54
|
+
const selected = reconciliationTask ?? tasks[0];
|
|
55
|
+
return requireString(selected.id, "selected task id");
|
|
56
|
+
}
|
|
57
|
+
export function renderLoopDesign(packetDir) {
|
|
58
|
+
const tasksDoc = loadTasksDoc(packetDir);
|
|
59
|
+
const specId = requireString(tasksDoc.spec_id, "tasks.yaml.spec_id");
|
|
60
|
+
const taskId = defaultLoopTask(tasksDoc);
|
|
61
|
+
return [
|
|
62
|
+
'schema_version: "0.1"',
|
|
63
|
+
`spec_id: ${specId}`,
|
|
64
|
+
'updated: null',
|
|
65
|
+
'loops:',
|
|
66
|
+
' - id: L-001',
|
|
67
|
+
' title: "Reconciliation loop contract"',
|
|
68
|
+
' status: draft',
|
|
69
|
+
' owner: product',
|
|
70
|
+
' refs:',
|
|
71
|
+
' requirements: [R-010]',
|
|
72
|
+
` tasks: [${taskId}]`,
|
|
73
|
+
' events: []',
|
|
74
|
+
' mode: human-in-loop',
|
|
75
|
+
' trigger: manual',
|
|
76
|
+
' goal: Review changed work against agreed tasks without editing canonical packet files.',
|
|
77
|
+
' lifecycle:',
|
|
78
|
+
' allowed_states: [draft, ready, active, succeeded, clean_no_op, blocked, approval_required, exhausted, stagnated, retired]',
|
|
79
|
+
' terminal_states: [succeeded, clean_no_op, blocked, exhausted, stagnated, retired]',
|
|
80
|
+
' observe:',
|
|
81
|
+
' sources: [spec.md, tasks.yaml, events.jsonl]',
|
|
82
|
+
' freshness: read files immediately before each pass',
|
|
83
|
+
' choose:',
|
|
84
|
+
' criteria: [highest-risk unmapped change]',
|
|
85
|
+
' act:',
|
|
86
|
+
' allowed: [propose one reconciliation finding]',
|
|
87
|
+
' forbidden:',
|
|
88
|
+
' - Do not edit canonical packet files without explicit approval',
|
|
89
|
+
' - Do not import raw private sources',
|
|
90
|
+
' verify:',
|
|
91
|
+
' check: "foo asp reconcile <packet-dir> --git-diff"',
|
|
92
|
+
' method: manual review plus CLI reconciliation',
|
|
93
|
+
' record:',
|
|
94
|
+
' event_type: com.adaptivespec.loop.iteration_recorded',
|
|
95
|
+
' artifact: artifacts/loops/L-001/',
|
|
96
|
+
' required_fields: [iteration_id, observed_sources, action_taken, verification_result, next_state]',
|
|
97
|
+
' stop:',
|
|
98
|
+
' succeeded: all changed work maps to agreed tasks or accepted events',
|
|
99
|
+
' clean_no_op: no relevant changed work is present',
|
|
100
|
+
' blocked: approval needed for canonical packet changes',
|
|
101
|
+
' exhausted: two passes produce no clearer loop contract',
|
|
102
|
+
' stagnated: a pass produces no new resolvable finding',
|
|
103
|
+
' ask_user_when:',
|
|
104
|
+
' - product meaning changes',
|
|
105
|
+
' - source boundary changes',
|
|
106
|
+
' - destructive, production, financial, privacy-sensitive, or external-message action would be enabled',
|
|
107
|
+
'',
|
|
108
|
+
].join("\n");
|
|
109
|
+
}
|
|
110
|
+
export function loadLoopContracts(packetDir) {
|
|
111
|
+
const loopsPath = path.join(packetDir, "loops.yaml");
|
|
112
|
+
if (!existsSync(loopsPath))
|
|
113
|
+
throw new NotFoundError("loops.yaml missing; run foo loop design <packet-dir> > <packet-dir>/loops.yaml", "E_PACKET_NOT_FOUND");
|
|
114
|
+
const tasksDoc = loadTasksDoc(packetDir);
|
|
115
|
+
const specId = requireString(tasksDoc.spec_id, "tasks.yaml.spec_id");
|
|
116
|
+
const ids = taskIds(tasksDoc);
|
|
117
|
+
const doc = asObject(parseYaml(readFileSync(loopsPath, "utf8")), "loops.yaml");
|
|
118
|
+
requireKeys(doc, ["schema_version", "spec_id", "loops"], "loops.yaml");
|
|
119
|
+
if (doc.spec_id !== specId)
|
|
120
|
+
throw new Error("loops.yaml.spec_id must match tasks.yaml.spec_id");
|
|
121
|
+
const loops = asArray(doc.loops, "loops.yaml.loops").map((loop, index) => {
|
|
122
|
+
const loopObject = asObject(loop, `loops.yaml.loops[${index}]`);
|
|
123
|
+
requireKeys(loopObject, ["id", "title", "status", "owner", "refs", "goal", "observe", "choose", "act", "verify", "record", "stop", "ask_user_when"], `loops.yaml.loops[${index}]`);
|
|
124
|
+
const id = requireString(loopObject.id, `loops.yaml.loops[${index}].id`);
|
|
125
|
+
const status = requireString(loopObject.status, `loops.yaml.loops[${index}].status`);
|
|
126
|
+
if (!LOOP_STATES.includes(status))
|
|
127
|
+
throw new Error(`loops.yaml.loops[${index}].status invalid: ${status}`);
|
|
128
|
+
const refs = asObject(loopObject.refs, `loops.yaml.loops[${index}].refs`);
|
|
129
|
+
const tasks = stringArray(refs.tasks, `loops.yaml.loops[${index}].refs.tasks`);
|
|
130
|
+
for (const task of tasks) {
|
|
131
|
+
if (!ids.has(task))
|
|
132
|
+
throw new Error(`loop ${id} references unknown task ${task}`);
|
|
133
|
+
}
|
|
134
|
+
const observe = asObject(loopObject.observe, `loops.yaml.loops[${index}].observe`);
|
|
135
|
+
const record = asObject(loopObject.record, `loops.yaml.loops[${index}].record`);
|
|
136
|
+
return {
|
|
137
|
+
id,
|
|
138
|
+
title: requireString(loopObject.title, `loops.yaml.loops[${index}].title`),
|
|
139
|
+
status,
|
|
140
|
+
owner: requireString(loopObject.owner, `loops.yaml.loops[${index}].owner`),
|
|
141
|
+
refs: {
|
|
142
|
+
requirements: stringArray(refs.requirements, `loops.yaml.loops[${index}].refs.requirements`),
|
|
143
|
+
tasks,
|
|
144
|
+
events: "events" in refs ? stringArray(refs.events, `loops.yaml.loops[${index}].refs.events`) : [],
|
|
145
|
+
},
|
|
146
|
+
goal: requireString(loopObject.goal, `loops.yaml.loops[${index}].goal`),
|
|
147
|
+
observe: {
|
|
148
|
+
sources: stringArray(observe.sources, `loops.yaml.loops[${index}].observe.sources`),
|
|
149
|
+
},
|
|
150
|
+
record: {
|
|
151
|
+
artifact: requireString(record.artifact, `loops.yaml.loops[${index}].record.artifact`),
|
|
152
|
+
},
|
|
153
|
+
ask_user_when: stringArray(loopObject.ask_user_when, `loops.yaml.loops[${index}].ask_user_when`),
|
|
154
|
+
budget: "budget" in loopObject ? parseBudget(loopObject.budget, `loops.yaml.loops[${index}].budget`) : undefined,
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
if (loops.length === 0)
|
|
158
|
+
throw new Error("loops.yaml.loops must be a nonempty array");
|
|
159
|
+
return { specId, loops };
|
|
160
|
+
}
|
|
161
|
+
// R-005: an unbudgeted loop is a malformed contract — but enforcement is
|
|
162
|
+
// size-aware (T-004). `--strict-budget` refuses any unbudgeted loop outright.
|
|
163
|
+
// Otherwise foo prices each unbudgeted loop with the cold/structural estimator:
|
|
164
|
+
// a loop under the handshake threshold (FOO_BUDGET_HANDSHAKE_FLOOR, default
|
|
165
|
+
// $0.50) is auto-approved and logged — cheap work keeps its momentum — while a
|
|
166
|
+
// loop at or above the threshold blocks until a human runs the budget handshake.
|
|
167
|
+
// Either way an estimate event is recorded for calibration.
|
|
168
|
+
function emitLoopEstimateEvent(packetDir, loopId, result, meta) {
|
|
169
|
+
const eventsPath = path.join(packetDir, "events.jsonl");
|
|
170
|
+
const id = `evt_loop_budget_estimated_${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
171
|
+
// UNMEASURABLE MEANS UNKNOWN — here too, not only on the screen.
|
|
172
|
+
//
|
|
173
|
+
// `priced` answers "was pricing.yaml found?", NOT "is the cost knowable?". A loop that
|
|
174
|
+
// reads the web is priced and unknowable at the same time, and this writer only ever asked
|
|
175
|
+
// the first question. So it recorded `expected_usd: 0.11, ceiling_usd: 0.275` on a loop
|
|
176
|
+
// whose own findings, in the same event, said "cost unknown: 5 observation source(s)
|
|
177
|
+
// cannot be sized offline". The ledger asserted a number the estimate had just refused to
|
|
178
|
+
// stand behind.
|
|
179
|
+
//
|
|
180
|
+
// That was already the packet's most-corrected mistake. `foo loop estimate` prints "unknown
|
|
181
|
+
// — this loop reads sources productfoo cannot size", and evt_mo_008 in model-orchestrator-v0 spent
|
|
182
|
+
// a whole event retiring the last fabricated range: "Prior events in this ledger quoting
|
|
183
|
+
// $0.60/$1.50/$0.275 per pass are superseded: the true answer is that productfoo does not know,
|
|
184
|
+
// and now says so." The fix landed on the RENDER path on 2026-07-11 and never reached this
|
|
185
|
+
// one, so the ledger kept accruing exactly the numbers that event retired — including one
|
|
186
|
+
// written three days later, quoting $0.275 again.
|
|
187
|
+
//
|
|
188
|
+
// A screen is read once; a ledger is what a future reader and `foo asp reconcile` treat as
|
|
189
|
+
// fact. Of the two doors, this is the one that had to hold.
|
|
190
|
+
const unknown = result.unmeasurable;
|
|
191
|
+
const summary = unknown
|
|
192
|
+
? `Estimated loop ${loopId}: cost UNKNOWN — ${result.findings.join("; ") || "this loop reads sources productfoo cannot size offline"}. Treated as at/above the $${meta.floor.toFixed(2)} threshold; handshake required. Authorise with an explicit cap: foo loop estimate <packet-dir> --agree --limit <usd>.`
|
|
193
|
+
: result.priced
|
|
194
|
+
? `Estimated loop ${loopId}: $${result.usd.expected.toFixed(4)} expected (threshold $${meta.floor.toFixed(2)}); ${meta.autoApproved ? "auto-approved below threshold" : "over threshold, handshake required"}.`
|
|
195
|
+
: `Estimated loop ${loopId}: unpriced (${result.findings.join("; ") || "pricing degraded"}); token-only estimate ${result.tokens.expected} expected — treated as at/above the $${meta.floor.toFixed(2)} threshold, handshake required (fail-closed).`;
|
|
196
|
+
const event = {
|
|
197
|
+
specversion: "0.1",
|
|
198
|
+
id,
|
|
199
|
+
source: "agent://foo/loop-check",
|
|
200
|
+
type: "com.adaptivespec.loop.budget_estimated",
|
|
201
|
+
subject: `loop:${loopId}`,
|
|
202
|
+
time: new Date().toISOString(),
|
|
203
|
+
actor: { type: "agent", id: "foo:loop-check", mode: "human-in-loop" },
|
|
204
|
+
data: {
|
|
205
|
+
summary,
|
|
206
|
+
auto_approved: meta.autoApproved,
|
|
207
|
+
priced: result.priced,
|
|
208
|
+
// Recorded so a reader can tell "productfoo does not know" apart from "foo forgot to write it".
|
|
209
|
+
unmeasurable: unknown,
|
|
210
|
+
findings: result.findings,
|
|
211
|
+
handshake_floor_usd: meta.floor,
|
|
212
|
+
// Omitted entirely when unknown, rather than written as a number nobody stands behind.
|
|
213
|
+
// An absent field cannot be averaged, charted, or quoted back as a prediction.
|
|
214
|
+
...(unknown
|
|
215
|
+
? {}
|
|
216
|
+
: { expected_usd: result.usd.expected, ceiling_usd: result.usd.ceiling, expected_tokens: result.tokens.expected }),
|
|
217
|
+
basis: result.basis,
|
|
218
|
+
confidence: result.confidence,
|
|
219
|
+
priced_with: result.priced_with,
|
|
220
|
+
},
|
|
221
|
+
extensions: {},
|
|
222
|
+
};
|
|
223
|
+
appendFileSync(eventsPath, `${JSON.stringify(event)}\n`);
|
|
224
|
+
return id;
|
|
225
|
+
}
|
|
226
|
+
/** Loop states that can act, and so spend. B4's budget handshake is due for
|
|
227
|
+
* these; a loop in any other state is a signal (AGENTS.md B4). */
|
|
228
|
+
export const ARMED_LOOP_STATES = new Set(["ready", "active"]);
|
|
229
|
+
export function checkLoopContracts(root, packetDir, options = {}) {
|
|
230
|
+
// Most packets declare no loops at all, and that is the healthy default — B4 governs
|
|
231
|
+
// packets that HAVE loops. Checking one that has none used to throw a bare Error, which
|
|
232
|
+
// classifies as `command_error`: the one class that pages a human and files a Bug Capsule.
|
|
233
|
+
// So a stranger's first packet reported a defect in foo for being perfectly ordinary (#702).
|
|
234
|
+
// Nothing to check is not a failure; say so and exit 0.
|
|
235
|
+
if (!existsSync(path.join(packetDir, "loops.yaml"))) {
|
|
236
|
+
return "loop contracts ok (0) — this packet declares no loops, so there is nothing to check.";
|
|
237
|
+
}
|
|
238
|
+
const { loops } = loadLoopContracts(packetDir);
|
|
239
|
+
const allUnbudgeted = loops.filter((loop) => !loop.budget?.agreed);
|
|
240
|
+
let base = `loop contracts ok (${loops.length})`;
|
|
241
|
+
if (allUnbudgeted.length === 0)
|
|
242
|
+
return base;
|
|
243
|
+
// Strict mode: any unbudgeted loop is refused, regardless of size.
|
|
244
|
+
if (options.strictBudget) {
|
|
245
|
+
// Strict means every loop, armed or not (AGENTS.md B4 --strict-budget).
|
|
246
|
+
const ids = allUnbudgeted.map((loop) => loop.id).join(", ");
|
|
247
|
+
// B4 refusing an unbudgeted loop is the product WORKING, not breaking. It exits nonzero
|
|
248
|
+
// because it stopped you on purpose — so it is a gate_block, and it never pages anyone.
|
|
249
|
+
// (It was being recorded as a crash, which is exactly the false alarm this taxonomy exists
|
|
250
|
+
// to kill: a pager that fires when the safety rules work correctly gets muted immediately.)
|
|
251
|
+
throw new GateBlock(`loop(s) without an agreed budget: ${ids} — run foo loop estimate <packet-dir> --agree`, "E_GATE_B4_BUDGET");
|
|
252
|
+
}
|
|
253
|
+
// #906 / AGENTS.md B4: "a draft unbudgeted loop is a signal; a ready/active
|
|
254
|
+
// armed loop without budget blocks." A draft cannot act, so it cannot spend:
|
|
255
|
+
// report it and move on — no estimate, no event, no auto-approved budget
|
|
256
|
+
// written into tracked packet files. Arming it is what makes the handshake due.
|
|
257
|
+
const drafts = allUnbudgeted.filter((loop) => !ARMED_LOOP_STATES.has(loop.status));
|
|
258
|
+
const unbudgeted = allUnbudgeted.filter((loop) => ARMED_LOOP_STATES.has(loop.status));
|
|
259
|
+
if (drafts.length > 0) {
|
|
260
|
+
base += `; signal: ${drafts.length} unarmed loop(s) without an agreed budget (${drafts.map((loop) => `${loop.id} ${loop.status}`).join(", ")}) — budget before arming with foo loop estimate <packet-dir> --agree`;
|
|
261
|
+
}
|
|
262
|
+
if (unbudgeted.length === 0)
|
|
263
|
+
return base;
|
|
264
|
+
const floor = handshakeFloorUsd();
|
|
265
|
+
const pricing = loadPricing(root);
|
|
266
|
+
const autoApproved = [];
|
|
267
|
+
const overThreshold = [];
|
|
268
|
+
const degradedFindings = new Set();
|
|
269
|
+
for (const loop of unbudgeted) {
|
|
270
|
+
const result = estimateLoopBudget({ packetDir, sources: loop.observe.sources, pricing, unit: "usd", budget: loop.budget });
|
|
271
|
+
// B4 fail-closed (model-roster-data-v0 R-002): an unpriced estimate (missing
|
|
272
|
+
// pricing.yaml or unknown model) cannot prove the loop is below the floor, so
|
|
273
|
+
// it is treated as at/above it — auto-approval is a convenience that requires
|
|
274
|
+
// priced data; the handshake is never waived by degraded pricing.
|
|
275
|
+
//
|
|
276
|
+
// Same fail-closed rule for UNMEASURABLE sources: a loop that observes the web
|
|
277
|
+
// cannot be sized offline. Such a source used to measure 0 tokens, so a
|
|
278
|
+
// scheduled research loop priced itself at ~$0, fell under the floor, and
|
|
279
|
+
// auto-approved — bypassing the handshake for exactly the loops whose cost is
|
|
280
|
+
// least knowable. An estimate resting on an unmeasurable source is a floor, not
|
|
281
|
+
// a measurement, and can never prove the loop is cheap. Unknown cost => a human
|
|
282
|
+
// decides.
|
|
283
|
+
const unmeasurable = hasExternalSources(result.measured);
|
|
284
|
+
const below = result.priced && !unmeasurable && result.usd.expected < floor;
|
|
285
|
+
emitLoopEstimateEvent(packetDir, loop.id, result, { autoApproved: below, floor });
|
|
286
|
+
if (below) {
|
|
287
|
+
// Auto-approve: write an agreed budget so the cheap loop runs and is not
|
|
288
|
+
// re-estimated next time. Logged, not blocked (C-008).
|
|
289
|
+
const at = new Date().toISOString();
|
|
290
|
+
writeAgreedBudget(packetDir, loop.id, buildAgreedBudget(result, { by: "foo:auto-approve", at }));
|
|
291
|
+
autoApproved.push(loop.id);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
overThreshold.push(loop.id);
|
|
295
|
+
if (!result.priced)
|
|
296
|
+
for (const finding of result.findings)
|
|
297
|
+
degradedFindings.add(finding);
|
|
298
|
+
if (unmeasurable) {
|
|
299
|
+
const external = result.measured.filter((m) => m.external).map((m) => m.source);
|
|
300
|
+
degradedFindings.add(`loop observes ${external.length} source(s) that cannot be sized offline (${external.slice(0, 3).join("; ")}${external.length > 3 ? "; …" : ""}) — priced at a conservative floor, so the estimate cannot prove the loop is below the threshold`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// A loop at or above the threshold is malformed until a human prices it (C-007).
|
|
305
|
+
if (overThreshold.length > 0) {
|
|
306
|
+
const degradedNote = degradedFindings.size > 0
|
|
307
|
+
? ` [degraded: ${[...degradedFindings].join("; ")} — an unpriced loop cannot be auto-approved and is treated as at/above the floor]`
|
|
308
|
+
: "";
|
|
309
|
+
// B4 stopping an armed loop is the gate working, not a crash (see the
|
|
310
|
+
// strict path above): a GateBlock, never a paging command_error.
|
|
311
|
+
throw new GateBlock(`loop(s) at or above the $${floor.toFixed(2)} handshake threshold without an agreed budget: ${overThreshold.join(", ")} — run foo loop estimate <packet-dir> --agree${degradedNote}`, "E_GATE_B4_BUDGET");
|
|
312
|
+
}
|
|
313
|
+
return `${base}; ${autoApproved.length} auto-approved below the $${floor.toFixed(2)} threshold: ${autoApproved.join(", ")} (estimate logged)`;
|
|
314
|
+
}
|
|
315
|
+
function nextAction(status) {
|
|
316
|
+
if (status === "blocked" || status === "approval_required")
|
|
317
|
+
return "ask";
|
|
318
|
+
if (["succeeded", "clean_no_op", "exhausted", "stagnated", "retired"].includes(status))
|
|
319
|
+
return "stop";
|
|
320
|
+
return "continue";
|
|
321
|
+
}
|
|
322
|
+
export function renderLoopStatus(packetDir) {
|
|
323
|
+
const { loops } = loadLoopContracts(packetDir);
|
|
324
|
+
return loops.map((loop) => ({
|
|
325
|
+
loop: loop.id,
|
|
326
|
+
title: loop.title,
|
|
327
|
+
owner: loop.owner,
|
|
328
|
+
state: loop.status,
|
|
329
|
+
why_it_exists: loop.goal,
|
|
330
|
+
current_evidence: {
|
|
331
|
+
sources: loop.observe.sources,
|
|
332
|
+
last_events: loop.refs.events,
|
|
333
|
+
},
|
|
334
|
+
next_action: nextAction(loop.status),
|
|
335
|
+
approval_boundary: loop.ask_user_when,
|
|
336
|
+
trail: loop.record.artifact,
|
|
337
|
+
// Surface budget only when authored, so status cards for pre-budget loops
|
|
338
|
+
// are unchanged.
|
|
339
|
+
...(loop.budget
|
|
340
|
+
? {
|
|
341
|
+
budget: {
|
|
342
|
+
unit: loop.budget.unit,
|
|
343
|
+
limit: loop.budget.limit,
|
|
344
|
+
agreed: loop.budget.agreed ?? null,
|
|
345
|
+
},
|
|
346
|
+
}
|
|
347
|
+
: {}),
|
|
348
|
+
}));
|
|
349
|
+
}
|
|
350
|
+
// Write an agreed budget back into a loop's entry in loops.yaml. The file is
|
|
351
|
+
// re-serialized from its parsed form; callers own when this runs (the
|
|
352
|
+
// dry-run-before-writes handshake in foo loop estimate --agree).
|
|
353
|
+
export function writeAgreedBudget(packetDir, loopId, budget) {
|
|
354
|
+
const loopsPath = path.join(packetDir, "loops.yaml");
|
|
355
|
+
if (!existsSync(loopsPath))
|
|
356
|
+
throw new NotFoundError("loops.yaml missing", "E_PACKET_NOT_FOUND");
|
|
357
|
+
const doc = asObject(parseYaml(readFileSync(loopsPath, "utf8")), "loops.yaml");
|
|
358
|
+
const loops = asArray(doc.loops, "loops.yaml.loops");
|
|
359
|
+
const target = loops.find((loop) => asObject(loop, "loops.yaml.loops[]").id === loopId);
|
|
360
|
+
if (!target)
|
|
361
|
+
throw new NotFoundError(`loop ${loopId} not found in loops.yaml`, "E_PACKET_NOT_FOUND");
|
|
362
|
+
target.budget = budget;
|
|
363
|
+
writeFileSync(loopsPath, stringifyYaml(doc));
|
|
364
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function machineStateDir(): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
// Machine-scoped state (event ledger, telemetry config, self-check scan cache)
|
|
4
|
+
// lives under the user's home, never the project dir — foo used to write
|
|
5
|
+
// telemetry INTO the user's git repo and nothing gitignored it (see the ledger
|
|
6
|
+
// history in src/commands/agent.ts). FOO_STATE_DIR overrides for tests.
|
|
7
|
+
export function machineStateDir() {
|
|
8
|
+
return process.env.FOO_STATE_DIR?.trim() || path.join(homedir(), ".foo");
|
|
9
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export type FetchLike = typeof globalThis.fetch;
|
|
2
|
+
export type NotionRuntimeErrorCode = "not_connected" | "auth_expired" | "access_denied" | "provider_failure" | "network" | "provider_timeout" | "content_too_large" | "budget_exhausted";
|
|
3
|
+
export declare class NotionRuntimeError extends Error {
|
|
4
|
+
readonly code: NotionRuntimeErrorCode;
|
|
5
|
+
constructor(code: NotionRuntimeErrorCode, message: string);
|
|
6
|
+
}
|
|
7
|
+
export type NotionConnection = {
|
|
8
|
+
connection_ref: string;
|
|
9
|
+
broker: "nango" | "direct";
|
|
10
|
+
credential: {
|
|
11
|
+
type: "oauth2";
|
|
12
|
+
access_token: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare const NOTION_POLICY: {
|
|
16
|
+
readonly policy_version: "notion-docs-0.1";
|
|
17
|
+
readonly max_response_bytes: number;
|
|
18
|
+
readonly fetch_timeout_ms: 90000;
|
|
19
|
+
readonly max_model_input_tokens: 100000;
|
|
20
|
+
readonly max_list_records: 50;
|
|
21
|
+
readonly min_request_interval_ms: 350;
|
|
22
|
+
};
|
|
23
|
+
export type NotionConnectionEnv = Record<string, string | undefined>;
|
|
24
|
+
export declare function notionConnectionConfigured(env: NotionConnectionEnv): boolean;
|
|
25
|
+
export declare function notionCredentialBroker(env: NotionConnectionEnv): "nango" | "direct" | null;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the company's Notion credential. Nango is the broker: it holds the
|
|
28
|
+
* OAuth credential, refreshes it, and is where an administrator revokes it.
|
|
29
|
+
* When Nango is not configured, an administrator-issued integration token set
|
|
30
|
+
* directly in this runtime's environment (FOO_NOTION_API_TOKEN) is accepted as
|
|
31
|
+
* the direct fallback — the same read-only surface, minus the central refresh
|
|
32
|
+
* and revoke story, and reported as `direct` so it is never mistaken for the
|
|
33
|
+
* company connection. Either way productfoo receives the credential for one operation
|
|
34
|
+
* and never persists or prints it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveNotionConnection(input: {
|
|
37
|
+
env: NotionConnectionEnv;
|
|
38
|
+
fetchImpl: FetchLike;
|
|
39
|
+
}): Promise<NotionConnection>;
|
|
40
|
+
export type NotionPageMeta = {
|
|
41
|
+
id: string;
|
|
42
|
+
title?: string;
|
|
43
|
+
last_edited_time?: string;
|
|
44
|
+
url?: string;
|
|
45
|
+
};
|
|
46
|
+
export type NotionBlock = {
|
|
47
|
+
text?: string;
|
|
48
|
+
has_children?: boolean;
|
|
49
|
+
};
|
|
50
|
+
type NotionClientOptions = {
|
|
51
|
+
fetchImpl: FetchLike;
|
|
52
|
+
baseUrl?: string;
|
|
53
|
+
sleep?: (ms: number) => Promise<void>;
|
|
54
|
+
now?: () => number;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Minimal read-only Notion REST client. Only the three documented read
|
|
58
|
+
* operations are reachable — page metadata, one page's block content, and a
|
|
59
|
+
* bounded database query. Any other method or path routes through the single
|
|
60
|
+
* write-refusal choke point (R-007), and the workspace-wide search endpoint is
|
|
61
|
+
* absent from the allow-list entirely (R-004).
|
|
62
|
+
*/
|
|
63
|
+
export declare class NotionClient {
|
|
64
|
+
private readonly credential;
|
|
65
|
+
private readonly options;
|
|
66
|
+
readonly requests: {
|
|
67
|
+
page: number;
|
|
68
|
+
content: number;
|
|
69
|
+
database: number;
|
|
70
|
+
};
|
|
71
|
+
private lastRequestAt;
|
|
72
|
+
private readonly baseUrl;
|
|
73
|
+
private readonly sleep;
|
|
74
|
+
private readonly now;
|
|
75
|
+
constructor(credential: NotionConnection["credential"], options: NotionClientOptions);
|
|
76
|
+
private request;
|
|
77
|
+
/** One page's metadata — title, last-edited time, canonical link. No content. */
|
|
78
|
+
page(pageId: string): Promise<NotionPageMeta>;
|
|
79
|
+
/**
|
|
80
|
+
* Exactly one page's content (R-005): the page's block children flattened to
|
|
81
|
+
* plain text, read once, bounded by the byte ceiling. The caller is the
|
|
82
|
+
* proof-gated summarize flow; no other path may call this.
|
|
83
|
+
*/
|
|
84
|
+
pageContent(pageId: string): Promise<string>;
|
|
85
|
+
/** Bounded metadata listing for one approved database selection. No content. */
|
|
86
|
+
queryDatabase(databaseId: string): Promise<Array<{
|
|
87
|
+
id: string;
|
|
88
|
+
title?: string;
|
|
89
|
+
last_edited_time?: string;
|
|
90
|
+
url?: string;
|
|
91
|
+
}>>;
|
|
92
|
+
}
|
|
93
|
+
export {};
|