@vimhead.dev/norn-cli 0.1.0-tip.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +13 -0
  2. package/assets/README.md +157 -0
  3. package/assets/docs/README.md +23 -0
  4. package/assets/docs/agents.md +64 -0
  5. package/assets/docs/cli.md +183 -0
  6. package/assets/docs/composition.md +76 -0
  7. package/assets/docs/persistence.md +56 -0
  8. package/assets/docs/projects.md +75 -0
  9. package/assets/docs/recovery.md +56 -0
  10. package/assets/docs/resources.md +61 -0
  11. package/assets/docs/workflows.md +57 -0
  12. package/assets/examples/agent-then-analysis/README.md +103 -0
  13. package/assets/examples/agent-then-analysis/input.json +5 -0
  14. package/assets/examples/agent-then-analysis/norn.project.json +4 -0
  15. package/assets/examples/agent-then-analysis/plugin.ts +89 -0
  16. package/assets/examples/coordinating-multiple-agents/README.md +56 -0
  17. package/assets/examples/coordinating-multiple-agents/input.json +10 -0
  18. package/assets/examples/coordinating-multiple-agents/norn.project.json +4 -0
  19. package/assets/examples/coordinating-multiple-agents/plugin.ts +102 -0
  20. package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +52 -0
  21. package/assets/examples/coordinating-multiple-agents/work-queue.ts +153 -0
  22. package/assets/examples/minimal-workflow/README.md +71 -0
  23. package/assets/examples/minimal-workflow/norn.project.json +4 -0
  24. package/assets/examples/minimal-workflow/plugin.ts +29 -0
  25. package/assets/examples/shared-state/README.md +19 -0
  26. package/assets/examples/shared-state/input.json +1 -0
  27. package/assets/examples/shared-state/norn.project.json +4 -0
  28. package/assets/examples/shared-state/plugin.ts +47 -0
  29. package/assets/examples/worktree-development-loop/README.md +66 -0
  30. package/assets/examples/worktree-development-loop/index.ts +1 -0
  31. package/assets/examples/worktree-development-loop/manifest.ts +26 -0
  32. package/assets/examples/worktree-development-loop/norn.project.json +9 -0
  33. package/assets/examples/worktree-development-loop/plugin.ts +27 -0
  34. package/assets/examples/worktree-development-loop/shared/commands.ts +6 -0
  35. package/assets/examples/worktree-development-loop/state.ts +23 -0
  36. package/assets/examples/worktree-development-loop/workflows/development-loop/declaration.ts +8 -0
  37. package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -0
  38. package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -0
  39. package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +22 -0
  40. package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +14 -0
  41. package/assets/examples/worktree-development-loop/workflows/implementation/declaration.ts +8 -0
  42. package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +54 -0
  43. package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +3 -0
  44. package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +12 -0
  45. package/assets/examples/worktree-development-loop/workflows/planning/declaration.ts +8 -0
  46. package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +28 -0
  47. package/assets/examples/worktree-development-loop/workflows/planning/index.ts +3 -0
  48. package/assets/examples/worktree-development-loop/workflows/planning/schema.ts +12 -0
  49. package/assets/examples/worktree-development-loop/workflows/review/declaration.ts +8 -0
  50. package/assets/examples/worktree-development-loop/workflows/review/execute.ts +53 -0
  51. package/assets/examples/worktree-development-loop/workflows/review/index.ts +10 -0
  52. package/assets/examples/worktree-development-loop/workflows/review/schema.ts +23 -0
  53. package/assets/examples/worktree-development-loop/workflows/review-router/declaration.ts +12 -0
  54. package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -0
  55. package/assets/examples/worktree-development-loop/workflows/review-router/index.ts +3 -0
  56. package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +12 -0
  57. package/assets/package.json +1 -0
  58. package/assets/packages/cli/src/build-info.ts +36 -0
  59. package/assets/packages/cli/src/bun/cli.ts +16 -0
  60. package/assets/packages/cli/src/cli.ts +1135 -0
  61. package/assets/packages/cli/src/client.ts +167 -0
  62. package/assets/packages/cli/src/documentation-intro.ts +30 -0
  63. package/assets/packages/cli/src/documentation.ts +149 -0
  64. package/assets/packages/cli/src/generated-build-info.ts +12 -0
  65. package/assets/packages/cli/src/internal/agent-directory.ts +5 -0
  66. package/assets/packages/cli/src/internal/agent-response-tool.ts +96 -0
  67. package/assets/packages/cli/src/internal/agents.ts +365 -0
  68. package/assets/packages/cli/src/internal/artifacts.ts +26 -0
  69. package/assets/packages/cli/src/internal/commands.ts +180 -0
  70. package/assets/packages/cli/src/internal/documentation-bundle.ts +49 -0
  71. package/assets/packages/cli/src/internal/engine.ts +501 -0
  72. package/assets/packages/cli/src/internal/errors.ts +39 -0
  73. package/assets/packages/cli/src/internal/file-names.ts +3 -0
  74. package/assets/packages/cli/src/internal/launch-request.ts +94 -0
  75. package/assets/packages/cli/src/internal/logs.ts +41 -0
  76. package/assets/packages/cli/src/internal/metrics.ts +356 -0
  77. package/assets/packages/cli/src/internal/pi-assets.ts +95 -0
  78. package/assets/packages/cli/src/internal/resource-bindings.ts +35 -0
  79. package/assets/packages/cli/src/internal/run-lease.ts +158 -0
  80. package/assets/packages/cli/src/internal/run-log.ts +59 -0
  81. package/assets/packages/cli/src/internal/run-names.ts +36 -0
  82. package/assets/packages/cli/src/internal/run-resources.ts +23 -0
  83. package/assets/packages/cli/src/internal/run-state.ts +380 -0
  84. package/assets/packages/cli/src/internal/run-store.ts +323 -0
  85. package/assets/packages/cli/src/internal/run.ts +133 -0
  86. package/assets/packages/cli/src/internal/state-store.ts +75 -0
  87. package/assets/packages/cli/src/internal/usage.ts +70 -0
  88. package/assets/packages/cli/src/internal/workflow-registry.ts +176 -0
  89. package/assets/packages/cli/src/plugin-loader.ts +412 -0
  90. package/assets/packages/cli/src/resources.ts +67 -0
  91. package/assets/packages/core/src/agent-protocol.ts +1 -0
  92. package/assets/packages/core/src/atomic-files.ts +24 -0
  93. package/assets/packages/core/src/errors.ts +3 -0
  94. package/assets/packages/sdk/src/agent-resource-adapter.ts +11 -0
  95. package/assets/packages/sdk/src/api.ts +821 -0
  96. package/assets/packages/sdk/src/files.ts +136 -0
  97. package/assets/packages/sdk/src/index.ts +6 -0
  98. package/assets/packages/sdk/src/resources.ts +20 -0
  99. package/assets/packages/sdk/src/schema.ts +48 -0
  100. package/assets/packages/sdk/src/seer/config.ts +62 -0
  101. package/assets/packages/sdk/src/seer/index.ts +7 -0
  102. package/assets/packages/sdk/src/state-adapter.ts +75 -0
  103. package/assets/setup/providers.md +128 -0
  104. package/assets/setup/releases.md +76 -0
  105. package/assets/tests/workflow-ref.test.ts +113 -0
  106. package/bin/norn.mjs +10 -0
  107. package/dist/build-info.d.ts +30 -0
  108. package/dist/build-info.js +6 -0
  109. package/dist/cli.d.ts +2 -0
  110. package/dist/cli.js +1032 -0
  111. package/dist/client.d.ts +48 -0
  112. package/dist/client.js +118 -0
  113. package/dist/documentation-intro.d.ts +5 -0
  114. package/dist/documentation-intro.js +29 -0
  115. package/dist/documentation.d.ts +33 -0
  116. package/dist/documentation.js +132 -0
  117. package/dist/generated-build-info.d.ts +10 -0
  118. package/dist/generated-build-info.js +14 -0
  119. package/dist/internal/agent-directory.d.ts +4 -0
  120. package/dist/internal/agent-directory.js +8 -0
  121. package/dist/internal/agent-response-tool.d.ts +21 -0
  122. package/dist/internal/agent-response-tool.js +79 -0
  123. package/dist/internal/agents.d.ts +29 -0
  124. package/dist/internal/agents.js +336 -0
  125. package/dist/internal/artifacts.d.ts +10 -0
  126. package/dist/internal/artifacts.js +29 -0
  127. package/dist/internal/commands.d.ts +18 -0
  128. package/dist/internal/commands.js +147 -0
  129. package/dist/internal/documentation-bundle.d.ts +16 -0
  130. package/dist/internal/documentation-bundle.js +42 -0
  131. package/dist/internal/engine.d.ts +44 -0
  132. package/dist/internal/engine.js +399 -0
  133. package/dist/internal/errors.d.ts +14 -0
  134. package/dist/internal/errors.js +38 -0
  135. package/dist/internal/file-names.d.ts +1 -0
  136. package/dist/internal/file-names.js +7 -0
  137. package/dist/internal/launch-request.d.ts +33 -0
  138. package/dist/internal/launch-request.js +110 -0
  139. package/dist/internal/logs.d.ts +16 -0
  140. package/dist/internal/logs.js +38 -0
  141. package/dist/internal/metrics.d.ts +19 -0
  142. package/dist/internal/metrics.js +282 -0
  143. package/dist/internal/pi-assets.d.ts +13 -0
  144. package/dist/internal/pi-assets.js +94 -0
  145. package/dist/internal/resource-bindings.d.ts +13 -0
  146. package/dist/internal/resource-bindings.js +34 -0
  147. package/dist/internal/run-lease.d.ts +32 -0
  148. package/dist/internal/run-lease.js +166 -0
  149. package/dist/internal/run-log.d.ts +30 -0
  150. package/dist/internal/run-log.js +71 -0
  151. package/dist/internal/run-names.d.ts +1 -0
  152. package/dist/internal/run-names.js +144 -0
  153. package/dist/internal/run-resources.d.ts +6 -0
  154. package/dist/internal/run-resources.js +26 -0
  155. package/dist/internal/run-state.d.ts +95 -0
  156. package/dist/internal/run-state.js +323 -0
  157. package/dist/internal/run-store.d.ts +35 -0
  158. package/dist/internal/run-store.js +314 -0
  159. package/dist/internal/run.d.ts +51 -0
  160. package/dist/internal/run.js +101 -0
  161. package/dist/internal/state-store.d.ts +22 -0
  162. package/dist/internal/state-store.js +97 -0
  163. package/dist/internal/usage.d.ts +5 -0
  164. package/dist/internal/usage.js +70 -0
  165. package/dist/internal/workflow-registry.d.ts +35 -0
  166. package/dist/internal/workflow-registry.js +129 -0
  167. package/dist/plugin-loader.d.ts +55 -0
  168. package/dist/plugin-loader.js +353 -0
  169. package/dist/resources.d.ts +11 -0
  170. package/dist/resources.js +98 -0
  171. package/package.json +52 -0
