@vimhead.dev/norn-cli 0.1.0-tip.35614826665.1 → 0.1.0-tip.35724621678.1
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/assets/README.md +41 -13
- package/assets/docs/README.md +16 -16
- package/assets/docs/agents.md +23 -15
- package/assets/docs/cli.md +10 -8
- package/assets/docs/composition.md +7 -7
- package/assets/docs/persistence.md +22 -22
- package/assets/docs/projects.md +6 -6
- package/assets/docs/recovery.md +10 -10
- package/assets/docs/schemas.md +42 -36
- package/assets/docs/workflows.md +39 -32
- package/assets/examples/agent-then-analysis/input.json +3 -3
- package/assets/examples/agent-then-analysis/norn.project.json +2 -2
- package/assets/examples/agent-then-analysis/plugin.ts +37 -9
- package/assets/examples/caller-owned-routing/README.md +6 -6
- package/assets/examples/caller-owned-routing/assessment.ts +18 -4
- package/assets/examples/caller-owned-routing/contracts.ts +7 -2
- package/assets/examples/caller-owned-routing/input.json +12 -12
- package/assets/examples/caller-owned-routing/norn.project.json +2 -2
- package/assets/examples/caller-owned-routing/revision.ts +7 -2
- package/assets/examples/caller-owned-routing/router.ts +14 -3
- package/assets/examples/caller-selected-continuation/caller.ts +38 -12
- package/assets/examples/caller-selected-continuation/input.json +7 -7
- package/assets/examples/caller-selected-continuation/norn.project.json +2 -2
- package/assets/examples/caller-selected-continuation/producer.ts +10 -3
- package/assets/examples/coordinating-multiple-agents/README.md +6 -6
- package/assets/examples/coordinating-multiple-agents/input.json +20 -8
- package/assets/examples/coordinating-multiple-agents/norn.project.json +2 -2
- package/assets/examples/coordinating-multiple-agents/plugin.ts +165 -46
- package/assets/examples/coordinating-multiple-agents/queue-tools.ts +30 -7
- package/assets/examples/coordinating-multiple-agents/work-queue.ts +211 -63
- package/assets/examples/getting-started/norn.project.json +2 -2
- package/assets/examples/getting-started/plugin.ts +33 -13
- package/assets/examples/minimal-workflow/norn.project.json +2 -2
- package/assets/examples/minimal-workflow/plugin.ts +12 -3
- package/assets/examples/shared-state/README.md +6 -6
- package/assets/examples/shared-state/input.json +1 -1
- package/assets/examples/shared-state/norn.project.json +2 -2
- package/assets/examples/shared-state/plugin.ts +44 -15
- package/assets/examples/shared-state/shared-state.ts +46 -13
- package/assets/examples/shared-state/state-tools.ts +68 -16
- package/assets/examples/worktree-development-loop/README.md +12 -12
- package/assets/examples/worktree-development-loop/norn.project.json +7 -7
- package/assets/examples/worktree-development-loop/plugin.ts +7 -1
- package/assets/examples/worktree-development-loop/scope.ts +4 -1
- package/assets/examples/worktree-development-loop/shared/commands.ts +6 -2
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -4
- package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +11 -1
- package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +6 -2
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +40 -10
- package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +5 -1
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +1 -1
- package/assets/examples/worktree-development-loop/workflows/planning/index.ts +5 -1
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +33 -9
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -8
- package/assets/package.json +1 -1
- package/assets/packages/cli/src/build-info.ts +4 -1
- package/assets/packages/cli/src/bun/cli.ts +21 -5
- package/assets/packages/cli/src/cli.ts +585 -170
- package/assets/packages/cli/src/client.ts +194 -36
- package/assets/packages/cli/src/documentation.ts +135 -30
- package/assets/packages/cli/src/generated-build-info.ts +2 -2
- package/assets/packages/cli/src/internal/agent-directory.ts +7 -2
- package/assets/packages/cli/src/internal/agent-response-tool.ts +57 -15
- package/assets/packages/cli/src/internal/agents.ts +248 -60
- package/assets/packages/cli/src/internal/commands.ts +82 -20
- package/assets/packages/cli/src/internal/documentation-bundle.ts +44 -13
- package/assets/packages/cli/src/internal/engine.ts +396 -87
- package/assets/packages/cli/src/internal/errors.ts +13 -5
- package/assets/packages/cli/src/internal/execution-context.ts +20 -12
- package/assets/packages/cli/src/internal/file-coordinator.ts +95 -26
- package/assets/packages/cli/src/internal/file-names.ts +4 -1
- package/assets/packages/cli/src/internal/launch-request.ts +74 -22
- package/assets/packages/cli/src/internal/logs.ts +10 -2
- package/assets/packages/cli/src/internal/metrics.ts +247 -61
- package/assets/packages/cli/src/internal/pi-assets.ts +105 -23
- package/assets/packages/cli/src/internal/run-lease.ts +72 -22
- package/assets/packages/cli/src/internal/run-log.ts +49 -20
- package/assets/packages/cli/src/internal/run-names.ts +120 -12
- package/assets/packages/cli/src/internal/run-state.ts +306 -71
- package/assets/packages/cli/src/internal/run-store.ts +216 -53
- package/assets/packages/cli/src/internal/usage.ts +23 -7
- package/assets/packages/cli/src/internal/worker-directory.ts +9 -3
- package/assets/packages/cli/src/internal/workflow-registry.ts +281 -70
- package/assets/packages/cli/src/internal/working-directory.ts +2 -1
- package/assets/packages/cli/src/workflow-loader.ts +341 -74
- package/assets/packages/core/src/atomic-files.ts +13 -4
- package/assets/packages/core/src/workflow-transition.ts +13 -2
- package/assets/packages/sdk/src/api.ts +248 -80
- package/assets/packages/sdk/src/index.ts +6 -1
- package/assets/packages/sdk/src/schema.ts +75 -21
- package/assets/tests/workflow-ref.test.ts +242 -57
- package/bin/norn.mjs +13 -4
- package/dist/cli.js +344 -90
- package/dist/client.js +100 -22
- package/dist/documentation.js +102 -25
- package/dist/generated-build-info.d.ts +2 -2
- package/dist/generated-build-info.js +2 -2
- package/dist/internal/agent-directory.js +3 -1
- package/dist/internal/agent-response-tool.js +42 -11
- package/dist/internal/agents.js +149 -37
- package/dist/internal/commands.js +65 -16
- package/dist/internal/documentation-bundle.js +23 -11
- package/dist/internal/engine.js +304 -66
- package/dist/internal/errors.js +4 -2
- package/dist/internal/execution-context.js +2 -1
- package/dist/internal/file-coordinator.js +59 -19
- package/dist/internal/launch-request.js +32 -14
- package/dist/internal/metrics.js +138 -38
- package/dist/internal/pi-assets.js +70 -19
- package/dist/internal/run-lease.js +29 -9
- package/dist/internal/run-log.js +24 -12
- package/dist/internal/run-state.js +172 -48
- package/dist/internal/run-store.js +157 -45
- package/dist/internal/worker-directory.js +6 -2
- package/dist/internal/workflow-registry.js +123 -32
- package/dist/internal/working-directory.js +2 -1
- package/dist/workflow-loader.js +198 -41
- package/package.json +2 -2
package/dist/workflow-loader.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/workflow-loader.ts
|
|
2
2
|
import * as nornModule from "@vimhead.dev/norn";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
isWorkflowDeclaration
|
|
5
|
+
} from "@vimhead.dev/norn";
|
|
4
6
|
|
|
5
7
|
// ../core/src/errors.ts
|
|
6
8
|
function isNodeError(error) {
|
|
@@ -21,14 +23,21 @@ import * as typeboxSchemaModule from "typebox/schema";
|
|
|
21
23
|
import * as typeboxValueModule from "typebox/value";
|
|
22
24
|
import { AssertError, Value } from "typebox/value";
|
|
23
25
|
import { errorMessage, NornProjectLoadError } from "./internal/errors.js";
|
|
24
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
assertCompatibleConfigurationOwners,
|
|
28
|
+
assertWorkflowDefinition,
|
|
29
|
+
NornWorkflowRegistry
|
|
30
|
+
} from "./internal/workflow-registry.js";
|
|
25
31
|
var NORN_PROJECT_FILE_NAME = "norn.project.json";
|
|
26
32
|
var nornConfigSchema = Type.Object({
|
|
27
33
|
workflows: Type.Array(Type.String({ minLength: 1 }), { default: [] }),
|
|
28
34
|
includes: Type.Array(Type.String({ minLength: 1 }), { default: [] }),
|
|
29
35
|
config: Type.Record(Type.String(), Type.Unknown(), { default: {} })
|
|
30
36
|
});
|
|
31
|
-
var nornProjectConfigSchema = Type.Object({
|
|
37
|
+
var nornProjectConfigSchema = Type.Object({
|
|
38
|
+
...nornConfigSchema.properties,
|
|
39
|
+
version: Type.Literal(1, { default: 1 })
|
|
40
|
+
});
|
|
32
41
|
async function loadNornProject(cwd) {
|
|
33
42
|
const { project, diagnostics } = await collectNornProject(cwd);
|
|
34
43
|
if (diagnostics.length) throw new NornProjectLoadError({ diagnostics });
|
|
@@ -36,20 +45,43 @@ async function loadNornProject(cwd) {
|
|
|
36
45
|
}
|
|
37
46
|
async function discoverNornProject(cwd) {
|
|
38
47
|
const { project, diagnostics } = await collectNornProject(cwd);
|
|
39
|
-
return {
|
|
48
|
+
return {
|
|
49
|
+
project: buildProjectInfo(project),
|
|
50
|
+
workflows: project.registry.list(),
|
|
51
|
+
isComplete: diagnostics.length === 0,
|
|
52
|
+
diagnostics
|
|
53
|
+
};
|
|
40
54
|
}
|
|
41
55
|
async function inspectNornWorkflow(input) {
|
|
42
56
|
const { project, diagnostics } = await collectNornProject(input.cwd);
|
|
43
57
|
const summary = project.registry.list().find((workflow) => workflow.id === input.workflowId);
|
|
44
58
|
if (!summary) {
|
|
45
|
-
if (!diagnostics.length)
|
|
59
|
+
if (!diagnostics.length)
|
|
60
|
+
throw new Error(`Unknown workflow: ${input.workflowId}`);
|
|
46
61
|
return { workflow: null, isComplete: false, diagnostics };
|
|
47
62
|
}
|
|
48
63
|
try {
|
|
49
|
-
return {
|
|
64
|
+
return {
|
|
65
|
+
workflow: project.registry.inspect(input.workflowId) ?? null,
|
|
66
|
+
isComplete: diagnostics.length === 0,
|
|
67
|
+
diagnostics
|
|
68
|
+
};
|
|
50
69
|
} catch (error) {
|
|
51
70
|
if (!summary.source) throw error;
|
|
52
|
-
return {
|
|
71
|
+
return {
|
|
72
|
+
workflow: null,
|
|
73
|
+
isComplete: false,
|
|
74
|
+
diagnostics: [
|
|
75
|
+
...diagnostics,
|
|
76
|
+
createDiagnostic({
|
|
77
|
+
source: summary.source,
|
|
78
|
+
workflowId: summary.id,
|
|
79
|
+
scopeId: summary.scope?.id ?? null,
|
|
80
|
+
stage: "schema",
|
|
81
|
+
error
|
|
82
|
+
})
|
|
83
|
+
]
|
|
84
|
+
};
|
|
53
85
|
}
|
|
54
86
|
}
|
|
55
87
|
function buildProjectInfo(project) {
|
|
@@ -66,7 +98,10 @@ function buildProjectInfo(project) {
|
|
|
66
98
|
}
|
|
67
99
|
async function findNornProject(cwd) {
|
|
68
100
|
const projectPath = await findNearestNornProject(cwd);
|
|
69
|
-
const projectFile = await readConfigFile({
|
|
101
|
+
const projectFile = await readConfigFile({
|
|
102
|
+
path: projectPath,
|
|
103
|
+
isProject: true
|
|
104
|
+
});
|
|
70
105
|
const configFiles = await loadConfigTree(projectFile, /* @__PURE__ */ new Set());
|
|
71
106
|
return {
|
|
72
107
|
cwd: resolve(cwd),
|
|
@@ -90,7 +125,8 @@ async function findNearestNornProject(cwd) {
|
|
|
90
125
|
if (!isNodeError(error) || error.code !== "ENOENT") throw error;
|
|
91
126
|
}
|
|
92
127
|
const parent = dirname(current);
|
|
93
|
-
if (parent === current)
|
|
128
|
+
if (parent === current)
|
|
129
|
+
throw new Error(`Could not find ${NORN_PROJECT_FILE_NAME} from ${cwd}`);
|
|
94
130
|
current = parent;
|
|
95
131
|
}
|
|
96
132
|
}
|
|
@@ -99,16 +135,29 @@ async function loadConfigTree(file, visited) {
|
|
|
99
135
|
visited.add(file.path);
|
|
100
136
|
const files = [file];
|
|
101
137
|
for (const include of file.config.includes) {
|
|
102
|
-
for (const path of await expandIncludePath(file.root, include))
|
|
138
|
+
for (const path of await expandIncludePath(file.root, include))
|
|
139
|
+
files.push(
|
|
140
|
+
...await loadConfigTree(
|
|
141
|
+
await readConfigFile({ path, isProject: false }),
|
|
142
|
+
visited
|
|
143
|
+
)
|
|
144
|
+
);
|
|
103
145
|
}
|
|
104
146
|
return files;
|
|
105
147
|
}
|
|
106
148
|
async function readConfigFile(input) {
|
|
107
149
|
const raw = JSON.parse(await readFile(input.path, "utf8"));
|
|
108
|
-
if (isPlainObject(raw) && Object.hasOwn(raw, "plugins"))
|
|
150
|
+
if (isPlainObject(raw) && Object.hasOwn(raw, "plugins"))
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Replace plugins with workflows in ${input.path}; registration modules must export workflow arrays`
|
|
153
|
+
);
|
|
109
154
|
const schema = input.isProject ? nornProjectConfigSchema : nornConfigSchema;
|
|
110
155
|
const config = Value.Parse(schema, Value.Default(schema, raw));
|
|
111
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
path: input.path,
|
|
158
|
+
root: dirname(input.path),
|
|
159
|
+
config: { ...config, version: 1 }
|
|
160
|
+
};
|
|
112
161
|
}
|
|
113
162
|
async function collectNornProject(cwd) {
|
|
114
163
|
const project = await findNornProject(cwd);
|
|
@@ -133,9 +182,23 @@ async function collectNornProject(cwd) {
|
|
|
133
182
|
stage = "declaration";
|
|
134
183
|
assertWorkflowDefinition(workflow);
|
|
135
184
|
stage = "config";
|
|
136
|
-
unregister.push(
|
|
185
|
+
unregister.push(
|
|
186
|
+
registry.register({
|
|
187
|
+
workflow,
|
|
188
|
+
config: project.projectConfig,
|
|
189
|
+
source: candidate.source
|
|
190
|
+
})
|
|
191
|
+
);
|
|
137
192
|
} catch (error) {
|
|
138
|
-
errors.push(
|
|
193
|
+
errors.push(
|
|
194
|
+
createDiagnostic({
|
|
195
|
+
source: candidate.source,
|
|
196
|
+
workflowId: workflow.id,
|
|
197
|
+
scopeId: workflow.scope?.id ?? null,
|
|
198
|
+
stage,
|
|
199
|
+
error
|
|
200
|
+
})
|
|
201
|
+
);
|
|
139
202
|
}
|
|
140
203
|
}
|
|
141
204
|
if (errors.length) {
|
|
@@ -144,23 +207,57 @@ async function collectNornProject(cwd) {
|
|
|
144
207
|
} else loaded.push(candidate);
|
|
145
208
|
}
|
|
146
209
|
const definitions = loaded.flatMap((module) => module.definitions);
|
|
147
|
-
return {
|
|
210
|
+
return {
|
|
211
|
+
project: {
|
|
212
|
+
...project,
|
|
213
|
+
definitions,
|
|
214
|
+
modules: loaded.map((module) => module.source),
|
|
215
|
+
configurations: registry.configurationInfos(),
|
|
216
|
+
registry,
|
|
217
|
+
workflows: registry.launchableEntries()
|
|
218
|
+
},
|
|
219
|
+
diagnostics
|
|
220
|
+
};
|
|
148
221
|
}
|
|
149
222
|
function collectModuleConflicts(modules) {
|
|
150
223
|
const conflicts = /* @__PURE__ */ new Map();
|
|
151
224
|
const byId = /* @__PURE__ */ new Map();
|
|
152
|
-
for (const module of modules)
|
|
225
|
+
for (const module of modules)
|
|
226
|
+
for (const workflow of module.definitions)
|
|
227
|
+
byId.set(workflow.id, [...byId.get(workflow.id) ?? [], module]);
|
|
153
228
|
for (const [id, owners] of byId) {
|
|
154
229
|
if (owners.length < 2) continue;
|
|
155
|
-
for (const module of new Set(owners))
|
|
230
|
+
for (const module of new Set(owners))
|
|
231
|
+
conflicts.set(module, [
|
|
232
|
+
...conflicts.get(module) ?? [],
|
|
233
|
+
createDiagnostic({
|
|
234
|
+
source: module.source,
|
|
235
|
+
workflowId: id,
|
|
236
|
+
scopeId: null,
|
|
237
|
+
stage: "duplicate",
|
|
238
|
+
error: new Error(`Workflow already registered: ${id}`)
|
|
239
|
+
})
|
|
240
|
+
]);
|
|
156
241
|
}
|
|
157
242
|
for (let index = 0; index < modules.length; index++) {
|
|
158
243
|
for (let other = index; other < modules.length; other++) {
|
|
159
244
|
const candidates = [.../* @__PURE__ */ new Set([modules[index], modules[other]])];
|
|
160
245
|
try {
|
|
161
|
-
assertCompatibleConfigurationOwners(
|
|
246
|
+
assertCompatibleConfigurationOwners(
|
|
247
|
+
candidates.flatMap((module) => module.definitions)
|
|
248
|
+
);
|
|
162
249
|
} catch (error) {
|
|
163
|
-
for (const module of candidates)
|
|
250
|
+
for (const module of candidates)
|
|
251
|
+
conflicts.set(module, [
|
|
252
|
+
...conflicts.get(module) ?? [],
|
|
253
|
+
createDiagnostic({
|
|
254
|
+
source: module.source,
|
|
255
|
+
workflowId: null,
|
|
256
|
+
scopeId: null,
|
|
257
|
+
stage: "config",
|
|
258
|
+
error
|
|
259
|
+
})
|
|
260
|
+
]);
|
|
164
261
|
}
|
|
165
262
|
}
|
|
166
263
|
}
|
|
@@ -169,43 +266,100 @@ function collectModuleConflicts(modules) {
|
|
|
169
266
|
async function importWorkflowModules(project) {
|
|
170
267
|
const modules = [];
|
|
171
268
|
const diagnostics = [];
|
|
172
|
-
const jiti = createJiti(pathToFileURL(project.configPath).href, {
|
|
269
|
+
const jiti = createJiti(pathToFileURL(project.configPath).href, {
|
|
270
|
+
moduleCache: false,
|
|
271
|
+
tryNative: false,
|
|
272
|
+
virtualModules: nornWorkflowVirtualModules()
|
|
273
|
+
});
|
|
173
274
|
const cache = /* @__PURE__ */ Object.create(null);
|
|
174
|
-
for (const file of project.configFiles)
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
stage = "
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
275
|
+
for (const file of project.configFiles)
|
|
276
|
+
for (const path of file.config.workflows) {
|
|
277
|
+
const source = {
|
|
278
|
+
configPath: file.path,
|
|
279
|
+
path: resolveConfigPath(file.root, path)
|
|
280
|
+
};
|
|
281
|
+
let stage = "import";
|
|
282
|
+
try {
|
|
283
|
+
const cached = cache[source.path];
|
|
284
|
+
const module = cached?.loaded ? cached.exports : await jiti.evalModule(await readFile(source.path, "utf8"), {
|
|
285
|
+
filename: source.path,
|
|
286
|
+
cache,
|
|
287
|
+
async: true
|
|
288
|
+
});
|
|
289
|
+
stage = "declaration";
|
|
290
|
+
if (!Array.isArray(module?.default) || !Array.from(module.default).every(isWorkflowDeclaration))
|
|
291
|
+
throw new Error(
|
|
292
|
+
`Workflow module must default-export an array of complete workflow definitions: ${source.path}`
|
|
293
|
+
);
|
|
294
|
+
modules.push({ definitions: module.default, source });
|
|
295
|
+
} catch (error) {
|
|
296
|
+
for (const [path2, module] of Object.entries(cache))
|
|
297
|
+
if (!module.loaded) delete cache[path2];
|
|
298
|
+
diagnostics.push(
|
|
299
|
+
createDiagnostic({
|
|
300
|
+
source,
|
|
301
|
+
workflowId: null,
|
|
302
|
+
scopeId: null,
|
|
303
|
+
stage,
|
|
304
|
+
error
|
|
305
|
+
})
|
|
306
|
+
);
|
|
307
|
+
}
|
|
186
308
|
}
|
|
187
|
-
}
|
|
188
309
|
return { modules, diagnostics };
|
|
189
310
|
}
|
|
190
311
|
function createDiagnostic(input) {
|
|
191
|
-
return {
|
|
312
|
+
return {
|
|
313
|
+
configPath: input.source.configPath,
|
|
314
|
+
modulePath: input.source.path,
|
|
315
|
+
scopeId: input.scopeId,
|
|
316
|
+
workflowId: input.workflowId,
|
|
317
|
+
stage: input.stage,
|
|
318
|
+
message: errorMessage(input.error),
|
|
319
|
+
issues: input.error instanceof AssertError ? input.error.cause.errors : []
|
|
320
|
+
};
|
|
192
321
|
}
|
|
193
322
|
function nornWorkflowVirtualModules() {
|
|
194
|
-
return {
|
|
323
|
+
return {
|
|
324
|
+
"@vimhead.dev/norn": nornModule,
|
|
325
|
+
"@vimhead.dev/norn/schema": nornSchemaModule,
|
|
326
|
+
typebox: typeboxModule,
|
|
327
|
+
"typebox/value": typeboxValueModule,
|
|
328
|
+
"typebox/compile": typeboxCompileModule,
|
|
329
|
+
"typebox/schema": typeboxSchemaModule
|
|
330
|
+
};
|
|
195
331
|
}
|
|
196
332
|
function mergeProjectConfig(files) {
|
|
197
333
|
const [project, ...reusable] = files;
|
|
198
334
|
let config = {};
|
|
199
|
-
for (const file of reusable)
|
|
200
|
-
|
|
335
|
+
for (const file of reusable)
|
|
336
|
+
config = mergeConfig({
|
|
337
|
+
base: config,
|
|
338
|
+
override: file.config.config,
|
|
339
|
+
path: ["config"],
|
|
340
|
+
rejectConflicts: true
|
|
341
|
+
});
|
|
342
|
+
return mergeConfig({
|
|
343
|
+
base: config,
|
|
344
|
+
override: project?.config.config ?? {},
|
|
345
|
+
path: ["config"],
|
|
346
|
+
rejectConflicts: false
|
|
347
|
+
});
|
|
201
348
|
}
|
|
202
349
|
function mergeConfig(input) {
|
|
203
350
|
const result = { ...input.base };
|
|
204
351
|
for (const [key, value] of Object.entries(input.override)) {
|
|
205
352
|
const previous = result[key];
|
|
206
353
|
const path = [...input.path, key];
|
|
207
|
-
if (isPlainObject(previous) && isPlainObject(value))
|
|
208
|
-
|
|
354
|
+
if (isPlainObject(previous) && isPlainObject(value))
|
|
355
|
+
result[key] = mergeConfig({
|
|
356
|
+
base: previous,
|
|
357
|
+
override: value,
|
|
358
|
+
path,
|
|
359
|
+
rejectConflicts: input.rejectConflicts
|
|
360
|
+
});
|
|
361
|
+
else if (input.rejectConflicts && Object.hasOwn(result, key) && JSON.stringify(previous) !== JSON.stringify(value))
|
|
362
|
+
throw new Error(`Conflicting Norn reusable config at ${path.join(".")}`);
|
|
209
363
|
else result[key] = value;
|
|
210
364
|
}
|
|
211
365
|
return result;
|
|
@@ -217,9 +371,12 @@ async function expandIncludePath(root, path) {
|
|
|
217
371
|
async function expandGlobSegments(root, segments) {
|
|
218
372
|
if (!segments.length) return [root];
|
|
219
373
|
const [segment, ...remaining] = segments;
|
|
220
|
-
if (segment !== "*")
|
|
374
|
+
if (segment !== "*")
|
|
375
|
+
return expandGlobSegments(join(root, segment), remaining);
|
|
221
376
|
const entries = await readdir(root, { withFileTypes: true });
|
|
222
|
-
return (await Promise.all(
|
|
377
|
+
return (await Promise.all(
|
|
378
|
+
entries.filter((entry) => entry.isDirectory()).sort((left, right) => left.name.localeCompare(right.name)).map((entry) => expandGlobSegments(join(root, entry.name), remaining))
|
|
379
|
+
)).flat();
|
|
223
380
|
}
|
|
224
381
|
function resolveConfigPath(root, path) {
|
|
225
382
|
if (!path.length) throw new Error("Norn path must not be empty");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vimhead.dev/norn-cli",
|
|
3
|
-
"version": "0.1.0-tip.
|
|
3
|
+
"version": "0.1.0-tip.35724621678.1",
|
|
4
4
|
"description": "Harness-agnostic runtime for agent-driven and code-driven workflows, built primarily for agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
44
44
|
"jiti": "^2.7.0",
|
|
45
45
|
"typebox": "^1.3.34",
|
|
46
|
-
"@vimhead.dev/norn": "0.1.0-tip.
|
|
46
|
+
"@vimhead.dev/norn": "0.1.0-tip.35724621678.1"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "node ../../scripts/build-package.ts"
|