naive-framework 0.1.0 → 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 +2 -1
- package/docs/decisions/decisions.json +27 -0
- package/docs/reference/ROUTES.md +36 -37
- package/governance/layer.json +2 -1
- 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 +4 -2
- package/interface/boundaries.mjs +218 -0
- package/interface/brand.mjs +214 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +78 -8
- package/interface/cli/naive.mjs +33 -8
- package/interface/context.mjs +524 -0
- package/interface/demo/board.mjs +1 -1
- package/interface/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +36 -0
- package/interface/harness/evals.mjs +3 -3
- package/interface/harness/run.mjs +4 -4
- package/interface/instrumentation.mjs +86 -0
- package/interface/kit-boundary.mjs +73 -0
- package/interface/layer.json +8 -2
- package/interface/mcp/server.mjs +47 -7
- package/interface/package-routes.mjs +196 -0
- package/interface/provenance.mjs +24 -1
- package/interface/reads.mjs +173 -0
- package/interface/release-card.mjs +42 -0
- package/interface/router.mjs +101 -28
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/vitals.mjs +115 -0
- package/interface/writes.mjs +190 -0
- package/knowledge/freshness.json +85 -0
- package/knowledge/layer.json +11 -3
- package/knowledge/templates/glossary.md +23 -0
- package/operations/layer.json +10 -3
- package/operations/templates/marketing-brand.md +46 -0
- package/operations/templates/public-assets.md +39 -0
- package/package.json +3 -1
- package/packages/catalog.json +8 -0
- package/packages/customers/package.json +21 -1
- package/packages/deals/package.json +27 -0
- package/packages/decisions/package.json +12 -1
- package/packages/finance/frames/fund-without-ceding-control.frame.json +177 -0
- package/packages/foundation/package.json +8 -1
- package/packages/fund/package.json +48 -0
- package/packages/marketing/package.json +10 -4
- package/packages/money/package.json +24 -3
- package/packages/validation/package.json +13 -0
- package/platform/README.md +6 -0
- package/platform/adapter.json +15 -0
- package/platform/integrations/remote-image.mjs +79 -0
- package/platform/integrations/rho.mjs +7 -6
- package/platform/kits/README.md +8 -1
- 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/webhooks/server.mjs +70 -0
- package/purpose/frameworks/idea-map/ideamap.mjs +4 -2
- package/schema/README.md +1 -0
- package/schema/access.mjs +57 -0
- package/schema/adapter.schema.json +33 -0
- package/schema/assets.mjs +212 -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-brand.mjs +24 -0
- package/schema/check-config.mjs +110 -0
- package/schema/check-nextjs-status.mjs +3 -1
- package/schema/check-people.mjs +70 -0
- package/schema/check-sources.mjs +9 -1
- package/schema/data-model.json +334 -8
- package/schema/deal.schema.json +80 -0
- package/schema/decisions.mjs +7 -1
- package/schema/examples/frames/judge.mjs +10 -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/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 +105 -11
- package/schema/glossary.schema.json +25 -0
- package/schema/init-venture.mjs +38 -8
- 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 +145 -15
- package/schema/layer.json +16 -3
- package/schema/marketing-brand.schema.json +116 -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 +171 -0
- package/schema/need-now.mjs +3 -2
- package/schema/packages.mjs +10 -5
- package/schema/people.schema.json +86 -0
- package/schema/public-assets.schema.json +76 -0
- package/schema/release-read.mjs +111 -0
- package/schema/releases.mjs +497 -0
- package/schema/setup.mjs +164 -0
- package/schema/stale.mjs +111 -0
- package/schema/start-release.mjs +41 -0
- package/schema/structure.mjs +186 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -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 +96 -118
- package/schema/upgrades.json +69 -0
- package/schema/upgrades.mjs +385 -0
- package/schema/validate.py +19 -10
- 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 +53 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// The router's hooks into the gate group (X-1 to X-6, P-2, P-7, U-4). Each is called from one
|
|
2
|
+
// marked line in interface/router.mjs, so the router stays the route table and the logic stays in
|
|
3
|
+
// schema/gate.mjs (the verdict), schema/gate-drafts.mjs (held drafts) and schema/naive-config.mjs
|
|
4
|
+
// (the venture's overrides).
|
|
5
|
+
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { evaluate } from "../schema/gate.mjs";
|
|
9
|
+
import { writeHeld } from "../schema/gate-drafts.mjs";
|
|
10
|
+
import { checksFor, configFor } from "../schema/naive-config.mjs";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The gate's verdict on a local write, or null when it passes. A person is never held, only
|
|
14
|
+
* refused. A naive.config.json that is not valid JSON fails closed: an agent's write is held.
|
|
15
|
+
*/
|
|
16
|
+
export function gateWrite({ dir, r, route, requested, file, actor, actor_id, provenance = {}, gate }) {
|
|
17
|
+
let config;
|
|
18
|
+
try { config = configFor(dir); }
|
|
19
|
+
catch (e) { return actor === "person" ? null : { then: "draft", rule: "naive.config.json", source: "naive.config.json", why: `${e.message}: the gate fails closed, written as a draft for a person to accept` }; }
|
|
20
|
+
const confidence = provenance.confidence === undefined || provenance.confidence === null || provenance.confidence === "" ? undefined : Number(provenance.confidence);
|
|
21
|
+
const v = evaluate({ config, dir, route, table: r.route, requested, record: r.record, actor, role: actor_id, confidence: Number.isNaN(confidence) ? undefined : confidence,
|
|
22
|
+
exists: existsSync(join(dir, file)), decisionOwner: gate?.owner ?? undefined });
|
|
23
|
+
if (v.then === "pass") return null;
|
|
24
|
+
if (actor === "person" && v.then !== "refuse") return null;
|
|
25
|
+
return v;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Stores an agent's held draft: its sidecar, the owner it was sent to, and variants side by side. */
|
|
29
|
+
export function heldDraft(dir, { draft, file, text, verdict, route, agent }) {
|
|
30
|
+
return writeHeld(dir, { draft, file, text, verdict, route, agent });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A route's checks with their mode from naive.config.json: run, warn or off. A broken config runs them all. */
|
|
34
|
+
export function checkPlan(dir, checks) {
|
|
35
|
+
try { return checksFor(configFor(dir), checks); } catch { return checks.map((check) => ({ check, mode: "run" })); }
|
|
36
|
+
}
|
|
@@ -72,7 +72,7 @@ export function evaluate(dir, { researched = [], settled = [] } = {}) {
|
|
|
72
72
|
add("founder_settled", settled.length ? 1 : 0, settled.length ? `settled: ${settled.join(", ")}` : "the founder settled nothing");
|
|
73
73
|
// Money (EE-070): when the venture has a money summary, it is fresh, the agent cites it, and no raw
|
|
74
74
|
// transaction leaves its sensitive stream for another record.
|
|
75
|
-
const summaryFile = join(dir, "money/
|
|
75
|
+
const summaryFile = join(dir, "money/_summary.json");
|
|
76
76
|
if (existsSync(summaryFile)) {
|
|
77
77
|
const m = load(summaryFile);
|
|
78
78
|
const age = (Date.now() - Date.parse(m.synced_at)) / 864e5;
|
|
@@ -80,8 +80,8 @@ export function evaluate(dir, { researched = [], settled = [] } = {}) {
|
|
|
80
80
|
// Only what touches money has to cite it: pricing, spend, hiring, runway, fundraising, cash.
|
|
81
81
|
const records = [...agentDrafted, ...(problem ? [{ problem: load(problem) }] : [])].map((d) => JSON.stringify(d));
|
|
82
82
|
const touching = records.filter((x) => /money\/summary|\b(pric(e|ing)|spend|budget|hir(e|ing)|runway|burn|fundrais|cash|revenue)\b/i.test(x));
|
|
83
|
-
const citing = touching.filter((x) => x.includes("money/
|
|
84
|
-
add("money_cited", frac(citing.length, touching.length), touching.length ? `${citing.length} of ${touching.length} agent records that touch money cite money/
|
|
83
|
+
const citing = touching.filter((x) => x.includes("money/_summary"));
|
|
84
|
+
add("money_cited", frac(citing.length, touching.length), touching.length ? `${citing.length} of ${touching.length} agent records that touch money cite money/_summary.json` : "no agent record touches money, so none needs to cite it");
|
|
85
85
|
const leaked = walk(dir).filter((f) => !f.includes("/money/_streams/") && f.endsWith(".json") && /:transaction:/.test(readFileSync(f, "utf8")));
|
|
86
86
|
add("money_private", leaked.length ? 0 : 1, leaked.length ? `transaction pointers outside the money stream: ${leaked.map((f) => f.slice(dir.length + 1)).join(", ")}` : "transactions stay in their sensitive stream");
|
|
87
87
|
}
|
|
@@ -83,9 +83,9 @@ if (opt("money")) {
|
|
|
83
83
|
const m = opt("money");
|
|
84
84
|
const src = m === "live" ? ["--live"] : m === "sandbox" ? [] : ["--fixture", resolve(m)];
|
|
85
85
|
const p = spawnSync("node", [join(repo, "platform/integrations/rho.mjs"), "sync", "--venture", dir, ...src], { encoding: "utf8" });
|
|
86
|
-
if (p.status === 0 && existsSync(join(dir, "money/
|
|
87
|
-
money = JSON.parse(readFileSync(join(dir, "money/
|
|
88
|
-
prov(dir, { entity: "money/
|
|
86
|
+
if (p.status === 0 && existsSync(join(dir, "money/_summary.json"))) {
|
|
87
|
+
money = JSON.parse(readFileSync(join(dir, "money/_summary.json"), "utf8"));
|
|
88
|
+
prov(dir, { entity: "money/_summary.json", route: "money/_summary", activity: "create", agent: agentFromEnv("agent", "harness", { id: "naive-money" }), note: `synced from ${money.source}; ${money.transactions} transactions kept as pointers in a sensitive stream` });
|
|
89
89
|
lap(`money synced from ${money.source}${money.business ? ` (${money.business})` : ""}: cash ${(money.cash.amount / 100).toFixed(2)} ${money.cash.currency}, monthly net ${(money.monthly_net.amount / 100).toFixed(2)}, runway ${money.runway_months ?? "not limited by burn"}, as of ${money.as_of}`);
|
|
90
90
|
} else console.log(` money sync failed, so the run goes on without it: ${(p.stderr || p.stdout).trim().slice(0, 300)}`);
|
|
91
91
|
}
|
|
@@ -236,7 +236,7 @@ lap("the founder confirmed Mentura's understanding of the ideas; routing them");
|
|
|
236
236
|
|
|
237
237
|
const rules = `You are the builder agent (agent_id builder) for the venture ${id}. Work only through the naive MCP tools${research ? " and the research tools" : ""}; pass agent_id "builder" on every naive call.
|
|
238
238
|
Rules: the founder's idea dump is theirs. Quote it, cite ideas by id (i-01...), never put words in their mouth. Never invent evidence: every evidence item cites a URL a research result gave you${research ? "" : " (you have no research tool in this run, so leave evidence empty and say so)"}. Records Naive treats as the founder's are written as drafts for the founder to accept; that is expected. You never decide; you propose. knowledge/venture-profile cannot be written over MCP; skip it.${money ? `
|
|
239
|
-
Money: Naive synced the venture's money summary from ${money.source}, the system of record (money/
|
|
239
|
+
Money: Naive synced the venture's money summary from ${money.source}, the system of record (money/_summary.json): ${JSON.stringify(money)}. Amounts are in minor units (cents). Any proposal or record that touches money (pricing, spend, hiring, runway, fundraising) cites "money/_summary.json as of ${money.as_of}" and uses only the figures the summary gives; never estimate the founder's finances.` : ""}`;
|
|
240
240
|
|
|
241
241
|
await agent(`${rules}
|
|
242
242
|
The founder has just submitted an idea dump. Naive mapped it onto its conventions.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Instrumentation (Naive's instrumentation.ts, P-4; logging fetches in development, F-4). One hook
|
|
2
|
+
// where every router call, every judgment and every provenance entry (write, draft, accept,
|
|
3
|
+
// confirm, decision) emits an event. Listeners registered in-process get every event. When a venture
|
|
4
|
+
// turns instrumentation on, events are also appended to <venture>/.naive/events.jsonl, which agent
|
|
5
|
+
// vitals read (interface/vitals.mjs, N-6). Provenance stays the record of what changed; events are
|
|
6
|
+
// what it cost. A platform exporter (platform/integrations/langsmith.mjs) can ship a trace made from
|
|
7
|
+
// the events; the framework names no vendor.
|
|
8
|
+
//
|
|
9
|
+
// Turn it on in the venture's naive.config.json, like adding instrumentation.ts to a Next.js app:
|
|
10
|
+
//
|
|
11
|
+
// { "instrumentation": { "events": true } }
|
|
12
|
+
//
|
|
13
|
+
// NAIVE_INSTRUMENT=1 turns it on for every venture in this process, and NAIVE_INSTRUMENT=0 off.
|
|
14
|
+
// No dependencies and no network. An event holds routes, sizes and times, never record content.
|
|
15
|
+
|
|
16
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
17
|
+
import { randomUUID } from "node:crypto";
|
|
18
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
|
|
21
|
+
export const EVENTS = ".naive/events.jsonl";
|
|
22
|
+
const listeners = new Set();
|
|
23
|
+
const scope = new AsyncLocalStorage();
|
|
24
|
+
|
|
25
|
+
/** Tokens a payload costs an agent, estimated as characters / 4 (the common rule of thumb). */
|
|
26
|
+
export const tokens = (x) => Math.ceil((x === undefined ? 0 : typeof x === "string" ? x.length : (JSON.stringify(x) ?? "").length) / 4);
|
|
27
|
+
|
|
28
|
+
/** Registers a listener for every event, like register() in instrumentation.ts. Returns the unregister. */
|
|
29
|
+
export function register(fn) { listeners.add(fn); return () => listeners.delete(fn); }
|
|
30
|
+
|
|
31
|
+
const configOf = (dir) => {
|
|
32
|
+
const f = dir && join(dir, "naive.config.json");
|
|
33
|
+
if (!f || !existsSync(f)) return {};
|
|
34
|
+
try { return JSON.parse(readFileSync(f, "utf8")); } catch { return {}; }
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Whether a venture writes its events log. */
|
|
38
|
+
export function enabled(dir) {
|
|
39
|
+
if (process.env.NAIVE_INSTRUMENT === "0") return false;
|
|
40
|
+
if (process.env.NAIVE_INSTRUMENT === "1") return !!dir;
|
|
41
|
+
const i = configOf(dir).instrumentation;
|
|
42
|
+
return i === true || i?.events === true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Emits one event: to every listener, and to the venture's events log when it is on. */
|
|
46
|
+
export function emit(dir, event) {
|
|
47
|
+
const e = { at: new Date().toISOString(), id: randomUUID(), session: process.env.MENTURA_SESSION ?? null, ...event };
|
|
48
|
+
for (const k of Object.keys(e)) if (e[k] === undefined) delete e[k];
|
|
49
|
+
for (const fn of listeners) { try { fn(e, dir); } catch {} }
|
|
50
|
+
if (dir && enabled(dir)) {
|
|
51
|
+
try { mkdirSync(join(dir, ".naive"), { recursive: true }); appendFileSync(join(dir, EVENTS), JSON.stringify(e) + "\n"); } catch {}
|
|
52
|
+
}
|
|
53
|
+
return e;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The venture's events, oldest first. */
|
|
57
|
+
export function events(dir) {
|
|
58
|
+
const f = join(dir, EVENTS);
|
|
59
|
+
if (!existsSync(f)) return [];
|
|
60
|
+
return readFileSync(f, "utf8").split("\n").filter((l) => l.trim()).flatMap((l) => { try { return [JSON.parse(l)]; } catch { return []; } });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Wraps one router call: times it, measures what it sends back, and emits a call event. A call made
|
|
65
|
+
* inside another (a view reads its record, the overview runs its slots) carries its parent's id, so
|
|
66
|
+
* vitals count only what the agent asked for. dirOf resolves the venture folder.
|
|
67
|
+
*/
|
|
68
|
+
export async function instrumented(args, fn, dirOf) {
|
|
69
|
+
const parent = scope.getStore() ?? null;
|
|
70
|
+
const id = randomUUID();
|
|
71
|
+
const t0 = performance.now();
|
|
72
|
+
const r = await scope.run(id, () => fn(args));
|
|
73
|
+
if (process.env.NAIVE_INSTRUMENT === "0" && !listeners.size) return r;
|
|
74
|
+
let dir = null;
|
|
75
|
+
try { dir = args.venture ? dirOf(args.venture, args.workspace) : null; } catch {}
|
|
76
|
+
if (!dir && !listeners.size) return r;
|
|
77
|
+
const useful = !!r?.ok && r.result !== null && r.result !== undefined && !r.needs_input;
|
|
78
|
+
emit(dir, { kind: "call", id, parent, session: args.session ?? args.input?.session ?? process.env.MENTURA_SESSION ?? null, venture: args.venture ?? null, route: args.route, action: args.action,
|
|
79
|
+
ok: !!r?.ok, useful, ms: Math.round(performance.now() - t0), tokens: tokens(r?.result ?? r?.error), actor: args.actor ?? "agent", via: args.via ?? "router" });
|
|
80
|
+
return r;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Records one judgment (a System 1 call or a scored check): what was asked, the score and the outcome. */
|
|
84
|
+
export function judgment(dir, { question, score, threshold, outcome, owner, model, route, ms }) {
|
|
85
|
+
return emit(dir, { kind: "judgment", question, score, threshold, outcome, owner, model, route, ms });
|
|
86
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The boundary around a kit's agent workflow (S-15): the Next.js advice to wrap a third-party component
|
|
2
|
+
// that uses client features, with the library author declaring "use client". A kit (Mentura's enhanced
|
|
3
|
+
// package, platform/kits/<name>/kit.json) declares, under boundary, what its workflow reads, what it
|
|
4
|
+
// writes and which of its steps use a model:
|
|
5
|
+
//
|
|
6
|
+
// { "name": "...", "package": "<the package it extends>",
|
|
7
|
+
// "boundary": { "reads": [<route globs>], "writes": [<route globs>], "use_model": [<model steps>] } }
|
|
8
|
+
//
|
|
9
|
+
// wrap(kit) returns the router's call with that boundary around it: a read outside reads or writes, a
|
|
10
|
+
// write outside writes, or a command it does not read is refused as an expected error, and every call
|
|
11
|
+
// acts as the kit's agent, never as a person. A kit that uses a model writes judgments, so it declares
|
|
12
|
+
// knowledge/judgment/** in writes. This module reads kit.json as data and never imports from platform/.
|
|
13
|
+
//
|
|
14
|
+
// node interface/kit-boundary.mjs <kit.json> check one kit's boundary
|
|
15
|
+
|
|
16
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { call as routerCall, glob, repo, routes } from "./router.mjs";
|
|
19
|
+
// schema/model-steps.mjs is imported inside checkKit, not here, because it imports wrap from this module.
|
|
20
|
+
|
|
21
|
+
const ID = /^[a-z0-9][a-z0-9-]*$/;
|
|
22
|
+
export const actorOf = (kit) => `kit-${kit.name}`;
|
|
23
|
+
const any = (globs, route) => (globs ?? []).some((g) => glob(g).test(route));
|
|
24
|
+
const reachable = (b, route) => any(b.reads, route) || any(b.writes, route);
|
|
25
|
+
|
|
26
|
+
/** Errors in a kit's declared boundary, its model steps included. */
|
|
27
|
+
export async function checkKit(kit) {
|
|
28
|
+
const { checkSteps, inputOf } = await import("../schema/model-steps.mjs");
|
|
29
|
+
const e = [];
|
|
30
|
+
if (!kit || typeof kit !== "object") return ["a kit.json is an object"];
|
|
31
|
+
if (typeof kit.name !== "string" || !ID.test(kit.name)) e.push("a kit has a name of lowercase letters, digits and hyphens");
|
|
32
|
+
if (typeof kit.package !== "string" || !existsSync(join(repo, "packages", kit.package, "package.json"))) e.push(`kit ${kit.name}: package ${kit.package} is not a package in packages/ (a kit extends one package and never replaces it)`);
|
|
33
|
+
const b = kit.boundary;
|
|
34
|
+
if (!b || typeof b !== "object") { e.push(`kit ${kit.name}: declares a boundary: what it reads, what it writes, which steps use a model`); return e; }
|
|
35
|
+
for (const k of ["reads", "writes", "use_model"]) if (!Array.isArray(b[k])) e.push(`kit ${kit.name}: boundary.${k} is an array${k === "use_model" ? ", empty when it calls no model" : ""}`);
|
|
36
|
+
if (e.length) return e;
|
|
37
|
+
const all = routes().map((r) => r.route);
|
|
38
|
+
for (const g of [...b.reads, ...b.writes]) if (!all.some((r) => glob(g).test(r) || glob(g).test(r.replace("[id]", "x")))) e.push(`kit ${kit.name}: ${g} matches no route`);
|
|
39
|
+
const steps = b.use_model.map((s) => ({ ...s, package: kit.name }));
|
|
40
|
+
e.push(...(await checkSteps(steps)).map((x) => `kit ${x}`));
|
|
41
|
+
for (const s of steps) {
|
|
42
|
+
if (s.for_each && !reachable(b, s.for_each)) e.push(`kit ${kit.name}/${s.step}: reads ${s.for_each}, outside the kit's reads`);
|
|
43
|
+
for (const i of s.inputs ?? []) { const p = inputOf(i); if (p.form === "route" && !reachable(b, p.route)) e.push(`kit ${kit.name}/${s.step}: input ${i} is outside the kit's reads`); }
|
|
44
|
+
for (const spec of Object.values(s.computed ?? {})) {
|
|
45
|
+
const route = spec?.count ?? (spec?.read ? String(spec.read).split("#")[0] : null);
|
|
46
|
+
if (route && !reachable(b, route)) e.push(`kit ${kit.name}/${s.step}: computes from ${route}, outside the kit's reads`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (steps.length && !any(b.writes, "knowledge/judgment/x")) e.push(`kit ${kit.name}: uses a model, so it writes judgments: declare knowledge/judgment/** in boundary.writes`);
|
|
50
|
+
return e;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The router's call, wrapped in the kit's boundary. */
|
|
54
|
+
export function wrap(kit, call = routerCall) {
|
|
55
|
+
const b = kit.boundary ?? { reads: [], writes: [], use_model: [] };
|
|
56
|
+
return async (args) => {
|
|
57
|
+
const route = args.route;
|
|
58
|
+
const act = args.action === "write" ? "write" : "read";
|
|
59
|
+
const ok = act === "write" ? any(b.writes, route) : reachable(b, route);
|
|
60
|
+
if (!ok) return { ok: false, outcome: "expected", error: `kit ${kit.name} declares no ${act} of ${route} (boundary.${act === "write" ? "writes" : "reads"} in its kit.json)` };
|
|
61
|
+
// The kit's workflow is an agent, whatever the caller passed.
|
|
62
|
+
return call({ ...args, actor: "agent", actor_id: actorOf(kit), provenance: { ...(args.provenance ?? {}), note: `kit ${kit.name}${args.provenance?.note ? `: ${args.provenance.note}` : ""}` } });
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
67
|
+
const f = process.argv[2];
|
|
68
|
+
if (!f) { console.error("usage: node interface/kit-boundary.mjs <kit.json>"); process.exit(2); }
|
|
69
|
+
const kit = JSON.parse(readFileSync(f, "utf8"));
|
|
70
|
+
const e = await checkKit(kit);
|
|
71
|
+
if (e.length) { console.error(e.join("\n")); process.exit(1); }
|
|
72
|
+
console.log(`kit ${kit.name}: reads ${kit.boundary.reads.length} route patterns, writes ${kit.boundary.writes.length}, ${kit.boundary.use_model.length} model steps, inside its boundary.`);
|
|
73
|
+
}
|
package/interface/layer.json
CHANGED
|
@@ -6,10 +6,15 @@
|
|
|
6
6
|
{
|
|
7
7
|
"id": "install-mentura",
|
|
8
8
|
"path": "interface/skills/install-mentura/SKILL.md"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "naive-setup",
|
|
12
|
+
"path": "interface/skills/naive-setup/SKILL.md"
|
|
9
13
|
}
|
|
10
14
|
],
|
|
11
15
|
"records": [
|
|
12
|
-
"route-aliases"
|
|
16
|
+
"route-aliases",
|
|
17
|
+
"adapter"
|
|
13
18
|
],
|
|
14
19
|
"reads": [
|
|
15
20
|
"integration"
|
|
@@ -17,7 +22,8 @@
|
|
|
17
22
|
"writes": [],
|
|
18
23
|
"checks": [
|
|
19
24
|
"schema/check-integrations.mjs",
|
|
20
|
-
"interface/test.mjs"
|
|
25
|
+
"interface/test.mjs",
|
|
26
|
+
"interface/adapters.mjs"
|
|
21
27
|
],
|
|
22
28
|
"gaps": [
|
|
23
29
|
"The operator console is a folder, not part of the layer yet",
|
package/interface/mcp/server.mjs
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { createInterface } from "node:readline";
|
|
12
12
|
import { call, routes, routesMd, ventureDir, ventures } from "../router.mjs";
|
|
13
|
+
import { runStreaming } from "../context.mjs";
|
|
13
14
|
import { propose } from "../../schema/decisions.mjs";
|
|
14
15
|
|
|
15
16
|
const PROTOCOL = "2025-06-18";
|
|
@@ -20,17 +21,42 @@ export const TOOLS = [
|
|
|
20
21
|
{ name: "mentura_layout", description: "The context for a route before you act on it: the system, layer and kit purpose, skills and references, the schema and template, the gate, and what need-now says about the record.", inputSchema: { type: "object", properties: { venture, route }, required: ["route"] } },
|
|
21
22
|
{ name: "mentura_view", description: "A route as a Markdown page for a person. system/overview shows the venture at a glance.", inputSchema: { type: "object", properties: { venture, route }, required: ["venture", "route"] } },
|
|
22
23
|
{ name: "mentura_ventures", description: "The ventures in the workspace, with their phase.", inputSchema: { type: "object", properties: {} } },
|
|
23
|
-
{ name: "mentura_read", description: "Read one record of a venture
|
|
24
|
+
{ name: "mentura_read", description: "Read one record of a venture, header first: the header (what it is, its state, owner and size in tokens), the budget, the layout (in a session, only the parts you have not seen), the template (need-now and open decisions, fresh), the record as result (null when the venture has no such record yet), and prefetch: short summaries of the routes you are likely to need next. Pass header_only to stop after the header. A list returns only the items your role may read.", inputSchema: { type: "object", properties: { venture, route,
|
|
25
|
+
session: { type: "string", description: "Your session id: the layout is sent once per session, then only the parts that changed" },
|
|
26
|
+
task: { type: "string", description: "What you are doing; a route it names is prefetched in full" },
|
|
27
|
+
prefetch: { description: "false turns prefetch off; a list of routes prefetches those in full", oneOf: [{ type: "boolean" }, { type: "array", items: { type: "string" } }] },
|
|
28
|
+
header_only: { type: "boolean", description: "Return only the header, to decide whether to read on" },
|
|
29
|
+
where: { type: "object", description: "For a list: keep only records whose fields match these values" },
|
|
30
|
+
agent_id: { type: "string", description: "Your role in the team plan, so its read routes apply" } }, required: ["venture", "route"] } },
|
|
24
31
|
{ name: "mentura_write", description: "Write one record of a venture. The record must match its schema. A gated record is written as a draft under _drafts/ for a person to accept. Every write is logged in the venture's provenance log: say what the content came from and the decision it acts under.", inputSchema: { type: "object", properties: { venture, route, record: { type: "object", description: "The whole record" },
|
|
25
32
|
derived_from: { type: "array", items: { type: "string" }, description: "What the content came from: venture-relative paths, URLs or decision ids" },
|
|
26
33
|
decision: { type: "string", description: "The decision the write acts under, such as EE-026" },
|
|
27
34
|
model: { type: "string", description: "Your model, for the provenance log" }, note: { type: "string" },
|
|
28
|
-
agent_id: { type: "string", description: "Your role in the team plan, so its routes and unlocks apply" }
|
|
35
|
+
agent_id: { type: "string", description: "Your role in the team plan, so its routes and unlocks apply" },
|
|
36
|
+
// Gate group hook (X-4): a score computed before the write; the gate's confidence rules read it and never compute one.
|
|
37
|
+
confidence: { type: "number", description: "Your confidence in this record, 0 to 1, if you computed one. Below the venture's threshold, a gate rule may send the write to its function owner" } }, required: ["venture", "route", "record"] } },
|
|
29
38
|
{ name: "mentura_decisions", description: "The decisions a venture owes (EE-054): each with its parent, boundary, phase, due time and whether it renders now, is overdue, waits, comes later, is stale or deferred. Work on what renders now or overdue; a decision that waits is held by an open fail-closed decision above it. A route a pending decision unlocks is refused until a person decides it.", inputSchema: { type: "object", properties: { venture,
|
|
30
39
|
render: { type: "array", items: { type: "string" }, description: "Only these states, such as [\"overdue\", \"now\"]" } }, required: ["venture"] } },
|
|
31
40
|
{ name: "mentura_propose_decision", description: "Propose a decision, or new wording for an open one. It stays open until a person approves it in the CLI (mentura decide); you can never mark a decision decided. Put it beside what it decides with at (a venture folder such as operations) and name (<record>[.<topic>]).", inputSchema: { type: "object", properties: { venture,
|
|
32
41
|
decision: { type: "object", description: "id, question, and optionally answer (your proposed wording), decides, layer, parent, needed_in, due, outcomes, unlocks, note, plus at and name for where it goes" },
|
|
33
42
|
agent_id: { type: "string", description: "Your role in the team plan" }, model: { type: "string" } }, required: ["venture", "decision"] } },
|
|
43
|
+
// Setup (EE-083, interface/skills/naive-setup): the questions that establish a new venture, in order, and an artifact of the progress.
|
|
44
|
+
{ name: "mentura_setup", description: "Where a new venture's setup stands: its people and hats, the source of truth for each function, the foundation and first decisions (the docket), the goal for discovery, and which steps are done. artifact is that state as one self-contained HTML page: render it as an artifact where your harness has artifacts, and again after every answer. Ask the founder the next open question, one at a time, in order.", inputSchema: { type: "object", properties: { venture }, required: ["venture"] } },
|
|
45
|
+
{ name: "mentura_setup_answer", description: "Write down the founder's answer to one setup question (a docket id from mentura_setup, or discovery-goal), exactly in their words. It is proposed with their words as its wording and stays open until the founder confirms it at the keyboard (naive setup confirm): you never decide for them.", inputSchema: { type: "object", properties: { venture,
|
|
46
|
+
id: { type: "string", description: "The question's id from mentura_setup's docket, or discovery-goal" },
|
|
47
|
+
answer: { type: "string", description: "The founder's answer, verbatim" },
|
|
48
|
+
agent_id: { type: "string", description: "Your role in the team plan" }, model: { type: "string" } }, required: ["venture", "id", "answer"] } },
|
|
49
|
+
{ name: "mentura_setup_point", description: "Point one function of the venture at the system that holds its truth (from mentura_setup's catalog), as the founder said, such as finance at rho or products at github with address acme/app. Naive keeps a pointer, never a copy; Rho is synced at once.", inputSchema: { type: "object", properties: { venture,
|
|
50
|
+
function: { type: "string", description: "A function, such as products, finance or legal" },
|
|
51
|
+
system: { type: "string", description: "A system the catalog offers for that function" },
|
|
52
|
+
address: { type: "string", description: "Where in that system, such as acme/app" },
|
|
53
|
+
agent_id: { type: "string", description: "Your role in the team plan" } }, required: ["venture", "function", "system"] } },
|
|
54
|
+
// The decision library (schema/frames.mjs): a founder's problem matched to a frame, its premises judged, its decisions proposed.
|
|
55
|
+
{ name: "mentura_frame", description: "When the founder states a problem in their own words (\"I need investors to scale but I don't want to give up control\"), match it to a frame in the decision library, judge the premises their sentence assumes against the venture's records, and propose the decisions hidden in it, open, with options. The result says which premises hold, whether the trade is false, real or a sequence, and the better question to ask. Never pick an option for the founder, and never name an instrument as the answer: options for how to do something come from a dated, cited lookup.", inputSchema: { type: "object", properties: { venture,
|
|
56
|
+
text: { type: "string", description: "The founder's words, verbatim" },
|
|
57
|
+
frame: { type: "string", description: "A frame id, when you already know it; otherwise the founder's words pick one" },
|
|
58
|
+
lookup: { type: "object", description: "Options a lookup found: { options: [{ option, source, as_of }] }; only dated, cited options within the frame's freshness are kept" },
|
|
59
|
+
agent_id: { type: "string", description: "Your role in the team plan" }, model: { type: "string" } } } },
|
|
34
60
|
{ name: "mentura_check", description: "Run the checks for one record of a venture.", inputSchema: { type: "object", properties: { venture, route }, required: ["venture", "route"] } },
|
|
35
61
|
{ name: "mentura_template", description: "The fill-in template for a record, where its kit has one.", inputSchema: { type: "object", properties: { route }, required: ["route"] } },
|
|
36
62
|
{ name: "mentura_run", description: "Run a command route for a venture: system/need-now (what to work on now), system/time-to-ready, purpose/validation/assess (what the problem's evidence is strong enough for), governance/payouts, knowledge/catalog (index every source the venture keeps records in), knowledge/search (find items in any of them). A result with needs_input lists what is missing: ask the person whether to fill it in or run as is, and only then call again with as_is.", inputSchema: { type: "object", properties: { venture, route, value: { type: ["number", "string"], description: "governance/payouts: a sale value; knowledge/search: the query" },
|
|
@@ -40,7 +66,8 @@ export const TOOLS = [
|
|
|
40
66
|
|
|
41
67
|
let client = null; // the harness, as it named itself at initialize
|
|
42
68
|
|
|
43
|
-
|
|
69
|
+
// notify sends a notification to the client; progress for a call that carries _meta.progressToken (N-5).
|
|
70
|
+
export async function handle(msg, notify = () => {}) {
|
|
44
71
|
const { id, method, params = {} } = msg;
|
|
45
72
|
const reply = (result) => ({ jsonrpc: "2.0", id, result });
|
|
46
73
|
const fail = (code, message) => ({ jsonrpc: "2.0", id, error: { code, message } });
|
|
@@ -54,15 +81,26 @@ export async function handle(msg) {
|
|
|
54
81
|
case "tools/list": return reply({ tools: TOOLS });
|
|
55
82
|
case "tools/call": {
|
|
56
83
|
const a = params.arguments ?? {};
|
|
84
|
+
const token = params._meta?.progressToken;
|
|
85
|
+
let step = 0;
|
|
86
|
+
const progress = token === undefined ? null : ({ progress: p, total, message }) => notify({ jsonrpc: "2.0", method: "notifications/progress", params: { progressToken: token, progress: Math.max(p ?? 0, ++step), ...(total ? { total } : {}), message } });
|
|
57
87
|
let r;
|
|
58
88
|
switch (params.name) {
|
|
59
89
|
case "mentura_routes": r = { ok: true, result: a.group ? routes().filter((x) => x.groups.includes(`(${a.group})`)) : routes() }; break;
|
|
60
90
|
case "mentura_ventures": r = { ok: true, result: ventures().map(({ id, name, phase, path }) => ({ id, name, phase, path })) }; break;
|
|
61
91
|
case "mentura_layout": r = await call({ venture: a.venture, route: a.route, action: "layout" }); break;
|
|
62
92
|
case "mentura_view": r = await call({ venture: a.venture, route: a.route, action: "view" }); break;
|
|
63
|
-
|
|
93
|
+
// Context hook (interface/context.mjs): a read is a navigation, header first, its parts streamed as progress.
|
|
94
|
+
// A filtered list (where) is a plain read (interface/writes.mjs); anything else is a navigation.
|
|
95
|
+
case "mentura_read": r = a.where ? await call({ venture: a.venture, route: a.route, action: "read", actor_id: a.agent_id, via: "mcp", input: { where: a.where } }) : await call({ venture: a.venture, route: a.route, action: "navigate", actor_id: a.agent_id, via: "mcp",
|
|
96
|
+
input: { session: a.session, task: a.task, prefetch: a.prefetch, header_only: a.header_only, ...(progress ? { onPart: ({ part, data }) => progress({ message: `${part}: ${JSON.stringify(data)}` }) } : {}) } }); break;
|
|
64
97
|
case "mentura_write": r = await call({ venture: a.venture, route: a.route, action: "write", input: a.record, actor: "agent", actor_id: a.agent_id, via: "mcp",
|
|
65
|
-
provenance: { derived_from: a.derived_from, decision: a.decision, note: a.note, agent: Object.fromEntries(Object.entries({ harness: client, model: a.model }).filter(([, v]) => v)) } }); break;
|
|
98
|
+
provenance: { derived_from: a.derived_from, decision: a.decision, note: a.note, confidence: a.confidence, agent: Object.fromEntries(Object.entries({ harness: client, model: a.model }).filter(([, v]) => v)) } }); break;
|
|
99
|
+
// Setup (EE-083): schema/setup.mjs holds the questions; answers are proposals the founder confirms.
|
|
100
|
+
case "mentura_setup": { const m = await import("../../schema/setup.mjs"); const dir = ventureDir(a.venture); r = { ok: true, result: { state: await m.state(dir), artifact: await m.artifact(dir) } }; break; }
|
|
101
|
+
case "mentura_setup_answer": r = await (await import("../../schema/setup.mjs")).relay(ventureDir(a.venture), { id: a.id, answer: a.answer, agentId: a.agent_id, harness: client, model: a.model }); break;
|
|
102
|
+
case "mentura_setup_point": r = await (await import("../../schema/setup.mjs")).point(ventureDir(a.venture), { fn: a.function, system: a.system, address: a.address, via: "mcp", actor: "agent", agentId: a.agent_id }); break;
|
|
103
|
+
case "mentura_frame": r = await (await import("../../schema/frames.mjs")).run(ventureDir(a.venture), { id: a.frame, text: a.text, lookup: a.lookup, agentId: a.agent_id ?? "frames", harness: client, model: a.model }); break;
|
|
66
104
|
case "mentura_decisions": {
|
|
67
105
|
r = await call({ venture: a.venture, route: "governance/decisions", action: "run", actor_id: a.agent_id });
|
|
68
106
|
if (r.ok && a.render?.length) r.result = { ...r.result, decisions: r.result.decisions.filter((d) => a.render.includes(d.render)) };
|
|
@@ -75,7 +113,9 @@ export async function handle(msg) {
|
|
|
75
113
|
}
|
|
76
114
|
case "mentura_check": r = await call({ venture: a.venture, route: a.route, action: "check" }); break;
|
|
77
115
|
case "mentura_template": r = await call({ route: a.route, action: "template" }); break;
|
|
78
|
-
case "mentura_run": r =
|
|
116
|
+
case "mentura_run": r = progress && a.route !== "system/overview" && a.route !== "system/robots"
|
|
117
|
+
? await runStreaming({ venture: a.venture, route: a.route, input: { value: a.value, as_is: a.as_is }, actor_id: a.agent_id }, progress)
|
|
118
|
+
: await call({ venture: a.venture, route: a.route, action: "run", actor_id: a.agent_id, input: a.value !== undefined || a.as_is ? { ...(a.value !== undefined ? { value: a.value } : {}), ...(a.as_is ? { as_is: true } : {}) } : undefined }); break;
|
|
79
119
|
default: return fail(-32602, `unknown tool ${params.name}`);
|
|
80
120
|
}
|
|
81
121
|
if (r.ok && r.needs_input) return reply({ content: [{ type: "text", text: `Ask the person before running. ${r.needs_input.ask}\n${JSON.stringify(r.needs_input, null, 2)}` }], isError: false });
|
|
@@ -115,7 +155,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
115
155
|
let msg;
|
|
116
156
|
try { msg = JSON.parse(line); } catch { process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } }) + "\n"); return; }
|
|
117
157
|
let res;
|
|
118
|
-
try { res = await handle(msg); } catch (e) { res = { jsonrpc: "2.0", id: msg.id ?? null, error: { code: -32603, message: String(e.message ?? e) } }; }
|
|
158
|
+
try { res = await handle(msg, (n) => process.stdout.write(JSON.stringify(n) + "\n")); } catch (e) { res = { jsonrpc: "2.0", id: msg.id ?? null, error: { code: -32603, message: String(e.message ?? e) } }; }
|
|
119
159
|
if (res) process.stdout.write(JSON.stringify(res) + "\n");
|
|
120
160
|
});
|
|
121
161
|
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Package routes, route groups and slot defaults (Next.js translation L-6, P-17, P-36, P-37, and the
|
|
2
|
+
// venture route for P-3). The router reaches these when a route is not in the route table, so the
|
|
3
|
+
// table, generated from the data model, stays as it is.
|
|
4
|
+
//
|
|
5
|
+
// <package> a package's own route (validation, foundation, marketing; packages/<name> works
|
|
6
|
+
// for every package, even one whose name is also a record route such as product).
|
|
7
|
+
// read returns its records and their state: progress, what moves now, what waits,
|
|
8
|
+
// and the drafts pending in it (its loading state, P-37). check runs its boundary
|
|
9
|
+
// (E-5). view is the same as a page. layout is its group's and its own context.
|
|
10
|
+
// (<group>) a route group (P-36): packages that share a layout, so they share context. The
|
|
11
|
+
// framework defines (go-to-market) for marketing, relationships and customers; a
|
|
12
|
+
// venture adds or changes groups in naive.config.json "groups", and may keep the
|
|
13
|
+
// group's own AGENTS.md in a (<group>)/ folder at its root. Only the packages in a
|
|
14
|
+
// group get its layout: the layout is opt-in.
|
|
15
|
+
// venture the venture's own route: read is the manifest venture.json declares (P-3), check
|
|
16
|
+
// runs every boundary (E-5).
|
|
17
|
+
// @packages the system/overview slot: each package in use, and for a package with nothing in
|
|
18
|
+
// it, its default: what to write first and where to start (P-17).
|
|
19
|
+
|
|
20
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
|
|
23
|
+
const repo = new URL("..", import.meta.url).pathname;
|
|
24
|
+
const load = (f) => JSON.parse(readFileSync(f, "utf8"));
|
|
25
|
+
|
|
26
|
+
/** The framework's route groups (P-36). A venture's naive.config.json "groups" adds to or replaces these. */
|
|
27
|
+
export const GROUPS = {
|
|
28
|
+
"go-to-market": { packages: ["marketing", "relationships", "customers"], context: "Who the venture sells to and how it reaches them. Marketing and sales share the ICP, the offer and the accounts, so they share one layout." },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const packageNames = () => existsSync(join(repo, "packages")) ? readdirSync(join(repo, "packages")).filter((n) => existsSync(join(repo, "packages", n, "package.json"))) : [];
|
|
32
|
+
export const packageDef = (n) => load(join(repo, "packages", n, "package.json"));
|
|
33
|
+
|
|
34
|
+
/** The venture's route groups: the framework's, then its own from naive.config.json. */
|
|
35
|
+
export function groupsOf(dir) {
|
|
36
|
+
const f = dir && join(dir, "naive.config.json");
|
|
37
|
+
const own = f && existsSync(f) ? load(f).groups ?? {} : {};
|
|
38
|
+
return { ...GROUPS, ...own };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The package a record type lives in: its core home first, else the data or folder package that declares it. */
|
|
42
|
+
export function packageOfRecord(type) {
|
|
43
|
+
if (!type) return null;
|
|
44
|
+
const names = packageNames();
|
|
45
|
+
const core = names.find((n) => { const p = packageDef(n); return p.kind === "core" && [...(p.required ?? []), ...(p.optional ?? [])].includes(type); });
|
|
46
|
+
if (core) return core;
|
|
47
|
+
return names.find((n) => { const p = packageDef(n); return p.kind !== "handoff" && (p.records ?? []).includes(type); }) ?? null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The group layout a package gets (P-36), or null when it is in no group. */
|
|
51
|
+
export function groupLayout(pkg, dir) {
|
|
52
|
+
const gs = groupsOf(dir);
|
|
53
|
+
const id = Object.keys(gs).find((g) => (gs[g].packages ?? []).includes(pkg));
|
|
54
|
+
if (!id) return null;
|
|
55
|
+
const g = gs[id];
|
|
56
|
+
const agents = dir && existsSync(join(dir, `(${id})`, "AGENTS.md")) ? `(${id})/AGENTS.md` : null;
|
|
57
|
+
const shares = [...new Set(g.packages.filter((n) => packageNames().includes(n)).flatMap((n) => packageDef(n).refs ?? []))];
|
|
58
|
+
return { group: `(${id})`, packages: g.packages, context: g.context ?? null, shares, agents };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Which special route a name is: a package, a group or the venture. */
|
|
62
|
+
export function special(route) {
|
|
63
|
+
if (route === "venture") return { kind: "venture" };
|
|
64
|
+
const g = route.match(/^\(([a-z0-9-]+)\)$/);
|
|
65
|
+
if (g) return { kind: "group", id: g[1] };
|
|
66
|
+
const name = route.startsWith("packages/") ? route.slice("packages/".length) : route;
|
|
67
|
+
return packageNames().includes(name) ? { kind: "package", name } : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The route of a record type: its item or single route, else its list route. */
|
|
71
|
+
export const routeOfType = async (type) => {
|
|
72
|
+
const { routes } = await import("./router.mjs");
|
|
73
|
+
const rs = routes().filter((r) => r.record === type);
|
|
74
|
+
return (rs.find((r) => r.collection !== "list") ?? rs[0])?.route ?? null;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// The drafts waiting in a package's records (P-37): its pending state.
|
|
78
|
+
async function pendingIn(dir, types, venture) {
|
|
79
|
+
const { recordOf } = await import("../schema/decisions.mjs");
|
|
80
|
+
const { entries } = await import("./provenance.mjs");
|
|
81
|
+
const d = join(dir, "_drafts");
|
|
82
|
+
if (!existsSync(d)) return [];
|
|
83
|
+
const log = entries(dir);
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const f of readdirSync(d).filter((x) => x.endsWith(".draft"))) {
|
|
86
|
+
const file = f.slice(0, -".draft".length).replace(/__/g, "/");
|
|
87
|
+
const type = recordOf(file);
|
|
88
|
+
if (!types.includes(type)) continue;
|
|
89
|
+
const route = await routeOfType(type);
|
|
90
|
+
const id = route?.endsWith("[id]") ? file.split("/").at(-1).replace(/\.json$/, "") : null;
|
|
91
|
+
const concrete = id ? route.replace("[id]", id) : route;
|
|
92
|
+
out.push({ record: type, route: concrete, draft: `_drafts/${f}`, since: log.filter((e) => e.entity === `_drafts/${f}`).at(-1)?.at ?? null, accept: `mentura accept ${concrete} --venture ${venture}` });
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A package's own route read (L-6, P-37): its records and their state, what moves now, what waits, what is pending. */
|
|
98
|
+
export async function packageState(dir, name, venture) {
|
|
99
|
+
const def = packageDef(name);
|
|
100
|
+
const { packages } = await import("../schema/packages.mjs");
|
|
101
|
+
const all = packages(dir);
|
|
102
|
+
const p = [...all.core, ...all.handoff].find((x) => x.id === name);
|
|
103
|
+
if (p?.kind === "core") {
|
|
104
|
+
const records = await Promise.all(p.records.map(async (r) => ({ record: r.record, title: r.title, role: r.role, route: await routeOfType(r.record), status: r.status, moves: r.moves, ...(r.waits_on ? { waits_on: r.waits_on } : {}), files: r.files })));
|
|
105
|
+
const pending = await pendingIn(dir, p.records.filter((r) => r.role !== "reads").map((r) => r.record), venture);
|
|
106
|
+
return { package: name, kind: "core", title: p.title, purpose: p.purpose, function: def.function ?? def.department ?? null, fit: p.fit, in: p.in, state: p.state, progress: p.progress,
|
|
107
|
+
records, moves_now: records.filter((r) => r.moves === "now" && r.role !== "reads").map((r) => r.route), waits: records.filter((r) => r.moves === "waits").map((r) => ({ route: r.route, on: r.waits_on })),
|
|
108
|
+
loading: pending.length ? { drafts: pending, note: `${pending.length} draft${pending.length > 1 ? "s" : ""} waiting for a person to accept` } : null, unlocks: p.unlocks, missing: p.missing, to_review: p.to_review };
|
|
109
|
+
}
|
|
110
|
+
if (p?.kind === "handoff") return { package: name, kind: "handoff", title: p.title, for: p.for, from: p.from, fit: p.fit, in: p.in, state: p.state, progress: p.progress, waits_on: p.waits_on, unlocks: p.unlocks, loading: null };
|
|
111
|
+
// Data, folder and raw packages: never complete; their state is what they hold.
|
|
112
|
+
const { routes, glob } = await import("./router.mjs");
|
|
113
|
+
const { files } = await import("./router.mjs");
|
|
114
|
+
const here = files(dir);
|
|
115
|
+
const records = await Promise.all((def.records ?? []).map(async (t) => {
|
|
116
|
+
const r = routes().find((x) => x.record === t);
|
|
117
|
+
const held = r ? here.filter((f) => r.files.some((g) => glob(g).test(f))) : [];
|
|
118
|
+
return { record: t, route: await routeOfType(t), items: held.length, files: held };
|
|
119
|
+
}));
|
|
120
|
+
const pending = await pendingIn(dir, def.records ?? [], venture);
|
|
121
|
+
return { package: name, kind: def.kind, title: def.title, purpose: def.purpose, state: records.some((r) => r.items) ? "holding" : "empty", records, gates: def.gates ?? [],
|
|
122
|
+
loading: pending.length ? { drafts: pending, note: `${pending.length} draft${pending.length > 1 ? "s" : ""} waiting for a person to accept` } : null };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The overview's @packages slot (P-17): each package in use, and the default a package with nothing in it shows. */
|
|
126
|
+
export async function packageSlots(dir, venture) {
|
|
127
|
+
const { manifest } = await import("../schema/structure.mjs");
|
|
128
|
+
const m = await manifest(dir);
|
|
129
|
+
const out = [];
|
|
130
|
+
for (const { name, known } of m.packages.list) {
|
|
131
|
+
if (!known) { out.push({ package: name, shows: "not a Naive package: check venture.json packages" }); continue; }
|
|
132
|
+
const s = await packageState(dir, name, venture);
|
|
133
|
+
const empty = s.kind === "core" ? s.records.filter((r) => r.role !== "reads").every((r) => r.status === "missing") : s.kind === "handoff" ? false : s.state === "empty";
|
|
134
|
+
if (!empty) { out.push({ package: name, title: s.title, state: s.state, progress: s.progress ?? null, moving: (s.moves_now ?? []).join(", ") || "none" }); continue; }
|
|
135
|
+
// The default: nothing written yet, and where to start.
|
|
136
|
+
const first = s.kind === "core" ? s.records.find((r) => r.role === "required" && r.moves === "now") ?? s.records.find((r) => r.role !== "reads") : s.records[0];
|
|
137
|
+
const route = first?.route ?? null;
|
|
138
|
+
const concrete = route?.endsWith("[id]") ? route.replace("[id]", "<id>") : route;
|
|
139
|
+
const { routes } = await import("./router.mjs");
|
|
140
|
+
const acts = routes().find((x) => x.route === route)?.actions ?? [];
|
|
141
|
+
const write = acts.includes("write") ? `mentura ${concrete} write --venture ${venture} --input <file>` : null;
|
|
142
|
+
const start = !concrete ? null : acts.includes("template") ? `mentura ${concrete} template${write ? `, then ${write}` : ""}` : write ?? `mentura ${concrete} layout --venture ${venture}`;
|
|
143
|
+
out.push({ package: name, title: s.title, default: true, shows: `${s.title}: nothing written yet`, start });
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const pageOf = (s) => {
|
|
149
|
+
const lines = [`# ${s.title ?? s.package}`, "", s.purpose ?? "", ""];
|
|
150
|
+
if (s.state) lines.push(`State: ${s.state}${s.progress != null ? ` (${Math.round(s.progress * 100)}%)` : ""}.`, "");
|
|
151
|
+
if (s.loading) lines.push(`> ${s.loading.note}: ${s.loading.drafts.map((d) => `\`${d.accept}\``).join(", ")}`, "");
|
|
152
|
+
if (s.records?.length) {
|
|
153
|
+
const cols = s.kind === "core" ? ["route", "role", "status", "moves"] : ["route", "items"];
|
|
154
|
+
lines.push(`| ${cols.join(" | ")} |`, `| ${cols.map(() => "---").join(" | ")} |`, ...s.records.map((r) => `| ${cols.map((c) => String(r[c] ?? "")).join(" | ")} |`), "");
|
|
155
|
+
}
|
|
156
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/** Answers a call to a package, group or venture route; null when the route is none of them. */
|
|
160
|
+
export async function specialCall({ venture, route, action, workspace }) {
|
|
161
|
+
const sp = special(route);
|
|
162
|
+
if (!sp) return null;
|
|
163
|
+
const { ventureDir } = await import("./router.mjs");
|
|
164
|
+
const actions = sp.kind === "group" ? ["read", "view", "layout"] : ["read", "view", "check", "layout"];
|
|
165
|
+
if (!actions.includes(action)) return { ok: false, error: `${route} has no action ${action} (it has ${actions.join(", ")})` };
|
|
166
|
+
const dir = venture ? ventureDir(venture, workspace) : null;
|
|
167
|
+
if (!dir && !(action === "layout" && sp.kind !== "venture")) return { ok: false, error: `no venture ${venture}` };
|
|
168
|
+
if (sp.kind === "venture") {
|
|
169
|
+
const { manifest } = await import("../schema/structure.mjs");
|
|
170
|
+
const { boundaries } = await import("./boundaries.mjs");
|
|
171
|
+
if (action === "check") { const b = await boundaries(dir); return { ok: b.ok, result: b }; }
|
|
172
|
+
const m = await manifest(dir);
|
|
173
|
+
if (action === "view") return { ok: true, result: `# ${m.identity.name}\n\nPhase: ${m.phase}. Packages: ${m.packages.list.map((p) => p.name).join(", ")}.\n\n| Function | Owner | Packages |\n| --- | --- | --- |\n${m.functions.map((f) => `| ${f.function} | ${f.owner ?? "nobody yet"} | ${f.packages.join(", ")} |`).join("\n")}\n` };
|
|
174
|
+
if (action === "layout") return { ok: true, result: { route: "venture", manifest: m } };
|
|
175
|
+
return { ok: true, result: m };
|
|
176
|
+
}
|
|
177
|
+
if (sp.kind === "group") {
|
|
178
|
+
const gs = groupsOf(dir);
|
|
179
|
+
const g = gs[sp.id];
|
|
180
|
+
if (!g) return { ok: false, error: `no route group (${sp.id}): the groups are ${Object.keys(gs).map((x) => `(${x})`).join(", ")}` };
|
|
181
|
+
const lay = groupLayout(g.packages[0], dir);
|
|
182
|
+
if (action === "layout" || !dir) return { ok: true, result: { ...lay, chain: lay.agents && dir ? [{ path: lay.agents, text: readFileSync(join(dir, lay.agents), "utf8") }] : [] } };
|
|
183
|
+
const states = [];
|
|
184
|
+
for (const n of g.packages.filter((x) => packageNames().includes(x))) states.push(await packageState(dir, n, venture));
|
|
185
|
+
const res = { group: `(${sp.id})`, context: g.context ?? null, packages: states.map((s) => ({ package: s.package, state: s.state, progress: s.progress ?? null, pending: s.loading?.drafts.length ?? 0 })) };
|
|
186
|
+
return { ok: true, result: action === "view" ? `# (${sp.id})\n\n${res.context ?? ""}\n\n${res.packages.map((p) => `- ${p.package}: ${p.state}${p.progress != null ? ` (${Math.round(p.progress * 100)}%)` : ""}${p.pending ? `, ${p.pending} pending` : ""}`).join("\n")}\n` : res };
|
|
187
|
+
}
|
|
188
|
+
const name = sp.name;
|
|
189
|
+
if (action === "layout") {
|
|
190
|
+
const def = packageDef(name);
|
|
191
|
+
return { ok: true, result: { route: name, package: { name, title: def.title, purpose: def.purpose, function: def.function ?? def.department ?? null, skill: def.skill?.path ?? null, gates: def.gates ?? [] }, group: groupLayout(name, dir) } };
|
|
192
|
+
}
|
|
193
|
+
if (action === "check") { const { boundaries } = await import("./boundaries.mjs"); const b = await boundaries(dir, { only: [name] }); return { ok: b.packages.every((p) => p.ok), result: b.packages[0] ?? { package: name, ok: true, note: "not in use by this venture" } }; }
|
|
194
|
+
const s = await packageState(dir, name, venture);
|
|
195
|
+
return action === "view" ? { ok: true, result: pageOf(s) } : { ok: true, result: s, ...(s.loading ? { pending_drafts: s.loading.drafts } : {}) };
|
|
196
|
+
}
|