naive-framework 0.0.0-stage → 0.1.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.
Files changed (338) hide show
  1. package/AGENTS.md +61 -0
  2. package/LICENSE +21 -0
  3. package/README.md +37 -2
  4. package/docs/decisions/decisions.json +861 -0
  5. package/docs/ontology.json +796 -0
  6. package/docs/ontology.md +163 -0
  7. package/docs/reference/ROUTES.md +125 -0
  8. package/evidence/benchmarks/2026-10-27-golden.jev.after-catalog-fix.json +558 -0
  9. package/evidence/benchmarks/2026-10-27-golden.jev.json +501 -0
  10. package/evidence/benchmarks/2026-10-27-golden.reasoning-answers.json +177 -0
  11. package/evidence/benchmarks/JEV_VS_REASONING_MODEL_2026-10-27.md +68 -0
  12. package/evidence/capabilities/agent-capabilities.json +86 -0
  13. package/evidence/design-documents.registry.json +2896 -0
  14. package/evidence/evals/evals.json +25 -0
  15. package/evidence/evals/harness-evals.json +140 -0
  16. package/evidence/layer.json +51 -0
  17. package/evidence/skills/agent-capabilities/SKILL.md +35 -0
  18. package/evidence/skills/design-coverage/SKILL.md +57 -0
  19. package/evidence/skills/design-coverage/check.mjs +192 -0
  20. package/evidence/skills/design-coverage/scan.mjs +45 -0
  21. package/evidence/skills/fidelity-ladder-audit/SKILL.md +121 -0
  22. package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +180 -0
  23. package/evidence/skills/findability/SKILL.md +32 -0
  24. package/evidence/skills/findability/inventory.rb +41 -0
  25. package/evidence/skills/foreshadowing-audit/SKILL.md +183 -0
  26. package/evidence/skills/metrics-review/SKILL.md +177 -0
  27. package/evidence/tooling/integrations.json +153 -0
  28. package/governance/layer.json +101 -0
  29. package/governance/references/references.json +26 -0
  30. package/governance/skills/board-design/SKILL.md +68 -0
  31. package/governance/skills/brand-pillar/SKILL.md +90 -0
  32. package/governance/skills/business-pillar/SKILL.md +96 -0
  33. package/governance/skills/chief-of-staff/SKILL.md +45 -0
  34. package/governance/skills/cofounder-structure/SKILL.md +64 -0
  35. package/governance/skills/cross-pillar-tradeoff/SKILL.md +168 -0
  36. package/governance/skills/equity-and-share-classes/SKILL.md +68 -0
  37. package/governance/skills/executive-roles/SKILL.md +95 -0
  38. package/governance/skills/exit-planning/SKILL.md +66 -0
  39. package/governance/skills/financing-and-investor-rights/SKILL.md +73 -0
  40. package/governance/skills/office-hours-mode/SKILL.md +113 -0
  41. package/governance/skills/product-pillar/SKILL.md +81 -0
  42. package/governance/skills/publication-policy/SKILL.md +63 -0
  43. package/governance/skills/role-and-candidate-authoring/SKILL.md +198 -0
  44. package/governance/skills/stakeholder-update/SKILL.md +177 -0
  45. package/governance/tooling/integrations.json +37 -0
  46. package/governance/venture-template/AGENT_ROLES_AND_DECISION_RIGHTS.md +58 -0
  47. package/governance/venture-template/COMPANY_VAULT_DEFINITION_2026-10-25.md +204 -0
  48. package/governance/venture-template/README.md +74 -0
  49. package/governance/venture-template/decision-rights.json +186 -0
  50. package/governance/venture-template/index.md +151 -0
  51. package/governance/venture-template/pillars/brand.md +209 -0
  52. package/governance/venture-template/pillars/business.md +255 -0
  53. package/governance/venture-template/pillars/product.md +254 -0
  54. package/governance/venture-template/render-roles.py +11 -0
  55. package/governance/venture-template/roles/README.md +85 -0
  56. package/governance/venture-template/roles/agent-roles.json +202 -0
  57. package/governance/venture-template/roles/ceo.md +163 -0
  58. package/governance/venture-template/roles/chief-of-staff.md +153 -0
  59. package/governance/venture-template/roles/cmo.md +165 -0
  60. package/governance/venture-template/roles/coo.md +189 -0
  61. package/governance/venture-template/roles/cto.md +158 -0
  62. package/governance/venture-template/roles/department-head.md +47 -0
  63. package/governance/venture-template/roles/forward-deployed-engineer.md +51 -0
  64. package/governance/venture-template/roles/founder.md +173 -0
  65. package/governance/venture-template/roles/hr-manager.md +49 -0
  66. package/governance/venture-template/roles/talent-acquisition-manager.md +50 -0
  67. package/interface/api/server.mjs +69 -0
  68. package/interface/cli/find-venture.mjs +15 -0
  69. package/interface/cli/mentura.mjs +271 -0
  70. package/interface/cli/naive.mjs +169 -0
  71. package/interface/demo/board.mjs +581 -0
  72. package/interface/demo/decision-harness.mjs +91 -0
  73. package/interface/demo/live-agent.mjs +47 -0
  74. package/interface/harness/evals.mjs +90 -0
  75. package/interface/harness/examples/pt-no-shows.dump.md +19 -0
  76. package/interface/harness/run.mjs +313 -0
  77. package/interface/harness/site.mjs +59 -0
  78. package/interface/layer.json +31 -0
  79. package/interface/mcp/server.mjs +122 -0
  80. package/interface/operator-console/README.md +58 -0
  81. package/interface/provenance.mjs +42 -0
  82. package/interface/references/references.json +28 -0
  83. package/interface/route-aliases.json +32 -0
  84. package/interface/router.mjs +580 -0
  85. package/interface/skills/install-mentura/SKILL.md +88 -0
  86. package/interface/tooling/integrations.json +112 -0
  87. package/knowledge/connectors/README.md +54 -0
  88. package/knowledge/connectors/catalog.mjs +118 -0
  89. package/knowledge/connectors/contract.mjs +56 -0
  90. package/knowledge/connectors/eval.mjs +176 -0
  91. package/knowledge/connectors/filesystem.mjs +79 -0
  92. package/knowledge/connectors/index.mjs +102 -0
  93. package/knowledge/connectors/resolve.mjs +44 -0
  94. package/knowledge/connectors/suite.mjs +80 -0
  95. package/knowledge/layer.json +42 -0
  96. package/knowledge/skills/find-reference/SKILL.md +38 -0
  97. package/knowledge/skills/industry-awareness/SKILL.md +277 -0
  98. package/knowledge/tooling/integrations.json +127 -0
  99. package/knowledge/tooling/questions.md +46 -0
  100. package/operations/FOUNDER_OPERATING_MODEL_CAMPAIGN_2026-10-14.md +16 -0
  101. package/operations/departments.json +162 -0
  102. package/operations/handoff-v2.md +93 -0
  103. package/operations/layer.json +60 -0
  104. package/operations/marketing/check-public.mjs +68 -0
  105. package/operations/marketing/site/README.md +86 -0
  106. package/operations/marketing/site/matrix.json +731 -0
  107. package/operations/marketing/site/plan.mjs +117 -0
  108. package/operations/marketing/site/presence.mjs +252 -0
  109. package/operations/references/references.json +45 -0
  110. package/operations/skills/recruiting/SKILL.md +32 -0
  111. package/operations/skills/team-design/SKILL.md +36 -0
  112. package/operations/skills/tool-selection/SKILL.md +46 -0
  113. package/operations/templates/recruiting-brief.md +35 -0
  114. package/operations/tooling/integrations.json +158 -0
  115. package/package.json +57 -4
  116. package/packages/catalog.json +272 -0
  117. package/packages/cofounder/package.json +42 -0
  118. package/packages/counsel/package.json +47 -0
  119. package/packages/customers/package.json +21 -0
  120. package/packages/decisions/package.json +17 -0
  121. package/packages/engineering/package.json +41 -0
  122. package/packages/evidence/package.json +17 -0
  123. package/packages/finance/package.json +48 -0
  124. package/packages/foundation/package.json +52 -0
  125. package/packages/investor/package.json +57 -0
  126. package/packages/investors/package.json +21 -0
  127. package/packages/legal/package.json +73 -0
  128. package/packages/marketing/package.json +57 -0
  129. package/packages/money/package.json +21 -0
  130. package/packages/operations/package.json +74 -0
  131. package/packages/partner/package.json +38 -0
  132. package/packages/people/package.json +64 -0
  133. package/packages/product/package.json +49 -0
  134. package/packages/raw/package.json +19 -0
  135. package/packages/relationships/package.json +52 -0
  136. package/packages/validation/package.json +99 -0
  137. package/platform/README.md +45 -0
  138. package/platform/connectors/eval.mjs +154 -0
  139. package/platform/connectors/fake-github.mjs +91 -0
  140. package/platform/connectors/github.mjs +105 -0
  141. package/platform/integrations/jev.mjs +30 -0
  142. package/platform/integrations/langsmith-docket.mjs +53 -0
  143. package/platform/integrations/langsmith-setup.mjs +115 -0
  144. package/platform/integrations/langsmith.mjs +47 -0
  145. package/platform/integrations/rho.mjs +145 -0
  146. package/platform/integrations/site.mjs +75 -0
  147. package/platform/kits/README.md +7 -0
  148. package/platform/mcp/research.mjs +64 -0
  149. package/purpose/checklists/vault-and-tooling-consultancy-setup.md +216 -0
  150. package/purpose/frameworks/business-discovery-framework-amendments-2026-05-20.md +181 -0
  151. package/purpose/frameworks/business-discovery-framework.html +938 -0
  152. package/purpose/frameworks/business-discovery-framework.md +326 -0
  153. package/purpose/frameworks/idea-map/README.md +74 -0
  154. package/purpose/frameworks/idea-map/conventions.json +146 -0
  155. package/purpose/frameworks/idea-map/graph.mjs +182 -0
  156. package/purpose/frameworks/idea-map/ideamap.mjs +179 -0
  157. package/purpose/frameworks/idea-map/route.mjs +140 -0
  158. package/purpose/frameworks/onboarding-handoff-framework.md +269 -0
  159. package/purpose/frameworks/post-discovery-deliverable-playbook.md +274 -0
  160. package/purpose/frameworks/product-anatomy/README.md +63 -0
  161. package/purpose/frameworks/product-anatomy/anatomy.mjs +195 -0
  162. package/purpose/frameworks/product-anatomy/examples/polity.answers.reasoned.json +11 -0
  163. package/purpose/frameworks/product-anatomy/examples/polity.json +89 -0
  164. package/purpose/frameworks/product-anatomy/phases.json +107 -0
  165. package/purpose/frameworks/product-anatomy/questions.json +74 -0
  166. package/purpose/kits/validation/README.md +14 -0
  167. package/purpose/kits/validation/references/references.json +122 -0
  168. package/purpose/kits/validation/skills/problem-validation/SKILL.md +43 -0
  169. package/purpose/kits/validation/skills/validation-score/SKILL.md +35 -0
  170. package/purpose/kits/validation/templates/go-to-market.md +19 -0
  171. package/purpose/kits/validation/templates/operations-readiness.md +20 -0
  172. package/purpose/kits/validation/templates/pricing.md +22 -0
  173. package/purpose/kits/validation/templates/problem-interview.md +36 -0
  174. package/purpose/kits/validation/templates/problem.md +45 -0
  175. package/purpose/kits/validation/templates/product-market-fit.md +23 -0
  176. package/purpose/kits/validation/templates/solution.md +18 -0
  177. package/purpose/kits/validation/templates/team.md +19 -0
  178. package/purpose/kits/validation/weights.json +22 -0
  179. package/purpose/layer.json +53 -0
  180. package/purpose/references/references.json +324 -0
  181. package/purpose/skills/ambition-tier/SKILL.md +205 -0
  182. package/purpose/skills/telos/SKILL.md +52 -0
  183. package/purpose/strategic-foundation.json +133 -0
  184. package/purpose/templates/telos-based-smart.md +39 -0
  185. package/purpose/tooling/integrations.json +48 -0
  186. package/schema/README.md +61 -0
  187. package/schema/VERSION +1 -0
  188. package/schema/account.schema.json +79 -0
  189. package/schema/add-department.mjs +41 -0
  190. package/schema/agent-capability.schema.json +94 -0
  191. package/schema/alignment.schema.json +61 -0
  192. package/schema/assess-problem.mjs +131 -0
  193. package/schema/bdd.mjs +56 -0
  194. package/schema/board.schema.json +101 -0
  195. package/schema/check-boundary.mjs +54 -0
  196. package/schema/check-data-model.mjs +170 -0
  197. package/schema/check-data-packages.mjs +83 -0
  198. package/schema/check-departments.mjs +45 -0
  199. package/schema/check-execution.mjs +85 -0
  200. package/schema/check-governance.mjs +98 -0
  201. package/schema/check-integrations.mjs +33 -0
  202. package/schema/check-nextjs-status.mjs +77 -0
  203. package/schema/check-nouns.mjs +49 -0
  204. package/schema/check-private.mjs +61 -0
  205. package/schema/check-provenance.mjs +55 -0
  206. package/schema/check-publication.mjs +96 -0
  207. package/schema/check-ready.mjs +89 -0
  208. package/schema/check-sources.mjs +66 -0
  209. package/schema/check-team.mjs +94 -0
  210. package/schema/check-telos.mjs +59 -0
  211. package/schema/check-validation.mjs +165 -0
  212. package/schema/check-ventures.mjs +81 -0
  213. package/schema/check-where-used.mjs +58 -0
  214. package/schema/data-master.schema.json +14 -0
  215. package/schema/data-model.json +1258 -0
  216. package/schema/data-model.schema.json +152 -0
  217. package/schema/decision-analytics.mjs +77 -0
  218. package/schema/decision-record.schema.json +290 -0
  219. package/schema/decision-rights.schema.json +34 -0
  220. package/schema/decision.schema.json +30 -0
  221. package/schema/decisions.mjs +321 -0
  222. package/schema/department-catalog.schema.json +55 -0
  223. package/schema/department.schema.json +15 -0
  224. package/schema/departments.mjs +32 -0
  225. package/schema/docket-eval.mjs +89 -0
  226. package/schema/docket.mjs +135 -0
  227. package/schema/eval-suite.schema.json +45 -0
  228. package/schema/evidence.schema.json +110 -0
  229. package/schema/examples/migrate/agent.mjs +12 -0
  230. package/schema/examples/workspace/ventures/studio/accounts/bakery/account.json +18 -0
  231. package/schema/examples/workspace/ventures/studio/evidence/.gitkeep +0 -0
  232. package/schema/examples/workspace/ventures/studio/governance/.gitkeep +0 -0
  233. package/schema/examples/workspace/ventures/studio/knowledge/.gitkeep +0 -0
  234. package/schema/examples/workspace/ventures/studio/operations/.gitkeep +0 -0
  235. package/schema/examples/workspace/ventures/studio/print/knowledge/.gitkeep +0 -0
  236. package/schema/examples/workspace/ventures/studio/print/purpose/.gitkeep +0 -0
  237. package/schema/examples/workspace/ventures/studio/print/venture.json +37 -0
  238. package/schema/examples/workspace/ventures/studio/products/brand-care.json +19 -0
  239. package/schema/examples/workspace/ventures/studio/products/brand-kit.json +23 -0
  240. package/schema/examples/workspace/ventures/studio/provenance.jsonl +3 -0
  241. package/schema/examples/workspace/ventures/studio/purpose/.gitkeep +0 -0
  242. package/schema/examples/workspace/ventures/studio/venture.json +43 -0
  243. package/schema/examples/workspace/ventures/tandem/accounts/.gitkeep +0 -0
  244. package/schema/examples/workspace/ventures/tandem/evidence/README.md +3 -0
  245. package/schema/examples/workspace/ventures/tandem/governance/README.md +3 -0
  246. package/schema/examples/workspace/ventures/tandem/governance/agreements/dee-fast.md +3 -0
  247. package/schema/examples/workspace/ventures/tandem/governance/board.json +51 -0
  248. package/schema/examples/workspace/ventures/tandem/governance/departments/finance/charter.md +5 -0
  249. package/schema/examples/workspace/ventures/tandem/governance/departments/finance/department.json +5 -0
  250. package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/charter.md +5 -0
  251. package/schema/examples/workspace/ventures/tandem/governance/departments/primary/marketing-and-sales/department.json +5 -0
  252. package/schema/examples/workspace/ventures/tandem/governance/exit.json +33 -0
  253. package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.json +24 -0
  254. package/schema/examples/workspace/ventures/tandem/governance/founder-agreement.md +3 -0
  255. package/schema/examples/workspace/ventures/tandem/governance/ownership.json +131 -0
  256. package/schema/examples/workspace/ventures/tandem/governance/publication-policy.json +171 -0
  257. package/schema/examples/workspace/ventures/tandem/knowledge/README.md +3 -0
  258. package/schema/examples/workspace/ventures/tandem/knowledge/research/friends-feedback-2026-08.md +3 -0
  259. package/schema/examples/workspace/ventures/tandem/knowledge/research/pilot-2026-10.md +3 -0
  260. package/schema/examples/workspace/ventures/tandem/knowledge/research/problem-interviews-2026-09.md +20 -0
  261. package/schema/examples/workspace/ventures/tandem/knowledge/sources.json +8 -0
  262. package/schema/examples/workspace/ventures/tandem/knowledge/where-used.json +13 -0
  263. package/schema/examples/workspace/ventures/tandem/operations/README.md +3 -0
  264. package/schema/examples/workspace/ventures/tandem/operations/departments/finance/README.md +5 -0
  265. package/schema/examples/workspace/ventures/tandem/operations/departments/finance/department.json +5 -0
  266. package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/README.md +5 -0
  267. package/schema/examples/workspace/ventures/tandem/operations/departments/primary/marketing-and-sales/department.json +5 -0
  268. package/schema/examples/workspace/ventures/tandem/pillars/brand.md +5 -0
  269. package/schema/examples/workspace/ventures/tandem/pillars/business.md +5 -0
  270. package/schema/examples/workspace/ventures/tandem/pillars/product.md +5 -0
  271. package/schema/examples/workspace/ventures/tandem/products/.gitkeep +0 -0
  272. package/schema/examples/workspace/ventures/tandem/products/shop-app.json +22 -0
  273. package/schema/examples/workspace/ventures/tandem/profile.md +5 -0
  274. package/schema/examples/workspace/ventures/tandem/provenance.jsonl +13 -0
  275. package/schema/examples/workspace/ventures/tandem/purpose/README.md +3 -0
  276. package/schema/examples/workspace/ventures/tandem/purpose/telos.json +78 -0
  277. package/schema/examples/workspace/ventures/tandem/purpose/validation/go-to-market.json +35 -0
  278. package/schema/examples/workspace/ventures/tandem/purpose/validation/pricing.json +37 -0
  279. package/schema/examples/workspace/ventures/tandem/purpose/validation/problem.json +74 -0
  280. package/schema/examples/workspace/ventures/tandem/purpose/validation/product-market-fit.json +66 -0
  281. package/schema/examples/workspace/ventures/tandem/purpose/validation/solution.json +33 -0
  282. package/schema/examples/workspace/ventures/tandem/purpose/validation/team.json +52 -0
  283. package/schema/examples/workspace/ventures/tandem/venture.json +79 -0
  284. package/schema/examples/workspace/ventures/woodworking/knowledge/.gitkeep +0 -0
  285. package/schema/examples/workspace/ventures/woodworking/venture.json +21 -0
  286. package/schema/examples/workspace/workspace.json +8 -0
  287. package/schema/exit.schema.json +74 -0
  288. package/schema/foundation.mjs +118 -0
  289. package/schema/founder-agreement.schema.json +83 -0
  290. package/schema/gate.mjs +39 -0
  291. package/schema/handoff.schema.json +239 -0
  292. package/schema/icp.schema.json +136 -0
  293. package/schema/idea-graph.schema.json +61 -0
  294. package/schema/init-venture.mjs +98 -0
  295. package/schema/integration.schema.json +143 -0
  296. package/schema/knowledge-sources.schema.json +130 -0
  297. package/schema/layer.json +46 -0
  298. package/schema/layer.schema.json +119 -0
  299. package/schema/load-packages.mjs +26 -0
  300. package/schema/marketing-sitemap.schema.json +232 -0
  301. package/schema/migrate-venture.mjs +192 -0
  302. package/schema/need-now.json +601 -0
  303. package/schema/need-now.mjs +107 -0
  304. package/schema/nextjs-status.schema.json +29 -0
  305. package/schema/ownership.schema.json +282 -0
  306. package/schema/packages.json +4 -0
  307. package/schema/packages.mjs +247 -0
  308. package/schema/payouts.mjs +91 -0
  309. package/schema/port-venture.mjs +161 -0
  310. package/schema/product.schema.json +223 -0
  311. package/schema/provenance-entry.schema.json +154 -0
  312. package/schema/publication-policy.schema.json +293 -0
  313. package/schema/raw-source.schema.json +15 -0
  314. package/schema/reference.schema.json +115 -0
  315. package/schema/route-aliases.schema.json +25 -0
  316. package/schema/run-handoff.mjs +51 -0
  317. package/schema/scaffold/defaults.json +54 -0
  318. package/schema/site-presence.schema.json +337 -0
  319. package/schema/strategic-foundation.schema.json +74 -0
  320. package/schema/stream.schema.json +14 -0
  321. package/schema/team-plan.schema.json +157 -0
  322. package/schema/telos.schema.json +197 -0
  323. package/schema/time-to-ready.mjs +93 -0
  324. package/schema/upgrade-venture.mjs +129 -0
  325. package/schema/use-model.mjs +10 -0
  326. package/schema/validate.py +49 -0
  327. package/schema/validation-evidence.schema.json +123 -0
  328. package/schema/validation-go-to-market.schema.json +110 -0
  329. package/schema/validation-operations-readiness.schema.json +91 -0
  330. package/schema/validation-pricing.schema.json +114 -0
  331. package/schema/validation-problem.schema.json +118 -0
  332. package/schema/validation-product-market-fit.schema.json +135 -0
  333. package/schema/validation-solution.schema.json +79 -0
  334. package/schema/validation-team.schema.json +116 -0
  335. package/schema/venture.schema.json +407 -0
  336. package/schema/vocab/business-models.json +298 -0
  337. package/schema/where-used.schema.json +56 -0
  338. package/schema/workspace.schema.json +38 -0
