@event4u/agent-config 2.15.0 → 2.16.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/.agent-src/templates/scripts/work_engine/_lib/agent_settings.py +299 -20
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +145 -225
- package/README.md +11 -0
- package/docs/archive/CHANGELOG-pre-2.15.0.md +244 -0
- package/docs/installation.md +221 -2
- package/package.json +1 -1
- package/scripts/_cli/cmd_doctor.py +238 -8
- package/scripts/_cli/cmd_migrate.py +6 -1
- package/scripts/_cli/cmd_prune.py +8 -3
- package/scripts/_cli/cmd_sync.py +7 -3
- package/scripts/_cli/cmd_uninstall.py +4 -3
- package/scripts/_cli/cmd_update.py +5 -1
- package/scripts/_cli/cmd_validate.py +6 -3
- package/scripts/_cli/cmd_versions.py +15 -2
- package/scripts/_lib/agent_settings.py +299 -20
- package/scripts/agent-config +64 -0
- package/scripts/install +39 -2
- package/scripts/install.py +171 -0
- package/scripts/install.sh +20 -0
- package/templates/agent-config-wrapper.sh +7 -0
- package/templates/minimal/.agent-settings.yml +23 -0
- package/templates/minimal/agents-gitkeep +2 -0
package/scripts/install.py
CHANGED
|
@@ -3223,6 +3223,19 @@ def parse_options(argv: list[str]) -> argparse.Namespace:
|
|
|
3223
3223
|
"explicit guarantee for air-gapped / CI runs."
|
|
3224
3224
|
),
|
|
3225
3225
|
)
|
|
3226
|
+
parser.add_argument(
|
|
3227
|
+
"--minimal",
|
|
3228
|
+
"--settings-only",
|
|
3229
|
+
dest="minimal",
|
|
3230
|
+
action="store_true",
|
|
3231
|
+
help=(
|
|
3232
|
+
"bootstrap only the project-local override layer: writes "
|
|
3233
|
+
"agents/.gitkeep and a .agent-settings.yml stub. No tool "
|
|
3234
|
+
"payload, no AGENTS.md, no symlinks. Refuses to install "
|
|
3235
|
+
"inside an existing agent-config project (nested-install "
|
|
3236
|
+
"guard). See docs/installation.md → Minimal init."
|
|
3237
|
+
),
|
|
3238
|
+
)
|
|
3226
3239
|
opts = parser.parse_args(argv)
|
|
3227
3240
|
opts.tools = _merge_tools_aliases(opts.tools, opts.ai)
|
|
3228
3241
|
if opts.scope == "global" and opts.custom_path:
|
|
@@ -3282,6 +3295,148 @@ def _is_tool_enabled(tools: set[str], tool_id: str) -> bool:
|
|
|
3282
3295
|
return tool_id in tools
|
|
3283
3296
|
|
|
3284
3297
|
|
|
3298
|
+
# --- Minimal init (Step 7 Phase 2) ---
|
|
3299
|
+
|
|
3300
|
+
|
|
3301
|
+
def _minimal_templates_root() -> Path:
|
|
3302
|
+
"""Resolve the bundled ``templates/minimal/`` directory.
|
|
3303
|
+
|
|
3304
|
+
Walks up from this file looking for ``templates/minimal/``; this
|
|
3305
|
+
works both in development mode (running the source tree) and from
|
|
3306
|
+
an ``npm install -g`` install (the script lives under the package
|
|
3307
|
+
root regardless).
|
|
3308
|
+
"""
|
|
3309
|
+
for ancestor in (Path(__file__).resolve(), *Path(__file__).resolve().parents):
|
|
3310
|
+
candidate = ancestor / "templates" / "minimal"
|
|
3311
|
+
if candidate.is_dir():
|
|
3312
|
+
return candidate
|
|
3313
|
+
fail("Could not locate templates/minimal/ — package install is corrupt.")
|
|
3314
|
+
return Path() # unreachable
|
|
3315
|
+
|
|
3316
|
+
|
|
3317
|
+
#: Relative path of the install-mode marker file written by both the
|
|
3318
|
+
#: minimal short-circuit and the full install path (Step 8 A5). Read by
|
|
3319
|
+
#: ``doctor --context`` (and any future tooling) instead of inferring
|
|
3320
|
+
#: install state from filesystem heuristics like ``AGENTS.md`` presence.
|
|
3321
|
+
INSTALL_MODE_MARKER_REL = "agents/.agent-state/install-mode.txt"
|
|
3322
|
+
|
|
3323
|
+
|
|
3324
|
+
def _write_install_mode_marker(project_root: Path, mode: str) -> None:
|
|
3325
|
+
"""Write ``agents/.agent-state/install-mode.txt`` = ``mode\\n``.
|
|
3326
|
+
|
|
3327
|
+
Idempotent: overwrites unconditionally so re-installs flip the
|
|
3328
|
+
state correctly (e.g. minimal → full upgrade). Failure to write
|
|
3329
|
+
is non-fatal — install proceeds and ``doctor --context`` falls
|
|
3330
|
+
back to the filesystem heuristic. ``mode`` must be ``minimal``
|
|
3331
|
+
or ``full``.
|
|
3332
|
+
"""
|
|
3333
|
+
if mode not in ("minimal", "full"):
|
|
3334
|
+
return
|
|
3335
|
+
marker = project_root / INSTALL_MODE_MARKER_REL
|
|
3336
|
+
try:
|
|
3337
|
+
marker.parent.mkdir(parents=True, exist_ok=True)
|
|
3338
|
+
marker.write_text(f"{mode}\n", encoding="utf-8")
|
|
3339
|
+
except OSError:
|
|
3340
|
+
# Marker is advisory; install must not abort because the
|
|
3341
|
+
# state dir is unwritable (e.g. read-only mount in CI).
|
|
3342
|
+
pass
|
|
3343
|
+
|
|
3344
|
+
|
|
3345
|
+
def install_minimal(target_root: Path, force: bool) -> int:
|
|
3346
|
+
"""Bootstrap the project-local override layer only (D2-compliant).
|
|
3347
|
+
|
|
3348
|
+
Writes:
|
|
3349
|
+
|
|
3350
|
+
* ``agents/.gitkeep`` so the folder is committable.
|
|
3351
|
+
* ``.agent-settings.yml`` stub (cost_profile=balanced, version pin
|
|
3352
|
+
commented out per D4).
|
|
3353
|
+
|
|
3354
|
+
Refuses (exit 1) when ``target_root`` is **inside** an existing
|
|
3355
|
+
agent-config project (Phase-1 anchor walk above the target). The
|
|
3356
|
+
in-target case is allowed and treated as idempotent — re-running
|
|
3357
|
+
``--minimal`` in a folder that already has ``.agent-settings.yml``
|
|
3358
|
+
does nothing unless ``--force`` is passed.
|
|
3359
|
+
|
|
3360
|
+
Does **not** touch ``.gitignore`` (D2 — user owns the ignore file).
|
|
3361
|
+
The ``./agent-config`` wrapper is installed by ``scripts/install.sh``
|
|
3362
|
+
in its own minimal short-circuit.
|
|
3363
|
+
"""
|
|
3364
|
+
try:
|
|
3365
|
+
from scripts._lib.agent_settings import find_project_root_with_anchor # noqa: PLC0415
|
|
3366
|
+
except ImportError: # pragma: no cover — alt sys.path layout
|
|
3367
|
+
from _lib.agent_settings import find_project_root_with_anchor # type: ignore[no-redef] # noqa: PLC0415
|
|
3368
|
+
|
|
3369
|
+
target_root = target_root.resolve()
|
|
3370
|
+
target_root.mkdir(parents=True, exist_ok=True)
|
|
3371
|
+
|
|
3372
|
+
# Nested-install guard: walk up from the *parent* of target_root.
|
|
3373
|
+
# An anchor at target_root itself is allowed (re-running --minimal
|
|
3374
|
+
# in the same project is idempotent); only a root *above* target
|
|
3375
|
+
# blocks the install.
|
|
3376
|
+
parent = target_root.parent
|
|
3377
|
+
if parent != target_root: # not filesystem root
|
|
3378
|
+
existing = find_project_root_with_anchor(parent)
|
|
3379
|
+
if existing is not None and existing[0] != target_root:
|
|
3380
|
+
root, anchor = existing
|
|
3381
|
+
fail(
|
|
3382
|
+
"Refusing to nest an agent-config layer inside an existing "
|
|
3383
|
+
f"project (anchor: {anchor}). Existing root: {root}. "
|
|
3384
|
+
"Remove the parent layer first or run `--minimal` outside it."
|
|
3385
|
+
)
|
|
3386
|
+
return 1 # unreachable; fail() exits
|
|
3387
|
+
|
|
3388
|
+
templates = _minimal_templates_root()
|
|
3389
|
+
settings_src = templates / SETTINGS_FILE
|
|
3390
|
+
gitkeep_src = templates / "agents-gitkeep"
|
|
3391
|
+
|
|
3392
|
+
if not settings_src.is_file() or not gitkeep_src.is_file():
|
|
3393
|
+
fail(f"Bundled minimal templates missing under {templates}")
|
|
3394
|
+
|
|
3395
|
+
info(f"Minimal init → {target_root}")
|
|
3396
|
+
|
|
3397
|
+
# 1. agents/.gitkeep
|
|
3398
|
+
agents_dir = target_root / "agents"
|
|
3399
|
+
agents_dir.mkdir(exist_ok=True)
|
|
3400
|
+
gitkeep_dst = agents_dir / ".gitkeep"
|
|
3401
|
+
if gitkeep_dst.exists() and not force:
|
|
3402
|
+
skip(f"agents/.gitkeep already exists (use --force to overwrite)")
|
|
3403
|
+
else:
|
|
3404
|
+
gitkeep_dst.write_text(gitkeep_src.read_text(encoding="utf-8"), encoding="utf-8")
|
|
3405
|
+
success("Wrote agents/.gitkeep")
|
|
3406
|
+
|
|
3407
|
+
# 2. .agent-settings.yml stub
|
|
3408
|
+
settings_dst = target_root / SETTINGS_FILE
|
|
3409
|
+
if settings_dst.exists() and not force:
|
|
3410
|
+
skip(f"{SETTINGS_FILE} already exists (use --force to overwrite)")
|
|
3411
|
+
else:
|
|
3412
|
+
settings_dst.write_text(settings_src.read_text(encoding="utf-8"), encoding="utf-8")
|
|
3413
|
+
success(f"Wrote {SETTINGS_FILE}")
|
|
3414
|
+
|
|
3415
|
+
# 3. install-mode marker (Step 8 A5) — authoritative state for
|
|
3416
|
+
# doctor --context and future install-aware tooling. Written even
|
|
3417
|
+
# on idempotent re-runs so the marker is repaired if removed.
|
|
3418
|
+
_write_install_mode_marker(target_root, "minimal")
|
|
3419
|
+
|
|
3420
|
+
# Stderr upgrade hint (Step 8 A5) — minimal installs are intentionally
|
|
3421
|
+
# stripped; surface the upgrade path on stderr so it appears in
|
|
3422
|
+
# human terminals without polluting stdout-parsed output. Suppressed
|
|
3423
|
+
# under --quiet to honor scripted-install contracts.
|
|
3424
|
+
if not QUIET:
|
|
3425
|
+
print(
|
|
3426
|
+
"ℹ️ Minimal install — run `agent-config install --force` "
|
|
3427
|
+
"to add AGENTS.md, bridges, and tool integrations.",
|
|
3428
|
+
file=sys.stderr,
|
|
3429
|
+
)
|
|
3430
|
+
|
|
3431
|
+
if not QUIET:
|
|
3432
|
+
print()
|
|
3433
|
+
info("Next steps:")
|
|
3434
|
+
info(" • Ensure `agent-config` is on $PATH: npm install -g @event4u/agent-config")
|
|
3435
|
+
info(" • Add `.agent-settings.yml` and `agents/` to git (or to .gitignore — your call).")
|
|
3436
|
+
info(" • Run `agent-config doctor` to verify the layer is picked up.")
|
|
3437
|
+
return 0
|
|
3438
|
+
|
|
3439
|
+
|
|
3285
3440
|
# --- Main ---
|
|
3286
3441
|
|
|
3287
3442
|
def main(argv: list[str]) -> int:
|
|
@@ -3302,6 +3457,17 @@ def main(argv: list[str]) -> int:
|
|
|
3302
3457
|
if opts.profile not in SUPPORTED_PROFILES:
|
|
3303
3458
|
fail(f"Unsupported profile: {opts.profile}. Supported: {', '.join(SUPPORTED_PROFILES)}")
|
|
3304
3459
|
|
|
3460
|
+
# Minimal-init short-circuit (Step 7 Phase 2): bypass scope
|
|
3461
|
+
# detection, conflict policy, and the full bridge install. Writes
|
|
3462
|
+
# only the project-local override layer (agents/.gitkeep +
|
|
3463
|
+
# .agent-settings.yml stub). The bash wrapper handles the
|
|
3464
|
+
# `./agent-config` script; everything else is intentionally absent.
|
|
3465
|
+
if opts.minimal:
|
|
3466
|
+
target_root = Path(
|
|
3467
|
+
opts.custom_path or opts.project or os.environ.get("PROJECT_ROOT") or os.getcwd()
|
|
3468
|
+
).resolve()
|
|
3469
|
+
return install_minimal(target_root, opts.force)
|
|
3470
|
+
|
|
3305
3471
|
# Multi-signal scope detection (Phase 1.3) + scope resolution
|
|
3306
3472
|
# (Phase 1.4). Order of precedence (highest first):
|
|
3307
3473
|
# 1. --scope=<x> — explicit user override (CI-friendly; auto = honor detection)
|
|
@@ -3376,6 +3542,11 @@ def _main_project_install(
|
|
|
3376
3542
|
|
|
3377
3543
|
ensure_agent_settings(project_root, package_root, opts.profile, opts.force)
|
|
3378
3544
|
|
|
3545
|
+
# Install-mode marker (Step 8 A5) — full path flips any prior
|
|
3546
|
+
# minimal marker to "full" so doctor --context reflects the
|
|
3547
|
+
# upgraded state. Idempotent on re-runs of the same scope.
|
|
3548
|
+
_write_install_mode_marker(project_root, "full")
|
|
3549
|
+
|
|
3379
3550
|
tools = parsed_tools
|
|
3380
3551
|
|
|
3381
3552
|
# Per-tool merged_keys collected from JSON bridge merges (P1.5).
|
package/scripts/install.sh
CHANGED
|
@@ -37,6 +37,10 @@ DRY_RUN=false
|
|
|
37
37
|
VERBOSE=false
|
|
38
38
|
QUIET=false
|
|
39
39
|
SKIP_GITIGNORE=false
|
|
40
|
+
# When true, skip payload sync entirely and only install the project-local
|
|
41
|
+
# `./agent-config` wrapper (Step 7 Phase 2). The bridge stage (install.py)
|
|
42
|
+
# handles the .agent-settings.yml stub + nested-install guard.
|
|
43
|
+
MINIMAL=false
|
|
40
44
|
# Comma-separated tool IDs (default: all). Set by --tools or the
|
|
41
45
|
# orchestrator (scripts/install). The .augment/ substrate is always
|
|
42
46
|
# synced because every other tool symlinks back into it.
|
|
@@ -75,6 +79,7 @@ parse_args() {
|
|
|
75
79
|
--skip-gitignore) SKIP_GITIGNORE=true; shift ;;
|
|
76
80
|
--tools) TOOLS="$2"; shift 2 ;;
|
|
77
81
|
--tools=*) TOOLS="${1#*=}"; shift ;;
|
|
82
|
+
--minimal|--settings-only) MINIMAL=true; shift ;;
|
|
78
83
|
--help|-h) show_help; exit 0 ;;
|
|
79
84
|
*) log_error "Unknown argument: $1"; show_help; exit 1 ;;
|
|
80
85
|
esac
|
|
@@ -726,6 +731,21 @@ install_cli_wrapper() {
|
|
|
726
731
|
main() {
|
|
727
732
|
parse_args "$@"
|
|
728
733
|
|
|
734
|
+
# Minimal-init short-circuit (Step 7 Phase 2): skip every payload-sync
|
|
735
|
+
# stage and only install the project-local `./agent-config` wrapper.
|
|
736
|
+
# The bridge stage (install.py) handles the .agent-settings.yml stub
|
|
737
|
+
# + nested-install guard. No .augment/, no AGENTS.md, no symlinks.
|
|
738
|
+
if $MINIMAL; then
|
|
739
|
+
if ! $QUIET; then
|
|
740
|
+
echo "🔧 Minimal init — installing ./agent-config wrapper only"
|
|
741
|
+
echo " Target: $TARGET_DIR"
|
|
742
|
+
$DRY_RUN && echo " Mode: DRY RUN"
|
|
743
|
+
fi
|
|
744
|
+
install_cli_wrapper "$TARGET_DIR"
|
|
745
|
+
$QUIET || echo "✅ Wrapper installed (payload sync skipped)."
|
|
746
|
+
return 0
|
|
747
|
+
fi
|
|
748
|
+
|
|
729
749
|
# First-run detection: gate the verbose source/target banner behind the
|
|
730
750
|
# absence of .agent-settings.yml. Re-runs print a single status line.
|
|
731
751
|
local is_first_run=false
|
|
@@ -21,6 +21,13 @@ set -euo pipefail
|
|
|
21
21
|
|
|
22
22
|
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
23
|
|
|
24
|
+
# Step-7 Phase 3 — pin the project root for the master CLI so subdir
|
|
25
|
+
# invocations of ``./agent-config`` (or symlinks to it) do not re-walk
|
|
26
|
+
# the anchor tree. The wrapper lives at the project root by definition,
|
|
27
|
+
# so SELF_DIR *is* the canonical root. Honor a pre-set value as an
|
|
28
|
+
# escape hatch (test harnesses, supervisors that already pinned it).
|
|
29
|
+
export AGENT_CONFIG_PROJECT_ROOT="${AGENT_CONFIG_PROJECT_ROOT:-$SELF_DIR}"
|
|
30
|
+
|
|
24
31
|
locate_master() {
|
|
25
32
|
if [[ -n "${AGENT_CONFIG_MASTER:-}" && -x "$AGENT_CONFIG_MASTER" ]]; then
|
|
26
33
|
printf '%s' "$AGENT_CONFIG_MASTER"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Agent Settings — minimal stub
|
|
2
|
+
#
|
|
3
|
+
# Created by `agent-config init --minimal`. This file marks the project as
|
|
4
|
+
# an agent-config layer without installing any tool payload. The global
|
|
5
|
+
# `agent-config` binary on $PATH supplies the rules / skills / commands;
|
|
6
|
+
# this file is your per-project override point.
|
|
7
|
+
#
|
|
8
|
+
# Resolution order (deepest wins, see docs/contracts/layered-settings.md):
|
|
9
|
+
# 1. <repo-root>/.agent-settings.yml ← this file
|
|
10
|
+
# 2. ~/.event4u/agent-config/agent-settings.yml (user-global; whitelisted)
|
|
11
|
+
#
|
|
12
|
+
# Add keys here as you need them. Reference template:
|
|
13
|
+
# templates/consumer-settings/ or docs/customization.md
|
|
14
|
+
|
|
15
|
+
# --- Cost profile ---
|
|
16
|
+
# Master switch for which rule tiers load. Options: minimal | balanced | full.
|
|
17
|
+
# `balanced` is the recommended default; flip to `minimal` for kernel-only.
|
|
18
|
+
cost_profile: balanced
|
|
19
|
+
|
|
20
|
+
# --- Version pin (opt-in) ---
|
|
21
|
+
# Leave commented out to follow whatever `agent-config` is on $PATH (per D4).
|
|
22
|
+
# Uncomment and pin to a specific release for reproducible installs.
|
|
23
|
+
# agent_config_version: ""
|