@event4u/agent-config 14.6.0 → 14.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +255 -145
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +264 -10
- package/dist/agent-src/commands/analyze.md +1 -1
- package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
- package/dist/agent-src/commands/evals/roadmap.json +17 -1
- package/dist/agent-src/commands/pr/create.md +14 -2
- package/dist/agent-src/commands/pr/merge.md +379 -0
- package/dist/agent-src/commands/roadmap/process-full.md +272 -32
- package/dist/agent-src/commands/roadmap.md +1 -1
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +95 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +289 -106
- package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
- package/dist/agent-src/rules/context-hygiene.md +2 -0
- package/dist/agent-src/rules/decision-revisit-gate.md +67 -32
- package/dist/agent-src/rules/fix-what-you-see.md +108 -0
- package/dist/agent-src/rules/no-roadmap-references.md +0 -5
- package/dist/agent-src/rules/recurring-criticism.md +110 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +20 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -2
- package/dist/agent-src/rules/self-repair-loop.md +17 -0
- package/dist/agent-src/rules/skill-quality.md +0 -2
- package/dist/agent-src/rules/source-confidentiality.md +37 -7
- package/dist/agent-src/scripts/archival_sweep.ts +117 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
- package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
- package/dist/agent-src/scripts/gate_budget.ts +282 -0
- package/dist/agent-src/scripts/gate_execute.ts +72 -31
- package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
- package/dist/agent-src/scripts/update_roadmap_progress.ts +296 -53
- package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
- package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
- package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
- package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
- package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
- package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
- package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
- package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
- package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
- package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
- package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
- package/dist/agent-src/skills/security/SKILL.md +8 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
- package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
- package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
- package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +65 -4
- package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/dist/cli/registry.js +2 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
- package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-7PRE6ZV2.js} +338 -0
- package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
- package/dist/cli-delegate/chunk-E4XIZDIB.js +402 -0
- package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-EEJUMCDZ.js} +16 -16
- package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
- package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-H4VIVD4C.js} +385 -284
- package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
- package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-LPPWO5CE.js} +12 -8
- package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
- package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-N7CDDGI3.js} +4 -23
- package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-PMEKGDED.js} +74 -167
- package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
- package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
- package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
- package/dist/cli-delegate/cmd_conformance.js +466 -716
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +10 -10
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +10 -9
- package/dist/cli-delegate/cmd_handoff.js +10 -9
- package/dist/cli-delegate/cmd_memory_get.js +2 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +5 -5
- package/dist/cli-delegate/cmd_prune.js +11 -10
- package/dist/cli-delegate/cmd_route_audit.js +6 -5
- package/dist/cli-delegate/cmd_route_explain.js +3 -3
- package/dist/cli-delegate/cmd_session_recycle.js +840 -30
- package/dist/cli-delegate/cmd_settings_get.js +3 -3
- package/dist/cli-delegate/cmd_settings_migrate.js +10 -9
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +11 -10
- package/dist/cli-delegate/cmd_uninstall.js +13 -11
- package/dist/cli-delegate/cmd_update.js +4 -4
- package/dist/cli-delegate/cmd_upgrade.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +11 -10
- package/dist/cli-delegate/cmd_versions.js +3 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +111 -37
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +5 -5
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +15 -12
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +11 -8
- package/dist/hooks/dispatch.js +3165 -2192
- package/dist/install/claudePathsPlan.js +187 -0
- package/dist/install/claudePathsPlan.js.map +1 -0
- package/dist/install/globalRuleLayers.js +169 -0
- package/dist/install/globalRuleLayers.js.map +1 -0
- package/dist/install/hostLayerFingerprint.js +136 -0
- package/dist/install/hostLayerFingerprint.js.map +1 -0
- package/dist/install/install.mjs +553 -425
- package/dist/install/partitionEligibility.js +432 -0
- package/dist/install/partitionEligibility.js.map +1 -0
- package/dist/install/ruleLayerPartition.js +157 -0
- package/dist/install/ruleLayerPartition.js.map +1 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1863 -1234
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_src.js +41 -0
- package/dist/scripts/_lib/agent_src.js.map +1 -1
- package/dist/scripts/_lib/install_layout.js +83 -0
- package/dist/scripts/_lib/install_layout.js.map +1 -0
- package/dist/scripts/_lib/installed_lock.js +444 -0
- package/dist/scripts/_lib/installed_lock.js.map +1 -0
- package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
- package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
- package/dist/scripts/condense.js +63 -251
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +4 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +12 -6
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
- package/docs/guidelines/agent-infra/false-green.md +47 -1
- package/docs/guidelines/agent-infra/memory-access.md +17 -0
- package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/archival_sweep.ts +117 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
- package/src/agent-src/scripts/dashboard_mode.ts +212 -0
- package/src/agent-src/scripts/gate_budget.ts +282 -0
- package/src/agent-src/scripts/gate_execute.ts +72 -31
- package/src/agent-src/scripts/roadmap_gates.ts +74 -3
- package/src/agent-src/scripts/update_roadmap_progress.ts +296 -53
- package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/src/config/agent-settings.template.yml +27 -2
- package/src/config/ci-local-parity.yml +39 -0
- package/src/config/estate-count-budget.json +612 -3
- package/src/config/evaluator-budgets.json +3 -3
- package/src/config/gate-coverage.yml +220 -12
- package/src/config/gate-violation-baselines.json +73 -63
- package/src/config/gitignore-block.txt +15 -2
- package/src/config/hook-latency-budget.json +5 -0
- package/src/config/rule-activation-census.json +14 -6
- package/src/config/rule-stub-ceilings.json +34 -7
- package/src/install/claudePathsPlan.ts +196 -0
- package/src/install/globalRuleLayers.ts +197 -0
- package/src/install/hostLayerFingerprint.ts +145 -0
- package/src/install/partitionEligibility.ts +502 -0
- package/src/install/ruleLayerPartition.ts +212 -0
- package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
- package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
- package/src/scripts/_dispatch.bash +17 -1
- package/src/scripts/_lib/ac_heading.ts +30 -0
- package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
- package/src/scripts/_lib/adr_frontmatter.ts +459 -0
- package/src/scripts/_lib/agent_src.ts +42 -0
- package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
- package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
- package/src/scripts/_lib/envelope_grounding.ts +49 -9
- package/src/scripts/_lib/installed_lock.ts +50 -5
- package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
- package/src/scripts/_lib/md_section.ts +129 -0
- package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
- package/src/scripts/_lib/scope_guard.sh +26 -3
- package/src/scripts/_lib/self_repair.ts +19 -0
- package/src/scripts/_lib/self_repair_class_b.ts +171 -0
- package/src/scripts/adr/evidence_census.ts +899 -0
- package/src/scripts/adr/regenerate_index.ts +12 -32
- package/src/scripts/adr_cite_check.ts +540 -39
- package/src/scripts/ai_council/cli_help.ts +55 -0
- package/src/scripts/audit_adr_coverage.ts +103 -17
- package/src/scripts/before_complete_hook.ts +153 -28
- package/src/scripts/bench_ab_v2_complexity.ts +27 -7
- package/src/scripts/bench_hook_latency.ts +204 -0
- package/src/scripts/build_archive_index.ts +34 -0
- package/src/scripts/check_adr_frontmatter.ts +574 -39
- package/src/scripts/check_hook_bundle_content.ts +219 -0
- package/src/scripts/check_new_adr_evidence.ts +529 -0
- package/src/scripts/check_references.ts +78 -0
- package/src/scripts/check_release_published.ts +32 -11
- package/src/scripts/check_rule_layer_partition.ts +531 -0
- package/src/scripts/check_single_delivery.ts +194 -17
- package/src/scripts/check_standing_rule_delivery.ts +8 -2
- package/src/scripts/ci_settle.ts +170 -0
- package/src/scripts/condense.ts +79 -259
- package/src/scripts/council_cli.ts +3 -3
- package/src/scripts/dispatch_r2_reviewer.ts +6 -14
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +77 -14
- package/src/scripts/hooks/block_no_verify.ts +61 -10
- package/src/scripts/hooks/block_unauthorized_git.ts +21 -1
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/dispatch_hook.ts +154 -187
- package/src/scripts/hooks/dispatch_issues.ts +38 -19
- package/src/scripts/hooks/fallback_yaml.ts +66 -0
- package/src/scripts/hooks/host_semantics.ts +21 -0
- package/src/scripts/hooks/injection_budget.ts +13 -3
- package/src/scripts/hooks/py_json_dumps.ts +103 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
- package/src/scripts/hooks/run_continuation_hook.ts +8 -5
- package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
- package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
- package/src/scripts/hooks/state_io.ts +870 -21
- package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
- package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
- package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
- package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
- package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
- package/src/scripts/injection_scan_hook.ts +137 -5
- package/src/scripts/install.ts +16 -11
- package/src/scripts/language_mirror_hook.ts +561 -25
- package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
- package/src/scripts/lint_hook_manifest.ts +49 -0
- package/src/scripts/lint_plan_risk_register.ts +4 -3
- package/src/scripts/lint_provenance_vocabulary.ts +472 -4
- package/src/scripts/lint_skill_router_head.ts +4 -4
- package/src/scripts/mcp_server/tools.ts +2 -2
- package/src/scripts/new_roadmap.ts +136 -0
- package/src/scripts/pack_mcp_content.ts +20 -5
- package/src/scripts/release.ts +37 -38
- package/src/scripts/report_layer_overlap.ts +6 -0
- package/src/scripts/routing_doctor.ts +5 -1
- package/src/scripts/self_repair_hook.ts +37 -5
- package/src/scripts/session_register_hook.ts +11 -0
- package/src/scripts/skill_usage_collect.ts +11 -1
- package/src/scripts/skill_usage_report.ts +278 -8
- package/src/scripts/sync_pr_branch.ts +170 -20
- package/src/server/schemas/settings.ts +8 -0
- package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
- package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
- package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
- package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
- package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-MMSGVZKV.js} +3 -3
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global-install lockfile at `~/.event4u/agent-config/installed.lock`.
|
|
3
|
+
*
|
|
4
|
+
* Ported from the retired Python `src/scripts/_lib/installed_lock.py` (ADR-200 — Python→TS
|
|
5
|
+
* migration, Phase 2 / Wave 2a). Public API mirrors the Python module exactly
|
|
6
|
+
* (snake_case kept deliberately — fidelity over TS idiom), including the
|
|
7
|
+
* byte-exact lockfile wire format and the tempfile + rename atomic write.
|
|
8
|
+
*
|
|
9
|
+
* Phase 1.6 of road-to-global-first-install (ADR-007 D5). Records the package
|
|
10
|
+
* version that performed the most recent user-scope install plus the tools
|
|
11
|
+
* that were scaffolded. `init --global` reads this file: on version mismatch
|
|
12
|
+
* the install refuses unless `--force` is passed; the `update` subcommand
|
|
13
|
+
* refreshes the entry in lockstep with the pin flip in `.agent-settings.yml`.
|
|
14
|
+
*
|
|
15
|
+
* The schema is intentionally minimal YAML so the module can read and write
|
|
16
|
+
* without depending on a YAML library. Atomic writes go through tempfile +
|
|
17
|
+
* rename per ADR-007 risk-mitigation row.
|
|
18
|
+
*
|
|
19
|
+
* Path resolution is delegated to `user_global_paths` (Phase 1 of
|
|
20
|
+
* road-to-event4u-namespace-and-claude-desktop.md): writes land at
|
|
21
|
+
* `~/.event4u/agent-config/installed.lock`; reads fall back to the legacy
|
|
22
|
+
* `~/.config/agent-config/installed.lock` if the new path is missing, so
|
|
23
|
+
* pre-2.4 installs keep working during the transition.
|
|
24
|
+
*/
|
|
25
|
+
import { randomBytes } from "node:crypto";
|
|
26
|
+
import * as fs from "node:fs";
|
|
27
|
+
import * as os from "node:os";
|
|
28
|
+
import * as path from "node:path";
|
|
29
|
+
import { fileURLToPath } from "node:url";
|
|
30
|
+
import * as user_global_paths from "./user_global_paths.js";
|
|
31
|
+
import { INSTALL_LAYOUT_VERSION, coerce_layout_version, needs_migration, } from "./install_layout.js";
|
|
32
|
+
export const LOCKFILE_ENV = "AGENT_CONFIG_INSTALLED_LOCK";
|
|
33
|
+
export const SCHEMA_VERSION = 1;
|
|
34
|
+
/** Canonical write target for the lockfile (new namespace). */
|
|
35
|
+
function _default_lockfile() {
|
|
36
|
+
return user_global_paths.write_target("installed.lock");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Module-level constant retained for back-compat with importers that read
|
|
40
|
+
* `DEFAULT_LOCKFILE` directly. Derived from the helper so the path tracks any
|
|
41
|
+
* future override of `event4u_root()`.
|
|
42
|
+
*/
|
|
43
|
+
export const DEFAULT_LOCKFILE = _default_lockfile();
|
|
44
|
+
const _VERSION_RE = /^\s*agent_config_version\s*:\s*"?([^"\s]+)"?\s*$/;
|
|
45
|
+
const _SCHEMA_RE = /^\s*schema_version\s*:\s*(\d+)\s*$/;
|
|
46
|
+
const _LAYOUT_RE = /^\s*install_layout_version\s*:\s*(\d+)\s*$/;
|
|
47
|
+
const _INSTALLED_AT_RE = /^\s*installed_at\s*:\s*"?([^"\s]+)"?\s*$/;
|
|
48
|
+
const _FINGERPRINT_RE = /^\s*host_layer_fingerprint\s*:\s*"?([0-9a-f]{64})"?\s*$/;
|
|
49
|
+
const _TOOL_RE = /^\s*-\s*([A-Za-z0-9_\-.]+)\s*$/;
|
|
50
|
+
/** Expand a leading `~` like Python's `Path.expanduser()`. */
|
|
51
|
+
function expanduser(p) {
|
|
52
|
+
if (p === "~") {
|
|
53
|
+
return os.homedir();
|
|
54
|
+
}
|
|
55
|
+
if (p.startsWith("~/") || (process.platform === "win32" && p.startsWith("~\\"))) {
|
|
56
|
+
return path.join(os.homedir(), p.slice(2));
|
|
57
|
+
}
|
|
58
|
+
return p;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Return the active lockfile path for **reads**, honoring overrides.
|
|
62
|
+
*
|
|
63
|
+
* Resolution order:
|
|
64
|
+
* 1. `$AGENT_CONFIG_INSTALLED_LOCK` — explicit full-path override.
|
|
65
|
+
* 2. `~/.event4u/agent-config/installed.lock` if it exists on disk.
|
|
66
|
+
* 3. `~/.config/agent-config/installed.lock` (legacy fallback, read-only).
|
|
67
|
+
* 4. Canonical write target under the new namespace (Step 2 fallthrough).
|
|
68
|
+
*/
|
|
69
|
+
export function lockfile_path(env) {
|
|
70
|
+
const env_map = env ?? process.env;
|
|
71
|
+
const override = env_map[LOCKFILE_ENV];
|
|
72
|
+
if (override) {
|
|
73
|
+
return expanduser(override);
|
|
74
|
+
}
|
|
75
|
+
const resolved = user_global_paths.resolve_with_fallback("installed.lock", { env: env ?? null });
|
|
76
|
+
if (resolved !== null) {
|
|
77
|
+
return resolved;
|
|
78
|
+
}
|
|
79
|
+
return user_global_paths.write_target("installed.lock", { env: env ?? null });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Return the canonical write target for the lockfile.
|
|
83
|
+
*
|
|
84
|
+
* Unlike {@link lockfile_path}, this never falls back to the legacy
|
|
85
|
+
* `~/.config/agent-config/` location. Honors the `$AGENT_CONFIG_INSTALLED_LOCK`
|
|
86
|
+
* override for tests, otherwise pins to `~/.event4u/agent-config/installed.lock`.
|
|
87
|
+
*/
|
|
88
|
+
export function lockfile_write_path(env) {
|
|
89
|
+
const env_map = env ?? process.env;
|
|
90
|
+
const override = env_map[LOCKFILE_ENV];
|
|
91
|
+
if (override) {
|
|
92
|
+
return expanduser(override);
|
|
93
|
+
}
|
|
94
|
+
return user_global_paths.write_target("installed.lock", { env: env ?? null });
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Parse `path` (or the default) into a dict; return `null` if absent.
|
|
98
|
+
*
|
|
99
|
+
* Tolerates partial / malformed files: missing keys yield missing dict entries
|
|
100
|
+
* rather than throwing, so a hand-edited corrupt file does not brick `init`.
|
|
101
|
+
*/
|
|
102
|
+
export function read_lockfile(path) {
|
|
103
|
+
const target = path ?? lockfile_path();
|
|
104
|
+
let text;
|
|
105
|
+
try {
|
|
106
|
+
text = fs.readFileSync(target, { encoding: "utf-8" });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const data = { tools: [] };
|
|
112
|
+
let in_tools = false;
|
|
113
|
+
for (const raw_line of splitlines(text)) {
|
|
114
|
+
const schema_m = _SCHEMA_RE.exec(raw_line);
|
|
115
|
+
if (schema_m) {
|
|
116
|
+
data.schema_version = Number.parseInt(schema_m[1], 10);
|
|
117
|
+
in_tools = false;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const layout_m = _LAYOUT_RE.exec(raw_line);
|
|
121
|
+
if (layout_m) {
|
|
122
|
+
data.install_layout_version = Number.parseInt(layout_m[1], 10);
|
|
123
|
+
in_tools = false;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const version_m = _VERSION_RE.exec(raw_line);
|
|
127
|
+
if (version_m) {
|
|
128
|
+
data.agent_config_version = version_m[1];
|
|
129
|
+
in_tools = false;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const installed_m = _INSTALLED_AT_RE.exec(raw_line);
|
|
133
|
+
if (installed_m) {
|
|
134
|
+
data.installed_at = installed_m[1];
|
|
135
|
+
in_tools = false;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const fp_m = _FINGERPRINT_RE.exec(raw_line);
|
|
139
|
+
if (fp_m) {
|
|
140
|
+
data.host_layer_fingerprint = fp_m[1];
|
|
141
|
+
in_tools = false;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (raw_line.trim().startsWith("tools:")) {
|
|
145
|
+
in_tools = true;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (in_tools) {
|
|
149
|
+
const m = _TOOL_RE.exec(raw_line);
|
|
150
|
+
if (m) {
|
|
151
|
+
data.tools.push(m[1]);
|
|
152
|
+
}
|
|
153
|
+
else if (raw_line.trim() &&
|
|
154
|
+
!(raw_line.startsWith(" ") || raw_line.startsWith("\t") || raw_line.startsWith("-"))) {
|
|
155
|
+
in_tools = false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return data;
|
|
160
|
+
}
|
|
161
|
+
/** `str.splitlines()` — split on `\n`, dropping a trailing empty segment. */
|
|
162
|
+
function splitlines(text) {
|
|
163
|
+
const parts = text.split("\n");
|
|
164
|
+
if (parts.length > 0 && parts[parts.length - 1] === "") {
|
|
165
|
+
parts.pop();
|
|
166
|
+
}
|
|
167
|
+
return parts;
|
|
168
|
+
}
|
|
169
|
+
function _render(version, tools, installed_at, fingerprint) {
|
|
170
|
+
const lines = [
|
|
171
|
+
`schema_version: ${SCHEMA_VERSION}`,
|
|
172
|
+
`install_layout_version: ${INSTALL_LAYOUT_VERSION}`,
|
|
173
|
+
`agent_config_version: "${version}"`,
|
|
174
|
+
`installed_at: "${installed_at}"`,
|
|
175
|
+
];
|
|
176
|
+
// Written BEFORE `tools:` so the sequence block stays last and the parser's
|
|
177
|
+
// in_tools state machine needs no extra exit condition.
|
|
178
|
+
if (fingerprint) {
|
|
179
|
+
lines.push(`host_layer_fingerprint: "${fingerprint}"`);
|
|
180
|
+
}
|
|
181
|
+
lines.push("tools:");
|
|
182
|
+
for (const tool of tools) {
|
|
183
|
+
lines.push(` - ${tool}`);
|
|
184
|
+
}
|
|
185
|
+
return lines.join("\n") + "\n";
|
|
186
|
+
}
|
|
187
|
+
/** Atomically write the lockfile; return the path written. */
|
|
188
|
+
export function write_lockfile(version, tools, options = {}) {
|
|
189
|
+
const target = options.path ?? lockfile_path();
|
|
190
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
191
|
+
const stamp = strftime_iso_z(options.now ?? new Date());
|
|
192
|
+
const rendered = _render(version, sorted_unique(tools), stamp, options.host_layer_fingerprint ?? null);
|
|
193
|
+
// Atomic write: tempfile in the same dir + rename. The same-dir constraint
|
|
194
|
+
// keeps the rename atomic across all POSIX filesystems and Windows when the
|
|
195
|
+
// file already exists.
|
|
196
|
+
const parent = path.dirname(target);
|
|
197
|
+
const { fd, tmp_name } = mkstemp(parent, ".installed.lock.");
|
|
198
|
+
try {
|
|
199
|
+
fs.writeFileSync(fd, rendered, { encoding: "utf-8" });
|
|
200
|
+
fs.closeSync(fd);
|
|
201
|
+
fs.renameSync(tmp_name, target);
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
try {
|
|
205
|
+
fs.closeSync(fd);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// fd may already be closed.
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
fs.unlinkSync(tmp_name);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
// Best-effort cleanup.
|
|
215
|
+
}
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
return target;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parse the lockfile's `installed_at` stamp back into a UTC Date.
|
|
222
|
+
*
|
|
223
|
+
* Returns `null` when absent or malformed, so a migration falls back to the
|
|
224
|
+
* current time rather than raising on a hand-edited file. Mirrors Python's
|
|
225
|
+
* `datetime.strptime(stamp, "%Y-%m-%dT%H:%M:%SZ")` — strict, UTC, no other
|
|
226
|
+
* formats accepted.
|
|
227
|
+
*/
|
|
228
|
+
function _parse_installed_at(stamp) {
|
|
229
|
+
if (!stamp) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/.exec(stamp);
|
|
233
|
+
if (!m) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const [, y, mo, d, h, mi, s] = m;
|
|
237
|
+
const ms = Date.UTC(Number.parseInt(y, 10), Number.parseInt(mo, 10) - 1, Number.parseInt(d, 10), Number.parseInt(h, 10), Number.parseInt(mi, 10), Number.parseInt(s, 10));
|
|
238
|
+
// strptime rejects out-of-range fields (e.g. month 13) rather than rolling
|
|
239
|
+
// over the way Date.UTC does; reject any value that round-trips differently.
|
|
240
|
+
const parsed = new Date(ms);
|
|
241
|
+
if (parsed.getUTCFullYear() !== Number.parseInt(y, 10) ||
|
|
242
|
+
parsed.getUTCMonth() !== Number.parseInt(mo, 10) - 1 ||
|
|
243
|
+
parsed.getUTCDate() !== Number.parseInt(d, 10) ||
|
|
244
|
+
parsed.getUTCHours() !== Number.parseInt(h, 10) ||
|
|
245
|
+
parsed.getUTCMinutes() !== Number.parseInt(mi, 10) ||
|
|
246
|
+
parsed.getUTCSeconds() !== Number.parseInt(s, 10)) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
return parsed;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Migrate an installed-tree lockfile to the current install-layout ABI.
|
|
253
|
+
*
|
|
254
|
+
* Idempotent. Detects `install_layout_version < INSTALL_LAYOUT_VERSION`
|
|
255
|
+
* (absent = pre-freeze v0) and migrates the on-disk shape in place,
|
|
256
|
+
* preserving the recorded tools, package version, and `installed_at` stamp
|
|
257
|
+
* and re-stamping the layout version.
|
|
258
|
+
*
|
|
259
|
+
* At the freeze baseline (v0 → v1) the only material change is stamping the
|
|
260
|
+
* version — the on-disk shape is unchanged. Future layout versions extend
|
|
261
|
+
* this function with the concrete shape transforms; surgical-uninstall
|
|
262
|
+
* pointers must always be preserved.
|
|
263
|
+
*
|
|
264
|
+
* Returns:
|
|
265
|
+
* - `null` — no lockfile exists (nothing installed).
|
|
266
|
+
* - `{from: v, to: v, changed: []}` — already current (no-op).
|
|
267
|
+
* - `{from: old, to: current, changed: [...]}` — migrated.
|
|
268
|
+
*/
|
|
269
|
+
export function migrate_layout(options = {}) {
|
|
270
|
+
const target = options.path ?? lockfile_write_path();
|
|
271
|
+
const existing = read_lockfile(target);
|
|
272
|
+
if (existing === null) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
const from_v = coerce_layout_version(existing.install_layout_version);
|
|
276
|
+
if (!needs_migration(existing.install_layout_version)) {
|
|
277
|
+
return { from: from_v, to: from_v, changed: [] };
|
|
278
|
+
}
|
|
279
|
+
const version = existing.agent_config_version || current_package_version();
|
|
280
|
+
const tools = [...(existing.tools ?? [])];
|
|
281
|
+
const when = options.now ?? _parse_installed_at(existing.installed_at);
|
|
282
|
+
// Carry the fingerprint across the migration. Dropping it would silently
|
|
283
|
+
// demote a verified host layer to "not verified" and turn the partition off
|
|
284
|
+
// on the next build — a layout migration must not change delivery.
|
|
285
|
+
write_lockfile(version, tools, {
|
|
286
|
+
path: target,
|
|
287
|
+
now: when,
|
|
288
|
+
host_layer_fingerprint: existing.host_layer_fingerprint ?? null,
|
|
289
|
+
});
|
|
290
|
+
return {
|
|
291
|
+
from: from_v,
|
|
292
|
+
to: INSTALL_LAYOUT_VERSION,
|
|
293
|
+
changed: [`install_layout_version ${from_v} → ${INSTALL_LAYOUT_VERSION}`],
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/** `sorted(set(tools))` — de-duplicate then sort ascending (codepoint). */
|
|
297
|
+
function sorted_unique(tools) {
|
|
298
|
+
return [...new Set(tools)].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
299
|
+
}
|
|
300
|
+
/** Format a Date as `%Y-%m-%dT%H:%M:%SZ` in UTC. */
|
|
301
|
+
function strftime_iso_z(now) {
|
|
302
|
+
// ISO string is `YYYY-MM-DDTHH:MM:SS.sssZ`; drop milliseconds → `...SSZ`.
|
|
303
|
+
return now.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
304
|
+
}
|
|
305
|
+
/** `tempfile.mkstemp` equivalent: exclusive-create a unique sibling, mode 0600. */
|
|
306
|
+
function mkstemp(dir, prefix) {
|
|
307
|
+
for (let attempt = 0; attempt < 32; attempt += 1) {
|
|
308
|
+
const tmp_name = path.join(dir, `${prefix}${randomBytes(6).toString("hex")}`);
|
|
309
|
+
try {
|
|
310
|
+
const fd = fs.openSync(tmp_name, "wx", 0o600);
|
|
311
|
+
return { fd, tmp_name };
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
if (err.code === "EEXIST") {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
throw err;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
throw new Error("mkstemp: could not create a unique temp file");
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Compare `installed_version` against the lockfile's recorded version.
|
|
324
|
+
*
|
|
325
|
+
* Returns `[ok, recorded_version_or_null]`:
|
|
326
|
+
* - `[true, null]` — no lockfile yet; `init` may proceed.
|
|
327
|
+
* - `[true, vX]` — matches; `init` may proceed.
|
|
328
|
+
* - `[false, vY]` — mismatch; caller must refuse without `--force`.
|
|
329
|
+
*/
|
|
330
|
+
export function check_version(installed_version, options = {}) {
|
|
331
|
+
const existing = read_lockfile(options.path ?? null);
|
|
332
|
+
if (existing === null) {
|
|
333
|
+
return [true, null];
|
|
334
|
+
}
|
|
335
|
+
const recorded = existing.agent_config_version;
|
|
336
|
+
if (!recorded) {
|
|
337
|
+
return [true, null];
|
|
338
|
+
}
|
|
339
|
+
return [recorded === installed_version, recorded];
|
|
340
|
+
}
|
|
341
|
+
const _SEMVER_RE = /^\s*v?(\d+)\.(\d+)\.(\d+)/;
|
|
342
|
+
/**
|
|
343
|
+
* Parse `X.Y.Z[-suffix]` into a `[major, minor, patch]` tuple.
|
|
344
|
+
*
|
|
345
|
+
* Returns `null` when the leading three numeric segments cannot be extracted.
|
|
346
|
+
* Suffixes (`-rc1`, `+build.5`) are ignored.
|
|
347
|
+
*/
|
|
348
|
+
function _parse_semver(version) {
|
|
349
|
+
const match = _SEMVER_RE.exec(version);
|
|
350
|
+
if (!match) {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
return [Number.parseInt(match[1], 10), Number.parseInt(match[2], 10), Number.parseInt(match[3], 10)];
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Classify the relationship between recorded and installed versions.
|
|
357
|
+
*
|
|
358
|
+
* Returns one of: `"none"`, `"match"`, `"upgrade"`, `"downgrade"`,
|
|
359
|
+
* `"unparseable"`.
|
|
360
|
+
*/
|
|
361
|
+
export function classify_mismatch(installed_version, recorded) {
|
|
362
|
+
if (recorded === null) {
|
|
363
|
+
return "none";
|
|
364
|
+
}
|
|
365
|
+
if (recorded === installed_version) {
|
|
366
|
+
return "match";
|
|
367
|
+
}
|
|
368
|
+
const rec = _parse_semver(recorded);
|
|
369
|
+
const inst = _parse_semver(installed_version);
|
|
370
|
+
if (rec === null || inst === null) {
|
|
371
|
+
return "unparseable";
|
|
372
|
+
}
|
|
373
|
+
if (tuple_lt(rec, inst)) {
|
|
374
|
+
return "upgrade";
|
|
375
|
+
}
|
|
376
|
+
return "downgrade";
|
|
377
|
+
}
|
|
378
|
+
/** Lexicographic tuple `<` comparison, mirroring Python's tuple ordering. */
|
|
379
|
+
function tuple_lt(a, b) {
|
|
380
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
381
|
+
const av = a[i];
|
|
382
|
+
const bv = b[i];
|
|
383
|
+
if (av < bv)
|
|
384
|
+
return true;
|
|
385
|
+
if (av > bv)
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
/** Read a string `version` from `<dir>/package.json`, or null if absent/malformed. */
|
|
391
|
+
function _read_package_version(dir) {
|
|
392
|
+
try {
|
|
393
|
+
const data = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), { encoding: "utf-8" }));
|
|
394
|
+
const version = data.version;
|
|
395
|
+
if (typeof version === "string" && version.trim()) {
|
|
396
|
+
return version.trim();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
// Missing / unreadable / malformed package.json → null.
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Walk upward from `start_dir` to the nearest `package.json` carrying a string
|
|
406
|
+
* `version`. Layout-independent: works in the source tree
|
|
407
|
+
* (`src/scripts/_lib/`), a per-file `dist/`, AND the esbuild single-file bundle
|
|
408
|
+
* (`dist/cli/agent-config.js`) where this module's runtime location differs
|
|
409
|
+
* from its source path. Returns null at the filesystem root.
|
|
410
|
+
*/
|
|
411
|
+
export function _find_package_version_upward(start_dir) {
|
|
412
|
+
let dir = start_dir;
|
|
413
|
+
for (;;) {
|
|
414
|
+
const found = _read_package_version(dir);
|
|
415
|
+
if (found !== null) {
|
|
416
|
+
return found;
|
|
417
|
+
}
|
|
418
|
+
const parent = path.dirname(dir);
|
|
419
|
+
if (parent === dir) {
|
|
420
|
+
return null; // reached the filesystem root
|
|
421
|
+
}
|
|
422
|
+
dir = parent;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Read `version` from the package's own `package.json`.
|
|
427
|
+
*
|
|
428
|
+
* With no `repo_root`, the version is resolved by walking **upward** from this
|
|
429
|
+
* module's runtime directory to the nearest `package.json`. A previous fixed
|
|
430
|
+
* `parents[3]` hop assumed the source layout (`src/scripts/_lib/`); in the
|
|
431
|
+
* shipped esbuild bundle the module runs from `dist/cli/`, so three hops
|
|
432
|
+
* overshot the package root → `package.json` not found → `"0.0.0"` → a
|
|
433
|
+
* spurious "downgrade" refusal on `agent-config global` (lockfile recorded a
|
|
434
|
+
* real version, current package read as `0.0.0`). The upward walk is correct
|
|
435
|
+
* in both layouts.
|
|
436
|
+
*/
|
|
437
|
+
export function current_package_version(repo_root) {
|
|
438
|
+
if (repo_root != null) {
|
|
439
|
+
return _read_package_version(repo_root) ?? "0.0.0";
|
|
440
|
+
}
|
|
441
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
442
|
+
return _find_package_version_upward(here) ?? "0.0.0";
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=installed_lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installed_lock.js","sourceRoot":"","sources":["../../../src/scripts/_lib/installed_lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove empty directories under a root, deepest first.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists as a shared helper
|
|
5
|
+
*
|
|
6
|
+
* A generator that stops writing into a tree leaves the tree's DIRECTORIES
|
|
7
|
+
* behind. Measured 2026-08-21 while extending the ADR-236 partition to the
|
|
8
|
+
* colon-form commands: after the generator was gated, `.claude/commands/`
|
|
9
|
+
* held **0 `.md` files and 40 empty cluster directories** — and
|
|
10
|
+
* `check_single_delivery` counts directory NAMES, so it still reported 40
|
|
11
|
+
* overlapping commands against a layer that delivered none. A partition that
|
|
12
|
+
* stops the writes and leaves the shape is invisible to the gate that is
|
|
13
|
+
* supposed to confirm it.
|
|
14
|
+
*
|
|
15
|
+
* `condense.ts` already carried this loop inline for the skills tree. It is
|
|
16
|
+
* lifted here rather than copied because the second caller is what turns an
|
|
17
|
+
* inline block into a shape two producers must agree on — and because
|
|
18
|
+
* `condense.ts` sits above `check_source_size_budget`'s ceiling, where a
|
|
19
|
+
* shrink-only ratchet refuses even a small addition.
|
|
20
|
+
*
|
|
21
|
+
* Best-effort by construction: a race or a permission error on one directory is
|
|
22
|
+
* swallowed, matching the inline original. Pruning is cleanup, never a gate.
|
|
23
|
+
*
|
|
24
|
+
* ## Symlinks: MEASURED, because a neutral review split on it
|
|
25
|
+
*
|
|
26
|
+
* One seat called an unguarded recursion a merge blocker (a directory symlink
|
|
27
|
+
* pointing at an ancestor would loop forever); the other called the finding
|
|
28
|
+
* false, on the grounds that `Dirent.isDirectory()` does not follow the target.
|
|
29
|
+
* A vote cannot settle that, so it was probed — `readdirSync(dir, {withFileTypes:
|
|
30
|
+
* true})` over a real symlink-to-directory on darwin, Node 26:
|
|
31
|
+
*
|
|
32
|
+
* link isDirectory=false isSymbolicLink=true
|
|
33
|
+
* real isDirectory=true isSymbolicLink=false
|
|
34
|
+
*
|
|
35
|
+
* `Dirent` carries `lstat` semantics, so the walk below cannot descend into a
|
|
36
|
+
* symlink at all and no loop is reachable — and no inode set is needed. The
|
|
37
|
+
* second seat was right. Recorded rather than fixed, so the next reader does not
|
|
38
|
+
* re-raise it: a defensive `!e.isSymbolicLink()` here would be dead code
|
|
39
|
+
* asserting something the type already guarantees.
|
|
40
|
+
*
|
|
41
|
+
* The same property is why this is safe on a tree of symlinks: `.claude/commands`
|
|
42
|
+
* holds symlinks into `src/domains/**`, and pruning must not follow one out of
|
|
43
|
+
* the tree it was pointed at.
|
|
44
|
+
*/
|
|
45
|
+
import * as fs from 'node:fs';
|
|
46
|
+
import * as path from 'node:path';
|
|
47
|
+
/** @returns the number of directories removed. */
|
|
48
|
+
export function pruneEmptyDirs(root) {
|
|
49
|
+
let removed = 0;
|
|
50
|
+
const walk = (dir) => {
|
|
51
|
+
let entries;
|
|
52
|
+
try {
|
|
53
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
for (const e of entries) {
|
|
59
|
+
if (e.isDirectory())
|
|
60
|
+
walk(path.join(dir, e.name));
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
if (fs.readdirSync(dir).length === 0) {
|
|
64
|
+
fs.rmdirSync(dir);
|
|
65
|
+
removed += 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* race or permission — cleanup is best-effort, mirrors the inline original */
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
let top;
|
|
73
|
+
try {
|
|
74
|
+
top = fs.readdirSync(root, { withFileTypes: true });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return 0;
|
|
78
|
+
}
|
|
79
|
+
for (const e of top) {
|
|
80
|
+
if (e.isDirectory())
|
|
81
|
+
walk(path.join(root, e.name));
|
|
82
|
+
}
|
|
83
|
+
return removed;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=prune_empty_dirs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune_empty_dirs.js","sourceRoot":"","sources":["../../../src/scripts/_lib/prune_empty_dirs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,kDAAkD;AAClD,MAAM,UAAU,cAAc,CAAC,IAAY;IACvC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,IAAI,GAAG,CAAC,GAAW,EAAQ,EAAE;QAC/B,IAAI,OAAoB,CAAC;QACzB,IAAI,CAAC;YACD,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACL,OAAO;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,IAAI,CAAC,CAAC,WAAW,EAAE;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC;YACD,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACnC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBAClB,OAAO,IAAI,CAAC,CAAC;YACjB,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,8EAA8E;QAClF,CAAC;IACL,CAAC,CAAC;IACF,IAAI,GAAgB,CAAC;IACrB,IAAI,CAAC;QACD,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,CAAC;IACb,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,CAAC,WAAW,EAAE;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC"}
|