@stjbrown/agent-knowledge 0.1.0-beta.8 → 0.1.0-beta.9

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/dist/headless.js CHANGED
@@ -3,8 +3,8 @@ import { createRequire as __janetCreateRequire } from "node:module";
3
3
  const require = __janetCreateRequire(import.meta.url);
4
4
  import {
5
5
  runHeadless
6
- } from "./chunk-3O3M2KG6.js";
7
- import "./chunk-K2TO22OI.js";
6
+ } from "./chunk-4C5RB7PY.js";
7
+ import "./chunk-S237DEGI.js";
8
8
  export {
9
9
  runHeadless
10
10
  };
package/dist/index.js CHANGED
@@ -3,11 +3,11 @@ import { createRequire as __janetCreateRequire } from "node:module";
3
3
  const require = __janetCreateRequire(import.meta.url);
4
4
  import {
5
5
  runHeadless
6
- } from "./chunk-3O3M2KG6.js";
6
+ } from "./chunk-4C5RB7PY.js";
7
7
  import {
8
8
  bootJanet,
9
9
  resolveProjectPaths
10
- } from "./chunk-K2TO22OI.js";
10
+ } from "./chunk-S237DEGI.js";
11
11
  export {
12
12
  bootJanet,
13
13
  resolveProjectPaths,
package/dist/main.js CHANGED
@@ -3,17 +3,15 @@ import { createRequire as __janetCreateRequire } from "node:module";
3
3
  const require = __janetCreateRequire(import.meta.url);
4
4
  import {
5
5
  runHeadless
6
- } from "./chunk-3O3M2KG6.js";
7
- import {
8
- availableModels,
9
- normalizeModelSelection
10
- } from "./chunk-UIDPQHJI.js";
6
+ } from "./chunk-4C5RB7PY.js";
11
7
  import {
12
8
  GREETING,
9
+ availableModels,
13
10
  loadSettings,
11
+ normalizeModelSelection,
14
12
  packageVersion,
15
13
  resolveProjectPaths
16
- } from "./chunk-K2TO22OI.js";
14
+ } from "./chunk-S237DEGI.js";
17
15
 
18
16
  // src/main.ts
19
17
  import { existsSync as existsSync2 } from "fs";
