@event4u/agent-config 4.1.0 → 4.3.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/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +51 -0
- package/README.md +3 -2
- package/dist/cli/agent-config.js +9 -0
- package/dist/cli/agent-config.js.map +1 -1
- package/dist/cli/commands/uiServe.js +1 -1
- package/dist/cli/commands/uiServe.js.map +1 -1
- package/dist/cli/initRouting.js +101 -0
- package/dist/cli/initRouting.js.map +1 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1 -1
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +1 -1
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +1 -1
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/server/routes/install.js +11 -200
- package/dist/server/routes/install.js.map +1 -1
- package/dist/server/routes/wizard.js +167 -26
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/server/schemas/settings.js +1 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/ui/assets/index-BDAhhpDV.js +40 -0
- package/dist/ui/assets/index-BDAhhpDV.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/contracts/gui-wizard.md +116 -96
- package/docs/decisions/ADR-021-deployment-shape.md +2 -2
- package/docs/deploy/connector-setup.md +2 -2
- package/docs/deploy/policy-cookbook.md +2 -2
- package/package.json +1 -1
- package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
- package/scripts/install.py +197 -34
- package/scripts/lint_framework_leakage_allowlist.json +8 -0
- package/dist/install/apply.js +0 -238
- package/dist/install/apply.js.map +0 -1
- package/dist/install/bridges/augment.js +0 -20
- package/dist/install/bridges/augment.js.map +0 -1
- package/dist/install/bridges/claude.js +0 -44
- package/dist/install/bridges/claude.js.map +0 -1
- package/dist/install/bridges/cline.js +0 -69
- package/dist/install/bridges/cline.js.map +0 -1
- package/dist/install/bridges/copilot.js +0 -28
- package/dist/install/bridges/copilot.js.map +0 -1
- package/dist/install/bridges/cursor.js +0 -34
- package/dist/install/bridges/cursor.js.map +0 -1
- package/dist/install/bridges/gemini.js +0 -39
- package/dist/install/bridges/gemini.js.map +0 -1
- package/dist/install/bridges/index.js +0 -88
- package/dist/install/bridges/index.js.map +0 -1
- package/dist/install/bridges/marker-content.js +0 -153
- package/dist/install/bridges/marker-content.js.map +0 -1
- package/dist/install/bridges/markers.js +0 -42
- package/dist/install/bridges/markers.js.map +0 -1
- package/dist/install/bridges/types.js +0 -31
- package/dist/install/bridges/types.js.map +0 -1
- package/dist/install/bridges/vscode.js +0 -26
- package/dist/install/bridges/vscode.js.map +0 -1
- package/dist/install/bridges/windsurf.js +0 -35
- package/dist/install/bridges/windsurf.js.map +0 -1
- package/dist/ui/assets/index-DLEuEW1V.js +0 -35
- package/dist/ui/assets/index-DLEuEW1V.js.map +0 -1
package/scripts/install.py
CHANGED
|
@@ -88,6 +88,43 @@ LEGACY_RENAME_MAP = {
|
|
|
88
88
|
|
|
89
89
|
QUIET = False
|
|
90
90
|
|
|
91
|
+
# Machine-readable progress stream for the wizard `--apply-payload` real-apply
|
|
92
|
+
# bridge (road-to-single-install-source-of-truth § Phase 1). When True,
|
|
93
|
+
# `_emit_progress` writes NDJSON lines to stdout so the GUI can stream install
|
|
94
|
+
# progress; human-readable `info`/`success` output is suppressed via QUIET so
|
|
95
|
+
# the two never interleave (`warn`/`fail` still surface on stderr). Off for
|
|
96
|
+
# every normal CLI install.
|
|
97
|
+
PROGRESS_NDJSON = False
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _emit_progress(obj: "dict[str, Any]") -> None:
|
|
101
|
+
"""Write one NDJSON progress line to stdout when PROGRESS_NDJSON is on.
|
|
102
|
+
|
|
103
|
+
No-op for normal CLI installs. The line shapes mirror the wizard SSE
|
|
104
|
+
frames the GUI already consumes: per-unit
|
|
105
|
+
``{"type":"file","file":...,"status":...,"written":N,"total":M}`` and the
|
|
106
|
+
terminal ``{"type":"done"|"error",...}``.
|
|
107
|
+
"""
|
|
108
|
+
if not PROGRESS_NDJSON:
|
|
109
|
+
return
|
|
110
|
+
sys.stdout.write(json.dumps(obj, separators=(",", ":")) + "\n")
|
|
111
|
+
sys.stdout.flush()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _emit_progress_terminal(rc: int) -> None:
|
|
115
|
+
"""Emit the terminal NDJSON frame for a real-apply run.
|
|
116
|
+
|
|
117
|
+
rc == 0 → ``{"type":"done"}``; otherwise ``{"type":"error",...}``. No-op
|
|
118
|
+
unless PROGRESS_NDJSON is on, so it is safe to call from every install
|
|
119
|
+
return path.
|
|
120
|
+
"""
|
|
121
|
+
if not PROGRESS_NDJSON:
|
|
122
|
+
return
|
|
123
|
+
if rc == 0:
|
|
124
|
+
_emit_progress({"type": "done"})
|
|
125
|
+
else:
|
|
126
|
+
_emit_progress({"type": "error", "code": "E_INSTALL", "exitCode": rc})
|
|
127
|
+
|
|
91
128
|
|
|
92
129
|
def info(msg: str) -> None:
|
|
93
130
|
if not QUIET:
|
|
@@ -817,12 +854,42 @@ def _validate_user_type(package_root: Path, value: str) -> str:
|
|
|
817
854
|
return cleaned
|
|
818
855
|
|
|
819
856
|
|
|
857
|
+
def _inject_packs(body: str, packs: "list[str]") -> str:
|
|
858
|
+
"""Insert a top-level ``packs:`` block into a rendered settings body.
|
|
859
|
+
|
|
860
|
+
Inserted directly after the ``cost_profile:`` line so the active pack
|
|
861
|
+
selection sits beside the other install-time knobs. No-op when ``packs``
|
|
862
|
+
is empty — non-pack installs stay byte-identical to the template render.
|
|
863
|
+
"""
|
|
864
|
+
if not packs:
|
|
865
|
+
return body
|
|
866
|
+
block = "packs:\n" + "".join(f" - {p}\n" for p in packs)
|
|
867
|
+
lines = body.splitlines(keepends=True)
|
|
868
|
+
out: list[str] = []
|
|
869
|
+
inserted = False
|
|
870
|
+
for line in lines:
|
|
871
|
+
out.append(line)
|
|
872
|
+
if not inserted and line.startswith("cost_profile:"):
|
|
873
|
+
if not line.endswith("\n"):
|
|
874
|
+
out[-1] = line + "\n"
|
|
875
|
+
out.append(block)
|
|
876
|
+
inserted = True
|
|
877
|
+
if not inserted:
|
|
878
|
+
# No cost_profile anchor (unexpected) — append at the end so the
|
|
879
|
+
# selection is still recorded rather than silently dropped.
|
|
880
|
+
if out and not out[-1].endswith("\n"):
|
|
881
|
+
out[-1] = out[-1] + "\n"
|
|
882
|
+
out.append(block)
|
|
883
|
+
return "".join(out)
|
|
884
|
+
|
|
885
|
+
|
|
820
886
|
def ensure_agent_settings(
|
|
821
887
|
project_root: Path,
|
|
822
888
|
package_root: Path,
|
|
823
889
|
profile: str,
|
|
824
890
|
force: bool,
|
|
825
891
|
user_type: str = "",
|
|
892
|
+
packs: "list[str] | None" = None,
|
|
826
893
|
) -> None:
|
|
827
894
|
target = project_root / SETTINGS_FILE
|
|
828
895
|
profile_source = package_root / "config" / "profiles" / f"{profile}.ini"
|
|
@@ -847,6 +914,7 @@ def ensure_agent_settings(
|
|
|
847
914
|
# Inject runtime-only values (not part of the .ini profile presets).
|
|
848
915
|
profile_values["user_type"] = _validate_user_type(package_root, user_type)
|
|
849
916
|
template_body = _render_template(template, profile_values)
|
|
917
|
+
template_body = _inject_packs(template_body, packs or [])
|
|
850
918
|
|
|
851
919
|
legacy_target = project_root / LEGACY_SETTINGS_FILE
|
|
852
920
|
if legacy_target.is_file() and target.exists():
|
|
@@ -3524,6 +3592,24 @@ def install_global(
|
|
|
3524
3592
|
package_root = _resolve_package_root_for_global()
|
|
3525
3593
|
deploy_results = _deploy_global_content(tools, force, package_root, written)
|
|
3526
3594
|
|
|
3595
|
+
# NDJSON progress for the wizard --apply-payload real-apply bridge. One
|
|
3596
|
+
# `file` frame per deployed tool unit (coarse, per AI-council 2026-05-27);
|
|
3597
|
+
# the GUI maps these to its SSE progress frames. No-op under normal CLI
|
|
3598
|
+
# installs (PROGRESS_NDJSON off). Emitted independent of QUIET because the
|
|
3599
|
+
# real-apply path sets QUIET=True to silence the human stream.
|
|
3600
|
+
if PROGRESS_NDJSON:
|
|
3601
|
+
ordered = sorted(deploy_results)
|
|
3602
|
+
total = len(ordered)
|
|
3603
|
+
for idx, tool_id in enumerate(ordered, start=1):
|
|
3604
|
+
w, _s, status, _ = deploy_results[tool_id]
|
|
3605
|
+
_emit_progress({
|
|
3606
|
+
"type": "file",
|
|
3607
|
+
"file": tool_id,
|
|
3608
|
+
"status": status,
|
|
3609
|
+
"written": idx,
|
|
3610
|
+
"total": total,
|
|
3611
|
+
})
|
|
3612
|
+
|
|
3527
3613
|
if not QUIET:
|
|
3528
3614
|
print()
|
|
3529
3615
|
info("Deployed per-tool content:")
|
|
@@ -3697,6 +3783,17 @@ def parse_options(argv: list[str]) -> argparse.Namespace:
|
|
|
3697
3783
|
"the comma-separated values are unioned. Default: all."
|
|
3698
3784
|
),
|
|
3699
3785
|
)
|
|
3786
|
+
parser.add_argument(
|
|
3787
|
+
"--packs",
|
|
3788
|
+
default=None,
|
|
3789
|
+
help=(
|
|
3790
|
+
"comma-separated pack IDs to record as the active selection in "
|
|
3791
|
+
".agent-settings.yml (project scope). Packs are a "
|
|
3792
|
+
"frontmatter/condense-time concept — recording the selection "
|
|
3793
|
+
"lets downstream condense/runtime honor it; install.py does not "
|
|
3794
|
+
"materialize packs. Default: none (base package only)."
|
|
3795
|
+
),
|
|
3796
|
+
)
|
|
3700
3797
|
parser.add_argument(
|
|
3701
3798
|
"--no-smoke",
|
|
3702
3799
|
action="store_true",
|
|
@@ -3805,6 +3902,13 @@ def parse_options(argv: list[str]) -> argparse.Namespace:
|
|
|
3805
3902
|
)
|
|
3806
3903
|
opts = parser.parse_args(argv)
|
|
3807
3904
|
opts.tools = _merge_tools_aliases(opts.tools, opts.ai)
|
|
3905
|
+
# Normalize --packs (comma-separated string | None) to a list so the CLI
|
|
3906
|
+
# and the --apply-payload bridge agree on opts.packs being list[str].
|
|
3907
|
+
opts.packs = (
|
|
3908
|
+
[p.strip() for p in opts.packs.split(",") if p.strip()]
|
|
3909
|
+
if isinstance(opts.packs, str)
|
|
3910
|
+
else []
|
|
3911
|
+
)
|
|
3808
3912
|
if opts.scope == "global" and opts.custom_path:
|
|
3809
3913
|
fail("--custom-path is incompatible with --scope=global")
|
|
3810
3914
|
if opts.global_install and opts.custom_path:
|
|
@@ -4167,16 +4271,17 @@ def run_interactive_init(project_root: Path, force: bool) -> int:
|
|
|
4167
4271
|
# --- Wizard auto-launch (Phase 6 follow-up) ---
|
|
4168
4272
|
#
|
|
4169
4273
|
# Auto-launches the browser configuration wizard at the tail of a
|
|
4170
|
-
# successful install. The
|
|
4171
|
-
#
|
|
4274
|
+
# successful install. The unified CLI ships an `install` subcommand
|
|
4275
|
+
# (`dist/cli/agent-config.js`); this Python parent acts as a supervisor that:
|
|
4172
4276
|
#
|
|
4173
4277
|
# 1. evaluates gate conditions (TTY, CI, --no-ui, env override),
|
|
4174
4278
|
# 2. validates the dist exists,
|
|
4175
|
-
# 3. spawns `node <cli>
|
|
4279
|
+
# 3. spawns `node <cli> install --no-open --project-root <root>` via subprocess.Popen,
|
|
4176
4280
|
# 4. captures stderr on a background thread (for failure surfacing),
|
|
4177
4281
|
# 5. reads stdout line-by-line with a progressive timeout
|
|
4178
4282
|
# (10s → 20s → 40s → 80s) and matches the strict readiness regex
|
|
4179
|
-
# `^WIZARD_READY
|
|
4283
|
+
# `^WIZARD_READY (http://(?:127.0.0.1|localhost):\d+/\S*)\r?$`
|
|
4284
|
+
# (the CLI prints `WIZARD_READY <url>` where url carries `?token=…`),
|
|
4180
4285
|
# 6. on success: prints the URL banner and waits for the child to
|
|
4181
4286
|
# exit (Ctrl-C in the parent terminal propagates to the child),
|
|
4182
4287
|
# 7. on timeout: kills the child, prints captured stderr tail, falls
|
|
@@ -4186,7 +4291,7 @@ def run_interactive_init(project_root: Path, force: bool) -> int:
|
|
|
4186
4291
|
# Roadmap: agents/roadmaps/wizard-install-py-wiring.md Step 3.
|
|
4187
4292
|
|
|
4188
4293
|
_WIZARD_READY_RE = re.compile(
|
|
4189
|
-
r"^WIZARD_READY
|
|
4294
|
+
r"^WIZARD_READY (http://(?:127\.0\.0\.1|localhost):\d+/\S*)\r?$"
|
|
4190
4295
|
)
|
|
4191
4296
|
_WIZARD_TIMEOUTS = (10.0, 20.0, 40.0, 80.0) # cumulative budget 150s.
|
|
4192
4297
|
|
|
@@ -4195,8 +4300,8 @@ def _wizard_should_launch(opts: argparse.Namespace) -> tuple[bool, str]:
|
|
|
4195
4300
|
"""Evaluate gate conditions for the post-install wizard auto-launch.
|
|
4196
4301
|
|
|
4197
4302
|
Returns (decision, reason). When decision is False the reason
|
|
4198
|
-
string explains why (CI / no-tty / --no-ui / env override
|
|
4199
|
-
suitable for the pre-install banner Council Tier 2 § 8.
|
|
4303
|
+
string explains why (CI / no-tty / --no-ui / env override / explicit
|
|
4304
|
+
--tools) and is suitable for the pre-install banner Council Tier 2 § 8.
|
|
4200
4305
|
"""
|
|
4201
4306
|
if getattr(opts, "no_ui", False):
|
|
4202
4307
|
return (False, "--no-ui flag set")
|
|
@@ -4207,18 +4312,26 @@ def _wizard_should_launch(opts: argparse.Namespace) -> tuple[bool, str]:
|
|
|
4207
4312
|
return (False, "CI environment detected")
|
|
4208
4313
|
if not sys.stdout.isatty():
|
|
4209
4314
|
return (False, "stdout is not a TTY")
|
|
4315
|
+
# Explicit `--tools=<list>` means the caller already knows what to
|
|
4316
|
+
# install — run the non-interactive CLI install, don't open the GUI
|
|
4317
|
+
# (road-to-single-install-source-of-truth § Phase 4). The implicit/
|
|
4318
|
+
# explicit `all` default does NOT suppress the wizard.
|
|
4319
|
+
tools_raw = getattr(opts, "tools", None)
|
|
4320
|
+
if tools_raw and not _tools_was_all(tools_raw):
|
|
4321
|
+
return (False, "explicit --tools= selection (headless install)")
|
|
4210
4322
|
return (True, "")
|
|
4211
4323
|
|
|
4212
4324
|
|
|
4213
4325
|
def _wizard_cli_dist(project_root: Path) -> Path | None:
|
|
4214
|
-
"""Resolve the
|
|
4326
|
+
"""Resolve the unified CLI dist path. Returns None if not built.
|
|
4215
4327
|
|
|
4216
|
-
Walks up from this file (scripts/install.py is at <pkg>/scripts/)
|
|
4217
|
-
|
|
4218
|
-
|
|
4328
|
+
Walks up from this file (scripts/install.py is at <pkg>/scripts/) to
|
|
4329
|
+
<pkg>/dist/cli/agent-config.js — the published bin entry (package.json
|
|
4330
|
+
`bin`). The dead `packages/core/installer/dist/cli.js` layout was
|
|
4331
|
+
retired in road-to-single-install-source-of-truth § Phase 4.
|
|
4219
4332
|
"""
|
|
4220
4333
|
package_root = Path(__file__).resolve().parent.parent
|
|
4221
|
-
cli = package_root / "
|
|
4334
|
+
cli = package_root / "dist" / "cli" / "agent-config.js"
|
|
4222
4335
|
return cli if cli.exists() else None
|
|
4223
4336
|
|
|
4224
4337
|
|
|
@@ -4233,17 +4346,18 @@ def _wizard_spawn(project_root: Path) -> int:
|
|
|
4233
4346
|
cli = _wizard_cli_dist(project_root)
|
|
4234
4347
|
if cli is None:
|
|
4235
4348
|
print(
|
|
4236
|
-
"(Wizard not available —
|
|
4237
|
-
"Run 'npm run build'
|
|
4349
|
+
"(Wizard not available — CLI bundle not built. "
|
|
4350
|
+
"Run 'npm run build' at the package root to produce dist/cli/.)"
|
|
4238
4351
|
)
|
|
4239
4352
|
return 0
|
|
4240
4353
|
|
|
4241
|
-
|
|
4354
|
+
# Spawn the unified CLI's `install` subcommand (boots the UI server,
|
|
4355
|
+
# lands on Step 1 / AI tools). `--no-open` keeps the Python parent in
|
|
4356
|
+
# charge of the user-facing URL print (Tier 2 § 8 ordering) — the dead
|
|
4357
|
+
# `gui` subcommand + AGENT_CONFIG_GUI_NO_OPEN env were retired in
|
|
4358
|
+
# road-to-single-install-source-of-truth § Phase 4.
|
|
4359
|
+
cmd = ["node", str(cli), "install", "--no-open", "--project-root", str(project_root)]
|
|
4242
4360
|
env = os.environ.copy()
|
|
4243
|
-
# The Node child writes its own readiness banner; suppress the
|
|
4244
|
-
# browser-open inside the child so the Python parent stays in
|
|
4245
|
-
# charge of the user-facing URL print (Tier 2 § 8 ordering).
|
|
4246
|
-
env.setdefault("AGENT_CONFIG_GUI_NO_OPEN", "1")
|
|
4247
4361
|
|
|
4248
4362
|
try:
|
|
4249
4363
|
child = subprocess.Popen( # noqa: S603 - cmd is locally-built, not user input
|
|
@@ -4255,7 +4369,7 @@ def _wizard_spawn(project_root: Path) -> int:
|
|
|
4255
4369
|
bufsize=1, # line-buffered
|
|
4256
4370
|
)
|
|
4257
4371
|
except OSError as exc:
|
|
4258
|
-
print(f"(Wizard failed to start: {exc}; run 'node {cli}
|
|
4372
|
+
print(f"(Wizard failed to start: {exc}; run 'node {cli} install --no-open' manually.)")
|
|
4259
4373
|
return 0
|
|
4260
4374
|
|
|
4261
4375
|
# Drain stderr on a background thread so a chatty child can't
|
|
@@ -4327,7 +4441,7 @@ def _wizard_await_ready(
|
|
|
4327
4441
|
tail = "\n ".join(stderr_tail[-20:]) if stderr_tail else "(no stderr captured)"
|
|
4328
4442
|
print(
|
|
4329
4443
|
f"(Wizard server boot timed out after {int(sum(_WIZARD_TIMEOUTS))}s; "
|
|
4330
|
-
f"run 'node {cli}
|
|
4444
|
+
f"run 'node {cli} install --no-open' manually.)\n"
|
|
4331
4445
|
f" Last stderr:\n {tail}"
|
|
4332
4446
|
)
|
|
4333
4447
|
return 0
|
|
@@ -4445,10 +4559,9 @@ def main(argv: list[str]) -> int:
|
|
|
4445
4559
|
f"--apply-payload schema_version must be 'wizard-v2' or "
|
|
4446
4560
|
f"'installer-v1', got {schema_version!r}"
|
|
4447
4561
|
)
|
|
4448
|
-
# Translate payload → opts so the
|
|
4449
|
-
# downstream sees the
|
|
4450
|
-
#
|
|
4451
|
-
# follow-up minor (kill-switch via package version per D7).
|
|
4562
|
+
# Translate payload → opts so the SAME canonical install path
|
|
4563
|
+
# downstream sees the shape it would from CLI flags — no second
|
|
4564
|
+
# code path (road-to-single-install-source-of-truth § Phase 1).
|
|
4452
4565
|
if schema_version == "wizard-v2":
|
|
4453
4566
|
tools = payload.get("tools") or []
|
|
4454
4567
|
if isinstance(tools, list) and tools:
|
|
@@ -4457,6 +4570,36 @@ def main(argv: list[str]) -> int:
|
|
|
4457
4570
|
opts.scope = "project"
|
|
4458
4571
|
else:
|
|
4459
4572
|
opts.scope = "global"
|
|
4573
|
+
# settings{} → --profile / --user-type. `settings` is the
|
|
4574
|
+
# merged .agent-settings.yml body; pull the two install-time
|
|
4575
|
+
# knobs the canonical installer consumes. Everything else in
|
|
4576
|
+
# the settings body is written by the wizard `/finish` 2PC
|
|
4577
|
+
# commit, not by install.py.
|
|
4578
|
+
settings = payload.get("settings") or {}
|
|
4579
|
+
if isinstance(settings, dict):
|
|
4580
|
+
cost_profile = settings.get("cost_profile")
|
|
4581
|
+
if isinstance(cost_profile, str) and cost_profile:
|
|
4582
|
+
opts.profile = cost_profile
|
|
4583
|
+
personal = settings.get("personal")
|
|
4584
|
+
if isinstance(personal, dict):
|
|
4585
|
+
user_type = personal.get("user_type")
|
|
4586
|
+
if isinstance(user_type, str) and user_type:
|
|
4587
|
+
opts.user_type = user_type
|
|
4588
|
+
# packs[] → declarative selection persisted into
|
|
4589
|
+
# .agent-settings.yml on the project path (ensure_agent_settings).
|
|
4590
|
+
# Packs are a frontmatter/condense-time concept; there is no
|
|
4591
|
+
# install-time materialization — the selection is recorded so
|
|
4592
|
+
# downstream condense/runtime honors it (AI-council 2026-05-27).
|
|
4593
|
+
packs = payload.get("packs") or []
|
|
4594
|
+
if isinstance(packs, list):
|
|
4595
|
+
opts.packs = [p for p in packs if isinstance(p, str)]
|
|
4596
|
+
# Per-tool user-hook opts → ensure_*_hook flags: deliberately
|
|
4597
|
+
# NOT wired. The wizard-v2 payload carries no hook fields, and
|
|
4598
|
+
# auto-enabling user-scope hooks (which write to the global
|
|
4599
|
+
# user config) on tool selection would silently widen install
|
|
4600
|
+
# scope — a non-destructive-by-default violation. Maps only
|
|
4601
|
+
# once the schema grows an explicit hooks field (AI-council
|
|
4602
|
+
# 2026-05-27, Gemini + Codex converged).
|
|
4460
4603
|
elif schema_version == "installer-v1":
|
|
4461
4604
|
ai_tools = payload.get("ai_tools") or []
|
|
4462
4605
|
if isinstance(ai_tools, list) and ai_tools:
|
|
@@ -4466,13 +4609,13 @@ def main(argv: list[str]) -> int:
|
|
|
4466
4609
|
opts.dry_run = True
|
|
4467
4610
|
if opts.dry_run:
|
|
4468
4611
|
return _apply_payload_preview(payload, opts)
|
|
4469
|
-
# Real
|
|
4470
|
-
#
|
|
4471
|
-
#
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4612
|
+
# Real apply: stream machine-readable NDJSON progress on stdout and
|
|
4613
|
+
# silence human output so the GUI gets a clean stream. Then fall
|
|
4614
|
+
# through to the canonical install path below — no separate apply
|
|
4615
|
+
# implementation.
|
|
4616
|
+
global PROGRESS_NDJSON
|
|
4617
|
+
PROGRESS_NDJSON = True
|
|
4618
|
+
QUIET = True
|
|
4476
4619
|
|
|
4477
4620
|
# --offline: propagate via env so child subprocesses (versions /
|
|
4478
4621
|
# update / check_update_banner) honor the air-gap guarantee
|
|
@@ -4561,7 +4704,9 @@ def main(argv: list[str]) -> int:
|
|
|
4561
4704
|
return rc
|
|
4562
4705
|
# Pass detect_root so the manifest refresh runs when --global is
|
|
4563
4706
|
# invoked from within a project tree (ADR-008 Phase 3.2).
|
|
4564
|
-
|
|
4707
|
+
rc = install_global(parsed_tools, opts.force, project_root=detect_root)
|
|
4708
|
+
_emit_progress_terminal(rc)
|
|
4709
|
+
return rc
|
|
4565
4710
|
|
|
4566
4711
|
project_root = custom_path or Path(opts.project or os.environ.get("PROJECT_ROOT") or os.getcwd()).resolve()
|
|
4567
4712
|
is_first_run = not (project_root / SETTINGS_FILE).exists()
|
|
@@ -4571,9 +4716,11 @@ def main(argv: list[str]) -> int:
|
|
|
4571
4716
|
# never ships ahead of the bridge files it parameterizes.
|
|
4572
4717
|
if rc == 0 and getattr(opts, "interactive", False):
|
|
4573
4718
|
run_interactive_init(project_root, opts.force)
|
|
4719
|
+
_emit_progress_terminal(rc)
|
|
4574
4720
|
return rc
|
|
4575
4721
|
except ConflictAbort as exc:
|
|
4576
4722
|
warn(exc.message)
|
|
4723
|
+
_emit_progress({"type": "error", "code": "E_CONFLICT_UNRESOLVED", "message": exc.message})
|
|
4577
4724
|
return 1
|
|
4578
4725
|
finally:
|
|
4579
4726
|
_set_conflict_policy(None)
|
|
@@ -4670,7 +4817,8 @@ def _main_project_install(
|
|
|
4670
4817
|
print()
|
|
4671
4818
|
|
|
4672
4819
|
ensure_agent_settings(
|
|
4673
|
-
project_root, package_root, opts.profile, opts.force, opts.user_type
|
|
4820
|
+
project_root, package_root, opts.profile, opts.force, opts.user_type,
|
|
4821
|
+
packs=getattr(opts, "packs", None),
|
|
4674
4822
|
)
|
|
4675
4823
|
|
|
4676
4824
|
# Install-mode marker (Step 8 A5) — full path flips any prior
|
|
@@ -4745,6 +4893,21 @@ def _main_project_install(
|
|
|
4745
4893
|
ensure_gemini_user_hooks(package_root, opts.force),
|
|
4746
4894
|
)
|
|
4747
4895
|
|
|
4896
|
+
# NDJSON progress for the wizard --apply-payload real-apply bridge on the
|
|
4897
|
+
# project scope (scope_to_project_only=true). One `file` frame per enabled
|
|
4898
|
+
# tool unit, coarse per AI-council 2026-05-27. No-op under normal CLI.
|
|
4899
|
+
if PROGRESS_NDJSON and not opts.skip_bridges:
|
|
4900
|
+
ordered = sorted(tools)
|
|
4901
|
+
total = len(ordered)
|
|
4902
|
+
for idx, tool_id in enumerate(ordered, start=1):
|
|
4903
|
+
_emit_progress({
|
|
4904
|
+
"type": "file",
|
|
4905
|
+
"file": tool_id,
|
|
4906
|
+
"status": "deployed",
|
|
4907
|
+
"written": idx,
|
|
4908
|
+
"total": total,
|
|
4909
|
+
})
|
|
4910
|
+
|
|
4748
4911
|
if not opts.skip_bridges and not opts.no_smoke:
|
|
4749
4912
|
if not QUIET:
|
|
4750
4913
|
print()
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"_doc": "Each entry: { file: relative path from repo root, lines: [int,...] | \"*\" for whole file, reason: short justification }. Use sparingly — first ask whether the file should be neutralized instead.",
|
|
4
4
|
"entries": [
|
|
5
|
+
{
|
|
6
|
+
"file": ".agent-src.uncondensed/skills/module-detect-on-the-fly/SKILL.md",
|
|
7
|
+
"lines": [
|
|
8
|
+
42,
|
|
9
|
+
104
|
|
10
|
+
],
|
|
11
|
+
"reason": "Cross-stack noise-segment enumeration (vendor + node_modules + dist/Modules) documenting the module-detection path filter"
|
|
12
|
+
},
|
|
5
13
|
{
|
|
6
14
|
"file": ".agent-src.uncondensed/skills/using-git-worktrees/SKILL.md",
|
|
7
15
|
"lines": [
|
package/dist/install/apply.js
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apply orchestrator \u2014 Phase A4 (atomic writes + transaction log).
|
|
3
|
-
*
|
|
4
|
-
* Consumes an {@link InstallPlan} produced by `plan.ts:buildInstallPlan`
|
|
5
|
-
* and walks every {@link FileEntry}, copying source bytes into the
|
|
6
|
-
* declared target with {@link atomicWriteFile}. Each successful write
|
|
7
|
-
* appends one {@link TxLogEntry} (kind=`write`) to the transaction log
|
|
8
|
-
* so a recovery pass on next boot can reverse-apply on crash.
|
|
9
|
-
*
|
|
10
|
-
* Pure orchestration:
|
|
11
|
-
* - No path recomputation (the plan is the source of truth).
|
|
12
|
-
* - No re-walking of the source tree.
|
|
13
|
-
* - SHA-256 idempotency: skip an entry whose target already exists
|
|
14
|
-
* with matching bytes, unless `force=true` flips the policy.
|
|
15
|
-
*
|
|
16
|
-
* Progress is surfaced via the optional `onProgress` callback so the
|
|
17
|
-
* Fastify SSE adapter (Phase B1) can stream per-file events to the
|
|
18
|
-
* Preact wizard.
|
|
19
|
-
*/
|
|
20
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
21
|
-
import { atomicWriteFile } from './atomic.js';
|
|
22
|
-
import { isJsonTarget, mergeJsonContent, parseJsonLenient, resolveFileConflict, } from './conflict.js';
|
|
23
|
-
import { sha256File } from './plan.js';
|
|
24
|
-
import { appendTxLog } from './txlog.js';
|
|
25
|
-
/**
|
|
26
|
-
* Execute an {@link InstallPlan}.
|
|
27
|
-
*
|
|
28
|
-
* One pass through every entry. Writes are atomic (write-to-temp +
|
|
29
|
-
* rename); the txlog records each success so recovery can reverse-apply.
|
|
30
|
-
*
|
|
31
|
-
* Returns {@link ApplyResult} aggregating per-file outcomes. Never
|
|
32
|
-
* throws on individual file errors \u2014 errors land in
|
|
33
|
-
* {@link ApplyResult.errors} so the wizard can render a partial-success
|
|
34
|
-
* screen rather than crashing the install.
|
|
35
|
-
*/
|
|
36
|
-
export function applyPlan(inputs) {
|
|
37
|
-
const { plan, sourceByTarget, logPath, onProgress, resolutions } = inputs;
|
|
38
|
-
const all = flattenEntries(plan);
|
|
39
|
-
const acc = newAccumulator();
|
|
40
|
-
let index = 0;
|
|
41
|
-
for (const entry of all) {
|
|
42
|
-
index += 1;
|
|
43
|
-
processEntry(entry, plan, sourceByTarget, logPath, acc, index, all.length, onProgress, resolutions);
|
|
44
|
-
}
|
|
45
|
-
return { target: plan.target, ...acc };
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Async streaming variant — yields the event loop between entries so an
|
|
49
|
-
* {@link AbortSignal} fired by a disconnecting SSE client can stop the
|
|
50
|
-
* loop. Each entry still executes synchronously (atomic write + txlog
|
|
51
|
-
* append) so a mid-write abort is impossible; aborts land between
|
|
52
|
-
* entries and append a single `abort` marker before resolving.
|
|
53
|
-
*
|
|
54
|
-
* Council Finding #24: SSE handlers must wire `req.on("close")` to an
|
|
55
|
-
* `AbortController` and pass the signal here so half-applied installs
|
|
56
|
-
* surface a clean partial result instead of a zombie loop.
|
|
57
|
-
*/
|
|
58
|
-
export async function applyPlanStreaming(inputs) {
|
|
59
|
-
const { plan, sourceByTarget, logPath, onProgress, signal, resolutions } = inputs;
|
|
60
|
-
const all = flattenEntries(plan);
|
|
61
|
-
const acc = newAccumulator();
|
|
62
|
-
let index = 0;
|
|
63
|
-
for (const entry of all) {
|
|
64
|
-
if (signal?.aborted === true) {
|
|
65
|
-
appendTxLog(logPath, {
|
|
66
|
-
ts: new Date().toISOString(),
|
|
67
|
-
kind: 'abort',
|
|
68
|
-
path: entry.path,
|
|
69
|
-
sha256: null,
|
|
70
|
-
note: 'client disconnect',
|
|
71
|
-
});
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
index += 1;
|
|
75
|
-
processEntry(entry, plan, sourceByTarget, logPath, acc, index, all.length, onProgress, resolutions);
|
|
76
|
-
// Yield to the event loop so the abort signal can fire and any
|
|
77
|
-
// SSE write buffers can drain before the next entry.
|
|
78
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
79
|
-
}
|
|
80
|
-
return { target: plan.target, ...acc };
|
|
81
|
-
}
|
|
82
|
-
function newAccumulator() {
|
|
83
|
-
return { written: [], skipped: [], conflicts: [], errors: [] };
|
|
84
|
-
}
|
|
85
|
-
function flattenEntries(plan) {
|
|
86
|
-
const all = [];
|
|
87
|
-
for (const entries of Object.values(plan.filesByTool)) {
|
|
88
|
-
for (const e of entries)
|
|
89
|
-
all.push(e);
|
|
90
|
-
}
|
|
91
|
-
return all;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Process one {@link FileEntry} — resolve conflict, write or skip, append
|
|
95
|
-
* txlog, push to accumulator, emit progress.
|
|
96
|
-
*
|
|
97
|
-
* Shared by sync {@link applyPlan} and async {@link applyPlanStreaming}
|
|
98
|
-
* so the conflict matrix lives in one place.
|
|
99
|
-
*/
|
|
100
|
-
function processEntry(entry, plan, sourceByTarget, logPath, acc, index, total, onProgress, resolutions) {
|
|
101
|
-
try {
|
|
102
|
-
if (entry.kind === 'bridge') {
|
|
103
|
-
// Bridges are pointers we don't own bytes-for-bytes — skip
|
|
104
|
-
// them at the apply layer; bridge generators (A6) produce
|
|
105
|
-
// them via their own writer pass.
|
|
106
|
-
acc.skipped.push(entry);
|
|
107
|
-
onProgress?.({ file: entry, written: index, total, status: 'skipped' });
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const source = sourceByTarget.get(entry.path);
|
|
111
|
-
if (source === undefined) {
|
|
112
|
-
acc.errors.push({
|
|
113
|
-
path: entry.path,
|
|
114
|
-
code: 'E_PLAN_MISSING_SOURCE',
|
|
115
|
-
message: 'no source mapping for target path',
|
|
116
|
-
});
|
|
117
|
-
onProgress?.({
|
|
118
|
-
file: entry,
|
|
119
|
-
written: index,
|
|
120
|
-
total,
|
|
121
|
-
status: 'error',
|
|
122
|
-
error: { code: 'E_PLAN_MISSING_SOURCE', message: 'no source mapping' },
|
|
123
|
-
});
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const idempotent = isIdempotent(entry, plan);
|
|
127
|
-
const exists = existsSync(entry.path);
|
|
128
|
-
const outcome = resolveFileConflict({
|
|
129
|
-
targetPath: entry.path,
|
|
130
|
-
idempotent,
|
|
131
|
-
exists,
|
|
132
|
-
policy: plan.policy,
|
|
133
|
-
});
|
|
134
|
-
if (outcome === 'skip') {
|
|
135
|
-
acc.skipped.push(entry);
|
|
136
|
-
onProgress?.({ file: entry, written: index, total, status: 'skipped' });
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (outcome === 'surface') {
|
|
140
|
-
// Phase B3 — the wizard may have pre-resolved this conflict
|
|
141
|
-
// before the apply call. The `resolutions` map is keyed by
|
|
142
|
-
// absolute target path. Missing keys → unresolved conflict
|
|
143
|
-
// (acc.conflicts) so the wizard surfaces them again. An
|
|
144
|
-
// explicit `skip` resolution → acc.skipped: the user decided
|
|
145
|
-
// to leave the file alone, that is a settled outcome.
|
|
146
|
-
const resolution = resolutions?.get(entry.path);
|
|
147
|
-
if (resolution === undefined) {
|
|
148
|
-
acc.conflicts.push(entry);
|
|
149
|
-
onProgress?.({ file: entry, written: index, total, status: 'conflict' });
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (resolution === 'skip') {
|
|
153
|
-
acc.skipped.push(entry);
|
|
154
|
-
onProgress?.({ file: entry, written: index, total, status: 'skipped' });
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const data = readFileSync(source);
|
|
158
|
-
const payload = resolution === 'merge' && isJsonTarget(entry) && exists
|
|
159
|
-
? mergeJsonPayload(entry.path, data)
|
|
160
|
-
: data;
|
|
161
|
-
atomicWriteFile(entry.path, payload);
|
|
162
|
-
appendTxLog(logPath, {
|
|
163
|
-
ts: new Date().toISOString(),
|
|
164
|
-
kind: 'write',
|
|
165
|
-
path: entry.path,
|
|
166
|
-
sha256: entry.sha256,
|
|
167
|
-
});
|
|
168
|
-
acc.written.push(entry);
|
|
169
|
-
onProgress?.({ file: entry, written: index, total, status: 'written' });
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const data = readFileSync(source);
|
|
173
|
-
const payload = isJsonTarget(entry) && exists ? mergeJsonPayload(entry.path, data) : data;
|
|
174
|
-
atomicWriteFile(entry.path, payload);
|
|
175
|
-
appendTxLog(logPath, {
|
|
176
|
-
ts: new Date().toISOString(),
|
|
177
|
-
kind: 'write',
|
|
178
|
-
path: entry.path,
|
|
179
|
-
sha256: entry.sha256,
|
|
180
|
-
});
|
|
181
|
-
acc.written.push(entry);
|
|
182
|
-
onProgress?.({ file: entry, written: index, total, status: 'written' });
|
|
183
|
-
}
|
|
184
|
-
catch (err) {
|
|
185
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
186
|
-
const code = errorCode(err);
|
|
187
|
-
acc.errors.push({ path: entry.path, code, message });
|
|
188
|
-
onProgress?.({
|
|
189
|
-
file: entry,
|
|
190
|
-
written: index,
|
|
191
|
-
total,
|
|
192
|
-
status: 'error',
|
|
193
|
-
error: { code, message },
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* True when the target already matches the planned bytes (no write needed).
|
|
199
|
-
*
|
|
200
|
-
* `policy.force` flips this off \u2014 `--force-overwrite` writes regardless.
|
|
201
|
-
*/
|
|
202
|
-
function isIdempotent(entry, plan) {
|
|
203
|
-
if (plan.policy.force)
|
|
204
|
-
return false;
|
|
205
|
-
if (entry.sha256 === null)
|
|
206
|
-
return false;
|
|
207
|
-
if (!existsSync(entry.path))
|
|
208
|
-
return false;
|
|
209
|
-
return sha256File(entry.path) === entry.sha256;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Read the existing JSON target, deep-merge with the planned payload,
|
|
213
|
-
* and return the canonical 4-space-indented bytes.
|
|
214
|
-
*
|
|
215
|
-
* Falls back to the raw planned payload when the existing file is
|
|
216
|
-
* corrupt or non-object — matches the Python `read_json_file` lenient
|
|
217
|
-
* contract so a truncated upstream config does not abort the install.
|
|
218
|
-
*/
|
|
219
|
-
function mergeJsonPayload(targetPath, planned) {
|
|
220
|
-
const existingText = readFileSync(targetPath, 'utf8');
|
|
221
|
-
const existing = parseJsonLenient(existingText);
|
|
222
|
-
const overlay = parseJsonLenient(planned.toString('utf8'));
|
|
223
|
-
const merged = mergeJsonContent(existing, overlay);
|
|
224
|
-
return Buffer.from(merged, 'utf8');
|
|
225
|
-
}
|
|
226
|
-
function errorCode(err) {
|
|
227
|
-
if (err !== null && typeof err === 'object' && 'code' in err) {
|
|
228
|
-
const c = err.code;
|
|
229
|
-
if (typeof c === 'string') {
|
|
230
|
-
if (c === 'ENOSPC')
|
|
231
|
-
return 'E_DISK_FULL';
|
|
232
|
-
if (c === 'EACCES' || c === 'EPERM')
|
|
233
|
-
return 'E_PERM';
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return 'E_WRITE';
|
|
237
|
-
}
|
|
238
|
-
//# sourceMappingURL=apply.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/install/apply.ts"],"names":[],"mappings":"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"}
|