naive-framework 0.1.0 → 0.3.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 +42 -0
- package/docs/reference/ROUTES.md +36 -37
- package/evidence/skills/fidelity-ladder-audit/ladder-census.mjs +2 -1
- 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 +183 -0
- package/interface/api/server.mjs +4 -2
- package/interface/boundaries.mjs +219 -0
- package/interface/brand.mjs +215 -0
- package/interface/cli/freshness.mjs +59 -0
- package/interface/cli/guard.mjs +6 -0
- package/interface/cli/mentura.mjs +82 -11
- package/interface/cli/naive.mjs +44 -14
- package/interface/context.mjs +525 -0
- package/interface/demo/board.mjs +9 -8
- package/interface/demo/decision-harness.mjs +4 -3
- package/interface/dev/setup.mjs +84 -0
- package/interface/gate-hooks.mjs +37 -0
- package/interface/harness/evals.mjs +10 -9
- package/interface/harness/run.mjs +16 -15
- package/interface/instrumentation.mjs +87 -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 +197 -0
- package/interface/provenance.mjs +28 -4
- package/interface/reads.mjs +174 -0
- package/interface/release-card.mjs +43 -0
- package/interface/router.mjs +110 -36
- package/interface/skills/naive-setup/SKILL.md +71 -0
- package/interface/skills/naive-setup/artifact.html +83 -0
- package/interface/vitals.mjs +116 -0
- package/interface/writes.mjs +190 -0
- package/knowledge/connectors/catalog.mjs +3 -2
- package/knowledge/connectors/filesystem.mjs +10 -5
- package/knowledge/connectors/index.mjs +3 -2
- 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/marketing/check-public.mjs +7 -6
- package/operations/marketing/site/plan.mjs +4 -3
- package/operations/marketing/site/presence.mjs +4 -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 +9 -7
- 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/purpose/frameworks/idea-map/route.mjs +5 -4
- package/purpose/frameworks/product-anatomy/anatomy.mjs +2 -1
- package/schema/README.md +1 -0
- package/schema/access.mjs +58 -0
- package/schema/adapter.schema.json +33 -0
- package/schema/add-department.mjs +3 -2
- package/schema/assess-problem.mjs +3 -2
- package/schema/assets.mjs +213 -0
- package/schema/brand.mjs +374 -0
- package/schema/build-release.mjs +61 -0
- package/schema/caching.mjs +506 -0
- package/schema/check-assets.mjs +24 -0
- package/schema/check-brand.mjs +24 -0
- package/schema/check-config.mjs +111 -0
- package/schema/check-data-model.mjs +2 -1
- package/schema/check-data-packages.mjs +5 -4
- package/schema/check-execution.mjs +4 -3
- package/schema/check-nextjs-status.mjs +3 -1
- package/schema/check-people.mjs +84 -0
- package/schema/check-private.mjs +2 -1
- package/schema/check-ready.mjs +6 -5
- package/schema/check-sources.mjs +11 -2
- package/schema/check-where-used.mjs +4 -3
- package/schema/data-model.json +334 -8
- package/schema/deal.schema.json +80 -0
- package/schema/decision-analytics.mjs +2 -1
- package/schema/decisions.mjs +21 -12
- 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/foundation.mjs +8 -7
- package/schema/founder.schema.json +72 -0
- package/schema/frame.schema.json +240 -0
- package/schema/frames.mjs +229 -0
- package/schema/freshness-log.schema.json +40 -0
- package/schema/freshness-profiles.schema.json +44 -0
- package/schema/freshness.mjs +474 -0
- package/schema/fund-terms.schema.json +107 -0
- package/schema/fund-thesis.schema.json +87 -0
- package/schema/gate-drafts.mjs +131 -0
- package/schema/gate.mjs +105 -11
- package/schema/glossary.schema.json +25 -0
- package/schema/init-venture.mjs +69 -21
- 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/migrate-venture.mjs +8 -7
- package/schema/migrations/kit-to-package.mjs +95 -0
- package/schema/migrations/package-layout.mjs +90 -0
- package/schema/model-steps.mjs +718 -0
- package/schema/money-summary.schema.json +138 -0
- package/schema/naive-config.mjs +166 -0
- package/schema/naive-config.schema.json +268 -0
- package/schema/need-now.json +171 -0
- package/schema/need-now.mjs +8 -6
- package/schema/packages.mjs +21 -15
- package/schema/payouts.mjs +3 -2
- package/schema/people.schema.json +85 -0
- package/schema/port-venture.mjs +206 -132
- package/schema/public-assets.schema.json +76 -0
- package/schema/release-read.mjs +112 -0
- package/schema/releases.mjs +498 -0
- package/schema/run-handoff.mjs +5 -4
- package/schema/setup.mjs +165 -0
- package/schema/stale.mjs +112 -0
- package/schema/start-release.mjs +41 -0
- package/schema/structure.mjs +187 -0
- package/schema/systems-catalog.json +126 -0
- package/schema/systems-catalog.schema.json +78 -0
- package/schema/time-to-ready.mjs +7 -6
- package/schema/trigger.schema.json +44 -0
- package/schema/triggers.mjs +238 -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 +386 -0
- package/schema/validate.py +19 -10
- package/schema/venture-paths.mjs +85 -0
- package/schema/venture-role.schema.json +64 -0
- package/schema/venture-type.mjs +62 -0
- package/schema/venture-types.json +346 -0
- package/schema/venture-types.schema.json +129 -0
- package/schema/venture.schema.json +61 -0
- package/schema/workspace.schema.json +5 -1
|
@@ -0,0 +1,215 @@
|
|
|
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
|
+
import { ventureFile } from "../schema/venture-paths.mjs";
|
|
29
|
+
|
|
30
|
+
const VERSION = "0.1.0";
|
|
31
|
+
const today = () => new Date().toISOString();
|
|
32
|
+
|
|
33
|
+
/** The facts code reads, in the register's shape. */
|
|
34
|
+
export const factsFor = (dir, path) => {
|
|
35
|
+
const f = factsOf(readFileSync(ventureFile(dir, path)));
|
|
36
|
+
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));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const emptyRegister = () => ({ version: VERSION, assets: [] });
|
|
40
|
+
|
|
41
|
+
/** Writes the register through the router: schema first, then provenance. */
|
|
42
|
+
export async function writeRegister(venture, workspace, reg, { actor = "agent", note } = {}) {
|
|
43
|
+
return call({ venture, route: "public/assets", action: "write", input: reg, actor, workspace, via: "cli", provenance: { note } });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Registers one file in public/ with its source, license and credit; code fills its facts. */
|
|
47
|
+
export async function addAsset({ venture, workspace, path, source, license, credit, alt, decorative, origin_url, design_system, actor = "agent" }) {
|
|
48
|
+
const dir = ventureDir(venture, workspace);
|
|
49
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
50
|
+
if (!path?.startsWith("public/")) return { ok: false, error: `${path} is not in public/` };
|
|
51
|
+
if (!existsSync(ventureFile(dir, path))) return { ok: false, error: `${path} is not in ${venture}` };
|
|
52
|
+
const reg = registerOf(dir) ?? emptyRegister();
|
|
53
|
+
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));
|
|
54
|
+
reg.assets = [...reg.assets.filter((a) => a.path !== path), entry].sort((a, b) => a.path.localeCompare(b.path));
|
|
55
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `registered ${path}; facts read by code` });
|
|
56
|
+
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` });
|
|
57
|
+
return w.ok ? { ok: true, result: { registered: path, facts: entry.facts, alt: entry.alt ?? null } } : w;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Refills every entry's facts from its file. Only code writes facts. */
|
|
61
|
+
export async function fill({ venture, workspace }) {
|
|
62
|
+
const dir = ventureDir(venture, workspace);
|
|
63
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
64
|
+
const reg = registerOf(dir);
|
|
65
|
+
if (!reg) return { ok: false, error: `${venture} has no ${REGISTER}; register a file first: mentura assets add public/<file> ...` };
|
|
66
|
+
const changed = [], missing = [];
|
|
67
|
+
for (const a of reg.assets) {
|
|
68
|
+
if (!existsSync(ventureFile(dir, a.path))) { missing.push(a.path); continue; }
|
|
69
|
+
const f = factsFor(dir, a.path);
|
|
70
|
+
if (!a.facts || ["type", "width", "height", "bytes", "sha256"].some((k) => (a.facts[k] ?? null) !== (f[k] ?? null))) { a.facts = f; changed.push(a.path); }
|
|
71
|
+
}
|
|
72
|
+
const listed = new Set(reg.assets.map((a) => a.path));
|
|
73
|
+
const unregistered = allFacts(dir).map((x) => x.path).filter((p) => !listed.has(p));
|
|
74
|
+
if (changed.length) { const w = await writeRegister(venture, workspace, reg, { note: `facts filled by code: ${changed.join(", ")}` }); if (!w.ok) return w; }
|
|
75
|
+
return { ok: true, result: { filled: changed, missing, unregistered } };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Writes alt text. An agent's carries agent provenance until a person confirms it; a person's is confirmed as written. */
|
|
79
|
+
export async function setAlt({ venture, workspace, path, text, actor = "agent" }) {
|
|
80
|
+
const dir = ventureDir(venture, workspace);
|
|
81
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
82
|
+
const reg = registerOf(dir);
|
|
83
|
+
const a = reg?.assets.find((x) => x.path === path);
|
|
84
|
+
if (!a) return { ok: false, error: `${path} is not registered; mentura assets add ${path} first` };
|
|
85
|
+
a.alt = { text, by: actor === "person" ? "person" : "agent" };
|
|
86
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `alt text of ${path} by ${a.alt.by}` });
|
|
87
|
+
if (!w.ok) return w;
|
|
88
|
+
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` });
|
|
89
|
+
return { ok: true, result: { path, alt: a.alt, confirmed: actor === "person" } };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** A person confirms an alt text exactly as it stands. The confirmation is bound to the text: change it and it is pending again. */
|
|
93
|
+
export function confirmAlt({ venture, workspace, path, by }) {
|
|
94
|
+
const dir = ventureDir(venture, workspace);
|
|
95
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
96
|
+
if (!by) return { ok: false, error: "say who is confirming: --by <name>" };
|
|
97
|
+
const a = registerOf(dir)?.assets.find((x) => x.path === path);
|
|
98
|
+
if (!a?.alt?.text) return { ok: false, error: `${path} has no alt text to confirm` };
|
|
99
|
+
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}` });
|
|
100
|
+
return { ok: true, result: { path, alt: a.alt.text, confirmed_by: by, provenance: e.hash } };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const rawId = (url) => {
|
|
104
|
+
const u = new URL(url);
|
|
105
|
+
const slug = `${u.host}-${u.pathname.split("/").filter(Boolean).pop() ?? "image"}`.toLowerCase().replace(/\.[a-z0-9]+$/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
106
|
+
return slug.slice(0, 60) || "remote-image";
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A remote image as a link with a note in raw/ (J-3): raw/sources/<id>/source.json says where it is,
|
|
111
|
+
* text.md is the note, and the register lists it under remote. Nothing is fetched here. The platform
|
|
112
|
+
* fetcher passes `fetched` (facts of the private original) and `copy` when it kept a public copy.
|
|
113
|
+
*/
|
|
114
|
+
export async function addRemote({ venture, workspace, url, license, credit, id, alt, note, fetched, copy, actor = "agent" }) {
|
|
115
|
+
const dir = ventureDir(venture, workspace);
|
|
116
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
117
|
+
try { new URL(url); } catch { return { ok: false, error: `${url} is not a URL` }; }
|
|
118
|
+
const rid = id ?? rawId(url);
|
|
119
|
+
const reg = registerOf(dir) ?? emptyRegister();
|
|
120
|
+
if (!existsSync(join(ventureFile(dir, "raw/sources"), rid, "source.json"))) {
|
|
121
|
+
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)" } });
|
|
122
|
+
if (!w.ok) return w;
|
|
123
|
+
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}`] : []), "",
|
|
124
|
+
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");
|
|
125
|
+
writeFileSync(join(ventureFile(dir, "raw/sources"), rid, "text.md"), text);
|
|
126
|
+
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)" });
|
|
127
|
+
}
|
|
128
|
+
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));
|
|
129
|
+
reg.remote = [...(reg.remote ?? []).filter((r) => r.url !== url), entry];
|
|
130
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `remote image ${url}` });
|
|
131
|
+
return w.ok ? { ok: true, result: { remote: url, raw: `raw/sources/${rid}/`, copy: copy ?? null } } : w;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** The hosts an agent may fetch from, a setting the platform fetcher enforces (J-3). */
|
|
135
|
+
export async function setHosts({ venture, workspace, hosts, actor = "agent" }) {
|
|
136
|
+
const dir = ventureDir(venture, workspace);
|
|
137
|
+
if (!dir) return { ok: false, error: `no venture ${venture}` };
|
|
138
|
+
const reg = registerOf(dir) ?? emptyRegister();
|
|
139
|
+
reg.remote_hosts = hosts;
|
|
140
|
+
const w = await writeRegister(venture, workspace, reg, { actor, note: `hosts an agent may fetch from: ${hosts.join(", ") || "none"}` });
|
|
141
|
+
return w.ok ? { ok: true, result: { remote_hosts: hosts } } : w;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** mentura brand ... and mentura assets ...: returns the exit code. */
|
|
145
|
+
export async function main(argv, { workspace } = {}) {
|
|
146
|
+
const opt = (n) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
147
|
+
const flag = (n) => argv.includes(`--${n}`);
|
|
148
|
+
const pos = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "decorative"].includes(argv[i - 1].slice(2))));
|
|
149
|
+
const [group, verb, arg] = pos;
|
|
150
|
+
const venture = opt("venture");
|
|
151
|
+
const actor = opt("as") === "person" ? "person" : "agent";
|
|
152
|
+
const show = (r, text) => {
|
|
153
|
+
if (!r.ok) { console.error(r.error ?? JSON.stringify(r, null, 2)); return 1; }
|
|
154
|
+
console.log(flag("json") || text === undefined ? JSON.stringify(r.result, null, 2) : text);
|
|
155
|
+
return 0;
|
|
156
|
+
};
|
|
157
|
+
if (!venture) { console.error(`mentura ${group} ${verb ?? ""}: --venture <id> is required`); return 2; }
|
|
158
|
+
const dir = ventureDir(venture, workspace);
|
|
159
|
+
if (!dir) { console.error(`no venture ${venture}`); return 1; }
|
|
160
|
+
|
|
161
|
+
if (group === "assets") {
|
|
162
|
+
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}`);
|
|
163
|
+
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); }
|
|
164
|
+
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" : ""}`);
|
|
165
|
+
if (verb === "confirm") {
|
|
166
|
+
// The person types the path to confirm, as with foundation statements (EE-051).
|
|
167
|
+
if (opt("confirm") !== arg) { console.error(`not confirmed: pass --confirm ${arg} to confirm the alt text of ${arg} as it stands`); return 1; }
|
|
168
|
+
return show(confirmAlt({ venture, workspace, path: arg, by: opt("by") ?? process.env.MENTURA_PERSON }), `confirmed: alt text of ${arg}`);
|
|
169
|
+
}
|
|
170
|
+
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}`);
|
|
171
|
+
if (verb === "hosts") return show(await setHosts({ venture, workspace, hosts: (arg ?? "").split(",").map((h) => h.trim()).filter(Boolean), actor }), `remote_hosts: ${arg ?? "none"}`);
|
|
172
|
+
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");
|
|
173
|
+
if (verb === "check") {
|
|
174
|
+
const r = checkAssets(dir);
|
|
175
|
+
if (flag("json")) console.log(JSON.stringify(r, null, 2));
|
|
176
|
+
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`); }
|
|
177
|
+
return r.ok ? 0 : 1;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (group === "brand") {
|
|
182
|
+
const brand = brandOf(dir);
|
|
183
|
+
const need = () => { console.error(`${venture} has no brand yet: write marketing/brand.json (mentura marketing/brand template)`); return 1; };
|
|
184
|
+
if (verb === "tokens" || verb === "css" || verb === "head") {
|
|
185
|
+
if (!brand) return need();
|
|
186
|
+
const t = tokens(dir, brand);
|
|
187
|
+
return show({ ok: true, result: verb === "tokens" ? t.tokens : verb === "head" ? t.head : t.css }, verb === "css" ? t.css.trimEnd() : undefined);
|
|
188
|
+
}
|
|
189
|
+
if (verb === "resolve") { if (!brand) return need(); return show({ ok: true, result: resolveBrand(brand, arg ?? "") }); }
|
|
190
|
+
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}`); }
|
|
191
|
+
if (verb === "voice") {
|
|
192
|
+
const v = opt("text") !== undefined ? { violations: (await import("../schema/brand.mjs")).voiceCheck(opt("text"), brand ? resolveBrand(brand, opt("for") ?? "").voice.rules : []) } : voice(dir, brand);
|
|
193
|
+
const lines = v.violations.map((x) => `${x.file ? `${x.file}:` : "line "}${x.line} "${x.found}" breaks ${x.rule}${x.use ? `; use "${x.use}"` : ""}`);
|
|
194
|
+
if (flag("json")) console.log(JSON.stringify(v, null, 2)); else console.log(lines.join("\n") || "keeps to the voice rules");
|
|
195
|
+
return v.violations.length ? 1 : 0;
|
|
196
|
+
}
|
|
197
|
+
if (verb === "check") {
|
|
198
|
+
const r = checkBrand(dir);
|
|
199
|
+
if (flag("json")) console.log(JSON.stringify(r, null, 2));
|
|
200
|
+
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`); }
|
|
201
|
+
return r.ok ? 0 : 1;
|
|
202
|
+
}
|
|
203
|
+
if (verb === "sitemap") {
|
|
204
|
+
const s = sitemapOf(dir);
|
|
205
|
+
if (!s) { console.error(`${venture} has no marketing sitemap yet: mentura operations/marketing/sitemap run, then write it to marketing/sitemap`); return 1; }
|
|
206
|
+
let xml;
|
|
207
|
+
try { xml = sitemapXml(s.record, opt("base") ?? ""); } catch (e) { console.error(`${e.message}; pass --base https://<your site>`); return 1; }
|
|
208
|
+
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; }
|
|
209
|
+
process.stdout.write(xml);
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
console.error(readFileSync(new URL(import.meta.url), "utf8").split("\n").filter((l) => l.startsWith("// mentura")).map((l) => l.slice(5)).join("\n"));
|
|
214
|
+
return 2;
|
|
215
|
+
}
|
|
@@ -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,7 +63,10 @@ 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";
|
|
69
|
+
import { ventureFile } from "../../schema/venture-paths.mjs";
|
|
50
70
|
|
|
51
71
|
// The danger zone (EE-051): a foundation statement is accepted or confirmed only when the person types
|
|
52
72
|
// its name, at the prompt or with --confirm <name>.
|
|
@@ -68,7 +88,7 @@ const here = !argv.includes("--venture") && !argv.includes("--workspace") ? find
|
|
|
68
88
|
if (here) { argv.push("--venture", asRouted(here).venture, "--workspace", asRouted(here).workspace); process.env.MENTURA_WORKSPACE = asRouted(here).workspace; }
|
|
69
89
|
const opt = (name) => { const i = argv.indexOf(`--${name}`); return i >= 0 ? argv[i + 1] : undefined; };
|
|
70
90
|
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))));
|
|
91
|
+
const positional = argv.filter((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--") && !["json", "hierarchy"].includes(argv[i - 1].slice(2))));
|
|
72
92
|
const workspace = opt("workspace") ? resolve(opt("workspace")) : undefined;
|
|
73
93
|
const out = (x) => console.log(typeof x === "string" ? x : JSON.stringify(x, null, 2));
|
|
74
94
|
|
|
@@ -95,6 +115,9 @@ if (cmd === "ventures") {
|
|
|
95
115
|
process.exit(0);
|
|
96
116
|
}
|
|
97
117
|
|
|
118
|
+
// 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.
|
|
119
|
+
if (cmd === "brand" || cmd === "assets") process.exit(await (await import("../brand.mjs")).main(argv, { workspace }));
|
|
120
|
+
|
|
98
121
|
if (cmd === "add") {
|
|
99
122
|
// Imports one function into a venture (EE-047, EE-048, EE-071). department is the alias for function for one release.
|
|
100
123
|
const v = opt("venture");
|
|
@@ -108,6 +131,27 @@ if (cmd === "typegen") {
|
|
|
108
131
|
process.exit(p.status ?? 1);
|
|
109
132
|
}
|
|
110
133
|
|
|
134
|
+
// Upgrades hook (M-12, E-7, V-9, Z-4): these commands live in their own modules; this only dispatches.
|
|
135
|
+
// mentura triggers --venture <id> [--fire] [--json]
|
|
136
|
+
// the venture's triggers, their runs and open failures; --fire runs what new events start
|
|
137
|
+
// mentura stale --venture <id> [--json] summaries whose system of record changed since they were written
|
|
138
|
+
// mentura notify --venture <id> --source <id> --path <p> [--version <v>]
|
|
139
|
+
// a system of record says <p> changed: mark what reads it stale (a webhook does this on Mentura)
|
|
140
|
+
// mentura adapt <adapter> --venture <id> write a harness's entry files into the venture (claude-code, codex, cursor)
|
|
141
|
+
// mentura adapters [verify [<adapter>]] [--json] the adapters, or run the framework's tests an adapter must pass
|
|
142
|
+
if (["triggers", "stale", "notify", "adapt", "adapters"].includes(cmd)) {
|
|
143
|
+
const d = ["triggers", "stale", "notify", "adapt"].includes(cmd) ? (opt("venture") ? ventureDir(opt("venture"), workspace) : null) : null;
|
|
144
|
+
if (cmd !== "adapters" && !d) { console.error(opt("venture") ? `no venture ${opt("venture")}` : `mentura ${cmd} needs --venture <id>`); process.exit(2); }
|
|
145
|
+
const pass = (...names) => names.flatMap((n) => argv.flatMap((a, i) => (a === `--${n}` ? [a, ...(argv[i + 1] && !argv[i + 1].startsWith("--") ? [argv[i + 1]] : [])] : [])));
|
|
146
|
+
const args = cmd === "triggers" ? ["schema/triggers.mjs", d, ...pass("fire", "json")]
|
|
147
|
+
: cmd === "stale" ? ["schema/stale.mjs", d, ...pass("json")]
|
|
148
|
+
: cmd === "notify" ? ["schema/stale.mjs", d, "--notify", ...pass("source", "path", "version", "json")]
|
|
149
|
+
: cmd === "adapt" ? ["interface/adapters.mjs", "adapt", action ?? "", d, ...pass("json")]
|
|
150
|
+
: ["interface/adapters.mjs", ...positional.slice(1), ...pass("json")];
|
|
151
|
+
const p = spawnSync("node", [join(repo, args[0]), ...args.slice(1)], { stdio: "inherit" });
|
|
152
|
+
process.exit(p.status ?? 1);
|
|
153
|
+
}
|
|
154
|
+
|
|
111
155
|
if (cmd === "migrate" && (opt("from") || flag("apply"))) {
|
|
112
156
|
// migrate means rewriting an outside project into a new venture (EE-076): the same as naive migrate.
|
|
113
157
|
const v = opt("venture");
|
|
@@ -127,6 +171,16 @@ if (cmd === "upgrade" || cmd === "migrate") {
|
|
|
127
171
|
process.exit(p.status ?? 1);
|
|
128
172
|
}
|
|
129
173
|
|
|
174
|
+
// Model steps hook (use model, EE-073): mentura steps <payload|run|check|waterfall|provenance|confirm> --venture <id> [...].
|
|
175
|
+
// The logic is schema/model-steps.mjs; this passes the venture folder and the other arguments through.
|
|
176
|
+
if (cmd === "steps") {
|
|
177
|
+
const v = opt("venture"), d = v && ventureDir(v, workspace);
|
|
178
|
+
if (!d || !action) usage();
|
|
179
|
+
const pass = argv.slice(argv.indexOf("steps") + 1).filter((a, i, all) => !["--venture", "--workspace"].includes(a) && !["--venture", "--workspace"].includes(all[i - 1]));
|
|
180
|
+
const p = spawnSync("node", [join(repo, "schema/model-steps.mjs"), d, ...pass], { stdio: "inherit" });
|
|
181
|
+
process.exit(p.status ?? 1);
|
|
182
|
+
}
|
|
183
|
+
|
|
130
184
|
if (cmd === "init") {
|
|
131
185
|
// The venture scaffold lives in schema/init-venture.mjs; this passes the options through.
|
|
132
186
|
const id = opt("id");
|
|
@@ -148,22 +202,27 @@ if (cmd === "accept") {
|
|
|
148
202
|
if (!d) { console.error(`${rt} is not a record route, or no venture ${venture}`); process.exit(1); }
|
|
149
203
|
const draft = join(d.dir, d.draft);
|
|
150
204
|
if (!existsSync(draft)) { console.error(`no draft of ${d.route} in ${venture}`); process.exit(1); }
|
|
205
|
+
// Gate group hook (X-2): drafts kept side by side are accepted one at a time with --pick <letter>.
|
|
206
|
+
let chosen;
|
|
207
|
+
try { chosen = pickDraft(d.dir, d.draft, opt("pick")); } catch (e) { console.error(e.message); process.exit(1); }
|
|
208
|
+
const chosenFile = join(d.dir, chosen);
|
|
151
209
|
const recType = routes().find((x) => x.route === d.route || x.route === d.route.replace(/[^/]+$/, "[id]"))?.record;
|
|
152
210
|
const touched = foundationOf(recType);
|
|
153
211
|
if (touched.length) {
|
|
154
212
|
const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
|
|
155
213
|
const now = existsSync(join(d.dir, d.file)) ? JSON.parse(readFileSync(join(d.dir, d.file), "utf8")) : {};
|
|
156
|
-
const next = JSON.parse(readFileSync(
|
|
214
|
+
const next = JSON.parse(readFileSync(chosenFile, "utf8"));
|
|
157
215
|
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
216
|
const names = new Set([d.route, rt]);
|
|
159
217
|
const confirmed = opt("confirm") !== undefined ? names.has(opt("confirm")) : await typed(d.route, `Accepting ${d.route} changes these foundation statements:\n${diff}`);
|
|
160
218
|
if (!confirmed) { console.error("not accepted: the confirmation did not match"); process.exit(1); }
|
|
161
219
|
}
|
|
162
|
-
const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(
|
|
163
|
-
provenance: { activity: "accept", derived_from: [
|
|
220
|
+
const w = await call({ venture, route: d.route, action: "write", input: JSON.parse(readFileSync(chosenFile, "utf8")), actor: "person", workspace, via: "cli",
|
|
221
|
+
provenance: { activity: "accept", derived_from: [chosen], note: opt("note") } });
|
|
164
222
|
if (!w.ok) { console.error(w.error); process.exit(1); }
|
|
165
|
-
|
|
166
|
-
|
|
223
|
+
// Gate group hook (X-2): removes the draft, its variants and sidecar, and decides the variants decision.
|
|
224
|
+
const settled = settleDraft(d.dir, d.draft, { picked: opt("pick") ?? "a", by: opt("by") });
|
|
225
|
+
out(`accepted: ${w.result.written}${settled.decision ? ` (draft ${settled.picked}; decision ${settled.decision} ${settled.decided ? "decided" : `not decided: ${settled.error}`})` : ""}`);
|
|
167
226
|
process.exit(0);
|
|
168
227
|
}
|
|
169
228
|
|
|
@@ -197,7 +256,7 @@ if (cmd === "understand") {
|
|
|
197
256
|
// Mentura got wrong, and confirms the understanding before the ideas are routed.
|
|
198
257
|
const venture = opt("venture"), dir = venture && ventureDir(venture, workspace);
|
|
199
258
|
if (!dir) usage();
|
|
200
|
-
const hits = ["purpose/ideas/map.mdx", "purpose/ideas/graph.json"].filter((f) => existsSync(
|
|
259
|
+
const hits = ["purpose/ideas/map.mdx", "purpose/ideas/graph.json"].filter((f) => existsSync(ventureFile(dir, f)));
|
|
201
260
|
if (!hits.length) { console.error(`${venture} has no idea map yet: map the idea dump first (purpose/ideas)`); process.exit(1); }
|
|
202
261
|
if (!(await typed("ideas", `Confirming Mentura's understanding of your ideas: ${hits.join(", ")}`, "Your ideas are routed to their records from this understanding."))) { console.error("not confirmed: the confirmation did not match"); process.exit(1); }
|
|
203
262
|
for (const f of hits) record(dir, { entity: f, route: f.endsWith(".mdx") ? "purpose/ideas" : "purpose/ideas/graph", activity: "confirm", statement: "ideas", agent: agentFromEnv("person", "cli"), note: opt("note") });
|
|
@@ -213,10 +272,10 @@ if (cmd === "confirm") {
|
|
|
213
272
|
const s = FRAME.questions.flatMap((q) => q.statements).find((x) => x.id === id);
|
|
214
273
|
if (!s?.record) { console.error(`${id} is not a foundation statement with a record (see mentura purpose/foundation run)`); process.exit(1); }
|
|
215
274
|
const rt = routes().find((x) => x.record === s.record && x.collection !== "list")?.route ?? s.record;
|
|
216
|
-
const hits = filesOf(dir, s.record).filter((f) => existsSync(
|
|
275
|
+
const hits = filesOf(dir, s.record).filter((f) => existsSync(ventureFile(dir, f)));
|
|
217
276
|
if (!hits.length) { console.error(`${venture} has no ${s.record} record to confirm`); process.exit(1); }
|
|
218
277
|
const get = (o, p) => p.split(".").reduce((x, k) => (x == null ? undefined : x[k]), o);
|
|
219
|
-
const shown = hits.map((f) => ` ${f}: ${JSON.stringify(get(JSON.parse(readFileSync(
|
|
278
|
+
const shown = hits.map((f) => ` ${f}: ${JSON.stringify(get(JSON.parse(readFileSync(ventureFile(dir, f), "utf8")), s.attribute) ?? null)}`).join("\n");
|
|
220
279
|
if (!(await typed(id, `Confirming ${id} as yours:\n${shown}`))) { console.error("not confirmed: the confirmation did not match"); process.exit(1); }
|
|
221
280
|
for (const f of hits) record(dir, { entity: f, route: rt, activity: "confirm", statement: id, agent: agentFromEnv("person", "cli"), note: opt("note") });
|
|
222
281
|
out(`confirmed: ${id}`);
|
|
@@ -243,6 +302,13 @@ if (cmd === "baseline") {
|
|
|
243
302
|
process.exit(0);
|
|
244
303
|
}
|
|
245
304
|
|
|
305
|
+
// Freshness (Revalidating, V-1 to V-8): revalidate, verify, phase and freshness live in ./freshness.mjs.
|
|
306
|
+
if (["revalidate", "verify", "phase", "freshness"].includes(cmd)) process.exit(await (await import("./freshness.mjs")).main(cmd, argv, workspace));
|
|
307
|
+
|
|
308
|
+
// Context hook (interface/context.mjs): navigate and head read a route header first, with its layout once per
|
|
309
|
+
// session (--session), its template and prefetched refs (--task, --prefetch, --no-prefetch, --stream); batch runs
|
|
310
|
+
// a list of calls with progress.
|
|
311
|
+
if (["navigate", "head"].includes(action) || cmd === "batch") { const { cli } = await import("../context.mjs"); process.exit(await cli(argv, { workspace })); }
|
|
246
312
|
if (!action) usage();
|
|
247
313
|
const venture = opt("venture");
|
|
248
314
|
if (!venture && !["template", "layout"].includes(action)) usage();
|
|
@@ -251,7 +317,12 @@ const src = opt("input");
|
|
|
251
317
|
if (src) input = JSON.parse(src === "-" ? readFileSync(0, "utf8") : readFileSync(src, "utf8"));
|
|
252
318
|
if (opt("value")) input = { ...(input ?? {}), value: Number.isNaN(Number(opt("value"))) ? opt("value") : Number(opt("value")) };
|
|
253
319
|
if (flag("as-is")) input = { ...(input ?? {}), as_is: true };
|
|
320
|
+
// wf/writes hook: --where k=v[,k=v] filters a read (L-10); --hierarchy returns its resolution levels (P-28).
|
|
321
|
+
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("=")])) };
|
|
322
|
+
if (["read", "view"].includes(action) && flag("hierarchy")) input = { ...(input ?? {}), hierarchy: true };
|
|
254
323
|
const provenance = { derived_from: opt("derived-from")?.split(",").filter(Boolean), decision: opt("decision"), note: opt("note") };
|
|
324
|
+
// Gate group hook (X-4): a confidence score computed before the write, for the gate's confidence rules.
|
|
325
|
+
if (opt("confidence") !== undefined) provenance.confidence = Number(opt("confidence"));
|
|
255
326
|
let r = await call({ venture, route: cmd, action, input, actor: opt("as") === "person" ? "person" : "agent", workspace, via: "cli", provenance });
|
|
256
327
|
// Asks before running (EE-050): at a terminal, the person chooses; otherwise it prints both choices.
|
|
257
328
|
if (r.ok && r.needs_input && !flag("json")) {
|
|
@@ -267,5 +338,5 @@ if (r.ok && r.needs_input && !flag("json")) {
|
|
|
267
338
|
}
|
|
268
339
|
if (!r.ok) { console.error(r.error ?? JSON.stringify(r.result, null, 2)); process.exit(1); }
|
|
269
340
|
if (r.deprecated) console.error(`note: ${r.deprecated}`);
|
|
270
|
-
const extra = ["not_found", "pending_draft", "fix", "needs_input"].some((k) => r[k]);
|
|
341
|
+
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
342
|
out(flag("json") || extra ? r : r.result);
|