@cad0p/pi-steering 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -4
- package/README.md +20 -6
- package/dist/__test-helpers__.d.ts +5 -4
- package/dist/__test-helpers__.d.ts.map +1 -1
- package/dist/__test-helpers__.js +5 -4
- package/dist/__test-helpers__.js.map +1 -1
- package/dist/bin/pi-steering.d.ts.map +1 -1
- package/dist/bin/pi-steering.js +22 -16
- package/dist/bin/pi-steering.js.map +1 -1
- package/dist/evaluator.d.ts +0 -41
- package/dist/evaluator.d.ts.map +1 -1
- package/dist/evaluator.js +42 -1
- package/dist/evaluator.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/ref-text.d.ts.map +1 -1
- package/dist/internal/ref-text.js +1 -1
- package/dist/internal/ref-text.js.map +1 -1
- package/dist/internal/session-runtime.d.ts.map +1 -1
- package/dist/internal/session-runtime.js +3 -2
- package/dist/internal/session-runtime.js.map +1 -1
- package/dist/loader.d.ts +23 -18
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +91 -87
- package/dist/loader.js.map +1 -1
- package/dist/plugins/git/cwd-extensions.d.ts.map +1 -1
- package/dist/plugins/git/cwd-extensions.js.map +1 -1
- package/dist/schema.d.ts +4 -4
- package/dist/testing/index.d.ts +4 -5
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4 -4
- package/dist/testing/index.js.map +1 -1
- package/examples/README.md +3 -3
- package/examples/combined-git-discipline/README.md +2 -2
- package/examples/combined-git-discipline/node_modules/.bin/pi-steering +16 -4
- package/examples/combined-git-discipline/package.json +16 -16
- package/examples/draft-prs-only/README.md +2 -2
- package/examples/draft-prs-only/node_modules/.bin/pi-steering +16 -4
- package/examples/draft-prs-only/package.json +16 -16
- package/examples/dynamic-reason-runtime-cwd/node_modules/.bin/pi-steering +16 -4
- package/examples/dynamic-reason-runtime-cwd/package.json +16 -16
- package/examples/dynamic-reason-runtime-cwd/steering.ts +5 -1
- package/examples/force-push-strict/README.md +1 -1
- package/examples/force-push-strict/node_modules/.bin/pi-steering +16 -4
- package/examples/force-push-strict/package.json +16 -16
- package/examples/no-amend/README.md +2 -2
- package/examples/no-amend/node_modules/.bin/pi-steering +16 -4
- package/examples/no-amend/package.json +16 -16
- package/examples/work-item-plugin/node_modules/.bin/pi-steering +16 -4
- package/examples/work-item-plugin/package.json +17 -17
- package/examples/work-item-plugin/src/observers/npm-test-tracker.ts +5 -1
- package/examples/work-item-plugin/src/observers/retest-required-tracker.ts +5 -1
- package/examples/work-item-plugin/src/rules/commit-requires-work-item.test.ts +5 -1
- package/package.json +2 -2
- package/src/__test-helpers__.ts +5 -4
- package/src/bin/pi-steering.test.ts +14 -4
- package/src/bin/pi-steering.ts +22 -16
- package/src/evaluator.ts +6 -6
- package/src/factory-time-load.test.ts +24 -18
- package/src/index.test.ts +4 -4
- package/src/index.ts +1 -1
- package/src/internal/ref-text.ts +5 -1
- package/src/internal/session-runtime.test.ts +2 -2
- package/src/internal/session-runtime.ts +3 -2
- package/src/loader.test.ts +226 -74
- package/src/loader.ts +99 -82
- package/src/plugins/git/cwd-extensions.ts +1 -1
- package/src/schema.ts +4 -4
- package/src/testing/index.ts +7 -7
|
@@ -25,7 +25,11 @@
|
|
|
25
25
|
* downstream typos become compile errors.
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import type {
|
|
28
|
+
import type {
|
|
29
|
+
Observer,
|
|
30
|
+
ObserverContext,
|
|
31
|
+
PredicateContext,
|
|
32
|
+
} from "@cad0p/pi-steering";
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* Session-entry type written when `npm test` succeeds. Rules gate via
|
|
@@ -28,7 +28,11 @@
|
|
|
28
28
|
* from downstream rules.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
import type {
|
|
31
|
+
import type {
|
|
32
|
+
Observer,
|
|
33
|
+
ObserverContext,
|
|
34
|
+
PredicateContext,
|
|
35
|
+
} from "@cad0p/pi-steering";
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
38
|
* Session-entry event written when `git pull` succeeds. Rules that
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
import { describe, it } from "node:test";
|
|
14
14
|
import type { Plugin } from "@cad0p/pi-steering";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
expectAllows,
|
|
17
|
+
expectBlocks,
|
|
18
|
+
loadHarness,
|
|
19
|
+
} from "@cad0p/pi-steering/testing";
|
|
16
20
|
import { workItemFormat } from "../predicates/work-item-format.ts";
|
|
17
21
|
import { commitRequiresWorkItem } from "./commit-requires-work-item.ts";
|
|
18
22
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cad0p/pi-steering",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "AST-backed steering hooks for pi
|
|
5
|
+
"description": "AST-backed steering hooks for pi — deterministic tool-call guardrails with command-level effective-cwd scoping.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"pi-package",
|
|
8
8
|
"extension",
|
package/src/__test-helpers__.ts
CHANGED
|
@@ -53,9 +53,10 @@ import type { EvaluatorHost } from "./evaluator-internals/context.ts";
|
|
|
53
53
|
* - restore `process.env["HOME"]` and recursively remove the temp
|
|
54
54
|
* dir on teardown.
|
|
55
55
|
*
|
|
56
|
-
* Used by every test surface that exercises the loader
|
|
57
|
-
* (`index.test.ts`, `loader.test.ts`,
|
|
58
|
-
* so the per-file scratch-HOME
|
|
56
|
+
* Used by every test surface that exercises the loader's two-layer
|
|
57
|
+
* discovery (`index.test.ts`, `loader.test.ts`,
|
|
58
|
+
* `internal/session-runtime.test.ts`) so the per-file scratch-HOME
|
|
59
|
+
* boilerplate stays in one place.
|
|
59
60
|
*
|
|
60
61
|
* The temp dir path is exposed via the optional `onReady` callback,
|
|
61
62
|
* fired inside `beforeEach`; tests typically stash it in a
|
|
@@ -81,7 +82,7 @@ export function useIsolatedHome(
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
|
-
* Like {@link useIsolatedHome} but also chdirs into the scratch dir, so factory-time tests find the per-test config via the loader
|
|
85
|
+
* Like {@link useIsolatedHome} but also chdirs into the scratch dir, so factory-time tests find the per-test config via the loader's project layer. macOS tmpdir is a symlink; canonicalized via `realpathSync` so cwd-mismatch tests don't see false-divergence.
|
|
85
86
|
*/
|
|
86
87
|
export function useScratchHome(
|
|
87
88
|
prefix: string,
|
|
@@ -50,8 +50,8 @@ interface RunResult {
|
|
|
50
50
|
* non-zero exit codes — the caller asserts on `code`.
|
|
51
51
|
*
|
|
52
52
|
* Accepts an optional `cwd` so tests for the `list` subcommand can
|
|
53
|
-
* point the
|
|
54
|
-
* the project.
|
|
53
|
+
* point the loader's project layer at a scratch directory and isolate
|
|
54
|
+
* HOME so no real global config leaks, without polluting the project.
|
|
55
55
|
*/
|
|
56
56
|
function runCli(...args: string[]): Promise<RunResult>;
|
|
57
57
|
function runCli(opts: { cwd?: string }, ...args: string[]): Promise<RunResult>;
|
|
@@ -67,6 +67,12 @@ function runCli(
|
|
|
67
67
|
} else {
|
|
68
68
|
args = first === undefined ? [...rest] : [first, ...rest];
|
|
69
69
|
}
|
|
70
|
+
// The child CLI reads the global layer from `$HOME`; without
|
|
71
|
+
// isolation the developer's real `~/.pi/agent/steering/` would
|
|
72
|
+
// leak into the tests. `PI_CODING_AGENT_DIR` must be removed (an
|
|
73
|
+
// empty string still overrides the default agent dir).
|
|
74
|
+
const env: NodeJS.ProcessEnv = { ...process.env, HOME: scratch };
|
|
75
|
+
delete env.PI_CODING_AGENT_DIR;
|
|
70
76
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
71
77
|
const child = spawn(
|
|
72
78
|
process.execPath,
|
|
@@ -74,6 +80,7 @@ function runCli(
|
|
|
74
80
|
{
|
|
75
81
|
stdio: ["ignore", "pipe", "pipe"],
|
|
76
82
|
...(cwd !== undefined ? { cwd } : {}),
|
|
83
|
+
env,
|
|
77
84
|
},
|
|
78
85
|
);
|
|
79
86
|
let stdout = "";
|
|
@@ -229,7 +236,10 @@ describe("pi-steering import-json: conversion", () => {
|
|
|
229
236
|
const r = await runCli("import-json", path);
|
|
230
237
|
assert.equal(r.code, 0);
|
|
231
238
|
assert.equal(r.stderr.trim(), "");
|
|
232
|
-
assert.match(
|
|
239
|
+
assert.match(
|
|
240
|
+
r.stdout,
|
|
241
|
+
/import \{ defineConfig \} from "@cad0p\/pi-steering"/,
|
|
242
|
+
);
|
|
233
243
|
assert.match(r.stdout, /export default defineConfig\(/);
|
|
234
244
|
assert.match(r.stdout, /"no-amend"/);
|
|
235
245
|
assert.match(r.stdout, /"Don't rewrite history\."/);
|
|
@@ -345,7 +355,7 @@ describe("pi-steering list", () => {
|
|
|
345
355
|
assert.match(r.stdout, /Resolved config: 1 plugin, 2 rules, 0 observers\./);
|
|
346
356
|
assert.match(r.stdout, /git\s+\[pi-steering\/plugins\/git\]/);
|
|
347
357
|
assert.match(r.stdout, /no-main-commit\s+bash\s+when: branch/);
|
|
348
|
-
assert.match(r.stdout, /User \(
|
|
358
|
+
assert.match(r.stdout, /User \(project \+ global\):/);
|
|
349
359
|
assert.match(r.stdout, /my-rule\s+bash/);
|
|
350
360
|
assert.match(r.stdout, /Disabled rules: some-disabled-rule/);
|
|
351
361
|
});
|
package/src/bin/pi-steering.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* Convert a v1 JSON config to the v2 TS config shape.
|
|
10
10
|
*
|
|
11
11
|
* pi-steering list [--format=text|json]
|
|
12
|
-
* Resolve the
|
|
12
|
+
* Resolve the two-layer config (project `<cwd>/.pi/steering/` +
|
|
13
|
+
* global `<agentDir>/steering/`) from the CWD and print the
|
|
13
14
|
* effective plugins / rules / observers / disables.
|
|
14
15
|
*
|
|
15
16
|
* Exit codes:
|
|
@@ -75,10 +76,12 @@ SUBCOMMANDS
|
|
|
75
76
|
the JSON-to-TS conversion surface and rejected features.
|
|
76
77
|
|
|
77
78
|
list [--format=text|json]
|
|
78
|
-
Load the effective config for the current directory (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
Load the effective config for the current directory (project
|
|
80
|
+
layer at <cwd>/.pi/steering/ + global layer at
|
|
81
|
+
<agentDir>/steering/) and print the resolved plugins, rules,
|
|
82
|
+
and observers, grouped by source. Useful for answering "which
|
|
83
|
+
rules are active here?" without reading the config files by
|
|
84
|
+
hand.
|
|
82
85
|
|
|
83
86
|
OPTIONS
|
|
84
87
|
-h, --help Show this help.
|
|
@@ -214,8 +217,9 @@ async function runList(args: string[]): Promise<number> {
|
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
//
|
|
218
|
-
// DEFAULT_PLUGINS / DEFAULT_RULES; runtime
|
|
220
|
+
// Load project layer (cwd) + global layer (agent dir) and merge.
|
|
221
|
+
// CLI deliberately omits DEFAULT_PLUGINS / DEFAULT_RULES; runtime
|
|
222
|
+
// injects them.
|
|
219
223
|
let layers: readonly SteeringConfig[];
|
|
220
224
|
let loaderDiagnostics: readonly SteeringDiagnostic[] = [];
|
|
221
225
|
try {
|
|
@@ -280,10 +284,11 @@ async function runList(args: string[]): Promise<number> {
|
|
|
280
284
|
/**
|
|
281
285
|
* CLI variant of the merge pipeline. Redirects `console.info`
|
|
282
286
|
* breadcrumbs (disabled-plugin / disabled-rule / dropped-observer)
|
|
283
|
-
* onto stderr so stdout stays clean for `--format=json`.
|
|
284
|
-
*
|
|
285
|
-
* `disabledRules` filter +
|
|
286
|
-
* list` reports the same
|
|
287
|
+
* onto stderr so stdout stays clean for `--format=json`. Loads the
|
|
288
|
+
* project + global layers and merges them inner-first (project wins),
|
|
289
|
+
* then mirrors {@link buildSessionRuntime}'s `disabledRules` filter +
|
|
290
|
+
* `finalizePluginState` so `pi-steering list` reports the same
|
|
291
|
+
* observer-drop set production sees.
|
|
287
292
|
*/
|
|
288
293
|
function runCliMergeWithInfoCapture(layers: readonly SteeringConfig[]): {
|
|
289
294
|
config: SteeringConfig;
|
|
@@ -333,9 +338,10 @@ function printListHelp(): void {
|
|
|
333
338
|
USAGE
|
|
334
339
|
pi-steering list [--format=text|json]
|
|
335
340
|
|
|
336
|
-
|
|
337
|
-
(or
|
|
338
|
-
|
|
341
|
+
Loads the project layer (<cwd>/.pi/steering/) and the global layer
|
|
342
|
+
(~/.pi/agent/steering/, or $PI_CODING_AGENT_DIR/steering) and prints
|
|
343
|
+
the effective plugins, rules, and observers. Project layer wins on
|
|
344
|
+
rule-name collision.
|
|
339
345
|
|
|
340
346
|
FLAGS
|
|
341
347
|
--format=text (default) human-readable grouped output
|
|
@@ -443,7 +449,7 @@ function observerJSON(o: Observer): unknown {
|
|
|
443
449
|
* no-main-commit bash when: branch
|
|
444
450
|
* ...
|
|
445
451
|
*
|
|
446
|
-
* User (
|
|
452
|
+
* User (project + global):
|
|
447
453
|
* (none)
|
|
448
454
|
*
|
|
449
455
|
* Disabled: (none)
|
|
@@ -484,7 +490,7 @@ function renderListText(config: SteeringConfig): string {
|
|
|
484
490
|
}
|
|
485
491
|
|
|
486
492
|
// User block.
|
|
487
|
-
lines.push("User (
|
|
493
|
+
lines.push("User (project + global):");
|
|
488
494
|
if (userRules.length === 0 && userObservers.length === 0) {
|
|
489
495
|
lines.push(" (none)");
|
|
490
496
|
} else {
|
package/src/evaluator.ts
CHANGED
|
@@ -43,12 +43,6 @@
|
|
|
43
43
|
* `evaluateCandidate` stays tool-agnostic.
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
-
import type {
|
|
47
|
-
ExtensionContext,
|
|
48
|
-
ToolCallEvent,
|
|
49
|
-
ToolCallEventResult,
|
|
50
|
-
} from "@earendil-works/pi-coding-agent";
|
|
51
|
-
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
|
|
52
46
|
import {
|
|
53
47
|
type CommandRef,
|
|
54
48
|
cwdTracker,
|
|
@@ -63,6 +57,12 @@ import {
|
|
|
63
57
|
type Word,
|
|
64
58
|
walk,
|
|
65
59
|
} from "@cad0p/unbash-walker";
|
|
60
|
+
import type {
|
|
61
|
+
ExtensionContext,
|
|
62
|
+
ToolCallEvent,
|
|
63
|
+
ToolCallEventResult,
|
|
64
|
+
} from "@earendil-works/pi-coding-agent";
|
|
65
|
+
import { isToolCallEventType } from "@earendil-works/pi-coding-agent";
|
|
66
66
|
import {
|
|
67
67
|
createAppendEntry,
|
|
68
68
|
createExecCache,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* - aggregated render snapshot pins the multi-line format.
|
|
27
27
|
*
|
|
28
28
|
* The bridge factory is async; tests `chdir` into a fresh scratch
|
|
29
|
-
* `$HOME` so the loader
|
|
29
|
+
* `$HOME` so the loader's global layer reads the per-test config.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import assert from "node:assert/strict";
|
|
@@ -224,24 +224,27 @@ describe("register(): factory throws on diagnostics", () => {
|
|
|
224
224
|
});
|
|
225
225
|
|
|
226
226
|
it("throws on plugin-name-collision (warning-class, failOnWarnings default)", async () => {
|
|
227
|
-
//
|
|
228
|
-
// warning-class but escalates under the default
|
|
227
|
+
// Project and global layers ship the same plugin name. The
|
|
228
|
+
// collision is warning-class but escalates under the default
|
|
229
229
|
// `failOnWarnings: true`.
|
|
230
|
-
|
|
230
|
+
const inner = join(tmpHome, "inner");
|
|
231
|
+
mkdirSync(inner, { recursive: true });
|
|
231
232
|
writeSteeringSingleFileConfig(
|
|
232
|
-
|
|
233
|
+
inner,
|
|
233
234
|
`export default {
|
|
234
|
-
disableDefaults: true,
|
|
235
235
|
plugins: [{ name: "shared" }],
|
|
236
236
|
};`,
|
|
237
237
|
);
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
mkdirSync(join(tmpHome, ".pi", "agent"), { recursive: true });
|
|
239
|
+
writeFileSync(
|
|
240
|
+
join(tmpHome, ".pi", "agent", "steering.ts"),
|
|
240
241
|
`export default {
|
|
242
|
+
disableDefaults: true,
|
|
241
243
|
plugins: [{ name: "shared" }],
|
|
242
244
|
};`,
|
|
245
|
+
"utf8",
|
|
243
246
|
);
|
|
244
|
-
process.chdir(
|
|
247
|
+
process.chdir(inner);
|
|
245
248
|
await expectRegisterThrow([/\[warning\]/, /plugin "shared"/]);
|
|
246
249
|
});
|
|
247
250
|
|
|
@@ -385,26 +388,29 @@ describe("register(): factory does NOT throw", () => {
|
|
|
385
388
|
});
|
|
386
389
|
|
|
387
390
|
it("disabledPlugins resolves plugin-name-collision before the check runs", async () => {
|
|
388
|
-
// The
|
|
391
|
+
// The global layer's `disabledPlugins: ["shared"]` removes
|
|
389
392
|
// the would-be collision before cross-layer detection,
|
|
390
393
|
// matching the disable-then-detect ordering. No throw, no
|
|
391
394
|
// warn.
|
|
392
|
-
|
|
395
|
+
const inner = join(tmpHome, "inner");
|
|
396
|
+
mkdirSync(inner, { recursive: true });
|
|
393
397
|
writeSteeringSingleFileConfig(
|
|
394
|
-
|
|
398
|
+
inner,
|
|
395
399
|
`export default {
|
|
396
|
-
disableDefaults: true,
|
|
397
|
-
disabledPlugins: ["shared"],
|
|
398
400
|
plugins: [{ name: "shared" }],
|
|
399
401
|
};`,
|
|
400
402
|
);
|
|
401
|
-
|
|
402
|
-
|
|
403
|
+
mkdirSync(join(tmpHome, ".pi", "agent"), { recursive: true });
|
|
404
|
+
writeFileSync(
|
|
405
|
+
join(tmpHome, ".pi", "agent", "steering.ts"),
|
|
403
406
|
`export default {
|
|
407
|
+
disableDefaults: true,
|
|
408
|
+
disabledPlugins: ["shared"],
|
|
404
409
|
plugins: [{ name: "shared" }],
|
|
405
410
|
};`,
|
|
411
|
+
"utf8",
|
|
406
412
|
);
|
|
407
|
-
process.chdir(
|
|
413
|
+
process.chdir(inner);
|
|
408
414
|
const mock = makeMockPi();
|
|
409
415
|
await register(mock.api as ExtensionAPI);
|
|
410
416
|
const collisionWarn = capturedWarns.find((m) => /plugin "shared"/i.test(m));
|
|
@@ -448,7 +454,7 @@ describe("register(): cwd-mismatch session_start warn", () => {
|
|
|
448
454
|
|
|
449
455
|
it("emits console.warn when ctx.cwd !== launchCwd; engine continues evaluating", async () => {
|
|
450
456
|
// A user-defined rule lives in the launch-cwd config (tmpHome) but
|
|
451
|
-
// not in foreignCwd's
|
|
457
|
+
// not in foreignCwd's project layer; if it still fires after the
|
|
452
458
|
// cwd-mismatch warn, launch-cwd config remained in force.
|
|
453
459
|
writeSteeringSingleFileConfig(
|
|
454
460
|
tmpHome,
|
package/src/index.test.ts
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* - the audit-log side effect for accepted overrides,
|
|
15
15
|
* - the observer-dispatcher side effect on matching tool_result
|
|
16
16
|
* events,
|
|
17
|
-
* - the
|
|
18
|
-
* `.pi/steering.ts` from an isolated `$HOME`.
|
|
17
|
+
* - the two-layer TS-config loader: {@link buildSessionRuntime}
|
|
18
|
+
* reads `.pi/steering.ts` from an isolated `$HOME`.
|
|
19
19
|
*
|
|
20
20
|
* The bridge factory's lifecycle wiring + config-loading glue isn't
|
|
21
21
|
* covered by the unit suites; this file is the only end-to-end check.
|
|
@@ -180,8 +180,8 @@ let tmpHome: string;
|
|
|
180
180
|
* Bind a fresh `$HOME` per test AND chdir into it via the shared
|
|
181
181
|
* {@link useScratchHome} helper. The bridge factory eagerly loads
|
|
182
182
|
* from `process.cwd()` at register time, so tests must launch from
|
|
183
|
-
* the scratch home for the loader
|
|
184
|
-
* config.
|
|
183
|
+
* the scratch home for the loader's project + global layers to find
|
|
184
|
+
* the per-test config.
|
|
185
185
|
*/
|
|
186
186
|
function useRegisterScratchHome(): void {
|
|
187
187
|
useScratchHome("pi-steering-register-", (t) => {
|
package/src/index.ts
CHANGED
|
@@ -141,7 +141,7 @@ export { AGENT_LOOP_INDEX_KEY } from "./evaluator-internals/context.ts";
|
|
|
141
141
|
// `if (ctx.walkerState?.cwd === "unknown") return "unknown";`,
|
|
142
142
|
// then the engine projects via `onUnknown:` policy).
|
|
143
143
|
export { walkerUnknownCwdReason } from "./helpers/walker-unknown-cwd-reason.ts";
|
|
144
|
-
// Loader —
|
|
144
|
+
// Loader — two-layer config discovery + merge.
|
|
145
145
|
export { buildConfig, loadConfigs, loadSteeringConfig } from "./loader.ts";
|
|
146
146
|
// Schema types — the public authoring surface.
|
|
147
147
|
export type {
|
package/src/internal/ref-text.ts
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
* @internal — not part of the public pi-steering surface.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type CommandRef,
|
|
14
|
+
getBasename,
|
|
15
|
+
getCommandArgs,
|
|
16
|
+
} from "@cad0p/unbash-walker";
|
|
13
17
|
|
|
14
18
|
/** Render a ref as `"{basename} {args joined by space}"`, trimmed. */
|
|
15
19
|
export function refToText(ref: CommandRef): string {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - the aggregated error message follows the rule-based spec
|
|
14
14
|
* (header + bullets, errors first, optional path prefix).
|
|
15
15
|
*
|
|
16
|
-
* Uses a tmp `$HOME` so the
|
|
17
|
-
*
|
|
16
|
+
* Uses a tmp `$HOME` so the loader's global layer is scoped to the
|
|
17
|
+
* test directory.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import assert from "node:assert/strict";
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Internal module — not part of the package's public API.
|
|
6
6
|
*
|
|
7
7
|
* This module holds the wiring that the bridge factory in `index.ts`
|
|
8
|
-
* uses to spin up an evaluator + observer dispatcher from
|
|
9
|
-
*
|
|
8
|
+
* uses to spin up an evaluator + observer dispatcher from the
|
|
9
|
+
* two-layer config (project layer at `cwd`, global layer at the
|
|
10
|
+
* agent dir). It is intentionally NOT re-exported from
|
|
10
11
|
* `index.ts` or any other public entry point; consumers building
|
|
11
12
|
* their own extensions should go through `loadHarness` (subpath
|
|
12
13
|
* `@cad0p/pi-steering/testing`) or call `buildEvaluator` /
|