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,214 @@
|
|
|
1
|
+
// The brand and asset verbs of the CLI (mentura brand ..., mentura assets ...). Every write goes
|
|
2
|
+
// through the router, so it is checked against its schema and logged in provenance; the logic lives
|
|
3
|
+
// in schema/brand.mjs and schema/assets.mjs.
|
|
4
|
+
//
|
|
5
|
+
// mentura assets add <public/file> --venture <id> --source <who or url> --license <id> --credit <who>
|
|
6
|
+
// [--alt <text>] [--decorative] [--as person]
|
|
7
|
+
// register a file in public/; code fills its facts (J-2, J-4)
|
|
8
|
+
// mentura assets fill --venture <id> refill every entry's facts from its file, by code (J-4)
|
|
9
|
+
// mentura assets alt <public/file> --text <alt> --venture <id> [--as person]
|
|
10
|
+
// write alt text; an agent's waits for a person to confirm it
|
|
11
|
+
// mentura assets confirm <public/file> --venture <id> --by <name> [--confirm <public/file>]
|
|
12
|
+
// a person confirms the alt text as it stands (J-4)
|
|
13
|
+
// mentura assets remote <url> --venture <id> --license <id> --credit <who> [--id <raw id>] [--alt <text>] [--note <text>]
|
|
14
|
+
// record a remote image as a link with a note in raw/ (J-3)
|
|
15
|
+
// mentura assets hosts <host,host> --venture <id> set the hosts an agent may fetch from (J-3)
|
|
16
|
+
// mentura assets check|facts --venture <id>
|
|
17
|
+
// mentura brand tokens|css|head|check|voice --venture <id> [--json]
|
|
18
|
+
// mentura brand resolve <route> --venture <id> the brand at a route, nearer context winning (Y-5, T-2)
|
|
19
|
+
// mentura brand terms <package> --venture <id> the terms a package sees (Y-2)
|
|
20
|
+
// mentura brand sitemap --venture <id> --base <url> [--out <file>] sitemap.xml from the marketing sitemap (P-26)
|
|
21
|
+
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
23
|
+
import { dirname, join, resolve as resolvePath } from "node:path";
|
|
24
|
+
import { call, ventureDir } from "./router.mjs";
|
|
25
|
+
import { agentFromEnv, record as prov } from "./provenance.mjs";
|
|
26
|
+
import { altStatement, checkAssets, facts as allFacts, factsOf, REGISTER, registerOf } from "../schema/assets.mjs";
|
|
27
|
+
import { brandOf, checkBrand, resolveBrand, sitemapOf, sitemapXml, termsFor, tokens, voice } from "../schema/brand.mjs";
|
|
28
|
+
|
|
29
|
+
const VERSION = "0.1.0";
|
|
30
|
+
const today = () => new Date().toISOString();
|
|
31
|
+
|
|
32
|
+
/** The facts code reads, in the register's shape. */
|
|
33
|
+
export const factsFor = (dir, path) => {
|
|
34
|
+
const f = factsOf(readFileSync(join(dir, path)));
|
|
35
|
+
return Object.fromEntries(Object.entries({ type: f.type, width: f.width, height: f.height, sizes: f.sizes, bytes: f.bytes, sha256: f.sha256, filled_at: today() }).filter(([, v]) => v !== undefined));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const emptyRegister = () => ({ version: VERSION, assets: [] });
|
|
39
|
+
|
|
40
|
+
/** Writes the register through the router: schema first, then provenance. */
|
|
41
|
+
export async function writeRegister(venture, workspace, reg, { actor = "agent", note } = {}) {
|
|
42
|
+
return call({ venture, route: "public/assets", action: "write", input: reg, actor, workspace, via: "cli", provenance: { note } });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Registers one file in public/ with its source, license and credit; code fills its facts. */
|
|
46
|
+
export async function addAsset({ venture, workspace, path, source, license, credit, alt, decorative, origin_url, design_system, actor = "agent" }) {
|
|
47
|
+
const dir = ventureDir(venture, workspace);
|
|
48
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
49
|
+
if (!path?.startsWith("public/")) return { ok: false, error: `${path} is not in public/` };
|
|
50
|
+
if (!existsSync(join(dir, path))) return { ok: false, error: `${path} is not in ${venture}` };
|
|
51
|
+
const reg = registerOf(dir) ?? emptyRegister();
|
|
52
|
+
const entry = Object.fromEntries(Object.entries({ path, source, license, credit, alt: alt ? { text: alt, by: actor === "person" ? "person" : "agent" } : undefined, decorative: decorative || undefined, origin_url, design_system, facts: factsFor(dir, path) }).filter(([, v]) => v !== undefined));
|
|
53
|
+
reg.assets = [...reg.assets.filter((a) => a.path !== path), entry].sort((a, b) => a.path.localeCompare(b.path));
|
|
54
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `registered ${path}; facts read by code` });
|
|
55
|
+
if (w.ok && alt && actor === "person") prov(dir, { entity: REGISTER, route: "public/assets", activity: "confirm", statement: altStatement(path, alt), agent: agentFromEnv("person", "cli"), note: `alt text of ${path} written by a person` });
|
|
56
|
+
return w.ok ? { ok: true, result: { registered: path, facts: entry.facts, alt: entry.alt ?? null } } : w;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Refills every entry's facts from its file. Only code writes facts. */
|
|
60
|
+
export async function fill({ venture, workspace }) {
|
|
61
|
+
const dir = ventureDir(venture, workspace);
|
|
62
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
63
|
+
const reg = registerOf(dir);
|
|
64
|
+
if (!reg) return { ok: false, error: `${venture} has no ${REGISTER}; register a file first: mentura assets add public/<file> ...` };
|
|
65
|
+
const changed = [], missing = [];
|
|
66
|
+
for (const a of reg.assets) {
|
|
67
|
+
if (!existsSync(join(dir, a.path))) { missing.push(a.path); continue; }
|
|
68
|
+
const f = factsFor(dir, a.path);
|
|
69
|
+
if (!a.facts || ["type", "width", "height", "bytes", "sha256"].some((k) => (a.facts[k] ?? null) !== (f[k] ?? null))) { a.facts = f; changed.push(a.path); }
|
|
70
|
+
}
|
|
71
|
+
const listed = new Set(reg.assets.map((a) => a.path));
|
|
72
|
+
const unregistered = allFacts(dir).map((x) => x.path).filter((p) => !listed.has(p));
|
|
73
|
+
if (changed.length) { const w = await writeRegister(venture, workspace, reg, { note: `facts filled by code: ${changed.join(", ")}` }); if (!w.ok) return w; }
|
|
74
|
+
return { ok: true, result: { filled: changed, missing, unregistered } };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Writes alt text. An agent's carries agent provenance until a person confirms it; a person's is confirmed as written. */
|
|
78
|
+
export async function setAlt({ venture, workspace, path, text, actor = "agent" }) {
|
|
79
|
+
const dir = ventureDir(venture, workspace);
|
|
80
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
81
|
+
const reg = registerOf(dir);
|
|
82
|
+
const a = reg?.assets.find((x) => x.path === path);
|
|
83
|
+
if (!a) return { ok: false, error: `${path} is not registered; mentura assets add ${path} first` };
|
|
84
|
+
a.alt = { text, by: actor === "person" ? "person" : "agent" };
|
|
85
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `alt text of ${path} by ${a.alt.by}` });
|
|
86
|
+
if (!w.ok) return w;
|
|
87
|
+
if (actor === "person") prov(dir, { entity: REGISTER, route: "public/assets", activity: "confirm", statement: altStatement(path, text), agent: agentFromEnv("person", "cli"), note: `alt text of ${path} written by a person` });
|
|
88
|
+
return { ok: true, result: { path, alt: a.alt, confirmed: actor === "person" } };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A person confirms an alt text exactly as it stands. The confirmation is bound to the text: change it and it is pending again. */
|
|
92
|
+
export function confirmAlt({ venture, workspace, path, by }) {
|
|
93
|
+
const dir = ventureDir(venture, workspace);
|
|
94
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
95
|
+
if (!by) return { ok: false, error: "say who is confirming: --by <name>" };
|
|
96
|
+
const a = registerOf(dir)?.assets.find((x) => x.path === path);
|
|
97
|
+
if (!a?.alt?.text) return { ok: false, error: `${path} has no alt text to confirm` };
|
|
98
|
+
const e = prov(dir, { entity: REGISTER, route: "public/assets", activity: "confirm", statement: altStatement(path, a.alt.text), agent: agentFromEnv("person", "cli", { id: by }), note: `${by} confirmed the alt text of ${path}: ${a.alt.text}` });
|
|
99
|
+
return { ok: true, result: { path, alt: a.alt.text, confirmed_by: by, provenance: e.hash } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const rawId = (url) => {
|
|
103
|
+
const u = new URL(url);
|
|
104
|
+
const slug = `${u.host}-${u.pathname.split("/").filter(Boolean).pop() ?? "image"}`.toLowerCase().replace(/\.[a-z0-9]+$/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
105
|
+
return slug.slice(0, 60) || "remote-image";
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A remote image as a link with a note in raw/ (J-3): raw/sources/<id>/source.json says where it is,
|
|
110
|
+
* text.md is the note, and the register lists it under remote. Nothing is fetched here. The platform
|
|
111
|
+
* fetcher passes `fetched` (facts of the private original) and `copy` when it kept a public copy.
|
|
112
|
+
*/
|
|
113
|
+
export async function addRemote({ venture, workspace, url, license, credit, id, alt, note, fetched, copy, actor = "agent" }) {
|
|
114
|
+
const dir = ventureDir(venture, workspace);
|
|
115
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
116
|
+
try { new URL(url); } catch { return { ok: false, error: `${url} is not a URL` }; }
|
|
117
|
+
const rid = id ?? rawId(url);
|
|
118
|
+
const reg = registerOf(dir) ?? emptyRegister();
|
|
119
|
+
if (!existsSync(join(dir, "raw/sources", rid, "source.json"))) {
|
|
120
|
+
const w = await call({ venture, route: `raw/sources/${rid}`, action: "write", input: { id: rid, origin: url, title: `Remote image at ${new URL(url).host}`, fetched: today().slice(0, 10) }, actor, workspace, via: "cli", provenance: { note: "a remote image, kept as a link (J-3)" } });
|
|
121
|
+
if (!w.ok) return w;
|
|
122
|
+
const text = [`# Remote image`, "", `- Link: ${url}`, `- Host: ${new URL(url).host}`, `- License: ${license}`, `- Credit: ${credit ?? "(missing)"}`, ...(fetched ? [`- Fetched: ${fetched.type}${fetched.width ? `, ${fetched.width}x${fetched.height}` : ""}, ${fetched.bytes} bytes, sha256 ${fetched.sha256}`] : []), "",
|
|
123
|
+
copy ? `A copy is kept at ${copy}: the license allows it.` : "The venture links to this image. A full copy stays private (raw/sources/<id>/original.*, out of git) unless the license allows one.", ...(note ? ["", note] : []), ""].join("\n");
|
|
124
|
+
writeFileSync(join(dir, "raw/sources", rid, "text.md"), text);
|
|
125
|
+
prov(dir, { entity: `raw/sources/${rid}/text.md`, route: `raw/sources/${rid}`, activity: "write", agent: agentFromEnv(actor === "person" ? "person" : "agent", "cli"), note: "the note beside a remote image (J-3)" });
|
|
126
|
+
}
|
|
127
|
+
const entry = Object.fromEntries(Object.entries({ id: rid, url, license, credit, alt: alt ? { text: alt, by: actor === "person" ? "person" : "agent" } : undefined, fetched, copy }).filter(([, v]) => v !== undefined));
|
|
128
|
+
reg.remote = [...(reg.remote ?? []).filter((r) => r.url !== url), entry];
|
|
129
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `remote image ${url}` });
|
|
130
|
+
return w.ok ? { ok: true, result: { remote: url, raw: `raw/sources/${rid}/`, copy: copy ?? null } } : w;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The hosts an agent may fetch from, a setting the platform fetcher enforces (J-3). */
|
|
134
|
+
export async function setHosts({ venture, workspace, hosts, actor = "agent" }) {
|
|
135
|
+
const dir = ventureDir(venture, workspace);
|
|
136
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
137
|
+
const reg = registerOf(dir) ?? emptyRegister();
|
|
138
|
+
reg.remote_hosts = hosts;
|
|
139
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `hosts an agent may fetch from: ${hosts.join(", ") || "none"}` });
|
|
140
|
+
return w.ok ? { ok: true, result: { remote_hosts: hosts } } : w;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** mentura brand ... and mentura assets ...: returns the exit code. */
|
|
144
|
+
export async function main(argv, { workspace } = {}) {
|
|
145
|
+
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
146
|
+
const flag = (n) => argv.includes(`--${n}`);
|
|
147
|
+
const pos = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "decorative"].includes(argv[i - 1].slice(2))));
|
|
148
|
+
const [group, verb, arg] = pos;
|
|
149
|
+
const venture = opt("venture");
|
|
150
|
+
const actor = opt("as") === "person" ? "person" : "agent";
|
|
151
|
+
const show = (r, text) => {
|
|
152
|
+
if (!r.ok) { console.error(r.error ?? JSON.stringify(r, null, 2)); return 1; }
|
|
153
|
+
console.log(flag("json") || text === undefined ? JSON.stringify(r.result, null, 2) : text);
|
|
154
|
+
return 0;
|
|
155
|
+
};
|
|
156
|
+
if (!venture) { console.error(`mentura ${group} ${verb ?? ""}: --venture <id> is required`); return 2; }
|
|
157
|
+
const dir = ventureDir(venture, workspace);
|
|
158
|
+
if (!dir) { console.error(`no venture ${venture}`); return 1; }
|
|
159
|
+
|
|
160
|
+
if (group === "assets") {
|
|
161
|
+
if (verb === "add") return show(await addAsset({ venture, workspace, path: arg, source: opt("source"), license: opt("license"), credit: opt("credit"), alt: opt("alt"), decorative: flag("decorative"), origin_url: opt("origin-url"), design_system: opt("design-system"), actor }), `registered: ${arg}`);
|
|
162
|
+
if (verb === "fill") { const r = await fill({ venture, workspace }); return show(r, r.ok ? `filled: ${r.result.filled.length ? r.result.filled.join(", ") : "nothing changed"}${r.result.unregistered.length ? `\nnot registered: ${r.result.unregistered.join(", ")}` : ""}` : undefined); }
|
|
163
|
+
if (verb === "alt") return show(await setAlt({ venture, workspace, path: arg, text: opt("text"), actor }), `alt text of ${arg} written by ${actor}${actor === "agent" ? "; a person confirms it with mentura assets confirm" : ""}`);
|
|
164
|
+
if (verb === "confirm") {
|
|
165
|
+
// The person types the path to confirm, as with foundation statements (EE-051).
|
|
166
|
+
if (opt("confirm") !== arg) { console.error(`not confirmed: pass --confirm ${arg} to confirm the alt text of ${arg} as it stands`); return 1; }
|
|
167
|
+
return show(confirmAlt({ venture, workspace, path: arg, by: opt("by") ?? process.env.MENTURA_PERSON }), `confirmed: alt text of ${arg}`);
|
|
168
|
+
}
|
|
169
|
+
if (verb === "remote") return show(await addRemote({ venture, workspace, url: arg, license: opt("license"), credit: opt("credit"), id: opt("id"), alt: opt("alt"), note: opt("note"), actor }), `linked: ${arg}`);
|
|
170
|
+
if (verb === "hosts") return show(await setHosts({ venture, workspace, hosts: (arg ?? "").split(",").map((h) => h.trim()).filter(Boolean), actor }), `remote_hosts: ${arg ?? "none"}`);
|
|
171
|
+
if (verb === "facts") return show({ ok: true, result: allFacts(dir) }, allFacts(dir).map((x) => `${x.path} ${x.type}${x.width ? ` ${x.width}x${x.height}` : ""} ${x.bytes} bytes`).join("\n") || "public/ is empty");
|
|
172
|
+
if (verb === "check") {
|
|
173
|
+
const r = checkAssets(dir);
|
|
174
|
+
if (flag("json")) console.log(JSON.stringify(r, null, 2));
|
|
175
|
+
else { for (const e of r.errors) console.log(`error ${e}`); for (const p of r.pending) console.log(`pending ${p.path}: alt text by ${p.by}, not confirmed by a person`); console.log(r.ok ? `assets: ${r.assets} files, ${r.remote} remote links, ${r.pending.length} alt texts pending` : `assets: ${r.errors.length} errors`); }
|
|
176
|
+
return r.ok ? 0 : 1;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (group === "brand") {
|
|
181
|
+
const brand = brandOf(dir);
|
|
182
|
+
const need = () => { console.error(`${venture} has no brand yet: write marketing/brand.json (mentura marketing/brand template)`); return 1; };
|
|
183
|
+
if (verb === "tokens" || verb === "css" || verb === "head") {
|
|
184
|
+
if (!brand) return need();
|
|
185
|
+
const t = tokens(dir, brand);
|
|
186
|
+
return show({ ok: true, result: verb === "tokens" ? t.tokens : verb === "head" ? t.head : t.css }, verb === "css" ? t.css.trimEnd() : undefined);
|
|
187
|
+
}
|
|
188
|
+
if (verb === "resolve") { if (!brand) return need(); return show({ ok: true, result: resolveBrand(brand, arg ?? "") }); }
|
|
189
|
+
if (verb === "terms") { const t = termsFor(dir, arg); return show({ ok: true, result: t }, t.terms.map((x) => `${x.term} (${x.from}): ${x.meaning}`).join("\n") || `no terms reach ${arg}`); }
|
|
190
|
+
if (verb === "voice") {
|
|
191
|
+
const v = opt("text") !== undefined ? { violations: (await import("../schema/brand.mjs")).voiceCheck(opt("text"), brand ? resolveBrand(brand, opt("for") ?? "").voice.rules : []) } : voice(dir, brand);
|
|
192
|
+
const lines = v.violations.map((x) => `${x.file ? `${x.file}:` : "line "}${x.line} "${x.found}" breaks ${x.rule}${x.use ? `; use "${x.use}"` : ""}`);
|
|
193
|
+
if (flag("json")) console.log(JSON.stringify(v, null, 2)); else console.log(lines.join("\n") || "keeps to the voice rules");
|
|
194
|
+
return v.violations.length ? 1 : 0;
|
|
195
|
+
}
|
|
196
|
+
if (verb === "check") {
|
|
197
|
+
const r = checkBrand(dir);
|
|
198
|
+
if (flag("json")) console.log(JSON.stringify(r, null, 2));
|
|
199
|
+
else { for (const e of r.errors) console.log(`error ${e}`); for (const w of r.warnings) console.log(`warn ${w}`); console.log(r.ok ? "brand: ok" : `brand: ${r.errors.length} errors`); }
|
|
200
|
+
return r.ok ? 0 : 1;
|
|
201
|
+
}
|
|
202
|
+
if (verb === "sitemap") {
|
|
203
|
+
const s = sitemapOf(dir);
|
|
204
|
+
if (!s) { console.error(`${venture} has no marketing sitemap yet: mentura operations/marketing/sitemap run, then write it to marketing/sitemap`); return 1; }
|
|
205
|
+
let xml;
|
|
206
|
+
try { xml = sitemapXml(s.record, opt("base") ?? ""); } catch (e) { console.error(`${e.message}; pass --base https://<your site>`); return 1; }
|
|
207
|
+
if (opt("out")) { const f = resolvePath(opt("out")); mkdirSync(dirname(f), { recursive: true }); writeFileSync(f, xml); console.log(`wrote ${opt("out")}: ${s.record.pages.length} pages from ${s.file}`); return 0; }
|
|
208
|
+
process.stdout.write(xml);
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// mentura")).map((l) => l.slice(5)).join("\n"));
|
|
213
|
+
return 2;
|
|
214
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// The freshness commands of the mentura CLI (Revalidating, V-1 to V-8). mentura.mjs hands these
|
|
2
|
+
// commands here; the logic is schema/freshness.mjs.
|
|
3
|
+
//
|
|
4
|
+
// mentura revalidate <target> --venture <id> [--reason <text>] [--as person] [--json]
|
|
5
|
+
// target: tag:<route or file> (the record and what depends on it), path:<folder>,
|
|
6
|
+
// package:<name>, or a plain route or file (a tag); a plain folder is refused with the tags to use
|
|
7
|
+
// mentura revalidate --pivot --venture <id> [--reason <text>] every assumption in the validation package
|
|
8
|
+
// mentura verify <route|folder> --venture <id> [--reason <text>] [--as person]
|
|
9
|
+
// checked and still right, unchanged: its age starts again
|
|
10
|
+
// mentura phase <phase> --venture <id> --as person [--reason <text>]
|
|
11
|
+
// the venture changes phase; records with a per-phase profile go stale
|
|
12
|
+
// mentura freshness --venture <id> [--json] [--now <iso>] [--freeze <out dir>]
|
|
13
|
+
// what is fresh, stale, expired or a live hole; --freeze writes the static shell
|
|
14
|
+
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { agentFromEnv } from "../provenance.mjs";
|
|
17
|
+
import { ventureDir } from "../router.mjs";
|
|
18
|
+
import { freeze, freshness, phase, revalidate, verify } from "../../schema/freshness.mjs";
|
|
19
|
+
|
|
20
|
+
export const COMMANDS = ["revalidate", "verify", "phase", "freshness"];
|
|
21
|
+
|
|
22
|
+
/** Runs one freshness command. Returns the exit code. */
|
|
23
|
+
export async function main(cmd, argv, workspace) {
|
|
24
|
+
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
25
|
+
const flag = (n) => argv.includes(`--${n}`);
|
|
26
|
+
const target = argv[1] && !argv[1].startsWith("--") ? argv[1] : undefined;
|
|
27
|
+
const venture = opt("venture");
|
|
28
|
+
const dir = venture && ventureDir(venture, workspace);
|
|
29
|
+
const print = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
|
|
30
|
+
if (!dir) { console.error(venture ? `no venture ${venture}` : `usage: mentura ${cmd} ${cmd === "freshness" ? "" : "<target> "}--venture <id>`); return venture ? 1 : 2; }
|
|
31
|
+
const by = agentFromEnv(opt("as") === "person" ? "person" : "agent", "cli");
|
|
32
|
+
const now = opt("now");
|
|
33
|
+
let r;
|
|
34
|
+
if (cmd === "revalidate") {
|
|
35
|
+
if (!target && !flag("pivot")) { console.error("usage: mentura revalidate <tag:route|path:folder|package:name|route> --venture <id>, or --pivot"); return 2; }
|
|
36
|
+
r = await revalidate(dir, flag("pivot") ? "pivot" : target, { reason: opt("reason"), by, now });
|
|
37
|
+
} else if (cmd === "verify") {
|
|
38
|
+
if (!target) { console.error("usage: mentura verify <route|folder> --venture <id>"); return 2; }
|
|
39
|
+
r = verify(dir, target, { reason: opt("reason"), by, now });
|
|
40
|
+
} else if (cmd === "phase") {
|
|
41
|
+
if (!target) { console.error("usage: mentura phase <phase> --venture <id> --as person"); return 2; }
|
|
42
|
+
// A phase change is the founder's decision: an agent may suggest it, not make it.
|
|
43
|
+
if (opt("as") !== "person") { console.error(`a phase change is a person's: run mentura phase ${target} --venture ${venture} --as person`); return 1; }
|
|
44
|
+
r = phase(dir, target, { reason: opt("reason"), by, now });
|
|
45
|
+
} else if (cmd === "freshness") {
|
|
46
|
+
if (opt("freeze")) r = await freeze(dir, resolve(opt("freeze")), { now });
|
|
47
|
+
else { const f = await freshness(dir, { now }); print(flag("json") ? f : [`${f.venture} (${f.phase}): ${f.summary.fresh} fresh, ${f.summary.stale} stale, ${f.summary.expired} expired, ${f.summary.holes} live ${f.summary.holes === 1 ? "hole" : "holes"}, of ${f.summary.records} records.`,
|
|
48
|
+
...f.items.filter((i) => i.state !== "fresh").map((i) => ` ${i.state.padEnd(7)} ${i.route}: ${i.reasons.map((x) => x.note).join("; ")}`),
|
|
49
|
+
...f.copies.filter((c) => c.state !== "fresh").map((c) => ` ${c.state.padEnd(7)} ${c.path} (copy of ${c.parent}): ${c.note}`),
|
|
50
|
+
...f.warnings.map((w) => `warning: ${w}`)].join("\n")); return 0; }
|
|
51
|
+
}
|
|
52
|
+
if (!r.ok) {
|
|
53
|
+
console.error(r.error);
|
|
54
|
+
if (r.suggest) console.error(` ${r.suggest.join("\n ")}\n or, for the whole folder: mentura revalidate ${r.or} --venture ${venture}`);
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
print(flag("json") ? r.result : r.result.note ?? r.result);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// The framework's global error for the command line (E-8, interface/boundaries.mjs). Imported first, so a
|
|
2
|
+
// broken data model or manifest answers with recovery steps before any module that reads it loads.
|
|
3
|
+
import { frameworkHealth, globalError } from "../boundaries.mjs";
|
|
4
|
+
|
|
5
|
+
const fw = frameworkHealth();
|
|
6
|
+
if (fw) { console.error(globalError(fw).error); process.exit(1); }
|
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
// mentura migrate --from <project> --venture <id|dir> [--apply]
|
|
13
13
|
// alias of naive migrate: an agent rewrites a project as Naive records (EE-076)
|
|
14
14
|
// mentura typegen alias of naive typegen (EE-073)
|
|
15
|
+
// mentura steps <payload|run|check|waterfall|provenance|confirm> --venture <id> [--answers <file>] [--speculate] [--stream]
|
|
16
|
+
// model steps (use model, EE-073): schema/model-steps.mjs
|
|
15
17
|
// mentura accept <route> --venture <id> [--confirm <route>]
|
|
16
18
|
// a person accepts an agent's draft of a gated record;
|
|
17
|
-
// one holding a foundation statement asks you to type the route
|
|
19
|
+
// one holding a foundation statement asks you to type the route;
|
|
20
|
+
// [--pick <letter>] [--by <name>] accepts one of drafts kept side by side
|
|
18
21
|
// mentura decide <decision> --venture <id> --by <name> [--answer <text>] [--confirm <decision>]
|
|
19
22
|
// a person makes a decision: approves its wording or gives their own (EE-058)
|
|
20
23
|
// mentura propose --venture <id> --input <file.json> [--as <role>]
|
|
@@ -29,16 +32,30 @@
|
|
|
29
32
|
// a collection item is <route>/<id> (purpose/product/kettle); a nested venture is its
|
|
30
33
|
// folder path (--venture studio/print); system/overview view shows the venture at a glance
|
|
31
34
|
// [--derived-from a,b] [--decision <id>] [--note <text>] provenance for a write (EE-031)
|
|
35
|
+
// [--confidence <n>] a score computed before the write, read by the gate's confidence rules
|
|
32
36
|
// knowledge/catalog run builds the catalog over the venture's sources; knowledge/search run
|
|
33
37
|
// --value <query> finds items in any of them (EE-037)
|
|
34
38
|
// purpose/validation/assess run lists what is missing and asks to fill it in or run as is;
|
|
35
39
|
// --as-is runs it anyway (EE-050)
|
|
40
|
+
// [--where status=active|trial,phase=build] filters a read; --hierarchy shows how it resolved
|
|
41
|
+
// a package's own route (validation, packages/product) reads its state; (go-to-market) is a
|
|
42
|
+
// route group; venture read is the manifest, venture check runs every boundary
|
|
36
43
|
// mentura baseline --venture <id> adopt existing records into the provenance log
|
|
44
|
+
// mentura revalidate <tag:route|path:folder|package:name|route> --venture <id> [--pivot] [--reason <text>]
|
|
45
|
+
// mark records stale on demand; a plain folder is refused for exact tags (Revalidating)
|
|
46
|
+
// mentura verify <route|folder> --venture <id> checked and still right, unchanged: its age starts again
|
|
47
|
+
// mentura phase <phase> --venture <id> --as person change phase; per-phase records go stale
|
|
48
|
+
// mentura freshness --venture <id> [--freeze <out dir>] what is fresh, stale, expired or a live hole
|
|
49
|
+
// mentura <route> navigate|head --venture <id> [--session <s>] [--task <text>] [--prefetch a,b | --no-prefetch] [--stream]
|
|
50
|
+
// read a route header first, with layout once per session, template and prefetch
|
|
51
|
+
// mentura batch --venture <id> --input <calls.json> [--stream]
|
|
52
|
+
// run a list of {route, action, input} calls, reporting each as it finishes
|
|
37
53
|
//
|
|
38
54
|
// The workspace is MENTURA_WORKSPACE, else --workspace <dir>, else this repository's instance/.
|
|
39
55
|
// Writes default to an agent actor; --as person is for a person at the keyboard, and a gated
|
|
40
56
|
// record written by an agent lands in <venture>/_drafts/ for a person to accept.
|
|
41
57
|
|
|
58
|
+
import "./guard.mjs"; // wf/writes hook: a broken framework file answers with recovery steps (E-8)
|
|
42
59
|
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
43
60
|
import { join, relative, resolve } from "node:path";
|
|
44
61
|
import { spawnSync } from "node:child_process";
|
|
@@ -46,6 +63,8 @@ import { call, draftOf, files, glob, groups, repo, routes, routesMd, ventureDir,
|
|
|
46
63
|
import { agentFromEnv, entries, record } from "../provenance.mjs";
|
|
47
64
|
import { FRAME, filesOf } from "../../schema/foundation.mjs";
|
|
48
65
|
import { approve, decisions, propose } from "../../schema/decisions.mjs";
|
|
66
|
+
// Gate group hook (X-2): held drafts kept side by side.
|
|
67
|
+
import { pick as pickDraft, settle as settleDraft } from "../../schema/gate-drafts.mjs";
|
|
49
68
|
import { asRouted, findVenture } from "./find-venture.mjs";
|
|
50
69
|
|
|
51
70
|
// The danger zone (EE-051): a foundation statement is accepted or confirmed only when the person types
|
|
@@ -68,7 +87,7 @@ const here = !argv.includes("--venture") && !argv.includes("--workspace") ? find
|
|
|
68
87
|
if (here) { argv.push("--venture", asRouted(here).venture, "--workspace", asRouted(here).workspace); process.env.MENTURA_WORKSPACE = asRouted(here).workspace; }
|
|
69
88
|
const opt = (name) => { const i = argv.indexOf(`--${name}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
70
89
|
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))));
|
|
90
|
+
const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "hierarchy"].includes(argv[i - 1].slice(2))));
|
|
72
91
|
const workspace = opt("workspace") ? resolve(opt("workspace")) : undefined;
|
|
73
92
|
const out = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
|
|
74
93
|
|
|
@@ -95,6 +114,9 @@ if (cmd === "ventures") {
|
|
|
95
114
|
process.exit(0);
|
|
96
115
|
}
|
|
97
116
|
|
|
117
|
+
// Brand hook: mentura brand ... and mentura assets ... (Y-2..Y-4, J-2..J-4, T-1, T-2, P-24, P-26) live in interface/brand.mjs.
|
|
118
|
+
if (cmd === "brand" || cmd === "assets") process.exit(await (await import("../brand.mjs")).main(argv, { workspace }));
|
|
119
|
+
|
|
98
120
|
if (cmd === "add") {
|
|
99
121
|
// Imports one function into a venture (EE-047, EE-048, EE-071). department is the alias for function for one release.
|
|
100
122
|
const v = opt("venture");
|
|
@@ -108,6 +130,27 @@ if (cmd === "typegen") {
|
|
|
108
130
|
process.exit(p.status ?? 1);
|
|
109
131
|
}
|
|
110
132
|
|
|
133
|
+
// Upgrades hook (M-12, E-7, V-9, Z-4): these commands live in their own modules; this only dispatches.
|
|
134
|
+
// mentura triggers --venture <id> [--fire] [--json]
|
|
135
|
+
// the venture's triggers, their runs and open failures; --fire runs what new events start
|
|
136
|
+
// mentura stale --venture <id> [--json] summaries whose system of record changed since they were written
|
|
137
|
+
// mentura notify --venture <id> --source <id> --path <p> [--version <v>]
|
|
138
|
+
// a system of record says <p> changed: mark what reads it stale (a webhook does this on Mentura)
|
|
139
|
+
// mentura adapt <adapter> --venture <id> write a harness's entry files into the venture (claude-code, codex, cursor)
|
|
140
|
+
// mentura adapters [verify [<adapter>]] [--json] the adapters, or run the framework's tests an adapter must pass
|
|
141
|
+
if (["triggers", "stale", "notify", "adapt", "adapters"].includes(cmd)) {
|
|
142
|
+
const d = ["triggers", "stale", "notify", "adapt"].includes(cmd) ? (opt("venture") ? ventureDir(opt("venture"), workspace) : null) : null;
|
|
143
|
+
if (cmd !== "adapters" && !d) { console.error(opt("venture") ? `no venture ${opt("venture")}` : `mentura ${cmd} needs --venture <id>`); process.exit(2); }
|
|
144
|
+
const pass = (...names) => names.flatMap((n) => argv.flatMap((a, i) => (a === `--${n}` ? [a, ...(argv[i + 1] && !argv[i + 1].startsWith("--") ? [argv[i + 1]] : [])] : [])));
|
|
145
|
+
const args = cmd === "triggers" ? ["schema/triggers.mjs", d, ...pass("fire", "json")]
|
|
146
|
+
: cmd === "stale" ? ["schema/stale.mjs", d, ...pass("json")]
|
|
147
|
+
: cmd === "notify" ? ["schema/stale.mjs", d, "--notify", ...pass("source", "path", "version", "json")]
|
|
148
|
+
: cmd === "adapt" ? ["interface/adapters.mjs", "adapt", action ?? "", d, ...pass("json")]
|
|
149
|
+
: ["interface/adapters.mjs", ...positional.slice(1), ...pass("json")];
|
|
150
|
+
const p = spawnSync("node", [join(repo, args[0]), ...args.slice(1)], { stdio: "inherit" });
|
|
151
|
+
process.exit(p.status ?? 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
111
154
|
if (cmd === "migrate" && (opt("from") || flag("apply"))) {
|
|
112
155
|
// migrate means rewriting an outside project into a new venture (EE-076): the same as naive migrate.
|
|
113
156
|
const v = opt("venture");
|
|
@@ -127,6 +170,16 @@ if (cmd === "upgrade" || cmd === "migrate") {
|
|
|
127
170
|
process.exit(p.status ?? 1);
|
|
128
171
|
}
|
|
129
172
|
|
|
173
|
+
// Model steps hook (use model, EE-073): mentura steps <payload|run|check|waterfall|provenance|confirm> --venture <id> [...].
|
|
174
|
+
// The logic is schema/model-steps.mjs; this passes the venture folder and the other arguments through.
|
|
175
|
+
if (cmd === "steps") {
|
|
176
|
+
const v = opt("venture"), d = v && ventureDir(v, workspace);
|
|
177
|
+
if (!d || !action) usage();
|
|
178
|
+
const pass = argv.slice(argv.indexOf("steps") + 1).filter((a, i, all) => !["--venture", "--workspace"].includes(a) && !["--venture", "--workspace"].includes(all[i - 1]));
|
|
179
|
+
const p = spawnSync("node", [join(repo, "schema/model-steps.mjs"), d, ...pass], { stdio: "inherit" });
|
|
180
|
+
process.exit(p.status ?? 1);
|
|
181
|
+
}
|
|
182
|
+
|
|
130
183
|
if (cmd === "init") {
|
|
131
184
|
// The venture scaffold lives in schema/init-venture.mjs; this passes the options through.
|
|
132
185
|
const id = opt("id");
|
|
@@ -148,22 +201,27 @@ if (cmd === "accept") {
|
|
|
148
201
|
if (!d) { console.error(`${rt} is not a record route, or no venture ${venture}`); process.exit(1); }
|
|
149
202
|
const draft = join(d.dir, d.draft);
|
|
150
203
|
if (!existsSync(draft)) { console.error(`no draft of ${d.route} in ${venture}`); process.exit(1); }
|
|
204
|
+
// Gate group hook (X-2): drafts kept side by side are accepted one at a time with --pick <letter>.
|
|
205
|
+
let chosen;
|
|
206
|
+
try { chosen = pickDraft(d.dir, d.draft, opt("pick")); } catch (e) { console.error(e.message); process.exit(1); }
|
|
207
|
+
const chosenFile = join(d.dir, chosen);
|
|
151
208
|
const recType = routes().find((x) => x.route === d.route || x.route === d.route.replace(/[^/]+$/, "[id]"))?.record;
|
|
152
209
|
const touched = foundationOf(recType);
|
|
153
210
|
if (touched.length) {
|
|
154
211
|
const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
|
|
155
212
|
const now = existsSync(join(d.dir, d.file)) ? JSON.parse(readFileSync(join(d.dir, d.file), "utf8")) : {};
|
|
156
|
-
const next = JSON.parse(readFileSync(
|
|
213
|
+
const next = JSON.parse(readFileSync(chosenFile, "utf8"));
|
|
157
214
|
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
215
|
const names = new Set([d.route, rt]);
|
|
159
216
|
const confirmed = opt("confirm") !== undefined ? names.has(opt("confirm")) : await typed(d.route, `Accepting ${d.route} changes these foundation statements:\n${diff}`);
|
|
160
217
|
if (!confirmed) { console.error("not accepted: the confirmation did not match"); process.exit(1); }
|
|
161
218
|
}
|
|
162
|
-
const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(
|
|
163
|
-
provenance: { activity: "accept", derived_from: [
|
|
219
|
+
const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(chosenFile, "utf8")), actor: "person", workspace, via: "cli",
|
|
220
|
+
provenance: { activity: "accept", derived_from: [chosen], note: opt("note") } });
|
|
164
221
|
if (!w.ok) { console.error(w.error); process.exit(1); }
|
|
165
|
-
|
|
166
|
-
|
|
222
|
+
// Gate group hook (X-2): removes the draft, its variants and sidecar, and decides the variants decision.
|
|
223
|
+
const settled = settleDraft(d.dir, d.draft, { picked: opt("pick") ?? "a", by: opt("by") });
|
|
224
|
+
out(`accepted: ${w.result.written}${settled.decision ? ` (draft ${settled.picked}; decision ${settled.decision} ${settled.decided ? "decided" : `not decided: ${settled.error}`})` : ""}`);
|
|
167
225
|
process.exit(0);
|
|
168
226
|
}
|
|
169
227
|
|
|
@@ -243,6 +301,13 @@ if (cmd === "baseline") {
|
|
|
243
301
|
process.exit(0);
|
|
244
302
|
}
|
|
245
303
|
|
|
304
|
+
// Freshness (Revalidating, V-1 to V-8): revalidate, verify, phase and freshness live in ./freshness.mjs.
|
|
305
|
+
if (["revalidate", "verify", "phase", "freshness"].includes(cmd)) process.exit(await (await import("./freshness.mjs")).main(cmd, argv, workspace));
|
|
306
|
+
|
|
307
|
+
// Context hook (interface/context.mjs): navigate and head read a route header first, with its layout once per
|
|
308
|
+
// session (--session), its template and prefetched refs (--task, --prefetch, --no-prefetch, --stream); batch runs
|
|
309
|
+
// a list of calls with progress.
|
|
310
|
+
if (["navigate", "head"].includes(action) || cmd === "batch") { const { cli } = await import("../context.mjs"); process.exit(await cli(argv, { workspace })); }
|
|
246
311
|
if (!action) usage();
|
|
247
312
|
const venture = opt("venture");
|
|
248
313
|
if (!venture && !["template", "layout"].includes(action)) usage();
|
|
@@ -251,7 +316,12 @@ const src = opt("input");
|
|
|
251
316
|
if (src) input = JSON.parse(src === "-" ? readFileSync(0, "utf8") : readFileSync(src, "utf8"));
|
|
252
317
|
if (opt("value")) input = { ...(input ?? {}), value: Number.isNaN(Number(opt("value"))) ? opt("value") : Number(opt("value")) };
|
|
253
318
|
if (flag("as-is")) input = { ...(input ?? {}), as_is: true };
|
|
319
|
+
// wf/writes hook: --where k=v[,k=v] filters a read (L-10); --hierarchy returns its resolution levels (P-28).
|
|
320
|
+
if (["read", "view"].includes(action) && opt("where")) input = { ...(input ?? {}), where: Object.fromEntries(opt("where").split(",").map((kv) => kv.split("=")).map(([k, ...v]) => [k, v.join("=").includes("|") ? v.join("=").split("|") : v.join("=")])) };
|
|
321
|
+
if (["read", "view"].includes(action) && flag("hierarchy")) input = { ...(input ?? {}), hierarchy: true };
|
|
254
322
|
const provenance = { derived_from: opt("derived-from")?.split(",").filter(Boolean), decision: opt("decision"), note: opt("note") };
|
|
323
|
+
// Gate group hook (X-4): a confidence score computed before the write, for the gate's confidence rules.
|
|
324
|
+
if (opt("confidence") !== undefined) provenance.confidence = Number(opt("confidence"));
|
|
255
325
|
let r = await call({ venture, route: cmd, action, input, actor: opt("as") === "person" ? "person" : "agent", workspace, via: "cli", provenance });
|
|
256
326
|
// Asks before running (EE-050): at a terminal, the person chooses; otherwise it prints both choices.
|
|
257
327
|
if (r.ok && r.needs_input && !flag("json")) {
|
|
@@ -267,5 +337,5 @@ if (r.ok && r.needs_input && !flag("json")) {
|
|
|
267
337
|
}
|
|
268
338
|
if (!r.ok) { console.error(r.error ?? JSON.stringify(r.result, null, 2)); process.exit(1); }
|
|
269
339
|
if (r.deprecated) console.error(`note: ${r.deprecated}`);
|
|
270
|
-
const extra = ["not_found", "pending_draft", "fix", "needs_input"].some((k) => r[k]);
|
|
340
|
+
const extra = ["not_found", "pending_draft", "fix", "needs_input", "errors", "filtered_out", "hierarchy", "stale"].some((k) => r[k]); // stale: V-9 (schema/stale.mjs)
|
|
271
341
|
out(flag("json") || extra ? r : r.result);
|
package/interface/cli/naive.mjs
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
// venture.json in the current directory or its parents. mentura stays for platform commands and as an
|
|
4
4
|
// alias, so `mentura typegen` and `mentura upgrade` run these same commands.
|
|
5
5
|
//
|
|
6
|
-
// naive dev [--port 4331] the board, live: edit a record and it updates; each saved
|
|
6
|
+
// naive dev [--port 4331] the setup page at /setup and the board, live: edit a record and it updates; each saved
|
|
7
7
|
// record is checked against its schema as you go (I-17, I-24)
|
|
8
8
|
// naive build check every package and write a release (I-17)
|
|
9
9
|
// naive start serve the latest release read-only (I-17)
|
|
10
10
|
// naive check run every check that applies to the venture (I-9)
|
|
11
11
|
// naive info versions, the venture, and which agents are connected (I-6)
|
|
12
12
|
// naive mcp serve the venture to an agent over MCP (stdio)
|
|
13
|
+
// naive setup [state|artifact|answer|point|confirm] set the venture up: the state, an artifact of it, and
|
|
14
|
+
// confirming the answers the model relayed (EE-083)
|
|
15
|
+
// naive frame "<the founder's words>" match the words to a frame in the decision library, judge its
|
|
16
|
+
// premises against the venture and propose its decisions, open
|
|
13
17
|
// naive typegen write .naive/types/ from the route table and the MCP tools
|
|
14
18
|
// naive upgrade [<venture dir>] [--framework] move a venture onto the next Naive layout; --framework first
|
|
15
19
|
// updates the installed framework with the venture's package manager (I-29)
|
|
@@ -62,10 +66,21 @@ async function dev(dir) {
|
|
|
62
66
|
const { workspace, venture } = asRouted(dir);
|
|
63
67
|
process.env.MENTURA_WORKSPACE = workspace;
|
|
64
68
|
const port = await free(Number(opt("port") ?? 4331));
|
|
69
|
+
// One origin: /setup is the setup page (interface/dev/setup.mjs); everything else is the board, served
|
|
70
|
+
// on a port of its own and passed through.
|
|
65
71
|
const { serve } = await import("../demo/board.mjs");
|
|
66
|
-
|
|
72
|
+
const { handle } = await import("../dev/setup.mjs");
|
|
73
|
+
const boardPort = await free(port + 1000);
|
|
74
|
+
serve(boardPort, venture);
|
|
75
|
+
const { createServer: http, request } = await import("node:http");
|
|
76
|
+
http(async (req, res) => {
|
|
77
|
+
if (await handle(req, res, dir)) return;
|
|
78
|
+
const up = request({ host: "127.0.0.1", port: boardPort, path: req.url, method: req.method, headers: req.headers }, (r) => { res.writeHead(r.statusCode, r.headers); r.pipe(res); });
|
|
79
|
+
up.on("error", (e) => { res.writeHead(502); res.end(String(e.message)); });
|
|
80
|
+
req.pipe(up);
|
|
81
|
+
}).listen(port, "127.0.0.1");
|
|
67
82
|
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`);
|
|
83
|
+
console.log(`\n ▲ Naive ${version()}\n - Venture: ${name} (${relative(process.cwd(), dir) || "."})\n - Setup: http://127.0.0.1:${port}/setup\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
84
|
// Live feedback on save (I-24): a record that no longer matches its schema says so at once.
|
|
70
85
|
const pending = new Map();
|
|
71
86
|
watch(dir, { recursive: true }, (_, f) => {
|
|
@@ -85,13 +100,12 @@ async function dev(dir) {
|
|
|
85
100
|
});
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
// The checks that read a workspace, run on the
|
|
89
|
-
const CHECKS = ["check-ventures", "check-provenance", "check-sources", "check-data-packages", "check-governance", "check-telos", "check-publication", "check-private", "check-where-used"];
|
|
103
|
+
// The checks that read a workspace, run on this venture: it and the ventures nested in it, not its neighbours.
|
|
104
|
+
const CHECKS = ["check-ventures", "check-people", "check-provenance", "check-sources", "check-data-packages", "check-governance", "check-telos", "check-publication", "check-private", "check-where-used"];
|
|
90
105
|
function check(dir) {
|
|
91
|
-
const { workspace } = asRouted(dir);
|
|
92
106
|
let failed = 0;
|
|
93
107
|
for (const c of CHECKS) {
|
|
94
|
-
const r = spawnSync("node", [join(repo, `schema/${c}.mjs`),
|
|
108
|
+
const r = spawnSync("node", [join(repo, `schema/${c}.mjs`), dir], { encoding: "utf8" });
|
|
95
109
|
const last = (r.stdout + r.stderr).trim().split("\n").filter(Boolean).at(-1) ?? "";
|
|
96
110
|
if (r.status !== 0) failed++;
|
|
97
111
|
console.log(`${r.status === 0 ? "pass" : "FAIL"} ${c.replace("check-", "")}: ${last}`);
|
|
@@ -150,7 +164,18 @@ else if (cmd === "build" || cmd === "start") {
|
|
|
150
164
|
// Outside a venture with none named, upgrade-venture.mjs prints its usage.
|
|
151
165
|
const dir = rest[0] && !rest[0].startsWith("--") ? resolve(rest[0]) : (opt("venture") ? here() : findVenture());
|
|
152
166
|
if (dir && rest.includes("--framework") && upgradeFramework(dir) !== 0) process.exit(1);
|
|
153
|
-
|
|
167
|
+
// Flags (--check, --guide, --adopt, --canary, --agent, --accept, --confirm) pass through to upgrade-venture.mjs; --framework and --venture are handled here.
|
|
168
|
+
const flags = rest.filter((a, i) => !(i === 0 && !a.startsWith("--")) && a !== "--framework" && a !== "--venture" && rest[i - 1] !== "--venture");
|
|
169
|
+
process.exit(run("schema/upgrade-venture.mjs", dir ? [dir, ...flags] : flags));
|
|
170
|
+
} else if (cmd === "frame") {
|
|
171
|
+
// The decision library (schema/frames.mjs): naive frame "<the founder's words>" runs the frame those words fit.
|
|
172
|
+
const sub = rest[0];
|
|
173
|
+
if (["list", "match", "check", "eval"].includes(sub)) process.exit(run("schema/frames.mjs", rest));
|
|
174
|
+
const words = rest.filter((a, i) => a !== "--venture" && rest[i - 1] !== "--venture" && a !== "--frame" && rest[i - 1] !== "--frame" && a !== "--lookup" && rest[i - 1] !== "--lookup").join(" ");
|
|
175
|
+
process.exit(run("schema/frames.mjs", ["run", here(), ...(opt("frame") ? [opt("frame")] : []), ...(words ? ["--text", words] : []), ...(opt("lookup") ? ["--lookup", opt("lookup")] : [])]));
|
|
176
|
+
} else if (cmd === "setup") {
|
|
177
|
+
// Setup in the model's harness (EE-083): the state, the artifact, and the founder confirming relayed answers.
|
|
178
|
+
process.exit(run("schema/setup.mjs", [here(), ...(rest.length ? rest.filter((a, i) => a !== "--venture" && rest[i - 1] !== "--venture") : ["state"])]));
|
|
154
179
|
} else if (cmd === "port" || cmd === "migrate") {
|
|
155
180
|
// Inside a venture, --venture defaults to it.
|
|
156
181
|
const args = rest.includes("--venture") ? rest : [...rest, "--venture", here()];
|