naive-framework 0.0.0-stage → 0.2.0
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/AGENTS.md +62 -0
- package/LICENSE +21 -0
- package/README.md +37 -2
- package/docs/decisions/decisions.json +888 -0
- package/docs/ontology.json +796 -0
- package/docs/ontology.md +163 -0
- package/docs/reference/ROUTES.md +124 -0
- package/evidence/benchmarks/2026-10-27-golden.jev.after-catalog-fix.json +558 -0
- package/evidence/benchmarks/2026-10-27-golden.jev.json +501 -0
- package/evidence/benchmarks/2026-10-27-golden.reasoning-answers.json +177 -0
- package/evidence/benchmarks/JEV_VS_REASONING_MODEL_2026-10-27.md +68 -0
- package/evidence/capabilities/agent-capabilities.json +86 -0
- package/evidence/design-documents.registry.json +2896 -0
- package/evidence/evals/evals.json +25 -0
- package/evidence/evals/harness-evals.json +140 -0
- package/evidence/layer.json +51 -0
- package/evidence/skills/agent-capabilities/SKILL.md +35 -0
- package/evidence/skills/design-coverage/SKILL.md +57 -0
- package/evidence/skills/design-coverage/check.mjs +192 -0
- package/evidence/skills/design-coverage/scan.mjs +45 -0
- package/evidence/skills/fidelity-ladder-audit/SKILL.md +121 -0
- package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +180 -0
- package/evidence/skills/findability/SKILL.md +32 -0
- package/evidence/skills/findability/inventory.rb +41 -0
- package/evidence/skills/foreshadowing-audit/SKILL.md +183 -0
- package/evidence/skills/metrics-review/SKILL.md +177 -0
- package/evidence/tooling/integrations.json +153 -0
- package/governance/layer.json +102 -0
- package/governance/references/references.json +26 -0
- package/governance/skills/board-design/SKILL.md +68 -0
- package/governance/skills/brand-pillar/SKILL.md +90 -0
- package/governance/skills/business-pillar/SKILL.md +96 -0
- package/governance/skills/chief-of-staff/SKILL.md +45 -0
- package/governance/skills/cofounder-structure/SKILL.md +64 -0
- package/governance/skills/cross-pillar-tradeoff/SKILL.md +168 -0
- package/governance/skills/equity-and-share-classes/SKILL.md +68 -0
- package/governance/skills/executive-roles/SKILL.md +95 -0
- package/governance/skills/exit-planning/SKILL.md +66 -0
- package/governance/skills/financing-and-investor-rights/SKILL.md +73 -0
- package/governance/skills/office-hours-mode/SKILL.md +113 -0
- package/governance/skills/product-pillar/SKILL.md +81 -0
- package/governance/skills/publication-policy/SKILL.md +63 -0
- package/governance/skills/role-and-candidate-authoring/SKILL.md +198 -0
- package/governance/skills/stakeholder-update/SKILL.md +177 -0
- package/governance/tooling/integrations.json +37 -0
- package/governance/venture-template/AGENT_ROLES_AND_DECISION_RIGHTS.md +58 -0
- package/governance/venture-template/COMPANY_VAULT_DEFINITION_2026-10-25.md +204 -0
- package/governance/venture-template/README.md +74 -0
- package/governance/venture-template/decision-rights.json +186 -0
- package/governance/venture-template/index.md +151 -0
- package/governance/venture-template/pillars/brand.md +209 -0
- package/governance/venture-template/pillars/business.md +255 -0
- package/governance/venture-template/pillars/product.md +254 -0
- package/governance/venture-template/render-roles.py +11 -0
- package/governance/venture-template/roles/README.md +85 -0
- package/governance/venture-template/roles/agent-roles.json +202 -0
- package/governance/venture-template/roles/ceo.md +163 -0
- package/governance/venture-template/roles/chief-of-staff.md +153 -0
- package/governance/venture-template/roles/cmo.md +165 -0
- package/governance/venture-template/roles/coo.md +189 -0
- package/governance/venture-template/roles/cto.md +158 -0
- package/governance/venture-template/roles/department-head.md +47 -0
- package/governance/venture-template/roles/forward-deployed-engineer.md +51 -0
- package/governance/venture-template/roles/founder.md +173 -0
- package/governance/venture-template/roles/hr-manager.md +49 -0
- package/governance/venture-template/roles/talent-acquisition-manager.md +50 -0
- package/governance/venture-template/roles/venture-roles.json +62 -0
- package/interface/acting.mjs +83 -0
- package/interface/adapters/claude-code.json +16 -0
- package/interface/adapters/codex.json +14 -0
- package/interface/adapters/cursor.json +16 -0
- package/interface/adapters.mjs +182 -0
- package/interface/api/server.mjs +71 -0
- package/interface/boundaries.mjs +218 -0
- package/interface/brand.mjs +214 -0
- package/interface/cli/find-venture.mjs +15 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +341 -0
- package/interface/cli/naive.mjs +194 -0
- package/interface/context.mjs +524 -0
- package/interface/demo/board.mjs +581 -0
- package/interface/demo/decision-harness.mjs +91 -0
- package/interface/demo/live-agent.mjs +47 -0
- package/interface/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +36 -0
- package/interface/harness/evals.mjs +90 -0
- package/interface/harness/examples/pt-no-shows.dump.md +19 -0
- package/interface/harness/run.mjs +313 -0
- package/interface/harness/site.mjs +59 -0
- package/interface/instrumentation.mjs +86 -0
- package/interface/kit-boundary.mjs +73 -0
- package/interface/layer.json +37 -0
- package/interface/mcp/server.mjs +162 -0
- package/interface/operator-console/README.md +58 -0
- package/interface/package-routes.mjs +196 -0
- package/interface/provenance.mjs +65 -0
- package/interface/reads.mjs +173 -0
- package/interface/references/references.json +28 -0
- package/interface/release-card.mjs +42 -0
- package/interface/route-aliases.json +32 -0
- package/interface/router.mjs +653 -0
- package/interface/skills/install-mentura/SKILL.md +88 -0
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/tooling/integrations.json +112 -0
- package/interface/vitals.mjs +115 -0
- package/interface/writes.mjs +190 -0
- package/knowledge/connectors/README.md +54 -0
- package/knowledge/connectors/catalog.mjs +118 -0
- package/knowledge/connectors/contract.mjs +56 -0
- package/knowledge/connectors/eval.mjs +176 -0
- package/knowledge/connectors/filesystem.mjs +79 -0
- package/knowledge/connectors/index.mjs +102 -0
- package/knowledge/connectors/resolve.mjs +44 -0
- package/knowledge/connectors/suite.mjs +80 -0
- package/knowledge/freshness.json +85 -0
- package/knowledge/layer.json +50 -0
- package/knowledge/skills/find-reference/SKILL.md +38 -0
- package/knowledge/skills/industry-awareness/SKILL.md +277 -0
- package/knowledge/templates/glossary.md +23 -0
- package/knowledge/tooling/integrations.json +127 -0
- package/knowledge/tooling/questions.md +46 -0
- package/operations/FOUNDER_OPERATING_MODEL_CAMPAIGN_2026-10-14.md +16 -0
- package/operations/departments.json +162 -0
- package/operations/handoff-v2.md +93 -0
- package/operations/layer.json +67 -0
- package/operations/marketing/check-public.mjs +68 -0
- package/operations/marketing/site/README.md +86 -0
- package/operations/marketing/site/matrix.json +731 -0
- package/operations/marketing/site/plan.mjs +117 -0
- package/operations/marketing/site/presence.mjs +252 -0
- package/operations/references/references.json +45 -0
- package/operations/skills/recruiting/SKILL.md +32 -0
- package/operations/skills/team-design/SKILL.md +36 -0
- package/operations/skills/tool-selection/SKILL.md +46 -0
- package/operations/templates/marketing-brand.md +46 -0
- package/operations/templates/public-assets.md +39 -0
- package/operations/templates/recruiting-brief.md +35 -0
- package/operations/tooling/integrations.json +158 -0
- package/package.json +59 -4
- package/packages/catalog.json +280 -0
- package/packages/cofounder/package.json +42 -0
- package/packages/counsel/package.json +47 -0
- package/packages/customers/package.json +41 -0
- package/packages/deals/package.json +27 -0
- package/packages/decisions/package.json +28 -0
- package/packages/engineering/package.json +41 -0
- package/packages/evidence/package.json +17 -0
- package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
- package/packages/finance/package.json +48 -0
- package/packages/foundation/package.json +59 -0
- package/packages/fund/package.json +48 -0
- package/packages/investor/package.json +57 -0
- package/packages/investors/package.json +21 -0
- package/packages/legal/package.json +73 -0
- package/packages/marketing/package.json +63 -0
- package/packages/money/package.json +42 -0
- package/packages/operations/package.json +74 -0
- package/packages/partner/package.json +38 -0
- package/packages/people/package.json +64 -0
- package/packages/product/package.json +49 -0
- package/packages/raw/package.json +19 -0
- package/packages/relationships/package.json +52 -0
- package/packages/validation/package.json +112 -0
- package/platform/README.md +51 -0
- package/platform/adapter.json +15 -0
- package/platform/connectors/eval.mjs +154 -0
- package/platform/connectors/fake-github.mjs +91 -0
- package/platform/connectors/github.mjs +105 -0
- package/platform/integrations/jev.mjs +30 -0
- package/platform/integrations/langsmith-docket.mjs +53 -0
- package/platform/integrations/langsmith-setup.mjs +115 -0
- package/platform/integrations/langsmith.mjs +47 -0
- package/platform/integrations/remote-image.mjs +79 -0
- package/platform/integrations/rho.mjs +146 -0
- package/platform/integrations/site.mjs +75 -0
- package/platform/kits/README.md +14 -0
- package/platform/kits/agent-upgrade/kit.json +15 -0
- package/platform/kits/agent-upgrade/run.mjs +82 -0
- package/platform/kits/agent-upgrade/stand-in-agent.mjs +32 -0
- package/platform/kits/validation-kickoff/kit.json +29 -0
- package/platform/mcp/research.mjs +64 -0
- package/platform/webhooks/server.mjs +70 -0
- package/purpose/checklists/vault-and-tooling-consultancy-setup.md +216 -0
- package/purpose/frameworks/business-discovery-framework-amendments-2026-05-20.md +181 -0
- package/purpose/frameworks/business-discovery-framework.html +938 -0
- package/purpose/frameworks/business-discovery-framework.md +326 -0
- package/purpose/frameworks/idea-map/README.md +74 -0
- package/purpose/frameworks/idea-map/conventions.json +146 -0
- package/purpose/frameworks/idea-map/graph.mjs +182 -0
- package/purpose/frameworks/idea-map/ideamap.mjs +181 -0
- package/purpose/frameworks/idea-map/route.mjs +140 -0
- package/purpose/frameworks/onboarding-handoff-framework.md +269 -0
- package/purpose/frameworks/post-discovery-deliverable-playbook.md +274 -0
- package/purpose/frameworks/product-anatomy/README.md +63 -0
- package/purpose/frameworks/product-anatomy/anatomy.mjs +195 -0
- package/purpose/frameworks/product-anatomy/examples/polity.answers.reasoned.json +11 -0
- package/purpose/frameworks/product-anatomy/examples/polity.json +89 -0
- package/purpose/frameworks/product-anatomy/phases.json +107 -0
- package/purpose/frameworks/product-anatomy/questions.json +74 -0
- package/purpose/kits/validation/README.md +14 -0
- package/purpose/kits/validation/references/references.json +122 -0
- package/purpose/kits/validation/skills/problem-validation/SKILL.md +43 -0
- package/purpose/kits/validation/skills/validation-score/SKILL.md +35 -0
- package/purpose/kits/validation/templates/go-to-market.md +19 -0
- package/purpose/kits/validation/templates/operations-readiness.md +20 -0
- package/purpose/kits/validation/templates/pricing.md +22 -0
- package/purpose/kits/validation/templates/problem-interview.md +36 -0
- package/purpose/kits/validation/templates/problem.md +45 -0
- package/purpose/kits/validation/templates/product-market-fit.md +23 -0
- package/purpose/kits/validation/templates/solution.md +18 -0
- package/purpose/kits/validation/templates/team.md +19 -0
- package/purpose/kits/validation/weights.json +22 -0
- package/purpose/layer.json +53 -0
- package/purpose/references/references.json +324 -0
- package/purpose/skills/ambition-tier/SKILL.md +205 -0
- package/purpose/skills/telos/SKILL.md +52 -0
- package/purpose/strategic-foundation.json +133 -0
- package/purpose/templates/telos-based-smart.md +39 -0
- package/purpose/tooling/integrations.json +48 -0
- package/schema/README.md +62 -0
- package/schema/VERSION +1 -0
- package/schema/access.mjs +57 -0
- package/schema/account.schema.json +79 -0
- package/schema/adapter.schema.json +33 -0
- package/schema/add-department.mjs +41 -0
- package/schema/agent-capability.schema.json +94 -0
- package/schema/alignment.schema.json +61 -0
- package/schema/assess-problem.mjs +131 -0
- package/schema/assets.mjs +212 -0
- package/schema/bdd.mjs +56 -0
- package/schema/board.schema.json +101 -0
- package/schema/brand.mjs +373 -0
- package/schema/build-release.mjs +61 -0
- package/schema/caching.mjs +505 -0
- package/schema/check-assets.mjs +24 -0
- package/schema/check-boundary.mjs +54 -0
- package/schema/check-brand.mjs +24 -0
- package/schema/check-config.mjs +110 -0
- package/schema/check-data-model.mjs +170 -0
- package/schema/check-data-packages.mjs +83 -0
- package/schema/check-departments.mjs +45 -0
- package/schema/check-execution.mjs +85 -0
- package/schema/check-governance.mjs +98 -0
- package/schema/check-integrations.mjs +33 -0
- package/schema/check-nextjs-status.mjs +79 -0
- package/schema/check-nouns.mjs +49 -0
- package/schema/check-people.mjs +70 -0
- package/schema/check-private.mjs +61 -0
- package/schema/check-provenance.mjs +55 -0
- package/schema/check-publication.mjs +96 -0
- package/schema/check-ready.mjs +89 -0
- package/schema/check-sources.mjs +74 -0
- package/schema/check-team.mjs +94 -0
- package/schema/check-telos.mjs +59 -0
- package/schema/check-validation.mjs +165 -0
- package/schema/check-ventures.mjs +81 -0
- package/schema/check-where-used.mjs +58 -0
- package/schema/data-master.schema.json +14 -0
- package/schema/data-model.json +1584 -0
- package/schema/data-model.schema.json +152 -0
- package/schema/deal.schema.json +80 -0
- package/schema/decision-analytics.mjs +77 -0
- package/schema/decision-record.schema.json +290 -0
- package/schema/decision-rights.schema.json +34 -0
- package/schema/decision.schema.json +30 -0
- package/schema/decisions.mjs +327 -0
- package/schema/department-catalog.schema.json +55 -0
- package/schema/department.schema.json +15 -0
- package/schema/departments.mjs +32 -0
- package/schema/docket-eval.mjs +89 -0
- package/schema/docket.mjs +135 -0
- package/schema/eval-suite.schema.json +45 -0
- package/schema/evidence.schema.json +110 -0
- package/schema/examples/frames/judge.mjs +10 -0
- package/schema/examples/migrate/agent.mjs +12 -0
- package/schema/examples/venture-type/jev.mjs +12 -0
- package/schema/examples/workspace/ventures/acme-labs/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/acme-labs/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/discovery-goal.decision.json +23 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/statement-icp.decision.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/statement-problem.decision.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/decisions/venture.team.decision.json +37 -0
- package/schema/examples/workspace/ventures/acme-labs/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/acme-labs/knowledge/sources.json +46 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000001.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000002.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000003.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000004.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000005.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000006.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000007.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000008.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000009.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000010.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000011.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000012.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000013.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/30000000-0000-4000-8000-000000000014.json +12 -0
- package/schema/examples/workspace/ventures/acme-labs/money/_streams/transactions/stream.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/money/_summary.json +30 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/customer-service/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/it/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/products/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/acme-labs/people/founders/maya-chen.json +15 -0
- package/schema/examples/workspace/ventures/acme-labs/people/people.json +22 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/acme-labs/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.core-product.decision.json +28 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.exists.decision.json +38 -0
- package/schema/examples/workspace/ventures/acme-labs/products/decisions/product.flagship.decision.json +27 -0
- package/schema/examples/workspace/ventures/acme-labs/profile.md +12 -0
- package/schema/examples/workspace/ventures/acme-labs/provenance.jsonl +12 -0
- package/schema/examples/workspace/ventures/acme-labs/raw/dump.md +6 -0
- package/schema/examples/workspace/ventures/acme-labs/venture.json +27 -0
- package/schema/examples/workspace/ventures/northstar/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/northstar/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/deals/ductline.json +8 -0
- package/schema/examples/workspace/ventures/northstar/deals/kilnworks.json +14 -0
- package/schema/examples/workspace/ventures/northstar/decisions/venture.team.decision.json +31 -0
- package/schema/examples/workspace/ventures/northstar/evidence/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/terms.first-close.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/fund/decisions/thesis.decision.json +26 -0
- package/schema/examples/workspace/ventures/northstar/fund/terms.json +22 -0
- package/schema/examples/workspace/ventures/northstar/fund/thesis.json +22 -0
- package/schema/examples/workspace/ventures/northstar/governance/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/external-relationships/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/finance/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/risk-and-compliance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/charter.md +5 -0
- package/schema/examples/workspace/ventures/northstar/governance/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/operations/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/external-relationships/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/risk-and-compliance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/people/founders/ana-ruiz.json +13 -0
- package/schema/examples/workspace/ventures/northstar/people/founders/ben-okafor.json +7 -0
- package/schema/examples/workspace/ventures/northstar/people/people.json +27 -0
- package/schema/examples/workspace/ventures/northstar/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/northstar/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/northstar/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/AGENTS.md +17 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/decisions/venture.team.decision.json +31 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/customer-service/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/human-capital/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/it/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/products/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/README.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/operations/departments/strategy/department.json +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.core-product.decision.json +28 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.exists.decision.json +32 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/products/decisions/product.flagship.decision.json +27 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/profile.md +5 -0
- package/schema/examples/workspace/ventures/northstar/portfolio/kilnworks/venture.json +27 -0
- package/schema/examples/workspace/ventures/northstar/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/northstar/profile.md +5 -0
- package/schema/examples/workspace/ventures/northstar/provenance.jsonl +3 -0
- package/schema/examples/workspace/ventures/northstar/purpose/README.md +3 -0
- package/schema/examples/workspace/ventures/northstar/venture.json +49 -0
- package/schema/examples/workspace/ventures/studio/accounts/bakery/account.json +18 -0
- package/schema/examples/workspace/ventures/studio/evidence/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/governance/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/knowledge/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/operations/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/print/knowledge/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/print/purpose/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/print/venture.json +37 -0
- package/schema/examples/workspace/ventures/studio/products/brand-care.json +19 -0
- package/schema/examples/workspace/ventures/studio/products/brand-kit.json +23 -0
- package/schema/examples/workspace/ventures/studio/provenance.jsonl +3 -0
- package/schema/examples/workspace/ventures/studio/purpose/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/studio/venture.json +43 -0
- package/schema/examples/workspace/ventures/tandem/accounts/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/tandem/evidence/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/agreements/dee-fast.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/board.json +51 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/finance/charter.md +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/charter.md +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/governance/exit.json +33 -0
- package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.json +24 -0
- package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.md +3 -0
- package/schema/examples/workspace/ventures/tandem/governance/ownership.json +131 -0
- package/schema/examples/workspace/ventures/tandem/governance/publication-policy.json +171 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/research/friends-feedback-2026-08.md +3 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/research/pilot-2026-10.md +3 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/research/problem-interviews-2026-09.md +20 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/sources.json +8 -0
- package/schema/examples/workspace/ventures/tandem/knowledge/where-used.json +13 -0
- package/schema/examples/workspace/ventures/tandem/operations/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/finance/README.md +5 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/finance/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/README.md +5 -0
- package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/department.json +5 -0
- package/schema/examples/workspace/ventures/tandem/pillars/brand.md +5 -0
- package/schema/examples/workspace/ventures/tandem/pillars/business.md +5 -0
- package/schema/examples/workspace/ventures/tandem/pillars/product.md +5 -0
- package/schema/examples/workspace/ventures/tandem/products/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/tandem/products/shop-app.json +22 -0
- package/schema/examples/workspace/ventures/tandem/profile.md +5 -0
- package/schema/examples/workspace/ventures/tandem/provenance.jsonl +13 -0
- package/schema/examples/workspace/ventures/tandem/purpose/README.md +3 -0
- package/schema/examples/workspace/ventures/tandem/purpose/telos.json +78 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/go-to-market.json +35 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/pricing.json +37 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/problem.json +74 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/product-market-fit.json +66 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/solution.json +33 -0
- package/schema/examples/workspace/ventures/tandem/purpose/validation/team.json +52 -0
- package/schema/examples/workspace/ventures/tandem/venture.json +79 -0
- package/schema/examples/workspace/ventures/woodworking/knowledge/.gitkeep +0 -0
- package/schema/examples/workspace/ventures/woodworking/venture.json +21 -0
- package/schema/examples/workspace/workspace.json +8 -0
- package/schema/exit.schema.json +74 -0
- package/schema/foundation.mjs +118 -0
- package/schema/founder-agreement.schema.json +83 -0
- package/schema/founder.schema.json +72 -0
- package/schema/frame.schema.json +240 -0
- package/schema/frames.mjs +228 -0
- package/schema/freshness-log.schema.json +40 -0
- package/schema/freshness-profiles.schema.json +44 -0
- package/schema/freshness.mjs +473 -0
- package/schema/fund-terms.schema.json +107 -0
- package/schema/fund-thesis.schema.json +87 -0
- package/schema/gate-drafts.mjs +130 -0
- package/schema/gate.mjs +133 -0
- package/schema/glossary.schema.json +25 -0
- package/schema/handoff.schema.json +239 -0
- package/schema/icp.schema.json +136 -0
- package/schema/idea-graph.schema.json +61 -0
- package/schema/init-venture.mjs +128 -0
- package/schema/integration.schema.json +143 -0
- package/schema/judgment.schema.json +38 -0
- package/schema/kit.schema.json +47 -0
- package/schema/kits.mjs +24 -0
- package/schema/knowledge-sources.schema.json +260 -0
- package/schema/layer.json +59 -0
- package/schema/layer.schema.json +119 -0
- package/schema/load-packages.mjs +26 -0
- package/schema/marketing-brand.schema.json +116 -0
- package/schema/marketing-sitemap.schema.json +232 -0
- package/schema/migrate-venture.mjs +192 -0
- package/schema/migrations/kit-to-package.mjs +94 -0
- package/schema/migrations/package-layout.mjs +86 -0
- package/schema/model-steps.mjs +717 -0
- package/schema/money-summary.schema.json +138 -0
- package/schema/naive-config.mjs +165 -0
- package/schema/naive-config.schema.json +268 -0
- package/schema/need-now.json +772 -0
- package/schema/need-now.mjs +108 -0
- package/schema/nextjs-status.schema.json +29 -0
- package/schema/ownership.schema.json +282 -0
- package/schema/packages.json +4 -0
- package/schema/packages.mjs +252 -0
- package/schema/payouts.mjs +91 -0
- package/schema/people.schema.json +86 -0
- package/schema/port-venture.mjs +161 -0
- package/schema/product.schema.json +223 -0
- package/schema/provenance-entry.schema.json +154 -0
- package/schema/public-assets.schema.json +76 -0
- package/schema/publication-policy.schema.json +293 -0
- package/schema/raw-source.schema.json +15 -0
- package/schema/reference.schema.json +115 -0
- package/schema/release-read.mjs +111 -0
- package/schema/releases.mjs +497 -0
- package/schema/route-aliases.schema.json +25 -0
- package/schema/run-handoff.mjs +51 -0
- package/schema/scaffold/defaults.json +54 -0
- package/schema/setup.mjs +164 -0
- package/schema/site-presence.schema.json +337 -0
- package/schema/stale.mjs +111 -0
- package/schema/start-release.mjs +41 -0
- package/schema/strategic-foundation.schema.json +74 -0
- package/schema/stream.schema.json +14 -0
- package/schema/structure.mjs +186 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -0
- package/schema/team-plan.schema.json +157 -0
- package/schema/telos.schema.json +197 -0
- package/schema/time-to-ready.mjs +93 -0
- package/schema/trigger.schema.json +44 -0
- package/schema/triggers.mjs +237 -0
- package/schema/upgrade-registry.schema.json +60 -0
- package/schema/upgrade-venture.mjs +107 -0
- package/schema/upgrades.json +69 -0
- package/schema/upgrades.mjs +385 -0
- package/schema/use-model.mjs +10 -0
- package/schema/validate.py +58 -0
- package/schema/validation-evidence.schema.json +123 -0
- package/schema/validation-go-to-market.schema.json +110 -0
- package/schema/validation-operations-readiness.schema.json +91 -0
- package/schema/validation-pricing.schema.json +114 -0
- package/schema/validation-problem.schema.json +118 -0
- package/schema/validation-product-market-fit.schema.json +135 -0
- package/schema/validation-solution.schema.json +79 -0
- package/schema/validation-team.schema.json +116 -0
- package/schema/venture-role.schema.json +64 -0
- package/schema/venture-type.mjs +62 -0
- package/schema/venture-types.json +346 -0
- package/schema/venture-types.schema.json +129 -0
- package/schema/venture.schema.json +460 -0
- package/schema/vocab/business-models.json +298 -0
- package/schema/where-used.schema.json +56 -0
- package/schema/workspace.schema.json +38 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Rho, the founder's bank and spend suite, as the venture's money source (platform, EE-041). Rho stays
|
|
3
|
+
// the system of record for money (EE-070): this writes the venture's money data package as pointers
|
|
4
|
+
// and a summary, never as Naive's own books. It speaks Rho's MCP server over HTTP (JSON-RPC, no
|
|
5
|
+
// dependencies) and calls only tools Rho marks read-only.
|
|
6
|
+
//
|
|
7
|
+
// node platform/integrations/rho.mjs sync --venture <dir> [--live] [--fixture <file>] [--save-fixture <file>]
|
|
8
|
+
//
|
|
9
|
+
// default Rho's sandbox (fictional company, any token, shared by all callers)
|
|
10
|
+
// --live the founder's Rho, with RHO_API_TOKEN (scopes accounts:read, transactions:read)
|
|
11
|
+
// --fixture replays a saved sync offline (tests, or a demo with no network)
|
|
12
|
+
// --save-fixture keeps what Rho answered, for --fixture later
|
|
13
|
+
//
|
|
14
|
+
// The package it writes (data package shape, PR #23 stage C):
|
|
15
|
+
// money/<rho id>.json master record, with a rho:account pointer
|
|
16
|
+
// money/_streams/transactions/stream.json sensitive: kept out of git and out of traces
|
|
17
|
+
// money/_streams/transactions/YYYY/MM/<date>-<id>.json append-only; a resync never rewrites one
|
|
18
|
+
// money/_summary.json versioned: cash, net burn, runway, as of when
|
|
19
|
+
//
|
|
20
|
+
// Amounts stay in minor units (cents for USD), as Rho returns them.
|
|
21
|
+
|
|
22
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
|
|
25
|
+
export const SANDBOX = "https://rhoapi-sandbox.rho.co/mcp/v1";
|
|
26
|
+
export const LIVE = "https://rhoapi.rho.co/mcp/v1";
|
|
27
|
+
const PROTOCOL = "2025-06-18";
|
|
28
|
+
|
|
29
|
+
// One MCP call over Streamable HTTP. Rho answers either JSON or a one-event SSE stream.
|
|
30
|
+
export function client(url, token) {
|
|
31
|
+
let id = 0, session = null, tools = null;
|
|
32
|
+
const rpc = async (method, params) => {
|
|
33
|
+
const res = await fetch(url, { method: "POST", signal: AbortSignal.timeout(60000), headers: {
|
|
34
|
+
"content-type": "application/json", accept: "application/json, text/event-stream", authorization: `Bearer ${token}`,
|
|
35
|
+
"mcp-protocol-version": PROTOCOL, ...(session ? { "mcp-session-id": session } : {}) }, body: JSON.stringify({ jsonrpc: "2.0", id: ++id, method, params }) });
|
|
36
|
+
if (!res.ok) throw new Error(`Rho answered ${res.status} to ${method}`);
|
|
37
|
+
session = res.headers.get("mcp-session-id") ?? session;
|
|
38
|
+
const body = await res.text();
|
|
39
|
+
const msg = JSON.parse(body.trimStart().startsWith("{") ? body : body.split("\n").filter((l) => l.startsWith("data: ")).map((l) => l.slice(6)).join(""));
|
|
40
|
+
if (msg.error) throw new Error(`Rho ${method}: ${msg.error.message}`);
|
|
41
|
+
return msg.result;
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
async init() {
|
|
45
|
+
await rpc("initialize", { protocolVersion: PROTOCOL, capabilities: {}, clientInfo: { name: "naive-money", version: "1" } });
|
|
46
|
+
tools = new Map((await rpc("tools/list", {})).tools.map((t) => [t.name, t]));
|
|
47
|
+
},
|
|
48
|
+
async call(name, args = {}) {
|
|
49
|
+
const t = tools?.get(name);
|
|
50
|
+
if (!t) throw new Error(`Rho has no tool ${name}`);
|
|
51
|
+
if (t.annotations?.readOnlyHint !== true) throw new Error(`${name} is not read-only: Naive never writes to the system of record`);
|
|
52
|
+
const r = await rpc("tools/call", { name, arguments: args });
|
|
53
|
+
if (r.isError) throw new Error(`${name}: ${r.content?.[0]?.text ?? "error"}`);
|
|
54
|
+
return JSON.parse(r.content[0].text);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Every page of a list tool.
|
|
60
|
+
async function all(rho, name, key, args = {}) {
|
|
61
|
+
const out = [];
|
|
62
|
+
let token;
|
|
63
|
+
do {
|
|
64
|
+
const page = await rho.call(name, { ...args, page_size: 100, ...(token ? { page_token: token } : {}) });
|
|
65
|
+
out.push(...(page[key] ?? []));
|
|
66
|
+
token = page.page?.next_page_token;
|
|
67
|
+
} while (token);
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function pull({ url, token }) {
|
|
72
|
+
const rho = client(url, token);
|
|
73
|
+
await rho.init();
|
|
74
|
+
const business = await rho.call("getBusiness").catch(() => null);
|
|
75
|
+
const accounts = await all(rho, "listAccounts", "accounts");
|
|
76
|
+
const transactions = await all(rho, "listTransactions", "transactions", { order: "asc" });
|
|
77
|
+
const last = transactions.map((t) => t.posted_at ?? t.initiated_at).filter(Boolean).sort().at(-1);
|
|
78
|
+
const to = last ?? new Date().toISOString();
|
|
79
|
+
const from = new Date(Date.parse(to) - 90 * 864e5).toISOString();
|
|
80
|
+
const cashflow = await rho.call("getCashflow", { from, to, group: "month" });
|
|
81
|
+
return { source: url === LIVE ? "rho" : "rho-sandbox", business, accounts, transactions, cashflow: { from, to, ...cashflow } };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const CASH = new Set(["checking", "savings"]);
|
|
85
|
+
const usd = (n) => ({ amount: n, currency: "USD" });
|
|
86
|
+
|
|
87
|
+
// The summary is the only money record that is versioned and the only one an agent or a trace sees.
|
|
88
|
+
// Burn comes from Rho's cash flow (business-level in and out, so moves between the business's own
|
|
89
|
+
// accounts don't count), over the 90 days that end at the latest activity, which the summary names.
|
|
90
|
+
export function summarize(data, syncedAt) {
|
|
91
|
+
const cash = data.accounts.filter((a) => CASH.has(a.account_type)).reduce((s, a) => s + (a.balance?.amount ?? 0), 0);
|
|
92
|
+
const buckets = data.cashflow.buckets ?? [];
|
|
93
|
+
const sum = (k) => buckets.reduce((s, b) => s + (b[k]?.amount ?? 0), 0);
|
|
94
|
+
const days = Math.max(1, (Date.parse(data.cashflow.to) - Date.parse(data.cashflow.from)) / 864e5);
|
|
95
|
+
const monthly = (n) => Math.round((n / days) * 30);
|
|
96
|
+
const net = monthly(sum("net_amount"));
|
|
97
|
+
return {
|
|
98
|
+
source: data.source, business: data.business?.name ?? data.business?.legal_name ?? null, synced_at: syncedAt,
|
|
99
|
+
as_of: data.cashflow.to, window: { from: data.cashflow.from, to: data.cashflow.to },
|
|
100
|
+
cash: usd(cash), monthly_inflow: usd(monthly(sum("inflow_amount"))), monthly_outflow: usd(monthly(sum("outflow_amount"))), monthly_net: usd(net),
|
|
101
|
+
runway_months: net < 0 ? Math.round((cash / -net) * 10) / 10 : null,
|
|
102
|
+
accounts: data.accounts.length, transactions: data.transactions.length,
|
|
103
|
+
note: net < 0 ? "Runway is cash over the average monthly net outflow in the window." : "Net cash flow is not negative in the window, so runway is not limited by burn.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const json = (f, v) => { mkdirSync(join(f, ".."), { recursive: true }); writeFileSync(f, `${JSON.stringify(v, null, 2)}\n`); };
|
|
108
|
+
|
|
109
|
+
export function write(venture, data, syncedAt = new Date().toISOString()) {
|
|
110
|
+
const money = join(venture, "money");
|
|
111
|
+
const pointer = (kind, id) => `${data.source}:${kind}:${id}`;
|
|
112
|
+
for (const a of data.accounts)
|
|
113
|
+
json(join(money, `${a.id}.json`), { id: a.id, name: a.account_name, type: a.account_type, last_4: a.account_number_last_4 ?? null, balance: a.balance, source: pointer("account", a.id), synced_at: syncedAt });
|
|
114
|
+
const stream = join(money, "_streams/transactions");
|
|
115
|
+
// The stream's declaration, as schema/stream.schema.json has it. Its events are append-only and stay out of git.
|
|
116
|
+
json(join(stream, "stream.json"), { id: "transactions", sensitive: true,
|
|
117
|
+
summary: `Every transaction ${data.source} posted, one file each, as Rho returned it; Rho is the system of record. The versioned view is money/_summary.json.` });
|
|
118
|
+
let added = 0;
|
|
119
|
+
for (const t of data.transactions) {
|
|
120
|
+
const at = t.posted_at ?? t.initiated_at;
|
|
121
|
+
const f = join(stream, at.slice(0, 4), at.slice(5, 7), `${at.slice(0, 10)}-${t.id}.json`);
|
|
122
|
+
if (existsSync(f)) continue;
|
|
123
|
+
json(f, { id: t.id, at, amount: t.amount, counterparty: t.counterparty_name ?? null, type: t.transaction_type, status: t.status, account: pointer("account", t.account_id), source: pointer("transaction", t.id), fields: t.fields ?? [] });
|
|
124
|
+
added++;
|
|
125
|
+
}
|
|
126
|
+
// Sensitive streams never reach git (stage C of PR #23 makes this a framework rule).
|
|
127
|
+
const ignore = join(money, ".gitignore");
|
|
128
|
+
if (!existsSync(ignore)) writeFileSync(ignore, "_streams/transactions/*/\n");
|
|
129
|
+
const summary = summarize(data, syncedAt);
|
|
130
|
+
json(join(money, "_summary.json"), summary);
|
|
131
|
+
return { added, summary };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const argv = process.argv.slice(2);
|
|
135
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
136
|
+
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
137
|
+
const venture = opt("venture");
|
|
138
|
+
if (argv[0] !== "sync" || !venture) { console.error("usage: node platform/integrations/rho.mjs sync --venture <dir> [--live] [--fixture <file>] [--save-fixture <file>]"); process.exit(2); }
|
|
139
|
+
const live = argv.includes("--live");
|
|
140
|
+
if (live && !process.env.RHO_API_TOKEN) { console.error("RHO_API_TOKEN is not set"); process.exit(3); }
|
|
141
|
+
const data = opt("fixture") ? JSON.parse(readFileSync(opt("fixture"), "utf8"))
|
|
142
|
+
: await pull(live ? { url: LIVE, token: process.env.RHO_API_TOKEN } : { url: SANDBOX, token: "sandbox" });
|
|
143
|
+
if (opt("save-fixture")) json(opt("save-fixture"), data);
|
|
144
|
+
const { added, summary } = write(venture, data);
|
|
145
|
+
console.log(JSON.stringify({ venture, added, summary }, null, 2));
|
|
146
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Read a venture's website (platform, EE-041; EE-062): the home page and the pages it links to, as
|
|
3
|
+
// text, with where each page sits (the home page, one click away in the header or nav, or one click
|
|
4
|
+
// away elsewhere). Firecrawl renders the pages when FIRECRAWL_API_KEY is set; otherwise a plain fetch
|
|
5
|
+
// reads the HTML, which misses sites that render only in the browser. It reads public pages only,
|
|
6
|
+
// stays on the site's own origin, and sends nothing but the requests themselves.
|
|
7
|
+
//
|
|
8
|
+
// node platform/integrations/site.mjs <url> [--pages 10] prints {site, via, pages:[...]}
|
|
9
|
+
|
|
10
|
+
const UA = "Mozilla/5.0 (compatible; NaiveSiteReader/0.1; +https://github.com/tchjhnsn/everything-ecosystem)";
|
|
11
|
+
const FIRECRAWL = "https://api.firecrawl.dev/v1";
|
|
12
|
+
// Pages a marketing site usually has; a link whose path or text names one is read first.
|
|
13
|
+
const LIKELY = /about|team|company|story|mission|pricing|plans|careers|jobs|hiring|join|investor|ir\b|community|contribut|open-source|press|news|media|blog|contact|security|privacy|trust|product|features|how-it-works|customers|case-stud|faq/i;
|
|
14
|
+
|
|
15
|
+
const decode = (s) => s.replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'|'|’/g, "'").replace(/&#(\d+);/g, (_, n) => String.fromCharCode(+n));
|
|
16
|
+
export function text(html) {
|
|
17
|
+
return decode(html.replace(/<(script|style|noscript|svg|template)[\s\S]*?<\/\1>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ")
|
|
18
|
+
.replace(/<\/(p|div|li|h[1-6]|section|article|br|tr|header|footer|nav)>/gi, "\n").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, " "))
|
|
19
|
+
.replace(/[ \t]+/g, " ").replace(/\n\s*\n+/g, "\n").trim();
|
|
20
|
+
}
|
|
21
|
+
export function links(html, base) {
|
|
22
|
+
const out = [], seen = new Set();
|
|
23
|
+
const navHtml = (html.match(/<(nav|header)[\s\S]*?<\/\1>/gi) ?? []).join(" ");
|
|
24
|
+
const navHrefs = new Set([...navHtml.matchAll(/href\s*=\s*["']([^"'#]+)/gi)].map((m) => m[1]));
|
|
25
|
+
for (const m of html.matchAll(/<a\b[^>]*href\s*=\s*["']([^"'#]+)[^"']*["'][^>]*>([\s\S]*?)<\/a>/gi)) {
|
|
26
|
+
let u;
|
|
27
|
+
try { u = new URL(m[1], base); } catch { continue; }
|
|
28
|
+
if (u.origin !== new URL(base).origin || /^\/(log-?in|sign-?in|sign-?up|register|auth|app|dashboard|homepage)\b/i.test(u.pathname) || /\.(png|jpe?g|gif|svg|webp|pdf|zip|mp4|css|js|xml|ico)$/i.test(u.pathname)) continue;
|
|
29
|
+
u.hash = ""; u.search = "";
|
|
30
|
+
const key = u.href.replace(/\/$/, "");
|
|
31
|
+
if (seen.has(key) || key === new URL(base).href.replace(/\/$/, "")) continue;
|
|
32
|
+
seen.add(key);
|
|
33
|
+
out.push({ url: u.href, label: text(m[2]).slice(0, 60), in_nav: navHrefs.has(m[1]) });
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function plain(url) {
|
|
39
|
+
const r = await fetch(url, { headers: { "user-agent": UA, accept: "text/html" }, redirect: "follow", signal: AbortSignal.timeout(15000) });
|
|
40
|
+
if (!r.ok) throw new Error(`${url} answered ${r.status}`);
|
|
41
|
+
const html = await r.text();
|
|
42
|
+
return { url: r.url, html, title: decode((html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1] ?? "").trim()), text: text(html) };
|
|
43
|
+
}
|
|
44
|
+
async function firecrawl(url) {
|
|
45
|
+
const r = await fetch(`${FIRECRAWL}/scrape`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${process.env.FIRECRAWL_API_KEY}` },
|
|
46
|
+
body: JSON.stringify({ url, formats: ["markdown", "html"], onlyMainContent: false }), signal: AbortSignal.timeout(60000) });
|
|
47
|
+
if (!r.ok) throw new Error(`Firecrawl answered ${r.status} for ${url}`);
|
|
48
|
+
const d = (await r.json()).data ?? {};
|
|
49
|
+
return { url: d.metadata?.sourceURL ?? url, html: d.html ?? "", title: d.metadata?.title ?? "", text: d.markdown ?? text(d.html ?? "") };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function read(site, { pages = 10, chars = 6000 } = {}) {
|
|
53
|
+
const url = /^https?:\/\//.test(site) ? site : `https://${site}`;
|
|
54
|
+
const via = process.env.FIRECRAWL_API_KEY ? "firecrawl" : "fetch";
|
|
55
|
+
const get = via === "firecrawl" ? firecrawl : plain;
|
|
56
|
+
const home = await get(url);
|
|
57
|
+
const ls = links(home.html, home.url);
|
|
58
|
+
// Links that name a page marketing sites usually have first (about, careers, pricing...), header and nav links next, then the rest.
|
|
59
|
+
const score = (l) => (LIKELY.test(`${new URL(l.url).pathname} ${l.label}`) ? 2 : 0) + (l.in_nav ? 1 : 0);
|
|
60
|
+
// One page per section first (/customers, not every /customers/<name>), then the rest if room is left.
|
|
61
|
+
const ranked = ls.map((l, i) => [l, i]).sort(([a, i], [b, j]) => score(b) - score(a) || i - j).map(([l]) => l);
|
|
62
|
+
const section = (l) => new URL(l.url).pathname.split("/")[1];
|
|
63
|
+
const firsts = ranked.filter((l, k) => ranked.findIndex((m) => section(m) === section(l)) === k);
|
|
64
|
+
const pick = [...firsts, ...ranked.filter((l) => !firsts.includes(l))].slice(0, pages - 1);
|
|
65
|
+
const rest = await Promise.all(pick.map(async (l) => { try { return { ...(await get(l.url)), link: l }; } catch (e) { return { url: l.url, error: e.message, link: l }; } }));
|
|
66
|
+
const page = (p, depth) => ({ url: p.url, path: new URL(p.url).pathname, title: p.title ?? "", depth, in_nav: depth === 0 ? true : !!p.link?.in_nav, label: p.link?.label ?? "", ...(p.error ? { error: p.error } : { text: p.text.slice(0, chars) }) });
|
|
67
|
+
return { site: new URL(home.url).origin, via, read_at: new Date().toISOString().replace(/\.\d+Z$/, "Z"), links_found: ls.length, links: ls.map((l) => ({ path: new URL(l.url).pathname, label: l.label, in_nav: l.in_nav })), pages: [page(home, 0), ...rest.map((p) => page(p, 1))] };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
71
|
+
const site = process.argv[2];
|
|
72
|
+
if (!site) { console.error("usage: node platform/integrations/site.mjs <url> [--pages 10]"); process.exit(2); }
|
|
73
|
+
const i = process.argv.indexOf("--pages");
|
|
74
|
+
read(site, { pages: i > 0 ? Number(process.argv[i + 1]) : 10 }).then((r) => console.log(JSON.stringify(r)), (e) => { console.error(e.message); process.exit(1); });
|
|
75
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Kits
|
|
2
|
+
|
|
3
|
+
A kit belongs to Mentura. It is an enhanced package: automations, agent workflows, richer skills and connectors, on top of one Naive package.
|
|
4
|
+
|
|
5
|
+
A kit lives at `platform/kits/<name>/kit.json`. It names the one package it extends (`package`) and never replaces that package. The package at `packages/<name>/package.json` works with no kit installed: schemas, templates, deterministic checks and at most one skill, offline, naming no vendor (EE-071).
|
|
6
|
+
|
|
7
|
+
A kit can also extend a framework command (`extends.command`), and bring automations: triggers that name the package they need and run only once it is complete (`schema/kit.schema.json`). A venture turns kits on with `kits` in its `naive.config.json`.
|
|
8
|
+
|
|
9
|
+
| Kit | What it does |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `agent-upgrade/` | Runs `naive upgrade --agent`: an agent does the guides' by-hand steps on a staged copy; the framework's checks and a typed confirmation decide what lands (U-3) |
|
|
12
|
+
| `validation-kickoff/` | Proposes the decision to start validating once the foundation is complete (M-12) |
|
|
13
|
+
|
|
14
|
+
`node platform/kits/test.mjs` tests them offline.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-upgrade",
|
|
3
|
+
"title": "Agent-run upgrade",
|
|
4
|
+
"purpose": "An agent works the upgrade of a venture (U-3, I-29): the framework's migrations run on a copy, the agent does what the version guides leave to a person (prose such as AGENTS.md), and the framework's checks decide. Nothing lands until the checks pass and the founder types the confirmation.",
|
|
5
|
+
"extends": { "command": "upgrade --agent" },
|
|
6
|
+
"entry": "run.mjs",
|
|
7
|
+
"agent": {
|
|
8
|
+
"command": ["claude", "-p", "--output-format", "json", "--permission-mode", "acceptEdits", "--allowedTools", "Read,Edit,Write,Glob,Grep", "--strict-mcp-config"],
|
|
9
|
+
"env": ["MENTURA_AGENT_CMD"]
|
|
10
|
+
},
|
|
11
|
+
"use_model": ["the agent edits the staged copy by the version guides' by-hand steps"],
|
|
12
|
+
"checks": ["schema/upgrades.mjs"],
|
|
13
|
+
"status": "built",
|
|
14
|
+
"note": "The agent sees only the staged copy, never _private/, and its claims are not trusted: every file it changed is found by hash and logged as an agent's write, and the framework's checks run after it. MENTURA_AGENT_CMD replaces the default agent (platform/kits/agent-upgrade/stand-in-agent.mjs is the offline stand-in the tests use)."
|
|
15
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The agent-run upgrade (U-3, I-29), a Mentura kit over the framework's staged upgrade. Next.js's
|
|
3
|
+
// `next upgrade --agent` has a coding agent perform and verify the upgrade; here the work is split the
|
|
4
|
+
// System 1 way. Code runs the migrations on a copy (schema/upgrades.mjs stage). The agent does only what
|
|
5
|
+
// the version guides leave to a person, in the copy. Code then finds every file the agent changed (by
|
|
6
|
+
// hash, not by its word), logs each as an agent's write, and runs the framework's checks. The upgrade
|
|
7
|
+
// stays a draft until the checks pass and the founder types the confirmation:
|
|
8
|
+
//
|
|
9
|
+
// node platform/kits/agent-upgrade/run.mjs <venture dir> [--json] (naive upgrade <dir> --agent)
|
|
10
|
+
// node interface/cli/naive.mjs upgrade <dir> --accept --confirm upgrade
|
|
11
|
+
//
|
|
12
|
+
// The agent is `claude -p` with file tools only, working in the copy, or MENTURA_AGENT_CMD (a command that
|
|
13
|
+
// reads the prompt on stdin, works in its current directory and answers as claude -p --output-format json).
|
|
14
|
+
|
|
15
|
+
import { spawnSync } from "node:child_process";
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { join, resolve } from "node:path";
|
|
18
|
+
import { agentFromEnv, record } from "../../../interface/provenance.mjs";
|
|
19
|
+
import { annotate, check, CONFIRM, snapshot, stage, STAGED } from "../../../schema/upgrades.mjs";
|
|
20
|
+
|
|
21
|
+
const repo = new URL("../../..", import.meta.url).pathname;
|
|
22
|
+
const kit = JSON.parse(readFileSync(new URL("./kit.json", import.meta.url), "utf8"));
|
|
23
|
+
const dirArg = process.argv[2];
|
|
24
|
+
const json = process.argv.includes("--json");
|
|
25
|
+
if (!dirArg || !existsSync(join(dirArg, "venture.json"))) { console.error("usage: node platform/kits/agent-upgrade/run.mjs <venture dir> [--json]"); process.exit(2); }
|
|
26
|
+
const dir = resolve(dirArg);
|
|
27
|
+
|
|
28
|
+
/** Runs the agent in the staged copy and returns its answer (an object), or throws saying why. */
|
|
29
|
+
export function runAgent(prompt, cwd) {
|
|
30
|
+
const cmd = process.env.MENTURA_AGENT_CMD ? process.env.MENTURA_AGENT_CMD.split(" ") : kit.agent.command;
|
|
31
|
+
// A relative program path in MENTURA_AGENT_CMD is relative to where naive was run, not to the copy.
|
|
32
|
+
const args = cmd.slice(1).map((a) => (/\.m?js$/.test(a) && !a.startsWith("/") && existsSync(resolve(a)) ? resolve(a) : a));
|
|
33
|
+
const p = spawnSync(cmd[0], args, { cwd, input: prompt, encoding: "utf8", maxBuffer: 1 << 26, env: { ...process.env, NAIVE_STAGE: cwd, NAIVE_TRIGGERS_ACTIVE: "upgrade" } });
|
|
34
|
+
if (p.error) throw new Error(`could not run the agent (${cmd[0]}): ${p.error.message}`);
|
|
35
|
+
let text = p.stdout;
|
|
36
|
+
try { const r = JSON.parse(p.stdout); if (r && typeof r.result === "string") { if (r.is_error) throw new Error(`the agent stopped: ${r.result}`); text = r.result; } }
|
|
37
|
+
catch (e) { if (e.message.startsWith("the agent stopped")) throw e; }
|
|
38
|
+
if (p.status !== 0) throw new Error(`the agent stopped with status ${p.status}: ${(p.stderr || text).trim().slice(0, 500)}${/login/i.test(p.stderr + text) ? " (run claude, then /login)" : ""}`);
|
|
39
|
+
const m = text.match(/\{[\s\S]*\}/);
|
|
40
|
+
try { return m ? JSON.parse(m[0]) : { summary: text.trim() }; } catch { return { summary: text.trim().slice(0, 2000) }; }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const staged = await stage(dir, { by: kit.name }).catch((e) => { console.error(e.message); process.exit(1); });
|
|
44
|
+
const copy = join(dir, STAGED);
|
|
45
|
+
const guides = staged.guides.map((g) => `--- ${g}\n${readFileSync(join(repo, g), "utf8")}`).join("\n\n");
|
|
46
|
+
const prompt = [
|
|
47
|
+
`You are upgrading the venture in your current directory from Naive ${staged.from} to ${staged.to}. It is a copy: nothing you do reaches the venture until its founder accepts it.`,
|
|
48
|
+
"The framework's migrations have already run. Do only what the guides below leave to a person (their \"By hand\" sections) and what the migrations escalated, by editing files in this directory.",
|
|
49
|
+
"Rules: never open anything under _private/. Do not edit provenance.jsonl, naive.config.json, or anything under .release/ or .naive/. Do not delete files. Keep every JSON record valid against its schema. When something needs the founder's judgment, leave it and say so.",
|
|
50
|
+
`Escalated by the migrations:\n${staged.escalated.length ? staged.escalated.map((e) => `- ${e}`).join("\n") : "- nothing"}`,
|
|
51
|
+
`The guides:\n\n${guides || "(none)"}`,
|
|
52
|
+
'When you are done, answer with JSON only: {"summary": "<one sentence>", "edits": [{"path": "<file>", "why": "<the guide step>"}], "left": [{"what": "<item>", "why": "<why it needs the founder>"}]}',
|
|
53
|
+
].join("\n\n");
|
|
54
|
+
|
|
55
|
+
const before = snapshot(copy);
|
|
56
|
+
let answer;
|
|
57
|
+
try { answer = runAgent(prompt, copy); }
|
|
58
|
+
catch (e) {
|
|
59
|
+
annotate(dir, { agent: { kit: kit.name, error: e.message } });
|
|
60
|
+
const d = check(dir);
|
|
61
|
+
console.error(`${e.message}\nThe migrations' copy is staged without the agent's work (${d.status}). Accept it with --accept --confirm ${CONFIRM}, or --reject.`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
// What the agent changed, by hash. Each change is an agent's write in the copy's provenance.
|
|
65
|
+
const after = snapshot(copy);
|
|
66
|
+
const changed = Object.keys(after).filter((f) => f !== "provenance.jsonl" && after[f] !== before[f]).sort();
|
|
67
|
+
const deleted = Object.keys(before).filter((f) => !(f in after)).sort();
|
|
68
|
+
const who = agentFromEnv("agent", "cli", { id: kit.name, ...(process.env.MENTURA_AGENT_CMD ? {} : { harness: "Claude Code" }) });
|
|
69
|
+
const why = new Map((answer.edits ?? []).map((e) => [e.path, e.why]));
|
|
70
|
+
for (const f of changed) record(copy, { entity: f, activity: "write", agent: who, note: `agent-run upgrade to Naive ${staged.to}: ${why.get(f) ?? "changed by the agent (it did not say why)"}` });
|
|
71
|
+
annotate(dir, { agent: { kit: kit.name, summary: answer.summary ?? null, changed, deleted, reported: [...why.keys()], left: answer.left ?? [] } });
|
|
72
|
+
const d = check(dir);
|
|
73
|
+
|
|
74
|
+
if (json) { console.log(JSON.stringify(d, null, 2)); process.exit(d.status === "ready" ? 0 : 1); }
|
|
75
|
+
console.log(`staged upgrade of ${staged.from} to ${staged.to}, worked by an agent: ${d.status}`);
|
|
76
|
+
if (answer.summary) console.log(`agent ${answer.summary}`);
|
|
77
|
+
for (const f of changed) console.log(`edited ${f}${why.has(f) ? `: ${why.get(f)}` : " (not reported by the agent)"}`);
|
|
78
|
+
for (const f of deleted) console.log(`deleted ${f} (the agent was told not to delete; the founder decides)`);
|
|
79
|
+
for (const l of answer.left ?? []) console.log(`left ${l.what}: ${l.why}`);
|
|
80
|
+
for (const c of d.checks) console.log(`${c.passed ? "pass" : "FAIL"} ${c.check}${c.passed ? "" : `: ${c.output.split("\n").slice(-2).join(" / ")}`}`);
|
|
81
|
+
console.log(d.status === "ready" ? `Nothing has landed. To land it: node interface/cli/naive.mjs upgrade ${dirArg} --accept --confirm ${CONFIRM}` : `Nothing has landed, and it cannot until the checks pass. Fix the copy in ${dirArg}/${STAGED}, then run --status; or --reject.`);
|
|
82
|
+
process.exit(d.status === "ready" ? 0 : 1);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// A stand-in for the upgrade agent, for tests and docs that run offline
|
|
3
|
+
// (MENTURA_AGENT_CMD="node platform/kits/agent-upgrade/stand-in-agent.mjs"). It does the 0.3.0 guide's
|
|
4
|
+
// by-hand step the way an agent would: in every AGENTS.md of the copy it is working in (its current
|
|
5
|
+
// directory), it says package where the text says kit. It answers as claude -p --output-format json does.
|
|
6
|
+
// STAND_IN_BREAK=1 also breaks a record, so tests can show the checks stop a bad upgrade.
|
|
7
|
+
|
|
8
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
let prompt = "";
|
|
12
|
+
for await (const c of process.stdin) prompt += c;
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
const edits = [];
|
|
15
|
+
const walk = (rel) => {
|
|
16
|
+
for (const e of readdirSync(join(cwd, rel), { withFileTypes: true })) {
|
|
17
|
+
const r = rel ? `${rel}/${e.name}` : e.name;
|
|
18
|
+
if (e.isDirectory()) { if (!e.name.startsWith(".") && e.name !== "_private") walk(r); continue; }
|
|
19
|
+
if (e.name !== "AGENTS.md") continue;
|
|
20
|
+
const text = readFileSync(join(cwd, r), "utf8");
|
|
21
|
+
const next = text.replace(/\bkits\b/g, "packages").replace(/\bkit\b/g, "package");
|
|
22
|
+
if (next !== text) { writeFileSync(join(cwd, r), next); edits.push({ path: r, why: "0.3.0 by hand: say package where the text says kit" }); }
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
if (/say package where you wrote kit/.test(prompt)) walk("");
|
|
26
|
+
if (process.env.STAND_IN_BREAK === "1") {
|
|
27
|
+
mkdirSync(join(cwd, "foundation"), { recursive: true });
|
|
28
|
+
writeFileSync(join(cwd, "foundation/icp.json"), JSON.stringify({ broken: true }) + "\n");
|
|
29
|
+
edits.push({ path: "foundation/icp.json", why: "rewrote the ICP" });
|
|
30
|
+
}
|
|
31
|
+
const answer = { summary: edits.length ? `Updated ${edits.length} file(s) by the guides' by-hand steps.` : "Nothing in the guides' by-hand steps applied to this venture.", edits, left: [] };
|
|
32
|
+
console.log(JSON.stringify({ type: "result", is_error: false, result: JSON.stringify(answer) }));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "validation-kickoff",
|
|
3
|
+
"title": "Validation kickoff",
|
|
4
|
+
"purpose": "When the foundation is settled, start the next step: put the decision to begin validating in front of the founder (M-12). An automation over the validation package, so it waits until the foundation package it needs is complete.",
|
|
5
|
+
"extends": { "package": "validation" },
|
|
6
|
+
"automations": [
|
|
7
|
+
{
|
|
8
|
+
"id": "start",
|
|
9
|
+
"on": { "activity": ["write", "accept", "confirm"], "route": "foundation/**", "by": "person" },
|
|
10
|
+
"needs": "foundation",
|
|
11
|
+
"once": true,
|
|
12
|
+
"run": {
|
|
13
|
+
"propose": {
|
|
14
|
+
"id": "start-validation",
|
|
15
|
+
"question": "The foundation is settled. Start validating the solution now: write the solution record and plan the first problem interviews?",
|
|
16
|
+
"options": ["Yes: start validation now", "Not yet: keep working the foundation"],
|
|
17
|
+
"layer": "purpose",
|
|
18
|
+
"boundary": "fail-open",
|
|
19
|
+
"decides": { "record": "validation-solution", "path": "validation/solution.json" },
|
|
20
|
+
"unlocks": [{ "activity": "Draft the solution record and the interview plan", "routes": ["validation/solution"] }]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"note": "Proposes a decision rather than writing records: the founder decides whether validation starts."
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"use_model": [],
|
|
27
|
+
"status": "built",
|
|
28
|
+
"note": "Turn it on with \"kits\": [\"validation-kickoff\"] in the venture's naive.config.json. The framework's trigger runner (schema/triggers.mjs) runs it; this kit holds no code."
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Market research for agents, through Firecrawl (platform, EE-041): an MCP server, stdio, no
|
|
3
|
+
// dependencies. It names a vendor and needs a credential, so it lives on the platform side; the
|
|
4
|
+
// Naive harness hands it to an agent next to Naive's own MCP server. Every result carries its URL,
|
|
5
|
+
// so an agent can cite it as evidence (EE-050) and nothing it reports is unsourced.
|
|
6
|
+
//
|
|
7
|
+
// FIRECRAWL_API_KEY=... node platform/mcp/research.mjs
|
|
8
|
+
//
|
|
9
|
+
// Tools: research_search searches the web (Firecrawl /v1/search); research_read reads one page as
|
|
10
|
+
// Markdown (Firecrawl /v1/scrape), for quoting what a result says.
|
|
11
|
+
|
|
12
|
+
import { createInterface } from "node:readline";
|
|
13
|
+
|
|
14
|
+
const API = "https://api.firecrawl.dev/v1";
|
|
15
|
+
export const TOOLS = [
|
|
16
|
+
{ name: "research_search", description: "Search the web for market research (competitors, existing alternatives, prices, complaints, market size) through Firecrawl. Returns each result's title, URL and snippet. Cite the URL of anything you use as evidence; never state a finding without one.",
|
|
17
|
+
inputSchema: { type: "object", properties: { queries: { type: "array", items: { type: "string" }, maxItems: 5, description: "Up to five search queries" }, results: { type: "integer", minimum: 1, maximum: 10, description: "Results per query (default 5)" } }, required: ["queries"] } },
|
|
18
|
+
{ name: "research_read", description: "Read one web page as Markdown through Firecrawl, to quote what it says. Use it on a URL research_search returned.",
|
|
19
|
+
inputSchema: { type: "object", properties: { url: { type: "string" }, chars: { type: "integer", minimum: 500, maximum: 20000, description: "Characters to return (default 6000)" } }, required: ["url"] } },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const key = () => {
|
|
23
|
+
const k = process.env.FIRECRAWL_API_KEY;
|
|
24
|
+
if (!k) throw new Error("FIRECRAWL_API_KEY is not set: research is unavailable, so say so rather than guessing");
|
|
25
|
+
return k;
|
|
26
|
+
};
|
|
27
|
+
async function post(path, body) {
|
|
28
|
+
const res = await fetch(`${API}${path}`, { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${key()}` }, body: JSON.stringify(body), signal: AbortSignal.timeout(60000) });
|
|
29
|
+
if (!res.ok) throw new Error(`Firecrawl answered ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
30
|
+
return res.json();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function search({ queries, results = 5 }) {
|
|
34
|
+
const pages = await Promise.all(queries.slice(0, 5).map(async (q) => ({ q, r: await post("/search", { query: q, limit: results }) })));
|
|
35
|
+
return pages.flatMap(({ q, r }) => (r.data ?? []).slice(0, results).map((x) => ({ query: q, title: x.title ?? x.metadata?.title ?? null, url: x.url, snippet: x.description ?? x.metadata?.description ?? null })));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function read({ url, chars = 6000 }) {
|
|
39
|
+
const r = await post("/scrape", { url, formats: ["markdown"], onlyMainContent: true });
|
|
40
|
+
return { url, title: r.data?.metadata?.title ?? null, markdown: (r.data?.markdown ?? "").slice(0, chars) };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function handle({ id, method, params = {} }) {
|
|
44
|
+
const reply = (result) => ({ jsonrpc: "2.0", id, result });
|
|
45
|
+
switch (method) {
|
|
46
|
+
case "initialize": return reply({ protocolVersion: "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "research", version: "0.1.0" }, instructions: "Research the market with research_search (and research_read to quote a page) and cite every URL you use." });
|
|
47
|
+
case "notifications/initialized": case "notifications/cancelled": return null;
|
|
48
|
+
case "ping": return reply({});
|
|
49
|
+
case "tools/list": return reply({ tools: TOOLS });
|
|
50
|
+
case "tools/call": {
|
|
51
|
+
const fn = { research_search: search, research_read: read }[params.name];
|
|
52
|
+
if (!fn) return { jsonrpc: "2.0", id, error: { code: -32602, message: `unknown tool ${params.name}` } };
|
|
53
|
+
try { return reply({ content: [{ type: "text", text: JSON.stringify(await fn(params.arguments ?? {}), null, 2) }], isError: false }); }
|
|
54
|
+
catch (e) { return reply({ content: [{ type: "text", text: String(e.message ?? e) }], isError: true }); }
|
|
55
|
+
}
|
|
56
|
+
default: return id === undefined ? null : { jsonrpc: "2.0", id, error: { code: -32601, message: `method not found: ${method}` } };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
61
|
+
const rl = createInterface({ input: process.stdin });
|
|
62
|
+
let chain = Promise.resolve();
|
|
63
|
+
rl.on("line", (line) => { chain = chain.then(async () => { if (!line.trim()) return; const out = await handle(JSON.parse(line)); if (out) process.stdout.write(JSON.stringify(out) + "\n"); }); });
|
|
64
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Webhooks from systems of record (V-9, platform): a CRM, a bank or a spend tool calls Mentura when
|
|
3
|
+
// something it holds changes, the way a CMS calls a Next.js revalidation webhook. The request is checked
|
|
4
|
+
// (an HMAC-SHA256 signature of the raw body with the source's shared secret), and the framework marks
|
|
5
|
+
// every summary that reads the changed items stale (schema/stale.mjs notify). Nothing is fetched and no
|
|
6
|
+
// record is rewritten here: the next write of each summary makes it fresh.
|
|
7
|
+
//
|
|
8
|
+
// node platform/webhooks/server.mjs [--port 4330] [--host 127.0.0.1] [--workspace <dir>]
|
|
9
|
+
//
|
|
10
|
+
// POST /webhooks/<venture id>/<source id>
|
|
11
|
+
// header x-naive-signature: sha256=<hex HMAC-SHA256 of the raw body>
|
|
12
|
+
// body { "paths": ["customers/acme.json"], "version": "<the source's version, optional>", "at": "<when, optional>" }
|
|
13
|
+
// 202 { "venture", "source", "paths", "stale": [the files marked stale] }
|
|
14
|
+
//
|
|
15
|
+
// The secret is NAIVE_WEBHOOK_SECRET_<SOURCE> (the source id upper-cased, hyphens as underscores), else
|
|
16
|
+
// NAIVE_WEBHOOK_SECRET; with neither set every request is refused. A source must be in the venture's
|
|
17
|
+
// knowledge/sources.json. It binds to 127.0.0.1 by default; put it behind the platform's TLS to expose it.
|
|
18
|
+
|
|
19
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
20
|
+
import { createServer } from "node:http";
|
|
21
|
+
import { resolve } from "node:path";
|
|
22
|
+
import { ventureDir } from "../../interface/router.mjs";
|
|
23
|
+
import { notify } from "../../schema/stale.mjs";
|
|
24
|
+
|
|
25
|
+
const MAX = 256 * 1024;
|
|
26
|
+
export const secretFor = (source, env = process.env) => env[`NAIVE_WEBHOOK_SECRET_${source.toUpperCase().replace(/-/g, "_")}`] ?? env.NAIVE_WEBHOOK_SECRET ?? null;
|
|
27
|
+
export const sign = (secret, body) => `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;
|
|
28
|
+
|
|
29
|
+
function verified(secret, body, header) {
|
|
30
|
+
if (!secret || typeof header !== "string") return false;
|
|
31
|
+
const want = Buffer.from(sign(secret, body)), got = Buffer.from(header);
|
|
32
|
+
return want.length === got.length && timingSafeEqual(want, got);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The webhook server over a workspace. env supplies the secrets (default process.env). */
|
|
36
|
+
export function webhooks({ workspace = process.env.MENTURA_WORKSPACE, env = process.env } = {}) {
|
|
37
|
+
return createServer((req, res) => {
|
|
38
|
+
const send = (code, body) => { res.writeHead(code, { "content-type": "application/json" }); res.end(JSON.stringify(body) + "\n"); };
|
|
39
|
+
const m = req.url?.match(/^\/webhooks\/([a-z0-9][a-z0-9-]*)\/([a-z][a-z0-9-]*)\/?$/);
|
|
40
|
+
if (!m) return send(404, { error: "POST /webhooks/<venture id>/<source id>" });
|
|
41
|
+
if (req.method !== "POST") return send(405, { error: "webhooks are POST" });
|
|
42
|
+
const chunks = [];
|
|
43
|
+
let size = 0;
|
|
44
|
+
req.on("data", (c) => { size += c.length; if (size > MAX) { send(413, { error: `a notification is at most ${MAX / 1024} KB` }); req.destroy(); } else chunks.push(c); });
|
|
45
|
+
req.on("end", () => {
|
|
46
|
+
if (size > MAX) return;
|
|
47
|
+
const raw = Buffer.concat(chunks);
|
|
48
|
+
const [, venture, source] = m;
|
|
49
|
+
if (!verified(secretFor(source, env), raw, req.headers["x-naive-signature"])) return send(401, { error: "the signature does not match this source's secret" });
|
|
50
|
+
const dir = ventureDir(venture, workspace);
|
|
51
|
+
if (!dir) return send(404, { error: `no venture ${venture}` });
|
|
52
|
+
let body;
|
|
53
|
+
try { body = JSON.parse(raw.toString("utf8")); } catch { return send(400, { error: "the body is not JSON" }); }
|
|
54
|
+
const paths = [body.paths ?? body.path].flat().filter((p) => typeof p === "string" && p && !p.startsWith("/") && !p.split("/").includes(".."));
|
|
55
|
+
if (!paths.length) return send(400, { error: "name the changed items in paths" });
|
|
56
|
+
try {
|
|
57
|
+
const r = notify(dir, { source, paths, version: typeof body.version === "string" ? body.version : undefined, at: typeof body.at === "string" ? body.at : undefined, via: "webhook" });
|
|
58
|
+
return send(202, { venture, ...r });
|
|
59
|
+
} catch (e) { return send(/no source/.test(e.message) ? 404 : 400, { error: e.message }); }
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
65
|
+
const a = process.argv.slice(2);
|
|
66
|
+
const opt = (n, d) => { const i = a.indexOf(`--${n}`); return i >= 0 ? a[i + 1] : d; };
|
|
67
|
+
const workspace = opt("workspace") ? resolve(opt("workspace")) : process.env.MENTURA_WORKSPACE;
|
|
68
|
+
const s = webhooks({ workspace });
|
|
69
|
+
s.listen(Number(opt("port", 4330)), opt("host", "127.0.0.1"), () => console.log(`webhooks on http://${opt("host", "127.0.0.1")}:${s.address().port}/webhooks/<venture>/<source> for ${workspace ?? "this repository's instance/"}`));
|
|
70
|
+
}
|