@event4u/agent-config 14.21.0 → 14.22.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +198 -0
- package/CREDITS.md +37 -0
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-BIXEDQPZ.js} +6 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +26 -2
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +8 -8
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1193 -917
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +6 -0
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +39219 -38995
- package/dist/scripts/condense.js +32 -25
- package/docs/MIGRATION.md +49 -1
- package/docs/contracts/settings-classes.md +1 -1
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/continuity-surface.json +509 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +1 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/ai_council/api_transport.ts +5 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/code_graph/build.ts +138 -15
- package/src/scripts/code_graph/cli.ts +76 -8
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +121 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +247 -6
- package/src/scripts/code_graph/types.ts +66 -1
- package/src/scripts/code_graph/validate.ts +23 -0
- package/src/scripts/condense.ts +34 -22
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +11 -12
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/handoff_context_hook.ts +79 -0
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +64 -12
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
- package/src/scripts/hooks/concern_registry.ts +4 -2
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/rule_inject_hook.ts +1 -1
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +82 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tools.ts +2 -56
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/vendor/grammars/README.md +61 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
|
@@ -0,0 +1,923 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
3
|
+
|
|
4
|
+
// src/scripts/check_update_banner.ts
|
|
5
|
+
import * as fs5 from "node:fs";
|
|
6
|
+
import * as path5 from "node:path";
|
|
7
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
8
|
+
|
|
9
|
+
// src/scripts/_lib/agent_settings.ts
|
|
10
|
+
import { createRequire } from "node:module";
|
|
11
|
+
import * as fs2 from "node:fs";
|
|
12
|
+
import * as path2 from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
// src/scripts/_lib/user_global_paths.ts
|
|
16
|
+
import * as fs from "node:fs";
|
|
17
|
+
import * as os from "node:os";
|
|
18
|
+
import * as path from "node:path";
|
|
19
|
+
var EVENT4U_HOME_ENV = "EVENT4U_CONFIG_HOME";
|
|
20
|
+
var DEFAULT_EVENT4U_ROOT_RELATIVE = path.join(".event4u", "agent-config");
|
|
21
|
+
var LEGACY_XDG_ROOT_RELATIVE = path.join(".config", "agent-config");
|
|
22
|
+
function expanduser(p) {
|
|
23
|
+
if (p === "~") {
|
|
24
|
+
return os.homedir();
|
|
25
|
+
}
|
|
26
|
+
if (p.startsWith("~/") || process.platform === "win32" && p.startsWith("~\\")) {
|
|
27
|
+
return path.join(os.homedir(), p.slice(2));
|
|
28
|
+
}
|
|
29
|
+
return p;
|
|
30
|
+
}
|
|
31
|
+
function is_absolute_like_python(p) {
|
|
32
|
+
if (process.platform === "win32") {
|
|
33
|
+
return /^[a-zA-Z]:[\\/]/.test(p) || /^([\\/]{2})[^\\/]+[\\/][^\\/]+/.test(p);
|
|
34
|
+
}
|
|
35
|
+
return p.startsWith("/");
|
|
36
|
+
}
|
|
37
|
+
function event4u_root(env) {
|
|
38
|
+
const env_map = env ?? process.env;
|
|
39
|
+
const override = env_map[EVENT4U_HOME_ENV];
|
|
40
|
+
if (override) {
|
|
41
|
+
return expanduser(override);
|
|
42
|
+
}
|
|
43
|
+
return path.join(os.homedir(), DEFAULT_EVENT4U_ROOT_RELATIVE);
|
|
44
|
+
}
|
|
45
|
+
function legacy_xdg_root() {
|
|
46
|
+
return path.join(os.homedir(), LEGACY_XDG_ROOT_RELATIVE);
|
|
47
|
+
}
|
|
48
|
+
function resolve_with_fallback(relative_name, options = {}) {
|
|
49
|
+
if (is_absolute_like_python(relative_name)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`resolve_with_fallback expects a relative path, got '${relative_name}'`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const new_path = path.join(event4u_root(options.env ?? null), relative_name);
|
|
55
|
+
if (fs.existsSync(new_path)) {
|
|
56
|
+
return new_path;
|
|
57
|
+
}
|
|
58
|
+
const legacy_path = path.join(legacy_xdg_root(), relative_name);
|
|
59
|
+
if (fs.existsSync(legacy_path)) {
|
|
60
|
+
return legacy_path;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
function write_target(relative_name, options = {}) {
|
|
65
|
+
if (is_absolute_like_python(relative_name)) {
|
|
66
|
+
throw new Error(`write_target expects a relative path, got '${relative_name}'`);
|
|
67
|
+
}
|
|
68
|
+
return path.join(event4u_root(options.env ?? null), relative_name);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/shared/settingsCarveOut.ts
|
|
72
|
+
var SETTINGS_CARVE_OUT = [
|
|
73
|
+
{
|
|
74
|
+
key: "projection.mode",
|
|
75
|
+
reader: "src/scripts/install.ts:3409 _resolve_scoped_projection",
|
|
76
|
+
absentResolvesTo: "legacy-all",
|
|
77
|
+
reason: "The template fallback applies only when NO global settings file exists. Once a file exists, an absent key means legacy-all by documented contract, so an existing install is never silently narrowed on upgrade."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: "projection.rule_workspaces",
|
|
81
|
+
reader: "src/install/rule_scope.ts:96 ruleScopeFromSettings",
|
|
82
|
+
absentResolvesTo: "null \u2014 LEGACY_ALL, i.e. every rule ships including maintainer-only ones",
|
|
83
|
+
reason: "Same upgrade contract as projection.mode, and the failure is louder: an absent list widens the projection instead of narrowing it."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
key: "discipline_profile",
|
|
87
|
+
reader: "work_engine/_lib/agent_settings.ts:1263",
|
|
88
|
+
absentResolvesTo: "essential, unconditionally",
|
|
89
|
+
reason: "The template ships `auto`, which resolves to `off` on any measured-null model, any non-Claude host, and any host exposing no model id. Absent skips that resolution entirely and loads the essential tier everywhere."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: "chat_history.frequency",
|
|
93
|
+
reader: "src/scripts/chat_history.ts:1140",
|
|
94
|
+
absentResolvesTo: "per_phase",
|
|
95
|
+
reason: "Canonical substitution is `per_turn`. Absent gives coarser capture than the shipped default \u2014 an audit-thinning change, which the class contract's own test 8 (what can the attacker HIDE) names as the severe direction."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: "profile.id",
|
|
99
|
+
reader: "src/scripts/config/profiles.ts:221 resolve_profile",
|
|
100
|
+
absentResolvesTo: "the id `developer`, but a DEGRADED profile \u2014 packs: [], personas: [], hints: []",
|
|
101
|
+
reason: "Exactly the projection.mode shape: the template default is honoured only when no settings file exists. With a file present the id resolves but its profile body does not load, so the install silently loses every pack and persona."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "quality.local_auto_run",
|
|
105
|
+
reader: "src/scripts/lint_roadmap_ci_steps.ts:106",
|
|
106
|
+
absentResolvesTo: "true \u2014 which DISABLES the CI-step gate",
|
|
107
|
+
reason: "Inverted polarity, and the most dangerous row in the set: the template ships `false`, which ARMS the gate. Omitting the key would disarm a quality gate for every fresh install while the reference page says it is on."
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: "onboarding.onboarded",
|
|
111
|
+
reader: "src/scripts/onboarding_gate_hook.ts:99",
|
|
112
|
+
absentResolvesTo: "gate skipped, as if onboarding had already completed",
|
|
113
|
+
reason: "Absent behaves like `onboarded: true`, not like the template default `false`. Omitting it would skip the onboarding gate on precisely the fresh installs the gate exists for."
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: "chat_history.enabled",
|
|
117
|
+
reader: "src/scripts/chat_history.ts:1025 _read_chat_history_enabled",
|
|
118
|
+
absentResolvesTo: "false",
|
|
119
|
+
reason: "The reader falls back to false while the template ships true. This one is privacy-shaped: fixing the reader would start recording history for every install whose file lacks the key. Writing it explicitly keeps the decision visible in the file the user can read."
|
|
120
|
+
}
|
|
121
|
+
];
|
|
122
|
+
function carveOutKeys() {
|
|
123
|
+
return SETTINGS_CARVE_OUT.map((c) => c.key);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/scripts/_lib/agent_settings.ts
|
|
127
|
+
var _require = createRequire(import.meta.url);
|
|
128
|
+
var Logger = class {
|
|
129
|
+
name = "scripts._lib.agent_settings";
|
|
130
|
+
records = [];
|
|
131
|
+
info(message, ...args) {
|
|
132
|
+
this.records.push({ level: "INFO", message: _format(message, args) });
|
|
133
|
+
}
|
|
134
|
+
warning(message, ...args) {
|
|
135
|
+
this.records.push({ level: "WARNING", message: _format(message, args) });
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
function _format(template, args) {
|
|
139
|
+
let i = 0;
|
|
140
|
+
return template.replace(/%s/g, () => {
|
|
141
|
+
if (i < args.length) {
|
|
142
|
+
const value = args[i];
|
|
143
|
+
i += 1;
|
|
144
|
+
return _pystr(value);
|
|
145
|
+
}
|
|
146
|
+
return "%s";
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function _pystr(value) {
|
|
150
|
+
if (Array.isArray(value)) {
|
|
151
|
+
return `[${value.map((v) => _pyrepr(v)).join(", ")}]`;
|
|
152
|
+
}
|
|
153
|
+
return String(value);
|
|
154
|
+
}
|
|
155
|
+
function _pyrepr(value) {
|
|
156
|
+
if (typeof value === "string") {
|
|
157
|
+
return `'${value}'`;
|
|
158
|
+
}
|
|
159
|
+
return String(value);
|
|
160
|
+
}
|
|
161
|
+
var logger = new Logger();
|
|
162
|
+
var DEFAULT_PROJECT_FILE = ".agent-settings.yml";
|
|
163
|
+
var LOCAL_PROJECT_FILE = ".agent-settings.local.yml";
|
|
164
|
+
var LOCAL_PROJECT_SUBDIR = ["agents", "settings"];
|
|
165
|
+
function _local_settings_path(project_root) {
|
|
166
|
+
return path2.join(project_root, ...LOCAL_PROJECT_SUBDIR, LOCAL_PROJECT_FILE);
|
|
167
|
+
}
|
|
168
|
+
function _canonical_settings_path(project_root) {
|
|
169
|
+
return path2.join(project_root, ...LOCAL_PROJECT_SUBDIR, DEFAULT_PROJECT_FILE);
|
|
170
|
+
}
|
|
171
|
+
var USER_GLOBAL_FILENAME = "agent-settings.yml";
|
|
172
|
+
function DEFAULT_USER_GLOBAL_FILE() {
|
|
173
|
+
return write_target(USER_GLOBAL_FILENAME);
|
|
174
|
+
}
|
|
175
|
+
var USER_GLOBAL_CANONICAL_RELATIVE = "settings/.agent-settings.yml";
|
|
176
|
+
function user_global_settings_paths() {
|
|
177
|
+
const paths = [];
|
|
178
|
+
const flat = resolve_with_fallback(USER_GLOBAL_FILENAME);
|
|
179
|
+
if (flat !== null) {
|
|
180
|
+
paths.push(flat);
|
|
181
|
+
}
|
|
182
|
+
const canonical = resolve_with_fallback(USER_GLOBAL_CANONICAL_RELATIVE);
|
|
183
|
+
if (canonical !== null) {
|
|
184
|
+
paths.push(canonical);
|
|
185
|
+
}
|
|
186
|
+
if (paths.length === 0) {
|
|
187
|
+
paths.push(DEFAULT_USER_GLOBAL_FILE());
|
|
188
|
+
}
|
|
189
|
+
return paths;
|
|
190
|
+
}
|
|
191
|
+
var MERGEABLE_KEYS = [
|
|
192
|
+
// `name`, `ide` and `personal.bot_icon` are the PRE-MIGRATION spellings.
|
|
193
|
+
// `install.ts` migrates `ide` → `personal.ide` and `pr_comment_bot_icon`
|
|
194
|
+
// into its `personal.` home, and this list was never moved with them — so
|
|
195
|
+
// the whitelist named keys the template does not have while the keys it
|
|
196
|
+
// does have were filtered out silently. A user setting either preference
|
|
197
|
+
// user-globally got no error, no warning, and no effect
|
|
198
|
+
// (road-to-capability-answerability 4.3, ADR-219).
|
|
199
|
+
//
|
|
200
|
+
// Both spellings are listed rather than replaced: a legacy file that still
|
|
201
|
+
// uses the old name keeps working, and nothing that resolved before
|
|
202
|
+
// resolves differently. `name` has no reader anywhere and no template key;
|
|
203
|
+
// it is kept only so this change stays purely additive.
|
|
204
|
+
"name",
|
|
205
|
+
"ide",
|
|
206
|
+
"personal.ide",
|
|
207
|
+
"rule_loading_tier",
|
|
208
|
+
"memory.cadence",
|
|
209
|
+
"personal.bot_icon",
|
|
210
|
+
"personal.pr_comment_bot_icon",
|
|
211
|
+
"personal.autonomy",
|
|
212
|
+
// Knowledge-card global cross-project sharing is a USER-GLOBAL setting
|
|
213
|
+
// (ADR-100 / road-to-structure-grounding-v2). Whitelisted so the
|
|
214
|
+
// ~/.event4u/agent-config/agent-settings.yml values are honoured.
|
|
215
|
+
"knowledge.global_sharing.enabled",
|
|
216
|
+
"knowledge.global_sharing.allowed_tiers",
|
|
217
|
+
"knowledge.global_sharing.redaction.enabled",
|
|
218
|
+
"knowledge.global_sharing.redaction.halt_on_trigger",
|
|
219
|
+
"knowledge.global_sharing.auto_promote_threshold",
|
|
220
|
+
"knowledge.global_sharing.freshness.hypothesis_after_days",
|
|
221
|
+
"knowledge.global_sharing.freshness.stale_after_days"
|
|
222
|
+
];
|
|
223
|
+
var TEMPLATE_RELATIVE = path2.join("src", "config", "agent-settings.template.yml");
|
|
224
|
+
var _PACKAGE_ROOT = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
|
|
225
|
+
function default_template_path() {
|
|
226
|
+
return path2.join(_PACKAGE_ROOT, TEMPLATE_RELATIVE);
|
|
227
|
+
}
|
|
228
|
+
function _is_placeholder(value) {
|
|
229
|
+
return typeof value === "string" && /^__[A-Z0-9_]+__$/.test(value);
|
|
230
|
+
}
|
|
231
|
+
function _prune_template(tree, excluded, prefix = "") {
|
|
232
|
+
const out = {};
|
|
233
|
+
for (const [key, value] of Object.entries(tree)) {
|
|
234
|
+
const dotted = prefix === "" ? key : `${prefix}.${key}`;
|
|
235
|
+
if (excluded.has(dotted) || _is_placeholder(value)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (_is_plain_dict(value)) {
|
|
239
|
+
const child = _prune_template(value, excluded, dotted);
|
|
240
|
+
if (Object.keys(child).length > 0) {
|
|
241
|
+
out[key] = child;
|
|
242
|
+
}
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
out[key] = value;
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
var _template_defaults_cache = null;
|
|
250
|
+
function template_defaults(template_path) {
|
|
251
|
+
const use_cache = template_path === void 0;
|
|
252
|
+
if (use_cache && _template_defaults_cache !== null) {
|
|
253
|
+
return _deepcopy(_template_defaults_cache);
|
|
254
|
+
}
|
|
255
|
+
const parsed = _read_yaml(template_path ?? default_template_path()) ?? {};
|
|
256
|
+
const pruned = _prune_template(parsed, new Set(carveOutKeys()));
|
|
257
|
+
if (use_cache) {
|
|
258
|
+
_template_defaults_cache = pruned;
|
|
259
|
+
}
|
|
260
|
+
return _deepcopy(pruned);
|
|
261
|
+
}
|
|
262
|
+
var ANCHOR_AGENT_SETTINGS = "agent-settings";
|
|
263
|
+
var ANCHOR_AGENTS_DIR = "agents-dir";
|
|
264
|
+
var ANCHOR_GIT = "git";
|
|
265
|
+
var _AGENTS_DIR_MARKERS = [
|
|
266
|
+
"roadmaps",
|
|
267
|
+
"settings/.ai-council.yml",
|
|
268
|
+
"roadmaps-progress.md",
|
|
269
|
+
// `overrides/` is the guaranteed minimal-consumer surface (ADR-020).
|
|
270
|
+
// Replaced the retired `.event4u-bridge.yml` marker (ADR-020 amendment
|
|
271
|
+
// 2026-07-13) so a bare consumer `agents/` dir stays anchorable.
|
|
272
|
+
"overrides"
|
|
273
|
+
];
|
|
274
|
+
var _LEGACY_ANCHOR_ENV = "AGENT_CONFIG_LEGACY_ANCHOR";
|
|
275
|
+
function _exists(p) {
|
|
276
|
+
try {
|
|
277
|
+
fs2.lstatSync(p);
|
|
278
|
+
fs2.statSync(p);
|
|
279
|
+
return true;
|
|
280
|
+
} catch {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function _is_dir(p) {
|
|
285
|
+
try {
|
|
286
|
+
return fs2.statSync(p).isDirectory();
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function _is_file(p) {
|
|
292
|
+
try {
|
|
293
|
+
return fs2.statSync(p).isFile();
|
|
294
|
+
} catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function _resolve(p) {
|
|
299
|
+
const absolute = path2.resolve(p);
|
|
300
|
+
try {
|
|
301
|
+
return fs2.realpathSync(absolute);
|
|
302
|
+
} catch {
|
|
303
|
+
return absolute;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function _ancestor_chain(start) {
|
|
307
|
+
const chain = [];
|
|
308
|
+
let cursor = start;
|
|
309
|
+
for (; ; ) {
|
|
310
|
+
chain.push(cursor);
|
|
311
|
+
const parent = path2.dirname(cursor);
|
|
312
|
+
if (parent === cursor) {
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
cursor = parent;
|
|
316
|
+
}
|
|
317
|
+
return chain;
|
|
318
|
+
}
|
|
319
|
+
function _boundary_anchor_at(dir) {
|
|
320
|
+
const agents_dir = path2.join(dir, "agents");
|
|
321
|
+
if (_is_dir(agents_dir)) {
|
|
322
|
+
for (const marker of _AGENTS_DIR_MARKERS) {
|
|
323
|
+
if (_exists(path2.join(agents_dir, marker))) {
|
|
324
|
+
return ANCHOR_AGENTS_DIR;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (_exists(path2.join(dir, ".git"))) {
|
|
329
|
+
return ANCHOR_GIT;
|
|
330
|
+
}
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
function find_project_root_with_anchor(start) {
|
|
334
|
+
const current = _exists(start) ? _resolve(start) : start;
|
|
335
|
+
const legacy = process.env[_LEGACY_ANCHOR_ENV] === "1";
|
|
336
|
+
const chain = _ancestor_chain(current);
|
|
337
|
+
if (legacy) {
|
|
338
|
+
for (const candidate of chain) {
|
|
339
|
+
if (_exists(path2.join(candidate, ".git"))) {
|
|
340
|
+
return [candidate, ANCHOR_GIT];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
for (const candidate of chain) {
|
|
346
|
+
const anchor = _boundary_anchor_at(candidate);
|
|
347
|
+
if (anchor !== null) {
|
|
348
|
+
return [candidate, anchor];
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
let outermost = null;
|
|
352
|
+
for (const candidate of chain) {
|
|
353
|
+
if (_exists(path2.join(candidate, DEFAULT_PROJECT_FILE))) {
|
|
354
|
+
outermost = candidate;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (outermost !== null) {
|
|
358
|
+
return [outermost, ANCHOR_AGENT_SETTINGS];
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
function find_project_root(start) {
|
|
363
|
+
const result = find_project_root_with_anchor(start);
|
|
364
|
+
return result !== null ? result[0] : null;
|
|
365
|
+
}
|
|
366
|
+
function _resolve_cascade_paths(cwd, project_path) {
|
|
367
|
+
if (cwd === null) {
|
|
368
|
+
const legacy = project_path ? project_path : DEFAULT_PROJECT_FILE;
|
|
369
|
+
const parent = path2.dirname(legacy);
|
|
370
|
+
return [legacy, _canonical_settings_path(parent), _local_settings_path(parent)];
|
|
371
|
+
}
|
|
372
|
+
const root = find_project_root(cwd);
|
|
373
|
+
if (root === null) {
|
|
374
|
+
const legacy = project_path ? project_path : DEFAULT_PROJECT_FILE;
|
|
375
|
+
const parent = path2.dirname(legacy);
|
|
376
|
+
return [legacy, _canonical_settings_path(parent), _local_settings_path(parent)];
|
|
377
|
+
}
|
|
378
|
+
const cwd_resolved = _resolve(cwd);
|
|
379
|
+
const chain = [];
|
|
380
|
+
let cursor = cwd_resolved;
|
|
381
|
+
for (; ; ) {
|
|
382
|
+
chain.push(cursor);
|
|
383
|
+
if (cursor === root) {
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
const parent = path2.dirname(cursor);
|
|
387
|
+
if (parent === cursor) {
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
cursor = parent;
|
|
391
|
+
}
|
|
392
|
+
chain.reverse();
|
|
393
|
+
return [
|
|
394
|
+
...chain.map((d) => path2.join(d, DEFAULT_PROJECT_FILE)),
|
|
395
|
+
_canonical_settings_path(root),
|
|
396
|
+
_local_settings_path(root)
|
|
397
|
+
];
|
|
398
|
+
}
|
|
399
|
+
function load_agent_settings(options = {}) {
|
|
400
|
+
const project_path = options.project_path ?? null;
|
|
401
|
+
const user_global_path = options.user_global_path ?? null;
|
|
402
|
+
const verbose = options.verbose ?? false;
|
|
403
|
+
const cwd = options.cwd ?? null;
|
|
404
|
+
const template_path = options.template_path ?? null;
|
|
405
|
+
const user_global_raw = {};
|
|
406
|
+
for (const p of user_global_path ? [user_global_path] : user_global_settings_paths()) {
|
|
407
|
+
_deep_merge(user_global_raw, _read_yaml(p) ?? {});
|
|
408
|
+
}
|
|
409
|
+
const [user_global_filtered, ignored] = _filter_whitelist(user_global_raw, MERGEABLE_KEYS);
|
|
410
|
+
if (verbose && ignored.length > 0) {
|
|
411
|
+
logger.info(
|
|
412
|
+
"agent_settings: ignored non-whitelisted user-global keys: %s",
|
|
413
|
+
[...ignored].sort()
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
const cascade = _resolve_cascade_paths(cwd, project_path);
|
|
417
|
+
const merged = template_defaults(template_path ?? void 0);
|
|
418
|
+
_deep_merge(merged, user_global_filtered);
|
|
419
|
+
for (const p of cascade) {
|
|
420
|
+
const layer = _read_yaml(p) ?? {};
|
|
421
|
+
if (Object.keys(layer).length > 0) {
|
|
422
|
+
_deep_merge(merged, layer);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
_warn_removed_always_on_keys(merged);
|
|
426
|
+
return merged;
|
|
427
|
+
}
|
|
428
|
+
var REMOVED_KEYS = /* @__PURE__ */ new Map([
|
|
429
|
+
["subagents.enabled", "always-on orchestration"],
|
|
430
|
+
["subagents.auto", "always-on orchestration"],
|
|
431
|
+
["subagents.host_capabilities", "always-on orchestration"],
|
|
432
|
+
["subagents.budget_routing", "always-on orchestration"],
|
|
433
|
+
["ai_team.enabled", "always-on orchestration"],
|
|
434
|
+
["hooks.turn_end_gate.enabled", "the turn-end gate is always armed"],
|
|
435
|
+
["hooks.turn_end_gate.promissory", "the turn-end gate is always armed"],
|
|
436
|
+
["hooks.turn_end_gate.language", "the turn-end gate is always armed"],
|
|
437
|
+
["hooks.turn_end_gate.verification", "the turn-end gate is always armed"],
|
|
438
|
+
["telegraph.speak_scope", "the rule body states its own scope; compile_router gates on telegraph.speak alone"],
|
|
439
|
+
["chat_history.max_size_kb", "the rotate command takes --max-kb from argv; session-count pruning bounds the file"],
|
|
440
|
+
["chat_history.on_overflow", "the overflow mode comes from the rotate command --mode argv"],
|
|
441
|
+
["quality.wait_for_remote_ci", "whether to poll follows from the push plus a detectable remote pipeline"],
|
|
442
|
+
["legal_review_prep.consented_at", "the provenance sidecar settings:set writes and consentVerdict reads"],
|
|
443
|
+
["worktrees.mode", "the user asking for a worktree in the chat; creation is instruction-only and hardcoded"]
|
|
444
|
+
]);
|
|
445
|
+
var _warnedRemovedKeys = /* @__PURE__ */ new Set();
|
|
446
|
+
function _readDottedSettingsPath(root, dotted) {
|
|
447
|
+
let node = root;
|
|
448
|
+
for (const part of dotted.split(".")) {
|
|
449
|
+
if (typeof node !== "object" || node === null || Array.isArray(node)) {
|
|
450
|
+
return void 0;
|
|
451
|
+
}
|
|
452
|
+
node = node[part];
|
|
453
|
+
}
|
|
454
|
+
return node;
|
|
455
|
+
}
|
|
456
|
+
function _warn_removed_always_on_keys(merged) {
|
|
457
|
+
for (const [key, reason] of REMOVED_KEYS) {
|
|
458
|
+
if (_warnedRemovedKeys.has(key)) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (_readDottedSettingsPath(merged, key) === void 0) {
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
_warnedRemovedKeys.add(key);
|
|
465
|
+
process.stderr.write(`${key} was removed (${reason}); ignored.
|
|
466
|
+
`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
function _read_yaml(p) {
|
|
470
|
+
if (!_is_file(p)) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
let YAML;
|
|
474
|
+
try {
|
|
475
|
+
YAML = _require("yaml");
|
|
476
|
+
} catch {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
let data;
|
|
480
|
+
try {
|
|
481
|
+
const text = fs2.readFileSync(p, "utf-8");
|
|
482
|
+
data = YAML.parse(text, { version: "1.1" });
|
|
483
|
+
if (data === null || data === void 0) {
|
|
484
|
+
data = {};
|
|
485
|
+
}
|
|
486
|
+
} catch (err) {
|
|
487
|
+
if (_is_yaml_error(err) || _is_os_error(err)) {
|
|
488
|
+
logger.warning("agent_settings: unreadable or malformed YAML at %s", p);
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
throw err;
|
|
492
|
+
}
|
|
493
|
+
return _is_plain_dict(data) ? data : null;
|
|
494
|
+
}
|
|
495
|
+
function _is_yaml_error(err) {
|
|
496
|
+
if (err instanceof Error) {
|
|
497
|
+
return err.name.startsWith("YAML") || err.name === "Error" || err.name === "TypeError";
|
|
498
|
+
}
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
function _is_os_error(err) {
|
|
502
|
+
return typeof err === "object" && err !== null && "code" in err && typeof err.code === "string";
|
|
503
|
+
}
|
|
504
|
+
function _filter_whitelist(raw, allowed) {
|
|
505
|
+
const filtered = {};
|
|
506
|
+
for (const dotted of allowed) {
|
|
507
|
+
const value = _get_dotted(raw, dotted);
|
|
508
|
+
if (value !== null && value !== void 0) {
|
|
509
|
+
_set_dotted(filtered, dotted, value);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const ignored = _leaf_paths(raw).filter((p) => !allowed.includes(p));
|
|
513
|
+
return [filtered, ignored];
|
|
514
|
+
}
|
|
515
|
+
function _get_dotted(data, dotted) {
|
|
516
|
+
let cursor = data;
|
|
517
|
+
for (const part of dotted.split(".")) {
|
|
518
|
+
if (!_is_plain_dict(cursor) || !(part in cursor)) {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
cursor = cursor[part];
|
|
522
|
+
}
|
|
523
|
+
return cursor;
|
|
524
|
+
}
|
|
525
|
+
function _set_dotted(target, dotted, value) {
|
|
526
|
+
const parts = dotted.split(".");
|
|
527
|
+
let cursor = target;
|
|
528
|
+
for (const part of parts.slice(0, -1)) {
|
|
529
|
+
let nxt = cursor[part];
|
|
530
|
+
if (nxt === void 0) {
|
|
531
|
+
nxt = {};
|
|
532
|
+
cursor[part] = nxt;
|
|
533
|
+
}
|
|
534
|
+
if (!_is_plain_dict(nxt)) {
|
|
535
|
+
nxt = {};
|
|
536
|
+
cursor[part] = nxt;
|
|
537
|
+
}
|
|
538
|
+
cursor = nxt;
|
|
539
|
+
}
|
|
540
|
+
cursor[parts[parts.length - 1]] = value;
|
|
541
|
+
}
|
|
542
|
+
function _leaf_paths(data, prefix = "") {
|
|
543
|
+
const paths = [];
|
|
544
|
+
for (const [key, value] of Object.entries(data)) {
|
|
545
|
+
const p = prefix ? `${prefix}.${key}` : key;
|
|
546
|
+
if (_is_plain_dict(value) && Object.keys(value).length > 0) {
|
|
547
|
+
paths.push(..._leaf_paths(value, p));
|
|
548
|
+
} else {
|
|
549
|
+
paths.push(p);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return paths;
|
|
553
|
+
}
|
|
554
|
+
function _deep_merge(dst, src) {
|
|
555
|
+
for (const [key, value] of Object.entries(src)) {
|
|
556
|
+
if (_is_plain_dict(value) && _is_plain_dict(dst[key])) {
|
|
557
|
+
_deep_merge(dst[key], value);
|
|
558
|
+
} else {
|
|
559
|
+
dst[key] = value;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function _deepcopy(value) {
|
|
564
|
+
if (Array.isArray(value)) {
|
|
565
|
+
return value.map((v) => _deepcopy(v));
|
|
566
|
+
}
|
|
567
|
+
if (_is_plain_dict(value)) {
|
|
568
|
+
const out = {};
|
|
569
|
+
for (const [k, v] of Object.entries(value)) {
|
|
570
|
+
out[k] = _deepcopy(v);
|
|
571
|
+
}
|
|
572
|
+
return out;
|
|
573
|
+
}
|
|
574
|
+
return value;
|
|
575
|
+
}
|
|
576
|
+
function _is_plain_dict(value) {
|
|
577
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// src/scripts/_lib/scan_scope.ts
|
|
581
|
+
import * as fs3 from "node:fs";
|
|
582
|
+
import * as path3 from "node:path";
|
|
583
|
+
var DeadScopeError = class extends Error {
|
|
584
|
+
gate;
|
|
585
|
+
constructor(gate, message) {
|
|
586
|
+
super(message);
|
|
587
|
+
this.name = "DeadScopeError";
|
|
588
|
+
this.gate = gate;
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
function assertWatchlistResolves(opts) {
|
|
592
|
+
const resolved = opts.candidates.filter((rel) => {
|
|
593
|
+
try {
|
|
594
|
+
return fs3.statSync(path3.join(opts.repoRoot, rel)).isFile();
|
|
595
|
+
} catch {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
if (resolved.length === 0) {
|
|
600
|
+
throw new DeadScopeError(
|
|
601
|
+
opts.gate,
|
|
602
|
+
`${opts.gate}: none of its ${opts.candidates.length} guarded path(s) exist under ${opts.repoRoot} \u2014 the guard is watching phantoms and can never fire. Checked: ${opts.candidates.join(", ")}`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
return resolved;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/scripts/_lib/update_check.ts
|
|
609
|
+
import { randomBytes } from "node:crypto";
|
|
610
|
+
import * as fs4 from "node:fs";
|
|
611
|
+
import * as path4 from "node:path";
|
|
612
|
+
var PACKAGE_NAME = "@event4u/agent-config";
|
|
613
|
+
var NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
614
|
+
var FETCH_TIMEOUT_S = 1;
|
|
615
|
+
var CHECK_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
616
|
+
var STATE_FILENAME = "update-check.json";
|
|
617
|
+
var DEFAULT_STATE_PATH = write_target(STATE_FILENAME);
|
|
618
|
+
function _resolve_state_path() {
|
|
619
|
+
const found = resolve_with_fallback(STATE_FILENAME);
|
|
620
|
+
if (found !== null) {
|
|
621
|
+
return found;
|
|
622
|
+
}
|
|
623
|
+
return DEFAULT_STATE_PATH;
|
|
624
|
+
}
|
|
625
|
+
async function fetch_latest_from_npm(options = {}) {
|
|
626
|
+
const timeout = options.timeout ?? FETCH_TIMEOUT_S;
|
|
627
|
+
const url = options.url ?? NPM_REGISTRY_URL;
|
|
628
|
+
const fetch_impl = options.fetch_impl ?? fetch;
|
|
629
|
+
try {
|
|
630
|
+
const resp = await fetch_impl(url, {
|
|
631
|
+
headers: {
|
|
632
|
+
Accept: "application/json",
|
|
633
|
+
"User-Agent": "agent-config-update-check"
|
|
634
|
+
},
|
|
635
|
+
signal: AbortSignal.timeout(timeout * 1e3)
|
|
636
|
+
});
|
|
637
|
+
const payload = await resp.json();
|
|
638
|
+
if (payload !== null && typeof payload === "object" && !Array.isArray(payload)) {
|
|
639
|
+
const version = payload.version;
|
|
640
|
+
if (typeof version === "string" && version.trim()) {
|
|
641
|
+
return version.trim();
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
} catch {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
649
|
+
function _read_state(state_path) {
|
|
650
|
+
try {
|
|
651
|
+
const raw = fs4.readFileSync(state_path, "utf-8");
|
|
652
|
+
const data = JSON.parse(raw);
|
|
653
|
+
if (data !== null && typeof data === "object" && !Array.isArray(data)) {
|
|
654
|
+
return data;
|
|
655
|
+
}
|
|
656
|
+
} catch {
|
|
657
|
+
}
|
|
658
|
+
return {};
|
|
659
|
+
}
|
|
660
|
+
function _write_state(state_path, payload) {
|
|
661
|
+
const parent = path4.dirname(state_path);
|
|
662
|
+
fs4.mkdirSync(parent, { recursive: true });
|
|
663
|
+
const tmp = path4.join(parent, `.update-check-${randomBytes(8).toString("hex")}`);
|
|
664
|
+
try {
|
|
665
|
+
const sorted = {};
|
|
666
|
+
for (const key of Object.keys(payload).sort()) {
|
|
667
|
+
sorted[key] = payload[key];
|
|
668
|
+
}
|
|
669
|
+
fs4.writeFileSync(tmp, JSON.stringify(sorted, null, 2), { encoding: "utf-8", mode: 384 });
|
|
670
|
+
fs4.chmodSync(tmp, 384);
|
|
671
|
+
fs4.renameSync(tmp, state_path);
|
|
672
|
+
} catch (err) {
|
|
673
|
+
try {
|
|
674
|
+
fs4.unlinkSync(tmp);
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
throw err;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function _should_check(state, now) {
|
|
681
|
+
const last = state.last_check_utc;
|
|
682
|
+
if (typeof last !== "string") {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
let normalized = last.replace(/Z/g, "+00:00");
|
|
686
|
+
if (!/[+-]\d{2}:?\d{2}$/.test(normalized)) {
|
|
687
|
+
normalized = `${normalized}+00:00`;
|
|
688
|
+
}
|
|
689
|
+
const last_ms = Date.parse(normalized);
|
|
690
|
+
if (Number.isNaN(last_ms)) {
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
return now.getTime() - last_ms >= CHECK_WINDOW_MS;
|
|
694
|
+
}
|
|
695
|
+
function _format_banner(latest, installed) {
|
|
696
|
+
return `\u2139\uFE0F agent-config ${latest} available (you have ${installed}).
|
|
697
|
+
Update: npx ${PACKAGE_NAME} update`;
|
|
698
|
+
}
|
|
699
|
+
function _int_or_zero(part) {
|
|
700
|
+
const trimmed = part.trim();
|
|
701
|
+
if (/^[+-]?\d+$/.test(trimmed)) {
|
|
702
|
+
return parseInt(trimmed, 10);
|
|
703
|
+
}
|
|
704
|
+
return 0;
|
|
705
|
+
}
|
|
706
|
+
function _is_newer(latest, installed) {
|
|
707
|
+
const parse = (v) => {
|
|
708
|
+
const core = v.replace(/^v+/, "").split("-")[0] ?? "";
|
|
709
|
+
const parts = core.split(".");
|
|
710
|
+
const out = [];
|
|
711
|
+
for (const p of parts.slice(0, 3)) {
|
|
712
|
+
out.push(_int_or_zero(p));
|
|
713
|
+
}
|
|
714
|
+
while (out.length < 3) {
|
|
715
|
+
out.push(0);
|
|
716
|
+
}
|
|
717
|
+
return [out[0], out[1], out[2]];
|
|
718
|
+
};
|
|
719
|
+
const a = parse(latest);
|
|
720
|
+
const b = parse(installed);
|
|
721
|
+
for (let i = 0; i < 3; i += 1) {
|
|
722
|
+
if (a[i] > b[i]) {
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
if (a[i] < b[i]) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
function _format_utc(d) {
|
|
732
|
+
return d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
733
|
+
}
|
|
734
|
+
async function check_for_update(installed_version, options = {}) {
|
|
735
|
+
const env = options.env ?? process.env;
|
|
736
|
+
if (env.AGENT_CONFIG_NO_UPDATE_CHECK === "1") {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
if (env.CI === "1" || env.CI === "true" || env.GITHUB_ACTIONS === "true") {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
const settings_enabled = options.settings_enabled ?? true;
|
|
743
|
+
if (!settings_enabled) {
|
|
744
|
+
return null;
|
|
745
|
+
}
|
|
746
|
+
let is_tty = options.is_tty ?? null;
|
|
747
|
+
if (is_tty === null) {
|
|
748
|
+
is_tty = Boolean(process.stdout.isTTY);
|
|
749
|
+
}
|
|
750
|
+
if (!is_tty) {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
754
|
+
const read_path = options.state_path ?? _resolve_state_path();
|
|
755
|
+
const write_path = options.state_path ?? DEFAULT_STATE_PATH;
|
|
756
|
+
const state = _read_state(read_path);
|
|
757
|
+
if (!_should_check(state, now)) {
|
|
758
|
+
const latest2 = state.last_seen_version;
|
|
759
|
+
if (typeof latest2 === "string" && _is_newer(latest2, installed_version)) {
|
|
760
|
+
return _format_banner(latest2, installed_version);
|
|
761
|
+
}
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
const fetcher = options.fetcher ?? (() => fetch_latest_from_npm());
|
|
765
|
+
const latest = await fetcher();
|
|
766
|
+
const fallback_seen = Object.prototype.hasOwnProperty.call(state, "last_seen_version") ? state.last_seen_version : "";
|
|
767
|
+
const payload = {
|
|
768
|
+
last_check_utc: _format_utc(now),
|
|
769
|
+
// Python: `latest or state.get("last_seen_version", "")`.
|
|
770
|
+
last_seen_version: latest ? latest : fallback_seen,
|
|
771
|
+
installed_version
|
|
772
|
+
};
|
|
773
|
+
try {
|
|
774
|
+
_write_state(write_path, payload);
|
|
775
|
+
} catch (err) {
|
|
776
|
+
const code = err.code;
|
|
777
|
+
if (typeof code !== "string") {
|
|
778
|
+
throw err;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (!latest || !_is_newer(latest, installed_version)) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
return _format_banner(latest, installed_version);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/scripts/check_update_banner.ts
|
|
788
|
+
var _HERE = fileURLToPath2(import.meta.url);
|
|
789
|
+
var ROOT = path5.resolve(path5.dirname(_HERE), "..", "..");
|
|
790
|
+
var DOC = `Thin CLI wrapper: emit the daily update-check banner to stderr.
|
|
791
|
+
|
|
792
|
+
Invoked by the \`scripts/agent-config\` dispatcher **after** a
|
|
793
|
+
subcommand finishes. Never raises, never exits non-zero \u2014 banner is
|
|
794
|
+
best-effort. See \`scripts/_lib/update_check.ts\` for the decision
|
|
795
|
+
logic.
|
|
796
|
+
|
|
797
|
+
Usage:
|
|
798
|
+
./scripts-run src/scripts/check_update_banner [--installed-version X.Y.Z]
|
|
799
|
+
|
|
800
|
+
When \`--installed-version\` is omitted, reads \`package.json\` next to
|
|
801
|
+
the package root (\`$PACKAGE_ROOT/package.json\`).`;
|
|
802
|
+
function _read_installed_version(package_root) {
|
|
803
|
+
const candidate = path5.join(package_root, "package.json");
|
|
804
|
+
try {
|
|
805
|
+
const data = JSON.parse(fs5.readFileSync(candidate, "utf-8"));
|
|
806
|
+
const version = data["version"];
|
|
807
|
+
if (typeof version === "string" && version.trim()) {
|
|
808
|
+
return version.trim();
|
|
809
|
+
}
|
|
810
|
+
} catch {
|
|
811
|
+
}
|
|
812
|
+
return "";
|
|
813
|
+
}
|
|
814
|
+
function _read_settings_flag(cwd) {
|
|
815
|
+
let settings;
|
|
816
|
+
try {
|
|
817
|
+
settings = load_agent_settings({ cwd });
|
|
818
|
+
} catch {
|
|
819
|
+
return true;
|
|
820
|
+
}
|
|
821
|
+
const block = settings["update_check"];
|
|
822
|
+
if (block && typeof block === "object" && !Array.isArray(block)) {
|
|
823
|
+
if (block["enabled"] === false) {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
function parse_args(argv) {
|
|
830
|
+
let installed_version = "";
|
|
831
|
+
let cwd = process.cwd();
|
|
832
|
+
let help = false;
|
|
833
|
+
for (let i = 0; i < argv.length; i++) {
|
|
834
|
+
const arg = argv[i];
|
|
835
|
+
if (arg === "--installed-version") {
|
|
836
|
+
installed_version = argv[++i] ?? "";
|
|
837
|
+
} else if (arg.startsWith("--installed-version=")) {
|
|
838
|
+
installed_version = arg.slice("--installed-version=".length);
|
|
839
|
+
} else if (arg === "--cwd") {
|
|
840
|
+
cwd = argv[++i] ?? cwd;
|
|
841
|
+
} else if (arg.startsWith("--cwd=")) {
|
|
842
|
+
cwd = arg.slice("--cwd=".length);
|
|
843
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
844
|
+
help = true;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return { installed_version, cwd, help };
|
|
848
|
+
}
|
|
849
|
+
async function main(argv = process.argv.slice(2)) {
|
|
850
|
+
const args = parse_args(argv);
|
|
851
|
+
if (args.help) {
|
|
852
|
+
process.stdout.write(DOC + "\n");
|
|
853
|
+
return 0;
|
|
854
|
+
}
|
|
855
|
+
if (!args.installed_version) {
|
|
856
|
+
try {
|
|
857
|
+
assertWatchlistResolves({
|
|
858
|
+
gate: "check_update_banner",
|
|
859
|
+
candidates: ["package.json"],
|
|
860
|
+
repoRoot: ROOT
|
|
861
|
+
});
|
|
862
|
+
} catch (err) {
|
|
863
|
+
if (err instanceof DeadScopeError) {
|
|
864
|
+
process.stderr.write(`\u274C ${err.message}
|
|
865
|
+
`);
|
|
866
|
+
return 0;
|
|
867
|
+
}
|
|
868
|
+
throw err;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const installed = args.installed_version || _read_installed_version(ROOT);
|
|
872
|
+
if (!installed) {
|
|
873
|
+
return 0;
|
|
874
|
+
}
|
|
875
|
+
let cwd;
|
|
876
|
+
try {
|
|
877
|
+
cwd = path5.resolve(args.cwd);
|
|
878
|
+
} catch {
|
|
879
|
+
cwd = ROOT;
|
|
880
|
+
}
|
|
881
|
+
const enabled = _read_settings_flag(cwd);
|
|
882
|
+
let banner;
|
|
883
|
+
try {
|
|
884
|
+
banner = await check_for_update(installed, { settings_enabled: enabled });
|
|
885
|
+
} catch {
|
|
886
|
+
return 0;
|
|
887
|
+
}
|
|
888
|
+
if (banner) {
|
|
889
|
+
process.stderr.write(banner + "\n");
|
|
890
|
+
}
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
893
|
+
function _isCliEntry() {
|
|
894
|
+
if (process.argv[1] === void 0) {
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
if (true) {
|
|
898
|
+
if (path5.basename(process.argv[1], ".js") === "check_update_banner") {
|
|
899
|
+
return true;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
const argvUrl = pathToFileURL(path5.resolve(process.argv[1])).href;
|
|
903
|
+
if (import.meta.url === argvUrl) {
|
|
904
|
+
return true;
|
|
905
|
+
}
|
|
906
|
+
try {
|
|
907
|
+
const here = fs5.realpathSync(fileURLToPath2(import.meta.url));
|
|
908
|
+
const argv = fs5.realpathSync(path5.resolve(process.argv[1]));
|
|
909
|
+
return here === argv;
|
|
910
|
+
} catch {
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (_isCliEntry() || process.argv[1] === _HERE) {
|
|
915
|
+
void main().then((code) => process.exit(code));
|
|
916
|
+
}
|
|
917
|
+
export {
|
|
918
|
+
ROOT,
|
|
919
|
+
_read_installed_version,
|
|
920
|
+
_read_settings_flag,
|
|
921
|
+
main,
|
|
922
|
+
parse_args
|
|
923
|
+
};
|