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,334 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, appendFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import readline from "node:readline/promises";
|
|
5
|
+
import { agentLogPath, nextAgentEventId, readAgentTelemetryConfig, resolveTesterId } from "./commands/agent.js";
|
|
6
|
+
import { readJson } from "./jsonFile.js";
|
|
7
|
+
import { fileStatKey, readMemoized, writeMemoized } from "./processMemo.js";
|
|
8
|
+
// Roles that mean "lead with the product view": people who think in outcomes,
|
|
9
|
+
// agreements, and proof before code. Engineers and designers keep the repo
|
|
10
|
+
// read first (their raw surfaces are the point).
|
|
11
|
+
export const PRODUCT_VIEW_ROLES = ["product", "founder", "executive"];
|
|
12
|
+
// machine-wide-profile-v0: how you work is a fact about YOU, not about a repo —
|
|
13
|
+
// the same reasoning that already puts the tester id under the home dir (one
|
|
14
|
+
// human, one identity). So the profile's home is machine-wide by default:
|
|
15
|
+
// answer once, and every project foo touches leads with your view. A per-repo
|
|
16
|
+
// file is still honored as a deliberate override when someone wants a different
|
|
17
|
+
// lens in one repo. FOO_STATE_DIR overrides the home for tests (mirrors the
|
|
18
|
+
// tester-id resolution exactly).
|
|
19
|
+
function machineStateDir() {
|
|
20
|
+
return process.env.FOO_STATE_DIR?.trim() || path.join(homedir(), ".foo");
|
|
21
|
+
}
|
|
22
|
+
export function machineProfilePath() {
|
|
23
|
+
return path.join(machineStateDir(), "profile.json");
|
|
24
|
+
}
|
|
25
|
+
// The per-repo override path. Not written by the survey (which is about the
|
|
26
|
+
// person); present only when someone deliberately wants a repo-specific lens.
|
|
27
|
+
export function profilePath(root) {
|
|
28
|
+
return path.join(root, ".foo", "profile.json");
|
|
29
|
+
}
|
|
30
|
+
function readProfileFile(file) {
|
|
31
|
+
try {
|
|
32
|
+
if (!existsSync(file))
|
|
33
|
+
return null;
|
|
34
|
+
const parsed = readJson(file);
|
|
35
|
+
if (!parsed || typeof parsed !== "object")
|
|
36
|
+
return null;
|
|
37
|
+
// v1 files carried a single `role`; fold it into `roles` so nobody's
|
|
38
|
+
// first answer is lost to a schema change.
|
|
39
|
+
if (!parsed.roles && parsed.role)
|
|
40
|
+
parsed.roles = [parsed.role];
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null; // a corrupt profile never breaks a read (R-005)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Precedence: a repo override wins over the machine-wide profile. This is the
|
|
48
|
+
// same shape as telemetry (per-repo file can override the machine default).
|
|
49
|
+
// Memoized per process (cli_performance-v0 R-002): profile reads happen on
|
|
50
|
+
// several surfaces per invocation; the (mtime, size) key keeps an external
|
|
51
|
+
// write — a test fixture, `foo profile redo` from another process — honest.
|
|
52
|
+
const profileCache = new Map();
|
|
53
|
+
export function loadProfile(root) {
|
|
54
|
+
const repoFile = profilePath(root);
|
|
55
|
+
const machineFile = machineProfilePath();
|
|
56
|
+
const key = `${fileStatKey(repoFile)}|${fileStatKey(machineFile)}`;
|
|
57
|
+
const cached = readMemoized(profileCache, root, key);
|
|
58
|
+
if (cached !== undefined)
|
|
59
|
+
return cached;
|
|
60
|
+
const value = readProfileFile(repoFile) ?? readProfileFile(machineFile);
|
|
61
|
+
writeMemoized(profileCache, root, key, value);
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
// Where the survey and `foo profile redo` write: machine-wide, so the answer
|
|
65
|
+
// follows the person across every project. `scope: "repo"` writes the override.
|
|
66
|
+
export function saveProfile(profile, opts) {
|
|
67
|
+
const file = opts?.scope === "repo" && opts.root ? profilePath(opts.root) : machineProfilePath();
|
|
68
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
69
|
+
writeFileSync(file, `${JSON.stringify(profile, null, 2)}\n`);
|
|
70
|
+
// The machine file feeds every root's merge; a save is rare, so clearing the
|
|
71
|
+
// whole per-process memo is the simple honest invalidation.
|
|
72
|
+
profileCache.clear();
|
|
73
|
+
}
|
|
74
|
+
// The survey corpus (R-002): every user-visible survey string, exported as one
|
|
75
|
+
// constant so the language eval scans exactly what renders — same contract as
|
|
76
|
+
// FIRST_RUN_OPENER. productfoo voice: productfoo reads you the way it reads the project,
|
|
77
|
+
// options teach what the answer changes, and nothing sounds like a form.
|
|
78
|
+
export const SURVEY_INTRO = "It also reads you — a few quick questions so the work fits how you work. Enter skips any of them.";
|
|
79
|
+
export const SURVEY_Q_ROLE = "What's your role? Pick any — this chooses the view productfoo leads with. 1 product · 2 engineer · 3 designer · 4 founder · 5 executive";
|
|
80
|
+
export const SURVEY_Q_HARNESS = "Which coding agents do you build with? Pick any — productfoo offers to set up its commands in these. 1 Claude Code · 2 Cursor · 3 Codex · or type them";
|
|
81
|
+
export const SURVEY_Q_BUILDING = "What are you building? A few words is plenty — this stays on your machine.";
|
|
82
|
+
export const SURVEY_Q_TEAM = "Who's building with you? 1 just me · 2 a small team · 3 a bigger org";
|
|
83
|
+
export const SURVEY_CLOSE_PRODUCT = "productfoo leads with the product view from here — no flags needed. Redo any time: foo profile";
|
|
84
|
+
export const SURVEY_CLOSE_SAVED = "Saved — productfoo keeps this on your machine. Redo any time: foo profile";
|
|
85
|
+
export const SURVEY_CLOSE_SKIPPED = "Nothing saved — productfoo stays as-is. Ask again any time: foo profile";
|
|
86
|
+
export const SURVEY_DIDNT_CATCH = "didn't catch";
|
|
87
|
+
export const PROFILE_NONE = "foo hasn't read you yet. Run it any time: foo profile redo";
|
|
88
|
+
export const PROFILE_REDO_NEEDS_TTY = "Redo needs a live terminal — run foo profile redo where you can type.";
|
|
89
|
+
export const SURVEY_CORPUS = [
|
|
90
|
+
SURVEY_INTRO,
|
|
91
|
+
SURVEY_Q_ROLE,
|
|
92
|
+
SURVEY_Q_HARNESS,
|
|
93
|
+
SURVEY_Q_BUILDING,
|
|
94
|
+
SURVEY_Q_TEAM,
|
|
95
|
+
SURVEY_CLOSE_PRODUCT,
|
|
96
|
+
SURVEY_CLOSE_SAVED,
|
|
97
|
+
SURVEY_CLOSE_SKIPPED,
|
|
98
|
+
SURVEY_DIDNT_CATCH,
|
|
99
|
+
PROFILE_NONE,
|
|
100
|
+
PROFILE_REDO_NEEDS_TTY,
|
|
101
|
+
];
|
|
102
|
+
const ROLE_BY_DIGIT = { "1": "product", "2": "engineer", "3": "designer", "4": "founder", "5": "executive" };
|
|
103
|
+
const ROLE_LABELS = { product: "product", engineer: "engineer", designer: "designer", founder: "founder", executive: "executive" };
|
|
104
|
+
const HARNESS_BY_DIGIT = { "1": "claude-code", "2": "cursor", "3": "codex" };
|
|
105
|
+
const HARNESS_LABELS = { "claude-code": "Claude Code", cursor: "Cursor", codex: "Codex" };
|
|
106
|
+
const TEAM_BY_DIGIT = { "1": "just-me", "2": "small-team", "3": "larger-org" };
|
|
107
|
+
const TEAM_LABELS = { "just-me": "just you", "small-team": "a small team", "larger-org": "a bigger org" };
|
|
108
|
+
// Lenient on purpose (T-004): "1, 3", "1 and 3", "product + agent", "pm" all
|
|
109
|
+
// land. Digits and words both count; order kept; duplicates dropped.
|
|
110
|
+
export function parseRoles(answer) {
|
|
111
|
+
const roles = [];
|
|
112
|
+
const push = (role) => {
|
|
113
|
+
if (role && !roles.includes(role))
|
|
114
|
+
roles.push(role);
|
|
115
|
+
};
|
|
116
|
+
for (const ch of answer)
|
|
117
|
+
push(ROLE_BY_DIGIT[ch]);
|
|
118
|
+
const lower = answer.toLowerCase();
|
|
119
|
+
if (/\b(product|pm)/.test(lower))
|
|
120
|
+
push("product");
|
|
121
|
+
if (/\b(eng|dev|code)/.test(lower))
|
|
122
|
+
push("engineer");
|
|
123
|
+
if (/\bdesign/.test(lower))
|
|
124
|
+
push("designer");
|
|
125
|
+
if (/\bfound/.test(lower))
|
|
126
|
+
push("founder");
|
|
127
|
+
if (/\b(exec|ceo|cpo|cto|vp)/.test(lower))
|
|
128
|
+
push("executive");
|
|
129
|
+
return roles;
|
|
130
|
+
}
|
|
131
|
+
export function parseTeam(answer) {
|
|
132
|
+
const direct = TEAM_BY_DIGIT[answer.trim()];
|
|
133
|
+
if (direct)
|
|
134
|
+
return direct;
|
|
135
|
+
const lower = answer.toLowerCase();
|
|
136
|
+
if (/\b(just me|solo|alone|me)\b/.test(lower))
|
|
137
|
+
return "just-me";
|
|
138
|
+
if (/\bsmall\b/.test(lower))
|
|
139
|
+
return "small-team";
|
|
140
|
+
if (/\b(big|large|org|company)/.test(lower))
|
|
141
|
+
return "larger-org";
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
// "Which do you build with MOST" — people lead with their main tool, so the
|
|
145
|
+
// first recognized mention (digit or name) wins: "1, sometimes 3" is Claude
|
|
146
|
+
// Code, "2 mostly, codex rarely" is Cursor. (Dogfood 2026-07-11: the digit was
|
|
147
|
+
// only matched against the whole answer, and the owner's pick was missed.)
|
|
148
|
+
const HARNESS_NAMES = { claude: "claude-code", cursor: "cursor", codex: "codex" };
|
|
149
|
+
// #622: plural, like roles. "1, 3", "claude and codex", "cursor + claude" all
|
|
150
|
+
// land, in the order given; duplicates dropped. With no known agent named, a
|
|
151
|
+
// free-typed answer is kept as the single value (it is still the person's word).
|
|
152
|
+
export function parseHarnesses(answer) {
|
|
153
|
+
const lower = answer.toLowerCase();
|
|
154
|
+
const hits = [];
|
|
155
|
+
for (const [name, id] of Object.entries(HARNESS_NAMES)) {
|
|
156
|
+
for (let at = lower.indexOf(name); at !== -1; at = lower.indexOf(name, at + 1))
|
|
157
|
+
hits.push({ at, id });
|
|
158
|
+
}
|
|
159
|
+
for (const [digit, id] of Object.entries(HARNESS_BY_DIGIT)) {
|
|
160
|
+
for (let at = answer.indexOf(digit); at !== -1; at = answer.indexOf(digit, at + 1))
|
|
161
|
+
hits.push({ at, id });
|
|
162
|
+
}
|
|
163
|
+
const ids = [];
|
|
164
|
+
for (const { id } of hits.sort((a, b) => a.at - b.at))
|
|
165
|
+
if (!ids.includes(id))
|
|
166
|
+
ids.push(id);
|
|
167
|
+
if (ids.length > 0)
|
|
168
|
+
return ids;
|
|
169
|
+
const typed = answer.trim();
|
|
170
|
+
return typed ? [typed] : [];
|
|
171
|
+
}
|
|
172
|
+
export function parseHarness(answer) {
|
|
173
|
+
return parseHarnesses(answer)[0];
|
|
174
|
+
}
|
|
175
|
+
/** Every harness a profile names, from the plural field or an older single one. */
|
|
176
|
+
export function profileHarnesses(profile) {
|
|
177
|
+
if (profile?.harnesses?.length)
|
|
178
|
+
return [...profile.harnesses];
|
|
179
|
+
return profile?.harness ? [profile.harness] : [];
|
|
180
|
+
}
|
|
181
|
+
export function harnessLabel(id) {
|
|
182
|
+
return HARNESS_LABELS[id] ?? id;
|
|
183
|
+
}
|
|
184
|
+
// The read-back (T-004): foo answers the survey the way it answers a repo —
|
|
185
|
+
// one Read: line naming exactly what it understood. This is the legibility
|
|
186
|
+
// beat v1 was missing: a dropped answer becomes visible the moment it happens.
|
|
187
|
+
export function renderProfileReadback(profile) {
|
|
188
|
+
const parts = [];
|
|
189
|
+
if (profile.roles?.length)
|
|
190
|
+
parts.push(profile.roles.map((role) => ROLE_LABELS[role]).join(" + "));
|
|
191
|
+
const harnesses = profileHarnesses(profile);
|
|
192
|
+
if (harnesses.length)
|
|
193
|
+
parts.push(harnesses.map(harnessLabel).join(" + "));
|
|
194
|
+
if (profile.building)
|
|
195
|
+
parts.push(`building ${profile.building}`);
|
|
196
|
+
if (profile.team)
|
|
197
|
+
parts.push(TEAM_LABELS[profile.team] ?? profile.team);
|
|
198
|
+
return parts.length ? `Read: ${parts.join(" · ")}.` : "";
|
|
199
|
+
}
|
|
200
|
+
export function surveyClose(profile) {
|
|
201
|
+
const answered = Boolean(profile.roles?.length || profile.harness || profile.building || profile.team);
|
|
202
|
+
if (profile.roles?.some((role) => PRODUCT_VIEW_ROLES.includes(role)))
|
|
203
|
+
return SURVEY_CLOSE_PRODUCT;
|
|
204
|
+
return answered ? SURVEY_CLOSE_SAVED : SURVEY_CLOSE_SKIPPED;
|
|
205
|
+
}
|
|
206
|
+
// Ask four optional questions. Answers remain on this machine.
|
|
207
|
+
// Every answer is optional: Enter moves on, an answer productfoo can't place is named
|
|
208
|
+
// out loud — `(didn't catch "…" — skipped)` — never silently dropped (T-004),
|
|
209
|
+
// and the survey never nags (R-002/R-005). Streams are injected for tests.
|
|
210
|
+
export async function runFirstRunSurvey(io, now = () => new Date().toISOString()) {
|
|
211
|
+
const rl = readline.createInterface({ input: io.input, output: io.output });
|
|
212
|
+
const ask = async (question) => (await rl.question(`${question}\n> `)).trim();
|
|
213
|
+
const missed = (answer) => io.output.write(`(${SURVEY_DIDNT_CATCH} "${answer}" — skipped)\n`);
|
|
214
|
+
try {
|
|
215
|
+
io.output.write(`${SURVEY_INTRO}\n\n`);
|
|
216
|
+
const roleAnswer = await ask(SURVEY_Q_ROLE);
|
|
217
|
+
const roles = parseRoles(roleAnswer);
|
|
218
|
+
if (roleAnswer && roles.length === 0)
|
|
219
|
+
missed(roleAnswer);
|
|
220
|
+
const harnesses = parseHarnesses(await ask(SURVEY_Q_HARNESS));
|
|
221
|
+
const building = (await ask(SURVEY_Q_BUILDING)) || undefined;
|
|
222
|
+
const teamAnswer = await ask(SURVEY_Q_TEAM);
|
|
223
|
+
const team = parseTeam(teamAnswer);
|
|
224
|
+
if (teamAnswer && !team)
|
|
225
|
+
missed(teamAnswer);
|
|
226
|
+
const profile = {
|
|
227
|
+
...(roles.length ? { roles } : {}),
|
|
228
|
+
...(harnesses.length ? { harnesses, harness: harnesses[0] } : {}),
|
|
229
|
+
...(building ? { building } : {}),
|
|
230
|
+
...(team ? { team } : {}),
|
|
231
|
+
answered_at: now(),
|
|
232
|
+
};
|
|
233
|
+
const readback = renderProfileReadback(profile);
|
|
234
|
+
if (readback)
|
|
235
|
+
io.output.write(`${readback}\n`);
|
|
236
|
+
io.output.write(`${surveyClose(profile)}\n\n`);
|
|
237
|
+
return profile;
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
rl.close();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// The report (R-004, amended by onboarding-trust-boundary-v0): picks only. A
|
|
244
|
+
// free-typed harness reports as "other" so no free text ever rides along; the
|
|
245
|
+
// `building` answer is structurally absent — the event is built from an
|
|
246
|
+
// allowlist, not by filtering the profile.
|
|
247
|
+
export function profileReportEvent(profile, version) {
|
|
248
|
+
if (!profile.roles?.length && !profile.harness && !profile.team)
|
|
249
|
+
return null;
|
|
250
|
+
return {
|
|
251
|
+
schema_version: "0.1",
|
|
252
|
+
event_type: "profile",
|
|
253
|
+
id: nextAgentEventId(),
|
|
254
|
+
timestamp: profile.answered_at,
|
|
255
|
+
version,
|
|
256
|
+
...(profile.roles?.length ? { roles: profile.roles } : {}),
|
|
257
|
+
...(profile.harness ? { harness: HARNESS_LABELS[profile.harness] ? profile.harness : "other" } : {}),
|
|
258
|
+
...(profile.team ? { team: profile.team } : {}),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// Record bounded profile picks locally; free text stays only in the profile.
|
|
262
|
+
export function reportProfile(root, profile, version, env = process.env) {
|
|
263
|
+
try {
|
|
264
|
+
const event = profileReportEvent(profile, version);
|
|
265
|
+
if (!event || env.DO_NOT_TRACK)
|
|
266
|
+
return undefined;
|
|
267
|
+
if (!readAgentTelemetryConfig(root).config.enabled)
|
|
268
|
+
return undefined;
|
|
269
|
+
const testerId = resolveTesterId(root);
|
|
270
|
+
const keyed = { ...event, ...(testerId ? { tester_id: testerId } : {}) };
|
|
271
|
+
const logPath = agentLogPath(root);
|
|
272
|
+
mkdirSync(path.dirname(logPath), { recursive: true });
|
|
273
|
+
appendFileSync(logPath, `${JSON.stringify(keyed)}\n`);
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return undefined; // reporting never alters the read (R-005)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// `foo profile` (T-004): what productfoo knows about how you work, in the same Read:
|
|
281
|
+
// grammar the survey closes with — plus where it lives and how to redo it.
|
|
282
|
+
export function renderProfileStatus(root) {
|
|
283
|
+
const profile = loadProfile(root);
|
|
284
|
+
if (!profile)
|
|
285
|
+
return `${PROFILE_NONE}\n`;
|
|
286
|
+
const lines = [];
|
|
287
|
+
const readback = renderProfileReadback(profile);
|
|
288
|
+
lines.push(readback || "Nothing answered yet.");
|
|
289
|
+
const overriding = readProfileFile(profilePath(root)) !== null;
|
|
290
|
+
lines.push(overriding
|
|
291
|
+
? " This repo has its own profile (.foo/profile.json); what you typed free-form never leaves the machine."
|
|
292
|
+
: " Lives on your machine and follows you across projects; what you typed free-form never leaves it.");
|
|
293
|
+
lines.push(" Redo any time: foo profile redo");
|
|
294
|
+
return `${lines.join("\n")}\n`;
|
|
295
|
+
}
|
|
296
|
+
// `foo profile redo` (T-004): the recovery path v1 didn't have. Re-runs the
|
|
297
|
+
// same survey on a live terminal, saves, and records local profile picks.
|
|
298
|
+
export async function runProfileRedo(root, version, io) {
|
|
299
|
+
const input = io?.input ?? process.stdin;
|
|
300
|
+
const output = io?.output ?? process.stdout;
|
|
301
|
+
const inputTTY = io?.inputTTY ?? Boolean(process.stdin.isTTY);
|
|
302
|
+
const outputTTY = io?.outputTTY ?? Boolean(process.stdout.isTTY);
|
|
303
|
+
if (!inputTTY || !outputTTY)
|
|
304
|
+
return `${PROFILE_REDO_NEEDS_TTY}\n`;
|
|
305
|
+
const profile = await runFirstRunSurvey({ input, output });
|
|
306
|
+
saveProfile(profile); // machine-wide: the answer follows the person
|
|
307
|
+
void reportProfile(root, profile, version);
|
|
308
|
+
return "";
|
|
309
|
+
}
|
|
310
|
+
export const PROFILE_SET_EMPTY = "Nothing to set — give at least one of --role, --harness, --team, --building.";
|
|
311
|
+
export function runProfileSet(root, version, input, now = () => new Date().toISOString()) {
|
|
312
|
+
const roles = input.roles ? parseRoles(input.roles) : [];
|
|
313
|
+
const harnesses = input.harness ? parseHarnesses(input.harness) : [];
|
|
314
|
+
const team = input.team ? parseTeam(input.team) : undefined;
|
|
315
|
+
const building = input.building?.trim() || undefined;
|
|
316
|
+
if (!roles.length && !harnesses.length && !building && !team)
|
|
317
|
+
throw new Error(PROFILE_SET_EMPTY);
|
|
318
|
+
const profile = {
|
|
319
|
+
...(roles.length ? { roles } : {}),
|
|
320
|
+
...(harnesses.length ? { harnesses, harness: harnesses[0] } : {}),
|
|
321
|
+
...(building ? { building } : {}),
|
|
322
|
+
...(team ? { team } : {}),
|
|
323
|
+
answered_at: now(),
|
|
324
|
+
};
|
|
325
|
+
saveProfile(profile); // machine-wide: the profile is about the person
|
|
326
|
+
void reportProfile(root, profile, version);
|
|
327
|
+
if (input.json) {
|
|
328
|
+
// The free text is deliberately absent from the machine-readable echo the
|
|
329
|
+
// agent parses — the surface never invites it back out.
|
|
330
|
+
return `${JSON.stringify({ roles: profile.roles ?? [], harnesses: profileHarnesses(profile), harness: profile.harness ?? null, team: profile.team ?? null }, null, 2)}\n`;
|
|
331
|
+
}
|
|
332
|
+
const readback = renderProfileReadback(profile);
|
|
333
|
+
return `${readback ? `${readback}\n` : ""}${surveyClose(profile)}\n`;
|
|
334
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type Confidence = "low" | "medium" | "high";
|
|
2
|
+
export type QuestionOption = {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
};
|
|
7
|
+
export type Judgment = {
|
|
8
|
+
belief: string;
|
|
9
|
+
evidence: string;
|
|
10
|
+
confidence: Confidence;
|
|
11
|
+
dissent?: string;
|
|
12
|
+
};
|
|
13
|
+
export type QuestionModel = {
|
|
14
|
+
header: string;
|
|
15
|
+
prompt: string;
|
|
16
|
+
options: QuestionOption[];
|
|
17
|
+
recommendedOptionId: string;
|
|
18
|
+
judgment: Judgment;
|
|
19
|
+
multiSelect: boolean;
|
|
20
|
+
};
|
|
21
|
+
export type QuestionInput = {
|
|
22
|
+
header: string;
|
|
23
|
+
prompt: string;
|
|
24
|
+
options: Array<{
|
|
25
|
+
id?: string;
|
|
26
|
+
label: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
}>;
|
|
29
|
+
recommendedOptionId?: string;
|
|
30
|
+
judgment: Judgment;
|
|
31
|
+
multiSelect?: boolean;
|
|
32
|
+
};
|
|
33
|
+
export type ProjectedOption = QuestionOption & {
|
|
34
|
+
number: number;
|
|
35
|
+
recommended: boolean;
|
|
36
|
+
};
|
|
37
|
+
export type ProjectedQuestion = {
|
|
38
|
+
header: string;
|
|
39
|
+
prompt: string;
|
|
40
|
+
options: ProjectedOption[];
|
|
41
|
+
recommendedOptionId: string;
|
|
42
|
+
judgment: Judgment;
|
|
43
|
+
multiSelect: boolean;
|
|
44
|
+
};
|
|
45
|
+
export declare function defineQuestion(input: QuestionInput): QuestionModel;
|
|
46
|
+
export declare function projectQuestion(model: QuestionModel): ProjectedQuestion;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Runtime-neutral question model for productfoo's branded question moment.
|
|
2
|
+
//
|
|
3
|
+
// Packet: specs/foo-branded-elicitation-v0 — T-002 (R-005 / R-006). One model, two
|
|
4
|
+
// renderers: the numbered-text renderer (T-003) and the branded widget renderer (T-004)
|
|
5
|
+
// both read this model and never hold their own option set or wording, so the surfaces
|
|
6
|
+
// cannot drift. The model carries productfoo's question contract — a recommendation (R-005's
|
|
7
|
+
// "recommend one"), numbered options, and the legible-judgment fields (belief, evidence,
|
|
8
|
+
// confidence, dissent) — so a judgment surface is never reduced to a bare menu.
|
|
9
|
+
const CONFIDENCE_VALUES = new Set(["low", "medium", "high"]);
|
|
10
|
+
function defaultId(index) {
|
|
11
|
+
return `opt-${index + 1}`;
|
|
12
|
+
}
|
|
13
|
+
function validateJudgment(judgment) {
|
|
14
|
+
if (!judgment.belief.trim())
|
|
15
|
+
throw new Error("judgment.belief is required");
|
|
16
|
+
if (!judgment.evidence.trim())
|
|
17
|
+
throw new Error("judgment.evidence is required");
|
|
18
|
+
if (!CONFIDENCE_VALUES.has(judgment.confidence)) {
|
|
19
|
+
throw new Error("judgment.confidence must be one of low|medium|high");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Build and validate a canonical QuestionModel. Throws on a malformed question so a
|
|
23
|
+
// drifted or bare-menu question fails fast rather than rendering something wrong.
|
|
24
|
+
export function defineQuestion(input) {
|
|
25
|
+
if (!input.header.trim())
|
|
26
|
+
throw new Error("question header is required");
|
|
27
|
+
if (!input.prompt.trim())
|
|
28
|
+
throw new Error("question prompt is required");
|
|
29
|
+
if (input.options.length < 2)
|
|
30
|
+
throw new Error("a question needs at least two options");
|
|
31
|
+
const options = input.options.map((option, index) => {
|
|
32
|
+
if (!option.label.trim())
|
|
33
|
+
throw new Error(`option ${index + 1} is missing a label`);
|
|
34
|
+
return { id: option.id?.trim() || defaultId(index), label: option.label, description: option.description };
|
|
35
|
+
});
|
|
36
|
+
const ids = options.map((option) => option.id);
|
|
37
|
+
if (new Set(ids).size !== ids.length)
|
|
38
|
+
throw new Error("option ids must be unique");
|
|
39
|
+
const recommendedOptionId = input.recommendedOptionId ?? options[0].id;
|
|
40
|
+
if (!ids.includes(recommendedOptionId)) {
|
|
41
|
+
throw new Error(`recommendedOptionId "${recommendedOptionId}" does not match any option`);
|
|
42
|
+
}
|
|
43
|
+
validateJudgment(input.judgment);
|
|
44
|
+
return {
|
|
45
|
+
header: input.header,
|
|
46
|
+
prompt: input.prompt,
|
|
47
|
+
options,
|
|
48
|
+
recommendedOptionId,
|
|
49
|
+
judgment: input.judgment,
|
|
50
|
+
multiSelect: input.multiSelect ?? false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Project the model into the render-agnostic shape both renderers consume: the
|
|
54
|
+
// recommended option first, then the rest in author order, each numbered 1..n.
|
|
55
|
+
export function projectQuestion(model) {
|
|
56
|
+
const recommended = model.options.find((option) => option.id === model.recommendedOptionId);
|
|
57
|
+
if (!recommended) {
|
|
58
|
+
throw new Error(`recommendedOptionId "${model.recommendedOptionId}" is not in options`);
|
|
59
|
+
}
|
|
60
|
+
const ordered = [recommended, ...model.options.filter((option) => option.id !== model.recommendedOptionId)];
|
|
61
|
+
const options = ordered.map((option, index) => ({
|
|
62
|
+
...option,
|
|
63
|
+
number: index + 1,
|
|
64
|
+
recommended: option.id === model.recommendedOptionId,
|
|
65
|
+
}));
|
|
66
|
+
return {
|
|
67
|
+
header: model.header,
|
|
68
|
+
prompt: model.prompt,
|
|
69
|
+
options,
|
|
70
|
+
recommendedOptionId: model.recommendedOptionId,
|
|
71
|
+
judgment: model.judgment,
|
|
72
|
+
multiSelect: model.multiSelect,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ElicitationSurface, SurfaceContext } from "./elicitationSurface.js";
|
|
2
|
+
import type { QuestionModel } from "./questionModel.js";
|
|
3
|
+
export declare function renderQuestionText(model: QuestionModel): string;
|
|
4
|
+
export declare function renderQuestion(model: QuestionModel, context?: SurfaceContext): {
|
|
5
|
+
surface: ElicitationSurface;
|
|
6
|
+
text: string;
|
|
7
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Numbered-text renderer for productfoo's branded question moment — the guaranteed fallback.
|
|
2
|
+
//
|
|
3
|
+
// Packet: specs/foo-branded-elicitation-v0 — T-003 (R-004 / R-005). Renders a
|
|
4
|
+
// QuestionModel as productfoo's canonical numbered text: the recommended option first and
|
|
5
|
+
// marked, every option numbered, the legible-judgment fields shown, and a reply-with-a-
|
|
6
|
+
// number prompt. This is the surface every runtime can render and a coding agent can
|
|
7
|
+
// read, so it is the fallback whenever the branded widget cannot render.
|
|
8
|
+
import { detectElicitationSurface } from "./elicitationSurface.js";
|
|
9
|
+
import { projectQuestion } from "./questionModel.js";
|
|
10
|
+
// Render the model as canonical numbered text. Pure: the same model always renders the
|
|
11
|
+
// same text, so the text path shows identical content whatever the widget could have
|
|
12
|
+
// done (R-004 / C-006).
|
|
13
|
+
export function renderQuestionText(model) {
|
|
14
|
+
const projected = projectQuestion(model);
|
|
15
|
+
const lines = [`${model.header} — ${model.prompt}`, ""];
|
|
16
|
+
for (const option of projected.options) {
|
|
17
|
+
lines.push(` ${option.number}. ${option.label}${option.recommended ? " (recommended)" : ""}`);
|
|
18
|
+
if (option.description)
|
|
19
|
+
lines.push(` ${option.description}`);
|
|
20
|
+
}
|
|
21
|
+
lines.push("");
|
|
22
|
+
lines.push(`Why this pick: ${model.judgment.belief}`);
|
|
23
|
+
lines.push(`Evidence: ${model.judgment.evidence}`);
|
|
24
|
+
lines.push(`Confidence: ${model.judgment.confidence}`);
|
|
25
|
+
if (model.judgment.dissent)
|
|
26
|
+
lines.push(`Dissent: ${model.judgment.dissent}`);
|
|
27
|
+
lines.push("");
|
|
28
|
+
const count = projected.options.length;
|
|
29
|
+
lines.push(model.multiSelect ? `Reply with one or more numbers (1-${count}).` : `Reply with a number (1-${count}).`);
|
|
30
|
+
return `${lines.join("\n")}\n`;
|
|
31
|
+
}
|
|
32
|
+
// Route a question to a surface and render it. The widget renderer lands in T-004; until
|
|
33
|
+
// then the text renderer is the guaranteed fallback for every surface, which is exactly
|
|
34
|
+
// the C-006 case (the widget is unavailable, so the text path renders identical content).
|
|
35
|
+
export function renderQuestion(model, context) {
|
|
36
|
+
const decision = detectElicitationSurface(context);
|
|
37
|
+
return { surface: decision.surface, text: renderQuestionText(model) };
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseAllowlist } from "./redactEngine.js";
|
|
4
|
+
// Repo-level redaction allowlist (OQ2): a human-curated `.foo-redact-allow` at
|
|
5
|
+
// the repo root, one literal value per line. Pairs with the inline
|
|
6
|
+
// `foo-redact: allow` pragma. R-007: human-authored only, never auto-populated.
|
|
7
|
+
export const REDACT_ALLOW_FILE = ".foo-redact-allow";
|
|
8
|
+
export function loadRepoAllowlist(root) {
|
|
9
|
+
const file = path.join(root, REDACT_ALLOW_FILE);
|
|
10
|
+
return existsSync(file) ? parseAllowlist(readFileSync(file, "utf8")) : [];
|
|
11
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type Confidence = "high" | "medium" | "low";
|
|
2
|
+
export type Finding = {
|
|
3
|
+
detector: string;
|
|
4
|
+
category: string;
|
|
5
|
+
confidence: Confidence;
|
|
6
|
+
line: number;
|
|
7
|
+
column: number;
|
|
8
|
+
length: number;
|
|
9
|
+
entropy?: number;
|
|
10
|
+
};
|
|
11
|
+
export type ScanResult = {
|
|
12
|
+
schema_version: "0.1";
|
|
13
|
+
findings: Finding[];
|
|
14
|
+
counts: {
|
|
15
|
+
high: number;
|
|
16
|
+
medium: number;
|
|
17
|
+
low: number;
|
|
18
|
+
total: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export type ScanOptions = {
|
|
22
|
+
allowlist?: string[];
|
|
23
|
+
allowlistPatterns?: RegExp[];
|
|
24
|
+
minEntropyLen?: number;
|
|
25
|
+
profiles?: string[];
|
|
26
|
+
};
|
|
27
|
+
export declare function shannonEntropy(value: string): number;
|
|
28
|
+
export declare const PII_PROFILE = "pii";
|
|
29
|
+
export declare const DEFAULT_PROFILES: string[];
|
|
30
|
+
export declare function knownProfiles(): string[];
|
|
31
|
+
export declare function scanText(text: string, options?: ScanOptions): ScanResult;
|
|
32
|
+
export declare function parseAllowlist(text: string): string[];
|
|
33
|
+
export type RedactThreshold = Confidence;
|
|
34
|
+
export type RedactionReceipt = {
|
|
35
|
+
schema_version: "0.1";
|
|
36
|
+
redactions: number;
|
|
37
|
+
by_type: Record<string, number>;
|
|
38
|
+
by_confidence: {
|
|
39
|
+
high: number;
|
|
40
|
+
medium: number;
|
|
41
|
+
low: number;
|
|
42
|
+
};
|
|
43
|
+
placeholders: string[];
|
|
44
|
+
kept: Array<{
|
|
45
|
+
detector: string;
|
|
46
|
+
confidence: Confidence;
|
|
47
|
+
line: number;
|
|
48
|
+
column: number;
|
|
49
|
+
reason: string;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
52
|
+
export type RedactionResult = {
|
|
53
|
+
redactedText: string;
|
|
54
|
+
receipt: RedactionReceipt;
|
|
55
|
+
};
|
|
56
|
+
export type RedactOptions = ScanOptions & {
|
|
57
|
+
threshold?: RedactThreshold;
|
|
58
|
+
};
|
|
59
|
+
export declare function redactText(text: string, options?: RedactOptions): RedactionResult;
|