@@ -0,0 +1,48 @@
1
+ import type { DeletedNornRunInfo, NornProjectInspection, NornWorkflowCatalogInfo, NornWorkflowInspection, NornRunCheckpoint, NornRunInfo, NornRunMetrics, NornWorkflowStateReader } from "@vimhead.dev/norn";
2
+ import type { NornRegisteredWorkflow } from "./internal/workflow-registry.ts";
3
+ import type { NornResolvedSeerModeConfig } from "@vimhead.dev/norn/seer";
4
+ export { NornProjectLoadError } from "./internal/errors.ts";
5
+ export type NornClientInput = {
6
+ readonly spawnCwd?: string;
7
+ readonly executablePath?: string;
8
+ };
9
+ export type NornClient = {
10
+ readonly project: {
11
+ inspect(): Promise<NornProjectInspection>;
12
+ };
13
+ readonly workflows: {
14
+ list(options?: {
15
+ readonly all?: boolean;
16
+ }): Promise<NornWorkflowCatalogInfo>;
17
+ inspect(workflowId: string): Promise<NornWorkflowInspection>;
18
+ entries(): Promise<readonly NornRegisteredWorkflow[]>;
19
+ };
20
+ readonly state: NornWorkflowStateReader;
21
+ readonly seer: {
22
+ inspect(): Promise<NornResolvedSeerModeConfig | null>;
23
+ };
24
+ readonly runs: {
25
+ start(input: {
26
+ readonly workflowId: string;
27
+ readonly params: unknown;
28
+ readonly configOverride?: unknown;
29
+ }): Promise<NornRunInfo>;
30
+ resume(input: {
31
+ readonly run: string;
32
+ readonly params?: unknown;
33
+ }): Promise<NornRunInfo>;
34
+ wait(run: string): Promise<NornRunInfo>;
35
+ list(): Promise<NornRunInfo[]>;
36
+ inspect(run: string): Promise<NornRunInfo>;
37
+ checkpoints(run: string): Promise<NornRunCheckpoint[]>;
38
+ metrics(run: string): Promise<NornRunMetrics>;
39
+ rollback(run: string, checkpointId: string): Promise<NornRunInfo>;
40
+ stop(run: string): Promise<NornRunInfo>;
41
+ kill(run: string): Promise<NornRunInfo>;
42
+ delete(run: string): Promise<DeletedNornRunInfo>;
43
+ logs(run: string, options?: {
44
+ readonly follow?: boolean;
45
+ }): AsyncIterable<Record<string, unknown>>;
46
+ };
47
+ };
48
+ export declare function createNornClient(input?: NornClientInput): NornClient;
package/dist/client.js ADDED
@@ -0,0 +1,118 @@
1
+ // src/client.ts
2
+ import { spawn } from "node:child_process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { createInterface } from "node:readline";
5
+ import { loadNornProject } from "./plugin-loader.js";
6
+ import { NornProjectLoadError } from "./internal/errors.js";
7
+ import { NornProjectLoadError as NornProjectLoadError2 } from "./internal/errors.js";
8
+ function createNornClient(input = {}) {
9
+ const processRunner = new NornProcessRunner(input);
10
+ const catalog = new NornWorkflowCatalog(input.spawnCwd ?? process.cwd());
11
+ const client = {
12
+ project: {
13
+ inspect: async () => processRunner.readJson(["project", "inspect"])
14
+ },
15
+ workflows: {
16
+ list: async (options) => processRunner.readJson(["workflows", "list", ...options?.all ? ["--all"] : []]),
17
+ inspect: async (workflowId) => processRunner.readJson(["workflows", "inspect", workflowId]),
18
+ entries: async () => (await catalog.load()).workflows
19
+ },
20
+ state: catalog.state,
21
+ seer: {
22
+ inspect: async () => (await processRunner.readJson(["seer", "inspect"])).seerMode
23
+ },
24
+ runs: {
25
+ start: async (startInput) => (await processRunner.readJson(["runs", "start", startInput.workflowId], JSON.stringify(runStartInput(startInput)))).run,
26
+ resume: async (resumeInput) => (await processRunner.readJson(["runs", "resume", resumeInput.run], runResumeStdin(resumeInput))).run,
27
+ wait: async (run) => (await processRunner.readJson(["runs", "wait", run])).run,
28
+ list: async () => (await processRunner.readJson(["runs", "list"])).runs,
29
+ inspect: async (run) => (await processRunner.readJson(["runs", "inspect", run])).run,
30
+ checkpoints: async (run) => (await processRunner.readJson(["runs", "checkpoints", run])).checkpoints,
31
+ metrics: async (run) => (await processRunner.readJson(["runs", "metrics", run])).metrics,
32
+ rollback: async (run, checkpointId) => (await processRunner.readJson(["runs", "rollback", run, checkpointId])).run,
33
+ stop: async (run) => (await processRunner.readJson(["runs", "stop", run])).run,
34
+ kill: async (run) => (await processRunner.readJson(["runs", "kill", run])).run,
35
+ delete: async (run) => (await processRunner.readJson(["runs", "delete", run])).deleted,
36
+ logs: (run, options) => processRunner.readJsonLines(["runs", "logs", run, ...options?.follow ? ["--follow"] : []])
37
+ }
38
+ };
39
+ return client;
40
+ }
41
+ var NornWorkflowCatalog = class {
42
+ constructor(cwd) {
43
+ this.cwd = cwd;
44
+ }
45
+ cwd;
46
+ loaded;
47
+ state = {
48
+ get: async (state) => (await this.load()).state.get(state),
49
+ getOptional: async (state) => (await this.load()).state.getOptional(state)
50
+ };
51
+ load() {
52
+ this.loaded ??= loadNornProject(this.cwd).catch((error) => {
53
+ this.loaded = void 0;
54
+ throw error;
55
+ });
56
+ return this.loaded;
57
+ }
58
+ };
59
+ var NornProcessRunner = class {
60
+ constructor(input) {
61
+ this.input = input;
62
+ this.executablePath = input.executablePath ?? fileURLToPath(new URL("../bin/norn.mjs", import.meta.url));
63
+ }
64
+ input;
65
+ executablePath;
66
+ async readJson(args, stdin) {
67
+ const result = await this.spawn(args, stdin);
68
+ const parsed = JSON.parse(result.stdout);
69
+ if (parsed && typeof parsed === "object" && "error" in parsed) {
70
+ if (parsed.error?.code === "NORN_PROJECT_INVALID" && Array.isArray(parsed.error.diagnostics)) throw new NornProjectLoadError({ diagnostics: parsed.error.diagnostics });
71
+ throw new Error(parsed.error?.message ?? "Norn command failed");
72
+ }
73
+ if (result.exitCode !== 0) throw new Error(result.stderr || `Norn exited with code ${result.exitCode}`);
74
+ return parsed;
75
+ }
76
+ async *readJsonLines(args) {
77
+ const child = spawn(process.execPath, [this.executablePath, ...args], { cwd: this.input.spawnCwd, stdio: ["ignore", "pipe", "pipe"] });
78
+ let stderr = "";
79
+ child.stderr?.on("data", (chunk) => {
80
+ stderr += chunk.toString();
81
+ });
82
+ const lines = createInterface({ input: child.stdout });
83
+ for await (const line of lines) {
84
+ if (line.trim().length === 0) continue;
85
+ const parsed = JSON.parse(line);
86
+ if (parsed.error) throw new Error(String(parsed.error.message ?? "Norn stream failed"));
87
+ yield parsed;
88
+ }
89
+ const exitCode = await new Promise((resolvePromise) => child.on("close", resolvePromise));
90
+ if (exitCode !== 0) throw new Error(stderr || `Norn exited with code ${exitCode}`);
91
+ }
92
+ async spawn(args, stdin) {
93
+ return new Promise((resolvePromise, reject) => {
94
+ const child = spawn(process.execPath, [this.executablePath, ...args], { cwd: this.input.spawnCwd, stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "pipe"] });
95
+ let stdout = "";
96
+ let stderr = "";
97
+ child.stdout?.on("data", (chunk) => {
98
+ stdout += chunk.toString();
99
+ });
100
+ child.stderr?.on("data", (chunk) => {
101
+ stderr += chunk.toString();
102
+ });
103
+ child.on("error", reject);
104
+ if (stdin !== void 0) child.stdin?.end(stdin);
105
+ child.on("close", (exitCode) => resolvePromise({ stdout, stderr, exitCode }));
106
+ });
107
+ }
108
+ };
109
+ function runStartInput(input) {
110
+ return input.configOverride === void 0 ? { params: input.params } : { params: input.params, config: input.configOverride };
111
+ }
112
+ function runResumeStdin(input) {
113
+ return input.params === void 0 ? void 0 : JSON.stringify({ params: input.params });
114
+ }
115
+ export {
116
+ NornProjectLoadError2 as NornProjectLoadError,
117
+ createNornClient
118
+ };
@@ -0,0 +1,5 @@
1
+ import type { NornDocumentationLocation } from "./documentation.ts";
2
+ export declare function renderNornDocumentationIntro(input: {
3
+ readonly documentation: NornDocumentationLocation;
4
+ readonly invocation: readonly [string, ...string[]];
5
+ }): string;
@@ -0,0 +1,29 @@
1
+ // src/documentation-intro.ts
2
+ function renderNornDocumentationIntro(input) {
3
+ const { documentation, invocation } = input;
4
+ return [
5
+ "# Norn",
6
+ "Norn is a harness-agnostic workflow runtime built primarily for agents. Build agent-driven and code-driven TypeScript workflows with the Norn SDK; use the CLI to discover, run, inspect, and recover them. Norn agents are powered by the bundled, open-source and extensible Pi coding agent, independently of the outer harness. Capabilities can be authored, exercised, repaired, and reused during a task; Norn is optional.",
7
+ "",
8
+ `Runtime argv (JSON array; CLI arguments follow these entries): ${JSON.stringify(invocation)}`,
9
+ `Version: ${JSON.stringify(documentation.version)}; build commit: ${documentation.commit === null ? "unknown" : JSON.stringify(documentation.commit)}`,
10
+ "",
11
+ `Documentation index and topic routing: ${JSON.stringify(documentation.paths.index)}`,
12
+ `Main README: ${JSON.stringify(documentation.paths.readme)}`,
13
+ `Documentation directory: ${JSON.stringify(documentation.paths.docs)}`,
14
+ `Runnable examples: ${JSON.stringify(documentation.paths.examples)}`,
15
+ "Relative documentation links resolve from the containing file, not the task's working directory.",
16
+ "",
17
+ "| Rule | GOOD | BAD |",
18
+ "|---|---|---|",
19
+ "| IF authoring or repairing a Norn capability, THEN read the index and relevant linked documentation/examples. ELSE leave unrelated documentation unloaded. | Read the Norn agents reference before creating an agent. | Guess the API or load every manual upfront. |",
20
+ "",
21
+ "Live CLI contracts (argument arrays appended to the runtime argv above):",
22
+ '- ["workflows", "list"] \u2014 currently registered entrypoints.',
23
+ '- ["workflows", "inspect", "<workflow-id>"] \u2014 current instructions, params schema, and source.',
24
+ '- ["help"] \u2014 command usage.'
25
+ ].join("\n");
26
+ }
27
+ export {
28
+ renderNornDocumentationIntro
29
+ };
@@ -0,0 +1,33 @@
1
+ import type { NornBuildInfo } from "./build-info.ts";
2
+ import { type NornDocumentationBundle } from "./internal/documentation-bundle.ts";
3
+ export { renderNornDocumentationIntro } from "./documentation-intro.ts";
4
+ export type NornDocumentationSource = {
5
+ readonly kind: "local";
6
+ readonly root: string;
7
+ } | {
8
+ readonly kind: "embedded";
9
+ readonly bundle: NornDocumentationBundle;
10
+ };
11
+ export type NornDocumentationLocation = {
12
+ readonly storage: "installation" | "cache";
13
+ readonly version: string;
14
+ readonly commit: string | null;
15
+ readonly assetDigest: string | null;
16
+ readonly paths: {
17
+ readonly root: string;
18
+ readonly readme: string;
19
+ readonly index: string;
20
+ readonly docs: string;
21
+ readonly examples: string;
22
+ };
23
+ };
24
+ export declare function resolveDocumentationCacheRoot(input: {
25
+ readonly platform: string;
26
+ readonly home: string;
27
+ readonly environment: Readonly<Record<string, string | undefined>>;
28
+ }): string;
29
+ export declare function resolveNornDocumentation(input: {
30
+ readonly source: NornDocumentationSource;
31
+ readonly build: NornBuildInfo;
32
+ readonly cacheRoot: string;
33
+ }): Promise<NornDocumentationLocation>;
@@ -0,0 +1,132 @@
1
+ // src/documentation.ts
2
+ import { createHash } from "node:crypto";
3
+ import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
4
+ import { join, resolve } from "node:path";
5
+ import { DOCUMENTATION_PATHS, hashDocumentationBundle, validateDocumentationBundle } from "./internal/documentation-bundle.js";
6
+
7
+ // ../core/src/errors.ts
8
+ function isNodeError(error) {
9
+ return error instanceof Error && "code" in error;
10
+ }
11
+
12
+ // src/documentation.ts
13
+ import { renderNornDocumentationIntro } from "./documentation-intro.js";
14
+ function resolveDocumentationCacheRoot(input) {
15
+ const override = input.environment.NORN_DOCS_CACHE_DIR;
16
+ if (override) return resolve(override);
17
+ if (input.platform === "win32") return join(input.environment.LOCALAPPDATA || join(input.home, "AppData", "Local"), "norn", "docs");
18
+ if (input.platform === "darwin") return join(input.home, "Library", "Caches", "norn", "docs");
19
+ const xdgCache = input.environment.XDG_CACHE_HOME;
20
+ return join(xdgCache?.startsWith("/") ? xdgCache : join(input.home, ".cache"), "norn", "docs");
21
+ }
22
+ async function resolveNornDocumentation(input) {
23
+ let root;
24
+ let version;
25
+ let assetDigest = null;
26
+ if (input.source.kind === "local") {
27
+ root = resolve(input.source.root);
28
+ const packageInfo = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
29
+ if (typeof packageInfo.version !== "string" || packageInfo.version.length === 0) throw new Error("Invalid documentation package version");
30
+ version = packageInfo.version;
31
+ if (version !== input.build.version) throw new Error("Local documentation version does not match the runtime; rebuild or reinstall this CLI package");
32
+ await validateLocalDocumentation(root);
33
+ } else {
34
+ validateDocumentationBundle(input.source.bundle);
35
+ version = input.source.bundle.version;
36
+ if (version !== input.build.version) throw new Error("Embedded documentation version does not match the binary build");
37
+ assetDigest = hashDocumentationBundle(input.source.bundle);
38
+ const cacheKey = createHash("sha256").update(JSON.stringify({ format: 1, assetDigest, version, commit: input.build.commit })).digest("hex");
39
+ root = await new NornDocumentationCache({ cacheRoot: resolve(input.cacheRoot), cacheKey, bundle: input.source.bundle }).materialize();
40
+ }
41
+ return {
42
+ storage: input.source.kind === "local" ? "installation" : "cache",
43
+ version,
44
+ commit: input.build.commit,
45
+ assetDigest,
46
+ paths: {
47
+ root,
48
+ readme: join(root, DOCUMENTATION_PATHS.readme),
49
+ index: join(root, DOCUMENTATION_PATHS.index),
50
+ docs: join(root, DOCUMENTATION_PATHS.docs),
51
+ examples: join(root, DOCUMENTATION_PATHS.examples)
52
+ }
53
+ };
54
+ }
55
+ async function validateLocalDocumentation(root) {
56
+ for (const [key, path] of Object.entries(DOCUMENTATION_PATHS)) {
57
+ const stat = await lstat(join(root, path));
58
+ const isExpectedType = key === "docs" || key === "examples" ? stat.isDirectory() : stat.isFile();
59
+ if (!isExpectedType) throw new Error(`Invalid local documentation path: ${join(root, path)}`);
60
+ }
61
+ }
62
+ var NornDocumentationCache = class {
63
+ constructor(input) {
64
+ this.input = input;
65
+ this.root = join(input.cacheRoot, `v1-${input.cacheKey}`);
66
+ this.files = new Map(input.bundle.files.map((file) => [file.path, file.content]));
67
+ }
68
+ input;
69
+ root;
70
+ files;
71
+ async materialize() {
72
+ await mkdir(this.input.cacheRoot, { recursive: true, mode: 448 });
73
+ const cacheStat = await lstat(this.input.cacheRoot);
74
+ if (!cacheStat.isDirectory() || cacheStat.isSymbolicLink()) throw new Error(`Documentation cache root must be a real directory: ${this.input.cacheRoot}`);
75
+ if (await this.validateExistingCache()) return this.root;
76
+ const staging = await mkdtemp(join(this.input.cacheRoot, ".extract-"));
77
+ try {
78
+ for (const file of this.input.bundle.files) {
79
+ const parts = file.path.split("/");
80
+ await mkdir(join(staging, ...parts.slice(0, -1)), { recursive: true, mode: 448 });
81
+ await writeFile(join(staging, ...parts), file.content, { encoding: "utf8", flag: "wx", mode: 384 });
82
+ }
83
+ try {
84
+ await rename(staging, this.root);
85
+ } catch (error) {
86
+ if (!isNodeError(error) || !["EEXIST", "ENOTEMPTY", "EPERM", "EACCES"].includes(error.code ?? "")) throw error;
87
+ if (!await this.validateExistingCache()) throw error;
88
+ }
89
+ if (!await this.validateExistingCache()) throw new Error(`Documentation cache disappeared after extraction: ${this.root}`);
90
+ return this.root;
91
+ } finally {
92
+ await rm(staging, { recursive: true, force: true });
93
+ }
94
+ }
95
+ async validateExistingCache() {
96
+ let stat;
97
+ try {
98
+ stat = await lstat(this.root);
99
+ } catch (error) {
100
+ if (isNodeError(error) && error.code === "ENOENT") return false;
101
+ throw error;
102
+ }
103
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw this.invalidCacheError();
104
+ const seenFiles = /* @__PURE__ */ new Set();
105
+ await this.validateDirectory({ directory: this.root, prefix: "", seenFiles });
106
+ if (seenFiles.size !== this.files.size) throw this.invalidCacheError();
107
+ return true;
108
+ }
109
+ async validateDirectory(input) {
110
+ for (const entry of await readdir(input.directory, { withFileTypes: true })) {
111
+ const path = input.prefix ? `${input.prefix}/${entry.name}` : entry.name;
112
+ const absolutePath = join(input.directory, entry.name);
113
+ if (entry.isDirectory()) {
114
+ if (![...this.files.keys()].some((file) => file.startsWith(`${path}/`))) throw this.invalidCacheError();
115
+ await this.validateDirectory({ directory: absolutePath, prefix: path, seenFiles: input.seenFiles });
116
+ } else if (entry.isFile() && this.files.has(path)) {
117
+ if (!(await readFile(absolutePath)).equals(Buffer.from(this.files.get(path), "utf8"))) throw this.invalidCacheError();
118
+ input.seenFiles.add(path);
119
+ } else {
120
+ throw this.invalidCacheError();
121
+ }
122
+ }
123
+ }
124
+ invalidCacheError() {
125
+ return new Error(`Documentation cache is incomplete or modified: ${this.root}. Remove this cache entry and retry; copy examples elsewhere before editing them.`);
126
+ }
127
+ };
128
+ export {
129
+ renderNornDocumentationIntro,
130
+ resolveDocumentationCacheRoot,
131
+ resolveNornDocumentation
132
+ };
@@ -0,0 +1,10 @@
1
+ export declare const NORN_GENERATED_BUILD_INFO: {
2
+ readonly kind: "npm-registry";
3
+ readonly version: "0.1.0-tip.0";
4
+ readonly commit: "f5872d022f9a38d5c0828bf39fa4ad49ca536caa";
5
+ readonly packageSpec: "@vimhead.dev/norn-cli@tip";
6
+ readonly upgrade: {
7
+ readonly supported: false;
8
+ readonly reason: "Update @vimhead.dev/norn-cli@tip through your package manager in its existing installation scope (npm install -g only for global npm installations).";
9
+ };
10
+ };
@@ -0,0 +1,14 @@
1
+ // src/generated-build-info.ts
2
+ var NORN_GENERATED_BUILD_INFO = {
3
+ "kind": "npm-registry",
4
+ "version": "0.1.0-tip.0",
5
+ "commit": "f5872d022f9a38d5c0828bf39fa4ad49ca536caa",
6
+ "packageSpec": "@vimhead.dev/norn-cli@tip",
7
+ "upgrade": {
8
+ "supported": false,
9
+ "reason": "Update @vimhead.dev/norn-cli@tip through your package manager in its existing installation scope (npm install -g only for global npm installations)."
10
+ }
11
+ };
12
+ export {
13
+ NORN_GENERATED_BUILD_INFO
14
+ };
@@ -0,0 +1,4 @@
1
+ export declare function resolveNornAgentDirectory(input: {
2
+ home: string;
3
+ environment: NodeJS.ProcessEnv;
4
+ }): string;
@@ -0,0 +1,8 @@
1
+ // src/internal/agent-directory.ts
2
+ import { join, resolve } from "node:path";
3
+ function resolveNornAgentDirectory(input) {
4
+ return resolve(input.environment.NORN_AGENT_DIR || join(input.home, ".norn", "agent"));
5
+ }
6
+ export {
7
+ resolveNornAgentDirectory
8
+ };
@@ -0,0 +1,21 @@
1
+ import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
2
+ import type { z } from "zod";
3
+ import type { NornDispose } from "@vimhead.dev/norn";
4
+ export type NornCapturedAgentResponse = {
5
+ readonly called: boolean;
6
+ readonly response?: unknown;
7
+ };
8
+ export declare class NornAgentResponseCollector {
9
+ private readonly pending;
10
+ begin(runId: string, label: string, responseSchema: z.ZodType): NornDispose;
11
+ capture(runId: string, label: string, response: unknown): {
12
+ label: string;
13
+ response: unknown;
14
+ };
15
+ get(runId: string): NornCapturedAgentResponse;
16
+ }
17
+ export declare class NornAgentResponseToolFactory {
18
+ private readonly responseCollector;
19
+ constructor(responseCollector: NornAgentResponseCollector);
20
+ create(): ToolDefinition;
21
+ }
@@ -0,0 +1,79 @@
1
+ // src/internal/agent-response-tool.ts
2
+ import { defineTool } from "@earendil-works/pi-coding-agent";
3
+ import { Type } from "typebox";
4
+
5
+ // ../core/src/agent-protocol.ts
6
+ var AGENT_RESPONSE_TOOL_NAME = "pi_workflows_agent_response";
7
+
8
+ // src/internal/agent-response-tool.ts
9
+ import { zodErrorMessage } from "./errors.js";
10
+ var NornAgentResponseCollector = class {
11
+ pending = /* @__PURE__ */ new Map();
12
+ begin(runId, label, responseSchema) {
13
+ this.pending.set(runId, { label, responseSchema, called: false });
14
+ return () => this.pending.delete(runId);
15
+ }
16
+ capture(runId, label, response) {
17
+ const pending = this.pending.get(runId);
18
+ if (!pending) throw new Error(`No active workflow agent response slot: ${runId}`);
19
+ if (pending.label !== label) {
20
+ throw new Error(`Workflow agent response label mismatch: expected ${pending.label}, received ${label}`);
21
+ }
22
+ if (pending.called) throw new Error(`Workflow agent response already recorded for ${runId}`);
23
+ const parsed = pending.responseSchema.safeParse(response);
24
+ if (!parsed.success) throw new Error(zodErrorMessage(parsed.error));
25
+ pending.called = true;
26
+ pending.response = parsed.data;
27
+ return { label: pending.label, response: parsed.data };
28
+ }
29
+ get(runId) {
30
+ const pending = this.pending.get(runId);
31
+ return pending ? { called: pending.called, response: pending.response } : { called: false };
32
+ }
33
+ };
34
+ var NornAgentResponseToolFactory = class {
35
+ constructor(responseCollector) {
36
+ this.responseCollector = responseCollector;
37
+ }
38
+ responseCollector;
39
+ create() {
40
+ const responseCollector = this.responseCollector;
41
+ return defineTool({
42
+ name: AGENT_RESPONSE_TOOL_NAME,
43
+ label: "Workflow Agent Response",
44
+ description: "Record the final structured response for a Norn nested agent run.",
45
+ promptSnippet: "Record a final structured response for Norn agent automation",
46
+ promptGuidelines: [
47
+ `Use ${AGENT_RESPONSE_TOOL_NAME} as your final action when a workflow prompt gives you a workflow response runId.`,
48
+ `After calling ${AGENT_RESPONSE_TOOL_NAME}, do not emit another assistant response in the same turn.`
49
+ ],
50
+ parameters: Type.Object({
51
+ runId: Type.String({ description: "The exact workflow response runId provided in the prompt." }),
52
+ label: Type.String({ description: "The exact workflow agent label provided in the prompt." }),
53
+ response: Type.Unknown({ description: "The structured response object requested by the workflow." })
54
+ }),
55
+ async execute(_toolCallId, params) {
56
+ try {
57
+ const captured = responseCollector.capture(params.runId, params.label, params.response);
58
+ return {
59
+ content: [{ type: "text", text: `Recorded workflow agent response for ${captured.label}.` }],
60
+ details: { valid: true, runId: params.runId, label: captured.label, response: captured.response },
61
+ terminate: true
62
+ };
63
+ } catch (error) {
64
+ return invalidAgentResponseToolResult(error instanceof Error ? error.message : String(error));
65
+ }
66
+ }
67
+ });
68
+ }
69
+ };
70
+ function invalidAgentResponseToolResult(error) {
71
+ return {
72
+ content: [{ type: "text", text: `Invalid workflow agent response: ${error}. Call the tool again with a corrected response.` }],
73
+ details: { valid: false, error }
74
+ };
75
+ }
76
+ export {
77
+ NornAgentResponseCollector,
78
+ NornAgentResponseToolFactory
79
+ };
@@ -0,0 +1,29 @@
1
+ import { type CreateAgentSessionOptions } from "@earendil-works/pi-coding-agent";
2
+ import { z } from "zod";
3
+ import type { NornAgentCreateSessionInput, NornAgentSession, NornAgentSinglePromptInput } from "@vimhead.dev/norn";
4
+ import { NornAgentResponseCollector } from "./agent-response-tool.ts";
5
+ import type { NornRunLogs } from "./logs.ts";
6
+ import type { NornRunLogger } from "./run-log.ts";
7
+ type NornAgentRunnerInput = {
8
+ readonly id: string;
9
+ readonly runRoot: string;
10
+ readonly boundaryRoot: string;
11
+ readonly boundaryName: string;
12
+ readonly cwd: string;
13
+ readonly signal?: AbortSignal;
14
+ readonly model?: CreateAgentSessionOptions["model"];
15
+ readonly thinkingLevel?: CreateAgentSessionOptions["thinkingLevel"];
16
+ readonly agentDir?: string;
17
+ readonly logs: NornRunLogs;
18
+ readonly logger: NornRunLogger;
19
+ readonly responseCollector: NornAgentResponseCollector;
20
+ };
21
+ export declare class NornAgentRunner {
22
+ private readonly input;
23
+ private readonly responseToolFactory;
24
+ constructor(input: NornAgentRunnerInput);
25
+ createSession(agentInput: NornAgentCreateSessionInput): Promise<NornAgentSession>;
26
+ prompt<ResponseSchema extends z.ZodType>(agentInput: NornAgentSinglePromptInput<ResponseSchema>): Promise<z.output<ResponseSchema>>;
27
+ private resolveFromCwd;
28
+ }
29
+ export {};