@@ -0,0 +1,50 @@
1
+ ---
2
+ type: venture-template-role
3
+ ai-model: claude-opus-5
4
+ role: talent-acquisition-manager
5
+ primary-pillar: business
6
+ secondary-pillars: [product, brand]
7
+ source: hybrid
8
+ created: 2026-08-10
9
+ last-reviewed: 2026-08-10
10
+ authorship: hybrid
11
+ ip-sensitivity: normal
12
+ scope: global
13
+ scope-entity: "[[everything-ecosystem]]"
14
+ tags: [venture-template, role, talent-acquisition, recruiting, business-pillar, stub]
15
+ canonical-for: "talent-acquisition-manager-role-reference"
16
+ related:
17
+ - "[[governance/venture-template/roles/README]]"
18
+ - "[[position-schema]]"
19
+ - "[[role-and-candidate-authoring]]"
20
+ - "[[hr-manager]]"
21
+ status: STUB
22
+ ---
23
+
24
+ # Talent Acquisition Manager
25
+
26
+ > **Purpose:** Owns the recruiting pipeline — role intake, sourcing, screening coordination, offer process — for seats the company has decided to fill with humans.
27
+
28
+ ## Primary pillar
29
+
30
+ - **Business** (people / growth). Partners with hiring managers in Product and Brand.
31
+
32
+ ## Typical responsibilities
33
+
34
+ - Translate position instances + [[role-and-candidate-authoring]] artifacts into search plans
35
+ - Pipeline health; candidate experience; interview loop logistics
36
+ - Close coordination with HR Manager on offers / onboarding handoff
37
+ - Does **not** invent headcount — consumes approved vacant positions with human staffing posture
38
+
39
+ ## Staffing posture (default for instances)
40
+
41
+ - `human-required` for the TA manager seat once volume justifies a specialist
42
+ - Pipeline **triage / scheduling / draft outreach** may be `agent-capable` as *duties* under a hybrid seat or supporting agent roles — not a substitute for the TA seat’s judgment on fit
43
+
44
+ ## Position instances
45
+
46
+ Per [[position-schema]]. Link assignments to open reqs / position vacancies.
47
+
48
+ ## Status
49
+
50
+ Stub 2026-08-10.
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ // The HTTP API over the router (EE-030). Local by default: it binds to 127.0.0.1 and has no
3
+ // authentication, so it is for one builder's machine, not the internet. Every caller is treated as
4
+ // an agent, so gated records are written as drafts for a person to accept.
5
+ //
6
+ // node interface/api/server.mjs [--port 4330]
7
+ //
8
+ // GET /routes, /groups the route table; route groups by phase
9
+ // GET /ventures ventures in the workspace
10
+ // GET /templates/<route> a record's template
11
+ // GET /ventures/<venture> the venture overview (system/overview)
12
+ // GET /ventures/<venture>/<route> read a record (a collection item: <route>/<id>)
13
+ // GET /ventures/<venture>/<route>/view the route as a Markdown page
14
+ // GET /ventures/<venture>/<route>/layout the layout an agent gets before acting
15
+ // PUT /ventures/<venture>/<route> write a record (JSON body); provenance from the
16
+ // headers x-mentura-derived-from (comma-separated), x-mentura-decision, x-mentura-model;
17
+ // the role acting from x-mentura-actor (checked against the team plan's routes)
18
+ // POST /ventures/<venture>/<route>/check run its checks
19
+ // POST /ventures/<venture>/<route>/run run a command route (optional JSON body {value})
20
+ //
21
+ // <venture> is an id or, for a nested venture, its folder path (studio/print).
22
+
23
+ import { createServer } from "node:http";
24
+ import { call, groups, resolve, routes, ventureDir, ventures } from "../router.mjs";
25
+
26
+ const status = (r) => r.ok ? 200 : /^no (route|venture)/.test(r.error ?? "") ? 404 : /has no action/.test(r.error ?? "") ? 405 : / may not /.test(r.error ?? "") ? 403 : 422;
27
+ const VERBS = { check: "POST", run: "POST", view: "GET", layout: "GET" };
28
+
29
+ export async function route(method, url, body, headers = {}) {
30
+ const path = decodeURIComponent(new URL(url, "http://x").pathname).replace(/\/+$/, "");
31
+ if (method === "GET" && path === "/routes") return [200, routes()];
32
+ if (method === "GET" && path === "/ventures") return [200, ventures().map(({ id, name, phase }) => ({ id, name, phase }))];
33
+ let m = path.match(/^\/templates\/(.+)$/);
34
+ if (method === "GET" && m) { const r = await call({ route: m[1], action: "template" }); return [status(r), r]; }
35
+ if (method === "GET" && path === "/groups") return [200, groups()];
36
+ m = path.match(/^\/ventures\/(.+)$/);
37
+ if (!m) return [404, { ok: false, error: `no endpoint ${method} ${path}` }];
38
+ // [...venture]: the longest leading run of segments that names a venture; the rest is the route.
39
+ const parts = m[1].split("/");
40
+ let venture, rest;
41
+ for (let i = parts.length; i >= 1; i--) if (ventureDir(parts.slice(0, i).join("/"))) { venture = parts.slice(0, i).join("/"); rest = parts.slice(i); break; }
42
+ if (!venture) return [404, { ok: false, error: `no venture in ${path}` }];
43
+ if (!rest.length) { if (method !== "GET") return [405, { ok: false, error: `${method} is not allowed on ${path}` }]; const r = await call({ venture, route: "system/overview", action: "run", via: "api" }); return [status(r), r]; }
44
+ let verb = VERBS[rest.at(-1)] && resolve(rest.slice(0, -1).join("/")).r ? rest.pop() : null;
45
+ const rt = rest.join("/");
46
+ const action = verb ?? (method === "GET" ? "read" : method === "PUT" ? "write" : null);
47
+ if (!action || (verb && VERBS[verb] !== method)) return [405, { ok: false, error: `${method} is not allowed on ${path}` }];
48
+ const h = (k) => headers[k] || undefined;
49
+ const r = await call({ venture, route: rt, action, input: body, actor: "agent", actor_id: h("x-mentura-actor"), via: "api",
50
+ provenance: { derived_from: h("x-mentura-derived-from")?.split(",").map((x) => x.trim()).filter(Boolean), decision: h("x-mentura-decision"),
51
+ agent: Object.fromEntries(Object.entries({ model: h("x-mentura-model"), harness: h("user-agent") }).filter(([, v]) => v)) } });
52
+ return [status(r), r];
53
+ }
54
+
55
+ if (import.meta.url === `file://${process.argv[1]}`) {
56
+ const i = process.argv.indexOf("--port");
57
+ const port = i >= 0 ? Number(process.argv[i + 1]) : 4330;
58
+ createServer((req, res) => {
59
+ let raw = "";
60
+ req.on("data", (c) => { raw += c; if (raw.length > 1e6) req.destroy(); });
61
+ req.on("end", async () => {
62
+ let body;
63
+ try { body = raw ? JSON.parse(raw) : undefined; } catch { res.writeHead(400, { "content-type": "application/json" }); res.end(JSON.stringify({ ok: false, error: "body is not JSON" })); return; }
64
+ const [code, out] = await route(req.method, req.url, body, req.headers);
65
+ res.writeHead(code, { "content-type": "application/json" });
66
+ res.end(JSON.stringify(out, null, 2));
67
+ });
68
+ }).listen(port, "127.0.0.1", () => console.log(`mentura API on http://127.0.0.1:${port}`));
69
+ }
@@ -0,0 +1,15 @@
1
+ // The venture a command runs on when none is named: the nearest venture.json in the current
2
+ // directory or its parents, the way next finds the project it runs in (I-2).
3
+
4
+ import { existsSync } from "node:fs";
5
+ import { basename, dirname, join, resolve } from "node:path";
6
+
7
+ export function findVenture(from = process.cwd()) {
8
+ for (let d = resolve(from); ; d = dirname(d)) {
9
+ if (existsSync(join(d, "venture.json"))) return d;
10
+ if (dirname(d) === d) return null;
11
+ }
12
+ }
13
+
14
+ /** The router's view of a venture directory: its workspace is the folder that holds it. */
15
+ export const asRouted = (dir) => ({ workspace: dirname(dir), venture: basename(dir) });
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env node
2
+ // The command line over the router (EE-030). Same routes and gates as the MCP server and the API.
3
+ //
4
+ // mentura routes [--group <phase>] [--json] the route table, or one route group
5
+ // mentura groups [--json] route groups: the routes each phase needs
6
+ // mentura ventures [--json] ventures in the workspace
7
+ // mentura init <dir> --id <id> [--phase <phase>] [--layers purpose,governance]
8
+ // start a venture; --layers turns on only those
9
+ // mentura add <function> --venture <id> [--group]
10
+ // import a function (department is the alias for one release): list it and create its folders
11
+ // mentura upgrade --venture <id> alias of naive upgrade: move the venture onto the package folder layout (EE-070, EE-076)
12
+ // mentura migrate --from <project> --venture <id|dir> [--apply]
13
+ // alias of naive migrate: an agent rewrites a project as Naive records (EE-076)
14
+ // mentura typegen alias of naive typegen (EE-073)
15
+ // mentura accept <route> --venture <id> [--confirm <route>]
16
+ // a person accepts an agent's draft of a gated record;
17
+ // one holding a foundation statement asks you to type the route
18
+ // mentura decide <decision> --venture <id> --by <name> [--answer <text>] [--confirm <decision>]
19
+ // a person makes a decision: approves its wording or gives their own (EE-058)
20
+ // mentura propose --venture <id> --input <file.json> [--as <role>]
21
+ // an agent proposes a decision; it stays open until a person decides
22
+ // mentura understand --venture <id> [--confirm ideas] [--note <corrections made>]
23
+ // a person confirms Mentura read their ideas right (the map
24
+ // and graph, after any corrections), before routing (EE-069)
25
+ // mentura confirm <statement> --venture <id> [--confirm <statement>]
26
+ // a person confirms one foundation statement as theirs (EE-051)
27
+ // mentura <route> <action> --venture <id|path> [--input <file>|-] [--as person] [--value <n>] [--json]
28
+ // actions: read, write, check, template, layout, view (a page), run (commands)
29
+ // a collection item is <route>/<id> (purpose/product/kettle); a nested venture is its
30
+ // folder path (--venture studio/print); system/overview view shows the venture at a glance
31
+ // [--derived-from a,b] [--decision <id>] [--note <text>] provenance for a write (EE-031)
32
+ // knowledge/catalog run builds the catalog over the venture's sources; knowledge/search run
33
+ // --value <query> finds items in any of them (EE-037)
34
+ // purpose/validation/assess run lists what is missing and asks to fill it in or run as is;
35
+ // --as-is runs it anyway (EE-050)
36
+ // mentura baseline --venture <id> adopt existing records into the provenance log
37
+ //
38
+ // The workspace is MENTURA_WORKSPACE, else --workspace <dir>, else this repository's instance/.
39
+ // Writes default to an agent actor; --as person is for a person at the keyboard, and a gated
40
+ // record written by an agent lands in <venture>/_drafts/ for a person to accept.
41
+
42
+ import { existsSync, readFileSync, rmSync } from "node:fs";
43
+ import { join, relative, resolve } from "node:path";
44
+ import { spawnSync } from "node:child_process";
45
+ import { call, draftOf, files, glob, groups, repo, routes, routesMd, ventureDir, ventures } from "../router.mjs";
46
+ import { agentFromEnv, entries, record } from "../provenance.mjs";
47
+ import { FRAME, filesOf } from "../../schema/foundation.mjs";
48
+ import { approve, decisions, propose } from "../../schema/decisions.mjs";
49
+ import { asRouted, findVenture } from "./find-venture.mjs";
50
+
51
+ // The danger zone (EE-051): a foundation statement is accepted or confirmed only when the person types
52
+ // its name, at the prompt or with --confirm <name>.
53
+ async function typed(name, what, why = "This is a foundation statement: changing it changes the venture.") {
54
+ if (opt("confirm") !== undefined) return opt("confirm") === name;
55
+ console.log(`${what}\n${why} Type ${name} to confirm.`);
56
+ if (!process.stdin.isTTY) { console.log(`(not at a terminal: pass --confirm ${name})`); return false; }
57
+ const { createInterface } = await import("node:readline/promises");
58
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
59
+ const a = (await rl.question("> ")).trim();
60
+ rl.close();
61
+ return a === name;
62
+ }
63
+ const foundationOf = (recordType) => FRAME.questions.flatMap((q) => q.statements).filter((s) => s.record === recordType);
64
+
65
+ const argv = process.argv.slice(2);
66
+ // Inside a venture, --venture and the workspace default to it (I-2): `cd my-venture && mentura system/overview view`.
67
+ const here = !argv.includes("--venture") && !argv.includes("--workspace") ? findVenture() : null;
68
+ if (here) { argv.push("--venture", asRouted(here).venture, "--workspace", asRouted(here).workspace); process.env.MENTURA_WORKSPACE = asRouted(here).workspace; }
69
+ const opt = (name) => { const i = argv.indexOf(`--${name}`); return i >= 0 ? argv[i + 1] : undefined; };
70
+ const flag = (name) => argv.includes(`--${name}`);
71
+ const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json"].includes(argv[i - 1].slice(2))));
72
+ const workspace = opt("workspace") ? resolve(opt("workspace")) : undefined;
73
+ const out = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
74
+
75
+ function usage(code = 2) {
76
+ console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// ")).map((l) => l.slice(5)).join("\n"));
77
+ process.exit(code);
78
+ }
79
+
80
+ const [cmd, action] = positional;
81
+ if (!cmd || cmd === "help" || flag("help")) usage(cmd ? 0 : 2);
82
+
83
+ if (cmd === "routes") {
84
+ const g = opt("group")?.replace(/^\(|\)$/g, "");
85
+ const rs = g ? routes().filter((r) => r.groups.includes(`(${g})`)) : routes();
86
+ out(flag("json") || g ? (flag("json") ? rs : rs.map((r) => `${r.route.padEnd(44)} ${r.actions.join(", ")}`).join("\n")) : routesMd());
87
+ process.exit(0);
88
+ }
89
+
90
+ if (cmd === "groups") { out(flag("json") ? groups() : groups().map((g) => `${g.group.padEnd(14)} ${g.routes.length} routes ${g.question}`).join("\n")); process.exit(0); }
91
+
92
+ if (cmd === "ventures") {
93
+ const vs = ventures(workspace);
94
+ out(flag("json") ? vs : vs.map((v) => `${v.id.padEnd(24)} ${String(v.phase).padEnd(12)} ${v.path}`).join("\n") || "no ventures");
95
+ process.exit(0);
96
+ }
97
+
98
+ if (cmd === "add") {
99
+ // Imports one function into a venture (EE-047, EE-048, EE-071). department is the alias for function for one release.
100
+ const v = opt("venture");
101
+ if (!action || !v) usage();
102
+ const p = spawnSync("node", [join(repo, "schema/add-department.mjs"), ventureDir(v, workspace), action, ...(flag("group") ? ["--group"] : [])], { stdio: "inherit" });
103
+ process.exit(p.status ?? 1);
104
+ }
105
+
106
+ if (cmd === "typegen") {
107
+ const p = spawnSync("node", [join(repo, "interface/cli/naive.mjs"), "typegen"], { stdio: "inherit" });
108
+ process.exit(p.status ?? 1);
109
+ }
110
+
111
+ if (cmd === "migrate" && (opt("from") || flag("apply"))) {
112
+ // migrate means rewriting an outside project into a new venture (EE-076): the same as naive migrate.
113
+ const v = opt("venture");
114
+ const d = v && (existsSync(join(resolve(v), "venture.json")) ? resolve(v) : ventureDir(v, workspace));
115
+ if (!d) usage();
116
+ const p = spawnSync("node", [join(repo, "schema/migrate-venture.mjs"), "--venture", d, ...(opt("from") ? ["--from", opt("from")] : ["--apply"])], { stdio: "inherit" });
117
+ process.exit(p.status ?? 1);
118
+ }
119
+
120
+ if (cmd === "upgrade" || cmd === "migrate") {
121
+ // migrate without --from is the old name of upgrade: for one release it still runs upgrade, with a notice.
122
+ if (cmd === "migrate") console.error("mentura migrate --venture <id> is now naive upgrade (EE-076); migrate --from <project> rewrites an outside project into Naive");
123
+ const v = opt("venture");
124
+ const d = v && ventureDir(v, workspace);
125
+ if (!d) usage();
126
+ const p = spawnSync("node", [join(repo, "schema/upgrade-venture.mjs"), d], { stdio: "inherit" });
127
+ process.exit(p.status ?? 1);
128
+ }
129
+
130
+ if (cmd === "init") {
131
+ // The venture scaffold lives in schema/init-venture.mjs; this passes the options through.
132
+ const id = opt("id");
133
+ if (!action || !id) usage();
134
+ const pass = ["phase", "parent", "legal-form", "layers"].flatMap((k) => opt(k) !== undefined ? [`--${k}`, opt(k)] : []);
135
+ const p = spawnSync("node", [join(repo, "schema/init-venture.mjs"), resolve(action), id, ...pass], { stdio: "inherit" });
136
+ process.exit(p.status ?? 1);
137
+ }
138
+
139
+ if (cmd === "accept") {
140
+ // The person at the keyboard accepts a draft: it is checked again and moved into place. The CLI
141
+ // cannot tell a person from an agent running it, so the repository's review (a pull request a
142
+ // person merges) remains the gate that holds; this makes the person's step explicit.
143
+ const venture = opt("venture"), rt = action;
144
+ if (!venture || !rt) usage();
145
+ const d = draftOf(rt, venture, workspace);
146
+ // Any held draft can be accepted: one gated by its decision class, held by an open decision, or held by
147
+ // the venture's naive.config.json gate.
148
+ if (!d) { console.error(`${rt} is not a record route, or no venture ${venture}`); process.exit(1); }
149
+ const draft = join(d.dir, d.draft);
150
+ if (!existsSync(draft)) { console.error(`no draft of ${d.route} in ${venture}`); process.exit(1); }
151
+ const recType = routes().find((x) => x.route === d.route || x.route === d.route.replace(/[^/]+$/, "[id]"))?.record;
152
+ const touched = foundationOf(recType);
153
+ if (touched.length) {
154
+ const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
155
+ const now = existsSync(join(d.dir, d.file)) ? JSON.parse(readFileSync(join(d.dir, d.file), "utf8")) : {};
156
+ const next = JSON.parse(readFileSync(draft, "utf8"));
157
+ const diff = touched.map((s) => ` ${s.id}:\n now: ${JSON.stringify(get(now, s.attribute) ?? null)}\n draft: ${JSON.stringify(get(next, s.attribute) ?? null)}`).join("\n");
158
+ const names = new Set([d.route, rt]);
159
+ const confirmed = opt("confirm") !== undefined ? names.has(opt("confirm")) : await typed(d.route, `Accepting ${d.route} changes these foundation statements:\n${diff}`);
160
+ if (!confirmed) { console.error("not accepted: the confirmation did not match"); process.exit(1); }
161
+ }
162
+ const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(draft, "utf8")), actor: "person", workspace, via: "cli",
163
+ provenance: { activity: "accept", derived_from: [d.draft], note: opt("note") } });
164
+ if (!w.ok) { console.error(w.error); process.exit(1); }
165
+ rmSync(draft);
166
+ out(`accepted: ${w.result.written}`);
167
+ process.exit(0);
168
+ }
169
+
170
+ if (cmd === "decide") {
171
+ // A person makes a decision (EE-058): approves the drafted wording, or gives their own with
172
+ // --answer. Typing the decision's id confirms it. Provenance records who, when and in which phase.
173
+ const venture = opt("venture"), id = action;
174
+ const dir = venture && ventureDir(venture, workspace);
175
+ if (!dir || !id) usage();
176
+ const d = decisions(dir).decisions.find((x) => x.id === id);
177
+ if (!d || d.source !== "file") { console.error(`no decision record ${id} in ${venture}`); process.exit(1); }
178
+ if (!(await typed(id, `Deciding ${id}: ${d.question}\n ${opt("answer") ? "your answer" : "drafted wording"}: ${opt("answer") ?? d.answer ?? "(none)"}`, "This is a decision: it unlocks what waits on it."))) { console.error("not decided: the confirmation did not match"); process.exit(1); }
179
+ const r = approve(dir, id, { by: opt("by") ?? process.env.MENTURA_PERSON, answer: opt("answer"), note: opt("note") });
180
+ if (!r.ok) { console.error(r.error); process.exit(1); }
181
+ out(r.result);
182
+ process.exit(0);
183
+ }
184
+
185
+ if (cmd === "propose") {
186
+ // An agent proposes a decision from a JSON file; it stays open until a person decides it.
187
+ const venture = opt("venture"), dir = venture && ventureDir(venture, workspace);
188
+ if (!dir || !opt("input")) usage();
189
+ const r = propose(dir, JSON.parse(readFileSync(resolve(opt("input")), "utf8")), { agentId: opt("as") ?? process.env.MENTURA_ACTOR_ID, via: "cli" });
190
+ if (!r.ok) { console.error(r.error); process.exit(1); }
191
+ out(r.result);
192
+ process.exit(0);
193
+ }
194
+
195
+ if (cmd === "understand") {
196
+ // The Understand gate (EE-069): the founder has looked over the map and the graph, corrected what
197
+ // Mentura got wrong, and confirms the understanding before the ideas are routed.
198
+ const venture = opt("venture"), dir = venture && ventureDir(venture, workspace);
199
+ if (!dir) usage();
200
+ const hits = ["purpose/ideas/map.mdx", "purpose/ideas/graph.json"].filter((f) => existsSync(join(dir, f)));
201
+ if (!hits.length) { console.error(`${venture} has no idea map yet: map the idea dump first (purpose/ideas)`); process.exit(1); }
202
+ if (!(await typed("ideas", `Confirming Mentura's understanding of your ideas: ${hits.join(", ")}`, "Your ideas are routed to their records from this understanding."))) { console.error("not confirmed: the confirmation did not match"); process.exit(1); }
203
+ for (const f of hits) record(dir, { entity: f, route: f.endsWith(".mdx") ? "purpose/ideas" : "purpose/ideas/graph", activity: "confirm", statement: "ideas", agent: agentFromEnv("person", "cli"), note: opt("note") });
204
+ out("confirmed: ideas");
205
+ process.exit(0);
206
+ }
207
+
208
+ if (cmd === "confirm") {
209
+ // A person confirms a foundation statement an agent wrote as their own; the content is unchanged.
210
+ const venture = opt("venture"), id = action;
211
+ const dir = venture && ventureDir(venture, workspace);
212
+ if (!dir || !id) usage();
213
+ const s = FRAME.questions.flatMap((q) => q.statements).find((x) => x.id === id);
214
+ if (!s?.record) { console.error(`${id} is not a foundation statement with a record (see mentura purpose/foundation run)`); process.exit(1); }
215
+ const rt = routes().find((x) => x.record === s.record && x.collection !== "list")?.route ?? s.record;
216
+ const hits = filesOf(dir, s.record).filter((f) => existsSync(join(dir, f)));
217
+ if (!hits.length) { console.error(`${venture} has no ${s.record} record to confirm`); process.exit(1); }
218
+ const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
219
+ const shown = hits.map((f) => ` ${f}: ${JSON.stringify(get(JSON.parse(readFileSync(join(dir, f), "utf8")), s.attribute) ?? null)}`).join("\n");
220
+ if (!(await typed(id, `Confirming ${id} as yours:\n${shown}`))) { console.error("not confirmed: the confirmation did not match"); process.exit(1); }
221
+ for (const f of hits) record(dir, { entity: f, route: rt, activity: "confirm", statement: id, agent: agentFromEnv("person", "cli"), note: opt("note") });
222
+ out(`confirmed: ${id}`);
223
+ process.exit(0);
224
+ }
225
+
226
+ if (cmd === "baseline") {
227
+ // A venture that existed before the log: record each record file as it is now, once, so later
228
+ // writes have a starting point. Files already in the log are left alone.
229
+ const venture = opt("venture");
230
+ const dir = venture && ventureDir(venture, workspace);
231
+ if (!dir) usage();
232
+ const logged = new Set(entries(dir).map((e) => e.entity));
233
+ const globs = routes().filter((r) => r.kind === "record" && r.form === "json" && r.collection !== "list").flatMap((r) => r.files.map((g) => [r.route, g]));
234
+ const routeOf = (rt, g, f) => rt.endsWith("/[id]") ? rt.replace("[id]", f.match(new RegExp("^" + g.split("*").map((x) => x.replace(/[.+^${}()|[\]\\]/g, "\\$&")).join("([^/]*)") + "$"))[1]) : rt;
235
+ let n = 0;
236
+ for (const f of files(dir).filter((f) => !f.startsWith("_drafts/"))) {
237
+ const hit = globs.find(([, g]) => glob(g).test(f));
238
+ if (!hit || logged.has(f)) continue;
239
+ record(dir, { entity: f, route: routeOf(hit[0], hit[1], f), activity: "baseline", agent: agentFromEnv(opt("as") === "person" ? "person" : "agent", "cli"), note: "existed before the provenance log; its history before this is in git" });
240
+ n++;
241
+ }
242
+ out(`baseline: ${n} records added to ${venture}'s provenance log`);
243
+ process.exit(0);
244
+ }
245
+
246
+ if (!action) usage();
247
+ const venture = opt("venture");
248
+ if (!venture && !["template", "layout"].includes(action)) usage();
249
+ let input;
250
+ const src = opt("input");
251
+ if (src) input = JSON.parse(src === "-" ? readFileSync(0, "utf8") : readFileSync(src, "utf8"));
252
+ if (opt("value")) input = { ...(input ?? {}), value: Number.isNaN(Number(opt("value"))) ? opt("value") : Number(opt("value")) };
253
+ if (flag("as-is")) input = { ...(input ?? {}), as_is: true };
254
+ const provenance = { derived_from: opt("derived-from")?.split(",").filter(Boolean), decision: opt("decision"), note: opt("note") };
255
+ let r = await call({ venture, route: cmd, action, input, actor: opt("as") === "person" ? "person" : "agent", workspace, via: "cli", provenance });
256
+ // Asks before running (EE-050): at a terminal, the person chooses; otherwise it prints both choices.
257
+ if (r.ok && r.needs_input && !flag("json")) {
258
+ const n = r.needs_input;
259
+ console.log(`${n.ask}\n${n.missing.map((m) => ` - ${m.field}${m.items ? ` (${m.items.join(", ")})` : ""}: ${m.why}. As is: ${m.as_is}`).join("\n")}`);
260
+ if (!process.stdin.isTTY) { console.log(`Fill in: ${n.options[0].how}\nRun as is: ${n.options[1].how}`); process.exit(0); }
261
+ const { createInterface } = await import("node:readline/promises");
262
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
263
+ const a = (await rl.question("Fill in (f) or run as is (r)? ")).trim().toLowerCase();
264
+ rl.close();
265
+ if (!a.startsWith("r")) { console.log(`Fill in: ${n.options[0].how}`); process.exit(0); }
266
+ r = await call({ venture, route: cmd, action, input: { ...(input ?? {}), as_is: true }, actor: opt("as") === "person" ? "person" : "agent", workspace, via: "cli", provenance });
267
+ }
268
+ if (!r.ok) { console.error(r.error ?? JSON.stringify(r.result, null, 2)); process.exit(1); }
269
+ if (r.deprecated) console.error(`note: ${r.deprecated}`);
270
+ const extra = ["not_found", "pending_draft", "fix", "needs_input"].some((k) => r[k]);
271
+ out(flag("json") || extra ? r : r.result);
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ // The framework CLI (EE-072), the way `next` is Next.js's: run it inside a venture and it finds the
3
+ // venture.json in the current directory or its parents. mentura stays for platform commands and as an
4
+ // alias, so `mentura typegen` and `mentura upgrade` run these same commands.
5
+ //
6
+ // naive dev [--port 4331] the board, live: edit a record and it updates; each saved
7
+ // record is checked against its schema as you go (I-17, I-24)
8
+ // naive build check every package and write a release (I-17)
9
+ // naive start serve the latest release read-only (I-17)
10
+ // naive check run every check that applies to the venture (I-9)
11
+ // naive info versions, the venture, and which agents are connected (I-6)
12
+ // naive mcp serve the venture to an agent over MCP (stdio)
13
+ // naive typegen write .naive/types/ from the route table and the MCP tools
14
+ // naive upgrade [<venture dir>] [--framework] move a venture onto the next Naive layout; --framework first
15
+ // updates the installed framework with the venture's package manager (I-29)
16
+ // naive port --from <project> --venture <dir> plan bringing a project in with its files as they are
17
+ // naive port --venture <dir> --apply --confirm port
18
+ // carry out that plan (EE-076)
19
+ // naive migrate --from <project> --venture <dir> an agent rewrites the project as Naive records, as a plan and decisions
20
+ // naive migrate --venture <dir> --apply write what a person decided (EE-076)
21
+ //
22
+ // --venture <dir> names the venture when you are not inside it.
23
+
24
+ import { spawnSync } from "node:child_process";
25
+ import { existsSync, mkdirSync, readFileSync, watch, writeFileSync } from "node:fs";
26
+ import { createServer } from "node:net";
27
+ import { basename, join, relative, resolve } from "node:path";
28
+ import { asRouted, findVenture } from "./find-venture.mjs";
29
+
30
+ const repo = new URL("../..", import.meta.url).pathname;
31
+ const [cmd, ...rest] = process.argv.slice(2);
32
+ const opt = (n) => { const i = rest.indexOf(`--${n}`); return i >= 0 ? rest[i + 1] : undefined; };
33
+ const run = (script, args, opts = {}) => spawnSync("node", [join(repo, script), ...args], { stdio: "inherit", ...opts }).status ?? 1;
34
+ const version = () => JSON.parse(readFileSync(join(repo, "package.json"), "utf8")).version;
35
+
36
+ // The venture: --venture <dir>, else the one this command runs inside.
37
+ function here() {
38
+ const named = opt("venture");
39
+ const dir = named ? (existsSync(join(resolve(named), "venture.json")) ? resolve(named) : null) : findVenture();
40
+ if (!dir) {
41
+ console.error(named ? `${named} has no venture.json` : `no venture here: run naive ${cmd} inside a venture (a directory with venture.json), or pass --venture <dir>`);
42
+ process.exit(1);
43
+ }
44
+ return dir;
45
+ }
46
+
47
+ // A port that is free, starting at the one asked for, the way next dev moves on from 3000.
48
+ const free = (p) => new Promise((ok) => { const s = createServer().once("error", () => ok(free(p + 1))).once("listening", () => s.close(() => ok(p))).listen(p, "127.0.0.1"); });
49
+
50
+ // The schema a venture file is checked against, from the route table (records only).
51
+ async function schemaFor(file) {
52
+ const { glob, routes } = await import("../router.mjs");
53
+ return routes().find((r) => r.kind === "record" && r.schema && (r.files ?? []).some((g) => glob(g).test(file)))?.schema ?? null;
54
+ }
55
+
56
+ async function dev(dir) {
57
+ // The root layout is required (I-21): a venture without AGENTS.md gets a stub, as next dev writes a root layout.
58
+ if (!existsSync(join(dir, "AGENTS.md"))) {
59
+ writeFileSync(join(dir, "AGENTS.md"), `# AGENTS.md: ${basename(dir)}\n\nThe map of this venture for agents. Read it, then a route's layout, before writing anything here.\n\n## Instructions\n\nphase: ${JSON.parse(readFileSync(join(dir, "venture.json"), "utf8")).phase}\nprivate: _private/\n`);
60
+ console.log(" wrote AGENTS.md, the venture's root layout, because it was missing");
61
+ }
62
+ const { workspace, venture } = asRouted(dir);
63
+ process.env.MENTURA_WORKSPACE = workspace;
64
+ const port = await free(Number(opt("port") ?? 4331));
65
+ const { serve } = await import("../demo/board.mjs");
66
+ serve(port, venture);
67
+ const name = JSON.parse(readFileSync(join(dir, "venture.json"), "utf8")).name ?? venture;
68
+ console.log(`\n ▲ Naive ${version()}\n - Venture: ${name} (${relative(process.cwd(), dir) || "."})\n - Board: http://127.0.0.1:${port}/\n - Map: http://127.0.0.1:${port}/map\n\n Watching for changes. Each saved record is checked against its schema. Ctrl-C to stop.\n`);
69
+ // Live feedback on save (I-24): a record that no longer matches its schema says so at once.
70
+ const pending = new Map();
71
+ watch(dir, { recursive: true }, (_, f) => {
72
+ if (!f || !f.endsWith(".json") || f.startsWith("node_modules") || f.startsWith(".naive") || f.startsWith("_private")) return;
73
+ clearTimeout(pending.get(f));
74
+ pending.set(f, setTimeout(async () => {
75
+ pending.delete(f);
76
+ if (!existsSync(join(dir, f))) return;
77
+ const schema = await schemaFor(f);
78
+ if (!schema) return;
79
+ const v = spawnSync("python3", [join(repo, "schema/validate.py"), "--schema", schema.replace(/^schema\//, ""), "--file", join(dir, f)], { encoding: "utf8" });
80
+ const out = (v.stdout + v.stderr).trim();
81
+ if (v.status === 0) console.log(` ✓ ${f}`);
82
+ else if (/needs jsonschema/.test(out)) console.log(` - ${f} changed (install python3's jsonschema package to check it as you save)`);
83
+ else console.log(` ✗ ${f} does not match ${schema}\n${out.split("\n").map((l) => ` ${l}`).join("\n")}`);
84
+ }, 150));
85
+ });
86
+ }
87
+
88
+ // The checks that read a workspace, run on the folder that holds this venture.
89
+ const CHECKS = ["check-ventures", "check-provenance", "check-sources", "check-data-packages", "check-governance", "check-telos", "check-publication", "check-private", "check-where-used"];
90
+ function check(dir) {
91
+ const { workspace } = asRouted(dir);
92
+ let failed = 0;
93
+ for (const c of CHECKS) {
94
+ const r = spawnSync("node", [join(repo, `schema/${c}.mjs`), workspace], { encoding: "utf8" });
95
+ const last = (r.stdout + r.stderr).trim().split("\n").filter(Boolean).at(-1) ?? "";
96
+ if (r.status !== 0) failed++;
97
+ console.log(`${r.status === 0 ? "pass" : "FAIL"} ${c.replace("check-", "")}: ${last}`);
98
+ if (r.status !== 0) console.log((r.stdout + r.stderr).trim().split("\n").slice(0, 12).map((l) => ` ${l}`).join("\n"));
99
+ }
100
+ console.log(failed ? `\n${failed} failing` : "\nall passing");
101
+ return failed ? 1 : 0;
102
+ }
103
+
104
+ function info(dir) {
105
+ const v = dir && JSON.parse(readFileSync(join(dir, "venture.json"), "utf8"));
106
+ // Supported agents (I-6): the MCP clients Naive is tested with, and whether this venture is connected to each.
107
+ const agents = [
108
+ ["Claude Code", ".mcp.json", (j) => j?.mcpServers?.naive || j?.mcpServers?.mentura],
109
+ ["Cursor", ".cursor/mcp.json", (j) => j?.mcpServers?.naive || j?.mcpServers?.mentura],
110
+ ];
111
+ const read = (f) => { try { return JSON.parse(readFileSync(f, "utf8")); } catch { return null; } };
112
+ const lines = [
113
+ `Naive ${version()} (${repo.replace(/\/$/, "")})`,
114
+ `Node ${process.version}`,
115
+ `Platform ${process.platform} ${process.arch}`,
116
+ `python3 ${spawnSync("python3", ["-c", "import jsonschema; print('jsonschema ' + __import__('importlib.metadata').metadata.version('jsonschema'))"], { encoding: "utf8" }).stdout.trim() || "jsonschema missing (needed by schema checks)"}`,
117
+ `Venture ${v ? `${v.name ?? v.id} (${v.phase}) at ${dir}` : "none here"}`,
118
+ "Agents " + agents.map(([name, f, ok]) => `${name}: ${dir && ok(read(join(dir, f))) ? "connected" : "not connected"}`).join(", "),
119
+ ];
120
+ console.log(lines.join("\n"));
121
+ }
122
+
123
+ function upgradeFramework(dir) {
124
+ const pkg = join(dir, "package.json");
125
+ const deps = existsSync(pkg) ? { ...JSON.parse(readFileSync(pkg, "utf8")).devDependencies, ...JSON.parse(readFileSync(pkg, "utf8")).dependencies } : {};
126
+ const name = Object.keys(deps).find((d) => d === "naive-framework");
127
+ if (!name) { console.error("this venture does not install the framework from its package.json, so there is nothing to update; upgrading its layout only"); return 0; }
128
+ const pm = existsSync(join(dir, "pnpm-lock.yaml")) ? "pnpm" : existsSync(join(dir, "yarn.lock")) ? "yarn" : existsSync(join(dir, "bun.lockb")) || existsSync(join(dir, "bun.lock")) ? "bun" : "npm";
129
+ const spec = deps[name].startsWith("file:") || deps[name].startsWith("github:") ? deps[name] : `${name}@latest`;
130
+ const args = pm === "npm" ? ["install", "--save-dev", spec] : pm === "yarn" ? ["add", "--dev", spec] : ["add", "-D", spec];
131
+ console.log(`updating the framework: ${pm} ${args.join(" ")}`);
132
+ return spawnSync(pm, args, { cwd: dir, stdio: "inherit" }).status ?? 1;
133
+ }
134
+
135
+ if (cmd === "dev") await dev(here());
136
+ else if (cmd === "check") process.exit(check(here()));
137
+ else if (cmd === "mcp") {
138
+ // The MCP server, on the venture this runs in: `claude mcp add naive -- npx naive mcp`.
139
+ const env = { ...process.env };
140
+ if (!env.MENTURA_WORKSPACE) env.MENTURA_WORKSPACE = asRouted(here()).workspace;
141
+ process.exit(spawnSync("node", [join(repo, "interface/mcp/server.mjs")], { stdio: "inherit", env }).status ?? 1);
142
+ }
143
+ else if (cmd === "info") info(findVenture() ?? (opt("venture") && existsSync(join(resolve(opt("venture")), "venture.json")) ? resolve(opt("venture")) : null));
144
+ else if (cmd === "build" || cmd === "start") {
145
+ // The release itself is written and served by schema/build-release.mjs and schema/start-release.mjs.
146
+ const script = `schema/${cmd}-release.mjs`;
147
+ if (!existsSync(join(repo, script))) { console.error(`naive ${cmd} runs ${script}, which this version of Naive does not have yet`); process.exit(1); }
148
+ process.exit(run(script, [here(), ...rest.filter((a, i) => a !== "--venture" && rest[i - 1] !== "--venture")]));
149
+ } else if (cmd === "upgrade") {
150
+ // Outside a venture with none named, upgrade-venture.mjs prints its usage.
151
+ const dir = rest[0] && !rest[0].startsWith("--") ? resolve(rest[0]) : (opt("venture") ? here() : findVenture());
152
+ if (dir && rest.includes("--framework") && upgradeFramework(dir) !== 0) process.exit(1);
153
+ process.exit(run("schema/upgrade-venture.mjs", dir ? [dir] : []));
154
+ } else if (cmd === "port" || cmd === "migrate") {
155
+ // Inside a venture, --venture defaults to it.
156
+ const args = rest.includes("--venture") ? rest : [...rest, "--venture", here()];
157
+ process.exit(run(`schema/${cmd}-venture.mjs`, args));
158
+ } else if (cmd === "typegen") {
159
+ const { TOOLS } = await import("../mcp/server.mjs");
160
+ const { routes } = await import("../router.mjs");
161
+ const out = join(repo, ".naive/types");
162
+ mkdirSync(out, { recursive: true });
163
+ writeFileSync(join(out, "tools.json"), JSON.stringify(TOOLS, null, 2) + "\n");
164
+ writeFileSync(join(out, "routes.json"), JSON.stringify(routes().map((r) => ({ route: r.route, actions: r.actions, kind: r.kind })), null, 2) + "\n");
165
+ console.log(`wrote ${out}`);
166
+ } else {
167
+ console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// ")).map((l) => l.slice(5)).join("\n"));
168
+ process.exit(cmd ? 1 : 2);
169
+ }