@@ -299,7 +297,7 @@ async function main(argv) {
299
297
  const sub = parsed.subcommand;
300
298
  if (!sub) {
301
299
  if (!headless) {
302
- const { runTui } = await import("./tui-WYFA6QZ2.js");
300
+ const { runTui } = await import("./tui-VQA6AJPW.js");
303
301
  if (modelId && !process.env["JANET_MODEL"]) process.env["JANET_MODEL"] = modelId;
304
302
  return runTui({ dir, bundle: bundleOverride, threadId });
305
303
  }
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/main.ts","../../kb-tools/src/conformance.ts","../../kb-tools/src/shared.ts","../../kb-tools/src/graph.ts","../src/headless/flags.ts","../src/commands.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { checkConformance, formatReport } from \"@agent-knowledge/kb-tools\";\nimport { loadSettings } from \"./onboarding/settings.js\";\nimport { availableModels, normalizeModelSelection } from \"./onboarding/providers.js\";\nimport { parseArgs } from \"./headless/flags.js\";\nimport { runHeadless } from \"./headless/run.js\";\nimport {\n buildDirective,\n commandExitCode,\n headlessCapabilities,\n isSubcommand,\n} from \"./commands.js\";\nimport { resolveProjectPaths } from \"./agent/paths.js\";\nimport { GREETING } from \"./agent/persona.js\";\nimport { packageVersion } from \"./version.js\";\n\nconst HELP = `${GREETING}\n\nUsage:\n janet Start an interactive session (chat with Janet)\n janet init Scaffold a new knowledge/ bundle here\n janet ingest <src...> Ingest source(s) into the bundle\n janet query \"<question>\" Answer from the bundle, with citations\n janet lint [--fix] Health-check the bundle (conformance + drift)\n janet viz [scope] Render the bundle as a graph\n\nOptions:\n -C, --dir <path> Operate on <path> instead of the current directory\n --bundle <path> Bundle location within <dir> (default: knowledge)\n -p, --print Headless: stream to stdout and exit\n --model <provider/id> Model to use (or set JANET_MODEL)\n --thread <id> Resume a thread\n --allow-exec Allow shell commands in a one-shot run\n -h, --help Show this help\n -v, --version Show version\n\nAlso installed as \\`ding\\` (you summon Janet with a ding).`;\n\nfunction resolveModelId(values: Record<string, string>): string | undefined {\n const selected =\n values[\"model\"] ??\n process.env[\"JANET_MODEL\"] ??\n loadSettings().defaultModelId ??\n undefined;\n return selected ? normalizeModelSelection(selected, availableModels()) : undefined;\n}\n\nasync function main(argv: string[]): Promise<number> {\n const parsed = parseArgs(argv);\n\n if (parsed.flags.has(\"help\") || parsed.subcommand === \"help\") {\n process.stdout.write(HELP + \"\\n\");\n return 0;\n }\n if (parsed.flags.has(\"version\")) {\n process.stdout.write(packageVersion() + \"\\n\");\n return 0;\n }\n\n const dir = parsed.values[\"dir\"];\n const bundleOverride = parsed.values[\"bundle\"];\n const paths = resolveProjectPaths({ dir, bundle: bundleOverride });\n const modelId = resolveModelId(parsed.values);\n const threadId = parsed.values[\"thread\"] ?? parsed.values[\"resume\"];\n const headless = parsed.flags.has(\"print\") || !process.stdout.isTTY;\n\n const sub = parsed.subcommand;\n\n // No subcommand → interactive TUI (chat).\n if (!sub) {\n if (!headless) {\n const { runTui } = await import(\"./tui/index.js\");\n if (modelId && !process.env[\"JANET_MODEL\"]) process.env[\"JANET_MODEL\"] = modelId;\n return runTui({ dir, bundle: bundleOverride, threadId });\n }\n process.stderr.write(\"No subcommand. Try `janet --help`.\\n\");\n return 2;\n }\n\n if (!isSubcommand(sub)) {\n process.stderr.write(`Unknown command: ${sub}\\nTry \\`janet --help\\`.\\n`);\n return 2;\n }\n\n // `lint` runs the deterministic conformance check in-process first (no tokens,\n // CI-gateable), then hands the drift audit to the agent.\n let conformanceErrors = 0;\n if (sub === \"lint\") {\n if (!existsSync(paths.bundlePath)) {\n process.stderr.write(\n `No bundle at ${paths.bundlePath}. Run \\`janet init\\` to scaffold one.\\n`,\n );\n return 2;\n }\n const report = checkConformance(paths.bundlePath);\n conformanceErrors = report.errors.length;\n process.stdout.write(formatReport(report) + \"\\n\");\n // If no model is configured, stop after the deterministic pass (still useful\n // and exit-coded for CI).\n if (!modelId) {\n process.stdout.write(\n \"\\n(No model configured — ran the deterministic conformance pass only. \" +\n \"Set --model or JANET_MODEL for the drift audit.)\\n\",\n );\n return report.errors.length ? 1 : 0;\n }\n }\n\n // Bundle must exist for ingest/query/lint/viz (init creates it).\n if (sub !== \"init\" && !existsSync(paths.bundlePath)) {\n process.stderr.write(\n `No bundle at ${paths.bundlePath}. Run \\`janet init\\` to scaffold one.\\n`,\n );\n return 2;\n }\n\n const directive = buildDirective(sub, {\n bundlePath: paths.bundlePath,\n args: parsed.positionals,\n flags: parsed.flags,\n });\n const capabilities = headlessCapabilities(sub, parsed.flags);\n\n const result = await runHeadless({\n message: directive,\n dir,\n bundle: bundleOverride,\n modelId,\n threadId,\n operation: sub,\n ...capabilities,\n });\n return commandExitCode(sub, result.exitCode, conformanceErrors);\n}\n\nmain(process.argv.slice(2))\n .then((code) => process.exit(code))\n .catch((err) => {\n process.stderr.write(`\\nJanet hit a snag: ${err?.message ?? err}\\n`);\n process.exit(1);\n });\n","/**\n * Deterministic OKF v0.1 conformance check for a knowledge bundle (SPEC §9).\n *\n * A TypeScript port of skills/kb-lint/scripts/conformance.py, behaviour-identical.\n * ERRORs fail conformance; broken links and soft-guidance issues are WARN and\n * never fail (SPEC §5.3 / §9 — consumers MUST tolerate them). Structure only;\n * drift is the fuzzy, agent-driven half of kb-lint.\n */\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n RESERVED,\n collectMarkdown,\n frontmatter,\n normalizePosix,\n parseYamlFrontmatter,\n pythonJson,\n} from \"./shared.js\";\n\nconst HEADING_LOG_RE = /^##\\s+(.+?)\\s*$/gm;\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst LINK_RE = /\\]\\(([^)#\\s]+\\.md)(#[^)]*)?\\)/g;\n\nexport interface ConformanceReport {\n bundle: string;\n concepts: number;\n files: number;\n errors: string[];\n warnings: string[];\n}\n\nexport function checkConformance(bundle: string): ConformanceReport {\n const errors: string[] = [];\n const warnings: string[] = [];\n const md = collectMarkdown(bundle);\n\n const posixBasename = (rel: string): string => rel.split(\"/\").pop() ?? rel;\n\n for (const rel of [...md].sort()) {\n const text = readFileSync(join(bundle, rel), \"utf-8\");\n const base = posixBasename(rel);\n const fm = frontmatter(text);\n\n if (RESERVED.has(base)) {\n // Reserved files carry no frontmatter, except the ROOT index.md may\n // declare okf_version (SPEC §6/§11).\n if (fm !== null) {\n const isRootIndex = rel === \"index.md\";\n const parsed = parseYamlFrontmatter(fm);\n if (\n !isRootIndex ||\n parsed.errors.length > 0 ||\n !parsed.data ||\n !Object.prototype.hasOwnProperty.call(parsed.data, \"okf_version\")\n ) {\n errors.push(`${rel}: reserved file must not carry frontmatter`);\n }\n }\n if (base === \"log.md\") {\n for (const m of text.matchAll(HEADING_LOG_RE)) {\n if (!ISO_DATE_RE.test(m[1]!)) {\n warnings.push(`${rel}: log date heading not ISO 8601: '${m[1]}'`);\n }\n }\n }\n continue;\n }\n\n // Concept document: rules 1 & 2.\n if (fm === null) {\n errors.push(`${rel}: concept has no parseable frontmatter`);\n continue;\n }\n const parsed = parseYamlFrontmatter(fm);\n if (parsed.errors.length > 0 || !parsed.data) {\n errors.push(`${rel}: concept has no parseable frontmatter`);\n continue;\n }\n const type = parsed.data[\"type\"];\n if (typeof type !== \"string\" || !type.trim()) {\n errors.push(`${rel}: missing or empty required 'type'`);\n }\n }\n\n // Broken relative links → WARN only (never a conformance failure).\n for (const rel of md) {\n const srcdir = dirname(rel) === \".\" ? \"\" : dirname(rel);\n const text = readFileSync(join(bundle, rel), \"utf-8\");\n for (const m of text.matchAll(LINK_RE)) {\n const tgt = m[1]!;\n if (tgt.includes(\"://\")) continue;\n const resolved = tgt.startsWith(\"/\")\n ? tgt.replace(/^\\/+/, \"\")\n : normalizePosix(srcdir ? `${srcdir}/${tgt}` : tgt);\n if (!existsSync(join(bundle, resolved))) {\n warnings.push(`${rel}: broken link -> ${tgt}`);\n }\n }\n }\n\n return {\n bundle,\n concepts: md.filter((f) => !RESERVED.has(posixBasename(f))).length,\n files: md.length,\n errors,\n warnings,\n };\n}\n\nexport function formatReport(r: ConformanceReport): string {\n const lines = [`${r.bundle}: ${r.files} files, ${r.concepts} concepts`];\n for (const e of r.errors) lines.push(` ERROR ${e}`);\n for (const w of r.warnings) lines.push(` warn ${w}`);\n const verdict = r.errors.length === 0 ? \"CONFORMANT\" : \"NON-CONFORMANT\";\n lines.push(` => ${verdict} (${r.errors.length} errors, ${r.warnings.length} warnings)`);\n return lines.join(\"\\n\");\n}\n\nexport function runCli(argv: string[]): number {\n const args = argv.filter((a) => !a.startsWith(\"--\"));\n const asJson = argv.includes(\"--json\");\n const bundle = args[0] ?? \".\";\n let isDir = false;\n try {\n isDir = statSync(bundle).isDirectory();\n } catch {\n isDir = false;\n }\n if (!isDir) {\n process.stderr.write(`not a directory: ${bundle}\\n`);\n return 2;\n }\n const r = checkConformance(bundle);\n if (asJson) {\n process.stdout.write(pythonJson(r) + \"\\n\");\n } else {\n process.stdout.write(formatReport(r) + \"\\n\");\n }\n return r.errors.length ? 1 : 0;\n}\n","import { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport { parseDocument } from \"yaml\";\n\n/** Frontmatter block at the very top, accepting LF or CRLF. */\nexport const FM_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/;\n\n/** Reserved (non-concept) filenames. */\nexport const RESERVED = new Set([\"index.md\", \"log.md\"]);\n\n/**\n * Recursively collect `.md` files under `bundle`, returned as bundle-relative\n * POSIX paths. Directory entries are sorted so traversal is deterministic\n * across platforms (Python's `sorted(md)` callers rely on this ordering).\n */\nexport function collectMarkdown(bundle: string): string[] {\n const out: string[] = [];\n const walk = (dir: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(dir).sort();\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(dir, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(full);\n } else if (name.endsWith(\".md\")) {\n out.push(relative(bundle, full).split(sep).join(\"/\"));\n }\n }\n };\n walk(bundle);\n return out;\n}\n\n/** Extract the raw frontmatter text, or null if none. */\nexport function frontmatter(text: string): string | null {\n const m = FM_RE.exec(text);\n return m ? m[1]! : null;\n}\n\nexport interface YamlFrontmatter {\n data: Record<string, unknown> | null;\n errors: string[];\n}\n\n/** Parse frontmatter as a YAML mapping and retain parser diagnostics. */\nexport function parseYamlFrontmatter(fm: string): YamlFrontmatter {\n const document = parseDocument(fm, { uniqueKeys: true });\n const errors = document.errors.map((error) => error.message);\n if (errors.length) return { data: null, errors };\n const value = document.toJS() as unknown;\n if (value === null) return { data: {}, errors: [] };\n if (typeof value !== \"object\" || Array.isArray(value)) {\n return { data: null, errors: [\"frontmatter must be a YAML mapping\"] };\n }\n return { data: value as Record<string, unknown>, errors: [] };\n}\n\n/** Strip a bundle-relative `.md` path to its concept id. */\nexport function conceptId(rel: string): string {\n return rel.endsWith(\".md\") ? rel.slice(0, -3) : rel;\n}\n\n// Matches any character in U+0080..U+FFFF (built programmatically to keep the\n// source ASCII-clean and unambiguous).\nconst NON_ASCII = new RegExp(\"[\" + String.fromCharCode(0x80) + \"-\" + String.fromCharCode(0xffff) + \"]\", \"g\");\n\n/**\n * JSON.stringify with 2-space indent, escaping non-ASCII as \\uXXXX so output is\n * byte-identical to Python's `json.dumps(..., indent=2)` (ensure_ascii=True).\n */\nexport function pythonJson(value: unknown): string {\n return JSON.stringify(value, null, 2).replace(\n NON_ASCII,\n (c) => \"\\\\u\" + c.charCodeAt(0).toString(16).padStart(4, \"0\"),\n );\n}\n\n/** POSIX path normalize matching Python's posixpath.normpath for our inputs. */\nexport function normalizePosix(p: string): string {\n const isAbs = p.startsWith(\"/\");\n const parts = p.split(\"/\");\n const stack: string[] = [];\n for (const part of parts) {\n if (part === \"\" || part === \".\") continue;\n if (part === \"..\") {\n if (stack.length && stack[stack.length - 1] !== \"..\") stack.pop();\n else if (!isAbs) stack.push(\"..\");\n } else {\n stack.push(part);\n }\n }\n const joined = stack.join(\"/\");\n if (isAbs) return \"/\" + joined;\n return joined === \"\" ? \".\" : joined;\n}\n","/**\n * Extract the graph model of an OKF bundle — the deterministic half of\n * kb-visualize. A TypeScript port of skills/kb-visualize/scripts/graph.py,\n * behaviour-identical. The agent renders this model into a view.\n *\n * Node id = concept id = path within the bundle minus `.md`. Reserved\n * index.md/log.md are excluded. Links are resolved to concept ids; links whose\n * target is not a concept in the bundle are dropped (SPEC §5.3 tolerates them).\n */\nimport { readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n FM_RE,\n RESERVED,\n collectMarkdown,\n conceptId,\n normalizePosix,\n parseYamlFrontmatter,\n pythonJson,\n} from \"./shared.js\";\n\nconst LINK_RE = /\\[[^\\]]*\\]\\(([^)#\\s]+\\.md)(?:#[^)]*)?\\)/g;\n\nexport interface GraphNode {\n id: string;\n path: string;\n type: string;\n title: string;\n description: string;\n tags: string[];\n resource: string;\n status: string;\n body: string;\n links: string[];\n cited_by: string[];\n}\n\nexport interface GraphModel {\n bundle: string;\n nodes: GraphNode[];\n types: string[];\n edges: { source: string; target: string }[];\n}\n\ntype FrontmatterData = Record<string, unknown>;\n\n/** Parse YAML for graph metadata; malformed frontmatter degrades to an empty map. */\nexport function parseFrontmatter(fm: string): FrontmatterData {\n return parseYamlFrontmatter(fm).data ?? {};\n}\n\nfunction scalar(data: FrontmatterData, k: string, dflt: string): string {\n const v = data[k];\n if (v === undefined) return dflt;\n return typeof v === \"string\" ? v : dflt;\n}\n\n/** Resolve a markdown link target (relative or bundle-absolute) to a concept id. */\nfunction resolve(srcRel: string, target: string): string {\n const resolved = target.startsWith(\"/\")\n ? target.replace(/^\\/+/, \"\")\n : normalizePosix(`${dirname(srcRel) === \".\" ? \"\" : dirname(srcRel)}/${target}`.replace(/^\\//, \"\"));\n return conceptId(resolved);\n}\n\nexport function extractGraph(bundle: string): GraphModel {\n const md = collectMarkdown(bundle);\n const posixBasename = (rel: string): string => rel.split(\"/\").pop() ?? rel;\n\n const ids = new Set<string>();\n for (const rel of md) {\n if (RESERVED.has(posixBasename(rel))) continue;\n ids.add(conceptId(rel));\n }\n\n const nodes = new Map<string, GraphNode>();\n for (const rel of [...md].sort()) {\n if (RESERVED.has(posixBasename(rel))) continue;\n const text = readFileSync(join(bundle, rel), \"utf-8\");\n const m = FM_RE.exec(text);\n const fm = m ? parseFrontmatter(m[1]!) : {};\n const body = m ? text.slice(m[0].length) : text;\n const cid = conceptId(rel);\n\n const links: string[] = [];\n for (const lm of body.matchAll(LINK_RE)) {\n const tgt = lm[1]!;\n if (tgt.includes(\"://\")) continue;\n const rid = resolve(rel, tgt);\n if (ids.has(rid) && rid !== cid && !links.includes(rid)) links.push(rid);\n }\n\n const rawTags = fm[\"tags\"];\n const tags = Array.isArray(rawTags)\n ? rawTags.filter((tag): tag is string => typeof tag === \"string\")\n : typeof rawTags === \"string\"\n ? [rawTags]\n : [];\n\n nodes.set(cid, {\n id: cid,\n path: rel,\n type: scalar(fm, \"type\", \"\"),\n title: scalar(fm, \"title\", cid.split(\"/\").pop() ?? cid),\n description: scalar(fm, \"description\", \"\"),\n tags,\n resource: scalar(fm, \"resource\", \"\"),\n status: scalar(fm, \"status\", \"active\"),\n body: body.trim(),\n links,\n cited_by: [],\n });\n }\n\n const edges: { source: string; target: string }[] = [];\n for (const n of nodes.values()) {\n for (const tgt of n.links) {\n edges.push({ source: n.id, target: tgt });\n nodes.get(tgt)!.cited_by.push(n.id);\n }\n }\n\n const types = [...new Set([...nodes.values()].map((n) => n.type).filter((t) => t))].sort();\n return { bundle, nodes: [...nodes.values()], types, edges };\n}\n\nexport function runCli(argv: string[]): number {\n const bundle = argv.find((a) => !a.startsWith(\"--\")) ?? \".\";\n let isDir = false;\n try {\n isDir = statSync(bundle).isDirectory();\n } catch {\n isDir = false;\n }\n if (!isDir) {\n process.stderr.write(`not a directory: ${bundle}\\n`);\n return 2;\n }\n process.stdout.write(pythonJson(extractGraph(bundle)) + \"\\n\");\n return 0;\n}\n","export interface ParsedArgs {\n /** First positional token (subcommand or undefined). */\n subcommand?: string;\n /** Remaining positional tokens. */\n positionals: string[];\n /** Boolean flags present (e.g. \"fix\", \"print\", \"help\"). */\n flags: Set<string>;\n /** Value flags (e.g. --model x, --dir path, --bundle path, --thread id). */\n values: Record<string, string>;\n}\n\nconst VALUE_FLAGS = new Set([\"model\", \"dir\", \"bundle\", \"thread\", \"resume\", \"C\"]);\n\n/**\n * Minimal arg parser. Supports `--flag`, `--key value`, `--key=value`, short\n * `-p`/`-h`/`-C`, and positionals. Deliberately dependency-free.\n */\nexport function parseArgs(argv: string[]): ParsedArgs {\n const positionals: string[] = [];\n const flags = new Set<string>();\n const values: Record<string, string> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!;\n if (tok === \"--\") {\n positionals.push(...argv.slice(i + 1));\n break;\n }\n if (tok.startsWith(\"--\")) {\n const body = tok.slice(2);\n const eq = body.indexOf(\"=\");\n if (eq >= 0) {\n values[body.slice(0, eq)] = body.slice(eq + 1);\n } else if (VALUE_FLAGS.has(body)) {\n values[body] = argv[++i] ?? \"\";\n } else {\n flags.add(body);\n }\n } else if (tok.startsWith(\"-\") && tok.length > 1) {\n const short = tok.slice(1);\n if (short === \"p\") flags.add(\"print\");\n else if (short === \"h\") flags.add(\"help\");\n else if (short === \"v\") flags.add(\"version\");\n else if (short === \"C\") values[\"dir\"] = argv[++i] ?? \"\";\n else flags.add(short);\n } else {\n positionals.push(tok);\n }\n }\n\n return {\n subcommand: positionals[0],\n positionals: positionals.slice(1),\n flags,\n values,\n };\n}\n","/**\n * Subcommand → skill directive mapping. Each CLI subcommand becomes a message\n * telling Janet to load and follow the matching kb-* skill against the target\n * bundle. The procedures live in the skills; this only routes to them.\n */\nexport type SubcommandName = \"init\" | \"ingest\" | \"query\" | \"lint\" | \"viz\";\n\nexport interface DirectiveContext {\n bundlePath: string;\n /** Positional args after the subcommand (sources, query text, scope, etc.). */\n args: string[];\n /** Flags like --fix. */\n flags: Set<string>;\n}\n\nexport const SUBCOMMANDS: readonly SubcommandName[] = [\"init\", \"ingest\", \"query\", \"lint\", \"viz\"];\n\nexport function isSubcommand(x: string): x is SubcommandName {\n return (SUBCOMMANDS as readonly string[]).includes(x);\n}\n\n/** Preserve deterministic lint failures even when the agent audit itself succeeds. */\nexport function commandExitCode(\n command: SubcommandName,\n agentExitCode: number,\n conformanceErrors: number = 0,\n): number {\n return command === \"lint\" && conformanceErrors > 0 ? 1 : agentExitCode;\n}\n\nexport function headlessCapabilities(command: SubcommandName, flags: Set<string>) {\n return {\n allowEdits:\n command === \"init\" ||\n command === \"ingest\" ||\n command === \"viz\" ||\n (command === \"lint\" && flags.has(\"fix\")),\n allowExec: flags.has(\"allow-exec\"),\n };\n}\n\nexport function buildDirective(cmd: SubcommandName, ctx: DirectiveContext): string {\n const bundle = ctx.bundlePath;\n switch (cmd) {\n case \"init\":\n return `Load and follow the kb-init skill to scaffold a new OKF knowledge bundle at ${bundle}. If it already exists, say so and stop rather than overwriting.`;\n case \"ingest\": {\n const sources = ctx.args.length ? ctx.args.join(\", \") : \"(no source given)\";\n return `Load and follow the kb-ingest skill to ingest the following source(s) into the bundle at ${bundle}: ${sources}. Integrate per the trust model — update the index and log.md.`;\n }\n case \"query\": {\n const q = ctx.args.join(\" \").trim();\n return `Load and follow the kb-query skill to answer this question from the bundle at ${bundle}, with citations: ${q || \"(no question given)\"}`;\n }\n case \"lint\": {\n const fix = ctx.flags.has(\"fix\") ? \" Run in fix mode: repair what is safe.\" : \"\";\n return `Load and follow the kb-lint skill to health-check the bundle at ${bundle}. The deterministic conformance pass has already run; focus on the drift audit and report findings by severity.${fix}`;\n }\n case \"viz\": {\n const scope = ctx.args.join(\" \").trim();\n return `Load and follow the kb-visualize skill to render the bundle at ${bundle} as a graph${scope ? ` scoped to: ${scope}` : \"\"}. Write a self-contained HTML file next to the bundle and give the path.`;\n }\n }\n}\n"],"mappings":"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","names":["existsSync","statSync","join","join","parsed","readFileSync","statSync","dirname","join","existsSync"]}
1
+ {"version":3,"sources":["../src/main.ts","../../kb-tools/src/conformance.ts","../../kb-tools/src/shared.ts","../../kb-tools/src/graph.ts","../src/headless/flags.ts","../src/commands.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { checkConformance, formatReport } from \"@agent-knowledge/kb-tools\";\nimport { loadSettings } from \"./onboarding/settings.js\";\nimport { availableModels, normalizeModelSelection } from \"./onboarding/providers.js\";\nimport { parseArgs } from \"./headless/flags.js\";\nimport { runHeadless } from \"./headless/run.js\";\nimport {\n buildDirective,\n commandExitCode,\n headlessCapabilities,\n isSubcommand,\n} from \"./commands.js\";\nimport { resolveProjectPaths } from \"./agent/paths.js\";\nimport { GREETING } from \"./agent/persona.js\";\nimport { packageVersion } from \"./version.js\";\n\nconst HELP = `${GREETING}\n\nUsage:\n janet Start an interactive session (chat with Janet)\n janet init Scaffold a new knowledge/ bundle here\n janet ingest <src...> Ingest source(s) into the bundle\n janet query \"<question>\" Answer from the bundle, with citations\n janet lint [--fix] Health-check the bundle (conformance + drift)\n janet viz [scope] Render the bundle as a graph\n\nOptions:\n -C, --dir <path> Operate on <path> instead of the current directory\n --bundle <path> Bundle location within <dir> (default: knowledge)\n -p, --print Headless: stream to stdout and exit\n --model <provider/id> Model to use (or set JANET_MODEL)\n --thread <id> Resume a thread\n --allow-exec Allow shell commands in a one-shot run\n -h, --help Show this help\n -v, --version Show version\n\nAlso installed as \\`ding\\` (you summon Janet with a ding).`;\n\nfunction resolveModelId(values: Record<string, string>): string | undefined {\n const selected =\n values[\"model\"] ??\n process.env[\"JANET_MODEL\"] ??\n loadSettings().defaultModelId ??\n undefined;\n return selected ? normalizeModelSelection(selected, availableModels()) : undefined;\n}\n\nasync function main(argv: string[]): Promise<number> {\n const parsed = parseArgs(argv);\n\n if (parsed.flags.has(\"help\") || parsed.subcommand === \"help\") {\n process.stdout.write(HELP + \"\\n\");\n return 0;\n }\n if (parsed.flags.has(\"version\")) {\n process.stdout.write(packageVersion() + \"\\n\");\n return 0;\n }\n\n const dir = parsed.values[\"dir\"];\n const bundleOverride = parsed.values[\"bundle\"];\n const paths = resolveProjectPaths({ dir, bundle: bundleOverride });\n const modelId = resolveModelId(parsed.values);\n const threadId = parsed.values[\"thread\"] ?? parsed.values[\"resume\"];\n const headless = parsed.flags.has(\"print\") || !process.stdout.isTTY;\n\n const sub = parsed.subcommand;\n\n // No subcommand → interactive TUI (chat).\n if (!sub) {\n if (!headless) {\n const { runTui } = await import(\"./tui/index.js\");\n if (modelId && !process.env[\"JANET_MODEL\"]) process.env[\"JANET_MODEL\"] = modelId;\n return runTui({ dir, bundle: bundleOverride, threadId });\n }\n process.stderr.write(\"No subcommand. Try `janet --help`.\\n\");\n return 2;\n }\n\n if (!isSubcommand(sub)) {\n process.stderr.write(`Unknown command: ${sub}\\nTry \\`janet --help\\`.\\n`);\n return 2;\n }\n\n // `lint` runs the deterministic conformance check in-process first (no tokens,\n // CI-gateable), then hands the drift audit to the agent.\n let conformanceErrors = 0;\n if (sub === \"lint\") {\n if (!existsSync(paths.bundlePath)) {\n process.stderr.write(\n `No bundle at ${paths.bundlePath}. Run \\`janet init\\` to scaffold one.\\n`,\n );\n return 2;\n }\n const report = checkConformance(paths.bundlePath);\n conformanceErrors = report.errors.length;\n process.stdout.write(formatReport(report) + \"\\n\");\n // If no model is configured, stop after the deterministic pass (still useful\n // and exit-coded for CI).\n if (!modelId) {\n process.stdout.write(\n \"\\n(No model configured — ran the deterministic conformance pass only. \" +\n \"Set --model or JANET_MODEL for the drift audit.)\\n\",\n );\n return report.errors.length ? 1 : 0;\n }\n }\n\n // Bundle must exist for ingest/query/lint/viz (init creates it).\n if (sub !== \"init\" && !existsSync(paths.bundlePath)) {\n process.stderr.write(\n `No bundle at ${paths.bundlePath}. Run \\`janet init\\` to scaffold one.\\n`,\n );\n return 2;\n }\n\n const directive = buildDirective(sub, {\n bundlePath: paths.bundlePath,\n args: parsed.positionals,\n flags: parsed.flags,\n });\n const capabilities = headlessCapabilities(sub, parsed.flags);\n\n const result = await runHeadless({\n message: directive,\n dir,\n bundle: bundleOverride,\n modelId,\n threadId,\n operation: sub,\n ...capabilities,\n });\n return commandExitCode(sub, result.exitCode, conformanceErrors);\n}\n\nmain(process.argv.slice(2))\n .then((code) => process.exit(code))\n .catch((err) => {\n process.stderr.write(`\\nJanet hit a snag: ${err?.message ?? err}\\n`);\n process.exit(1);\n });\n","/**\n * Deterministic OKF v0.1 conformance check for a knowledge bundle (SPEC §9).\n *\n * A TypeScript port of skills/kb-lint/scripts/conformance.py, behaviour-identical.\n * ERRORs fail conformance; broken links and soft-guidance issues are WARN and\n * never fail (SPEC §5.3 / §9 — consumers MUST tolerate them). Structure only;\n * drift is the fuzzy, agent-driven half of kb-lint.\n */\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n RESERVED,\n collectMarkdown,\n frontmatter,\n normalizePosix,\n parseYamlFrontmatter,\n pythonJson,\n} from \"./shared.js\";\n\nconst HEADING_LOG_RE = /^##\\s+(.+?)\\s*$/gm;\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst LINK_RE = /\\]\\(([^)#\\s]+\\.md)(#[^)]*)?\\)/g;\n\nexport interface ConformanceReport {\n bundle: string;\n concepts: number;\n files: number;\n errors: string[];\n warnings: string[];\n}\n\nexport function checkConformance(bundle: string): ConformanceReport {\n const errors: string[] = [];\n const warnings: string[] = [];\n const md = collectMarkdown(bundle);\n\n const posixBasename = (rel: string): string => rel.split(\"/\").pop() ?? rel;\n\n for (const rel of [...md].sort()) {\n const text = readFileSync(join(bundle, rel), \"utf-8\");\n const base = posixBasename(rel);\n const fm = frontmatter(text);\n\n if (RESERVED.has(base)) {\n // Reserved files carry no frontmatter, except the ROOT index.md may\n // declare okf_version (SPEC §6/§11).\n if (fm !== null) {\n const isRootIndex = rel === \"index.md\";\n const parsed = parseYamlFrontmatter(fm);\n if (\n !isRootIndex ||\n parsed.errors.length > 0 ||\n !parsed.data ||\n !Object.prototype.hasOwnProperty.call(parsed.data, \"okf_version\")\n ) {\n errors.push(`${rel}: reserved file must not carry frontmatter`);\n }\n }\n if (base === \"log.md\") {\n for (const m of text.matchAll(HEADING_LOG_RE)) {\n if (!ISO_DATE_RE.test(m[1]!)) {\n warnings.push(`${rel}: log date heading not ISO 8601: '${m[1]}'`);\n }\n }\n }\n continue;\n }\n\n // Concept document: rules 1 & 2.\n if (fm === null) {\n errors.push(`${rel}: concept has no parseable frontmatter`);\n continue;\n }\n const parsed = parseYamlFrontmatter(fm);\n if (parsed.errors.length > 0 || !parsed.data) {\n errors.push(`${rel}: concept has no parseable frontmatter`);\n continue;\n }\n const type = parsed.data[\"type\"];\n if (typeof type !== \"string\" || !type.trim()) {\n errors.push(`${rel}: missing or empty required 'type'`);\n }\n }\n\n // Broken relative links → WARN only (never a conformance failure).\n for (const rel of md) {\n const srcdir = dirname(rel) === \".\" ? \"\" : dirname(rel);\n const text = readFileSync(join(bundle, rel), \"utf-8\");\n for (const m of text.matchAll(LINK_RE)) {\n const tgt = m[1]!;\n if (tgt.includes(\"://\")) continue;\n const resolved = tgt.startsWith(\"/\")\n ? tgt.replace(/^\\/+/, \"\")\n : normalizePosix(srcdir ? `${srcdir}/${tgt}` : tgt);\n if (!existsSync(join(bundle, resolved))) {\n warnings.push(`${rel}: broken link -> ${tgt}`);\n }\n }\n }\n\n return {\n bundle,\n concepts: md.filter((f) => !RESERVED.has(posixBasename(f))).length,\n files: md.length,\n errors,\n warnings,\n };\n}\n\nexport function formatReport(r: ConformanceReport): string {\n const lines = [`${r.bundle}: ${r.files} files, ${r.concepts} concepts`];\n for (const e of r.errors) lines.push(` ERROR ${e}`);\n for (const w of r.warnings) lines.push(` warn ${w}`);\n const verdict = r.errors.length === 0 ? \"CONFORMANT\" : \"NON-CONFORMANT\";\n lines.push(` => ${verdict} (${r.errors.length} errors, ${r.warnings.length} warnings)`);\n return lines.join(\"\\n\");\n}\n\nexport function runCli(argv: string[]): number {\n const args = argv.filter((a) => !a.startsWith(\"--\"));\n const asJson = argv.includes(\"--json\");\n const bundle = args[0] ?? \".\";\n let isDir = false;\n try {\n isDir = statSync(bundle).isDirectory();\n } catch {\n isDir = false;\n }\n if (!isDir) {\n process.stderr.write(`not a directory: ${bundle}\\n`);\n return 2;\n }\n const r = checkConformance(bundle);\n if (asJson) {\n process.stdout.write(pythonJson(r) + \"\\n\");\n } else {\n process.stdout.write(formatReport(r) + \"\\n\");\n }\n return r.errors.length ? 1 : 0;\n}\n","import { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport { parseDocument } from \"yaml\";\n\n/** Frontmatter block at the very top, accepting LF or CRLF. */\nexport const FM_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---(?:\\r?\\n|$)/;\n\n/** Reserved (non-concept) filenames. */\nexport const RESERVED = new Set([\"index.md\", \"log.md\"]);\n\n/**\n * Recursively collect `.md` files under `bundle`, returned as bundle-relative\n * POSIX paths. Directory entries are sorted so traversal is deterministic\n * across platforms (Python's `sorted(md)` callers rely on this ordering).\n */\nexport function collectMarkdown(bundle: string): string[] {\n const out: string[] = [];\n const walk = (dir: string): void => {\n let entries: string[];\n try {\n entries = readdirSync(dir).sort();\n } catch {\n return;\n }\n for (const name of entries) {\n const full = join(dir, name);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(full);\n } else if (name.endsWith(\".md\")) {\n out.push(relative(bundle, full).split(sep).join(\"/\"));\n }\n }\n };\n walk(bundle);\n return out;\n}\n\n/** Extract the raw frontmatter text, or null if none. */\nexport function frontmatter(text: string): string | null {\n const m = FM_RE.exec(text);\n return m ? m[1]! : null;\n}\n\nexport interface YamlFrontmatter {\n data: Record<string, unknown> | null;\n errors: string[];\n}\n\n/** Parse frontmatter as a YAML mapping and retain parser diagnostics. */\nexport function parseYamlFrontmatter(fm: string): YamlFrontmatter {\n const document = parseDocument(fm, { uniqueKeys: true });\n const errors = document.errors.map((error) => error.message);\n if (errors.length) return { data: null, errors };\n const value = document.toJS() as unknown;\n if (value === null) return { data: {}, errors: [] };\n if (typeof value !== \"object\" || Array.isArray(value)) {\n return { data: null, errors: [\"frontmatter must be a YAML mapping\"] };\n }\n return { data: value as Record<string, unknown>, errors: [] };\n}\n\n/** Strip a bundle-relative `.md` path to its concept id. */\nexport function conceptId(rel: string): string {\n return rel.endsWith(\".md\") ? rel.slice(0, -3) : rel;\n}\n\n// Matches any character in U+0080..U+FFFF (built programmatically to keep the\n// source ASCII-clean and unambiguous).\nconst NON_ASCII = new RegExp(\"[\" + String.fromCharCode(0x80) + \"-\" + String.fromCharCode(0xffff) + \"]\", \"g\");\n\n/**\n * JSON.stringify with 2-space indent, escaping non-ASCII as \\uXXXX so output is\n * byte-identical to Python's `json.dumps(..., indent=2)` (ensure_ascii=True).\n */\nexport function pythonJson(value: unknown): string {\n return JSON.stringify(value, null, 2).replace(\n NON_ASCII,\n (c) => \"\\\\u\" + c.charCodeAt(0).toString(16).padStart(4, \"0\"),\n );\n}\n\n/** POSIX path normalize matching Python's posixpath.normpath for our inputs. */\nexport function normalizePosix(p: string): string {\n const isAbs = p.startsWith(\"/\");\n const parts = p.split(\"/\");\n const stack: string[] = [];\n for (const part of parts) {\n if (part === \"\" || part === \".\") continue;\n if (part === \"..\") {\n if (stack.length && stack[stack.length - 1] !== \"..\") stack.pop();\n else if (!isAbs) stack.push(\"..\");\n } else {\n stack.push(part);\n }\n }\n const joined = stack.join(\"/\");\n if (isAbs) return \"/\" + joined;\n return joined === \"\" ? \".\" : joined;\n}\n","/**\n * Extract the graph model of an OKF bundle — the deterministic half of\n * kb-visualize. A TypeScript port of skills/kb-visualize/scripts/graph.py,\n * behaviour-identical. The agent renders this model into a view.\n *\n * Node id = concept id = path within the bundle minus `.md`. Reserved\n * index.md/log.md are excluded. Links are resolved to concept ids; links whose\n * target is not a concept in the bundle are dropped (SPEC §5.3 tolerates them).\n */\nimport { readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport {\n FM_RE,\n RESERVED,\n collectMarkdown,\n conceptId,\n normalizePosix,\n parseYamlFrontmatter,\n pythonJson,\n} from \"./shared.js\";\n\nconst LINK_RE = /\\[[^\\]]*\\]\\(([^)#\\s]+\\.md)(?:#[^)]*)?\\)/g;\n\nexport interface GraphNode {\n id: string;\n path: string;\n type: string;\n title: string;\n description: string;\n tags: string[];\n resource: string;\n status: string;\n body: string;\n links: string[];\n cited_by: string[];\n}\n\nexport interface GraphModel {\n bundle: string;\n nodes: GraphNode[];\n types: string[];\n edges: { source: string; target: string }[];\n}\n\ntype FrontmatterData = Record<string, unknown>;\n\n/** Parse YAML for graph metadata; malformed frontmatter degrades to an empty map. */\nexport function parseFrontmatter(fm: string): FrontmatterData {\n return parseYamlFrontmatter(fm).data ?? {};\n}\n\nfunction scalar(data: FrontmatterData, k: string, dflt: string): string {\n const v = data[k];\n if (v === undefined) return dflt;\n return typeof v === \"string\" ? v : dflt;\n}\n\n/** Resolve a markdown link target (relative or bundle-absolute) to a concept id. */\nfunction resolve(srcRel: string, target: string): string {\n const resolved = target.startsWith(\"/\")\n ? target.replace(/^\\/+/, \"\")\n : normalizePosix(`${dirname(srcRel) === \".\" ? \"\" : dirname(srcRel)}/${target}`.replace(/^\\//, \"\"));\n return conceptId(resolved);\n}\n\nexport function extractGraph(bundle: string): GraphModel {\n const md = collectMarkdown(bundle);\n const posixBasename = (rel: string): string => rel.split(\"/\").pop() ?? rel;\n\n const ids = new Set<string>();\n for (const rel of md) {\n if (RESERVED.has(posixBasename(rel))) continue;\n ids.add(conceptId(rel));\n }\n\n const nodes = new Map<string, GraphNode>();\n for (const rel of [...md].sort()) {\n if (RESERVED.has(posixBasename(rel))) continue;\n const text = readFileSync(join(bundle, rel), \"utf-8\");\n const m = FM_RE.exec(text);\n const fm = m ? parseFrontmatter(m[1]!) : {};\n const body = m ? text.slice(m[0].length) : text;\n const cid = conceptId(rel);\n\n const links: string[] = [];\n for (const lm of body.matchAll(LINK_RE)) {\n const tgt = lm[1]!;\n if (tgt.includes(\"://\")) continue;\n const rid = resolve(rel, tgt);\n if (ids.has(rid) && rid !== cid && !links.includes(rid)) links.push(rid);\n }\n\n const rawTags = fm[\"tags\"];\n const tags = Array.isArray(rawTags)\n ? rawTags.filter((tag): tag is string => typeof tag === \"string\")\n : typeof rawTags === \"string\"\n ? [rawTags]\n : [];\n\n nodes.set(cid, {\n id: cid,\n path: rel,\n type: scalar(fm, \"type\", \"\"),\n title: scalar(fm, \"title\", cid.split(\"/\").pop() ?? cid),\n description: scalar(fm, \"description\", \"\"),\n tags,\n resource: scalar(fm, \"resource\", \"\"),\n status: scalar(fm, \"status\", \"active\"),\n body: body.trim(),\n links,\n cited_by: [],\n });\n }\n\n const edges: { source: string; target: string }[] = [];\n for (const n of nodes.values()) {\n for (const tgt of n.links) {\n edges.push({ source: n.id, target: tgt });\n nodes.get(tgt)!.cited_by.push(n.id);\n }\n }\n\n const types = [...new Set([...nodes.values()].map((n) => n.type).filter((t) => t))].sort();\n return { bundle, nodes: [...nodes.values()], types, edges };\n}\n\nexport function runCli(argv: string[]): number {\n const bundle = argv.find((a) => !a.startsWith(\"--\")) ?? \".\";\n let isDir = false;\n try {\n isDir = statSync(bundle).isDirectory();\n } catch {\n isDir = false;\n }\n if (!isDir) {\n process.stderr.write(`not a directory: ${bundle}\\n`);\n return 2;\n }\n process.stdout.write(pythonJson(extractGraph(bundle)) + \"\\n\");\n return 0;\n}\n","export interface ParsedArgs {\n /** First positional token (subcommand or undefined). */\n subcommand?: string;\n /** Remaining positional tokens. */\n positionals: string[];\n /** Boolean flags present (e.g. \"fix\", \"print\", \"help\"). */\n flags: Set<string>;\n /** Value flags (e.g. --model x, --dir path, --bundle path, --thread id). */\n values: Record<string, string>;\n}\n\nconst VALUE_FLAGS = new Set([\"model\", \"dir\", \"bundle\", \"thread\", \"resume\", \"C\"]);\n\n/**\n * Minimal arg parser. Supports `--flag`, `--key value`, `--key=value`, short\n * `-p`/`-h`/`-C`, and positionals. Deliberately dependency-free.\n */\nexport function parseArgs(argv: string[]): ParsedArgs {\n const positionals: string[] = [];\n const flags = new Set<string>();\n const values: Record<string, string> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const tok = argv[i]!;\n if (tok === \"--\") {\n positionals.push(...argv.slice(i + 1));\n break;\n }\n if (tok.startsWith(\"--\")) {\n const body = tok.slice(2);\n const eq = body.indexOf(\"=\");\n if (eq >= 0) {\n values[body.slice(0, eq)] = body.slice(eq + 1);\n } else if (VALUE_FLAGS.has(body)) {\n values[body] = argv[++i] ?? \"\";\n } else {\n flags.add(body);\n }\n } else if (tok.startsWith(\"-\") && tok.length > 1) {\n const short = tok.slice(1);\n if (short === \"p\") flags.add(\"print\");\n else if (short === \"h\") flags.add(\"help\");\n else if (short === \"v\") flags.add(\"version\");\n else if (short === \"C\") values[\"dir\"] = argv[++i] ?? \"\";\n else flags.add(short);\n } else {\n positionals.push(tok);\n }\n }\n\n return {\n subcommand: positionals[0],\n positionals: positionals.slice(1),\n flags,\n values,\n };\n}\n","/**\n * Subcommand → skill directive mapping. Each CLI subcommand becomes a message\n * telling Janet to load and follow the matching kb-* skill against the target\n * bundle. The procedures live in the skills; this only routes to them.\n */\nexport type SubcommandName = \"init\" | \"ingest\" | \"query\" | \"lint\" | \"viz\";\n\nexport interface DirectiveContext {\n bundlePath: string;\n /** Positional args after the subcommand (sources, query text, scope, etc.). */\n args: string[];\n /** Flags like --fix. */\n flags: Set<string>;\n}\n\nexport const SUBCOMMANDS: readonly SubcommandName[] = [\"init\", \"ingest\", \"query\", \"lint\", \"viz\"];\n\nexport function isSubcommand(x: string): x is SubcommandName {\n return (SUBCOMMANDS as readonly string[]).includes(x);\n}\n\n/** Preserve deterministic lint failures even when the agent audit itself succeeds. */\nexport function commandExitCode(\n command: SubcommandName,\n agentExitCode: number,\n conformanceErrors: number = 0,\n): number {\n return command === \"lint\" && conformanceErrors > 0 ? 1 : agentExitCode;\n}\n\nexport function headlessCapabilities(command: SubcommandName, flags: Set<string>) {\n return {\n allowEdits:\n command === \"init\" ||\n command === \"ingest\" ||\n command === \"viz\" ||\n (command === \"lint\" && flags.has(\"fix\")),\n allowExec: flags.has(\"allow-exec\"),\n };\n}\n\nexport function buildDirective(cmd: SubcommandName, ctx: DirectiveContext): string {\n const bundle = ctx.bundlePath;\n switch (cmd) {\n case \"init\":\n return `Load and follow the kb-init skill to scaffold a new OKF knowledge bundle at ${bundle}. If it already exists, say so and stop rather than overwriting.`;\n case \"ingest\": {\n const sources = ctx.args.length ? ctx.args.join(\", \") : \"(no source given)\";\n return `Load and follow the kb-ingest skill to ingest the following source(s) into the bundle at ${bundle}: ${sources}. Integrate per the trust model — update the index and log.md.`;\n }\n case \"query\": {\n const q = ctx.args.join(\" \").trim();\n return `Load and follow the kb-query skill to answer this question from the bundle at ${bundle}, with citations: ${q || \"(no question given)\"}`;\n }\n case \"lint\": {\n const fix = ctx.flags.has(\"fix\") ? \" Run in fix mode: repair what is safe.\" : \"\";\n return `Load and follow the kb-lint skill to health-check the bundle at ${bundle}. The deterministic conformance pass has already run; focus on the drift audit and report findings by severity.${fix}`;\n }\n case \"viz\": {\n const scope = ctx.args.join(\" \").trim();\n return `Load and follow the kb-visualize skill to render the bundle at ${bundle} as a graph${scope ? ` scoped to: ${scope}` : \"\"}. Write a self-contained HTML file next to the bundle and give the path.`;\n }\n }\n}\n"],"mappings":"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","names":["existsSync","statSync","join","join","parsed","readFileSync","statSync","dirname","join","existsSync"]}
@@ -1,23 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __janetCreateRequire } from "node:module";
3
3
  const require = __janetCreateRequire(import.meta.url);
