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,30 @@
|
|
|
1
|
+
export type DetectSeverity = "error" | "warn";
|
|
2
|
+
export type DetectCategory = "confidence-theater" | "proof-gap" | "evidence-gap" | "acceptance-gap" | "scope-drift" | "jargon";
|
|
3
|
+
export type Finding = {
|
|
4
|
+
rule: string;
|
|
5
|
+
category: DetectCategory;
|
|
6
|
+
severity: DetectSeverity;
|
|
7
|
+
file: string;
|
|
8
|
+
line: number;
|
|
9
|
+
message: string;
|
|
10
|
+
snippet: string;
|
|
11
|
+
};
|
|
12
|
+
export type DetectResult = {
|
|
13
|
+
scanned: string[];
|
|
14
|
+
findings: Finding[];
|
|
15
|
+
counts: {
|
|
16
|
+
error: number;
|
|
17
|
+
warn: number;
|
|
18
|
+
total: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare const JARGON_TERMS: readonly ["packet", "asp", "spec_id", "tasks.yaml", "events.jsonl", "done_when", "reconcile", "loop contract"];
|
|
22
|
+
export declare function runDetect(root: string, target: string): DetectResult;
|
|
23
|
+
export declare function renderDetect(result: DetectResult, opts: {
|
|
24
|
+
json: boolean;
|
|
25
|
+
exitOnFindings: boolean;
|
|
26
|
+
strict: boolean;
|
|
27
|
+
}): {
|
|
28
|
+
output: string;
|
|
29
|
+
exitCode: number;
|
|
30
|
+
};
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, lstatSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse as parseYaml } from "./vendorYaml.js";
|
|
4
|
+
// Prose rules run line-by-line over spec.md (and any scanned .md). Patterns are
|
|
5
|
+
// case-insensitive and word-bounded so they fire on the tell, not on substrings.
|
|
6
|
+
const PROSE_RULES = [
|
|
7
|
+
{
|
|
8
|
+
id: "CT-001",
|
|
9
|
+
category: "confidence-theater",
|
|
10
|
+
severity: "error",
|
|
11
|
+
pattern: /\b(100%\s+(?:reliable|accurate|safe)|guaranteed|never fails?|zero risk|fully solved|completely eliminat\w*|bullet-?proof|fool-?proof|always works)\b/i,
|
|
12
|
+
message: "absolute-certainty claim — a spec states a probability, not a guarantee; bound it with proof.",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "CT-002",
|
|
16
|
+
category: "confidence-theater",
|
|
17
|
+
severity: "warn",
|
|
18
|
+
pattern: /\b(world-?class|best-?in-?class|cutting-?edge|state-of-the-art|revolutionary|game-?chang\w*|seamless\w*|magical|delightful|frictionless|blazing(?:ly)?|next-?gen\w*)\b/i,
|
|
19
|
+
message: "marketing adjective in a spec — describe the behavior and its proof, not the vibe.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "CT-003",
|
|
23
|
+
category: "confidence-theater",
|
|
24
|
+
severity: "warn",
|
|
25
|
+
pattern: /\b(just works|obviously|clearly|simply put|it'?s trivial|no-brainer|of course)\b/i,
|
|
26
|
+
message: "hand-wave — 'obvious/just works' hides an unstated assumption; name it.",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "EG-001",
|
|
30
|
+
category: "evidence-gap",
|
|
31
|
+
severity: "warn",
|
|
32
|
+
// Weasel attributions: a claim leaning on an unnamed authority.
|
|
33
|
+
pattern: /\b(studies show|research shows|experts? (?:say|agree)|everyone knows|it'?s well[- ]known|users? (?:want|love|hate|expect)|the data shows)\b/i,
|
|
34
|
+
message: "unsourced authority — attribute the claim to a named source (link, R-id, or evidence ref).",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "SD-001",
|
|
38
|
+
category: "scope-drift",
|
|
39
|
+
severity: "warn",
|
|
40
|
+
// Case-sensitive: the leftover-marker convention is uppercase. This avoids
|
|
41
|
+
// flagging specs that legitimately discuss the lowercase task statuses
|
|
42
|
+
// ("todo", "doing") or the concept word "placeholder".
|
|
43
|
+
pattern: /(\bTODO\b|\bTBD\b|\bFIXME\b|\bXXX\b|\?\?\?)/,
|
|
44
|
+
message: "unresolved leftover marker in a committed spec — resolve or move to scratch.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "SD-002",
|
|
48
|
+
category: "scope-drift",
|
|
49
|
+
severity: "warn",
|
|
50
|
+
pattern: /\b(lorem ipsum|fill in later|to be written|coming soon)\b/i,
|
|
51
|
+
message: "unfinished filler in a committed spec — write it or move it to scratch.",
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
// A quantified claim (40%, 2x, 3.5 points) carries authority; on its own line it
|
|
55
|
+
// should sit next to a source. These markers count as "sourced enough".
|
|
56
|
+
const QUANT_CLAIM = /(?<![\w.])(\d+(?:\.\d+)?\s?%|\d+(?:\.\d+)?x\b|\d+(?:\.\d+)?\s?(?:ms|x faster|x fewer|points?|percent)\b)/i;
|
|
57
|
+
const SOURCE_MARKER = /(https?:\/\/|source:|evidence|\bref\b|cite|\[\d+\]|§|\bR-\d+\b|\bC-\d+\b|footnote|per\s)/i;
|
|
58
|
+
// JN-001 (jargon-supply-chain-v0 R-003): foo-internal vocabulary on a SHIPPED
|
|
59
|
+
// surface — commands/ and skills/ prose gets pulled into the customer's agent's
|
|
60
|
+
// context and relayed into their chat, so jargon here reaches a customer, not a
|
|
61
|
+
// contributor. The list is the same one the PM surface bans (single source of
|
|
62
|
+
// truth); the match is case-insensitive (the old check was beaten by the Shift
|
|
63
|
+
// key) and token-bounded with the B5 lesson applied — `(?<![\w-])`, not `\b`,
|
|
64
|
+
// because a hyphen joins a compound word rather than starting a new one.
|
|
65
|
+
// Signal only: JN findings are warns, and detect never blocks on its own.
|
|
66
|
+
const SHIPPED_SURFACE = /^(commands|skills)\//;
|
|
67
|
+
// Canonical banned-word list. The PM surface (initCommand.ts) re-exports this
|
|
68
|
+
// as PM_SURFACE_FORBIDDEN — one list, two consumers, no drift.
|
|
69
|
+
export const JARGON_TERMS = ["packet", "asp", "spec_id", "tasks.yaml", "events.jsonl", "done_when", "reconcile", "loop contract"];
|
|
70
|
+
const JARGON_PATTERN = new RegExp(`(?<![\\w-])(${JARGON_TERMS.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})(?![\\w-])`, "i");
|
|
71
|
+
// A command invocation must name the command (`foo asp validate` cannot avoid
|
|
72
|
+
// "asp"), so inline code spans don't count as prose jargon. Blank the span
|
|
73
|
+
// content, keep length, so match indexes still point at the real line.
|
|
74
|
+
function blankInlineCode(line) {
|
|
75
|
+
return line.replace(/`[^`]*`/g, (m) => "`".padEnd(m.length - 1, " ") + "`");
|
|
76
|
+
}
|
|
77
|
+
function isFrontmatterFence(line) {
|
|
78
|
+
return line.trim() === "---";
|
|
79
|
+
}
|
|
80
|
+
// Scan markdown prose. Skips YAML frontmatter and fenced code blocks so a code
|
|
81
|
+
// sample that contains "TODO" or a URL-less number does not trip a rule.
|
|
82
|
+
function scanProse(rel, text) {
|
|
83
|
+
const findings = [];
|
|
84
|
+
const lines = text.split(/\r?\n/);
|
|
85
|
+
let inCodeFence = false;
|
|
86
|
+
let inFrontmatter = false;
|
|
87
|
+
let frontmatterClosed = false;
|
|
88
|
+
for (let i = 0; i < lines.length; i++) {
|
|
89
|
+
const raw = lines[i];
|
|
90
|
+
const trimmed = raw.trim();
|
|
91
|
+
// Frontmatter: only the leading block, only at file start.
|
|
92
|
+
if (i === 0 && isFrontmatterFence(raw)) {
|
|
93
|
+
inFrontmatter = true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (inFrontmatter) {
|
|
97
|
+
if (isFrontmatterFence(raw)) {
|
|
98
|
+
inFrontmatter = false;
|
|
99
|
+
frontmatterClosed = true;
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
void frontmatterClosed;
|
|
104
|
+
if (trimmed.startsWith("```") || trimmed.startsWith("~~~")) {
|
|
105
|
+
inCodeFence = !inCodeFence;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (inCodeFence)
|
|
109
|
+
continue;
|
|
110
|
+
// HTML comments are spec scaffolding, not committed product claims.
|
|
111
|
+
if (trimmed.startsWith("<!--"))
|
|
112
|
+
continue;
|
|
113
|
+
const lineNo = i + 1;
|
|
114
|
+
// Jargon fires only on shipped surfaces; a spec is allowed its own words.
|
|
115
|
+
if (SHIPPED_SURFACE.test(rel)) {
|
|
116
|
+
const prose = blankInlineCode(raw);
|
|
117
|
+
const jargonMatch = JARGON_PATTERN.exec(prose);
|
|
118
|
+
if (jargonMatch) {
|
|
119
|
+
findings.push({
|
|
120
|
+
rule: "JN-001",
|
|
121
|
+
category: "jargon",
|
|
122
|
+
severity: "warn",
|
|
123
|
+
file: rel,
|
|
124
|
+
line: lineNo,
|
|
125
|
+
message: "foo-internal vocabulary on a shipped surface — this prose reaches the customer's chat; use the plain word.",
|
|
126
|
+
snippet: snippetAround(raw, jargonMatch.index),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
for (const rule of PROSE_RULES) {
|
|
131
|
+
const match = rule.pattern.exec(raw);
|
|
132
|
+
if (match) {
|
|
133
|
+
findings.push({
|
|
134
|
+
rule: rule.id,
|
|
135
|
+
category: rule.category,
|
|
136
|
+
severity: rule.severity,
|
|
137
|
+
file: rel,
|
|
138
|
+
line: lineNo,
|
|
139
|
+
message: rule.message,
|
|
140
|
+
snippet: snippetAround(raw, match.index),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// EG-002: quantified claim with no nearby source marker (this line or the
|
|
145
|
+
// line before/after, to allow a citation on the next line).
|
|
146
|
+
if (QUANT_CLAIM.test(raw)) {
|
|
147
|
+
const context = `${lines[i - 1] ?? ""}\n${raw}\n${lines[i + 1] ?? ""}`;
|
|
148
|
+
if (!SOURCE_MARKER.test(context)) {
|
|
149
|
+
const m = QUANT_CLAIM.exec(raw);
|
|
150
|
+
findings.push({
|
|
151
|
+
rule: "EG-002",
|
|
152
|
+
category: "evidence-gap",
|
|
153
|
+
severity: "warn",
|
|
154
|
+
file: rel,
|
|
155
|
+
line: lineNo,
|
|
156
|
+
message: "quantified claim with no source — put a link, R-id, or evidence ref beside the number.",
|
|
157
|
+
snippet: snippetAround(raw, m ? m.index : 0),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// AG-001: a spec.md with no acceptance surface at all. Shipped command docs
|
|
163
|
+
// are exempt — commands/spec.md is the /foo:spec command's doc, not a spec.
|
|
164
|
+
if (path.basename(rel) === "spec.md" && !SHIPPED_SURFACE.test(rel)) {
|
|
165
|
+
const hasAcceptance = /(##\s*(acceptance|done[- ]when|validation|success)|done_when|acceptance criteria)/i.test(text);
|
|
166
|
+
if (!hasAcceptance) {
|
|
167
|
+
findings.push({
|
|
168
|
+
rule: "AG-001",
|
|
169
|
+
category: "acceptance-gap",
|
|
170
|
+
severity: "error",
|
|
171
|
+
file: rel,
|
|
172
|
+
line: 1,
|
|
173
|
+
message: "no acceptance/validation surface — a spec without a testable 'done' is a wish, not a contract.",
|
|
174
|
+
snippet: "(whole file)",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return findings;
|
|
179
|
+
}
|
|
180
|
+
function snippetAround(line, index) {
|
|
181
|
+
const start = Math.max(0, index - 12);
|
|
182
|
+
const end = Math.min(line.length, index + 48);
|
|
183
|
+
return `${start > 0 ? "…" : ""}${line.slice(start, end).trim()}${end < line.length ? "…" : ""}`;
|
|
184
|
+
}
|
|
185
|
+
/** #884: done_when as criteria. A string is one unproven criterion; an array
|
|
186
|
+
* keeps its object entries; anything else is no criteria. */
|
|
187
|
+
function doneWhenCriteria(value) {
|
|
188
|
+
if (typeof value === "string")
|
|
189
|
+
return value.trim() ? [{ text: value }] : [];
|
|
190
|
+
if (!Array.isArray(value))
|
|
191
|
+
return [];
|
|
192
|
+
return value.filter((entry) => entry !== null && typeof entry === "object");
|
|
193
|
+
}
|
|
194
|
+
function hasValue(v) {
|
|
195
|
+
if (typeof v === "string")
|
|
196
|
+
return v.trim().length > 0;
|
|
197
|
+
if (Array.isArray(v))
|
|
198
|
+
return v.length > 0;
|
|
199
|
+
return Boolean(v);
|
|
200
|
+
}
|
|
201
|
+
// Structural rules over tasks.yaml: a claim of "done" with no receipt is the
|
|
202
|
+
// purest form of product slop. These are errors — "Done is a claim. Proof is a
|
|
203
|
+
// receipt." (foo voice).
|
|
204
|
+
function scanTasks(rel, text) {
|
|
205
|
+
const findings = [];
|
|
206
|
+
let parsed;
|
|
207
|
+
try {
|
|
208
|
+
parsed = parseYaml(text);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return findings; // a malformed tasks.yaml is asp-validate's job, not detect's.
|
|
212
|
+
}
|
|
213
|
+
const tasks = Array.isArray(parsed?.tasks) ? parsed.tasks : [];
|
|
214
|
+
// Line lookup so a finding points at the offending task id.
|
|
215
|
+
const lineOf = (needle) => {
|
|
216
|
+
const idx = text.split(/\r?\n/).findIndex((l) => l.includes(needle));
|
|
217
|
+
return idx === -1 ? 1 : idx + 1;
|
|
218
|
+
};
|
|
219
|
+
for (const task of tasks) {
|
|
220
|
+
const id = typeof task.id === "string" ? task.id : "(unnamed task)";
|
|
221
|
+
// #884: done_when is a list of criteria, but hand-written packets carry a
|
|
222
|
+
// plain string (a single criterion) and other shapes. Read it defensively —
|
|
223
|
+
// a malformed packet is `asp validate`'s finding, never a detect crash.
|
|
224
|
+
const doneWhen = doneWhenCriteria(task.done_when);
|
|
225
|
+
const status = typeof task.status === "string" ? task.status : "";
|
|
226
|
+
// A task is proven if it carries a receipt anywhere the contract allows:
|
|
227
|
+
// a top-level proof, a completed.proof block, or proof on its done_when
|
|
228
|
+
// criteria. Only "done with no receipt at all" is slop.
|
|
229
|
+
const completedProof = task.completed && typeof task.completed === "object"
|
|
230
|
+
? hasValue(task.completed.proof)
|
|
231
|
+
: false;
|
|
232
|
+
const doneWhenProof = doneWhen.some((dw) => hasValue(dw.proof));
|
|
233
|
+
const hasProof = hasValue(task.proof) || completedProof || doneWhenProof;
|
|
234
|
+
if (status === "done" && !hasProof) {
|
|
235
|
+
findings.push({
|
|
236
|
+
rule: "PG-001",
|
|
237
|
+
category: "proof-gap",
|
|
238
|
+
severity: "error",
|
|
239
|
+
file: rel,
|
|
240
|
+
line: lineOf(`id: ${id}`),
|
|
241
|
+
message: `${id} is done with no proof — done is a claim; attach the receipt (proof:).`,
|
|
242
|
+
snippet: `${id}: status: done`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
for (const dw of doneWhen) {
|
|
246
|
+
const dwId = typeof dw.id === "string" ? dw.id : id;
|
|
247
|
+
const dwDone = dw.done === true;
|
|
248
|
+
const dwProof = typeof dw.proof === "string" ? dw.proof.trim().length > 0 : Boolean(dw.proof);
|
|
249
|
+
if (dwDone && !dwProof) {
|
|
250
|
+
findings.push({
|
|
251
|
+
rule: "PG-002",
|
|
252
|
+
category: "proof-gap",
|
|
253
|
+
severity: "error",
|
|
254
|
+
file: rel,
|
|
255
|
+
line: lineOf(`id: ${dwId}`),
|
|
256
|
+
message: `${dwId} (done_when) is checked done with no proof — every passed criterion needs a receipt.`,
|
|
257
|
+
snippet: `${dwId}: done: true`,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return findings;
|
|
263
|
+
}
|
|
264
|
+
const SCAN_SKIP_DIRS = new Set([".git", "node_modules", "dist", ".worktrees", "docs"]);
|
|
265
|
+
// Resolve a target into the concrete files detect reads. A packet dir → its
|
|
266
|
+
// spec.md + tasks.yaml; a directory of packets → each packet's pair; a single
|
|
267
|
+
// file → that file.
|
|
268
|
+
function collectTargets(target, acc) {
|
|
269
|
+
if (!existsSync(target))
|
|
270
|
+
throw new Error(`detect target not found: ${target}`);
|
|
271
|
+
const stat = lstatSync(target);
|
|
272
|
+
if (stat.isFile()) {
|
|
273
|
+
acc.push(target);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (!stat.isDirectory())
|
|
277
|
+
return;
|
|
278
|
+
if (SCAN_SKIP_DIRS.has(path.basename(target)))
|
|
279
|
+
return;
|
|
280
|
+
const spec = path.join(target, "spec.md");
|
|
281
|
+
const tasks = path.join(target, "tasks.yaml");
|
|
282
|
+
const isPacket = existsSync(spec);
|
|
283
|
+
if (isPacket) {
|
|
284
|
+
acc.push(spec);
|
|
285
|
+
if (existsSync(tasks))
|
|
286
|
+
acc.push(tasks);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
// Not a packet itself — recurse into sub-packets / markdown. Any .md counts:
|
|
290
|
+
// shipped prose (commands/, skills/) is exactly the surface the jargon signal
|
|
291
|
+
// exists for, and a stray .md in a packet home is still committed prose.
|
|
292
|
+
for (const entry of readdirSync(target).sort()) {
|
|
293
|
+
const full = path.join(target, entry);
|
|
294
|
+
const s = lstatSync(full);
|
|
295
|
+
if (s.isDirectory())
|
|
296
|
+
collectTargets(full, acc);
|
|
297
|
+
else if (entry.endsWith(".md") || entry === "tasks.yaml")
|
|
298
|
+
acc.push(full);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
export function runDetect(root, target) {
|
|
302
|
+
const resolved = path.resolve(root, target);
|
|
303
|
+
const files = [];
|
|
304
|
+
collectTargets(resolved, files);
|
|
305
|
+
const findings = [];
|
|
306
|
+
const scanned = [];
|
|
307
|
+
for (const file of files) {
|
|
308
|
+
const rel = path.relative(root, file).split(path.sep).join("/") || file;
|
|
309
|
+
let text;
|
|
310
|
+
try {
|
|
311
|
+
text = readFileSync(file, "utf8");
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
scanned.push(rel);
|
|
317
|
+
const base = path.basename(file);
|
|
318
|
+
if (base === "tasks.yaml")
|
|
319
|
+
findings.push(...scanTasks(rel, text));
|
|
320
|
+
else if (base.endsWith(".md"))
|
|
321
|
+
findings.push(...scanProse(rel, text));
|
|
322
|
+
}
|
|
323
|
+
findings.sort((a, b) => (a.file === b.file ? a.line - b.line : a.file.localeCompare(b.file)));
|
|
324
|
+
const counts = {
|
|
325
|
+
error: findings.filter((f) => f.severity === "error").length,
|
|
326
|
+
warn: findings.filter((f) => f.severity === "warn").length,
|
|
327
|
+
total: findings.length,
|
|
328
|
+
};
|
|
329
|
+
return { scanned, findings, counts };
|
|
330
|
+
}
|
|
331
|
+
const CATEGORY_LABEL = {
|
|
332
|
+
"confidence-theater": "confidence theater",
|
|
333
|
+
"proof-gap": "proof gap",
|
|
334
|
+
"evidence-gap": "evidence gap",
|
|
335
|
+
"acceptance-gap": "acceptance gap",
|
|
336
|
+
"scope-drift": "scope drift",
|
|
337
|
+
jargon: "jargon",
|
|
338
|
+
};
|
|
339
|
+
// Render the report. exitCode mirrors `foo scan`: 0 by default (pure signal),
|
|
340
|
+
// and 2 when `--exit-code` is set and an error-severity finding exists, so CI
|
|
341
|
+
// can opt into blocking on product slop. --strict makes warns count too.
|
|
342
|
+
export function renderDetect(result, opts) {
|
|
343
|
+
const blockingCount = opts.strict ? result.counts.total : result.counts.error;
|
|
344
|
+
const exitCode = opts.exitOnFindings && blockingCount > 0 ? 2 : 0;
|
|
345
|
+
if (opts.json) {
|
|
346
|
+
const payload = {
|
|
347
|
+
schema_version: "0.1",
|
|
348
|
+
scanned: result.scanned,
|
|
349
|
+
counts: result.counts,
|
|
350
|
+
findings: result.findings,
|
|
351
|
+
};
|
|
352
|
+
return { output: `${JSON.stringify(payload, null, 2)}\n`, exitCode };
|
|
353
|
+
}
|
|
354
|
+
const lines = ["foo detect", ""];
|
|
355
|
+
if (result.findings.length === 0) {
|
|
356
|
+
lines.push(` no product-slop tells across ${result.scanned.length} file(s).`);
|
|
357
|
+
lines.push("");
|
|
358
|
+
return { output: lines.join("\n"), exitCode };
|
|
359
|
+
}
|
|
360
|
+
let lastFile = "";
|
|
361
|
+
for (const f of result.findings) {
|
|
362
|
+
if (f.file !== lastFile) {
|
|
363
|
+
lines.push(` ${f.file}`);
|
|
364
|
+
lastFile = f.file;
|
|
365
|
+
}
|
|
366
|
+
const tag = f.severity === "error" ? "error" : "warn ";
|
|
367
|
+
lines.push(` ${f.line}:${tag} [${f.rule}] ${CATEGORY_LABEL[f.category]} — ${f.message}`);
|
|
368
|
+
if (f.snippet)
|
|
369
|
+
lines.push(` ${f.snippet}`);
|
|
370
|
+
}
|
|
371
|
+
lines.push("");
|
|
372
|
+
lines.push(` ${result.counts.error} error · ${result.counts.warn} warn across ${result.scanned.length} file(s).`);
|
|
373
|
+
lines.push(" Signal, not a boundary: detect never blocks on its own. Add --exit-code to block CI on errors (--strict to include warns).");
|
|
374
|
+
lines.push("");
|
|
375
|
+
return { output: lines.join("\n"), exitCode };
|
|
376
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
export type ProfileRole = "product" | "engineer" | "designer" | "founder" | "executive";
|
|
3
|
+
export declare const PRODUCT_VIEW_ROLES: readonly ProfileRole[];
|
|
4
|
+
export interface FooProfile {
|
|
5
|
+
roles?: ProfileRole[];
|
|
6
|
+
/** #622: every coding agent the person builds with, in the order given. */
|
|
7
|
+
harnesses?: string[];
|
|
8
|
+
/** The first of `harnesses`, kept for readers of older profiles and the picks report. */
|
|
9
|
+
harness?: string;
|
|
10
|
+
building?: string;
|
|
11
|
+
team?: "just-me" | "small-team" | "larger-org";
|
|
12
|
+
answered_at: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function machineProfilePath(): string;
|
|
15
|
+
export declare function profilePath(root: string): string;
|
|
16
|
+
export declare function loadProfile(root: string): FooProfile | null;
|
|
17
|
+
export declare function saveProfile(profile: FooProfile, opts?: {
|
|
18
|
+
scope?: "machine" | "repo";
|
|
19
|
+
root?: string;
|
|
20
|
+
}): void;
|
|
21
|
+
export declare const SURVEY_INTRO = "It also reads you \u2014 a few quick questions so the work fits how you work. Enter skips any of them.";
|
|
22
|
+
export declare const SURVEY_Q_ROLE = "What's your role? Pick any \u2014 this chooses the view productfoo leads with. 1 product \u00B7 2 engineer \u00B7 3 designer \u00B7 4 founder \u00B7 5 executive";
|
|
23
|
+
export declare const SURVEY_Q_HARNESS = "Which coding agents do you build with? Pick any \u2014 productfoo offers to set up its commands in these. 1 Claude Code \u00B7 2 Cursor \u00B7 3 Codex \u00B7 or type them";
|
|
24
|
+
export declare const SURVEY_Q_BUILDING = "What are you building? A few words is plenty \u2014 this stays on your machine.";
|
|
25
|
+
export declare const SURVEY_Q_TEAM = "Who's building with you? 1 just me \u00B7 2 a small team \u00B7 3 a bigger org";
|
|
26
|
+
export declare const SURVEY_CLOSE_PRODUCT = "productfoo leads with the product view from here \u2014 no flags needed. Redo any time: foo profile";
|
|
27
|
+
export declare const SURVEY_CLOSE_SAVED = "Saved \u2014 productfoo keeps this on your machine. Redo any time: foo profile";
|
|
28
|
+
export declare const SURVEY_CLOSE_SKIPPED = "Nothing saved \u2014 productfoo stays as-is. Ask again any time: foo profile";
|
|
29
|
+
export declare const SURVEY_DIDNT_CATCH = "didn't catch";
|
|
30
|
+
export declare const PROFILE_NONE = "foo hasn't read you yet. Run it any time: foo profile redo";
|
|
31
|
+
export declare const PROFILE_REDO_NEEDS_TTY = "Redo needs a live terminal \u2014 run foo profile redo where you can type.";
|
|
32
|
+
export declare const SURVEY_CORPUS: readonly string[];
|
|
33
|
+
export declare function parseRoles(answer: string): ProfileRole[];
|
|
34
|
+
export declare function parseTeam(answer: string): FooProfile["team"] | undefined;
|
|
35
|
+
export declare function parseHarnesses(answer: string): string[];
|
|
36
|
+
export declare function parseHarness(answer: string): string | undefined;
|
|
37
|
+
/** Every harness a profile names, from the plural field or an older single one. */
|
|
38
|
+
export declare function profileHarnesses(profile: Pick<FooProfile, "harnesses" | "harness"> | null | undefined): string[];
|
|
39
|
+
export declare function harnessLabel(id: string): string;
|
|
40
|
+
export declare function renderProfileReadback(profile: FooProfile): string;
|
|
41
|
+
export declare function surveyClose(profile: FooProfile): string;
|
|
42
|
+
export interface SurveyIO {
|
|
43
|
+
input: Readable;
|
|
44
|
+
output: Writable;
|
|
45
|
+
}
|
|
46
|
+
export declare function runFirstRunSurvey(io: SurveyIO, now?: () => string): Promise<FooProfile>;
|
|
47
|
+
export declare function profileReportEvent(profile: FooProfile, version: string): Record<string, unknown> | null;
|
|
48
|
+
export declare function reportProfile(root: string, profile: FooProfile, version: string, env?: NodeJS.ProcessEnv): Promise<void> | undefined;
|
|
49
|
+
export declare function renderProfileStatus(root: string): string;
|
|
50
|
+
export declare function runProfileRedo(root: string, version: string, io?: SurveyIO & {
|
|
51
|
+
inputTTY?: boolean;
|
|
52
|
+
outputTTY?: boolean;
|
|
53
|
+
}): Promise<string>;
|
|
54
|
+
export interface ProfileSetInput {
|
|
55
|
+
roles?: string;
|
|
56
|
+
harness?: string;
|
|
57
|
+
team?: string;
|
|
58
|
+
building?: string;
|
|
59
|
+
json?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export declare const PROFILE_SET_EMPTY = "Nothing to set \u2014 give at least one of --role, --harness, --team, --building.";
|
|
62
|
+
export declare function runProfileSet(root: string, version: string, input: ProfileSetInput, now?: () => string): string;
|