@ybotok/loam 0.0.0 → 0.1.0-beta.2
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/CHANGELOG.md +138 -0
- package/COMPARISON.md +63 -0
- package/LICENSE +21 -0
- package/MIGRATING-from-OpenSpec.md +202 -0
- package/README.md +246 -9
- package/SCHEMA.md +425 -0
- package/SECURITY.md +9 -0
- package/dist/cli.js +142 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/adopt.js +240 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/archive.js +1313 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/delta.js +388 -0
- package/dist/commands/delta.js.map +1 -0
- package/dist/commands/dependencies.js +83 -0
- package/dist/commands/dependencies.js.map +1 -0
- package/dist/commands/docs-repo-gate.js +104 -0
- package/dist/commands/docs-repo-gate.js.map +1 -0
- package/dist/commands/doctor.js +85 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/explore.js +195 -0
- package/dist/commands/explore.js.map +1 -0
- package/dist/commands/format.js +17 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/gherkin.js +309 -0
- package/dist/commands/gherkin.js.map +1 -0
- package/dist/commands/init.js +250 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/instructions.js +70 -0
- package/dist/commands/instructions.js.map +1 -0
- package/dist/commands/list.js +291 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/migrate-openspec.js +950 -0
- package/dist/commands/migrate-openspec.js.map +1 -0
- package/dist/commands/new.js +413 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/rebase.js +356 -0
- package/dist/commands/rebase.js.map +1 -0
- package/dist/commands/show.js +320 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/status.js +254 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/unarchive.js +317 -0
- package/dist/commands/unarchive.js.map +1 -0
- package/dist/commands/validate.js +1591 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/verify.js +775 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/vouch.js +343 -0
- package/dist/commands/vouch.js.map +1 -0
- package/dist/core/agent.js +2065 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/agents-stamp.js +68 -0
- package/dist/core/agents-stamp.js.map +1 -0
- package/dist/core/arch.js +130 -0
- package/dist/core/arch.js.map +1 -0
- package/dist/core/brief.js +733 -0
- package/dist/core/brief.js.map +1 -0
- package/dist/core/coherence.js +577 -0
- package/dist/core/coherence.js.map +1 -0
- package/dist/core/concurrency.js +51 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/config.js +180 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/delta.js +476 -0
- package/dist/core/delta.js.map +1 -0
- package/dist/core/dependencies.js +356 -0
- package/dist/core/dependencies.js.map +1 -0
- package/dist/core/docs.js +144 -0
- package/dist/core/docs.js.map +1 -0
- package/dist/core/doctor.js +559 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/document-bytes.js +59 -0
- package/dist/core/document-bytes.js.map +1 -0
- package/dist/core/explore.js +225 -0
- package/dist/core/explore.js.map +1 -0
- package/dist/core/fleet-context.js +225 -0
- package/dist/core/fleet-context.js.map +1 -0
- package/dist/core/frontmatter.js +199 -0
- package/dist/core/frontmatter.js.map +1 -0
- package/dist/core/gherkin.js +463 -0
- package/dist/core/gherkin.js.map +1 -0
- package/dist/core/health.js +67 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/ids.js +122 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/issue.js +9 -0
- package/dist/core/issue.js.map +1 -0
- package/dist/core/json.js +74 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/likec4.js +479 -0
- package/dist/core/likec4.js.map +1 -0
- package/dist/core/maturity.js +66 -0
- package/dist/core/maturity.js.map +1 -0
- package/dist/core/openapi-merge.js +552 -0
- package/dist/core/openapi-merge.js.map +1 -0
- package/dist/core/openapi.js +255 -0
- package/dist/core/openapi.js.map +1 -0
- package/dist/core/openspec-inventory.js +1440 -0
- package/dist/core/openspec-inventory.js.map +1 -0
- package/dist/core/path-safety.js +168 -0
- package/dist/core/path-safety.js.map +1 -0
- package/dist/core/provenance.js +720 -0
- package/dist/core/provenance.js.map +1 -0
- package/dist/core/records.js +33 -0
- package/dist/core/records.js.map +1 -0
- package/dist/core/repo.js +436 -0
- package/dist/core/repo.js.map +1 -0
- package/dist/core/report.js +47 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/results.js +199 -0
- package/dist/core/results.js.map +1 -0
- package/dist/core/spec.js +481 -0
- package/dist/core/spec.js.map +1 -0
- package/dist/core/staging.js +890 -0
- package/dist/core/staging.js.map +1 -0
- package/dist/core/status.js +1104 -0
- package/dist/core/status.js.map +1 -0
- package/dist/core/steps.js +72 -0
- package/dist/core/steps.js.map +1 -0
- package/dist/core/verify.js +807 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/core/version.js +24 -0
- package/dist/core/version.js.map +1 -0
- package/package.json +63 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { existsSync, statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport {\n CONFIG_FILENAME,\n findConfigPath,\n loadConfig,\n localConfigPath,\n saveConfig,\n type LoamConfig,\n} from \"../core/config.js\";\nimport { emitJson, fail } from \"../core/json.js\";\nimport { plannedDocsFiles, scaffoldDocs } from \"../core/docs.js\";\nimport { agentsPath, docsRepoState, listServices } from \"../core/repo.js\";\nimport { InvalidIdError, assertServiceId } from \"../core/ids.js\";\nimport {\n AGENT_TOOLS,\n detectAgentTools,\n plannedCommandFiles,\n scaffoldAgentCommands,\n type Delivery,\n} from \"../core/agent.js\";\n\ninterface InitOptions {\n docs: string;\n service?: string;\n /** --create: make a new docs repo at --docs instead of joining an existing one. */\n create?: boolean;\n /** --force: write loam.json here even though one already governs a parent directory. */\n force?: boolean;\n /** commander's --no-commands: true unless the flag is passed. */\n commands: boolean;\n /** commander's --no-skills: true unless the flag is passed. */\n skills: boolean;\n /** --tools: comma-separated AGENT_TOOLS ids, or \"all\". Absent = autodetect. */\n tools?: string;\n json?: boolean;\n}\n\n/**\n * Resolve --tools to registry ids, or null after reporting the refusal. The\n * default (the cwd scan) lives at the call site, not in commander, so an\n * explicit `--tools` is distinguishable from none — which is what lets both the\n * autodetection fallback and the `--no-*` contradiction be decided instead of\n * silently arbitrated.\n */\nfunction resolveTools(raw: string, json: boolean): string[] | null {\n const supported = Object.keys(AGENT_TOOLS);\n if (raw === \"all\") return supported;\n const ids = [...new Set(raw.split(\",\").map((t) => t.trim()).filter((t) => t !== \"\"))];\n const unknown = ids.filter((t) => !(t in AGENT_TOOLS));\n if (ids.length === 0 || unknown.length > 0) {\n fail(\n json,\n \"invalid-option\",\n (ids.length === 0\n ? \"--tools names no tool.\"\n : `--tools does not recognize: ${unknown.join(\", \")}.`) +\n ` Supported: ${supported.join(\", \")} — or \"all\".`,\n );\n return null;\n }\n return ids;\n}\n\n/**\n * `--docs` exactly as the caller wrote it, with separators normalised.\n *\n * Stored verbatim on purpose. `init` used to resolve it and write the absolute\n * result, which meant a committed loam.json named a directory that existed on\n * one laptop: every teammate who cloned the service repo got a docsDir under\n * someone else's home directory, and `loam list` reported an empty fleet\n * instead of saying so. `loadConfig` resolves relative paths against the config\n * file's own directory, so `../docs` keeps meaning \"next to this repo\" wherever\n * the pair is checked out. An absolute `--docs` is still stored absolute — that\n * is the caller's explicit choice, and `loam doctor` warns about it.\n *\n * Backslashes become forward slashes so a config written on Windows resolves on\n * POSIX; a trailing separator is dropped so the stored spelling is stable.\n */\nfunction storedDocsDir(raw: string): string {\n const slashed = raw.split(\"\\\\\").join(\"/\");\n if (slashed.length <= 1) return slashed;\n const trimmed = slashed.replace(/\\/+$/, \"\");\n return trimmed === \"\" ? \"/\" : trimmed;\n}\n\n/**\n * Does this directory already hold a docs repo? `services/` plus `AGENTS.md` —\n * the two things every docs repo has and no service repo does. The pair is the\n * whole point: a single marker would make `init --docs ../srv` (a typo for\n * `../docs`) look like a join and adopt the service repo as the fleet.\n */\nfunction isDocsRepo(dir: string): boolean {\n return docsRepoState(dir).kind === \"ok\" && existsSync(agentsPath(dir));\n}\n\nexport function registerInit(program: Command): void {\n program\n .command(\"init\")\n .description(\"Initialize loam: create/point at the shared docs repo and write local config\")\n .option(\"--docs <dir>\", \"path to the shared docs repo (the source of truth)\", \".loam-docs\")\n .option(\"--service <id>\", \"canonical id of the service in this repo\")\n .option(\"--create\", \"create a new docs repo at --docs instead of joining an existing one\")\n .option(\"--force\", `write ${CONFIG_FILENAME} here even though one governs a parent directory`)\n .option(\n \"--tools <ids>\",\n \"agent tools to generate for, overriding the scan of this directory: \" +\n `comma-separated (${Object.keys(AGENT_TOOLS).join(\", \")}) or \"all\"`,\n )\n .option(\"--no-commands\", \"skip the slash commands for this repo entirely\")\n .option(\"--no-skills\", \"skip the agent skills for this repo entirely\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .action(async (opts: InitOptions, command: Command) => {\n const json = opts.json === true;\n const cwd = process.cwd();\n\n // The id becomes services/<id>/ in the shared repo, so it is validated\n // before anything is written — by the same rule adopt, vouch and gherkin\n // use, never by a guard invented here.\n if (opts.service !== undefined) {\n try {\n assertServiceId(opts.service);\n } catch (err) {\n if (!(err instanceof InvalidIdError)) throw err;\n fail(json, \"invalid-option\", err.message);\n return;\n }\n }\n\n // A loam.json in an ancestor already governs this directory: every command\n // run from here resolves to it. Writing a second one below it would shadow\n // the first for this subtree only — the kind of split-brain nobody\n // discovers until two commands disagree about which docs repo is the fleet.\n const governing = findConfigPath(cwd);\n if (governing !== null && governing !== localConfigPath(cwd) && opts.force !== true) {\n fail(\n json,\n \"already-exists\",\n `${governing} already governs this directory. ` +\n `Run loam from ${resolve(governing, \"..\")}, or pass --force to give this ` +\n `subdirectory its own ${CONFIG_FILENAME}.`,\n );\n return;\n }\n\n // Only THIS directory's config is spread forward. Under --force there is\n // a config in an ancestor too, and inheriting its `service` would bind\n // this repo to a service somebody else's repo declared.\n const existing = existsSync(localConfigPath(cwd)) ? await loadConfig(cwd) : null;\n\n // A re-run must not move a committed pointer. `--docs` has a default\n // (`.loam-docs`), so an init invoked for any OTHER reason — `--service`,\n // a new `--tools`, the fix `doctor.agent-files-missing` prints — used to\n // arrive here carrying a docsDir nobody typed: the default then named no\n // docs repo, the refusal steered the caller to `--create`, and `--create`\n // rewrote `docsDir` in the committed loam.json to a freshly scaffolded\n // empty one. `validate --all` went green over an empty fleet, which is\n // the exact outcome `--create` exists to prevent. So the flag wins only\n // when it was actually passed; otherwise the file this repo already\n // committed does.\n const committedDocs = existing?.docsDirAsWritten;\n const docsTyped = command.getOptionValueSource(\"docs\") !== \"default\";\n const docsSource: \"flag\" | \"config\" | \"default\" =\n docsTyped ? \"flag\" : committedDocs === undefined ? \"default\" : \"config\";\n const docsOption = !docsTyped && committedDocs !== undefined ? committedDocs : opts.docs;\n const docsDir = resolve(cwd, docsOption);\n\n // The one predictable failure: --docs naming a file. Refused here so the\n // caller gets a clean envelope/message instead of mkdir's ENOTDIR throw.\n if (existsSync(docsDir) && !statSync(docsDir).isDirectory()) {\n fail(json, \"invalid-option\", `--docs points at a file, not a directory: ${docsDir}`);\n return;\n }\n\n // Joining an existing docs repo and creating a new one are different\n // intentions, and `init` used to perform the second one whenever the first\n // was misspelled: `--docs ../dcos` scaffolded a second, empty docs repo\n // beside the real one, and the fleet quietly split in two. Creation is now\n // something the caller asks for.\n const joining = isDocsRepo(docsDir);\n if (!joining && opts.create !== true) {\n fail(\n json,\n \"invalid-option\",\n docsRepoState(docsDir).kind === \"missing\"\n ? `--docs points at a directory that does not exist: ${docsDir}. ` +\n \"If that is a typo, fix it; to create a new docs repo there, pass --create.\"\n : `--docs points at ${docsDir}, which is not a docs repo ` +\n \"(a docs repo has services/ and AGENTS.md). \" +\n \"Point --docs at the shared docs repo, or pass --create to make a new one here.\",\n );\n return;\n }\n\n // --tools selects the files to write; --no-commands and --no-skills each\n // say write none of that kind. Together they contradict — refused, not\n // arbitrated, in either combination.\n const suppressed = [\n ...(opts.commands ? [] : [\"--no-commands\"]),\n ...(opts.skills ? [] : [\"--no-skills\"]),\n ];\n if (opts.tools !== undefined && suppressed.length > 0) {\n fail(\n json,\n \"invalid-option\",\n `--tools contradicts ${suppressed.join(\" and \")}: ` +\n \"one selects the files to generate, the other suppresses them.\",\n );\n return;\n }\n const delivery: Delivery[] = [\n ...(opts.commands ? ([\"commands\"] as const) : []),\n ...(opts.skills ? ([\"skills\"] as const) : []),\n ];\n\n // Without --tools the repo decides: whichever tools have left their own\n // dot-directories here. Falling back to claude keeps a bare repo behaving\n // exactly as it did before the scan existed. `detected` is reported either\n // way — what the scan saw is a fact about the repo, not about the flags.\n const detected = detectAgentTools(cwd);\n const scanned = detected.length > 0 ? detected : [\"claude\"];\n const tools = opts.tools === undefined ? scanned : resolveTools(opts.tools, json);\n if (tools === null) return;\n\n // `skipped` is the other half of the never-overwrite contract: the\n // scaffolds return only what they created, so what they left alone is\n // probed here, before they run — in the order they would create it, so\n // created + skipped is the same list on every repo. Both halves come from\n // the scaffolds' own planned-file lists, so the probe cannot drift from\n // what is actually written.\n const agentFiles = delivery.length > 0 ? plannedCommandFiles(cwd, tools, delivery) : [];\n const candidates = [...plannedDocsFiles(docsDir), ...agentFiles.map((f) => f.path)];\n const skipped = candidates.filter((p) => existsSync(p));\n\n // Joining touches the docs repo not at all: it is somebody's committed\n // repository, and \"point at it\" must never mean \"write to it\".\n const created = joining && opts.create !== true ? [] : (await scaffoldDocs(docsDir)).created;\n const generatedFor = delivery.length > 0 ? tools : [];\n if (delivery.length > 0) {\n created.push(...(await scaffoldAgentCommands(cwd, tools, delivery)));\n }\n\n const stored = storedDocsDir(docsOption);\n // Union, not replacement: this run's files join the ones earlier runs left\n // on disk, and `agentTools` is a record of what the repo HOLDS. A run that\n // generated nothing adds nothing and erases nothing.\n const recordedTools = [...new Set([...(existing?.agentTools ?? []), ...generatedFor])];\n const config: LoamConfig = {\n ...existing,\n docsDir: stored,\n ...(opts.service ? { service: opts.service } : {}),\n ...(recordedTools.length > 0 ? { agentTools: recordedTools } : {}),\n };\n const configFile = await saveConfig(config, cwd);\n\n const serviceCount = (await listServices(docsDir)).length;\n\n if (json) {\n // `docsDir` stays the resolved absolute path — it is what a consumer\n // needs to find the repo. `docsDirStored` is what was written to\n // loam.json, which is the fact this command is now careful about.\n // `tools` names what files were generated FOR — empty when every\n // delivery was suppressed and none were. `detected` is the separate\n // question of what the scan SAW, which `--tools` overrides without\n // changing: an agent comparing the two learns whether its selection\n // matches the repo.\n emitJson({\n docsDir,\n docsDirStored: stored,\n // Where the pointer came from: `flag` (a --docs somebody typed),\n // `config` (the one this repo already committed — a re-run cannot\n // move it), or `default` (nothing said, nothing committed).\n docsDirSource: docsSource,\n docsRepo: joining ? \"existing\" : \"created\",\n services: serviceCount,\n created,\n skipped,\n tools: generatedFor,\n detected,\n });\n return;\n }\n\n console.log(\"loam initialized.\");\n if (joining) {\n console.log(` docs repo: ${docsDir}`);\n console.log(` pointing at existing docs repo (${serviceCount} services)`);\n } else {\n console.log(` docs repo: ${docsDir} (created)`);\n }\n console.log(\n ` docsDir: ${stored} (as stored in ${CONFIG_FILENAME}`\n + (docsSource === \"config\" ? \" — kept; no --docs was passed\" : \"\") + \")\",\n );\n if (config.service) console.log(` service: ${config.service}`);\n console.log(` config: ${configFile}`);\n console.log(` commit ${CONFIG_FILENAME} — docsDir is resolved relative to it`);\n if (opts.tools === undefined && detected.length > 0) {\n console.log(` detected: ${detected.join(\", \")}`);\n }\n if (opts.commands) console.log(` commands: ${tools.join(\", \")}`);\n if (opts.skills) console.log(` skills: ${tools.join(\", \")}`);\n if (created.length > 0) {\n console.log(\" scaffolded:\");\n for (const c of created) console.log(` + ${c}`);\n }\n });\n}\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { PROTOCOLS, WORKFLOWS, protocolFor } from "../core/agent.js";
|
|
2
|
+
import { emitJson, fail } from "../core/json.js";
|
|
3
|
+
import { LOAM_VERSION } from "../core/version.js";
|
|
4
|
+
/**
|
|
5
|
+
* The workflow protocols, from the binary rather than from a file somebody
|
|
6
|
+
* generated once.
|
|
7
|
+
*
|
|
8
|
+
* This is the only command in loam that reads nothing: no `loam.json`, no docs
|
|
9
|
+
* repo, no service. That is deliberate and it is the point — an agent runs this
|
|
10
|
+
* BEFORE the repository is wired, and a protocol that refused with `no-config`
|
|
11
|
+
* would be unreachable exactly when it is most needed, which is the first
|
|
12
|
+
* minute in an unfamiliar repository. `loam-adopt`'s own first step is to run
|
|
13
|
+
* `loam init` when there is no config, and it cannot be the step that requires
|
|
14
|
+
* one.
|
|
15
|
+
*/
|
|
16
|
+
export function registerInstructions(program) {
|
|
17
|
+
program
|
|
18
|
+
.command("instructions")
|
|
19
|
+
.argument("[workflow]", "workflow name (loam-adopt, loam-feature, …); omit to list them")
|
|
20
|
+
.argument("[args...]", "values substituted for the protocol's $1, $2, … placeholders")
|
|
21
|
+
.description("Print a workflow protocol, version-matched to this binary")
|
|
22
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
23
|
+
// Every positional here is DATA — a feature id, a service id, a validate
|
|
24
|
+
// target — and one of those targets is spelled `--all`. Without this,
|
|
25
|
+
// commander reads a leading-dash argument as an option it does not have and
|
|
26
|
+
// refuses the whole invocation, so `/loam-check --all` (the first form the
|
|
27
|
+
// command file's own argument hint offers) got a usage error instead of the
|
|
28
|
+
// protocol. `--json` still parses as the flag it is, because it is declared.
|
|
29
|
+
.allowUnknownOption()
|
|
30
|
+
.action((workflow, args, opts) => {
|
|
31
|
+
const json = opts.json === true;
|
|
32
|
+
if (workflow === undefined) {
|
|
33
|
+
if (json) {
|
|
34
|
+
emitJson({ command: "instructions", version: LOAM_VERSION, workflows: WORKFLOWS });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log(`loam ${LOAM_VERSION} — workflows\n`);
|
|
38
|
+
const width = Math.max(...WORKFLOWS.map((w) => w.name.length));
|
|
39
|
+
for (const w of WORKFLOWS) {
|
|
40
|
+
console.log(` ${w.name.padEnd(width)} ${w.description}`);
|
|
41
|
+
}
|
|
42
|
+
console.log(`\nloam instructions <workflow> [args...] prints one, placeholders filled in.`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const body = protocolFor(workflow, args);
|
|
46
|
+
if (body === null) {
|
|
47
|
+
// `unknown-target`, the same code `show` uses for a name that resolves
|
|
48
|
+
// to nothing. The known set is small and closed, so it is listed rather
|
|
49
|
+
// than described: a caller that mistyped one of six names should not
|
|
50
|
+
// have to run a second command to see them.
|
|
51
|
+
fail(json, "unknown-target", `No workflow '${workflow}'. Known: ${Object.keys(PROTOCOLS).join(", ")}.`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (json) {
|
|
55
|
+
// `body` is the substituted text, not the template: a consumer that
|
|
56
|
+
// asked for `loam instructions loam-adopt payment-service --json` wants
|
|
57
|
+
// the protocol it is about to follow, and re-implementing the
|
|
58
|
+
// substitution on the other side is a second copy of the rule.
|
|
59
|
+
emitJson({ command: "instructions", version: LOAM_VERSION, workflow, args, body });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// `console.log`, like every other command, rather than a raw stdout
|
|
63
|
+
// write: the test harness captures the console and a direct write is
|
|
64
|
+
// invisible to it — an untestable print is how a protocol nobody notices
|
|
65
|
+
// went missing would ship. The body already ends in a newline, so the
|
|
66
|
+
// one console.log adds is trimmed rather than doubled.
|
|
67
|
+
console.log(body.replace(/\n+$/, ""));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/commands/instructions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMlD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,cAAc,CAAC;SACvB,QAAQ,CAAC,YAAY,EAAE,gEAAgE,CAAC;SACxF,QAAQ,CAAC,WAAW,EAAE,8DAA8D,CAAC;SACrF,WAAW,CAAC,2DAA2D,CAAC;SACxE,MAAM,CAAC,QAAQ,EAAE,qDAAqD,CAAC;QACxE,yEAAyE;QACzE,sEAAsE;QACtE,4EAA4E;QAC5E,2EAA2E;QAC3E,4EAA4E;QAC5E,6EAA6E;SAC5E,kBAAkB,EAAE;SACpB,MAAM,CAAC,CAAC,QAA4B,EAAE,IAAc,EAAE,IAAyB,EAAE,EAAE;QAClF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC;QAEhC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,IAAI,EAAE,CAAC;gBACT,QAAQ,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;gBACnF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,QAAQ,YAAY,gBAAgB,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YAC/D,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;gBAC1B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,8EAA8E,CAAC,CAAC;YAC5F,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,4CAA4C;YAC5C,IAAI,CACF,IAAI,EACJ,gBAAgB,EAChB,gBAAgB,QAAQ,aAAa,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC1E,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,IAAI,EAAE,CAAC;YACT,oEAAoE;YACpE,wEAAwE;YACxE,8DAA8D;YAC9D,+DAA+D;YAC/D,QAAQ,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACnF,OAAO;QACT,CAAC;QACD,oEAAoE;QACpE,qEAAqE;QACrE,yEAAyE;QACzE,sEAAsE;QACtE,uDAAuD;QACvD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import type { Command } from \"commander\";\nimport { PROTOCOLS, WORKFLOWS, protocolFor } from \"../core/agent.js\";\nimport { emitJson, fail } from \"../core/json.js\";\nimport { LOAM_VERSION } from \"../core/version.js\";\n\ninterface InstructionsOptions {\n json?: boolean;\n}\n\n/**\n * The workflow protocols, from the binary rather than from a file somebody\n * generated once.\n *\n * This is the only command in loam that reads nothing: no `loam.json`, no docs\n * repo, no service. That is deliberate and it is the point — an agent runs this\n * BEFORE the repository is wired, and a protocol that refused with `no-config`\n * would be unreachable exactly when it is most needed, which is the first\n * minute in an unfamiliar repository. `loam-adopt`'s own first step is to run\n * `loam init` when there is no config, and it cannot be the step that requires\n * one.\n */\nexport function registerInstructions(program: Command): void {\n program\n .command(\"instructions\")\n .argument(\"[workflow]\", \"workflow name (loam-adopt, loam-feature, …); omit to list them\")\n .argument(\"[args...]\", \"values substituted for the protocol's $1, $2, … placeholders\")\n .description(\"Print a workflow protocol, version-matched to this binary\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n // Every positional here is DATA — a feature id, a service id, a validate\n // target — and one of those targets is spelled `--all`. Without this,\n // commander reads a leading-dash argument as an option it does not have and\n // refuses the whole invocation, so `/loam-check --all` (the first form the\n // command file's own argument hint offers) got a usage error instead of the\n // protocol. `--json` still parses as the flag it is, because it is declared.\n .allowUnknownOption()\n .action((workflow: string | undefined, args: string[], opts: InstructionsOptions) => {\n const json = opts.json === true;\n\n if (workflow === undefined) {\n if (json) {\n emitJson({ command: \"instructions\", version: LOAM_VERSION, workflows: WORKFLOWS });\n return;\n }\n console.log(`loam ${LOAM_VERSION} — workflows\\n`);\n const width = Math.max(...WORKFLOWS.map((w) => w.name.length));\n for (const w of WORKFLOWS) {\n console.log(` ${w.name.padEnd(width)} ${w.description}`);\n }\n console.log(`\\nloam instructions <workflow> [args...] prints one, placeholders filled in.`);\n return;\n }\n\n const body = protocolFor(workflow, args);\n if (body === null) {\n // `unknown-target`, the same code `show` uses for a name that resolves\n // to nothing. The known set is small and closed, so it is listed rather\n // than described: a caller that mistyped one of six names should not\n // have to run a second command to see them.\n fail(\n json,\n \"unknown-target\",\n `No workflow '${workflow}'. Known: ${Object.keys(PROTOCOLS).join(\", \")}.`,\n );\n return;\n }\n\n if (json) {\n // `body` is the substituted text, not the template: a consumer that\n // asked for `loam instructions loam-adopt payment-service --json` wants\n // the protocol it is about to follow, and re-implementing the\n // substitution on the other side is a second copy of the rule.\n emitJson({ command: \"instructions\", version: LOAM_VERSION, workflow, args, body });\n return;\n }\n // `console.log`, like every other command, rather than a raw stdout\n // write: the test harness captures the console and a direct write is\n // invisible to it — an untestable print is how a protocol nobody notices\n // went missing would ship. The body already ends in a newline, so the\n // one console.log adds is trimmed rather than doubled.\n console.log(body.replace(/\\n+$/, \"\"));\n });\n}\n"]}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { loadConfig } from "../core/config.js";
|
|
3
|
+
import { emitJson, fail, repoPath, reportNoConfig } from "../core/json.js";
|
|
4
|
+
import { loadFile, serviceResolver } from "../core/likec4.js";
|
|
5
|
+
import { MATURITY_LADDER, maturityGaps, maturityRollup, serviceMaturity, } from "../core/maturity.js";
|
|
6
|
+
import { DocsRepoUnavailableError, compareIds, landscapePath, listFeatures, listServices, servicePaths, } from "../core/repo.js";
|
|
7
|
+
import { featureChecklist, readVerification, tallyRecord, verificationVerdict, } from "../core/verify.js";
|
|
8
|
+
import { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from "./docs-repo-gate.js";
|
|
9
|
+
const SECTIONS = ["services", "features"];
|
|
10
|
+
export function registerList(program) {
|
|
11
|
+
program
|
|
12
|
+
.command("list")
|
|
13
|
+
.argument("[section]", "services | features (default: both)")
|
|
14
|
+
.description("List the services and features in the docs repo")
|
|
15
|
+
.option("--json", "emit the machine contract instead of the human view")
|
|
16
|
+
.option("--archived", "include archived features")
|
|
17
|
+
.option("--needs-work", "services only: the adoption worklist — every service below `vouched`, with what it is missing")
|
|
18
|
+
.action(async (section, opts) => {
|
|
19
|
+
const json = opts.json === true;
|
|
20
|
+
const wanted = opts.needsWork
|
|
21
|
+
? SECTIONS.filter((s) => s === "services")
|
|
22
|
+
: section
|
|
23
|
+
? SECTIONS.filter((s) => s === section)
|
|
24
|
+
: SECTIONS;
|
|
25
|
+
if (section && !SECTIONS.includes(section)) {
|
|
26
|
+
// `invalid-option`, same as show's bad --type: one mistake class, one code.
|
|
27
|
+
fail(json, "invalid-option", `Unknown section '${section}'. Expected: ${SECTIONS.join(" | ")}.`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (opts.needsWork && section === "features") {
|
|
31
|
+
fail(json, "invalid-option", "--needs-work is the service adoption worklist; drop the 'features' section.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const config = await loadConfig();
|
|
35
|
+
if (!config) {
|
|
36
|
+
reportNoConfig(json);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const { docsDir } = config;
|
|
40
|
+
// A docsDir that is not a docs repo is refused, never rendered as an empty
|
|
41
|
+
// fleet — the whole point of the gate (see docs-repo-gate.ts).
|
|
42
|
+
// `list features` alone does not need services/, so it asks for less.
|
|
43
|
+
if (!docsRepoReady(json, docsDir, wanted.includes("services") ? "services" : "docs"))
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
const services = wanted.includes("services") ? await listServices(docsDir) : undefined;
|
|
47
|
+
const views = services ? await serviceViews(docsDir, services, config.service) : undefined;
|
|
48
|
+
const features = wanted.includes("features")
|
|
49
|
+
? await listFeatures(docsDir, { includeArchived: opts.archived })
|
|
50
|
+
: undefined;
|
|
51
|
+
const verification = features
|
|
52
|
+
? await Promise.all(features.map((f) => featureVerification(docsDir, f)))
|
|
53
|
+
: undefined;
|
|
54
|
+
const worklist = views?.filter((v) => v.maturity !== "vouched");
|
|
55
|
+
if (json) {
|
|
56
|
+
emitJson({
|
|
57
|
+
docsDir,
|
|
58
|
+
...(views
|
|
59
|
+
? {
|
|
60
|
+
services: (opts.needsWork ? worklist : views).map((v) => serviceJson(docsDir, v)),
|
|
61
|
+
maturity: maturityRollup(views),
|
|
62
|
+
}
|
|
63
|
+
: {}),
|
|
64
|
+
...(features
|
|
65
|
+
? { features: features.map((f, i) => featureJson(docsDir, f, verification[i] ?? null)) }
|
|
66
|
+
: {}),
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (opts.needsWork) {
|
|
71
|
+
printWorklist(worklist, views.length);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (views)
|
|
75
|
+
printServices(views);
|
|
76
|
+
if (views && features)
|
|
77
|
+
console.log("");
|
|
78
|
+
if (features)
|
|
79
|
+
printFeatures(features, verification);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
if (err instanceof DocsRepoUnavailableError) {
|
|
83
|
+
reportDocsRepoError(json, err);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// One unreadable file used to escape as a stack trace (`internal` in
|
|
87
|
+
// --json), naming nothing. The enumeration is all-or-nothing — it reads
|
|
88
|
+
// every service's frontmatter to build one table — so the honest answer
|
|
89
|
+
// is a refusal that says WHICH path could not be read.
|
|
90
|
+
reportRepositoryUnavailable(json, err, "the listing would be missing a service", docsDir);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Read the living landscape ONCE and decide, per service, whether an API is
|
|
96
|
+
* expected of it. `serviceResolver` is handed the real service ids so an edge
|
|
97
|
+
* drawn into a modelled container (`paymentService.api`) counts for the service
|
|
98
|
+
* that owns it — the same resolution `validate` uses, so the two commands
|
|
99
|
+
* cannot disagree about who is called.
|
|
100
|
+
*/
|
|
101
|
+
async function serviceViews(docsDir, services, boundService) {
|
|
102
|
+
const known = new Set(services.map((s) => s.id));
|
|
103
|
+
const lp = landscapePath(docsDir);
|
|
104
|
+
const land = existsSync(lp) ? await loadFile(lp) : null;
|
|
105
|
+
const called = new Set();
|
|
106
|
+
// Positive evidence only: with no landscape, or one that does not parse, we
|
|
107
|
+
// know nothing about who is called and every service is assumed to have an
|
|
108
|
+
// API — the conservative direction, because claiming a service needs no
|
|
109
|
+
// contract is how a real one goes undocumented.
|
|
110
|
+
const proven = land !== null && land.errors.length === 0;
|
|
111
|
+
if (proven) {
|
|
112
|
+
const svcOf = serviceResolver(land.elements, known);
|
|
113
|
+
for (const r of land.relationships) {
|
|
114
|
+
if (r.op !== undefined)
|
|
115
|
+
called.add(svcOf(r.target));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return services.map((entry) => {
|
|
119
|
+
const archSpec = existsSync(servicePaths(docsDir, entry.id).archSpec);
|
|
120
|
+
const apiExpected = !proven || called.has(entry.id);
|
|
121
|
+
const view = {
|
|
122
|
+
entry,
|
|
123
|
+
archSpec,
|
|
124
|
+
apiExpected,
|
|
125
|
+
unverifiableFromHere: entry.sources.declared && boundService !== entry.id,
|
|
126
|
+
};
|
|
127
|
+
const missing = maturityGaps(view);
|
|
128
|
+
return { ...view, maturity: serviceMaturity(view), missing };
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/* ------------------------------------------------------------------ */
|
|
132
|
+
/* JSON */
|
|
133
|
+
/* ------------------------------------------------------------------ */
|
|
134
|
+
function serviceJson(docsDir, v) {
|
|
135
|
+
const s = v.entry;
|
|
136
|
+
return {
|
|
137
|
+
id: s.id,
|
|
138
|
+
path: repoPath(docsDir, s.dir),
|
|
139
|
+
has: { ...s.has, archSpec: v.archSpec },
|
|
140
|
+
adrs: s.adrs,
|
|
141
|
+
status: s.status,
|
|
142
|
+
maturity: v.maturity,
|
|
143
|
+
missing: v.missing,
|
|
144
|
+
apiExpected: v.apiExpected,
|
|
145
|
+
// Never omitted when true: a consumer that filters this table into a
|
|
146
|
+
// worklist has to be able to tell "checked and fine" from "not checkable
|
|
147
|
+
// from here", and absence reads as the first one.
|
|
148
|
+
...(v.unverifiableFromHere ? { provenance: "unverifiable-from-here" } : {}),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function featureJson(docsDir, f, verification) {
|
|
152
|
+
return {
|
|
153
|
+
id: f.id,
|
|
154
|
+
dirName: f.dirName,
|
|
155
|
+
path: repoPath(docsDir, f.dir),
|
|
156
|
+
archived: f.archived,
|
|
157
|
+
services: f.services,
|
|
158
|
+
has: f.has,
|
|
159
|
+
verification,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The verification column. `readVerification` is one file read; the checklist
|
|
164
|
+
* is derived only when there is a record to judge — on a fleet-sized repo most
|
|
165
|
+
* features have none, and deriving N checklists to print N dashes is the cost
|
|
166
|
+
* that would get this column dropped.
|
|
167
|
+
*
|
|
168
|
+
* An archived feature's record is frozen history (see verify.ts): archive
|
|
169
|
+
* merged the feature's operations into the living openapi, so a re-derived
|
|
170
|
+
* checklist can only mismatch — the record is reported as recorded, never
|
|
171
|
+
* judged stale.
|
|
172
|
+
*/
|
|
173
|
+
async function featureVerification(docsDir, f) {
|
|
174
|
+
const v = await readVerification(f.dir);
|
|
175
|
+
if (v === null)
|
|
176
|
+
return null;
|
|
177
|
+
const stale = f.archived
|
|
178
|
+
? false
|
|
179
|
+
: (await featureChecklist(docsDir, f.dir, f.id)).digest !== v.checklist;
|
|
180
|
+
const tally = tallyRecord(v);
|
|
181
|
+
return {
|
|
182
|
+
state: stale ? "stale" : "recorded",
|
|
183
|
+
recorded: v.recorded,
|
|
184
|
+
verdict: verificationVerdict(tally, stale),
|
|
185
|
+
confirmed: tally.confirmed,
|
|
186
|
+
claims: tally.claims,
|
|
187
|
+
attested: tally.attested,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/* ------------------------------------------------------------------ */
|
|
191
|
+
/* Text */
|
|
192
|
+
/* ------------------------------------------------------------------ */
|
|
193
|
+
/**
|
|
194
|
+
* Fixed-width presence flags. `a` and `A` are deliberately the same letter in
|
|
195
|
+
* two cases: the arch spec and the API are the two things a reader most often
|
|
196
|
+
* mistakes for each other, and one is lowercase because it is optional.
|
|
197
|
+
*/
|
|
198
|
+
function serviceFlags(v) {
|
|
199
|
+
const s = v.entry;
|
|
200
|
+
return [
|
|
201
|
+
s.has.model ? "M" : "-",
|
|
202
|
+
s.has.spec ? "S" : "-",
|
|
203
|
+
v.archSpec ? "a" : "-",
|
|
204
|
+
s.has.openapi ? "A" : "-",
|
|
205
|
+
s.has.runbook ? "R" : "-",
|
|
206
|
+
s.has.health ? "H" : "-",
|
|
207
|
+
].join(" ");
|
|
208
|
+
}
|
|
209
|
+
function printServices(views) {
|
|
210
|
+
console.log(`services (${views.length}) [M]odel [S]pec [a]rch-spec [A]pi [R]unbook [H]ealth`);
|
|
211
|
+
const width = Math.max(0, ...views.map((v) => v.entry.id.length));
|
|
212
|
+
const rungWidth = Math.max(0, ...views.map((v) => v.maturity.length));
|
|
213
|
+
for (const v of views) {
|
|
214
|
+
const s = v.entry;
|
|
215
|
+
const adrs = s.adrs > 0 ? ` (${s.adrs} adr${s.adrs === 1 ? "" : "s"})` : "";
|
|
216
|
+
// The rung per service, not only in the rollup: "12 partial" tells a reader
|
|
217
|
+
// the fleet is unfinished and nothing about which twelve, which is the one
|
|
218
|
+
// question the line is read to answer.
|
|
219
|
+
console.log(` ${serviceFlags(v)} ${s.id.padEnd(width)} ${v.maturity.padEnd(rungWidth)}${adrs}`.trimEnd());
|
|
220
|
+
}
|
|
221
|
+
// How much of the fleet anyone has actually vouched for. On 100+ services this
|
|
222
|
+
// is the number that says whether the docs can be trusted at all.
|
|
223
|
+
if (views.length > 0) {
|
|
224
|
+
const counted = new Map();
|
|
225
|
+
for (const v of views) {
|
|
226
|
+
const key = v.entry.status ?? "unmarked";
|
|
227
|
+
counted.set(key, (counted.get(key) ?? 0) + 1);
|
|
228
|
+
}
|
|
229
|
+
const parts = [...counted.entries()]
|
|
230
|
+
.sort((a, b) => compareIds(a[0], b[0]))
|
|
231
|
+
.map(([status, n]) => `${n} ${status}`);
|
|
232
|
+
console.log(` status: ${parts.join(" · ")}`);
|
|
233
|
+
// The campaign dial next to the trust dial: rungs in ladder order, so the
|
|
234
|
+
// line reads as progress left to right. Presence and provenance state only
|
|
235
|
+
// — completeness is unchecked, so this line never says "adopted".
|
|
236
|
+
const rollup = maturityRollup(views);
|
|
237
|
+
const rungs = MATURITY_LADDER.filter((m) => rollup[m] > 0).map((m) => `${rollup[m]} ${m}`);
|
|
238
|
+
console.log(` maturity: ${rungs.join(" · ")}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The one-shot adoption worklist. Onboarding ten legacy services means asking
|
|
243
|
+
* "what is left" once a day for a month, and the answer used to require reading
|
|
244
|
+
* a presence table and a rollup and joining them by eye. A service whose
|
|
245
|
+
* provenance cannot be judged from here is LISTED, tagged, never dropped:
|
|
246
|
+
* omitting it would read as done.
|
|
247
|
+
*/
|
|
248
|
+
function printWorklist(views, total) {
|
|
249
|
+
if (views.length === 0) {
|
|
250
|
+
console.log(`nothing to do — all ${total} service(s) are vouched`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
console.log(`${views.length} of ${total} service(s) need work`);
|
|
254
|
+
const width = Math.max(0, ...views.map((v) => v.entry.id.length));
|
|
255
|
+
const rungWidth = Math.max(0, ...views.map((v) => v.maturity.length));
|
|
256
|
+
for (const v of views) {
|
|
257
|
+
const note = v.unverifiableFromHere ? " (provenance: unverifiable-from-here)" : "";
|
|
258
|
+
console.log(` ${v.entry.id.padEnd(width)} ${v.maturity.padEnd(rungWidth)} missing: ${v.missing.join(", ")}${note}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/** Narrow verification cell: confirmed/claims when a record answers, one word when it does not. */
|
|
262
|
+
function verificationMark(v) {
|
|
263
|
+
if (v === null)
|
|
264
|
+
return "-";
|
|
265
|
+
if (v.state === "stale")
|
|
266
|
+
return "stale";
|
|
267
|
+
// `11/11` and `11/11 attested` are different facts — the first is a
|
|
268
|
+
// digest-matched green run, the second is somebody's word about one. Without
|
|
269
|
+
// the suffix a complete count is the same glyph either way, which is exactly
|
|
270
|
+
// the reading `verify`, `status` and the JSON envelope stopped giving.
|
|
271
|
+
return `${v.confirmed}/${v.claims}${v.verdict === "attested" ? " attested" : ""}`;
|
|
272
|
+
}
|
|
273
|
+
function printFeatures(features, verification) {
|
|
274
|
+
const active = features.filter((f) => !f.archived).length;
|
|
275
|
+
const archived = features.length - active;
|
|
276
|
+
const counts = `${active} active${archived > 0 ? `, ${archived} archived` : ""}`;
|
|
277
|
+
// Not `verified`: the column reports three verdicts and only one of them is
|
|
278
|
+
// that one. A heading that names the strong verdict makes every other cell
|
|
279
|
+
// read as it.
|
|
280
|
+
console.log(`features (${counts}) [I]ntent [D]elta verification`);
|
|
281
|
+
const width = Math.max(0, ...features.map((f) => f.id.length));
|
|
282
|
+
const cells = verification.map(verificationMark);
|
|
283
|
+
const cellWidth = Math.max(0, ...cells.map((c) => c.length));
|
|
284
|
+
for (const [i, f] of features.entries()) {
|
|
285
|
+
const flags = `${f.has.intent ? "I" : "-"} ${f.has.delta ? "D" : "-"}`;
|
|
286
|
+
const svcs = f.services.length > 0 ? f.services.join(", ") : "—";
|
|
287
|
+
const tag = f.archived ? " (archived)" : "";
|
|
288
|
+
console.log(` ${flags} ${f.id.padEnd(width)} ${cells[i].padEnd(cellWidth)} ${svcs}${tag}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Command } from \"commander\";\nimport { existsSync } from \"node:fs\";\nimport { loadConfig } from \"../core/config.js\";\nimport { emitJson, fail, repoPath, reportNoConfig } from \"../core/json.js\";\nimport { loadFile, serviceResolver } from \"../core/likec4.js\";\nimport {\n MATURITY_LADDER,\n maturityGaps,\n maturityRollup,\n serviceMaturity,\n type Maturity,\n type MaturityInput,\n} from \"../core/maturity.js\";\nimport {\n DocsRepoUnavailableError,\n compareIds,\n landscapePath,\n listFeatures,\n listServices,\n servicePaths,\n type FeatureEntry,\n type ServiceEntry,\n} from \"../core/repo.js\";\nimport {\n featureChecklist,\n readVerification,\n tallyRecord,\n verificationVerdict,\n type VerificationVerdict,\n} from \"../core/verify.js\";\nimport { docsRepoReady, reportDocsRepoError, reportRepositoryUnavailable } from \"./docs-repo-gate.js\";\n\ntype Section = \"services\" | \"features\";\nconst SECTIONS: Section[] = [\"services\", \"features\"];\n\ninterface ListOptions {\n json?: boolean;\n archived?: boolean;\n needsWork?: boolean;\n}\n\nexport function registerList(program: Command): void {\n program\n .command(\"list\")\n .argument(\"[section]\", \"services | features (default: both)\")\n .description(\"List the services and features in the docs repo\")\n .option(\"--json\", \"emit the machine contract instead of the human view\")\n .option(\"--archived\", \"include archived features\")\n .option(\n \"--needs-work\",\n \"services only: the adoption worklist — every service below `vouched`, with what it is missing\",\n )\n .action(async (section: string | undefined, opts: ListOptions) => {\n const json = opts.json === true;\n const wanted = opts.needsWork\n ? SECTIONS.filter((s) => s === \"services\")\n : section\n ? SECTIONS.filter((s) => s === section)\n : SECTIONS;\n if (section && !SECTIONS.includes(section as Section)) {\n // `invalid-option`, same as show's bad --type: one mistake class, one code.\n fail(json, \"invalid-option\", `Unknown section '${section}'. Expected: ${SECTIONS.join(\" | \")}.`);\n return;\n }\n if (opts.needsWork && section === \"features\") {\n fail(json, \"invalid-option\", \"--needs-work is the service adoption worklist; drop the 'features' section.\");\n return;\n }\n\n const config = await loadConfig();\n if (!config) {\n reportNoConfig(json);\n return;\n }\n const { docsDir } = config;\n // A docsDir that is not a docs repo is refused, never rendered as an empty\n // fleet — the whole point of the gate (see docs-repo-gate.ts).\n // `list features` alone does not need services/, so it asks for less.\n if (!docsRepoReady(json, docsDir, wanted.includes(\"services\") ? \"services\" : \"docs\")) return;\n\n try {\n const services = wanted.includes(\"services\") ? await listServices(docsDir) : undefined;\n const views = services ? await serviceViews(docsDir, services, config.service) : undefined;\n const features = wanted.includes(\"features\")\n ? await listFeatures(docsDir, { includeArchived: opts.archived })\n : undefined;\n const verification = features\n ? await Promise.all(features.map((f) => featureVerification(docsDir, f)))\n : undefined;\n\n const worklist = views?.filter((v) => v.maturity !== \"vouched\");\n\n if (json) {\n emitJson({\n docsDir,\n ...(views\n ? {\n services: (opts.needsWork ? worklist! : views).map((v) => serviceJson(docsDir, v)),\n maturity: maturityRollup(views),\n }\n : {}),\n ...(features\n ? { features: features.map((f, i) => featureJson(docsDir, f, verification![i] ?? null)) }\n : {}),\n });\n return;\n }\n\n if (opts.needsWork) {\n printWorklist(worklist!, views!.length);\n return;\n }\n if (views) printServices(views);\n if (views && features) console.log(\"\");\n if (features) printFeatures(features, verification!);\n } catch (err) {\n if (err instanceof DocsRepoUnavailableError) {\n reportDocsRepoError(json, err);\n return;\n }\n // One unreadable file used to escape as a stack trace (`internal` in\n // --json), naming nothing. The enumeration is all-or-nothing — it reads\n // every service's frontmatter to build one table — so the honest answer\n // is a refusal that says WHICH path could not be read.\n reportRepositoryUnavailable(json, err, \"the listing would be missing a service\", docsDir);\n }\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* The per-service view */\n/* ------------------------------------------------------------------ */\n\n/**\n * The graded view: the rung's own inputs (core/maturity.ts) plus the one fact\n * only this command knows — whether anyone standing here could check the\n * service's `sources`, which needs loam.json's binding, not the directory.\n */\ninterface ServiceView extends MaturityInput {\n /** True when this service's `sources` can only be resolved from its own repo. */\n unverifiableFromHere: boolean;\n maturity: Maturity;\n /** What stands between this service and the next rung, in fix order. */\n missing: string[];\n}\n\n/**\n * Read the living landscape ONCE and decide, per service, whether an API is\n * expected of it. `serviceResolver` is handed the real service ids so an edge\n * drawn into a modelled container (`paymentService.api`) counts for the service\n * that owns it — the same resolution `validate` uses, so the two commands\n * cannot disagree about who is called.\n */\nasync function serviceViews(\n docsDir: string,\n services: ServiceEntry[],\n boundService: string | undefined,\n): Promise<ServiceView[]> {\n const known = new Set(services.map((s) => s.id));\n const lp = landscapePath(docsDir);\n const land = existsSync(lp) ? await loadFile(lp) : null;\n const called = new Set<string>();\n // Positive evidence only: with no landscape, or one that does not parse, we\n // know nothing about who is called and every service is assumed to have an\n // API — the conservative direction, because claiming a service needs no\n // contract is how a real one goes undocumented.\n const proven = land !== null && land.errors.length === 0;\n if (proven) {\n const svcOf = serviceResolver(land.elements, known);\n for (const r of land.relationships) {\n if (r.op !== undefined) called.add(svcOf(r.target));\n }\n }\n\n return services.map((entry) => {\n const archSpec = existsSync(servicePaths(docsDir, entry.id).archSpec);\n const apiExpected = !proven || called.has(entry.id);\n const view = {\n entry,\n archSpec,\n apiExpected,\n unverifiableFromHere: entry.sources.declared && boundService !== entry.id,\n };\n const missing = maturityGaps(view);\n return { ...view, maturity: serviceMaturity(view), missing };\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* JSON */\n/* ------------------------------------------------------------------ */\n\nfunction serviceJson(docsDir: string, v: ServiceView): Record<string, unknown> {\n const s = v.entry;\n return {\n id: s.id,\n path: repoPath(docsDir, s.dir),\n has: { ...s.has, archSpec: v.archSpec },\n adrs: s.adrs,\n status: s.status,\n maturity: v.maturity,\n missing: v.missing,\n apiExpected: v.apiExpected,\n // Never omitted when true: a consumer that filters this table into a\n // worklist has to be able to tell \"checked and fine\" from \"not checkable\n // from here\", and absence reads as the first one.\n ...(v.unverifiableFromHere ? { provenance: \"unverifiable-from-here\" } : {}),\n };\n}\n\nfunction featureJson(\n docsDir: string,\n f: FeatureEntry,\n verification: VerificationCell | null,\n): Record<string, unknown> {\n return {\n id: f.id,\n dirName: f.dirName,\n path: repoPath(docsDir, f.dir),\n archived: f.archived,\n services: f.services,\n has: f.has,\n verification,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Verification */\n/* ------------------------------------------------------------------ */\n\n/** What the features table says about verification without N `loam verify` runs. */\ninterface VerificationCell {\n state: \"recorded\" | \"stale\";\n /** The day the record was written. */\n recorded: string;\n /**\n * `verify --json`'s own three-valued verdict, through `verificationVerdict` —\n * not a fourth opinion computed here. `attested` is the one this column got\n * wrong: a scenario confirmed on an agent's word printed as `11/11` under a\n * heading that said `verified`, and `list --json` is how the fleet is graded.\n */\n verdict: VerificationVerdict;\n /** Both counts are `tallyRecord`'s — the record's `claims[]`, never its `summary:` block. */\n confirmed: number;\n claims: number;\n /** Of the confirmed, the scenario claims on an agent's word (`attestedClaims`). */\n attested: number;\n}\n\n/**\n * The verification column. `readVerification` is one file read; the checklist\n * is derived only when there is a record to judge — on a fleet-sized repo most\n * features have none, and deriving N checklists to print N dashes is the cost\n * that would get this column dropped.\n *\n * An archived feature's record is frozen history (see verify.ts): archive\n * merged the feature's operations into the living openapi, so a re-derived\n * checklist can only mismatch — the record is reported as recorded, never\n * judged stale.\n */\nasync function featureVerification(\n docsDir: string,\n f: FeatureEntry,\n): Promise<VerificationCell | null> {\n const v = await readVerification(f.dir);\n if (v === null) return null;\n const stale = f.archived\n ? false\n : (await featureChecklist(docsDir, f.dir, f.id)).digest !== v.checklist;\n const tally = tallyRecord(v);\n return {\n state: stale ? \"stale\" : \"recorded\",\n recorded: v.recorded,\n verdict: verificationVerdict(tally, stale),\n confirmed: tally.confirmed,\n claims: tally.claims,\n attested: tally.attested,\n };\n}\n\n/* ------------------------------------------------------------------ */\n/* Text */\n/* ------------------------------------------------------------------ */\n\n/**\n * Fixed-width presence flags. `a` and `A` are deliberately the same letter in\n * two cases: the arch spec and the API are the two things a reader most often\n * mistakes for each other, and one is lowercase because it is optional.\n */\nfunction serviceFlags(v: ServiceView): string {\n const s = v.entry;\n return [\n s.has.model ? \"M\" : \"-\",\n s.has.spec ? \"S\" : \"-\",\n v.archSpec ? \"a\" : \"-\",\n s.has.openapi ? \"A\" : \"-\",\n s.has.runbook ? \"R\" : \"-\",\n s.has.health ? \"H\" : \"-\",\n ].join(\" \");\n}\n\nfunction printServices(views: ServiceView[]): void {\n console.log(\n `services (${views.length}) [M]odel [S]pec [a]rch-spec [A]pi [R]unbook [H]ealth`,\n );\n const width = Math.max(0, ...views.map((v) => v.entry.id.length));\n const rungWidth = Math.max(0, ...views.map((v) => v.maturity.length));\n for (const v of views) {\n const s = v.entry;\n const adrs = s.adrs > 0 ? ` (${s.adrs} adr${s.adrs === 1 ? \"\" : \"s\"})` : \"\";\n // The rung per service, not only in the rollup: \"12 partial\" tells a reader\n // the fleet is unfinished and nothing about which twelve, which is the one\n // question the line is read to answer.\n console.log(\n ` ${serviceFlags(v)} ${s.id.padEnd(width)} ${v.maturity.padEnd(rungWidth)}${adrs}`.trimEnd(),\n );\n }\n // How much of the fleet anyone has actually vouched for. On 100+ services this\n // is the number that says whether the docs can be trusted at all.\n if (views.length > 0) {\n const counted = new Map<string, number>();\n for (const v of views) {\n const key = v.entry.status ?? \"unmarked\";\n counted.set(key, (counted.get(key) ?? 0) + 1);\n }\n const parts = [...counted.entries()]\n .sort((a, b) => compareIds(a[0], b[0]))\n .map(([status, n]) => `${n} ${status}`);\n console.log(` status: ${parts.join(\" · \")}`);\n // The campaign dial next to the trust dial: rungs in ladder order, so the\n // line reads as progress left to right. Presence and provenance state only\n // — completeness is unchecked, so this line never says \"adopted\".\n const rollup = maturityRollup(views);\n const rungs = MATURITY_LADDER.filter((m) => rollup[m] > 0).map((m) => `${rollup[m]} ${m}`);\n console.log(` maturity: ${rungs.join(\" · \")}`);\n }\n}\n\n/**\n * The one-shot adoption worklist. Onboarding ten legacy services means asking\n * \"what is left\" once a day for a month, and the answer used to require reading\n * a presence table and a rollup and joining them by eye. A service whose\n * provenance cannot be judged from here is LISTED, tagged, never dropped:\n * omitting it would read as done.\n */\nfunction printWorklist(views: ServiceView[], total: number): void {\n if (views.length === 0) {\n console.log(`nothing to do — all ${total} service(s) are vouched`);\n return;\n }\n console.log(`${views.length} of ${total} service(s) need work`);\n const width = Math.max(0, ...views.map((v) => v.entry.id.length));\n const rungWidth = Math.max(0, ...views.map((v) => v.maturity.length));\n for (const v of views) {\n const note = v.unverifiableFromHere ? \" (provenance: unverifiable-from-here)\" : \"\";\n console.log(\n ` ${v.entry.id.padEnd(width)} ${v.maturity.padEnd(rungWidth)} missing: ${v.missing.join(\", \")}${note}`,\n );\n }\n}\n\n/** Narrow verification cell: confirmed/claims when a record answers, one word when it does not. */\nfunction verificationMark(v: VerificationCell | null): string {\n if (v === null) return \"-\";\n if (v.state === \"stale\") return \"stale\";\n // `11/11` and `11/11 attested` are different facts — the first is a\n // digest-matched green run, the second is somebody's word about one. Without\n // the suffix a complete count is the same glyph either way, which is exactly\n // the reading `verify`, `status` and the JSON envelope stopped giving.\n return `${v.confirmed}/${v.claims}${v.verdict === \"attested\" ? \" attested\" : \"\"}`;\n}\n\nfunction printFeatures(features: FeatureEntry[], verification: (VerificationCell | null)[]): void {\n const active = features.filter((f) => !f.archived).length;\n const archived = features.length - active;\n const counts = `${active} active${archived > 0 ? `, ${archived} archived` : \"\"}`;\n // Not `verified`: the column reports three verdicts and only one of them is\n // that one. A heading that names the strong verdict makes every other cell\n // read as it.\n console.log(`features (${counts}) [I]ntent [D]elta verification`);\n const width = Math.max(0, ...features.map((f) => f.id.length));\n const cells = verification.map(verificationMark);\n const cellWidth = Math.max(0, ...cells.map((c) => c.length));\n for (const [i, f] of features.entries()) {\n const flags = `${f.has.intent ? \"I\" : \"-\"} ${f.has.delta ? \"D\" : \"-\"}`;\n const svcs = f.services.length > 0 ? f.services.join(\", \") : \"—\";\n const tag = f.archived ? \" (archived)\" : \"\";\n console.log(` ${flags} ${f.id.padEnd(width)} ${cells[i]!.padEnd(cellWidth)} ${svcs}${tag}`);\n }\n}\n"]}
|