4
- import {
5
- availableModels,
6
- normalizeModelSelection
7
- } from "./chunk-UIDPQHJI.js";
8
4
  import {
9
5
  GREETING,
6
+ NATIVE_PROVIDER_DEFINITIONS,
7
+ availableModels,
10
8
  bootJanet,
11
9
  completeOnboarding,
10
+ discoverAvailableModels,
11
+ environmentApiKeyConfigured,
12
12
  formatObservabilityStatus,
13
13
  getAuthStorage,
14
+ groupModelsByProvider,
14
15
  loadSettings,
15
16
  messageText,
17
+ normalizeModelSelection,
16
18
  rememberModel,
17
19
  rememberObservability,
18
20
  resolveObservabilityConfig,
19
21
  safeObservabilityEndpoint
20
- } from "./chunk-K2TO22OI.js";
22
+ } from "./chunk-S237DEGI.js";
21
23
 
22
24
  // src/tui/index.ts
23
25
  import {
@@ -197,8 +199,9 @@ var markdownTheme = {
197
199
  // src/tui/index.ts
198
200
  var OAUTH_PROVIDERS = ["anthropic", "openai-codex"];
199
201
  var HELP_TEXT = `Commands:
200
- /models Pick a model from a list (arrow keys)
202
+ /models Pick a provider, then a model
201
203
  /model [provider/id] Open the picker, or switch directly by id
204
+ /providers Show detected and available providers
202
205
  /login <provider> [mode]
203
206
  Log in; OpenAI mode is browser or device
204
207
  /logout <provider> Remove stored credentials for a provider
@@ -257,6 +260,7 @@ async function runTui(opts) {
257
260
  let activeSelect = null;
258
261
  let active = null;
259
262
  let cancelRequested = false;
263
+ let modelPickerLoading = false;
260
264
  const activeTools = /* @__PURE__ */ new Map();
261
265
  const updateStatus = () => {
262
266
  const model = session.model.hasSelection() ? session.model.get() : "no model \u2014 /model <id>";
@@ -503,43 +507,137 @@ async function runTui(opts) {
503
507
  updateStatus();
504
508
  });
505
509
  };
506
- const showModelPicker = (intro) => {
507
- const choices = availableModels();
508
- if (intro) addLine(c.accentBold(intro));
509
- if (!choices.length) {
510
+ const closeActiveSelect = (select) => {
511
+ chat.removeChild(select);
512
+ if (activeSelect === select) activeSelect = null;
513
+ ui.setFocus(editor);
514
+ };
515
+ const selectModel = async (modelId) => {
516
+ try {
517
+ await session.model.switch({ modelId });
518
+ completeOnboarding(modelId, (/* @__PURE__ */ new Date()).toISOString());
519
+ rememberModel(modelId);
520
+ addLine(c.accentBold(` \u2713 Using ${modelId}.`) + c.dim(" (saved as your default)"));
521
+ } catch (error) {
522
+ addLine(c.error(` Could not select ${modelId}: ${error.message}`));
523
+ } finally {
524
+ updateStatus();
525
+ }
526
+ };
527
+ const showProviderModels = (group) => {
528
+ const current = session.model.hasSelection() ? session.model.get() : null;
529
+ const currentChoice = group.models.find((choice) => choice.id === current);
530
+ const ordered = currentChoice ? [currentChoice, ...group.models.filter((choice) => choice.id !== current)] : group.models;
531
+ const visible = ordered.slice(0, 29);
532
+ const items = visible.map((choice) => ({
533
+ value: choice.id,
534
+ label: choice.id === current ? `${choice.label} (current)` : choice.label,
535
+ description: choice.id
536
+ }));
537
+ items.push({
538
+ value: "__janet_enter_model_id__",
539
+ label: "Enter another model ID\u2026",
540
+ description: ordered.length > visible.length ? `${ordered.length - visible.length} more catalog models; enter the exact id` : `Use any ${group.id}/model supported by Mastra`
541
+ });
542
+ addLine(c.accentBold(` ${group.label} models`));
543
+ addLine(c.dim(" \u2191/\u2193 to move, enter to choose:"));
544
+ const select = new SelectList(
545
+ items,
546
+ Math.min(items.length, 10),
547
+ editorTheme.selectList
548
+ );
549
+ select.onSelect = (item) => {
550
+ closeActiveSelect(select);
551
+ if (item.value === "__janet_enter_model_id__") {
552
+ void promptInput(
553
+ `Model id for ${group.label}:`,
554
+ `${group.id}/model-name`
555
+ ).then((input) => {
556
+ const modelId = input.startsWith(`${group.id}/`) ? input : `${group.id}/${input}`;
557
+ void selectModel(modelId);
558
+ });
559
+ return;
560
+ }
561
+ void selectModel(item.value);
562
+ };
563
+ activeSelect = select;
564
+ chat.addChild(select);
565
+ ui.setFocus(select);
566
+ updateStatus();
567
+ };
568
+ const showProviderPicker = (choices) => {
569
+ const groups = groupModelsByProvider(choices);
570
+ if (!groups.length) {
510
571
  addLine(c.dim(" No providers are configured yet. Set one up, then try again:"));
511
572
  addLine(c.dim(" \u2022 Vertex AI: gcloud auth application-default login (+ GOOGLE_VERTEX_PROJECT)"));
512
573
  addLine(c.dim(" \u2022 Anthropic: set ANTHROPIC_API_KEY, or /login anthropic"));
513
574
  addLine(c.dim(" \u2022 OpenAI: set OPENAI_API_KEY, or /login openai-codex"));
514
575
  addLine(c.dim(" \u2022 Bedrock: configure AWS credentials"));
576
+ addLine(c.dim(" \u2022 More: /providers lists native Mastra environment variables"));
515
577
  updateStatus();
516
578
  return;
517
579
  }
518
- const current = session.model.hasSelection() ? session.model.get() : null;
519
- addLine(c.dim(" \u2191/\u2193 to move, enter to choose:"));
580
+ addLine(c.dim(" \u2191/\u2193 to move, enter to choose a provider:"));
520
581
  const select = new SelectList(
521
- choices.map((ch) => ({
522
- value: ch.id,
523
- label: ch.id === current ? `${ch.label} (current)` : ch.label,
524
- description: ch.via
582
+ groups.map((group) => ({
583
+ value: group.id,
584
+ label: group.label,
585
+ description: `${group.models.length} model${group.models.length === 1 ? "" : "s"} \xB7 ${group.via}`
525
586
  })),
526
- Math.min(choices.length, 10),
587
+ Math.min(groups.length, 10),
527
588
  editorTheme.selectList
528
589
  );
529
590
  select.onSelect = (item) => {
530
- chat.removeChild(select);
531
- activeSelect = null;
532
- ui.setFocus(editor);
533
- void session.model.switch({ modelId: item.value });
534
- completeOnboarding(item.value, (/* @__PURE__ */ new Date()).toISOString());
535
- addLine(c.accentBold(` \u2713 Using ${item.value}.`) + c.dim(" (saved as your default)"));
536
- updateStatus();
591
+ closeActiveSelect(select);
592
+ const group = groups.find((candidate) => candidate.id === item.value);
593
+ if (group) showProviderModels(group);
537
594
  };
538
595
  activeSelect = select;
539
596
  chat.addChild(select);
540
597
  ui.setFocus(select);
541
598
  updateStatus();
542
599
  };
600
+ const showModelPicker = (intro) => {
601
+ if (modelPickerLoading) {
602
+ addLine(c.dim(" The provider catalog is already loading."));
603
+ return;
604
+ }
605
+ if (intro) addLine(c.accentBold(intro));
606
+ addLine(c.dim(" Loading configured providers\u2026"));
607
+ modelPickerLoading = true;
608
+ updateStatus();
609
+ void discoverAvailableModels(() => controller.listAvailableModels()).then(showProviderPicker).catch((error) => {
610
+ addLine(c.error(` Could not load providers: ${error.message}`));
611
+ showProviderPicker(availableModels());
612
+ }).finally(() => {
613
+ modelPickerLoading = false;
614
+ updateStatus();
615
+ });
616
+ };
617
+ const showProviders = () => {
618
+ addLine(c.accentBold(" Model providers"));
619
+ void discoverAvailableModels(() => controller.listAvailableModels()).then((choices) => {
620
+ const groups = groupModelsByProvider(choices);
621
+ if (groups.length) {
622
+ addLine(c.dim(" Available now:"));
623
+ for (const group of groups) {
624
+ addLine(c.dim(` \u2022 ${group.label}: ${group.via}`));
625
+ }
626
+ } else {
627
+ addLine(c.dim(" No provider credentials detected."));
628
+ }
629
+ const missing = NATIVE_PROVIDER_DEFINITIONS.filter(
630
+ (provider) => !environmentApiKeyConfigured(provider.id)
631
+ );
632
+ if (missing.length) {
633
+ addLine(c.dim(" Add another Mastra-native provider:"));
634
+ for (const provider of missing) {
635
+ addLine(c.dim(` \u2022 ${provider.label}: ${provider.envVars.join(" or ")}`));
636
+ }
637
+ }
638
+ updateStatus();
639
+ });
640
+ };
543
641
  const savedObservabilitySummary = () => {
544
642
  const saved = loadSettings().observability;
545
643
  const resolved = resolveObservabilityConfig(saved, {});
@@ -563,11 +661,6 @@ async function runTui(opts) {
563
661
  addLine(c.dim(" Restart Janet to apply the new setting."));
564
662
  updateStatus();
565
663
  };
566
- const closeActiveSelect = (select) => {
567
- chat.removeChild(select);
568
- if (activeSelect === select) activeSelect = null;
569
- ui.setFocus(editor);
570
- };
571
664
  const confirmFullCapture = (base) => {
572
665
  addLine(
573
666
  c.warn(
@@ -892,16 +985,15 @@ async function runTui(opts) {
892
985
  break;
893
986
  }
894
987
  const id = normalizeModelSelection(inputId, availableModels());
895
- await session.model.switch({ modelId: id });
896
- completeOnboarding(id, (/* @__PURE__ */ new Date()).toISOString());
897
- rememberModel(id);
898
- addLine(c.dim(`Model set to ${id}.`));
899
- updateStatus();
988
+ await selectModel(id);
900
989
  break;
901
990
  }
902
991
  case "models":
903
992
  showModelPicker();
904
993
  break;
994
+ case "providers":
995
+ showProviders();
996
+ break;
905
997
  default:
906
998
  addLine(c.dim(`Unknown command /${cmd}. Try /help.`));
907
999
  }
@@ -993,4 +1085,4 @@ Ask me anything in the bundle, or say what to ingest. /help for commands.`
993
1085
  export {
994
1086
  runTui
995
1087
  };
996
- //# sourceMappingURL=tui-WYFA6QZ2.js.map
1088
+ //# sourceMappingURL=tui-VQA6AJPW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tui/index.ts","../src/tui/activity.ts","../src/tui/interrupt.ts","../src/tui/traces.ts","../src/tui/theme.ts"],"sourcesContent":["/**\n * Janet's interactive TUI — a minimal pi-tui chat.\n *\n * The transcript renders in strict chronological order: each run of assistant\n * text becomes its own markdown block, and a tool line / question / approval\n * \"closes\" the current block so the next text appears BELOW it (rather than the\n * whole answer streaming at the top while tools pile up underneath).\n *\n * Approvals are governed by the controller's tool-category policy: reads,\n * skills, task bookkeeping, ask_user, and bundle edits never prompt. Execution,\n * MCP, and unknown future tools ask — and the prompt offers \"always allow\" for\n * the session. Questions with options render as an arrow-key SelectList.\n */\nimport {\n Container,\n Editor,\n Loader,\n Markdown,\n ProcessTerminal,\n SelectList,\n Spacer,\n TUI,\n Text,\n matchesKey,\n} from \"@earendil-works/pi-tui\";\nimport type { Component, SelectItem } from \"@earendil-works/pi-tui\";\nimport type { AgentControllerEvent } from \"@mastra/core/agent-controller\";\nimport { bootJanet, type BootOptions } from \"../agent/controller.js\";\nimport { messageText } from \"../headless/format.js\";\nimport { GREETING } from \"../agent/persona.js\";\nimport { getAuthStorage } from \"../gateways/oauth/claude-max.js\";\nimport {\n completeOnboarding,\n loadSettings,\n rememberModel,\n rememberObservability,\n} from \"../onboarding/settings.js\";\nimport {\n NATIVE_PROVIDER_DEFINITIONS,\n availableModels,\n discoverAvailableModels,\n environmentApiKeyConfigured,\n groupModelsByProvider,\n normalizeModelSelection,\n type ModelChoice,\n type ProviderModelGroup,\n} from \"../onboarding/providers.js\";\nimport { resolveObservabilityConfig } from \"../observability/config.js\";\nimport {\n formatObservabilityStatus,\n safeObservabilityEndpoint,\n} from \"../observability/runtime.js\";\nimport type {\n ObservabilityCaptureMode,\n ObservabilitySettings,\n} from \"../observability/types.js\";\nimport { toolActivityLabel, toolErrorLabel } from \"./activity.js\";\nimport { createInterruptController, type InterruptResult } from \"./interrupt.js\";\nimport { formatTraceTree, traceStatus } from \"./traces.js\";\nimport { c, editorTheme, markdownTheme } from \"./theme.js\";\n\n/** OAuth providers janet can log in to. */\nconst OAUTH_PROVIDERS = [\"anthropic\", \"openai-codex\"] as const;\n\nconst HELP_TEXT = `Commands:\n /models Pick a provider, then a model\n /model [provider/id] Open the picker, or switch directly by id\n /providers Show detected and available providers\n /login <provider> [mode]\n Log in; OpenAI mode is browser or device\n /logout <provider> Remove stored credentials for a provider\n /auth Show which providers are authenticated\n /observability Configure opt-in tracing\n /traces Browse recent local traces\n /cancel Cancel the active run\n /help This help\n /quit Exit (double Ctrl+C also works)\n\nWhile Janet is working, Esc or Ctrl+C cancels the active run.\nAnything else is a message to Janet.`;\n\ninterface PendingApproval {\n toolCallId: string;\n toolName: string;\n}\n\ninterface QuestionOption {\n label: string;\n description?: string;\n}\n\ninterface PendingQuestion {\n toolCallId: string;\n options?: QuestionOption[];\n multi: boolean;\n}\n\n/** The assistant text block currently being streamed (one segment between tools). */\ninterface ActiveMessage {\n id: string;\n committedLen: number;\n comp: Markdown | null;\n lastText: string;\n}\n\n/** Map a typed answer to ask_user resume data (free-text or multi-select). */\nfunction resolveAnswer(q: PendingQuestion, text: string): string | string[] | undefined {\n if (!q.options?.length) return text;\n const opts = q.options;\n const pick = (token: string): string | undefined => {\n const t = token.trim();\n if (!t) return undefined;\n const n = Number(t);\n if (Number.isInteger(n) && n >= 1 && n <= opts.length) return opts[n - 1]!.label;\n const exact = opts.find((o) => o.label.toLowerCase() === t.toLowerCase());\n if (exact) return exact.label;\n return opts.find((o) => o.label.toLowerCase().startsWith(t.toLowerCase()))?.label;\n };\n if (q.multi) {\n const picks = text.split(\",\").map(pick);\n return picks.some((p) => p === undefined) ? undefined : (picks as string[]);\n }\n return pick(text);\n}\n\nexport async function runTui(opts: Omit<BootOptions, \"interactive\">): Promise<number> {\n const { controller, session, paths, herdrDetach, observability } = await bootJanet({\n ...opts,\n interactive: true,\n });\n\n // The interactive approval policy is set deterministically in the controller's\n // initialState (reads/edits/meta never prompt; only execute asks, with an\n // \"always allow\" option) — see INTERACTIVE_RULES in controller.ts.\n\n // Model precedence: an already-persisted per-thread selection, else\n // JANET_MODEL, else the global onboarding default. If none, the first-run\n // wizard runs after the UI is up.\n const persistedModel = process.env[\"JANET_MODEL\"] || loadSettings().defaultModelId;\n const presetModel = persistedModel\n ? normalizeModelSelection(persistedModel, availableModels())\n : undefined;\n if (!session.model.hasSelection() && presetModel) {\n await session.model.switch({ modelId: presetModel });\n }\n\n const terminal = new ProcessTerminal();\n const ui = new TUI(terminal);\n const chat = new Container();\n const status = new Text(\"\", 1, 0);\n const editor = new Editor(ui, editorTheme);\n const loader = new Loader(ui, c.accent, c.dim, \"Janet is thinking…\");\n\n ui.addChild(chat);\n ui.addChild(new Spacer(1));\n ui.addChild(editor);\n ui.addChild(status);\n\n let running = false;\n let loaderMounted = false;\n let pendingApproval: PendingApproval | null = null;\n let pendingQuestion: PendingQuestion | null = null;\n let pendingInput: ((text: string) => void) | null = null;\n let activeSelect: SelectList | null = null;\n let active: ActiveMessage | null = null;\n let cancelRequested = false;\n let modelPickerLoading = false;\n const activeTools = new Map<string, string>();\n\n const updateStatus = (): void => {\n const model = session.model.hasSelection() ? session.model.get() : \"no model — /model <id>\";\n const tracing = observability.status.enabled\n ? c.dim(` · trace:${observability.status.capture}`)\n : \"\";\n const state =\n pendingInput\n ? \"enter the requested value\"\n : pendingQuestion || activeSelect\n ? \"answer Janet's question\"\n : pendingApproval\n ? \"awaiting approval\"\n : cancelRequested\n ? \"cancelling\"\n : running\n ? \"working · Esc/Ctrl+C cancels\"\n : \"idle\";\n status.setText(\n c.dim(`${paths.projectPath} · `) +\n c.accent(model) +\n c.dim(` · ${state}`) +\n tracing,\n );\n ui.requestRender();\n };\n\n // Keep the spinner (and any focused select) visually last by inserting new\n // content before them.\n const appendToChat = (comp: Component): void => {\n if (loaderMounted) chat.removeChild(loader);\n if (activeSelect) chat.removeChild(activeSelect);\n chat.addChild(comp);\n if (activeSelect) chat.addChild(activeSelect);\n if (loaderMounted) chat.addChild(loader);\n ui.requestRender();\n };\n\n const addLine = (text: string): void => appendToChat(new Text(text, 1, 0));\n\n const setLoader = (on: boolean): void => {\n if (on && !loaderMounted) {\n chat.addChild(loader);\n loader.start();\n loaderMounted = true;\n } else if (!on && loaderMounted) {\n loader.stop();\n chat.removeChild(loader);\n loaderMounted = false;\n }\n ui.requestRender();\n };\n\n // Freeze the current text segment so the next assistant text starts a new\n // block below whatever we're about to insert (a tool line, question, etc.).\n const closeSegment = (): void => {\n if (active) {\n active.committedLen = active.lastText.length;\n active.comp = null;\n }\n };\n\n const answerQuestion = (resumeData: string | string[], echo: string): void => {\n if (activeSelect) {\n chat.removeChild(activeSelect);\n activeSelect = null;\n }\n const q = pendingQuestion;\n pendingQuestion = null;\n ui.setFocus(editor);\n addLine(c.user(`❯ ${echo}`));\n setLoader(true);\n updateStatus();\n if (q) void session.respondToToolSuspension({ toolCallId: q.toolCallId, resumeData });\n };\n\n const onEvent = (event: AgentControllerEvent): void => {\n switch (event.type) {\n case \"agent_start\":\n running = true;\n cancelRequested = false;\n active = null;\n activeTools.clear();\n loader.setMessage(\"Janet is thinking…\");\n setLoader(true);\n updateStatus();\n break;\n case \"message_update\":\n case \"message_end\": {\n if (event.message.role !== \"assistant\") break;\n const text = messageText(event.message);\n if (!text) break;\n if (!active || active.id !== event.message.id) {\n active = { id: event.message.id, committedLen: 0, comp: null, lastText: \"\" };\n }\n active.lastText = text;\n const tail = text.slice(active.committedLen);\n if (!tail) break;\n if (!active.comp) {\n active.comp = new Markdown(tail, 1, 0, markdownTheme);\n appendToChat(active.comp);\n } else {\n active.comp.setText(tail);\n ui.requestRender();\n }\n break;\n }\n case \"tool_start\":\n closeSegment();\n if (event.toolName !== \"ask_user\") {\n activeTools.set(event.toolCallId, event.toolName);\n loader.setMessage(toolActivityLabel(event.toolName));\n }\n break;\n case \"tool_end\":\n activeTools.delete(event.toolCallId);\n loader.setMessage(\n activeTools.size\n ? toolActivityLabel(Array.from(activeTools.values()).at(-1)!)\n : \"Janet is thinking…\",\n );\n if (event.isError) {\n closeSegment();\n addLine(c.warn(` ${toolErrorLabel(event.result)}`));\n }\n break;\n case \"tool_suspended\": {\n closeSegment();\n activeTools.delete(event.toolCallId);\n setLoader(false);\n const payload = event.suspendPayload as {\n question?: string;\n options?: QuestionOption[];\n selectionMode?: string;\n };\n const question = payload?.question ?? `Janet needs input for ${event.toolName}.`;\n const options = payload?.options;\n const multi = payload?.selectionMode === \"multi_select\";\n addLine(c.accentBold(` Janet asks: ${question}`));\n\n if (options?.length && !multi) {\n // Arrow-key selection (↑/↓, enter), like a native picker.\n const items: SelectItem[] = options.map((o) => ({\n value: o.label,\n label: o.label,\n ...(o.description ? { description: o.description } : {}),\n }));\n const select = new SelectList(items, Math.min(items.length, 8), editorTheme.selectList);\n select.onSelect = (item: SelectItem) => answerQuestion(item.value, item.label);\n activeSelect = select;\n pendingQuestion = { toolCallId: event.toolCallId, options, multi: false };\n chat.addChild(select);\n addLine(c.dim(\" Use ↑/↓ and Enter.\"));\n ui.setFocus(select);\n } else {\n pendingQuestion = { toolCallId: event.toolCallId, options, multi };\n if (options?.length) {\n options.forEach((o, i) =>\n addLine(c.accent(` ${i + 1}. `) + o.label + (o.description ? c.dim(` — ${o.description}`) : \"\")),\n );\n addLine(c.dim(\" Reply with numbers or labels, then press Enter.\"));\n } else {\n addLine(c.dim(\" Type your answer, then press Enter.\"));\n }\n }\n updateStatus();\n break;\n }\n case \"tool_approval_required\":\n closeSegment();\n activeTools.delete(event.toolCallId);\n pendingApproval = { toolCallId: event.toolCallId, toolName: event.toolName };\n addLine(\n c.warn(` Janet wants to run ${c.bold(event.toolName)}.`) +\n c.dim(\" y = yes · n = no · a = always allow this kind\"),\n );\n updateStatus();\n break;\n case \"error\": {\n closeSegment();\n const err = event.error as Error & { responseBody?: string };\n addLine(\n c.error(` Error: ${err?.message || \"unknown\"}${err?.responseBody ? ` — ${err.responseBody.slice(0, 200)}` : \"\"}`),\n );\n break;\n }\n case \"model_changed\":\n updateStatus();\n break;\n case \"agent_end\":\n running = false;\n cancelRequested = false;\n activeTools.clear();\n loader.setMessage(\"Janet is thinking…\");\n if (event.reason !== \"suspended\") pendingQuestion = null;\n setLoader(false);\n updateStatus();\n break;\n }\n };\n const unsubscribe = session.subscribe(onEvent);\n let removeInputListener = (): void => {};\n let sigintHandler: (() => void) | undefined;\n\n const shutdown = async (code: number): Promise<never> => {\n removeInputListener();\n if (sigintHandler) process.off(\"SIGINT\", sigintHandler);\n unsubscribe();\n herdrDetach();\n ui.stop();\n await observability.flush().catch(() => {});\n await controller.destroy().catch(() => {});\n process.exit(code);\n };\n\n const notifyInterrupt = (result: Exclude<InterruptResult, \"ignored\">): void => {\n switch (result) {\n case \"cancelled\":\n addLine(c.dim(\" Cancelling the active run…\"));\n break;\n case \"cleared\":\n break;\n case \"exit\":\n break;\n case \"exit-hint\":\n addLine(c.dim(\" Press Ctrl+C again to quit.\"));\n break;\n }\n updateStatus();\n };\n\n const abortActiveRun = (): void => {\n if (cancelRequested) return;\n cancelRequested = true;\n pendingApproval = null;\n pendingQuestion = null;\n activeTools.clear();\n if (activeSelect) {\n chat.removeChild(activeSelect);\n activeSelect = null;\n }\n ui.setFocus(editor);\n loader.setMessage(\"Cancelling…\");\n session.abort();\n };\n\n const interrupts = createInterruptController({\n isRunning: () => running,\n hasInput: () => editor.getText().length > 0,\n abortRun: abortActiveRun,\n clearInput: () => {\n editor.setText(\"\");\n ui.requestRender();\n },\n exit: () => {\n void shutdown(0);\n },\n notify: notifyInterrupt,\n });\n\n // Input listeners run before the focused component, so cancellation works\n // during pickers, approvals, questions, and streamed tool activity.\n removeInputListener = ui.addInputListener((data) => {\n if (matchesKey(data, \"ctrl+c\")) {\n interrupts.handleCtrlC();\n return { consume: true };\n }\n if (matchesKey(data, \"escape\") && running) {\n interrupts.handleEscape();\n return { consume: true };\n }\n return undefined;\n });\n\n // Raw terminals normally deliver Ctrl+C as input. Keep a SIGINT fallback for\n // terminals and supervisors that preserve normal signal handling.\n sigintHandler = () => {\n interrupts.handleCtrlC();\n };\n process.on(\"SIGINT\", sigintHandler);\n\n // Ask the user for one value; the next editor submit resolves it. Used by the\n // OAuth login flow (paste-code / prompts).\n const promptInput = (message: string, placeholder?: string): Promise<string> => {\n addLine(c.accentBold(` ${message}`));\n if (placeholder) addLine(c.dim(` (${placeholder})`));\n return new Promise((resolve) => {\n pendingInput = resolve;\n updateStatus();\n });\n };\n\n const closeActiveSelect = (select: SelectList): void => {\n chat.removeChild(select);\n if (activeSelect === select) activeSelect = null;\n ui.setFocus(editor);\n };\n\n const selectModel = async (modelId: string): Promise<void> => {\n try {\n await session.model.switch({ modelId });\n completeOnboarding(modelId, new Date().toISOString());\n rememberModel(modelId);\n addLine(c.accentBold(` ✓ Using ${modelId}.`) + c.dim(\" (saved as your default)\"));\n } catch (error) {\n addLine(c.error(` Could not select ${modelId}: ${(error as Error).message}`));\n } finally {\n updateStatus();\n }\n };\n\n const showProviderModels = (group: ProviderModelGroup): void => {\n const current = session.model.hasSelection() ? session.model.get() : null;\n const currentChoice = group.models.find((choice) => choice.id === current);\n const ordered = currentChoice\n ? [currentChoice, ...group.models.filter((choice) => choice.id !== current)]\n : group.models;\n // Large gateways can expose hundreds of models. Keep the arrow list useful\n // and always offer an exact model-id entry path.\n const visible = ordered.slice(0, 29);\n const items: SelectItem[] = visible.map((choice) => ({\n value: choice.id,\n label: choice.id === current ? `${choice.label} (current)` : choice.label,\n description: choice.id,\n }));\n items.push({\n value: \"__janet_enter_model_id__\",\n label: \"Enter another model ID…\",\n description:\n ordered.length > visible.length\n ? `${ordered.length - visible.length} more catalog models; enter the exact id`\n : `Use any ${group.id}/model supported by Mastra`,\n });\n\n addLine(c.accentBold(` ${group.label} models`));\n addLine(c.dim(\" ↑/↓ to move, enter to choose:\"));\n const select = new SelectList(\n items,\n Math.min(items.length, 10),\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n closeActiveSelect(select);\n if (item.value === \"__janet_enter_model_id__\") {\n void promptInput(\n `Model id for ${group.label}:`,\n `${group.id}/model-name`,\n ).then((input) => {\n const modelId = input.startsWith(`${group.id}/`)\n ? input\n : `${group.id}/${input}`;\n void selectModel(modelId);\n });\n return;\n }\n void selectModel(item.value);\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n const showProviderPicker = (choices: ModelChoice[]): void => {\n const groups = groupModelsByProvider(choices);\n if (!groups.length) {\n addLine(c.dim(\" No providers are configured yet. Set one up, then try again:\"));\n addLine(c.dim(\" • Vertex AI: gcloud auth application-default login (+ GOOGLE_VERTEX_PROJECT)\"));\n addLine(c.dim(\" • Anthropic: set ANTHROPIC_API_KEY, or /login anthropic\"));\n addLine(c.dim(\" • OpenAI: set OPENAI_API_KEY, or /login openai-codex\"));\n addLine(c.dim(\" • Bedrock: configure AWS credentials\"));\n addLine(c.dim(\" • More: /providers lists native Mastra environment variables\"));\n updateStatus();\n return;\n }\n\n addLine(c.dim(\" ↑/↓ to move, enter to choose a provider:\"));\n const select = new SelectList(\n groups.map((group) => ({\n value: group.id,\n label: group.label,\n description: `${group.models.length} model${group.models.length === 1 ? \"\" : \"s\"} · ${group.via}`,\n })),\n Math.min(groups.length, 10),\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n closeActiveSelect(select);\n const group = groups.find((candidate) => candidate.id === item.value);\n if (group) showProviderModels(group);\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n // Mastra supplies the native provider catalog and auth status. Janet layers\n // its ADC/AWS/OAuth choices over that catalog and retains local fallbacks for\n // offline startup.\n const showModelPicker = (intro?: string): void => {\n if (modelPickerLoading) {\n addLine(c.dim(\" The provider catalog is already loading.\"));\n return;\n }\n if (intro) addLine(c.accentBold(intro));\n addLine(c.dim(\" Loading configured providers…\"));\n modelPickerLoading = true;\n updateStatus();\n void discoverAvailableModels(() => controller.listAvailableModels())\n .then(showProviderPicker)\n .catch((error: Error) => {\n addLine(c.error(` Could not load providers: ${error.message}`));\n showProviderPicker(availableModels());\n })\n .finally(() => {\n modelPickerLoading = false;\n updateStatus();\n });\n };\n\n const showProviders = (): void => {\n addLine(c.accentBold(\" Model providers\"));\n void discoverAvailableModels(() => controller.listAvailableModels()).then((choices) => {\n const groups = groupModelsByProvider(choices);\n if (groups.length) {\n addLine(c.dim(\" Available now:\"));\n for (const group of groups) {\n addLine(c.dim(` • ${group.label}: ${group.via}`));\n }\n } else {\n addLine(c.dim(\" No provider credentials detected.\"));\n }\n\n const missing = NATIVE_PROVIDER_DEFINITIONS.filter(\n (provider) => !environmentApiKeyConfigured(provider.id),\n );\n if (missing.length) {\n addLine(c.dim(\" Add another Mastra-native provider:\"));\n for (const provider of missing) {\n addLine(c.dim(` • ${provider.label}: ${provider.envVars.join(\" or \")}`));\n }\n }\n updateStatus();\n });\n };\n\n const savedObservabilitySummary = (): string => {\n const saved = loadSettings().observability;\n const resolved = resolveObservabilityConfig(saved, {});\n return formatObservabilityStatus({\n enabled: resolved.enabled,\n capture: resolved.capture,\n sampleRate: resolved.sampleRate,\n destinations: [\n ...(resolved.local.enabled ? [\"local\"] : []),\n ...(resolved.remote\n ? [\n resolved.remote.kind === \"phoenix\"\n ? `phoenix (${safeObservabilityEndpoint(resolved.remote.endpoint)})`\n : `otlp (${safeObservabilityEndpoint(resolved.remote.endpoint)})`,\n ]\n : []),\n ],\n warnings: resolved.warnings,\n });\n };\n\n const persistObservability = (settings: ObservabilitySettings): void => {\n rememberObservability(settings);\n addLine(c.accentBold(\" ✓ Observability settings saved.\"));\n addLine(c.dim(` Saved: ${savedObservabilitySummary()}`));\n addLine(c.dim(\" Restart Janet to apply the new setting.\"));\n updateStatus();\n };\n\n const confirmFullCapture = (\n base: Omit<ObservabilitySettings, \"capture\">,\n ): void => {\n addLine(\n c.warn(\n \" Full capture includes prompts, responses, and tool payloads. Do not use it with sensitive material.\",\n ),\n );\n const select = new SelectList(\n [\n {\n value: \"no\",\n label: \"Keep metadata-only capture\",\n description: \"Recommended. Content stays out of traces.\",\n },\n {\n value: \"yes\",\n label: \"Enable full capture\",\n description: \"I understand trace content may contain sensitive data.\",\n },\n ],\n 2,\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n closeActiveSelect(select);\n persistObservability({\n ...base,\n capture: item.value === \"yes\" ? \"full\" : \"metadata\",\n });\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n const chooseCaptureMode = (\n base: Omit<ObservabilitySettings, \"capture\">,\n ): void => {\n addLine(c.accentBold(\" What may Janet include in traces?\"));\n const select = new SelectList(\n [\n {\n value: \"metadata\",\n label: \"Metadata only\",\n description: \"Timing, tool names, model, tokens, status, and errors.\",\n },\n {\n value: \"full\",\n label: \"Full content\",\n description: \"Also includes prompts, responses, and tool payloads.\",\n },\n ],\n 2,\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n closeActiveSelect(select);\n const capture = item.value as ObservabilityCaptureMode;\n if (capture === \"full\") {\n confirmFullCapture(base);\n } else {\n persistObservability({ ...base, capture });\n }\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n const showObservabilityPicker = (): void => {\n if (running) {\n addLine(c.dim(\" Cancel the active run before changing observability settings.\"));\n return;\n }\n addLine(c.accentBold(\" Configure observability\"));\n addLine(c.dim(` Active now: ${formatObservabilityStatus(observability.status)}`));\n addLine(c.dim(\" Tracing is opt-in and changes apply after restart.\"));\n const select = new SelectList(\n [\n {\n value: \"off\",\n label: \"Off\",\n description: \"No spans, trace database, or network export.\",\n },\n {\n value: \"local\",\n label: \"Local trace history\",\n description: \"Store traces in ~/.agent-knowledge/observability.db.\",\n },\n {\n value: \"phoenix\",\n label: \"Phoenix\",\n description: \"Send OTLP traces to http://localhost:6006.\",\n },\n {\n value: \"otlp\",\n label: \"Custom OTLP\",\n description: \"Send OTLP/HTTP protobuf traces to your endpoint.\",\n },\n ],\n 4,\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n closeActiveSelect(select);\n if (item.value === \"off\") {\n persistObservability({\n capture: \"off\",\n sampleRate: 1,\n local: { enabled: false, retentionDays: 7 },\n });\n return;\n }\n if (item.value === \"local\") {\n chooseCaptureMode({\n sampleRate: 1,\n local: { enabled: true, retentionDays: 7 },\n });\n return;\n }\n if (item.value === \"phoenix\") {\n chooseCaptureMode({\n sampleRate: 1,\n local: { enabled: false, retentionDays: 7 },\n remote: {\n kind: \"phoenix\",\n endpoint: \"http://localhost:6006\",\n projectName: \"janet\",\n },\n });\n return;\n }\n void promptInput(\n \"OTLP endpoint (for example, http://localhost:4318):\",\n ).then((endpoint) => {\n try {\n const parsed = new URL(endpoint);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") throw new Error();\n if (parsed.username || parsed.password || parsed.search || parsed.hash) {\n addLine(\n c.error(\n \" Do not put credentials or query parameters in the saved endpoint. Use OTEL_EXPORTER_OTLP_HEADERS.\",\n ),\n );\n return;\n }\n } catch {\n addLine(c.error(\" Endpoint must be a valid HTTP or HTTPS URL.\"));\n return;\n }\n chooseCaptureMode({\n sampleRate: 1,\n local: { enabled: false, retentionDays: 7 },\n remote: {\n kind: \"otlp\",\n endpoint,\n },\n });\n });\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n const showLocalTraces = async (): Promise<void> => {\n if (running) {\n addLine(c.dim(\" Cancel the active run before browsing traces.\"));\n return;\n }\n if (!observability.config.local.enabled) {\n addLine(c.dim(\" Local trace history is not active. Use /observability to enable it.\"));\n return;\n }\n await observability.flush().catch(() => {});\n const store = await observability.storage.getStore(\"observability\");\n if (!store) {\n addLine(c.error(\" Local trace storage is unavailable.\"));\n return;\n }\n const recent = await store.listTraces({\n pagination: { page: 0, perPage: 10 },\n orderBy: { field: \"startedAt\", direction: \"DESC\" },\n });\n if (!recent.spans.length) {\n addLine(c.dim(\" No local traces yet.\"));\n return;\n }\n\n addLine(c.accentBold(\" Recent local traces\"));\n const select = new SelectList(\n recent.spans.map((span) => {\n const state = traceStatus(span);\n const marker = state === \"error\" ? \"✗\" : state === \"running\" ? \"…\" : \"✓\";\n return {\n value: span.traceId,\n label: `${marker} ${span.name}`,\n description: `${span.startedAt.toLocaleString()} · ${span.traceId}`,\n };\n }),\n Math.min(recent.spans.length, 10),\n editorTheme.selectList,\n );\n select.onSelect = (item: SelectItem) => {\n closeActiveSelect(select);\n void store.getTrace({ traceId: item.value }).then((trace) => {\n if (!trace) {\n addLine(c.error(` Trace not found: ${item.value}`));\n return;\n }\n addLine(c.accentBold(` Trace ${trace.traceId}`));\n for (const line of formatTraceTree(trace.spans)) {\n addLine(c.dim(` ${line}`));\n }\n }).catch((error: Error) => {\n addLine(c.error(` Could not read trace: ${error.message}`));\n });\n };\n activeSelect = select;\n chat.addChild(select);\n ui.setFocus(select);\n updateStatus();\n };\n\n const handleCommand = async (text: string): Promise<void> => {\n const [cmd, ...rest] = text.slice(1).split(/\\s+/);\n switch (cmd) {\n case \"quit\":\n case \"exit\":\n await shutdown(0);\n break;\n case \"help\":\n addLine(c.dim(HELP_TEXT));\n break;\n case \"cancel\":\n if (interrupts.handleEscape() === \"ignored\") {\n addLine(c.dim(\" No active run to cancel.\"));\n }\n break;\n case \"observability\": {\n const action = rest[0]?.trim().toLowerCase();\n if (action === \"status\") {\n addLine(c.dim(` Active: ${formatObservabilityStatus(observability.status)}`));\n addLine(c.dim(` Saved: ${savedObservabilitySummary()}`));\n } else if (action === \"off\") {\n persistObservability({\n capture: \"off\",\n sampleRate: 1,\n local: { enabled: false, retentionDays: 7 },\n });\n } else if (!action) {\n showObservabilityPicker();\n } else {\n addLine(c.dim(\"Usage: /observability [status | off]\"));\n }\n break;\n }\n case \"traces\":\n await showLocalTraces();\n break;\n case \"login\": {\n const providerId = (rest[0] || \"anthropic\").trim();\n if (!(OAUTH_PROVIDERS as readonly string[]).includes(providerId)) {\n addLine(c.dim(`Usage: /login <${OAUTH_PROVIDERS.join(\" | \")}>`));\n break;\n }\n const authMode = rest[1]?.trim();\n if (\n authMode &&\n (providerId !== \"openai-codex\" || ![\"browser\", \"device\"].includes(authMode))\n ) {\n addLine(c.dim(\"Usage: /login openai-codex [browser | device]\"));\n break;\n }\n addLine(c.dim(`Starting ${providerId} login…`));\n try {\n await getAuthStorage().login(providerId, {\n onAuth: (info) => {\n addLine(c.accent(\" Open this URL in your browser to authorize:\"));\n addLine(\" \" + info.url);\n if (info.instructions) addLine(c.dim(\" \" + info.instructions));\n },\n onProgress: (m) => addLine(c.dim(\" \" + m)),\n onManualCodeInput: () => promptInput(\"Paste the code shown after you authorize:\"),\n onPrompt: (p) => promptInput(p.message, p.placeholder),\n ...(authMode ? { authMode } : {}),\n });\n addLine(c.accentBold(` ✓ Logged in to ${providerId}.`));\n updateStatus();\n } catch (err) {\n addLine(c.error(` Login failed: ${(err as Error).message}`));\n } finally {\n // A successful browser callback can win the race with the manual-code\n // prompt. Disarm that abandoned prompt so it cannot consume the next\n // chat message after login completes.\n pendingInput = null;\n updateStatus();\n }\n break;\n }\n case \"logout\": {\n const providerId = rest[0]?.trim();\n if (!providerId) {\n addLine(c.dim(`Usage: /logout <${OAUTH_PROVIDERS.join(\" | \")}>`));\n break;\n }\n const storage = getAuthStorage();\n storage.logout(providerId); // OAuth credential\n storage.remove(`apikey:${providerId}`); // stored API key slot, if any\n addLine(c.dim(`Logged out of ${providerId}.`));\n break;\n }\n case \"auth\": {\n const storage = getAuthStorage();\n storage.reload();\n const providers = storage.list();\n if (!providers.length) {\n addLine(c.dim(\"No stored credentials. Use /login <provider>, or set an API key env var\"));\n addLine(c.dim(\"(ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_VERTEX_PROJECT, AWS_*).\"));\n } else {\n for (const p of providers) {\n const cred = storage.get(p);\n addLine(c.dim(` ${p}: `) + (cred?.type === \"oauth\" ? c.accent(\"OAuth (subscription)\") : \"API key\"));\n }\n }\n break;\n }\n case \"model\": {\n const inputId = rest.join(\" \").trim();\n // No id → open the picker; an explicit id still works for power users.\n if (!inputId) {\n showModelPicker();\n break;\n }\n const id = normalizeModelSelection(inputId, availableModels());\n await selectModel(id);\n break;\n }\n case \"models\":\n showModelPicker();\n break;\n case \"providers\":\n showProviders();\n break;\n default:\n addLine(c.dim(`Unknown command /${cmd}. Try /help.`));\n }\n };\n\n editor.onSubmit = (raw: string) => {\n const text = raw.trim();\n editor.setText(\"\");\n if (!text) return;\n\n // Feed normal prompt input (messages + slash commands) into the editor's\n // built-in up/down history. Skip transient responses — approvals, question\n // answers, and paste-codes shouldn't clutter recall.\n if (!pendingInput && !pendingApproval && !pendingQuestion) {\n editor.addToHistory(text);\n }\n\n // A requested value (e.g. an OAuth paste-code) consumes the next submit.\n // Don't echo it verbatim — it may be a credential.\n if (pendingInput) {\n const resolve = pendingInput;\n pendingInput = null;\n addLine(c.dim(\" ❯ (value entered)\"));\n updateStatus();\n resolve(text);\n return;\n }\n\n // A typed question (free-text or multi-select) consumes the next submit.\n if (pendingQuestion && !activeSelect) {\n const resumeData = resolveAnswer(pendingQuestion, text);\n if (resumeData === undefined) {\n addLine(c.dim(\" Didn't match an option — reply with a number or an exact label.\"));\n return;\n }\n answerQuestion(resumeData, Array.isArray(resumeData) ? resumeData.join(\", \") : resumeData);\n return;\n }\n\n // Pending tool approval: y / n / a (always allow this category).\n if (pendingApproval) {\n const approve = /^y(es)?$/i.test(text);\n const decline = /^n(o)?$/i.test(text);\n const always = /^a(lways)?$/i.test(text);\n if (approve || decline || always) {\n const { toolCallId } = pendingApproval;\n pendingApproval = null;\n addLine(c.dim(always ? \" ✓ always allowed\" : approve ? \" ✓ approved\" : \" ✗ declined\"));\n updateStatus();\n void session.respondToToolApproval({\n decision: always ? \"always_allow_category\" : approve ? \"approve\" : \"decline\",\n toolCallId,\n });\n return;\n }\n addLine(c.dim(\" Answer y (yes), n (no), or a (always allow) first.\"));\n return;\n }\n\n if (text.startsWith(\"/\")) {\n void handleCommand(text);\n return;\n }\n\n addLine(c.user(`❯ ${text}`));\n if (!session.model.hasSelection()) {\n showModelPicker(\" Pick a model first:\");\n return;\n }\n void session.sendMessage({\n content: text,\n tracingOptions: observability.tracingOptionsForTurn({\n interactive: true,\n operation: \"chat\",\n resourceId: paths.resourceId,\n threadId: session.thread.getId() ?? undefined,\n }),\n }).catch((err: Error) => {\n running = false;\n setLoader(false);\n addLine(c.error(` ✗ ${err.message}`));\n updateStatus();\n });\n };\n\n addLine(c.accentBold(GREETING));\n addLine(\n c.dim(\n `Knowledge bundle: ${paths.bundlePath}\\n` +\n `Ask me anything in the bundle, or say what to ingest. /help for commands.`,\n ),\n );\n for (const warning of observability.status.warnings) {\n addLine(c.warn(`Observability: ${warning}`));\n }\n updateStatus();\n ui.start();\n ui.setFocus(editor);\n ui.requestRender();\n\n // First run (no model configured): open the picker to get set up.\n if (!session.model.hasSelection()) showModelPicker(\" Let's pick a model to get you started.\");\n\n // The TUI owns the process from here; exit happens via shutdown().\n return await new Promise<number>(() => {});\n}\n","const WORKSPACE_READ = new Set([\n \"mastra_workspace_file_stat\",\n \"mastra_workspace_grep\",\n \"mastra_workspace_lsp_inspect\",\n \"mastra_workspace_list_files\",\n \"mastra_workspace_read_file\",\n \"mastra_workspace_search\",\n]);\n\nconst WORKSPACE_WRITE = new Set([\n \"mastra_workspace_ast_edit\",\n \"mastra_workspace_delete\",\n \"mastra_workspace_edit_file\",\n \"mastra_workspace_index\",\n \"mastra_workspace_mkdir\",\n \"mastra_workspace_write_file\",\n]);\n\nconst WORKSPACE_EXECUTE = new Set([\n \"mastra_workspace_execute_command\",\n \"mastra_workspace_get_process_output\",\n \"mastra_workspace_kill_process\",\n]);\n\nconst PDF_READ = new Set([\"janet_read_pdf\", \"janet_read_pdf_chunk\"]);\nconst WEB_READ = new Set([\"janet_web_fetch\", \"janet_web_fetch_chunk\"]);\n\n/** Friendly transient status for routine tool work. */\nexport function toolActivityLabel(toolName: string): string {\n if (toolName === \"skill\" || toolName === \"skill_read\" || toolName === \"skill_search\") {\n return \"Janet is reading the playbook…\";\n }\n if (PDF_READ.has(toolName)) return \"Janet is reading the document…\";\n if (WEB_READ.has(toolName)) return \"Janet is reading the page…\";\n if (WORKSPACE_READ.has(toolName)) return \"Janet is checking the workspace…\";\n if (WORKSPACE_WRITE.has(toolName)) return \"Janet is updating the bundle…\";\n if (WORKSPACE_EXECUTE.has(toolName) || toolName.includes(\"shell\")) {\n return \"Janet is running a check…\";\n }\n return \"Janet is working…\";\n}\n\n/** Turn recoverable workspace guard failures into useful user-facing status. */\nexport function toolErrorLabel(result: unknown): string {\n const detail = String(result);\n const readRequired = detail.match(\n /File \"([^\"]+)\" (?:has not been read|was modified since last read)/,\n );\n if (readRequired) {\n return `Update paused: Janet needs to re-read \"${readRequired[1]}\" first.`;\n }\n return `Tool error: ${detail.slice(0, 140)}`;\n}\n","export type InterruptResult =\n | \"cancelled\"\n | \"cleared\"\n | \"exit\"\n | \"exit-hint\"\n | \"ignored\";\n\nexport interface InterruptActions {\n isRunning(): boolean;\n hasInput(): boolean;\n abortRun(): void;\n clearInput(): void;\n exit(): void;\n notify(result: Exclude<InterruptResult, \"ignored\">): void;\n}\n\nexport interface InterruptController {\n handleCtrlC(): InterruptResult;\n handleEscape(): InterruptResult;\n}\n\n/**\n * Centralize Janet's interrupt behavior so it works independently of whichever\n * TUI component currently owns keyboard focus.\n */\nexport function createInterruptController(\n actions: InterruptActions,\n options: {\n doublePressMs?: number;\n now?: () => number;\n } = {},\n): InterruptController {\n const doublePressMs = options.doublePressMs ?? 800;\n const now = options.now ?? Date.now;\n let lastCtrlC: number | undefined;\n\n const cancelRun = (): InterruptResult => {\n if (!actions.isRunning()) return \"ignored\";\n actions.abortRun();\n actions.notify(\"cancelled\");\n return \"cancelled\";\n };\n\n return {\n handleCtrlC(): InterruptResult {\n const pressedAt = now();\n if (lastCtrlC !== undefined && pressedAt - lastCtrlC < doublePressMs) {\n actions.notify(\"exit\");\n actions.exit();\n return \"exit\";\n }\n lastCtrlC = pressedAt;\n\n const cancelled = cancelRun();\n if (cancelled !== \"ignored\") return cancelled;\n\n if (actions.hasInput()) {\n actions.clearInput();\n actions.notify(\"cleared\");\n return \"cleared\";\n }\n\n actions.notify(\"exit-hint\");\n return \"exit-hint\";\n },\n\n handleEscape(): InterruptResult {\n return cancelRun();\n },\n };\n}\n","export interface TraceSpanSummary {\n spanId: string;\n parentSpanId?: string | null;\n name: string;\n spanType: string;\n startedAt: Date;\n endedAt?: Date | null;\n error?: unknown;\n}\n\nfunction duration(span: TraceSpanSummary): string {\n if (!span.endedAt) return \"running\";\n const elapsed = Math.max(0, span.endedAt.getTime() - span.startedAt.getTime());\n return elapsed >= 1_000 ? `${(elapsed / 1_000).toFixed(1)}s` : `${elapsed}ms`;\n}\n\nexport function traceStatus(span: TraceSpanSummary): \"error\" | \"running\" | \"ok\" {\n if (span.error) return \"error\";\n return span.endedAt ? \"ok\" : \"running\";\n}\n\n/** Render Mastra's flat span records as a compact, content-free tree. */\nexport function formatTraceTree(spans: TraceSpanSummary[]): string[] {\n const byParent = new Map<string | null, TraceSpanSummary[]>();\n const ids = new Set(spans.map((span) => span.spanId));\n for (const span of spans) {\n const parent =\n span.parentSpanId && ids.has(span.parentSpanId) ? span.parentSpanId : null;\n const children = byParent.get(parent) ?? [];\n children.push(span);\n byParent.set(parent, children);\n }\n for (const children of byParent.values()) {\n children.sort((a, b) => a.startedAt.getTime() - b.startedAt.getTime());\n }\n\n const lines: string[] = [];\n const visited = new Set<string>();\n const visit = (span: TraceSpanSummary, depth: number): void => {\n if (visited.has(span.spanId)) return;\n visited.add(span.spanId);\n const status = traceStatus(span);\n const marker = status === \"error\" ? \"✗\" : status === \"running\" ? \"…\" : \"✓\";\n lines.push(\n `${\" \".repeat(depth)}${marker} ${span.name} · ${span.spanType} · ${duration(span)}`,\n );\n for (const child of byParent.get(span.spanId) ?? []) visit(child, depth + 1);\n };\n\n for (const root of byParent.get(null) ?? []) visit(root, 0);\n return lines;\n}\n","import chalk from \"chalk\";\nimport type { EditorTheme } from \"@earendil-works/pi-tui\";\n\n/**\n * Janet's minimal terminal theme. Good Place warm: cyan accents, soft dims.\n * Kept tiny on purpose — no gradients, no branding machinery.\n */\nexport const c = {\n accent: chalk.cyan,\n accentBold: chalk.cyan.bold,\n dim: chalk.dim,\n user: chalk.green,\n error: chalk.red,\n warn: chalk.yellow,\n bold: chalk.bold,\n italic: chalk.italic,\n};\n\nexport const editorTheme: EditorTheme = {\n borderColor: (s: string) => chalk.cyan(s),\n selectList: {\n selectedPrefix: (s: string) => chalk.cyan(s),\n selectedText: (s: string) => chalk.cyan.bold(s),\n description: (s: string) => chalk.dim(s),\n scrollInfo: (s: string) => chalk.dim(s),\n noMatch: (s: string) => chalk.dim(s),\n },\n};\n\nexport const markdownTheme = {\n heading: (s: string) => chalk.cyan.bold(s),\n link: (s: string) => chalk.cyan.underline(s),\n linkUrl: (s: string) => chalk.dim(s),\n code: (s: string) => chalk.yellow(s),\n codeBlock: (s: string) => chalk.yellow(s),\n codeBlockBorder: (s: string) => chalk.dim(s),\n quote: (s: string) => chalk.italic(s),\n quoteBorder: (s: string) => chalk.dim(s),\n hr: (s: string) => chalk.dim(s),\n listBullet: (s: string) => chalk.cyan(s),\n bold: (s: string) => chalk.bold(s),\n italic: (s: string) => chalk.italic(s),\n strikethrough: (s: string) => chalk.strikethrough(s),\n underline: (s: string) => chalk.underline(s),\n};\n"],"mappings":"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","names":[]}