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,405 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse } from "./vendorYaml.js";
|
|
4
|
+
export const LEARNING_ROUTING_POLICY = {
|
|
5
|
+
semantic_minimum: 0.45,
|
|
6
|
+
semantic_margin: 0.12,
|
|
7
|
+
catch_all_shared_max: 5,
|
|
8
|
+
synthesis_source_overlap_max: 0.82,
|
|
9
|
+
};
|
|
10
|
+
const STOPWORDS = new Set([
|
|
11
|
+
"about", "after", "again", "before", "being", "between", "could", "every",
|
|
12
|
+
"should", "their", "there", "these", "those", "through", "with", "without",
|
|
13
|
+
"would", "from", "into", "only", "that", "this", "when", "where", "which",
|
|
14
|
+
]);
|
|
15
|
+
// These are common product-label words, not distinctive concepts. They can
|
|
16
|
+
// still contribute to paragraph evidence, but never establish title custody.
|
|
17
|
+
const GENERIC_TITLE_TOKENS = new Set([
|
|
18
|
+
"agent", "business", "company", "control", "enterprise", "management",
|
|
19
|
+
"plane", "platform", "product", "system", "workflow",
|
|
20
|
+
]);
|
|
21
|
+
const CLUSTER_CONCEPTS = new Map([
|
|
22
|
+
["access", "governance"], ["approval", "governance"], ["credential", "governance"],
|
|
23
|
+
["gated", "governance"], ["govern", "governance"], ["guardrail", "governance"],
|
|
24
|
+
["inspectable", "governance"], ["permission", "governance"], ["regulated", "governance"],
|
|
25
|
+
["security", "governance"], ["backlog", "portfolio"], ["command", "portfolio"],
|
|
26
|
+
["coordination", "portfolio"], ["portfolio", "portfolio"], ["prioritization", "portfolio"],
|
|
27
|
+
["steering", "portfolio"], ["visibility", "portfolio"], ["adoption", "adoption"],
|
|
28
|
+
["blocked", "adoption"], ["change", "adoption"], ["distribution", "adoption"],
|
|
29
|
+
["onboarding", "adoption"], ["restriction", "adoption"], ["automation", "process"],
|
|
30
|
+
["mining", "process"], ["process", "process"], ["recurring", "process"],
|
|
31
|
+
["variation", "process"], ["workflow", "process"],
|
|
32
|
+
]);
|
|
33
|
+
function normalizePath(root, file) {
|
|
34
|
+
return path.relative(root, file).split(path.sep).join("/");
|
|
35
|
+
}
|
|
36
|
+
function titleCase(value) {
|
|
37
|
+
return value
|
|
38
|
+
.replace(/\.md$/i, "")
|
|
39
|
+
.replace(/^\d{4}-\d{2}-\d{2}-/, "")
|
|
40
|
+
.replace(/-v\d+$/i, "")
|
|
41
|
+
.split(/[-_]+/)
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
|
|
44
|
+
.join(" ");
|
|
45
|
+
}
|
|
46
|
+
function frontmatter(text) {
|
|
47
|
+
if (!text.startsWith("---\n"))
|
|
48
|
+
return { meta: {}, body: text };
|
|
49
|
+
const closing = text.indexOf("\n---", 4);
|
|
50
|
+
if (closing === -1)
|
|
51
|
+
return { meta: {}, body: text };
|
|
52
|
+
const after = text.indexOf("\n", closing + 1);
|
|
53
|
+
let meta = {};
|
|
54
|
+
try {
|
|
55
|
+
const parsed = parse(text.slice(4, closing));
|
|
56
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
57
|
+
meta = parsed;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
meta = {};
|
|
61
|
+
}
|
|
62
|
+
return { meta, body: after === -1 ? "" : text.slice(after + 1) };
|
|
63
|
+
}
|
|
64
|
+
function contentBlocks(body) {
|
|
65
|
+
const blocks = [];
|
|
66
|
+
for (const paragraph of body.split(/\n{2,}/)) {
|
|
67
|
+
const trimmed = paragraph.trim();
|
|
68
|
+
if (!trimmed)
|
|
69
|
+
continue;
|
|
70
|
+
if (trimmed.length > 1500) {
|
|
71
|
+
for (const line of trimmed.split("\n"))
|
|
72
|
+
if (line.trim())
|
|
73
|
+
blocks.push(line.trim());
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
blocks.push(trimmed);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return blocks;
|
|
80
|
+
}
|
|
81
|
+
function markdownFiles(dir) {
|
|
82
|
+
if (!existsSync(dir))
|
|
83
|
+
return [];
|
|
84
|
+
const files = [];
|
|
85
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
86
|
+
const full = path.join(dir, entry.name);
|
|
87
|
+
if (entry.isDirectory())
|
|
88
|
+
files.push(...markdownFiles(full));
|
|
89
|
+
else if (entry.isFile() && entry.name.endsWith(".md"))
|
|
90
|
+
files.push(full);
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
94
|
+
function destinationFromFile(root, file, type, ref) {
|
|
95
|
+
const text = readFileSync(file, "utf8");
|
|
96
|
+
const parsed = frontmatter(text);
|
|
97
|
+
const metadataTitle = typeof parsed.meta.title === "string"
|
|
98
|
+
? parsed.meta.title
|
|
99
|
+
: typeof parsed.meta.idea === "string"
|
|
100
|
+
? parsed.meta.idea
|
|
101
|
+
: null;
|
|
102
|
+
const heading = /^#\s+(.+)$/m.exec(parsed.body)?.[1]?.trim() ?? null;
|
|
103
|
+
return {
|
|
104
|
+
type,
|
|
105
|
+
ref,
|
|
106
|
+
path: normalizePath(root, file),
|
|
107
|
+
title: metadataTitle?.trim() || heading || titleCase(path.basename(type === "backlog" ? path.dirname(file) : file)),
|
|
108
|
+
text: parsed.body,
|
|
109
|
+
blocks: contentBlocks(parsed.body),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function isArchivedPath(root, file) {
|
|
113
|
+
return normalizePath(root, file).split("/").some((segment) => segment.toLowerCase() === "archive");
|
|
114
|
+
}
|
|
115
|
+
function isActivePacket(file) {
|
|
116
|
+
const { meta } = frontmatter(readFileSync(file, "utf8"));
|
|
117
|
+
const lifecycle = typeof meta.lifecycle === "string" ? meta.lifecycle.trim().toLowerCase() : "";
|
|
118
|
+
return !new Set(["complete", "completed", "done", "killed", "parked", "retired", "stopped"]).has(lifecycle);
|
|
119
|
+
}
|
|
120
|
+
export function buildLearningDestinationCatalog(root) {
|
|
121
|
+
const destinations = [];
|
|
122
|
+
for (const file of markdownFiles(path.join(root, "ideas"))) {
|
|
123
|
+
if (isArchivedPath(root, file))
|
|
124
|
+
continue;
|
|
125
|
+
destinations.push(destinationFromFile(root, file, "idea", normalizePath(root, file)));
|
|
126
|
+
}
|
|
127
|
+
for (const file of markdownFiles(path.join(root, "research"))) {
|
|
128
|
+
if (isArchivedPath(root, file))
|
|
129
|
+
continue;
|
|
130
|
+
destinations.push(destinationFromFile(root, file, "research", normalizePath(root, file)));
|
|
131
|
+
}
|
|
132
|
+
const specsDir = path.join(root, "specs");
|
|
133
|
+
if (existsSync(specsDir)) {
|
|
134
|
+
for (const entry of readdirSync(specsDir, { withFileTypes: true })) {
|
|
135
|
+
if (!entry.isDirectory())
|
|
136
|
+
continue;
|
|
137
|
+
const file = path.join(specsDir, entry.name, "spec.md");
|
|
138
|
+
if (existsSync(file) && isActivePacket(file))
|
|
139
|
+
destinations.push(destinationFromFile(root, file, "backlog", entry.name));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return destinations.sort((left, right) => left.path.localeCompare(right.path));
|
|
143
|
+
}
|
|
144
|
+
function normalizedToken(token) {
|
|
145
|
+
if (/^evaluat|^evals?$/.test(token))
|
|
146
|
+
return "eval";
|
|
147
|
+
if (/^orchestrat/.test(token))
|
|
148
|
+
return "orchestr";
|
|
149
|
+
if (/^rout(?:e|ed|es|ing)?$/.test(token))
|
|
150
|
+
return "route";
|
|
151
|
+
if (token.length > 5 && token.endsWith("ies"))
|
|
152
|
+
return `${token.slice(0, -3)}y`;
|
|
153
|
+
if (token.length > 6 && token.endsWith("ing"))
|
|
154
|
+
return token.slice(0, -3);
|
|
155
|
+
if (token.length > 5 && token.endsWith("ed"))
|
|
156
|
+
return token.slice(0, -2);
|
|
157
|
+
if (token.length > 5 && token.endsWith("es"))
|
|
158
|
+
return token.slice(0, -2);
|
|
159
|
+
if (token.length > 4 && token.endsWith("s") && !token.endsWith("ss"))
|
|
160
|
+
return token.slice(0, -1);
|
|
161
|
+
return token;
|
|
162
|
+
}
|
|
163
|
+
function tokens(text) {
|
|
164
|
+
return new Set((text.toLowerCase().match(/[a-z0-9]+/g) ?? [])
|
|
165
|
+
.filter((token) => token.length >= 4 && !STOPWORDS.has(token))
|
|
166
|
+
.map(normalizedToken));
|
|
167
|
+
}
|
|
168
|
+
function normalizedText(text) {
|
|
169
|
+
return (text.toLowerCase().match(/[a-z0-9]+/g) ?? []).map(normalizedToken).join(" ");
|
|
170
|
+
}
|
|
171
|
+
function clusterTokens(finding) {
|
|
172
|
+
return tokens(`${finding.headline} ${finding.paraphrase}`);
|
|
173
|
+
}
|
|
174
|
+
function clusterConcepts(finding) {
|
|
175
|
+
const concepts = new Set();
|
|
176
|
+
for (const token of clusterTokens(finding)) {
|
|
177
|
+
const concept = CLUSTER_CONCEPTS.get(token);
|
|
178
|
+
if (concept)
|
|
179
|
+
concepts.add(concept);
|
|
180
|
+
}
|
|
181
|
+
return concepts;
|
|
182
|
+
}
|
|
183
|
+
function relatedClusterFindings(left, right) {
|
|
184
|
+
const leftConcepts = clusterConcepts(left);
|
|
185
|
+
const rightConcepts = clusterConcepts(right);
|
|
186
|
+
for (const concept of leftConcepts)
|
|
187
|
+
if (rightConcepts.has(concept))
|
|
188
|
+
return true;
|
|
189
|
+
const leftTokens = clusterTokens(left);
|
|
190
|
+
const rightTokens = clusterTokens(right);
|
|
191
|
+
let overlap = 0;
|
|
192
|
+
for (const token of leftTokens)
|
|
193
|
+
if (rightTokens.has(token))
|
|
194
|
+
overlap += 1;
|
|
195
|
+
return overlap >= 3;
|
|
196
|
+
}
|
|
197
|
+
function clusterKey(findings) {
|
|
198
|
+
const sharedConcepts = [...clusterConcepts(findings[0] ?? { finding_id: "", inbox_scope_id: "", headline: "", paraphrase: "" })]
|
|
199
|
+
.filter((concept) => findings.every((finding) => clusterConcepts(finding).has(concept)))
|
|
200
|
+
.sort();
|
|
201
|
+
return `cluster:${sharedConcepts[0] ?? "emerging"}:${findings[0]?.finding_id ?? "unknown"}`;
|
|
202
|
+
}
|
|
203
|
+
export function clusterUnmatchedLearnings(input) {
|
|
204
|
+
const findings = input
|
|
205
|
+
.filter((finding) => /^F-\d{3}$/.test(finding.finding_id) && finding.inbox_scope_id.trim() && (finding.headline.trim() || finding.paraphrase.trim()))
|
|
206
|
+
.map((finding) => ({ ...finding }))
|
|
207
|
+
.sort((left, right) => left.finding_id.localeCompare(right.finding_id));
|
|
208
|
+
const visited = new Set();
|
|
209
|
+
const clusters = [];
|
|
210
|
+
for (const seed of findings) {
|
|
211
|
+
if (visited.has(seed.finding_id))
|
|
212
|
+
continue;
|
|
213
|
+
const members = [];
|
|
214
|
+
const queue = [seed];
|
|
215
|
+
visited.add(seed.finding_id);
|
|
216
|
+
while (queue.length > 0) {
|
|
217
|
+
const current = queue.shift();
|
|
218
|
+
members.push(current);
|
|
219
|
+
for (const candidate of findings) {
|
|
220
|
+
if (visited.has(candidate.finding_id) || !relatedClusterFindings(current, candidate))
|
|
221
|
+
continue;
|
|
222
|
+
visited.add(candidate.finding_id);
|
|
223
|
+
queue.push(candidate);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
members.sort((left, right) => left.finding_id.localeCompare(right.finding_id));
|
|
227
|
+
const sourceScopeIds = [...new Set(members.map((finding) => finding.inbox_scope_id))].sort();
|
|
228
|
+
clusters.push({
|
|
229
|
+
cluster_key: clusterKey(members),
|
|
230
|
+
status: sourceScopeIds.length >= 2 ? "eligible_for_synthesis" : "emerging",
|
|
231
|
+
source_finding_ids: members.map((finding) => finding.finding_id),
|
|
232
|
+
source_scope_ids: sourceScopeIds,
|
|
233
|
+
findings: members,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return clusters.sort((left, right) => left.cluster_key.localeCompare(right.cluster_key));
|
|
237
|
+
}
|
|
238
|
+
function tokenCoverage(left, right) {
|
|
239
|
+
const leftTokens = tokens(left);
|
|
240
|
+
const rightTokens = tokens(right);
|
|
241
|
+
if (leftTokens.size === 0 || rightTokens.size === 0)
|
|
242
|
+
return 0;
|
|
243
|
+
let overlap = 0;
|
|
244
|
+
for (const token of leftTokens)
|
|
245
|
+
if (rightTokens.has(token))
|
|
246
|
+
overlap += 1;
|
|
247
|
+
return overlap / Math.min(leftTokens.size, rightTokens.size);
|
|
248
|
+
}
|
|
249
|
+
export function validateHypothesisSynthesis(input) {
|
|
250
|
+
if (input.cluster.status !== "eligible_for_synthesis" || input.cluster.source_scope_ids.length < 2) {
|
|
251
|
+
return { ok: false, reason: "independent_calls_required" };
|
|
252
|
+
}
|
|
253
|
+
const actualSources = [...input.cluster.source_finding_ids].sort();
|
|
254
|
+
const requestedSources = [...new Set(input.synthesis.source_finding_ids)].sort();
|
|
255
|
+
if (requestedSources.length !== input.synthesis.source_finding_ids.length || requestedSources.join("\n") !== actualSources.join("\n")) {
|
|
256
|
+
return { ok: false, reason: "source_set_mismatch" };
|
|
257
|
+
}
|
|
258
|
+
for (const key of ["claim", "dissent", "falsifier", "next_evidence"]) {
|
|
259
|
+
const value = input.synthesis[key]?.trim();
|
|
260
|
+
if (!value || value.length > 1500)
|
|
261
|
+
return { ok: false, reason: `invalid_${key}` };
|
|
262
|
+
}
|
|
263
|
+
if (!new Set(["low", "medium"]).has(input.synthesis.starting_confidence)) {
|
|
264
|
+
return { ok: false, reason: "invalid_starting_confidence" };
|
|
265
|
+
}
|
|
266
|
+
const claim = input.synthesis.claim.trim();
|
|
267
|
+
const normalizedClaim = normalizedText(claim);
|
|
268
|
+
for (const finding of input.cluster.findings) {
|
|
269
|
+
for (const source of [finding.headline, finding.paraphrase, finding.proposal_text ?? ""]) {
|
|
270
|
+
const normalizedSource = normalizedText(source);
|
|
271
|
+
if (!normalizedSource)
|
|
272
|
+
continue;
|
|
273
|
+
const contained = normalizedClaim === normalizedSource
|
|
274
|
+
|| (normalizedClaim.length >= 40 && normalizedSource.includes(normalizedClaim))
|
|
275
|
+
|| (normalizedSource.length >= 40 && normalizedClaim.includes(normalizedSource));
|
|
276
|
+
if (contained || tokenCoverage(claim, source) >= LEARNING_ROUTING_POLICY.synthesis_source_overlap_max) {
|
|
277
|
+
return { ok: false, reason: "source_like_claim" };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
ok: true,
|
|
283
|
+
value: {
|
|
284
|
+
source_finding_ids: actualSources,
|
|
285
|
+
source_scope_ids: [...input.cluster.source_scope_ids],
|
|
286
|
+
claim,
|
|
287
|
+
dissent: input.synthesis.dissent.trim(),
|
|
288
|
+
falsifier: input.synthesis.falsifier.trim(),
|
|
289
|
+
next_evidence: input.synthesis.next_evidence.trim(),
|
|
290
|
+
starting_confidence: input.synthesis.starting_confidence,
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function blockScore(findingTokens, block) {
|
|
295
|
+
if (findingTokens.size === 0)
|
|
296
|
+
return 0;
|
|
297
|
+
const blockTokens = tokens(block);
|
|
298
|
+
let overlap = 0;
|
|
299
|
+
for (const token of findingTokens)
|
|
300
|
+
if (blockTokens.has(token))
|
|
301
|
+
overlap += 1;
|
|
302
|
+
if (overlap < 3)
|
|
303
|
+
return 0;
|
|
304
|
+
const findingCoverage = overlap / Math.min(findingTokens.size, 12);
|
|
305
|
+
const blockPrecision = overlap / Math.min(blockTokens.size, 12);
|
|
306
|
+
return Math.min(1, (findingCoverage * 0.6) + (blockPrecision * 0.4));
|
|
307
|
+
}
|
|
308
|
+
function titleScore(findingTokens, destination) {
|
|
309
|
+
const coreTitle = destination.title.split(/[:—]/, 1)[0] ?? destination.title;
|
|
310
|
+
const titleTokens = new Set([...tokens(coreTitle)].filter((token) => !GENERIC_TITLE_TOKENS.has(token)));
|
|
311
|
+
let titleOverlap = 0;
|
|
312
|
+
for (const token of titleTokens)
|
|
313
|
+
if (findingTokens.has(token))
|
|
314
|
+
titleOverlap += 1;
|
|
315
|
+
if (titleOverlap < 2 || titleTokens.size === 0)
|
|
316
|
+
return 0;
|
|
317
|
+
// A title echo is a useful routing label only when the document body also
|
|
318
|
+
// supports the learning. This keeps a two-word coincidence from taking custody.
|
|
319
|
+
const bodySupported = destination.blocks.some((block) => {
|
|
320
|
+
const blockTokens = tokens(block);
|
|
321
|
+
let overlap = 0;
|
|
322
|
+
for (const token of findingTokens)
|
|
323
|
+
if (blockTokens.has(token))
|
|
324
|
+
overlap += 1;
|
|
325
|
+
return overlap >= 3;
|
|
326
|
+
});
|
|
327
|
+
if (!bodySupported)
|
|
328
|
+
return 0;
|
|
329
|
+
const titleCoverage = titleOverlap / titleTokens.size;
|
|
330
|
+
const pairCoverage = Math.min(1, titleOverlap / 2);
|
|
331
|
+
return Math.min(1, (titleCoverage * 0.75) + (pairCoverage * 0.25));
|
|
332
|
+
}
|
|
333
|
+
function semanticCandidates(findingText, destinations) {
|
|
334
|
+
const findingTokens = tokens(findingText);
|
|
335
|
+
return destinations
|
|
336
|
+
.map((destination) => ({
|
|
337
|
+
destination,
|
|
338
|
+
score: Math.max(titleScore(findingTokens, destination), destination.blocks.reduce((best, block) => Math.max(best, blockScore(findingTokens, block)), 0)),
|
|
339
|
+
}))
|
|
340
|
+
.filter((candidate) => candidate.score > 0)
|
|
341
|
+
.sort((left, right) => right.score - left.score || left.destination.path.localeCompare(right.destination.path));
|
|
342
|
+
}
|
|
343
|
+
function explicitRefs(finding) {
|
|
344
|
+
return Array.isArray(finding.explicit_refs)
|
|
345
|
+
? finding.explicit_refs.filter((ref) => typeof ref === "string" && ref.trim().length > 0)
|
|
346
|
+
: [];
|
|
347
|
+
}
|
|
348
|
+
function includesExactRef(destination, ref) {
|
|
349
|
+
if (destination.ref === ref || destination.path === ref)
|
|
350
|
+
return true;
|
|
351
|
+
const escaped = ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
352
|
+
return new RegExp(`(^|[^A-Za-z0-9])${escaped}([^A-Za-z0-9]|$)`).test(destination.text);
|
|
353
|
+
}
|
|
354
|
+
function textField(record, key) {
|
|
355
|
+
const value = record?.[key];
|
|
356
|
+
return typeof value === "string" ? value.trim() : "";
|
|
357
|
+
}
|
|
358
|
+
export function routeLearning(input) {
|
|
359
|
+
for (const ref of explicitRefs(input.finding)) {
|
|
360
|
+
const exact = input.destinations.find((destination) => includesExactRef(destination, ref));
|
|
361
|
+
if (exact) {
|
|
362
|
+
return {
|
|
363
|
+
kind: "home",
|
|
364
|
+
destination: exact,
|
|
365
|
+
basis: "exact_ref",
|
|
366
|
+
placement_confidence: "high",
|
|
367
|
+
why: `The signal explicitly references ${ref} in ${exact.title}.`,
|
|
368
|
+
score: 1,
|
|
369
|
+
margin: 1,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const findingText = [textField(input.finding, "headline"), textField(input.finding, "paraphrase")]
|
|
374
|
+
.filter(Boolean)
|
|
375
|
+
.join(" ");
|
|
376
|
+
const candidates = semanticCandidates(findingText, input.destinations);
|
|
377
|
+
const eligible = candidates.filter((candidate) => (input.sharedTargetCounts?.get(candidate.destination.path) ?? 0) <= LEARNING_ROUTING_POLICY.catch_all_shared_max);
|
|
378
|
+
const best = eligible[0];
|
|
379
|
+
const runnerUp = eligible[1];
|
|
380
|
+
const margin = best ? best.score - (runnerUp?.score ?? 0) : 0;
|
|
381
|
+
if (best && best.score >= LEARNING_ROUTING_POLICY.semantic_minimum && margin >= LEARNING_ROUTING_POLICY.semantic_margin) {
|
|
382
|
+
return {
|
|
383
|
+
kind: "home",
|
|
384
|
+
destination: best.destination,
|
|
385
|
+
basis: "semantic",
|
|
386
|
+
placement_confidence: best.score >= 0.7 && margin >= 0.2 ? "high" : "medium",
|
|
387
|
+
why: `${best.destination.title} is the closest supported next-action match and clears the routing margin.`,
|
|
388
|
+
score: best.score,
|
|
389
|
+
margin,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
if (best && best.score >= LEARNING_ROUTING_POLICY.semantic_minimum && runnerUp) {
|
|
393
|
+
return {
|
|
394
|
+
kind: "unverified",
|
|
395
|
+
reason: "conflict",
|
|
396
|
+
candidates: eligible.slice(0, 2),
|
|
397
|
+
why: "Two destinations are similarly supported, so the primary home needs human judgment.",
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
kind: "unmatched",
|
|
402
|
+
candidates: (eligible.length ? eligible : candidates).slice(0, 2),
|
|
403
|
+
why: "No existing Idea, Research, or Backlog destination cleared admission; keep this signal emerging until an independent call corroborates it.",
|
|
404
|
+
};
|
|
405
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DiscoverySession } from "./discoverySources.js";
|
|
2
|
+
export type DiscoveryMatchTarget = {
|
|
3
|
+
provider: string;
|
|
4
|
+
expected_local_date: string | null;
|
|
5
|
+
timezone: string;
|
|
6
|
+
required_participants: string[];
|
|
7
|
+
required_title_tokens: string[];
|
|
8
|
+
};
|
|
9
|
+
export type DiscoveryCandidateRef = {
|
|
10
|
+
candidate_index: number;
|
|
11
|
+
source_fingerprint: string;
|
|
12
|
+
};
|
|
13
|
+
export type DiscoveryMatchResult = {
|
|
14
|
+
status: "none" | "unique" | "ambiguous";
|
|
15
|
+
matches: DiscoverySession[];
|
|
16
|
+
candidates: DiscoveryCandidateRef[];
|
|
17
|
+
};
|
|
18
|
+
export declare function sourceFingerprint(session: DiscoverySession): string;
|
|
19
|
+
export declare function matchDiscoverySessions(target: DiscoveryMatchTarget, sessions: DiscoverySession[]): DiscoveryMatchResult;
|
|
20
|
+
export type DiscoveryDuplicateProposal = {
|
|
21
|
+
left_fingerprint: string;
|
|
22
|
+
right_fingerprint: string;
|
|
23
|
+
confidence: "medium" | "high";
|
|
24
|
+
action: "review-canonical-source";
|
|
25
|
+
};
|
|
26
|
+
export declare function findProbableDiscoveryDuplicates(sessions: DiscoverySession[]): DiscoveryDuplicateProposal[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
function normalize(value) {
|
|
3
|
+
return value.trim().toLowerCase().replace(/\s+/g, " ");
|
|
4
|
+
}
|
|
5
|
+
function localDate(iso, timeZone) {
|
|
6
|
+
const date = new Date(iso);
|
|
7
|
+
if (!Number.isFinite(date.getTime()))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
const parts = new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric", month: "2-digit", day: "2-digit" }).formatToParts(date);
|
|
11
|
+
const map = Object.fromEntries(parts.map((part) => [part.type, part.value]));
|
|
12
|
+
return `${map.year}-${map.month}-${map.day}`;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function sessionMatches(target, session) {
|
|
19
|
+
if (session.completion_state !== "completed")
|
|
20
|
+
return false;
|
|
21
|
+
if (normalize(session.provider) !== normalize(target.provider))
|
|
22
|
+
return false;
|
|
23
|
+
if (target.expected_local_date && localDate(session.completed_at, target.timezone) !== target.expected_local_date)
|
|
24
|
+
return false;
|
|
25
|
+
const title = normalize(session.title);
|
|
26
|
+
if (!target.required_title_tokens.every((token) => title.includes(normalize(token))))
|
|
27
|
+
return false;
|
|
28
|
+
const participants = session.participants.map(normalize);
|
|
29
|
+
if (!target.required_participants.every((required) => participants.includes(normalize(required))))
|
|
30
|
+
return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
export function sourceFingerprint(session) {
|
|
34
|
+
const identity = [
|
|
35
|
+
normalize(session.provider),
|
|
36
|
+
normalize(session.connection_alias),
|
|
37
|
+
session.runtime_handle,
|
|
38
|
+
new Date(session.completed_at).toISOString(),
|
|
39
|
+
normalize(session.title),
|
|
40
|
+
session.stable_url ?? "",
|
|
41
|
+
].join("\n");
|
|
42
|
+
return `sha256:${createHash("sha256").update(identity).digest("hex")}`;
|
|
43
|
+
}
|
|
44
|
+
export function matchDiscoverySessions(target, sessions) {
|
|
45
|
+
const matches = sessions.filter((session) => sessionMatches(target, session));
|
|
46
|
+
const candidates = matches.map((session, index) => ({ candidate_index: index + 1, source_fingerprint: sourceFingerprint(session) }));
|
|
47
|
+
return { status: matches.length === 0 ? "none" : matches.length === 1 ? "unique" : "ambiguous", matches, candidates };
|
|
48
|
+
}
|
|
49
|
+
function participantOverlap(left, right) {
|
|
50
|
+
const rightNames = new Set(right.participants.map(normalize));
|
|
51
|
+
return left.participants.map(normalize).some((name) => rightNames.has(name));
|
|
52
|
+
}
|
|
53
|
+
export function findProbableDiscoveryDuplicates(sessions) {
|
|
54
|
+
const proposals = [];
|
|
55
|
+
for (let left = 0; left < sessions.length; left += 1) {
|
|
56
|
+
for (let right = left + 1; right < sessions.length; right += 1) {
|
|
57
|
+
const a = sessions[left];
|
|
58
|
+
const b = sessions[right];
|
|
59
|
+
if (normalize(a.provider) === normalize(b.provider))
|
|
60
|
+
continue;
|
|
61
|
+
const minutes = Math.abs(Date.parse(a.completed_at) - Date.parse(b.completed_at)) / 60_000;
|
|
62
|
+
if (minutes > 15 || normalize(a.title) !== normalize(b.title) || !participantOverlap(a, b))
|
|
63
|
+
continue;
|
|
64
|
+
proposals.push({
|
|
65
|
+
left_fingerprint: sourceFingerprint(a),
|
|
66
|
+
right_fingerprint: sourceFingerprint(b),
|
|
67
|
+
confidence: minutes <= 5 ? "high" : "medium",
|
|
68
|
+
action: "review-canonical-source",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return proposals;
|
|
73
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type DiscoveryCommandOutcome } from "./discoveryCommand.js";
|
|
2
|
+
import { type SynthesisTransport } from "./discoverySynthesis.js";
|
|
3
|
+
import { type FetchLike } from "./outlookNango.js";
|
|
4
|
+
export type OutlookDiscoveryDeps = {
|
|
5
|
+
fetchImpl?: FetchLike;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
now?: () => string;
|
|
8
|
+
sleep?: (ms: number) => Promise<void>;
|
|
9
|
+
transport?: SynthesisTransport;
|
|
10
|
+
fetchTimeoutMs?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function runOutlookDiscovery(root: string, args: string[], deps?: OutlookDiscoveryDeps): Promise<DiscoveryCommandOutcome>;
|