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,61 @@
|
|
|
1
|
+
import { type ClientKind } from "../channelFreshness.js";
|
|
2
|
+
export declare function fetchLatestVersion(registry?: string, timeoutMs?: number): Promise<string | null>;
|
|
3
|
+
export type UpdateAction = "install" | "current" | "unreachable" | "repo";
|
|
4
|
+
export type UpdatePlan = {
|
|
5
|
+
action: UpdateAction;
|
|
6
|
+
running: string;
|
|
7
|
+
latest: string | null;
|
|
8
|
+
/** Exactly what an install would exec — argv, so a path with a space is not a bug. */
|
|
9
|
+
argv: string[] | null;
|
|
10
|
+
/** The same thing, as the line we show the user before running it. */
|
|
11
|
+
command: string | null;
|
|
12
|
+
};
|
|
13
|
+
export declare function installArgv(kind: ClientKind, latest: string, home?: string): string[] | null;
|
|
14
|
+
export declare function updatePlan(input: {
|
|
15
|
+
running: string;
|
|
16
|
+
latest: string | null;
|
|
17
|
+
kind: ClientKind;
|
|
18
|
+
home?: string;
|
|
19
|
+
}): UpdatePlan;
|
|
20
|
+
export type UpdateOutcome = {
|
|
21
|
+
output: string;
|
|
22
|
+
exitCode: number;
|
|
23
|
+
};
|
|
24
|
+
export type Installer = (argv: string[]) => {
|
|
25
|
+
status: number | null;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The offer — the LAST thing a run does, and the only place foo installs
|
|
29
|
+
* anything you did not ask it to install.
|
|
30
|
+
*
|
|
31
|
+
* The same two rules that shape the error-report ask, pulling the same two ways:
|
|
32
|
+
*
|
|
33
|
+
* Never interrupt the work. The question waits until the command has finished
|
|
34
|
+
* and printed everything it meant to print. foo learns about the new build
|
|
35
|
+
* mid-run (from a response to a request it was already making) and says
|
|
36
|
+
* nothing until the work is done.
|
|
37
|
+
*
|
|
38
|
+
* Never hang. Most productfoo runs are piped or agent-driven. A prompt with nobody
|
|
39
|
+
* there to answer is not a question, it is a deadlock — so productfoo asks only when
|
|
40
|
+
* a real human is on BOTH ends of the terminal, and otherwise prints the one
|
|
41
|
+
* line and gets out of the way.
|
|
42
|
+
*
|
|
43
|
+
* The answer defaults to no. Silence is not consent — least of all consent to
|
|
44
|
+
* write to an npm prefix. And a `no` is a real no: foo records that it spoke,
|
|
45
|
+
* and does not ask again about this build today.
|
|
46
|
+
*/
|
|
47
|
+
export declare function offerUpdate(io?: {
|
|
48
|
+
stdin?: NodeJS.ReadStream;
|
|
49
|
+
stderr?: NodeJS.WriteStream;
|
|
50
|
+
args?: string[];
|
|
51
|
+
install?: Installer;
|
|
52
|
+
kind?: ClientKind;
|
|
53
|
+
}): Promise<void>;
|
|
54
|
+
export declare function updateCommand(args?: string[], deps?: {
|
|
55
|
+
latest?: () => Promise<string | null>;
|
|
56
|
+
install?: Installer;
|
|
57
|
+
running?: string;
|
|
58
|
+
kind?: ClientKind;
|
|
59
|
+
isTTY?: boolean;
|
|
60
|
+
env?: NodeJS.ProcessEnv;
|
|
61
|
+
}): Promise<UpdateOutcome>;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// `foo update` — take the build foo just told you about.
|
|
2
|
+
//
|
|
3
|
+
// The notice (updateNotice.ts) says a new version exists. This is the other
|
|
4
|
+
// half: the thing you type when it does. Without it, "0.1.19 is available" ends
|
|
5
|
+
// in a paste — select a long npm string out of a scrollback, trust it, run it —
|
|
6
|
+
// and a tester who is mid-task simply doesn't, which is how someone sits on a
|
|
7
|
+
// stale build for days while being told, correctly, every morning.
|
|
8
|
+
//
|
|
9
|
+
// Four rules:
|
|
10
|
+
// - UPDATE THE COPY THAT ACTUALLY RUNS. There can be two `foo` installs on one
|
|
11
|
+
// machine: the global npm one, and the channel one under ~/.foo/versions/dist
|
|
12
|
+
// that the PATH shim (`~/.foo/bin/foo`) execs. They are different directories,
|
|
13
|
+
// and `npm install -g` moves only the first. This is not hypothetical — it is
|
|
14
|
+
// why the owner's `foo` sat on 0.1.18 while a global install of 0.1.20
|
|
15
|
+
// reported success: the command updated a copy she never ran. An update
|
|
16
|
+
// command that installs somewhere other than where you are running from is
|
|
17
|
+
// worse than none, because it lies. So the install target is derived from
|
|
18
|
+
// where THIS process is executing, never assumed.
|
|
19
|
+
// - Never update a repo checkout. `npm install` over a dev build would replace
|
|
20
|
+
// what the developer is editing with a published copy. A repo run refuses.
|
|
21
|
+
// - Pin the version npm named. A bare package spec installs whatever
|
|
22
|
+
// is newest at that instant — the same drift the retired Git channel had
|
|
23
|
+
// when bare `main` floated — while `pkg@0.1.20` installs the build the
|
|
24
|
+
// user was actually told about.
|
|
25
|
+
// - Say what it will run before it runs it. This mutates an npm prefix; that
|
|
26
|
+
// is not something to do behind a spinner.
|
|
27
|
+
import { spawnSync } from "node:child_process";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import { compareVersions, distributionPackageName, fooHome, runningClientKind } from "../channelFreshness.js";
|
|
30
|
+
import { CHANGELOG_URL, clientVersion, pendingUpdate, recordRelaySpoken, recordUpdateSpoken, } from "../updateNotice.js";
|
|
31
|
+
import { glyphsEnabled, withGlyph } from "../statusGlyphs.js";
|
|
32
|
+
// Public updates use npm package metadata. No productfoo account, token or hosted
|
|
33
|
+
// service participates; old private clients retain their existing server path.
|
|
34
|
+
const STABLE_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
35
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
36
|
+
// Match npm install's effective registry, including local npmrc settings.
|
|
37
|
+
// Config lookup is local; an absent npm executable falls back to public npm.
|
|
38
|
+
function npmRegistry(env, kind) {
|
|
39
|
+
const configured = env.npm_config_registry?.trim() || env.NPM_CONFIG_REGISTRY?.trim();
|
|
40
|
+
if (configured)
|
|
41
|
+
return configured;
|
|
42
|
+
const args = ["config", "get", "registry"];
|
|
43
|
+
if (kind === "global")
|
|
44
|
+
args.push("--global");
|
|
45
|
+
if (kind === "channel")
|
|
46
|
+
args.push("--prefix", path.join(fooHome(), "versions", "dist"));
|
|
47
|
+
const result = spawnSync("npm", args, { env, encoding: "utf8", timeout: 2_000 });
|
|
48
|
+
return result.status === 0 && result.stdout.trim() ? result.stdout.trim() : DEFAULT_REGISTRY;
|
|
49
|
+
}
|
|
50
|
+
export async function fetchLatestVersion(registry = npmRegistry(process.env), timeoutMs = 10_000) {
|
|
51
|
+
try {
|
|
52
|
+
const packageName = distributionPackageName().replace("/", "%2f");
|
|
53
|
+
const res = await fetch(`${registry.replace(/\/+$/, "")}/${packageName}/latest`, {
|
|
54
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok)
|
|
57
|
+
return null;
|
|
58
|
+
const body = await res.json();
|
|
59
|
+
return typeof body?.version === "string" && STABLE_VERSION.test(body.version) ? body.version : null;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Unknown stays unknown: an offline or malformed reply is never proof
|
|
63
|
+
// that the running copy is current.
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// What npm installs: the published package at the version npm named,
|
|
68
|
+
// resolved through the ONE distribution-target contract (channelFreshness.ts)
|
|
69
|
+
// — the package name is never hardcoded here, so the owner's final npm
|
|
70
|
+
// identity (or a later successor rename) flows through unchanged.
|
|
71
|
+
//
|
|
72
|
+
// Where the foo that is RUNNING lives decides what npm has to be pointed at to
|
|
73
|
+
// replace it: global — the ordinary `npm install -g` copy; channel — the copy
|
|
74
|
+
// under ~/.foo/versions/<channel> that the PATH shim execs, where `-g` would
|
|
75
|
+
// install past it and change nothing the user runs.
|
|
76
|
+
//
|
|
77
|
+
// `--prefix <abs path>`, not the `~/.foo/...` string the docs print: argv is not
|
|
78
|
+
// a shell, and nothing expands a tilde here.
|
|
79
|
+
export function installArgv(kind, latest, home = fooHome()) {
|
|
80
|
+
const pkg = `${distributionPackageName()}@${latest}`;
|
|
81
|
+
if (kind === "global")
|
|
82
|
+
return ["npm", "install", "-g", pkg];
|
|
83
|
+
if (kind === "channel")
|
|
84
|
+
return ["npm", "install", "--prefix", path.join(home, "versions", "dist"), pkg];
|
|
85
|
+
return null; // repo — nothing to install over
|
|
86
|
+
}
|
|
87
|
+
// Pure decision, no network and no npm. Every branch below is a test.
|
|
88
|
+
export function updatePlan(input) {
|
|
89
|
+
const { running, latest, kind } = input;
|
|
90
|
+
const none = { running, latest, argv: null, command: null };
|
|
91
|
+
if (kind === "repo")
|
|
92
|
+
return { action: "repo", ...none };
|
|
93
|
+
if (!latest || !STABLE_VERSION.test(latest))
|
|
94
|
+
return { action: "unreachable", ...none, latest: null };
|
|
95
|
+
if (compareVersions(running, latest) >= 0)
|
|
96
|
+
return { action: "current", ...none };
|
|
97
|
+
const argv = installArgv(kind, latest, input.home ?? fooHome());
|
|
98
|
+
return { action: "install", running, latest, argv, command: argv ? argv.join(" ") : null };
|
|
99
|
+
}
|
|
100
|
+
const npmInstaller = ([command, ...args]) => spawnSync(command, args, { stdio: "inherit" });
|
|
101
|
+
/**
|
|
102
|
+
* The offer — the LAST thing a run does, and the only place foo installs
|
|
103
|
+
* anything you did not ask it to install.
|
|
104
|
+
*
|
|
105
|
+
* The same two rules that shape the error-report ask, pulling the same two ways:
|
|
106
|
+
*
|
|
107
|
+
* Never interrupt the work. The question waits until the command has finished
|
|
108
|
+
* and printed everything it meant to print. foo learns about the new build
|
|
109
|
+
* mid-run (from a response to a request it was already making) and says
|
|
110
|
+
* nothing until the work is done.
|
|
111
|
+
*
|
|
112
|
+
* Never hang. Most productfoo runs are piped or agent-driven. A prompt with nobody
|
|
113
|
+
* there to answer is not a question, it is a deadlock — so productfoo asks only when
|
|
114
|
+
* a real human is on BOTH ends of the terminal, and otherwise prints the one
|
|
115
|
+
* line and gets out of the way.
|
|
116
|
+
*
|
|
117
|
+
* The answer defaults to no. Silence is not consent — least of all consent to
|
|
118
|
+
* write to an npm prefix. And a `no` is a real no: foo records that it spoke,
|
|
119
|
+
* and does not ask again about this build today.
|
|
120
|
+
*/
|
|
121
|
+
export async function offerUpdate(io = {}) {
|
|
122
|
+
try {
|
|
123
|
+
const p = pendingUpdate();
|
|
124
|
+
if (!p)
|
|
125
|
+
return;
|
|
126
|
+
const stdin = io.stdin ?? process.stdin;
|
|
127
|
+
const stderr = io.stderr ?? process.stderr;
|
|
128
|
+
const args = io.args ?? [];
|
|
129
|
+
const kind = io.kind ?? runningClientKind();
|
|
130
|
+
// The agent relay: a line for the coding agent that ran foo to surface to its
|
|
131
|
+
// human. Never under --json — it would corrupt the payload the agent is
|
|
132
|
+
// parsing — and when suppressed there, do NOT record, so a later non-json run
|
|
133
|
+
// in the same session still gets to relay (R-004).
|
|
134
|
+
if (p.audience === "agent") {
|
|
135
|
+
if (args.includes("--json"))
|
|
136
|
+
return;
|
|
137
|
+
stderr.write(`${p.line}\n`);
|
|
138
|
+
// Record into the agent's OWN session ration, never the human's day
|
|
139
|
+
// ration — otherwise a person who also runs foo by hand loses their
|
|
140
|
+
// interactive offer for the rest of the day (R-001 says it is unchanged).
|
|
141
|
+
recordRelaySpoken(p.latest);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Whatever happens below, this build has now been spoken about today. Record
|
|
145
|
+
// it FIRST: if the install throws, or the user ^C's out of the prompt, the
|
|
146
|
+
// one thing that must not happen is foo re-asking on their very next command.
|
|
147
|
+
recordUpdateSpoken(p.latest);
|
|
148
|
+
// Three reasons to state the fact and never ask:
|
|
149
|
+
// - nobody is on the other end (piped, hooked, agent-driven);
|
|
150
|
+
// - the run is emitting machine output, and a machine cannot answer;
|
|
151
|
+
// - it is a repo checkout, where there is nothing to install over anyway.
|
|
152
|
+
const canAsk = Boolean(stdin.isTTY) && Boolean(stderr.isTTY) && !args.includes("--json") && kind !== "repo";
|
|
153
|
+
if (!canAsk) {
|
|
154
|
+
stderr.write(`${p.line}\n`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const plan = updatePlan({ running: p.running, latest: p.latest, kind });
|
|
158
|
+
if (plan.action !== "install" || !plan.argv) {
|
|
159
|
+
stderr.write(`${p.line}\n`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const readline = await import("node:readline/promises");
|
|
163
|
+
const rl = readline.createInterface({ input: stdin, output: stderr });
|
|
164
|
+
try {
|
|
165
|
+
stderr.write(`\nfoo ${p.running} → ${p.latest} is available.\n`);
|
|
166
|
+
stderr.write(` What's new: ${CHANGELOG_URL}\n`);
|
|
167
|
+
// Say what the yes DOES. "Install now? [y/N]" hides the fact that a key is
|
|
168
|
+
// about to write to an npm prefix; someone on a locked-down machine needs
|
|
169
|
+
// to know which command they are authorizing BEFORE they press y.
|
|
170
|
+
stderr.write(` Installing runs: ${plan.command}\n`);
|
|
171
|
+
const answer = (await rl.question(" Install it now? [y/N] ")).trim().toLowerCase();
|
|
172
|
+
if (answer !== "y" && answer !== "yes") {
|
|
173
|
+
stderr.write(" Not installing. Run `foo update` whenever you want it.\n");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
stderr.write("\n");
|
|
177
|
+
const result = (io.install ?? npmInstaller)(plan.argv);
|
|
178
|
+
stderr.write(result.status === 0
|
|
179
|
+
? `\nfoo ${p.latest} is installed — it takes effect on your next command.\n`
|
|
180
|
+
: `\nThe install failed, so you are still on foo ${p.running}. npm's error is above; \`foo update\` retries.\n`);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
rl.close();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// An update offer may never change what the command was asked to do, and may
|
|
188
|
+
// never be the reason a run fails.
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
export async function updateCommand(args = [], deps = {}) {
|
|
192
|
+
const env = deps.env ?? process.env;
|
|
193
|
+
const asJson = args.includes("--json");
|
|
194
|
+
const checkOnly = args.includes("--check");
|
|
195
|
+
const glyphs = glyphsEnabled({ isTTY: deps.isTTY ?? Boolean(process.stdout.isTTY), env, args });
|
|
196
|
+
const running = deps.running ?? clientVersion();
|
|
197
|
+
const kind = deps.kind ?? runningClientKind();
|
|
198
|
+
const latest = await (deps.latest ?? (() => fetchLatestVersion(npmRegistry(env, kind))))();
|
|
199
|
+
const plan = updatePlan({ running, latest, kind });
|
|
200
|
+
if (asJson) {
|
|
201
|
+
return {
|
|
202
|
+
output: `${JSON.stringify({ running: plan.running, latest: plan.latest, state: plan.action, command: plan.command }, null, 2)}\n`,
|
|
203
|
+
exitCode: plan.action === "unreachable" ? 1 : 0,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
switch (plan.action) {
|
|
207
|
+
case "repo":
|
|
208
|
+
// The owner, in the checkout. Nothing to install over, and saying "you are
|
|
209
|
+
// current" would be meaningless — the repo build is whatever they last
|
|
210
|
+
// edited. Give them the one fact they might actually want.
|
|
211
|
+
return {
|
|
212
|
+
output: `foo ${plan.running} — you're running the repo build, so there is nothing to install over it.${plan.latest ? ` The published release is ${plan.latest}.` : ""}\n`,
|
|
213
|
+
exitCode: 0,
|
|
214
|
+
};
|
|
215
|
+
case "unreachable":
|
|
216
|
+
return {
|
|
217
|
+
output: withGlyph("blocked", `blocked — couldn't get a valid latest version from npm, so we can't tell whether ${plan.running} is current. Your install is unchanged; retry with foo update --check.\n`, glyphs),
|
|
218
|
+
exitCode: 1,
|
|
219
|
+
};
|
|
220
|
+
case "current":
|
|
221
|
+
return { output: withGlyph("proven", `proven — foo ${plan.running} is the newest published build.\n`, glyphs), exitCode: 0 };
|
|
222
|
+
case "install": {
|
|
223
|
+
const header = `foo ${plan.running} → ${plan.latest}\nWhat's new: ${CHANGELOG_URL}\n`;
|
|
224
|
+
if (checkOnly) {
|
|
225
|
+
// --check is what a script or an agent runs: report, never mutate.
|
|
226
|
+
return { output: `${header}Update with: foo update\n`, exitCode: 0 };
|
|
227
|
+
}
|
|
228
|
+
process.stdout.write(`${header}Running: ${plan.command}\n\n`);
|
|
229
|
+
const result = (deps.install ?? npmInstaller)(plan.argv);
|
|
230
|
+
if (result.status !== 0) {
|
|
231
|
+
// npm already printed its own failure to the inherited stderr; do not
|
|
232
|
+
// paraphrase it. Say only what the user needs: nothing changed.
|
|
233
|
+
return {
|
|
234
|
+
output: withGlyph("blocked", `\nblocked — the install failed, so you are still on foo ${plan.running}. npm's error is above.\n`, glyphs),
|
|
235
|
+
exitCode: 1,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
output: withGlyph("proven", `\nproven — foo ${plan.latest} is installed. Check it: foo --version\n`, glyphs),
|
|
240
|
+
exitCode: 0,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface TurnClassification {
|
|
2
|
+
miss: boolean;
|
|
3
|
+
matched: string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare function classifyTurn(text: string): TurnClassification;
|
|
6
|
+
export declare function observeTurn(root: string, turnText: string, at?: string): TurnClassification;
|
|
7
|
+
export interface MissRate {
|
|
8
|
+
turns: number;
|
|
9
|
+
misses: number;
|
|
10
|
+
rate: number | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function missRate(root: string): MissRate;
|
|
13
|
+
export interface LintFinding {
|
|
14
|
+
file: string;
|
|
15
|
+
term: string;
|
|
16
|
+
line: number;
|
|
17
|
+
text: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function lintShippedSurfaces(root: string): LintFinding[];
|
|
20
|
+
export declare function renderLint(findings: LintFinding[]): string;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// comprehension-loop-v0 — the comprehension scoreboard and the shipped-surface
|
|
2
|
+
// jargon lint. Both are SIGNALS, never gates (R-004): nothing here blocks,
|
|
3
|
+
// rewrites, or judges output with a model. The scoreboard records when the
|
|
4
|
+
// human had to ask for a translation; the lint reports foo-internal vocabulary
|
|
5
|
+
// on the surfaces that ship to a customer's agent context (commands/, skills/).
|
|
6
|
+
//
|
|
7
|
+
// Boundary, stated where a builder will read it (T-005): comprehension is a
|
|
8
|
+
// signal. Do not add a runtime gate that blocks or rewrites output based on a
|
|
9
|
+
// model's judgment of whether a human understood — that gates on fluency, the
|
|
10
|
+
// exact property that made the bad messages dangerous, and it retypes a
|
|
11
|
+
// surface that produced zero of the nineteen observed complaints. If you are
|
|
12
|
+
// tempted, read specs/comprehension-loop-v0/spec.md R-004 and D-001 first.
|
|
13
|
+
import { appendFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { PM_SURFACE_FORBIDDEN } from "./initCommand.js";
|
|
16
|
+
// --- the translation-request detector (R-001) --------------------------------
|
|
17
|
+
// Deterministic, no model call. Each pattern names the complaint shape it
|
|
18
|
+
// catches; precision is measured against evals/comprehension/, never asserted.
|
|
19
|
+
// Token boundaries are (?<![\w-]) / (?![\w-]) — the researchGate lesson: a
|
|
20
|
+
// substring test is how `cite` once matched "excited".
|
|
21
|
+
const MISS_PATTERNS = [
|
|
22
|
+
{ name: "in-english", re: /(?<![\w-])in (?:plain )?english(?![\w-])/i },
|
|
23
|
+
{ name: "plain-english", re: /(?<![\w-])plain english(?![\w-])/i },
|
|
24
|
+
{ name: "what-does-that-mean", re: /(?<![\w-])what does (?:that|this|it) mean(?![\w-])/i },
|
|
25
|
+
{ name: "translate-that", re: /(?<![\w-])translate (?:that|this|it)(?![\w-])/i },
|
|
26
|
+
{ name: "dont-understand", re: /(?<![\w-])(?:i )?don'?t understand what(?![\w-])/i },
|
|
27
|
+
];
|
|
28
|
+
export function classifyTurn(text) {
|
|
29
|
+
for (const p of MISS_PATTERNS) {
|
|
30
|
+
if (p.re.test(text))
|
|
31
|
+
return { miss: true, matched: p.name };
|
|
32
|
+
}
|
|
33
|
+
return { miss: false, matched: null };
|
|
34
|
+
}
|
|
35
|
+
// --- the local scoreboard (D-003: this machine only, no rewrite authority) ---
|
|
36
|
+
const EVENTS_DIR = path.join(".foo", "comprehension");
|
|
37
|
+
const EVENTS_FILE = "events.jsonl";
|
|
38
|
+
export function observeTurn(root, turnText, at = new Date().toISOString()) {
|
|
39
|
+
const result = classifyTurn(turnText);
|
|
40
|
+
const dir = path.join(root, EVENTS_DIR);
|
|
41
|
+
mkdirSync(dir, { recursive: true });
|
|
42
|
+
const event = {
|
|
43
|
+
schema_version: "0.1",
|
|
44
|
+
type: result.miss ? "comprehension_miss" : "turn_observed",
|
|
45
|
+
at,
|
|
46
|
+
};
|
|
47
|
+
if (result.miss) {
|
|
48
|
+
event.matched = result.matched;
|
|
49
|
+
// Enough of the turn to audit a false positive later; local-only (.foo/ is
|
|
50
|
+
// gitignored) and never shipped anywhere.
|
|
51
|
+
event.excerpt = turnText.slice(0, 160);
|
|
52
|
+
}
|
|
53
|
+
appendFileSync(path.join(dir, EVENTS_FILE), `${JSON.stringify(event)}\n`);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
export function missRate(root) {
|
|
57
|
+
const file = path.join(root, EVENTS_DIR, EVENTS_FILE);
|
|
58
|
+
if (!existsSync(file))
|
|
59
|
+
return { turns: 0, misses: 0, rate: null };
|
|
60
|
+
let turns = 0;
|
|
61
|
+
let misses = 0;
|
|
62
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
63
|
+
if (!line.trim())
|
|
64
|
+
continue;
|
|
65
|
+
try {
|
|
66
|
+
const event = JSON.parse(line);
|
|
67
|
+
if (event.type === "comprehension_miss") {
|
|
68
|
+
turns += 1;
|
|
69
|
+
misses += 1;
|
|
70
|
+
}
|
|
71
|
+
else if (event.type === "turn_observed") {
|
|
72
|
+
turns += 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// A corrupt line is a log problem, not a reason to fail the scoreboard.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { turns, misses, rate: turns === 0 ? null : misses / turns };
|
|
80
|
+
}
|
|
81
|
+
// --- the shipped-surface jargon lint (R-002; a signal, never a block) --------
|
|
82
|
+
// Legitimate internal uses go here, narrowest scope that works: a relative
|
|
83
|
+
// path prefix plus the term it may use. Keep this list short and honest — an
|
|
84
|
+
// allowlist that grows faster than the fixes is the lint being ignored.
|
|
85
|
+
const LINT_ALLOWLIST = [];
|
|
86
|
+
const LINT_ROOTS = ["commands", "skills"];
|
|
87
|
+
function lintFile(root, rel, findings) {
|
|
88
|
+
const text = readFileSync(path.join(root, rel), "utf8");
|
|
89
|
+
const lines = text.split("\n");
|
|
90
|
+
for (const term of PM_SURFACE_FORBIDDEN) {
|
|
91
|
+
if (LINT_ALLOWLIST.some((a) => rel.startsWith(a.pathPrefix) && a.term === term))
|
|
92
|
+
continue;
|
|
93
|
+
const re = new RegExp(`(?<![\\w-])${term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`, "i");
|
|
94
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
95
|
+
if (re.test(lines[i])) {
|
|
96
|
+
findings.push({ file: rel, term, line: i + 1, text: lines[i].trim().slice(0, 120) });
|
|
97
|
+
break; // one finding per (file, term) keeps the signal readable
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function walk(root, rel, acc) {
|
|
103
|
+
const full = path.join(root, rel);
|
|
104
|
+
if (!existsSync(full))
|
|
105
|
+
return;
|
|
106
|
+
// lstat, never stat: skills/ carries adapter symlinks (.codex/ links back
|
|
107
|
+
// into the repo) and following them walks a loop forever. Dot-entries are
|
|
108
|
+
// adapter surfaces, not canonical shipped files — skip both.
|
|
109
|
+
const stat = lstatSync(full);
|
|
110
|
+
if (stat.isSymbolicLink())
|
|
111
|
+
return;
|
|
112
|
+
if (stat.isFile()) {
|
|
113
|
+
if (rel.endsWith(".md"))
|
|
114
|
+
acc.push(rel);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (!stat.isDirectory())
|
|
118
|
+
return;
|
|
119
|
+
for (const entry of readdirSync(full).sort()) {
|
|
120
|
+
if (entry.startsWith("."))
|
|
121
|
+
continue;
|
|
122
|
+
walk(root, path.join(rel, entry), acc);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// comprehension-loop-v0 R-004 — the boundary, written where the next person to
|
|
126
|
+
// extend this lint will read it. Comprehension is a SIGNAL, never a gate. Do not
|
|
127
|
+
// give it a blocking verdict path and do not add a model-judged
|
|
128
|
+
// "did the human understand?" chokepoint: that gates on fluency, which is the exact
|
|
129
|
+
// property that made the observed bad messages dangerous. Signals over gates —
|
|
130
|
+
// a lint that blocks at this detector's precision is switched off within a week,
|
|
131
|
+
// and a signal that keeps its audience beats a gate that loses it.
|
|
132
|
+
export function lintShippedSurfaces(root) {
|
|
133
|
+
const files = [];
|
|
134
|
+
for (const dir of LINT_ROOTS)
|
|
135
|
+
walk(root, dir, files);
|
|
136
|
+
const findings = [];
|
|
137
|
+
for (const rel of files)
|
|
138
|
+
lintFile(root, rel, findings);
|
|
139
|
+
return findings;
|
|
140
|
+
}
|
|
141
|
+
const LINT_DETAIL_CAP = 20;
|
|
142
|
+
export function renderLint(findings) {
|
|
143
|
+
if (findings.length === 0)
|
|
144
|
+
return "shipped surfaces carry no flagged internal vocabulary\n";
|
|
145
|
+
// A 3,000-line dump is noise wearing a signal's clothes — summarize by file,
|
|
146
|
+
// show the worst offenders in detail, and NAME what was dropped (no silent
|
|
147
|
+
// caps): the full list stays one --json away.
|
|
148
|
+
const byFile = new Map();
|
|
149
|
+
for (const f of findings)
|
|
150
|
+
byFile.set(f.file, (byFile.get(f.file) ?? 0) + 1);
|
|
151
|
+
const worst = [...byFile.entries()].sort((a, b) => b[1] - a[1]);
|
|
152
|
+
const lines = [
|
|
153
|
+
`signal: ${findings.length} internal-vocabulary use(s) across ${byFile.size} shipped file(s) (commands/, skills/) — advisory, never a build failure.`,
|
|
154
|
+
`Worst files first:`,
|
|
155
|
+
];
|
|
156
|
+
for (const [file, count] of worst.slice(0, 10))
|
|
157
|
+
lines.push(` ${file} — ${count} term(s)`);
|
|
158
|
+
lines.push(`First ${Math.min(LINT_DETAIL_CAP, findings.length)} finding(s) in detail:`);
|
|
159
|
+
for (const f of findings.slice(0, LINT_DETAIL_CAP))
|
|
160
|
+
lines.push(` ${f.file}:${f.line} says "${f.term}" — ${f.text}`);
|
|
161
|
+
if (findings.length > LINT_DETAIL_CAP) {
|
|
162
|
+
lines.push(` …and ${findings.length - LINT_DETAIL_CAP} more — run \`foo comprehension lint --json\` for the full list.`);
|
|
163
|
+
}
|
|
164
|
+
lines.push("Fix by saying it in the reader's words, or add the narrowest allowlist entry with a reason.");
|
|
165
|
+
return `${lines.join("\n")}\n`;
|
|
166
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface IdeaObject {
|
|
2
|
+
id: string;
|
|
3
|
+
claim: string;
|
|
4
|
+
frame: string;
|
|
5
|
+
lens: string;
|
|
6
|
+
reasoning: string;
|
|
7
|
+
mechanism: string;
|
|
8
|
+
assumption: string;
|
|
9
|
+
novelty: string;
|
|
10
|
+
claims_relied_on: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface DiversityMeter {
|
|
13
|
+
raw: number;
|
|
14
|
+
distinct_clusters: number;
|
|
15
|
+
near_dupes_merged: number;
|
|
16
|
+
exhausted: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface DiversityComputation extends DiversityMeter {
|
|
19
|
+
clusters: string[][];
|
|
20
|
+
}
|
|
21
|
+
export declare const CLUSTERER_ID = "idf-cosine-v3-moves";
|
|
22
|
+
export declare const NEAR_DUPE_THRESHOLD = 0.25;
|
|
23
|
+
export declare function featureSet(idea: IdeaObject): Map<string, number>;
|
|
24
|
+
export declare function similarityIn(a: IdeaObject, b: IdeaObject, pool: IdeaObject[]): number;
|
|
25
|
+
export declare function similarity(a: IdeaObject, b: IdeaObject): number;
|
|
26
|
+
export declare function computeDiversity(ideas: IdeaObject[], exhausted?: boolean): DiversityComputation;
|
|
27
|
+
export declare const IDEAS_FENCE_OPEN = "```json ideas";
|
|
28
|
+
export interface IdeaParseResult {
|
|
29
|
+
ideas: IdeaObject[];
|
|
30
|
+
errors: string[];
|
|
31
|
+
present: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function parseIdeaObjects(recordText: string): IdeaParseResult;
|
|
34
|
+
export declare const DIVERSITY_METER_LINE: RegExp;
|
|
35
|
+
export declare function readDeclaredMeter(recordText: string): DiversityMeter | null;
|
|
36
|
+
export type DiversityMethod = "measured" | "estimated";
|
|
37
|
+
export declare function readDiversityMethod(recordText: string): {
|
|
38
|
+
value: DiversityMethod;
|
|
39
|
+
declared: boolean;
|
|
40
|
+
} | null;
|
|
41
|
+
export declare function readClustererId(recordText: string): string | null;
|
|
42
|
+
export declare function clustererLine(): string;
|
|
43
|
+
export declare function meterLine(meter: DiversityMeter): string;
|
|
44
|
+
export declare const IDEATE_CONTRACT_VERSION = "frames-v1";
|
|
45
|
+
export declare function ideateFrontmatter(record: string): Record<string, unknown> | null;
|
|
46
|
+
export declare function ideateContractVersion(record: string): string | null;
|
|
47
|
+
export declare function hasExecutableFramesContract(recordText: string): boolean;
|
|
48
|
+
export declare function frameReasoningOffenders(ideas: IdeaObject[]): string[];
|