@event4u/agent-config 9.10.0 → 9.12.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +111 -0
- package/README.md +20 -16
- package/agents/templates/.ai-council.yml.example +481 -0
- package/agents/templates/.ai-video.xml.example +247 -0
- package/dist/agent-src/commands/fix/ci.md +29 -0
- package/dist/agent-src/commands/pr/create.md +60 -0
- package/dist/agent-src/contexts/authority/kernel-rule-edits.md +18 -2
- package/dist/agent-src/rules/design-fidelity.md +2 -2
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +1 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +27 -1
- package/dist/agent-src/skills/design-intelligence/data/font-pairings-reference.csv +74 -74
- package/dist/agent-src/skills/design-intelligence/data/manifest.json +2 -0
- package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +0 -7
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +15 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +10 -0
- package/dist/agent-src/skills/typography-system/SKILL.md +55 -11
- package/dist/agent-src/skills/ui-apply-generic/SKILL.md +180 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +34 -30
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +23 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +24 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +332 -0
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +449 -11
- package/dist/agent-src/templates/scripts/work_engine/state.ts +21 -4
- package/dist/cli/initRouting.js +74 -11
- package/dist/cli/initRouting.js.map +1 -1
- package/dist/cli/main.js +14 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/chunk-736JCWLM.js +1104 -0
- package/dist/cli-delegate/chunk-7HQ52DHU.js +74 -0
- package/dist/cli-delegate/chunk-A3HJ6NTI.js +1084 -0
- package/dist/cli-delegate/chunk-EJYIGZ3T.js +125 -0
- package/dist/cli-delegate/chunk-FTAISGDQ.js +117 -0
- package/dist/cli-delegate/chunk-GME3Q44L.js +82 -0
- package/dist/cli-delegate/chunk-GRRPEPRN.js +336 -0
- package/dist/cli-delegate/chunk-HJ6DAJHR.js +502 -0
- package/dist/cli-delegate/chunk-HYWSRNUX.js +179 -0
- package/dist/cli-delegate/chunk-J55WI3RR.js +47 -0
- package/dist/cli-delegate/chunk-MCJYCQPF.js +35 -0
- package/dist/cli-delegate/chunk-NDJLNC6Q.js +169 -0
- package/dist/cli-delegate/chunk-OMIVVCG2.js +26 -0
- package/dist/cli-delegate/chunk-OY53J6II.js +7336 -0
- package/dist/cli-delegate/chunk-QFKQLQ7X.js +10835 -0
- package/dist/cli-delegate/chunk-TEGJU2JX.js +6908 -0
- package/dist/cli-delegate/chunk-TJIAM5FW.js +133 -0
- package/dist/cli-delegate/chunk-XX6TOPD7.js +715 -0
- package/dist/cli-delegate/cmd_analyze_session.js +294 -0
- package/dist/cli-delegate/cmd_conformance.js +643 -0
- package/dist/cli-delegate/cmd_converge.js +326 -0
- package/dist/cli-delegate/cmd_doctor.js +142 -0
- package/dist/cli-delegate/cmd_explain.js +2576 -0
- package/dist/cli-delegate/cmd_export.js +298 -0
- package/dist/cli-delegate/cmd_fleet.js +333 -0
- package/dist/cli-delegate/cmd_memory_get.js +3878 -0
- package/dist/cli-delegate/cmd_migrate.js +11 -0
- package/dist/cli-delegate/cmd_preflight.js +153 -0
- package/dist/cli-delegate/cmd_prune.js +446 -0
- package/dist/cli-delegate/cmd_refresh.js +28 -0
- package/dist/cli-delegate/cmd_settings_check.js +574 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +225 -0
- package/dist/cli-delegate/cmd_sync.js +266 -0
- package/dist/cli-delegate/cmd_uninstall.js +657 -0
- package/dist/cli-delegate/cmd_update.js +425 -0
- package/dist/cli-delegate/cmd_upgrade.js +444 -0
- package/dist/cli-delegate/cmd_validate.js +307 -0
- package/dist/cli-delegate/cmd_versions.js +370 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +45 -16
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +13 -7
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -4
- package/dist/install/emit_host_rules_cli.js +2 -1
- package/dist/install/emit_host_rules_cli.js.map +1 -1
- package/dist/install/install.mjs +634 -409
- package/dist/install/ruleInScope.js +123 -0
- package/dist/install/ruleInScope.js.map +1 -0
- package/dist/install/rule_scope.js +7 -3
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/install/toolDetection.js +40 -12
- package/dist/install/toolDetection.js.map +1 -1
- package/dist/install/wizard-plan.js +12 -3
- package/dist/install/wizard-plan.js.map +1 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1553 -2728
- package/dist/router.json +1 -1
- package/dist/scripts/condense.js +6 -22
- package/dist/scripts/condense.js.map +1 -1
- package/docs/guidelines/design-fidelity-mechanics.md +14 -0
- package/package.json +4 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +34 -30
- package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +23 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +24 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +332 -0
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +449 -11
- package/src/agent-src/templates/scripts/work_engine/state.ts +21 -4
- package/src/config/discovery/packs.yml +7 -0
- package/src/config/evaluator-budgets.json +1 -1
- package/src/config/pack-size-budget.json +27 -0
- package/src/install/emit_host_rules_cli.ts +2 -1
- package/src/install/ruleInScope.ts +133 -0
- package/src/install/rule_scope.ts +7 -3
- package/src/install/toolDetection.ts +39 -8
- package/src/install/wizard-plan.ts +13 -3
- package/src/scripts/_cli/cmd_conformance.ts +2 -1
- package/src/scripts/_cli/cmd_converge.ts +2 -1
- package/src/scripts/_cli/cmd_doctor.ts +214 -13
- package/src/scripts/_cli/cmd_export.ts +2 -1
- package/src/scripts/_cli/cmd_fleet.ts +2 -1
- package/src/scripts/_cli/cmd_migrate.ts +2 -1
- package/src/scripts/_cli/cmd_preflight.ts +2 -1
- package/src/scripts/_cli/cmd_refresh.ts +2 -1
- package/src/scripts/_cli/cmd_update.ts +2 -1
- package/src/scripts/_cli/cmd_upgrade.ts +2 -1
- package/src/scripts/_cli/cmd_versions.ts +2 -1
- package/src/scripts/_cli/detection_report.ts +480 -0
- package/src/scripts/_dispatch.bash +52 -14
- package/src/scripts/_lib/changelog_eras.ts +103 -9
- package/src/scripts/_lib/environment_detector.ts +441 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +38 -2
- package/src/scripts/_lib/package_root.ts +72 -0
- package/src/scripts/ai_council/config.ts +15 -7
- package/src/scripts/ai_council/modes.ts +68 -2
- package/src/scripts/ai_council/transport_resolver.ts +331 -0
- package/src/scripts/check_bundle_path_leakage.ts +10 -4
- package/src/scripts/check_dependency_floors.ts +153 -0
- package/src/scripts/check_installer_import_purity.ts +295 -0
- package/src/scripts/check_pack_size.ts +188 -0
- package/src/scripts/condense.ts +7 -28
- package/src/scripts/council_cli.ts +6 -2
- package/src/scripts/evaluator_umbrella.sh +34 -1
- package/src/scripts/install-hooks.sh +36 -4
- package/src/scripts/install.ts +210 -5
- package/src/scripts/lint_eval_fixture_citations.ts +113 -0
- package/src/scripts/lint_ui_stack_bundles.ts +201 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule-scoping predicate — the ONE decision both pipelines consume.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this module exists (and must stay side-effect-free)
|
|
5
|
+
*
|
|
6
|
+
* `rule_in_scope` used to live in `src/scripts/condense.ts`, and
|
|
7
|
+
* `src/install/rule_scope.ts` imported it from there deliberately, so install
|
|
8
|
+
* semantics could never drift from projection semantics. That intent is right;
|
|
9
|
+
* the location was not.
|
|
10
|
+
*
|
|
11
|
+
* `condense.ts` is maintainer-only CLI tooling and ends in a module-level
|
|
12
|
+
* self-invoke:
|
|
13
|
+
*
|
|
14
|
+
* const isMain = _isCliEntry();
|
|
15
|
+
* if (isMain) { process.exit(main()); }
|
|
16
|
+
*
|
|
17
|
+
* `_isCliEntry()` compares `import.meta.url` against `process.argv[1]`. That is
|
|
18
|
+
* correct for a file run directly — but when esbuild BUNDLES the module into
|
|
19
|
+
* `dist/install/install.mjs`, `import.meta.url` becomes the bundle's own URL,
|
|
20
|
+
* which *is* `argv[1]` when a consumer runs `node dist/install/install.mjs`.
|
|
21
|
+
* The guard then fires inside the installer, `condense.main()` runs, finds no
|
|
22
|
+
* maintainer source tree in a consumer checkout, prints "No source directory
|
|
23
|
+
* found" and calls `process.exit(1)` — killing the install.
|
|
24
|
+
*
|
|
25
|
+
* That is not hypothetical: it reached CI on all four
|
|
26
|
+
* `smoke-public-install` legs the moment the installer began importing
|
|
27
|
+
* `rule_scope.ts` (road-to-consistent-rule-scoping, 2026-07-31).
|
|
28
|
+
*
|
|
29
|
+
* So the predicate lives here instead: **no I/O at module load, no CLI entry,
|
|
30
|
+
* no `process.exit`, and nothing that assumes a maintainer source tree.**
|
|
31
|
+
* `condense.ts` re-exports it for its four in-repo consumers; the install path
|
|
32
|
+
* imports it directly and never pulls the projection pipeline along.
|
|
33
|
+
*
|
|
34
|
+
* Lives under `src/install/` rather than `src/shared/` because it reads the file
|
|
35
|
+
* it classifies: an ESLint boundary keeps `src/shared/**` free of Node built-ins,
|
|
36
|
+
* and the install layer is where credentialled filesystem work already belongs.
|
|
37
|
+
*
|
|
38
|
+
* Keep it that way. Anything added to this file ships inside the consumer
|
|
39
|
+
* installer bundle.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import * as fs from 'node:fs';
|
|
43
|
+
|
|
44
|
+
import * as YAML from 'yaml';
|
|
45
|
+
|
|
46
|
+
/** `yaml.safe_load` semantics: parse failure and `undefined` both → `null`. */
|
|
47
|
+
function parseYaml(text: string): unknown {
|
|
48
|
+
try {
|
|
49
|
+
const data = YAML.parse(text, { version: '1.1' });
|
|
50
|
+
return data === undefined ? null : data;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Python `str.strip()` — whitespace from both ends, Unicode-aware. */
|
|
57
|
+
function strip(s: string): string {
|
|
58
|
+
return s.replace(/^\s+/u, '').replace(/\s+$/u, '');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Python `str.lstrip("\n")` — leading newlines only. */
|
|
62
|
+
function lstripNewlines(s: string): string {
|
|
63
|
+
return s.replace(/^\n+/, '');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Split `---`-delimited frontmatter from the body.
|
|
68
|
+
*
|
|
69
|
+
* Returns `[{}, content]` for anything that is not well-formed frontmatter —
|
|
70
|
+
* an absent block, an unterminated block, or a scalar/sequence document. The
|
|
71
|
+
* caller then sees an untagged artefact, which every axis below fails safe on.
|
|
72
|
+
*/
|
|
73
|
+
export function parseFrontmatter(content: string): [Record<string, unknown>, string] {
|
|
74
|
+
if (!content.startsWith('---')) {
|
|
75
|
+
return [{}, content];
|
|
76
|
+
}
|
|
77
|
+
const end = content.indexOf('\n---', 3);
|
|
78
|
+
if (end === -1) {
|
|
79
|
+
return [{}, content];
|
|
80
|
+
}
|
|
81
|
+
const raw = strip(content.slice(3, end));
|
|
82
|
+
const body = lstripNewlines(content.slice(end + 4));
|
|
83
|
+
let meta: unknown = parseYaml(raw);
|
|
84
|
+
if (meta === null || meta === undefined) {
|
|
85
|
+
meta = {};
|
|
86
|
+
}
|
|
87
|
+
if (typeof meta === 'object' && meta !== null && !Array.isArray(meta)) {
|
|
88
|
+
return [meta as Record<string, unknown>, body];
|
|
89
|
+
}
|
|
90
|
+
return [{}, body];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Does the rule at `source_path` project / install under the given scope?
|
|
95
|
+
*
|
|
96
|
+
* Three independent axes — `workspaces`, `packs`, `roles`. A `null` axis means
|
|
97
|
+
* "unset", i.e. that axis does not filter. Two fail-safe rules make the
|
|
98
|
+
* predicate over-ship rather than under-ship:
|
|
99
|
+
*
|
|
100
|
+
* - a KERNEL rule (`type: always` / `alwaysApply: true`) always ships;
|
|
101
|
+
* - an artefact carrying NO tags on a configured axis ships anyway — an
|
|
102
|
+
* untagged rule is unclassified, not excluded.
|
|
103
|
+
*
|
|
104
|
+
* Reads the file. Never throws for a missing frontmatter block; a genuinely
|
|
105
|
+
* unreadable path is the caller's problem (both call sites pre-check).
|
|
106
|
+
*/
|
|
107
|
+
export function rule_in_scope(
|
|
108
|
+
source_path: string,
|
|
109
|
+
scope: readonly string[] | null,
|
|
110
|
+
pack_scope: readonly string[] | null = null,
|
|
111
|
+
role_scope: readonly string[] | null = null,
|
|
112
|
+
): boolean {
|
|
113
|
+
if (scope === null && pack_scope === null && role_scope === null) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
const [meta] = parseFrontmatter(fs.readFileSync(source_path, 'utf-8'));
|
|
117
|
+
if (meta['type'] === 'always' || meta['alwaysApply'] === true) {
|
|
118
|
+
return true; // kernel always projects
|
|
119
|
+
}
|
|
120
|
+
const axis = (key: string, configured: readonly string[] | null): boolean => {
|
|
121
|
+
if (configured === null) {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
const values = Array.isArray(meta[key])
|
|
125
|
+
? (meta[key] as unknown[]).map((w) => String(w))
|
|
126
|
+
: [];
|
|
127
|
+
if (values.length === 0) {
|
|
128
|
+
return true; // untagged → fail safe: ship it
|
|
129
|
+
}
|
|
130
|
+
return values.some((v) => configured.includes(v));
|
|
131
|
+
};
|
|
132
|
+
return axis('workspaces', scope) && axis('packs', pack_scope) && axis('roles', role_scope);
|
|
133
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Install-time rule scoping
|
|
2
|
+
* Install-time rule scoping for the consumer install
|
|
3
3
|
* (road-to-request-scoped-rule-load Phase 1b).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Naming: this is the CONSUMER INSTALL path, not `docs/architecture.md`'s
|
|
6
|
+
* Pipeline B (which is the `.augment/` projection). Earlier drafts called it
|
|
7
|
+
* "Pipeline B" and collided with that name; use "the consumer install" here.
|
|
8
|
+
*
|
|
9
|
+
* The projection filter (`rule_in_scope`, now `shared/ruleInScope.ts`) covered only the
|
|
6
10
|
* maintainer projection; the consumer install pipeline shipped rules
|
|
7
11
|
* unfiltered (dead `EXCLUDE_RULES` in install.sh; no exclude at all on the
|
|
8
12
|
* global payload). This module is the ONE scoping surface both install
|
|
@@ -28,7 +32,7 @@
|
|
|
28
32
|
import * as fs from 'node:fs';
|
|
29
33
|
import * as path from 'node:path';
|
|
30
34
|
|
|
31
|
-
import { rule_in_scope } from '
|
|
35
|
+
import { rule_in_scope } from './ruleInScope.js';
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
38
|
* Rules excluded from EVERY consumer install path regardless of scope —
|
|
@@ -71,6 +71,44 @@ export function knownToolIds(): readonly string[] {
|
|
|
71
71
|
return Object.keys(TOOL_SIGNALS);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Executable names declared for `id` on `$PATH`, in table order. Empty for
|
|
76
|
+
* tools detected only by a config dir / app bundle (and for the two
|
|
77
|
+
* signal-less ids). Exposed so callers that need the resolved *path* (not
|
|
78
|
+
* just a boolean) can reuse this table instead of restating the binary names.
|
|
79
|
+
*/
|
|
80
|
+
export function toolBinaries(id: string): readonly string[] {
|
|
81
|
+
return TOOL_SIGNALS[id]?.bins ?? [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* First declared binary for `id` that resolves on `$PATH`, or `null`. This is
|
|
86
|
+
* the path-returning twin of `detectInstalledTools`, which reports presence as
|
|
87
|
+
* a boolean across all three signal kinds — a tool can be "installed" (config
|
|
88
|
+
* dir present) while this returns `null` (no executable on `$PATH`).
|
|
89
|
+
*/
|
|
90
|
+
export function resolveToolBinary(id: string, pathEnv?: string): string | null {
|
|
91
|
+
const env = pathEnv ?? process.env['PATH'] ?? '';
|
|
92
|
+
for (const bin of toolBinaries(id)) {
|
|
93
|
+
const resolved = resolveBinaryPath(bin, env);
|
|
94
|
+
if (resolved !== null) return resolved;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Absolute path of `name` on `pathEnv`, or `null`. Mirrors `binOnPath`'s probe order. */
|
|
100
|
+
function resolveBinaryPath(name: string, pathEnv: string): string | null {
|
|
101
|
+
if (pathEnv.length === 0) return null;
|
|
102
|
+
for (const dir of pathEnv.split(delimiter)) {
|
|
103
|
+
if (dir.length === 0) continue;
|
|
104
|
+
for (const candidate of [name, `${name}.exe`, `${name}.cmd`]) {
|
|
105
|
+
const full = join(dir, candidate);
|
|
106
|
+
if (existsSync(full)) return full;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
74
112
|
/**
|
|
75
113
|
* Is an executable named `name` resolvable on `$PATH`? Exported for one-off
|
|
76
114
|
* presence checks (e.g. `rtk` on the Editor-and-tooling step). Defaults to
|
|
@@ -81,14 +119,7 @@ export function isBinaryOnPath(name: string, pathEnv?: string): boolean {
|
|
|
81
119
|
}
|
|
82
120
|
|
|
83
121
|
function binOnPath(name: string, pathEnv: string): boolean {
|
|
84
|
-
|
|
85
|
-
for (const dir of pathEnv.split(delimiter)) {
|
|
86
|
-
if (dir.length === 0) continue;
|
|
87
|
-
if (existsSync(join(dir, name))) return true;
|
|
88
|
-
// Windows-style executables.
|
|
89
|
-
if (existsSync(join(dir, `${name}.exe`)) || existsSync(join(dir, `${name}.cmd`))) return true;
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
122
|
+
return resolveBinaryPath(name, pathEnv) !== null;
|
|
92
123
|
}
|
|
93
124
|
|
|
94
125
|
function isToolInstalled(signal: ToolSignal, home: string, pathEnv: string): boolean {
|
|
@@ -91,6 +91,16 @@ const CLAUDE_SKILL_BUNDLE: ReadonlyArray<readonly [string, string]> = [
|
|
|
91
91
|
* — they either follow a marker-only convention (handled by bridges in
|
|
92
92
|
* Phase A6) or have no markdown convention at all.
|
|
93
93
|
*/
|
|
94
|
+
/**
|
|
95
|
+
* The one deploy-source row that carries install-time rule scoping.
|
|
96
|
+
*
|
|
97
|
+
* Exported so the CLI global path (`scripts/install.ts::_deploy_global_content`)
|
|
98
|
+
* keys its filter off the SAME string this expander does. Two paths agreeing on
|
|
99
|
+
* the predicate but disagreeing on which source it applies to would reproduce
|
|
100
|
+
* exactly the divergence rule scoping exists to prevent.
|
|
101
|
+
*/
|
|
102
|
+
export const RULE_SOURCE_REL = 'dist/agent-src/rules';
|
|
103
|
+
|
|
94
104
|
export const GLOBAL_DEPLOY_SOURCES: Readonly<Record<string, ReadonlyArray<readonly [string, string]>>> = {
|
|
95
105
|
'claude-code': CLAUDE_SKILL_BUNDLE,
|
|
96
106
|
augment: [
|
|
@@ -193,9 +203,9 @@ export function expandWizardSources(inputs: WizardExpandInputs): PlanSource[] {
|
|
|
193
203
|
const srcDir = join(packageRoot, srcRel);
|
|
194
204
|
const destDir = destSub.length === 0 ? destRoot : join(destRoot, destSub);
|
|
195
205
|
// Rule sources get the install-time scoping filter — the same
|
|
196
|
-
// predicate the projection path uses (Phase 1b:
|
|
197
|
-
// never drift from Pipeline A).
|
|
198
|
-
const isRuleSource = srcRel ===
|
|
206
|
+
// predicate the projection path uses (Phase 1b: the consumer
|
|
207
|
+
// install may never drift from Pipeline A, the source projection).
|
|
208
|
+
const isRuleSource = srcRel === RULE_SOURCE_REL;
|
|
199
209
|
out.push({
|
|
200
210
|
toolId,
|
|
201
211
|
srcDir,
|
|
@@ -37,6 +37,7 @@ import * as installed_tools from '../_lib/installed_tools.js';
|
|
|
37
37
|
import { atomicAppendLine } from '../../install/atomic.js';
|
|
38
38
|
import { readRecentEntries } from '../../install/txlog.js';
|
|
39
39
|
import { detectToolPresence } from '../../install/detect.js';
|
|
40
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
40
41
|
import {
|
|
41
42
|
_classify,
|
|
42
43
|
_collect_manifest_entries,
|
|
@@ -56,7 +57,7 @@ type Dict = Record<string, unknown>;
|
|
|
56
57
|
const _HERE = fileURLToPath(import.meta.url);
|
|
57
58
|
|
|
58
59
|
/** Package root — two levels above `src/scripts/_cli/`. */
|
|
59
|
-
export const PACKAGE_ROOT =
|
|
60
|
+
export const PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
|
|
60
61
|
|
|
61
62
|
/** Ordered registry of conformance check identifiers. */
|
|
62
63
|
export const CONFORMANCE_CHECK_IDS = [
|
|
@@ -39,6 +39,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
39
39
|
import * as YAML from 'yaml';
|
|
40
40
|
|
|
41
41
|
import * as user_global_paths from '../_lib/user_global_paths.js';
|
|
42
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
42
43
|
|
|
43
44
|
type Dict = Record<string, unknown>;
|
|
44
45
|
|
|
@@ -67,7 +68,7 @@ const _HERE = fileURLToPath(import.meta.url);
|
|
|
67
68
|
|
|
68
69
|
/** Resolve the @event4u/agent-config package root (this repo). */
|
|
69
70
|
function _package_root(): string {
|
|
70
|
-
return
|
|
71
|
+
return resolvePackageRoot(import.meta.url);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
const SETTINGS_FILENAME = 'agent-settings.yml';
|
|
@@ -102,7 +102,22 @@ import {
|
|
|
102
102
|
} from '../_lib/agent_settings.js';
|
|
103
103
|
import * as ai_council_clients from '../ai_council/clients.js';
|
|
104
104
|
import * as ai_council_config from '../ai_council/config.js';
|
|
105
|
+
import {
|
|
106
|
+
PROVIDER_CLI_META,
|
|
107
|
+
codexHome,
|
|
108
|
+
detectEnvironment,
|
|
109
|
+
} from '../_lib/environment_detector.js';
|
|
110
|
+
import {
|
|
111
|
+
absentCouncilFacts,
|
|
112
|
+
buildDetectionReport,
|
|
113
|
+
detectionJson,
|
|
114
|
+
renderDetectionLines,
|
|
115
|
+
type CouncilFacts,
|
|
116
|
+
type CouncilMemberFacts,
|
|
117
|
+
type DetectionReport,
|
|
118
|
+
} from './detection_report.js';
|
|
105
119
|
import { review_gate_doctor_signal } from '../ai_team/review_gate.js';
|
|
120
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
106
121
|
import {
|
|
107
122
|
LEGACY_ALL,
|
|
108
123
|
excludedRuleBasenames,
|
|
@@ -758,6 +773,7 @@ const CHECK_IDS = [
|
|
|
758
773
|
'humanizer-runtime',
|
|
759
774
|
'tier-usage-readiness',
|
|
760
775
|
'council-cli',
|
|
776
|
+
'detection',
|
|
761
777
|
'team',
|
|
762
778
|
'unsupported-combos',
|
|
763
779
|
'wizard-state',
|
|
@@ -784,6 +800,7 @@ const GLOBAL_CHECK_IDS: ReadonlySet<string> = new Set([
|
|
|
784
800
|
'humanizer-runtime',
|
|
785
801
|
'tier-usage-readiness',
|
|
786
802
|
'council-cli',
|
|
803
|
+
'detection',
|
|
787
804
|
'team',
|
|
788
805
|
'wizard-state',
|
|
789
806
|
'settings-review-pending',
|
|
@@ -994,7 +1011,7 @@ function _check_manifest_integrity(manifest: Dict): Dict {
|
|
|
994
1011
|
/** Resolve the @event4u/agent-config package root (this repo). */
|
|
995
1012
|
function _package_root(): string {
|
|
996
1013
|
// cmd_doctor lives at src/scripts/_cli/cmd_doctor.{py,ts}; parents[3] = repo.
|
|
997
|
-
return
|
|
1014
|
+
return resolvePackageRoot(import.meta.url);
|
|
998
1015
|
}
|
|
999
1016
|
|
|
1000
1017
|
/** Read `version` from this package's package.json; null on error. */
|
|
@@ -2055,16 +2072,13 @@ function osErrorStr(exc: unknown): string {
|
|
|
2055
2072
|
}
|
|
2056
2073
|
|
|
2057
2074
|
/**
|
|
2058
|
-
* Provider → (default binary,
|
|
2059
|
-
*
|
|
2075
|
+
* Provider → (default binary, community-wrapper flag). Canonical definition
|
|
2076
|
+
* lives in `_lib/environment_detector.ts` so this check and the detection
|
|
2077
|
+
* report cannot drift on the billing-relevant boolean; aliased here to keep
|
|
2078
|
+
* the existing call sites unchanged.
|
|
2060
2079
|
*/
|
|
2061
|
-
const _CLI_PROVIDER_META: Record<string, [string, boolean]
|
|
2062
|
-
|
|
2063
|
-
openai: ['codex', false],
|
|
2064
|
-
gemini: ['gemini', false],
|
|
2065
|
-
xai: ['grok', true],
|
|
2066
|
-
perplexity: ['perplexity', true],
|
|
2067
|
-
};
|
|
2080
|
+
const _CLI_PROVIDER_META: Readonly<Record<string, readonly [string, boolean]>> =
|
|
2081
|
+
PROVIDER_CLI_META;
|
|
2068
2082
|
|
|
2069
2083
|
function _check_council_cli(project_root: string): Dict {
|
|
2070
2084
|
// Python wraps the council-module import in a try/except ImportError so a
|
|
@@ -2207,11 +2221,12 @@ function pyFloat(value: number): string {
|
|
|
2207
2221
|
|
|
2208
2222
|
/**
|
|
2209
2223
|
* Codex CLI home — `CODEX_HOME` env override (the codex CLI's own
|
|
2210
|
-
* convention), else `~/.codex`.
|
|
2224
|
+
* convention), else `~/.codex`. Single definition in
|
|
2225
|
+
* `_lib/environment_detector.ts` so the detection report and this check probe
|
|
2226
|
+
* the same path; aliased here to keep the existing call sites unchanged.
|
|
2211
2227
|
*/
|
|
2212
2228
|
function _codex_home(): string {
|
|
2213
|
-
|
|
2214
|
-
return env !== '' ? env : path.join(os.homedir(), '.codex');
|
|
2229
|
+
return codexHome();
|
|
2215
2230
|
}
|
|
2216
2231
|
|
|
2217
2232
|
/**
|
|
@@ -2368,6 +2383,162 @@ function _read_ai_team_block(project_root: string): Dict {
|
|
|
2368
2383
|
* check reports ok ("team mode not configured (default-off)"); WARN fires
|
|
2369
2384
|
* only on failing sub-signals or inconsistent half-configured states.
|
|
2370
2385
|
*/
|
|
2386
|
+
// ---------------------------------------------------------------------------
|
|
2387
|
+
// detection check (road-to-zero-ceremony-detection Phases 3 + 4)
|
|
2388
|
+
// ---------------------------------------------------------------------------
|
|
2389
|
+
|
|
2390
|
+
/** Per-process memo so the check row and the rendered section agree exactly. */
|
|
2391
|
+
let _detection_memo: { root: string; report: DetectionReport } | null = null;
|
|
2392
|
+
|
|
2393
|
+
/**
|
|
2394
|
+
* Read the council-config facts the detection rows need. Never throws — an
|
|
2395
|
+
* absent or unreadable config degrades to "no config", which the report renders
|
|
2396
|
+
* as a fix rather than a failure.
|
|
2397
|
+
*/
|
|
2398
|
+
function _read_council_facts(project_root: string): CouncilFacts {
|
|
2399
|
+
const { load_council_config, resolve_config_path } = ai_council_config;
|
|
2400
|
+
const config_path = String(resolve_config_path(project_root));
|
|
2401
|
+
if (!pathExists(config_path)) {
|
|
2402
|
+
return absentCouncilFacts(config_path);
|
|
2403
|
+
}
|
|
2404
|
+
let cfg: ai_council_config.CouncilConfig;
|
|
2405
|
+
try {
|
|
2406
|
+
cfg = load_council_config(config_path);
|
|
2407
|
+
} catch {
|
|
2408
|
+
// An invalid config is reported by the `council-cli` check, which owns
|
|
2409
|
+
// that problem; here it simply means no facts are derivable.
|
|
2410
|
+
return absentCouncilFacts(config_path);
|
|
2411
|
+
}
|
|
2412
|
+
const members: Record<string, CouncilMemberFacts> = {};
|
|
2413
|
+
for (const [name, m] of cfg.members) {
|
|
2414
|
+
members[name] = {
|
|
2415
|
+
enabled: m.enabled,
|
|
2416
|
+
mode: m.mode,
|
|
2417
|
+
binary: m.binary,
|
|
2418
|
+
apiKeyRef: m.api_key_ref,
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
const cliCaps: Record<string, number> = {};
|
|
2422
|
+
for (const [provider, cap] of cfg.cli_call_budget.max_calls_per_day) {
|
|
2423
|
+
cliCaps[provider] = cap;
|
|
2424
|
+
}
|
|
2425
|
+
return {
|
|
2426
|
+
configPath: config_path,
|
|
2427
|
+
configPresent: true,
|
|
2428
|
+
enabled: cfg.enabled,
|
|
2429
|
+
defaultsMode: cfg.defaults.mode,
|
|
2430
|
+
members,
|
|
2431
|
+
cliCaps,
|
|
2432
|
+
costBudgetMaxUsd: cfg.cost_budget.max_total_usd,
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
/** Build (once per process, per root) the detection report. */
|
|
2437
|
+
function _detection_report(project_root: string): DetectionReport {
|
|
2438
|
+
if (_detection_memo !== null && _detection_memo.root === project_root) {
|
|
2439
|
+
return _detection_memo.report;
|
|
2440
|
+
}
|
|
2441
|
+
const council = _read_council_facts(project_root);
|
|
2442
|
+
let used: Record<string, number> = {};
|
|
2443
|
+
try {
|
|
2444
|
+
const counts = ai_council_clients.load_cli_call_counts();
|
|
2445
|
+
for (const provider of Object.keys(council.cliCaps)) {
|
|
2446
|
+
used[provider] = Number((counts as Dict)[provider] ?? 0);
|
|
2447
|
+
}
|
|
2448
|
+
} catch {
|
|
2449
|
+
used = {};
|
|
2450
|
+
}
|
|
2451
|
+
const report = buildDetectionReport({
|
|
2452
|
+
environment: detectEnvironment(),
|
|
2453
|
+
council,
|
|
2454
|
+
cliCallsUsed: used,
|
|
2455
|
+
});
|
|
2456
|
+
_detection_memo = { root: project_root, report };
|
|
2457
|
+
return report;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
/** Test seam — drop the memo so a fixture root is not served a stale report. */
|
|
2461
|
+
function _reset_detection_memo(): void {
|
|
2462
|
+
_detection_memo = null;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
/**
|
|
2466
|
+
* `detection` health check — what this machine can do, and what it is allowed
|
|
2467
|
+
* to do, as two separate facts.
|
|
2468
|
+
*
|
|
2469
|
+
* Reports per provider: detected · authenticated · auth source · billing class
|
|
2470
|
+
* · enabled-in-config, plus the transport `auto` would pick and a one-line fix
|
|
2471
|
+
* for every unusable capability. All rows come from the read-only, spend-free
|
|
2472
|
+
* detector; nothing here makes a provider call.
|
|
2473
|
+
*
|
|
2474
|
+
* Status model — a recorded consent decision is NOT a defect:
|
|
2475
|
+
*
|
|
2476
|
+
* - no council config, or council disabled → `ok` ("not configured"), with the
|
|
2477
|
+
* fix. Absent feature ≠ broken feature (same stance as the `team` check).
|
|
2478
|
+
* - detected but `enabled: false` → `ok`. That flag is a deliberate spend gate;
|
|
2479
|
+
* warning on it would train the user to silence their own consent record.
|
|
2480
|
+
* - enabled to spend but no transport resolves → `warn`. That IS broken: the
|
|
2481
|
+
* member is permitted to run and cannot.
|
|
2482
|
+
*/
|
|
2483
|
+
function _check_detection(project_root: string): Dict {
|
|
2484
|
+
const report = _detection_report(project_root);
|
|
2485
|
+
const detected = report.providers.filter((r) => r.detected);
|
|
2486
|
+
const hosts_installed = report.hosts.filter((h) => h.installed).length;
|
|
2487
|
+
|
|
2488
|
+
if (!report.council_config_present) {
|
|
2489
|
+
return {
|
|
2490
|
+
id: 'detection',
|
|
2491
|
+
status: 'ok',
|
|
2492
|
+
message:
|
|
2493
|
+
`${hosts_installed} host(s) · ${detected.length} provider(s) detected · ` +
|
|
2494
|
+
'council not configured (default-off)',
|
|
2495
|
+
remedy: report.providers[0]?.fix ?? `create ${report.council_config_path}`,
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
if (!report.council_enabled) {
|
|
2500
|
+
return {
|
|
2501
|
+
id: 'detection',
|
|
2502
|
+
status: 'ok',
|
|
2503
|
+
message:
|
|
2504
|
+
`${hosts_installed} host(s) · ${detected.length} provider(s) detected · ` +
|
|
2505
|
+
'council disabled (default-off)',
|
|
2506
|
+
remedy: `set \`enabled: true\` in ${report.council_config_path}`,
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
const broken = report.providers.filter((r) => r.enabledInConfig && !r.available);
|
|
2511
|
+
const spending = report.providers.filter((r) => r.enabledInConfig && r.available);
|
|
2512
|
+
const unpermitted = detected.filter((r) => !r.enabledInConfig);
|
|
2513
|
+
|
|
2514
|
+
const summary =
|
|
2515
|
+
`${hosts_installed} host(s) · ${spending.length} member(s) ready ` +
|
|
2516
|
+
`(${spending.map((r) => `${r.provider}→${r.transport}/${r.billing}`).join(', ') || 'none'})` +
|
|
2517
|
+
(unpermitted.length === 0
|
|
2518
|
+
? ''
|
|
2519
|
+
: ` · ${unpermitted.length} detected but not allowed to spend: ` +
|
|
2520
|
+
unpermitted.map((r) => r.provider).join(', '));
|
|
2521
|
+
|
|
2522
|
+
if (broken.length > 0) {
|
|
2523
|
+
return {
|
|
2524
|
+
id: 'detection',
|
|
2525
|
+
status: 'warn',
|
|
2526
|
+
message: `${summary} · ${broken.length} enabled member(s) have no transport`,
|
|
2527
|
+
remedy: broken.map((r) => `${r.provider}: ${r.fix}`).join(' · '),
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
return {
|
|
2532
|
+
id: 'detection',
|
|
2533
|
+
status: 'ok',
|
|
2534
|
+
message: summary,
|
|
2535
|
+
remedy:
|
|
2536
|
+
unpermitted.length === 0
|
|
2537
|
+
? `run \`agent-config doctor --check detection\` for the full table`
|
|
2538
|
+
: unpermitted.map((r) => r.fix).join(' · '),
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2371
2542
|
function _check_team(project_root: string): Dict {
|
|
2372
2543
|
const ai_team = _read_ai_team_block(project_root);
|
|
2373
2544
|
const enabled = _settings_flag(ai_team['enabled']);
|
|
@@ -2819,6 +2990,7 @@ function _run_checks(
|
|
|
2819
2990
|
'humanizer-runtime': () => _check_humanizer_runtime(),
|
|
2820
2991
|
'tier-usage-readiness': () => _check_tier_usage_readiness(project_root),
|
|
2821
2992
|
'council-cli': () => _check_council_cli(project_root),
|
|
2993
|
+
'detection': () => _check_detection(project_root),
|
|
2822
2994
|
team: () => _check_team(project_root),
|
|
2823
2995
|
'unsupported-combos': () => _check_unsupported_combos(manifest),
|
|
2824
2996
|
'wizard-state': _check_wizard_state,
|
|
@@ -2895,6 +3067,7 @@ function _run_checks_no_manifest(
|
|
|
2895
3067
|
'humanizer-runtime': () => _check_humanizer_runtime(),
|
|
2896
3068
|
'tier-usage-readiness': () => _check_tier_usage_readiness(project_root),
|
|
2897
3069
|
'council-cli': () => _check_council_cli(project_root),
|
|
3070
|
+
'detection': () => _check_detection(project_root),
|
|
2898
3071
|
team: () => _check_team(project_root),
|
|
2899
3072
|
'unsupported-combos': () => _skipped_manifest_check('unsupported-combos'),
|
|
2900
3073
|
'wizard-state': _check_wizard_state,
|
|
@@ -2945,8 +3118,10 @@ function _run_no_manifest(
|
|
|
2945
3118
|
);
|
|
2946
3119
|
}
|
|
2947
3120
|
_emit_checks_text(checks);
|
|
3121
|
+
_emit_detection_text(project_root, checks);
|
|
2948
3122
|
} else {
|
|
2949
3123
|
_emit_checks_text(checks);
|
|
3124
|
+
_emit_detection_text(project_root, checks);
|
|
2950
3125
|
}
|
|
2951
3126
|
|
|
2952
3127
|
if (opts.check !== null) {
|
|
@@ -2983,6 +3158,12 @@ function _emit_json(
|
|
|
2983
3158
|
}
|
|
2984
3159
|
if (checks !== null) {
|
|
2985
3160
|
payload['checks'] = checks;
|
|
3161
|
+
// The detection section rides along whenever checks ran, so the agent /
|
|
3162
|
+
// GUI contract carries the full provider → transport → billing table
|
|
3163
|
+
// and not just the one-line check row.
|
|
3164
|
+
if (checks.some((c) => c['id'] === 'detection')) {
|
|
3165
|
+
payload['detection'] = detectionJson(_detection_report(project_root));
|
|
3166
|
+
}
|
|
2986
3167
|
}
|
|
2987
3168
|
print(_jsonDumpsIndentAscii(payload, 2));
|
|
2988
3169
|
}
|
|
@@ -3002,6 +3183,21 @@ function _emit_checks_text(checks: Dict[]): void {
|
|
|
3002
3183
|
print('');
|
|
3003
3184
|
}
|
|
3004
3185
|
|
|
3186
|
+
/**
|
|
3187
|
+
* The detection section in text mode. Rendered from the same report the
|
|
3188
|
+
* `detection` check row and the `--json` payload use, so the three cannot
|
|
3189
|
+
* disagree about which members spend money.
|
|
3190
|
+
*/
|
|
3191
|
+
function _emit_detection_text(project_root: string, checks: Dict[]): void {
|
|
3192
|
+
if (!checks.some((c) => c['id'] === 'detection')) {
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
3195
|
+
for (const line of renderDetectionLines(_detection_report(project_root))) {
|
|
3196
|
+
print(line);
|
|
3197
|
+
}
|
|
3198
|
+
print('');
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3005
3201
|
function _emit_text(
|
|
3006
3202
|
project_root: string,
|
|
3007
3203
|
missing: Dict[],
|
|
@@ -3304,6 +3500,7 @@ function main(argv: string[] | null = null): number {
|
|
|
3304
3500
|
print(` 📍 project_root: ${project_root} (origin: ${origin})`);
|
|
3305
3501
|
}
|
|
3306
3502
|
_emit_checks_text(checks);
|
|
3503
|
+
_emit_detection_text(project_root, checks);
|
|
3307
3504
|
if (opts.check === null) {
|
|
3308
3505
|
_emit_text(project_root, missing, modified, foreign, tag_drift);
|
|
3309
3506
|
}
|
|
@@ -3389,6 +3586,10 @@ export {
|
|
|
3389
3586
|
_check_mcp_beta_readiness,
|
|
3390
3587
|
_check_tier_usage_readiness,
|
|
3391
3588
|
_check_council_cli,
|
|
3589
|
+
_check_detection,
|
|
3590
|
+
_detection_report,
|
|
3591
|
+
_read_council_facts,
|
|
3592
|
+
_reset_detection_memo,
|
|
3392
3593
|
_check_team,
|
|
3393
3594
|
_check_unsupported_combos,
|
|
3394
3595
|
_check_wizard_state,
|
|
@@ -44,6 +44,7 @@ import * as os from 'node:os';
|
|
|
44
44
|
import * as path from 'node:path';
|
|
45
45
|
import * as crypto from 'node:crypto';
|
|
46
46
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
47
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
47
48
|
|
|
48
49
|
import {
|
|
49
50
|
AIDER_MARKER,
|
|
@@ -57,7 +58,7 @@ import {
|
|
|
57
58
|
ZED_MARKER,
|
|
58
59
|
} from '../install.js';
|
|
59
60
|
|
|
60
|
-
const PACKAGE_ROOT =
|
|
61
|
+
const PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
|
|
61
62
|
const TEMPLATES_DIR = path.join(PACKAGE_ROOT, 'dist/agent-src', 'templates');
|
|
62
63
|
|
|
63
64
|
// ---------------------------------------------------------------------------
|
|
@@ -32,9 +32,10 @@ import * as YAML from 'yaml';
|
|
|
32
32
|
|
|
33
33
|
import { runPreflight, hasBlockingFinding, type PreflightFinding } from '../../install/preflight.js';
|
|
34
34
|
import type { ConflictPolicy } from '../../install/types.js';
|
|
35
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
35
36
|
|
|
36
37
|
const _HERE = fileURLToPath(import.meta.url);
|
|
37
|
-
const PACKAGE_ROOT =
|
|
38
|
+
const PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
|
|
38
39
|
|
|
39
40
|
/** One fleet target, parsed from `fleet.yaml`. */
|
|
40
41
|
export interface FleetRepo {
|
|
@@ -75,6 +75,7 @@ import process from 'node:process';
|
|
|
75
75
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
76
76
|
|
|
77
77
|
import { resolve_project_root } from '../_lib/agent_settings.js';
|
|
78
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
78
79
|
// NOTE: the v0→v1 migrator is NOT statically imported. It lives in the
|
|
79
80
|
// consumer-shipped work_engine template, which ships as `.ts` (run via tsx),
|
|
80
81
|
// while this CLI is compiled to `dist/cli/*.js` and run under plain `node` in
|
|
@@ -521,7 +522,7 @@ function _load_state_migrator(): StateMigrator | null {
|
|
|
521
522
|
// Locate the shipped v0→v1 migration driver (`.ts`). Prefer the shipped
|
|
522
523
|
// `dist/` tree; fall back to the dev `src/` tree. A stripped install with
|
|
523
524
|
// no engine yields `null` (Python ImportError parity).
|
|
524
|
-
const pkg_root =
|
|
525
|
+
const pkg_root = resolvePackageRoot(import.meta.url);
|
|
525
526
|
const rel = path.join(
|
|
526
527
|
'agent-src', 'templates', 'scripts', 'work_engine', 'migration', 'v0_to_v1.ts',
|
|
527
528
|
);
|
|
@@ -28,11 +28,12 @@ import {
|
|
|
28
28
|
} from '../../install/preflight.js';
|
|
29
29
|
import { GLOBAL_DEPLOY_SOURCES, expandWizardSources } from '../../install/wizard-plan.js';
|
|
30
30
|
import type { ConflictPolicy } from '../../install/types.js';
|
|
31
|
+
import { resolvePackageRoot } from '../_lib/package_root.js';
|
|
31
32
|
|
|
32
33
|
const _HERE = fileURLToPath(import.meta.url);
|
|
33
34
|
|
|
34
35
|
/** Package root — three levels above `src/scripts/_cli/`. */
|
|
35
|
-
const PACKAGE_ROOT =
|
|
36
|
+
const PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
|
|
36
37
|
|
|
37
38
|
const USAGE =
|
|
38
39
|
'usage: cmd_preflight [-h] [--tools a,b,...] [--target DIR] [--json]\n';
|