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,463 @@
|
|
|
1
|
+
// Canonical skills resolve from this installed package, with release or development trust.
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { existsSync, lstatSync, readFileSync, realpathSync, chmodSync, closeSync, fsyncSync, mkdirSync, openSync, renameSync, rmSync, writeSync, } from "node:fs";
|
|
4
|
+
import { userInfo } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { markFailure } from "./errorTaxonomy.js";
|
|
8
|
+
import { BUNDLED_SKILL_TREE, BUNDLED_SKILL_TREE_DOMAIN } from "./skillClientPolicy.js";
|
|
9
|
+
import { skillTreeSnapshot, topLevelSkillFolderCount } from "./skillTreeDigest.js";
|
|
10
|
+
import { canonicalJson } from "./trustEnvelope.js";
|
|
11
|
+
// Classify missing or unapproved local skills as expected command failures.
|
|
12
|
+
export function markSkillFailure(kind, message) {
|
|
13
|
+
const mark = kind === "not-found"
|
|
14
|
+
? { errorClass: "not_found", errorCode: "E_SKILL_NOT_FOUND", message }
|
|
15
|
+
: kind === "unapproved-source"
|
|
16
|
+
? { errorClass: "gate_block", errorCode: "E_SKILL_SOURCE_UNAPPROVED", message }
|
|
17
|
+
: { errorClass: "io_error", errorCode: "E_IO", message };
|
|
18
|
+
markFailure(mark);
|
|
19
|
+
}
|
|
20
|
+
// productfoo's own package root (this file compiles to dist/src/skillLocal.js), so
|
|
21
|
+
// a FULL install's bundled skills/ resolves from any working directory — root
|
|
22
|
+
// (the caller's project) only covers dogfood/dev where cwd is the foo repo.
|
|
23
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
24
|
+
export class SkillDeliveryError extends Error {
|
|
25
|
+
kind;
|
|
26
|
+
constructor(kind, message, options) {
|
|
27
|
+
super(message, options);
|
|
28
|
+
this.kind = kind;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* productfoo executes only skill text it can tie to an approval made on this machine:
|
|
33
|
+
* the bundled release digest, or a development tree the operator approved by
|
|
34
|
+
* exact digest. That refusal is the control that stops a caller repository
|
|
35
|
+
* shadowing a canonical skill with its own instructions.
|
|
36
|
+
*
|
|
37
|
+
* It is a refusal, not an outage. The old wording called it a trust boundary
|
|
38
|
+
* being "rebuilt" and told the reader to "use package-root skills" — which is
|
|
39
|
+
* what they were already doing, so the one command that clears it never
|
|
40
|
+
* appeared. Each condition now names itself and the command that fixes it.
|
|
41
|
+
*/
|
|
42
|
+
export function refuseUnapprovedSkillSource(reason) {
|
|
43
|
+
throw new SkillDeliveryError("unapproved-source", reason === "missing"
|
|
44
|
+
? "This skill tree has not been approved on this machine. Review it, then approve that exact tree: `foo skill trust`."
|
|
45
|
+
: "This machine's skill approval no longer matches the skill tree — it was edited, it expired, or it was issued for another package root. Re-approve: `foo skill trust`.");
|
|
46
|
+
}
|
|
47
|
+
/** A missing bundled resource is a local installation error. */
|
|
48
|
+
export function skillNotBundledMessage(name) {
|
|
49
|
+
return `"${name}" is not bundled with this productfoo install.`;
|
|
50
|
+
}
|
|
51
|
+
// A deliverable name is either a skill id ("pitch" → skills/pitch/SKILL.md) or
|
|
52
|
+
// a markdown reference under a skill ("pitch/references/prfaq-template.md" →
|
|
53
|
+
// that literal file). Every path segment must start alphanumeric, so ".." can
|
|
54
|
+
// never form — this validator IS the traversal guard, used for every local read.
|
|
55
|
+
export function skillRelPath(name) {
|
|
56
|
+
if (/^[a-z0-9][a-z0-9-]*$/.test(name))
|
|
57
|
+
return path.join(name, "SKILL.md");
|
|
58
|
+
if (/^[a-z0-9][a-z0-9-]*(\/[A-Za-z0-9][A-Za-z0-9._-]*)+$/.test(name) && name.endsWith(".md"))
|
|
59
|
+
return name;
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const DEV_TRUST_SCHEMA = "1";
|
|
63
|
+
const DEV_TRUST_KIND = "foo.development-skill-source";
|
|
64
|
+
const DEV_TREE_HASH_DOMAIN = "foo.development-skill-tree.v1";
|
|
65
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
66
|
+
export function exactKeys(value, expected) {
|
|
67
|
+
const actual = Object.keys(value).sort();
|
|
68
|
+
const wanted = [...expected].sort();
|
|
69
|
+
return actual.length === wanted.length && actual.every((key, index) => key === wanted[index]);
|
|
70
|
+
}
|
|
71
|
+
function canonicalTimestamp(value) {
|
|
72
|
+
if (typeof value !== "string")
|
|
73
|
+
return null;
|
|
74
|
+
const parsed = Date.parse(value);
|
|
75
|
+
return Number.isFinite(parsed) && new Date(parsed).toISOString() === value ? parsed : null;
|
|
76
|
+
}
|
|
77
|
+
function boundedApprovalDays(value) {
|
|
78
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 365) {
|
|
79
|
+
throw new TypeError("development trust validity must be a whole number of days from 1 through 365");
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
export function shellWord(value) {
|
|
84
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
85
|
+
}
|
|
86
|
+
function approvalInterval(options) {
|
|
87
|
+
const issued_at = options.issued_at ?? new Date().toISOString();
|
|
88
|
+
const issuedAt = canonicalTimestamp(issued_at);
|
|
89
|
+
const days = boundedApprovalDays(options.days ?? 30);
|
|
90
|
+
if (issuedAt === null)
|
|
91
|
+
throw new TypeError("development trust approval requires a canonical issue time");
|
|
92
|
+
return {
|
|
93
|
+
issued_at,
|
|
94
|
+
expires_at: new Date(issuedAt + days * 86_400_000).toISOString(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function canonicalSkillTreeSnapshot(rootValue, targetRelative) {
|
|
98
|
+
// The walk is shared with the bundled-install anchor (skillTreeDigest.ts) so
|
|
99
|
+
// a development record and the release anchor can never disagree about what
|
|
100
|
+
// "this exact tree" hashes to. The domain keeps the two digest namespaces
|
|
101
|
+
// distinct; existing development records verify exactly as before.
|
|
102
|
+
return skillTreeSnapshot(rootValue, DEV_TREE_HASH_DOMAIN, targetRelative);
|
|
103
|
+
}
|
|
104
|
+
export function createDevelopmentSkillTrustRecord(rootValue, validity) {
|
|
105
|
+
const root = realpathSync(rootValue);
|
|
106
|
+
const issuedAt = canonicalTimestamp(validity.issued_at);
|
|
107
|
+
const expiresAt = canonicalTimestamp(validity.expires_at);
|
|
108
|
+
if (issuedAt === null || expiresAt === null || expiresAt <= issuedAt) {
|
|
109
|
+
throw new TypeError("development trust record requires a valid canonical time interval");
|
|
110
|
+
}
|
|
111
|
+
return Object.freeze({
|
|
112
|
+
schema_version: DEV_TRUST_SCHEMA,
|
|
113
|
+
record_kind: DEV_TRUST_KIND,
|
|
114
|
+
root,
|
|
115
|
+
skills_sha256: canonicalSkillTreeSnapshot(root).hash,
|
|
116
|
+
issued_at: validity.issued_at,
|
|
117
|
+
expires_at: validity.expires_at,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
export function developmentSkillTrustProposal(rootValue, options = {}) {
|
|
121
|
+
const record = createDevelopmentSkillTrustRecord(rootValue, approvalInterval(options));
|
|
122
|
+
return Object.freeze({
|
|
123
|
+
...record,
|
|
124
|
+
record_path: path.resolve(options.record_path ?? defaultDevelopmentTrustRecord()),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function assertExternalTrustRecordPath(rootValue, recordPathValue) {
|
|
128
|
+
const root = realpathSync(rootValue);
|
|
129
|
+
const recordPath = path.resolve(recordPathValue);
|
|
130
|
+
if (recordPath === root || recordPath.startsWith(`${root}${path.sep}`)) {
|
|
131
|
+
throw new TypeError("development trust record must stay outside the productfoo checkout");
|
|
132
|
+
}
|
|
133
|
+
const parent = path.dirname(recordPath);
|
|
134
|
+
mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
135
|
+
const parentReal = realpathSync(parent);
|
|
136
|
+
if ((lstatSync(parentReal).mode & 0o022) !== 0) {
|
|
137
|
+
throw new TypeError("development trust record directory must not be writable by other users");
|
|
138
|
+
}
|
|
139
|
+
const resolved = path.join(parentReal, path.basename(recordPath));
|
|
140
|
+
if (resolved === root || resolved.startsWith(`${root}${path.sep}`)) {
|
|
141
|
+
throw new TypeError("development trust record must stay outside the productfoo checkout");
|
|
142
|
+
}
|
|
143
|
+
if (existsSync(recordPath)) {
|
|
144
|
+
const existing = lstatSync(recordPath);
|
|
145
|
+
if (existing.isSymbolicLink() || !existing.isFile()) {
|
|
146
|
+
throw new TypeError("development trust record target must be a regular file, not a link");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return recordPath;
|
|
150
|
+
}
|
|
151
|
+
function writePrivateTrustRecord(recordPathValue, record) {
|
|
152
|
+
const recordPath = assertExternalTrustRecordPath(record.root, recordPathValue);
|
|
153
|
+
const parent = path.dirname(recordPath);
|
|
154
|
+
const temp = path.join(parent, `.${path.basename(recordPath)}.${process.pid}.${randomUUID()}.tmp`);
|
|
155
|
+
const fd = openSync(temp, "wx", 0o600);
|
|
156
|
+
try {
|
|
157
|
+
try {
|
|
158
|
+
const bytes = Buffer.from(`${canonicalJson(record)}\n`, "utf8");
|
|
159
|
+
writeSync(fd, bytes);
|
|
160
|
+
fsyncSync(fd);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
closeSync(fd);
|
|
164
|
+
}
|
|
165
|
+
renameSync(temp, recordPath);
|
|
166
|
+
chmodSync(recordPath, 0o600);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
if (existsSync(temp))
|
|
170
|
+
rmSync(temp, { force: true });
|
|
171
|
+
}
|
|
172
|
+
const parentFd = openSync(parent, "r");
|
|
173
|
+
try {
|
|
174
|
+
fsyncSync(parentFd);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
closeSync(parentFd);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export function approveDevelopmentSkillTrust(rootValue, expectedSkillsSha256, options = {}) {
|
|
181
|
+
if (!SHA256_HEX.test(expectedSkillsSha256)) {
|
|
182
|
+
throw new TypeError("development trust approval requires the exact reviewed skill-tree SHA-256");
|
|
183
|
+
}
|
|
184
|
+
const proposal = developmentSkillTrustProposal(rootValue, options);
|
|
185
|
+
if (proposal.skills_sha256 !== expectedSkillsSha256) {
|
|
186
|
+
throw new TypeError("development skill tree changed after review; review the new digest before approval");
|
|
187
|
+
}
|
|
188
|
+
const { record_path, ...record } = proposal;
|
|
189
|
+
writePrivateTrustRecord(record_path, record);
|
|
190
|
+
return proposal;
|
|
191
|
+
}
|
|
192
|
+
export function readDevelopmentSkillTrust(rootValue, recordPath, targetRelative, nowValue = new Date().toISOString()) {
|
|
193
|
+
try {
|
|
194
|
+
let recordLink;
|
|
195
|
+
try {
|
|
196
|
+
recordLink = lstatSync(recordPath);
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
return error?.code === "ENOENT"
|
|
200
|
+
? { status: "missing" }
|
|
201
|
+
: { status: "refused" };
|
|
202
|
+
}
|
|
203
|
+
if (!recordLink.isFile() || recordLink.isSymbolicLink() || (recordLink.mode & 0o022) !== 0)
|
|
204
|
+
return { status: "refused" };
|
|
205
|
+
const recordReal = realpathSync(recordPath);
|
|
206
|
+
const packageReal = realpathSync(rootValue);
|
|
207
|
+
if (recordReal === packageReal || recordReal.startsWith(`${packageReal}${path.sep}`))
|
|
208
|
+
return { status: "refused" };
|
|
209
|
+
const parsed = JSON.parse(readFileSync(recordReal, "utf8"));
|
|
210
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
211
|
+
return { status: "refused" };
|
|
212
|
+
const record = parsed;
|
|
213
|
+
if (!exactKeys(record, ["schema_version", "record_kind", "root", "skills_sha256", "issued_at", "expires_at"]))
|
|
214
|
+
return { status: "refused" };
|
|
215
|
+
if (record.schema_version !== DEV_TRUST_SCHEMA || record.record_kind !== DEV_TRUST_KIND)
|
|
216
|
+
return { status: "refused" };
|
|
217
|
+
if (typeof record.root !== "string" || realpathSync(record.root) !== packageReal)
|
|
218
|
+
return { status: "refused" };
|
|
219
|
+
if (typeof record.skills_sha256 !== "string" || !SHA256_HEX.test(record.skills_sha256))
|
|
220
|
+
return { status: "refused" };
|
|
221
|
+
const issuedAt = canonicalTimestamp(record.issued_at);
|
|
222
|
+
const expiresAt = canonicalTimestamp(record.expires_at);
|
|
223
|
+
const now = canonicalTimestamp(nowValue);
|
|
224
|
+
if (issuedAt === null || expiresAt === null || now === null || issuedAt > now || now >= expiresAt)
|
|
225
|
+
return { status: "refused" };
|
|
226
|
+
const snapshot = canonicalSkillTreeSnapshot(packageReal, targetRelative);
|
|
227
|
+
if (record.skills_sha256 !== snapshot.hash)
|
|
228
|
+
return { status: "refused" };
|
|
229
|
+
return {
|
|
230
|
+
status: "ready",
|
|
231
|
+
root: packageReal,
|
|
232
|
+
skills_sha256: record.skills_sha256,
|
|
233
|
+
issued_at: record.issued_at,
|
|
234
|
+
expires_at: record.expires_at,
|
|
235
|
+
record_path: recordReal,
|
|
236
|
+
target_bytes: targetRelative === undefined ? undefined : snapshot.targetBytes,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return { status: "refused" };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
export function inspectDevelopmentSkillTrust(rootValue, recordPath = defaultDevelopmentTrustRecord(), nowValue = new Date().toISOString()) {
|
|
244
|
+
const { target_bytes: _, ...status } = readDevelopmentSkillTrust(rootValue, recordPath, undefined, nowValue);
|
|
245
|
+
return status;
|
|
246
|
+
}
|
|
247
|
+
function defaultDevelopmentTrustRecord() {
|
|
248
|
+
return path.join(userInfo().homedir, ".foo", "trust", "development-skill-source.json");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* The install-anchored half of canonical-skill trust (foo-open-source-transition-v0
|
|
252
|
+
* T-003). When this package's skills tree is exactly the tree the release
|
|
253
|
+
* anchored, the bytes are the reviewed ones — npm's tarball integrity binds the
|
|
254
|
+
* install and BUNDLED_SKILL_TREE binds every read — so a clean install runs a
|
|
255
|
+
* bundled skill with no hosted call, no token, and no per-machine approval.
|
|
256
|
+
* Returns null whenever the anchor cannot prove the tree (absent tree, walk
|
|
257
|
+
* error, digest or count drift); the caller then falls back to the development
|
|
258
|
+
* record, which refuses when absent. The fallback is fail-closed either way.
|
|
259
|
+
*/
|
|
260
|
+
function readBundledCanonicalSkill(rel, trust) {
|
|
261
|
+
try {
|
|
262
|
+
const anchor = trust.bundled_skill_tree ?? BUNDLED_SKILL_TREE;
|
|
263
|
+
const snapshot = skillTreeSnapshot(PACKAGE_ROOT, BUNDLED_SKILL_TREE_DOMAIN, rel);
|
|
264
|
+
if (snapshot.hash !== anchor.tree_sha256)
|
|
265
|
+
return null;
|
|
266
|
+
if (topLevelSkillFolderCount(PACKAGE_ROOT) !== anchor.skill_count)
|
|
267
|
+
return null;
|
|
268
|
+
const bytes = snapshot.targetBytes;
|
|
269
|
+
if (bytes === null)
|
|
270
|
+
return null;
|
|
271
|
+
const text = bytes.toString("utf8");
|
|
272
|
+
if (!Buffer.from(text, "utf8").equals(bytes))
|
|
273
|
+
return null;
|
|
274
|
+
return text;
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// An incomplete install has no tree to walk and an unreadable tree cannot be
|
|
278
|
+
// proven; neither is an anchor case, and the dev-record fallback below
|
|
279
|
+
// still refuses honestly.
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* #897: doctor's view of the install-anchored path. True when this package's
|
|
285
|
+
* skills tree is exactly the release-anchored tree, so every bundled skill
|
|
286
|
+
* loads with no approval (readBundledCanonicalSkill). False when the tree is
|
|
287
|
+
* absent, unreadable, or edited; then `foo skill trust` is genuinely needed.
|
|
288
|
+
*/
|
|
289
|
+
export function bundledSkillTreeVerified(root = PACKAGE_ROOT, anchor = BUNDLED_SKILL_TREE) {
|
|
290
|
+
try {
|
|
291
|
+
return skillTreeSnapshot(root, BUNDLED_SKILL_TREE_DOMAIN).hash === anchor.tree_sha256
|
|
292
|
+
&& topLevelSkillFolderCount(root) === anchor.skill_count;
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function readTrustedCanonicalSkill(name, trust) {
|
|
299
|
+
const rel = skillRelPath(name);
|
|
300
|
+
if (rel === null)
|
|
301
|
+
throw new SkillDeliveryError("not-found", `skill "${name}" is not valid`);
|
|
302
|
+
const bundled = readBundledCanonicalSkill(rel, trust);
|
|
303
|
+
if (bundled !== null)
|
|
304
|
+
return bundled;
|
|
305
|
+
const recordPath = trust.development_trust_record ?? defaultDevelopmentTrustRecord();
|
|
306
|
+
// The read already tells missing (never approved) apart from refused (edited,
|
|
307
|
+
// expired, wrong root). Carry that distinction out to the reader instead of
|
|
308
|
+
// collapsing both into one "unavailable".
|
|
309
|
+
const read = readDevelopmentSkillTrust(PACKAGE_ROOT, recordPath, rel, trust.now);
|
|
310
|
+
if (read.status !== "ready")
|
|
311
|
+
return refuseUnapprovedSkillSource(read.status);
|
|
312
|
+
const bytes = read.target_bytes ?? null;
|
|
313
|
+
if (bytes === null)
|
|
314
|
+
return refuseUnapprovedSkillSource("refused");
|
|
315
|
+
const text = bytes.toString("utf8");
|
|
316
|
+
if (!Buffer.from(text, "utf8").equals(bytes))
|
|
317
|
+
return refuseUnapprovedSkillSource("refused");
|
|
318
|
+
return text;
|
|
319
|
+
}
|
|
320
|
+
// Resolve a skill from productfoo's own installed package and nowhere else.
|
|
321
|
+
//
|
|
322
|
+
// Skills ship on disk; a missing resource cannot fall back to a service.
|
|
323
|
+
export function resolveLocalSkill(name, trust = {}) {
|
|
324
|
+
const rel = skillRelPath(name);
|
|
325
|
+
if (rel === null) {
|
|
326
|
+
throw new SkillDeliveryError("not-found", `"${name}" is not a valid skill name (a skill id like "pitch", or a reference like "pitch/references/prfaq-template.md").`);
|
|
327
|
+
}
|
|
328
|
+
if (!existsSync(path.join(PACKAGE_ROOT, "skills", rel)))
|
|
329
|
+
return null;
|
|
330
|
+
return { text: readTrustedCanonicalSkill(name, trust), source: "local" };
|
|
331
|
+
}
|
|
332
|
+
export { PACKAGE_ROOT, readTrustedCanonicalSkill, SHA256_HEX };
|
|
333
|
+
// Resolve a skill only from productfoo's own installed package, never the caller repo.
|
|
334
|
+
export async function resolveSkill(root, name, env = process.env, trust = {}) {
|
|
335
|
+
const rel = skillRelPath(name);
|
|
336
|
+
if (rel === null) {
|
|
337
|
+
throw new SkillDeliveryError("not-found", `"${name}" is not a valid skill name (a skill id like "pitch", or a reference like "pitch/references/prfaq-template.md").`);
|
|
338
|
+
}
|
|
339
|
+
const local = path.join(PACKAGE_ROOT, "skills", rel);
|
|
340
|
+
if (existsSync(local))
|
|
341
|
+
return { text: readTrustedCanonicalSkill(name, trust), source: "local" };
|
|
342
|
+
void root;
|
|
343
|
+
void env; // Legacy configuration cannot change the canonical skill source.
|
|
344
|
+
throw new SkillDeliveryError("not-found", skillNotBundledMessage(name));
|
|
345
|
+
}
|
|
346
|
+
async function runDevelopmentSkillTrustCommand(args) {
|
|
347
|
+
const json = args.includes("--json");
|
|
348
|
+
const knownFlags = new Set(["--json", "--approve", "--days", "--out"]);
|
|
349
|
+
const values = new Map();
|
|
350
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
351
|
+
const flag = args[index];
|
|
352
|
+
if (!knownFlags.has(flag)) {
|
|
353
|
+
markFailure({ errorClass: "usage_error", errorCode: "E_BAD_FLAG", message: `unknown skill trust option ${flag}` });
|
|
354
|
+
return {
|
|
355
|
+
output: json
|
|
356
|
+
? `${JSON.stringify({ schema_version: "0.1", status: "refused", reason: "invalid_option" })}\n`
|
|
357
|
+
: "usage: foo skill trust [--json] [--approve <sha256>] [--days 1..365] [--out <external-file>]\n",
|
|
358
|
+
exitCode: 1,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
if (flag === "--json")
|
|
362
|
+
continue;
|
|
363
|
+
const value = args[index + 1];
|
|
364
|
+
if (!value || value.startsWith("--") || values.has(flag)) {
|
|
365
|
+
markFailure({ errorClass: "usage_error", errorCode: "E_BAD_FLAG", message: `${flag} requires one value` });
|
|
366
|
+
return {
|
|
367
|
+
output: json
|
|
368
|
+
? `${JSON.stringify({ schema_version: "0.1", status: "refused", reason: "invalid_option" })}\n`
|
|
369
|
+
: "usage: foo skill trust [--json] [--approve <sha256>] [--days 1..365] [--out <external-file>]\n",
|
|
370
|
+
exitCode: 1,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
values.set(flag, value);
|
|
374
|
+
index += 1;
|
|
375
|
+
}
|
|
376
|
+
const daysText = values.get("--days");
|
|
377
|
+
const days = daysText === undefined
|
|
378
|
+
? 30
|
|
379
|
+
: /^[1-9][0-9]{0,2}$/.test(daysText)
|
|
380
|
+
? Number(daysText)
|
|
381
|
+
: Number.NaN;
|
|
382
|
+
const options = {
|
|
383
|
+
record_path: values.get("--out"),
|
|
384
|
+
days,
|
|
385
|
+
};
|
|
386
|
+
try {
|
|
387
|
+
const approvedDigest = values.get("--approve");
|
|
388
|
+
const proposal = approvedDigest
|
|
389
|
+
? approveDevelopmentSkillTrust(PACKAGE_ROOT, approvedDigest, options)
|
|
390
|
+
: developmentSkillTrustProposal(PACKAGE_ROOT, options);
|
|
391
|
+
if (json) {
|
|
392
|
+
return {
|
|
393
|
+
output: `${JSON.stringify({
|
|
394
|
+
schema_version: "0.1",
|
|
395
|
+
status: approvedDigest ? "approved" : "review_required",
|
|
396
|
+
root: proposal.root,
|
|
397
|
+
skills_sha256: proposal.skills_sha256,
|
|
398
|
+
issued_at: proposal.issued_at,
|
|
399
|
+
expires_at: proposal.expires_at,
|
|
400
|
+
record_path: proposal.record_path,
|
|
401
|
+
})}\n`,
|
|
402
|
+
exitCode: 0,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (approvedDigest) {
|
|
406
|
+
return {
|
|
407
|
+
output: `Development skills approved for ${proposal.root}\nDigest: ${proposal.skills_sha256}\nExpires: ${proposal.expires_at}\nRecord: ${proposal.record_path}\n`,
|
|
408
|
+
exitCode: 0,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
const approveCommand = [
|
|
412
|
+
"foo skill trust --approve",
|
|
413
|
+
proposal.skills_sha256,
|
|
414
|
+
"--days",
|
|
415
|
+
String(days),
|
|
416
|
+
"--out",
|
|
417
|
+
shellWord(proposal.record_path),
|
|
418
|
+
].join(" ");
|
|
419
|
+
return {
|
|
420
|
+
output: `Review productfoo's canonical skill tree before approval.\nRoot: ${proposal.root}\nDigest: ${proposal.skills_sha256}\nProposed expiry: ${proposal.expires_at}\nExternal record: ${proposal.record_path}\nApprove only this exact digest with: ${approveCommand}\n`,
|
|
421
|
+
exitCode: 0,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
const message = error instanceof Error ? error.message : "development trust approval refused";
|
|
426
|
+
markFailure({ errorClass: "gate_block", errorCode: "E_GATE_BLOCKED", message });
|
|
427
|
+
return {
|
|
428
|
+
output: json
|
|
429
|
+
? `${JSON.stringify({ schema_version: "0.1", status: "refused", reason: message })}\n`
|
|
430
|
+
: `✋ needs your input — ${message}\n`,
|
|
431
|
+
exitCode: 1,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// CLI adapter: `foo skill <name>` prints the resolved leaf text — this is the
|
|
436
|
+
// handoff surface harnesses use when no skills/ directory exists.
|
|
437
|
+
export async function runSkillCommand(root, args, env = process.env, trust = {}) {
|
|
438
|
+
if (args[0] === "trust")
|
|
439
|
+
return runDevelopmentSkillTrustCommand(args);
|
|
440
|
+
const trustFlagIndex = args.indexOf("--dev-trust-record");
|
|
441
|
+
const trustRecord = trustFlagIndex === -1 ? trust.development_trust_record : args[trustFlagIndex + 1];
|
|
442
|
+
if (trustFlagIndex !== -1 && (!trustRecord || trustRecord.startsWith("--"))) {
|
|
443
|
+
markFailure({ errorClass: "usage_error", errorCode: "E_BAD_FLAG", message: "development trust record path missing" });
|
|
444
|
+
return { output: "usage: foo skill <name> [--dev-trust-record <external-record.json>]\n", exitCode: 1 };
|
|
445
|
+
}
|
|
446
|
+
const name = args.find((arg, index) => !arg.startsWith("--")
|
|
447
|
+
&& (trustFlagIndex === -1 || index !== trustFlagIndex + 1));
|
|
448
|
+
if (!name) {
|
|
449
|
+
markFailure({ errorClass: "usage_error", errorCode: "E_BAD_FLAG", message: "skill name missing" });
|
|
450
|
+
return { output: "usage: foo skill <name>\n", exitCode: 1 };
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const { text } = await resolveSkill(root, name, env, { ...trust, development_trust_record: trustRecord });
|
|
454
|
+
return { output: text.endsWith("\n") ? text : `${text}\n`, exitCode: 0 };
|
|
455
|
+
}
|
|
456
|
+
catch (err) {
|
|
457
|
+
if (err instanceof SkillDeliveryError) {
|
|
458
|
+
markSkillFailure(err.kind, err.message);
|
|
459
|
+
return { output: `${err.message}\n`, exitCode: 1 };
|
|
460
|
+
}
|
|
461
|
+
throw err;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface SkillTreeSnapshot {
|
|
2
|
+
/** Digest over the whole canonical tree under `domain`. */
|
|
3
|
+
hash: string;
|
|
4
|
+
/** Bytes of `targetRelative` (posix-style, relative to the skills root), when asked for. */
|
|
5
|
+
targetBytes: Buffer | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function skillTreeSnapshot(rootValue: string, domain: string, targetRelative?: string): SkillTreeSnapshot;
|
|
8
|
+
/** How many top-level folders the canonical tree carries (valid skill-id names only). */
|
|
9
|
+
export declare function topLevelSkillFolderCount(rootValue: string): number;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// One deterministic digest of a canonical skill tree, shared by every consumer
|
|
2
|
+
// that has to agree on what "this exact tree" means: the development-trust
|
|
3
|
+
// record (skillLocal.ts), the bundled-install anchor (skillClientPolicy.ts),
|
|
4
|
+
// the anchor drift test, and scripts/skill-tree-anchor.mjs. A second
|
|
5
|
+
// hand-written walk is exactly how two digests that claim to name the same
|
|
6
|
+
// tree drift apart, so the walk lives here and nowhere else.
|
|
7
|
+
//
|
|
8
|
+
// The digest covers every regular file, symlink, and directory under each
|
|
9
|
+
// top-level skill folder (folders whose names are valid skill ids), sorted
|
|
10
|
+
// canonically, domain-separated by the caller's hash-domain string. Any
|
|
11
|
+
// edit, addition, removal, or permission change under skills/ moves the hash.
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { lstatSync, readdirSync, readFileSync, readlinkSync, realpathSync } from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
const compareNames = (left, right) => left.localeCompare(right);
|
|
16
|
+
export function skillTreeSnapshot(rootValue, domain, targetRelative) {
|
|
17
|
+
const root = realpathSync(rootValue);
|
|
18
|
+
const skillsRoot = path.join(root, "skills");
|
|
19
|
+
const skillsReal = realpathSync(skillsRoot);
|
|
20
|
+
if (skillsReal !== skillsRoot)
|
|
21
|
+
throw new TypeError("development skills root must not be a symlink");
|
|
22
|
+
const normalizedTarget = targetRelative?.split(path.sep).join(path.posix.sep);
|
|
23
|
+
const entries = [];
|
|
24
|
+
const topLevel = readdirSync(skillsRoot, { withFileTypes: true })
|
|
25
|
+
.filter((entry) => entry.isDirectory() && /^[a-z0-9][a-z0-9-]*$/.test(entry.name))
|
|
26
|
+
.sort((left, right) => compareNames(left.name, right.name));
|
|
27
|
+
const visit = (absolute, relative) => {
|
|
28
|
+
for (const entry of readdirSync(absolute, { withFileTypes: true }).sort((left, right) => compareNames(left.name, right.name))) {
|
|
29
|
+
const childAbsolute = path.join(absolute, entry.name);
|
|
30
|
+
const childRelative = path.posix.join(relative.split(path.sep).join(path.posix.sep), entry.name);
|
|
31
|
+
const stat = lstatSync(childAbsolute);
|
|
32
|
+
if (stat.isDirectory()) {
|
|
33
|
+
visit(childAbsolute, childRelative);
|
|
34
|
+
}
|
|
35
|
+
else if (stat.isFile()) {
|
|
36
|
+
entries.push({ relative: childRelative, kind: "file", bytes: readFileSync(childAbsolute) });
|
|
37
|
+
}
|
|
38
|
+
else if (stat.isSymbolicLink()) {
|
|
39
|
+
entries.push({ relative: childRelative, kind: "symlink", bytes: Buffer.from(readlinkSync(childAbsolute), "utf8") });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw new TypeError("development skill tree contains an unsupported filesystem object");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
for (const entry of topLevel)
|
|
47
|
+
visit(path.join(skillsRoot, entry.name), entry.name);
|
|
48
|
+
const hash = createHash("sha256").update(`${domain}\0`, "utf8");
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
hash.update(`${entry.kind}\0${entry.relative}\0${entry.bytes.length}\0`, "utf8");
|
|
51
|
+
hash.update(entry.bytes);
|
|
52
|
+
}
|
|
53
|
+
const target = normalizedTarget === undefined
|
|
54
|
+
? null
|
|
55
|
+
: entries.find((entry) => entry.relative === normalizedTarget && entry.kind === "file")?.bytes ?? null;
|
|
56
|
+
return { hash: hash.digest("hex"), targetBytes: target === null ? null : Buffer.from(target) };
|
|
57
|
+
}
|
|
58
|
+
/** How many top-level folders the canonical tree carries (valid skill-id names only). */
|
|
59
|
+
export function topLevelSkillFolderCount(rootValue) {
|
|
60
|
+
const skillsRoot = path.join(realpathSync(rootValue), "skills");
|
|
61
|
+
return readdirSync(realpathSync(skillsRoot), { withFileTypes: true })
|
|
62
|
+
.filter((entry) => entry.isDirectory() && /^[a-z0-9][a-z0-9-]*$/.test(entry.name))
|
|
63
|
+
.length;
|
|
64
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type FetchLike = typeof globalThis.fetch;
|
|
2
|
+
export type SlackRuntimeErrorCode = "not_connected" | "auth_expired" | "access_denied" | "provider_failure" | "network" | "provider_timeout" | "thread_too_large" | "budget_exhausted";
|
|
3
|
+
export declare class SlackRuntimeError extends Error {
|
|
4
|
+
readonly code: SlackRuntimeErrorCode;
|
|
5
|
+
constructor(code: SlackRuntimeErrorCode, message: string);
|
|
6
|
+
}
|
|
7
|
+
export type SlackConnection = {
|
|
8
|
+
connection_ref: string;
|
|
9
|
+
broker: "nango" | "direct";
|
|
10
|
+
token: string;
|
|
11
|
+
};
|
|
12
|
+
export type SlackResponseEnvelope = Record<string, unknown>;
|
|
13
|
+
export declare const SLACK_POLICY: {
|
|
14
|
+
readonly policy_version: "slack-local-worker-0.1";
|
|
15
|
+
readonly max_response_bytes: number;
|
|
16
|
+
readonly fetch_timeout_ms: 90000;
|
|
17
|
+
readonly max_model_input_tokens: 100000;
|
|
18
|
+
readonly max_replies_per_thread: 200;
|
|
19
|
+
readonly min_request_interval_ms: 1100;
|
|
20
|
+
readonly max_channels_probed: 10;
|
|
21
|
+
};
|
|
22
|
+
export type SlackConnectionEnv = Record<string, string | undefined>;
|
|
23
|
+
export declare function slackConnectionConfigured(env: SlackConnectionEnv): boolean;
|
|
24
|
+
export declare function slackCredentialBroker(env: SlackConnectionEnv): "nango" | "direct" | null;
|
|
25
|
+
export declare function resolveSlackConnection(input: {
|
|
26
|
+
env: SlackConnectionEnv;
|
|
27
|
+
fetchImpl: FetchLike;
|
|
28
|
+
}): Promise<SlackConnection>;
|
|
29
|
+
export type SlackMessage = {
|
|
30
|
+
ts?: string;
|
|
31
|
+
user?: string;
|
|
32
|
+
bot_id?: string;
|
|
33
|
+
subtype?: string;
|
|
34
|
+
text?: string;
|
|
35
|
+
};
|
|
36
|
+
export type SlackThreadRead = {
|
|
37
|
+
messages: SlackMessage[];
|
|
38
|
+
has_more: boolean;
|
|
39
|
+
};
|
|
40
|
+
export declare class SlackClient {
|
|
41
|
+
readonly requests: {
|
|
42
|
+
conversations_list: number;
|
|
43
|
+
conversations_info: number;
|
|
44
|
+
conversations_history: number;
|
|
45
|
+
conversations_replies: number;
|
|
46
|
+
};
|
|
47
|
+
private readonly connection;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private lastRequestAt;
|
|
50
|
+
constructor(connection: SlackConnection, options?: Partial<{
|
|
51
|
+
fetchImpl: FetchLike;
|
|
52
|
+
baseUrl: string;
|
|
53
|
+
now: () => number;
|
|
54
|
+
sleep: (ms: number) => Promise<void>;
|
|
55
|
+
fetchTimeoutMs: number;
|
|
56
|
+
}>);
|
|
57
|
+
private request;
|
|
58
|
+
probeWorkspace(): Promise<{
|
|
59
|
+
channels: number;
|
|
60
|
+
}>;
|
|
61
|
+
threadReplies(channelId: string, threadTs: string): Promise<SlackThreadRead>;
|
|
62
|
+
}
|