@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
package/dist/scripts/condense.js
CHANGED
|
@@ -227,7 +227,13 @@ function _stripChars(s, chars) {
|
|
|
227
227
|
// module-level CLI self-invoke into its bundle. Re-exported here because four
|
|
228
228
|
// in-repo consumers import it from `condense.js`.
|
|
229
229
|
export { rule_in_scope } from '../install/ruleInScope.js';
|
|
230
|
+
// Re-exported for in-repo consumers; definitions moved beside emit_host_rules_cli.ts (Phase 5).
|
|
231
|
+
export { CLAUDE_PATHS_PATTERN_BUDGET, _claude_paths_plan, _escape_claude_bracket, _expanded_pattern_count, _has_non_path_trigger, _is_unresolved_placeholder, derive_trigger_globs, } from '../install/claudePathsPlan.js';
|
|
230
232
|
import { rule_in_scope } from '../install/ruleInScope.js';
|
|
233
|
+
import { pruneEmptyDirs } from './_lib/prune_empty_dirs.js';
|
|
234
|
+
import { commandsWithheld, partitionActive, personaPartition, setPartitionAnnounce } from '../install/partitionEligibility.js'; // ADR-236
|
|
235
|
+
import { dedupableRules, partitionRulesForDir } from '../install/ruleLayerPartition.js'; // ADR-236, per-host evidence
|
|
236
|
+
import { _claude_paths_plan, derive_trigger_globs } from '../install/claudePathsPlan.js';
|
|
231
237
|
const _HERE = path.dirname(fileURLToPath(import.meta.url)); // <repo>/src/scripts
|
|
232
238
|
const _DEFAULT_PROJECT_ROOT = path.resolve(_HERE, '..', '..');
|
|
233
239
|
function _deriveState(root) {
|
|
@@ -256,6 +262,7 @@ function _deriveState(root) {
|
|
|
256
262
|
};
|
|
257
263
|
}
|
|
258
264
|
export const MODULE_STATE = _deriveState(_DEFAULT_PROJECT_ROOT);
|
|
265
|
+
setPartitionAnnounce(success); // ADR-236: the mode line must be visible at the default level
|
|
259
266
|
/**
|
|
260
267
|
* Test seam — reassign one or more module-state fields, mirroring the pytest
|
|
261
268
|
* monkeypatch of `condense.<NAME> = ...`. When `PROJECT_ROOT` is overridden,
|
|
@@ -361,14 +368,6 @@ function _read_augment_rules_use_symlinks() {
|
|
|
361
368
|
}
|
|
362
369
|
return false;
|
|
363
370
|
}
|
|
364
|
-
/**
|
|
365
|
-
* Per-tool user-scope rule directory, for the scope-dedup below. Only tools
|
|
366
|
-
* whose host reads a user-scope rules directory can have a redundant twin;
|
|
367
|
-
* everything else has nothing to de-duplicate against.
|
|
368
|
-
*/
|
|
369
|
-
const USER_SCOPE_RULE_DIRS = {
|
|
370
|
-
'.claude/rules': path.join('.claude', 'rules'),
|
|
371
|
-
};
|
|
372
371
|
function _read_projection_scope_dedup() {
|
|
373
372
|
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
374
373
|
const projection = data['projection'];
|
|
@@ -384,63 +383,6 @@ function _read_projection_scope_dedup() {
|
|
|
384
383
|
}
|
|
385
384
|
return false;
|
|
386
385
|
}
|
|
387
|
-
/**
|
|
388
|
-
* Rules whose user-scope twin is BYTE-IDENTICAL to the source, i.e. safe to
|
|
389
|
-
* skip at project scope: the host still loads the same text, once instead of
|
|
390
|
-
* twice, so nothing the model sees changes.
|
|
391
|
-
*
|
|
392
|
-
* Byte-identity is the whole safety argument and is not negotiable. Keying on
|
|
393
|
-
* the filename alone would silently let a stale globally-installed copy win
|
|
394
|
-
* whenever the two scopes hold different versions — which is the NORMAL state
|
|
395
|
-
* while developing the package (measured: 110/110 shared filenames differing in
|
|
396
|
-
* bytes). This is deliberately not the thin-projection mechanism: no rule
|
|
397
|
-
* becomes trigger-gated, so the quality floor that disabled thin projection
|
|
398
|
-
* does not apply here.
|
|
399
|
-
*/
|
|
400
|
-
function _dedupable_rules(tool_dir, rules, userHome) {
|
|
401
|
-
const relative = USER_SCOPE_RULE_DIRS[tool_dir];
|
|
402
|
-
if (relative === undefined) {
|
|
403
|
-
return new Set();
|
|
404
|
-
}
|
|
405
|
-
// A hostile or simply absent $HOME must make the dedup inert, not
|
|
406
|
-
// adventurous. In a container `$HOME` is often unset (so `homedir()` can
|
|
407
|
-
// resolve to `/`) or world-writable, and this function decides which rules
|
|
408
|
-
// to STOP emitting — reading an unexpected tree there is how a projection
|
|
409
|
-
// silently loses a rule. Council review of PR #1055 raised exactly this.
|
|
410
|
-
let userDirStat;
|
|
411
|
-
const userDir = path.join(userHome, relative);
|
|
412
|
-
try {
|
|
413
|
-
userDirStat = fs.statSync(userDir);
|
|
414
|
-
}
|
|
415
|
-
catch {
|
|
416
|
-
return new Set();
|
|
417
|
-
}
|
|
418
|
-
if (!userDirStat.isDirectory()) {
|
|
419
|
-
return new Set();
|
|
420
|
-
}
|
|
421
|
-
// World-writable user scope: anyone on the box could plant a byte-identical
|
|
422
|
-
// twin and thereby delete a rule from the project projection. Refuse.
|
|
423
|
-
if ((userDirStat.mode & 0o002) !== 0) {
|
|
424
|
-
_print(` ⚠️ ${tool_dir}: user-scope rules dir is world-writable — scope-dedup skipped`);
|
|
425
|
-
return new Set();
|
|
426
|
-
}
|
|
427
|
-
const skip = new Set();
|
|
428
|
-
for (const rule of rules) {
|
|
429
|
-
const twin = path.join(userDir, rule);
|
|
430
|
-
const source = path.join(MODULE_STATE.RULES_SOURCE, rule);
|
|
431
|
-
try {
|
|
432
|
-
if (!_isFile(twin) || !_isFile(source))
|
|
433
|
-
continue;
|
|
434
|
-
if (fs.readFileSync(twin).equals(fs.readFileSync(source))) {
|
|
435
|
-
skip.add(rule);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
catch {
|
|
439
|
-
// An unreadable twin is simply not de-duplicable — emit the project copy.
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return skip;
|
|
443
|
-
}
|
|
444
386
|
function _lean_projection_mode() {
|
|
445
387
|
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
446
388
|
const lean = data['lean_projection'];
|
|
@@ -1007,6 +949,13 @@ function _scoped_rule_basenames() {
|
|
|
1007
949
|
.map((p) => path.basename(p))
|
|
1008
950
|
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
1009
951
|
}
|
|
952
|
+
/** Adapter over {@link partitionRulesForDir}; that module carries the reasoning. */
|
|
953
|
+
export function partition_rules_for_dir(tool_dir, rules, user_home = process.env['HOME'] ?? os.homedir()) {
|
|
954
|
+
return partitionRulesForDir({
|
|
955
|
+
toolDir: tool_dir, rules, userHome: user_home, announce: _print,
|
|
956
|
+
projectRoot: MODULE_STATE.PROJECT_ROOT, rulesSource: MODULE_STATE.RULES_SOURCE,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
1010
959
|
/**
|
|
1011
960
|
* The emit plan of {@link generate_rule_symlinks}: active tool dir → the exact
|
|
1012
961
|
* rule basenames it projects there.
|
|
@@ -1029,8 +978,12 @@ export function projected_rule_trees(user_home = process.env['HOME'] ?? os.homed
|
|
|
1029
978
|
const dedup_on = _read_projection_scope_dedup();
|
|
1030
979
|
const out = {};
|
|
1031
980
|
for (const tool_dir of Object.keys(_filter_tool_dirs(TOOL_DIRS))) {
|
|
1032
|
-
const skip = dedup_on
|
|
1033
|
-
|
|
981
|
+
const skip = dedup_on
|
|
982
|
+
? dedupableRules({ toolDir: tool_dir, rules, userHome: user_home,
|
|
983
|
+
rulesSource: MODULE_STATE.RULES_SOURCE, announce: _print })
|
|
984
|
+
: new Set();
|
|
985
|
+
const kept = rules.filter((r) => !skip.has(r));
|
|
986
|
+
out[tool_dir] = partition_rules_for_dir(tool_dir, kept, user_home);
|
|
1034
987
|
}
|
|
1035
988
|
return out;
|
|
1036
989
|
}
|
|
@@ -1137,7 +1090,10 @@ export function generate_rule_symlinks() {
|
|
|
1137
1090
|
return total;
|
|
1138
1091
|
}
|
|
1139
1092
|
export function generate_windsurfrules() {
|
|
1140
|
-
|
|
1093
|
+
// Windsurf's LEGACY single-file surface — same host, so the same evidence, keyed
|
|
1094
|
+
// on `.windsurf/rules`. Without this line removing the per-run filter regressed
|
|
1095
|
+
// it 13 → 113: the per-run filter had been covering it by accident.
|
|
1096
|
+
const rules = partition_rules_for_dir('.windsurf/rules', _scoped_rule_basenames());
|
|
1141
1097
|
const parts = ['# Auto-generated from dist/agent-src/rules/ — do not edit directly\n'];
|
|
1142
1098
|
for (const rule of rules) {
|
|
1143
1099
|
const p = path.join(MODULE_STATE.RULES_SOURCE, rule);
|
|
@@ -1188,34 +1144,6 @@ export function _parse_frontmatter(content) {
|
|
|
1188
1144
|
function _yaml_scalar(value) {
|
|
1189
1145
|
return JSON.stringify(value);
|
|
1190
1146
|
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Derive host-native activation globs from a rule's `triggers:` frontmatter
|
|
1193
|
-
* (road-to-request-scoped-rule-load Phase 2). `file_pattern` triggers map
|
|
1194
|
-
* verbatim; `path_prefix` triggers map to `<prefix>**`. Keyword / phrase /
|
|
1195
|
-
* command triggers produce no glob — those rules stay
|
|
1196
|
-
* description-activated (Cursor Agent-Requested / Windsurf model_decision).
|
|
1197
|
-
*/
|
|
1198
|
-
export function derive_trigger_globs(meta) {
|
|
1199
|
-
const triggers = meta['triggers'];
|
|
1200
|
-
if (!Array.isArray(triggers)) {
|
|
1201
|
-
return [];
|
|
1202
|
-
}
|
|
1203
|
-
const globs = [];
|
|
1204
|
-
for (const t of triggers) {
|
|
1205
|
-
if (t === null || typeof t !== 'object' || Array.isArray(t))
|
|
1206
|
-
continue;
|
|
1207
|
-
const obj = t;
|
|
1208
|
-
if (typeof obj['file_pattern'] === 'string' && obj['file_pattern']) {
|
|
1209
|
-
globs.push(obj['file_pattern']);
|
|
1210
|
-
}
|
|
1211
|
-
else if (typeof obj['path_prefix'] === 'string' && obj['path_prefix']) {
|
|
1212
|
-
const prefix = obj['path_prefix'];
|
|
1213
|
-
globs.push(prefix.endsWith('/') ? `${prefix}**` : `${prefix}**`);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
// De-dup, keep declaration order (deterministic output).
|
|
1217
|
-
return [...new Set(globs)];
|
|
1218
|
-
}
|
|
1219
1147
|
export function _emit_cursor_mdc(source, target) {
|
|
1220
1148
|
const [meta, body] = _parse_frontmatter(_readText(source));
|
|
1221
1149
|
const description = _strip(String(meta['description'] ?? '').replace(/\n/g, ' '));
|
|
@@ -1255,141 +1183,6 @@ export function _emit_windsurf_rule(source, target) {
|
|
|
1255
1183
|
_mkdirp(path.dirname(target));
|
|
1256
1184
|
_writeText(target, lines.join('\n'));
|
|
1257
1185
|
}
|
|
1258
|
-
/**
|
|
1259
|
-
* A brace group the host expands, versus an agent-config placeholder that only
|
|
1260
|
-
* LOOKS like one.
|
|
1261
|
-
*
|
|
1262
|
-
* `path_prefix: "{module_root}/"` in `roadmap-ci-steps-policy` is resolved by
|
|
1263
|
-
* this suite from `modules.root_paths`. Emitted verbatim into a host `paths:`
|
|
1264
|
-
* list it is a brace group with no comma, which the host expands to the literal
|
|
1265
|
-
* string `{module_root}` — a pattern that matches no file. The rule would then
|
|
1266
|
-
* reach the model **never** instead of on-demand, and nothing would say so: the
|
|
1267
|
-
* probed contract records this exact degradation as silent
|
|
1268
|
-
* (`claude-code-rules-dir-contract.md` § constraints).
|
|
1269
|
-
*
|
|
1270
|
-
* Dropping the pattern is fail-safe in the one direction that matters. A rule
|
|
1271
|
-
* left with no pattern at all carries no `paths:` key and loads
|
|
1272
|
-
* unconditionally — the status quo, not a regression. A rule carrying a
|
|
1273
|
-
* no-op pattern is invisible.
|
|
1274
|
-
*
|
|
1275
|
-
* The discriminator is the comma, not a placeholder allowlist: `{ts,tsx}` is a
|
|
1276
|
-
* real alternation, `{module_root}` cannot be one.
|
|
1277
|
-
*/
|
|
1278
|
-
export function _is_unresolved_placeholder(pattern) {
|
|
1279
|
-
for (const m of pattern.matchAll(/\{([^}]*)\}/g)) {
|
|
1280
|
-
if (!m[1].includes(',')) {
|
|
1281
|
-
return true;
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
return false;
|
|
1285
|
-
}
|
|
1286
|
-
/**
|
|
1287
|
-
* `[` opens a bracket expression for the host. An unparseable one makes that
|
|
1288
|
-
* pattern match nothing while the rule's other patterns keep working — so a
|
|
1289
|
-
* literal `[` in a trigger is escaped rather than trusted.
|
|
1290
|
-
*/
|
|
1291
|
-
export function _escape_claude_bracket(pattern) {
|
|
1292
|
-
return pattern.replace(/\[/g, '\\[');
|
|
1293
|
-
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Expanded-pattern cost of one glob: the product of its brace alternations.
|
|
1296
|
-
* The host budgets a rule's whole `paths:` list at 1,000 expanded patterns and
|
|
1297
|
-
* 4 MiB, and a list over budget is used UNEXPANDED — braces then match
|
|
1298
|
-
* literally, i.e. nothing. Counting keeps the emitter under the cliff instead
|
|
1299
|
-
* of discovering it as a silent no-op.
|
|
1300
|
-
*/
|
|
1301
|
-
export function _expanded_pattern_count(pattern) {
|
|
1302
|
-
let n = 1;
|
|
1303
|
-
for (const m of pattern.matchAll(/\{([^}]*)\}/g)) {
|
|
1304
|
-
n *= m[1].split(',').length;
|
|
1305
|
-
}
|
|
1306
|
-
return n;
|
|
1307
|
-
}
|
|
1308
|
-
/** Host-documented budget for one rule's whole `paths:` list. */
|
|
1309
|
-
export const CLAUDE_PATHS_PATTERN_BUDGET = 1000;
|
|
1310
|
-
/**
|
|
1311
|
-
* Does this rule declare any trigger that is NOT path-shaped?
|
|
1312
|
-
*
|
|
1313
|
-
* Load-bearing for `_claude_paths_plan` below, because one `triggers:` list
|
|
1314
|
-
* means two different things on two hosts. Cursor and Windsurf treat globs as
|
|
1315
|
-
* ADDITIVE — a rule with empty globs still reaches the agent through its
|
|
1316
|
-
* description (see `_emit_cursor_mdc`'s own comment). Claude Code treats
|
|
1317
|
-
* `paths:` as EXCLUSIVE: with it the rule loads on a path match; without it the
|
|
1318
|
-
* rule loads unconditionally. The emitter fed both semantics from one list, so
|
|
1319
|
-
* a rule that declared keywords *and* one path glob silently became
|
|
1320
|
-
* path-gated-only on Claude.
|
|
1321
|
-
*/
|
|
1322
|
-
export function _has_non_path_trigger(meta) {
|
|
1323
|
-
const triggers = meta['triggers'];
|
|
1324
|
-
if (!Array.isArray(triggers))
|
|
1325
|
-
return false;
|
|
1326
|
-
for (const t of triggers) {
|
|
1327
|
-
if (t === null || typeof t !== 'object' || Array.isArray(t))
|
|
1328
|
-
continue;
|
|
1329
|
-
const obj = t;
|
|
1330
|
-
if (typeof obj['keyword'] === 'string' && obj['keyword'])
|
|
1331
|
-
return true;
|
|
1332
|
-
if (typeof obj['phrase'] === 'string' && obj['phrase'])
|
|
1333
|
-
return true;
|
|
1334
|
-
}
|
|
1335
|
-
return false;
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Derive the host's `paths:` list for one rule, with every drop recorded.
|
|
1339
|
-
*
|
|
1340
|
-
* Kernel / always-apply rules get an EMPTY list on purpose: absent `paths:` is
|
|
1341
|
-
* how this host says "load unconditionally", which is exactly what an Iron-Law
|
|
1342
|
-
* rule needs. Scoping one would be a correctness regression dressed as a byte
|
|
1343
|
-
* saving — and the same contract notes path-scoped rules are not re-injected
|
|
1344
|
-
* after `/compact`, so an obligation that must survive compaction cannot be
|
|
1345
|
-
* path-scoped at all.
|
|
1346
|
-
*/
|
|
1347
|
-
export function _claude_paths_plan(meta) {
|
|
1348
|
-
const always_apply = Boolean(meta['alwaysApply'] || meta['type'] === 'always');
|
|
1349
|
-
if (always_apply) {
|
|
1350
|
-
return { globs: [], dropped: [] };
|
|
1351
|
-
}
|
|
1352
|
-
const globs = [];
|
|
1353
|
-
const dropped = [];
|
|
1354
|
-
// A rule that ALSO declares keyword / phrase triggers gets no `paths:` at
|
|
1355
|
-
// all, and therefore keeps loading unconditionally on Claude Code.
|
|
1356
|
-
//
|
|
1357
|
-
// Emitting `paths:` here would narrow the rule to a path match and discard
|
|
1358
|
-
// every keyword the author wrote, because this host reads the list as the
|
|
1359
|
-
// whole gate rather than as one way in. Measured before this guard: 25 of
|
|
1360
|
-
// 110 projected rules carried `paths:` and 19 of them also carried keyword
|
|
1361
|
-
// triggers — `design-fidelity` lost 21 of them, in exactly the pasted-
|
|
1362
|
-
// screenshot and capability-URL handovers its own routing section names as
|
|
1363
|
-
// the classes it exists to catch. Nothing in the tree recorded a decision
|
|
1364
|
-
// to make that trade, which is why it is treated as a defect.
|
|
1365
|
-
//
|
|
1366
|
-
// The drop is REPORTED rather than silent, so a rule whose obligation
|
|
1367
|
-
// really is path-bound is visible as a candidate for removing its keyword
|
|
1368
|
-
// triggers — the narrowing then becomes an authoring decision instead of an
|
|
1369
|
-
// emitter side effect. Cursor and Windsurf are untouched: globs are
|
|
1370
|
-
// additive there, so the same rule keeps both routes.
|
|
1371
|
-
if (_has_non_path_trigger(meta)) {
|
|
1372
|
-
for (const raw of derive_trigger_globs(meta)) {
|
|
1373
|
-
dropped.push({ pattern: raw, reason: 'mixed-triggers' });
|
|
1374
|
-
}
|
|
1375
|
-
return { globs, dropped };
|
|
1376
|
-
}
|
|
1377
|
-
let spent = 0;
|
|
1378
|
-
for (const raw of derive_trigger_globs(meta)) {
|
|
1379
|
-
if (_is_unresolved_placeholder(raw)) {
|
|
1380
|
-
dropped.push({ pattern: raw, reason: 'unresolved-placeholder' });
|
|
1381
|
-
continue;
|
|
1382
|
-
}
|
|
1383
|
-
const cost = _expanded_pattern_count(raw);
|
|
1384
|
-
if (spent + cost > CLAUDE_PATHS_PATTERN_BUDGET) {
|
|
1385
|
-
dropped.push({ pattern: raw, reason: 'over-budget' });
|
|
1386
|
-
continue;
|
|
1387
|
-
}
|
|
1388
|
-
spent += cost;
|
|
1389
|
-
globs.push(_escape_claude_bracket(raw));
|
|
1390
|
-
}
|
|
1391
|
-
return { globs, dropped };
|
|
1392
|
-
}
|
|
1393
1186
|
/**
|
|
1394
1187
|
* Emit a Claude Code rule carrying the host's OWN activation key.
|
|
1395
1188
|
*
|
|
@@ -1455,12 +1248,16 @@ function _clean_modern_dir(target_dir, valid_names) {
|
|
|
1455
1248
|
export function generate_cursor_mdc_rules() {
|
|
1456
1249
|
const scope = _read_rule_workspaces();
|
|
1457
1250
|
const pack_scope = _read_rule_packs();
|
|
1458
|
-
|
|
1251
|
+
let rules = _rglobSorted(MODULE_STATE.RULES_SOURCE, '*.md')
|
|
1459
1252
|
.filter((p) => _isFile(p))
|
|
1460
1253
|
.filter((p) => rule_in_scope(p, scope, pack_scope))
|
|
1461
1254
|
// ADR-004 manual rules are reference-only — never auto-injected into a
|
|
1462
1255
|
// per-tool tree. Same predicate as generate_rule_symlinks.
|
|
1463
1256
|
.filter((p) => !_is_manual_rule(p));
|
|
1257
|
+
// ADR-236 per-host partition (src/install/ruleLayerPartition.ts). Narrowing the
|
|
1258
|
+
// list is a REMOVAL, because `_clean_modern_dir` below sweeps to `valid`.
|
|
1259
|
+
const kept = new Set(partition_rules_for_dir('.cursor/rules', rules.map((r) => path.basename(r))));
|
|
1260
|
+
rules = rules.filter((r) => kept.has(path.basename(r)));
|
|
1464
1261
|
const stems = rules.map((r) => path.basename(r, '.md'));
|
|
1465
1262
|
const valid = new Set([
|
|
1466
1263
|
...stems.map((s) => `${s}.mdc`),
|
|
@@ -1476,12 +1273,15 @@ export function generate_cursor_mdc_rules() {
|
|
|
1476
1273
|
export function generate_windsurf_modern_rules() {
|
|
1477
1274
|
const scope = _read_rule_workspaces();
|
|
1478
1275
|
const pack_scope = _read_rule_packs();
|
|
1479
|
-
|
|
1276
|
+
let rules = _rglobSorted(MODULE_STATE.RULES_SOURCE, '*.md')
|
|
1480
1277
|
.filter((p) => _isFile(p))
|
|
1481
1278
|
.filter((p) => rule_in_scope(p, scope, pack_scope))
|
|
1482
1279
|
// ADR-004 manual rules are reference-only — never auto-injected into a
|
|
1483
1280
|
// per-tool tree. Same predicate as generate_rule_symlinks.
|
|
1484
1281
|
.filter((p) => !_is_manual_rule(p));
|
|
1282
|
+
// ADR-236 per-host partition — same shape as the cursor emitter above.
|
|
1283
|
+
const kept = new Set(partition_rules_for_dir('.windsurf/rules', rules.map((r) => path.basename(r))));
|
|
1284
|
+
rules = rules.filter((r) => kept.has(path.basename(r)));
|
|
1485
1285
|
const valid = new Set(rules.map((r) => path.basename(r)));
|
|
1486
1286
|
_clean_modern_dir(_WINDSURF_RULES_DIR(), valid);
|
|
1487
1287
|
for (const rule of rules) {
|
|
@@ -1652,6 +1452,8 @@ function _render_native_model_md(src_md, tier) {
|
|
|
1652
1452
|
return text.replace(_MODEL_TIER_RE, `model: ${model}`);
|
|
1653
1453
|
}
|
|
1654
1454
|
export function generate_claude_skills(active_skill_names = null) {
|
|
1455
|
+
if (partitionActive(MODULE_STATE.PROJECT_ROOT))
|
|
1456
|
+
active_skill_names = new Set();
|
|
1655
1457
|
if (!_exists(MODULE_STATE.SKILLS_SOURCE)) {
|
|
1656
1458
|
process.stderr.write(' ⚠️ dist/agent-src/skills/ not found — skipping skills\n');
|
|
1657
1459
|
return 0;
|
|
@@ -1664,7 +1466,9 @@ export function generate_claude_skills(active_skill_names = null) {
|
|
|
1664
1466
|
skills = skills.filter((s) => active_skill_names.has(s));
|
|
1665
1467
|
}
|
|
1666
1468
|
const skill_set = new Set(skills);
|
|
1667
|
-
const command_slugs =
|
|
1469
|
+
const command_slugs = partitionActive(MODULE_STATE.PROJECT_ROOT)
|
|
1470
|
+
? new Set()
|
|
1471
|
+
: new Set([...iterCommands()].map(([, slug]) => slug));
|
|
1668
1472
|
_mkdirp(MODULE_STATE.CLAUDE_SKILLS_DIR);
|
|
1669
1473
|
const auto = _read_model_auto_switch() === 'auto';
|
|
1670
1474
|
for (const item of _iterdirSorted(MODULE_STATE.CLAUDE_SKILLS_DIR)) {
|
|
@@ -1752,23 +1556,24 @@ function _nested_command_subpath(source_file) {
|
|
|
1752
1556
|
/**
|
|
1753
1557
|
* Project-scope `.claude/commands/<cluster>/<sub>.md` — the colon form.
|
|
1754
1558
|
*
|
|
1755
|
-
* Before this, the package repo's own `.claude/` carried NO commands
|
|
1756
|
-
*
|
|
1757
|
-
*
|
|
1758
|
-
*
|
|
1759
|
-
*
|
|
1760
|
-
*
|
|
1761
|
-
* of always-loaded catalog for zero added reach — measured 2026-08-02.
|
|
1559
|
+
* Before this, the package repo's own `.claude/` carried NO commands directory:
|
|
1560
|
+
* every command reached Claude Code as a hyphen-named skill wrapper in
|
|
1561
|
+
* `.claude/skills/`, while the colon form existed only in the user-global tree
|
|
1562
|
+
* written by `install.ts`. Every clustered command was therefore listed TWICE
|
|
1563
|
+
* for anyone with a global install (`roadmap-process-full` AND
|
|
1564
|
+
* `roadmap:process-full`), costing 4,214 GPT tok of catalog — measured 2026-08-02.
|
|
1762
1565
|
*
|
|
1763
1566
|
* Emitting the nested form here lets `generate_claude_commands` stop wrapping
|
|
1764
1567
|
* the clustered commands: one listing, same reachability, and reachability no
|
|
1765
|
-
* longer depends on a global deploy
|
|
1766
|
-
* and user scope by name, so the two copies of `/cluster:sub` collapse.
|
|
1568
|
+
* longer depends on a global deploy. Dedup AND precedence: `commandsWithheld`.
|
|
1767
1569
|
*
|
|
1768
1570
|
* ADR-003 (colon canonical for clusters) and ADR-044 (flat commands stay
|
|
1769
1571
|
* hyphenated) both hold: flat commands are untouched and keep their wrapper.
|
|
1770
1572
|
*/
|
|
1771
1573
|
export function generate_claude_project_commands(active_command_slugs = null) {
|
|
1574
|
+
if (commandsWithheld(MODULE_STATE.PROJECT_ROOT))
|
|
1575
|
+
active_command_slugs = new Set();
|
|
1576
|
+
// Returns before the stale sweep — invariant, see `commandsWithheld`.
|
|
1772
1577
|
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1773
1578
|
return 0;
|
|
1774
1579
|
}
|
|
@@ -1784,8 +1589,7 @@ export function generate_claude_project_commands(active_command_slugs = null) {
|
|
|
1784
1589
|
}
|
|
1785
1590
|
const target_root = _CLAUDE_COMMANDS_DIR();
|
|
1786
1591
|
const valid = new Set(nested.map(([, sub]) => `${sub}.md`));
|
|
1787
|
-
// Sweep stale links first
|
|
1788
|
-
// a dangling `/cluster:sub`.
|
|
1592
|
+
// Sweep stale links first — a renamed, deleted or WITHHELD command must not linger.
|
|
1789
1593
|
if (_exists(target_root)) {
|
|
1790
1594
|
for (const item of _rglobSorted(target_root, '*.md')) {
|
|
1791
1595
|
const rel = _relativeToPosix(item, target_root);
|
|
@@ -1793,6 +1597,7 @@ export function generate_claude_project_commands(active_command_slugs = null) {
|
|
|
1793
1597
|
fs.unlinkSync(item);
|
|
1794
1598
|
}
|
|
1795
1599
|
}
|
|
1600
|
+
pruneEmptyDirs(target_root); // else the gate counts 40 empty cluster dirs as 40 commands
|
|
1796
1601
|
}
|
|
1797
1602
|
let count = 0;
|
|
1798
1603
|
for (const [source_file, sub] of nested) {
|
|
@@ -1812,6 +1617,8 @@ export function generate_claude_project_commands(active_command_slugs = null) {
|
|
|
1812
1617
|
return count;
|
|
1813
1618
|
}
|
|
1814
1619
|
export function generate_claude_commands(active_command_slugs = null) {
|
|
1620
|
+
if (partitionActive(MODULE_STATE.PROJECT_ROOT))
|
|
1621
|
+
active_command_slugs = new Set();
|
|
1815
1622
|
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1816
1623
|
process.stderr.write(' ⚠️ src/domains/ not found — skipping commands\n');
|
|
1817
1624
|
return 0;
|
|
@@ -2228,7 +2035,7 @@ export function generate_persona_symlinks() {
|
|
|
2228
2035
|
.map((p) => path.basename(p))
|
|
2229
2036
|
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
2230
2037
|
void personas;
|
|
2231
|
-
const
|
|
2038
|
+
const partition = personaPartition(MODULE_STATE.PROJECT_ROOT, direct);
|
|
2232
2039
|
const tool_dirs = _filter_tool_dirs(PERSONA_TOOL_DIRS);
|
|
2233
2040
|
let total = 0;
|
|
2234
2041
|
for (const [tool_dir, rel_prefix] of Object.entries(tool_dirs)) {
|
|
@@ -2236,11 +2043,11 @@ export function generate_persona_symlinks() {
|
|
|
2236
2043
|
_mkdirp(target_dir);
|
|
2237
2044
|
for (const item of _iterdirSorted(target_dir)) {
|
|
2238
2045
|
const name = path.basename(item);
|
|
2239
|
-
if (_isSymlink(item) && !
|
|
2046
|
+
if (_isSymlink(item) && !partition.listFor(tool_dir).includes(name) && name !== 'README.md') {
|
|
2240
2047
|
fs.unlinkSync(item);
|
|
2241
2048
|
}
|
|
2242
2049
|
}
|
|
2243
|
-
for (const persona of
|
|
2050
|
+
for (const persona of partition.listFor(tool_dir)) {
|
|
2244
2051
|
const link = path.join(target_dir, persona);
|
|
2245
2052
|
const target = path.join(rel_prefix, persona);
|
|
2246
2053
|
if (_existsOrSymlink(link)) {
|
|
@@ -2250,7 +2057,8 @@ export function generate_persona_symlinks() {
|
|
|
2250
2057
|
total += 1;
|
|
2251
2058
|
}
|
|
2252
2059
|
}
|
|
2253
|
-
|
|
2060
|
+
const per_dir = Object.keys(tool_dirs).map((d) => `${d}=${partition.countFor(d)}`);
|
|
2061
|
+
info(` ✅ Created ${total} persona symlinks — ${per_dir.join(' · ')}${partition.note}`);
|
|
2254
2062
|
return total;
|
|
2255
2063
|
}
|
|
2256
2064
|
export function generate_user_type_symlinks() {
|
|
@@ -2423,10 +2231,14 @@ export function project_to_augment() {
|
|
|
2423
2231
|
const current = new Set();
|
|
2424
2232
|
let written = 0;
|
|
2425
2233
|
if (_exists(src_rules)) {
|
|
2426
|
-
|
|
2234
|
+
// ADR-236 per-host partition, ahead of the `use_symlinks` branch so both
|
|
2235
|
+
// writers narrow; the `existing` minus `current` sweep below makes it a
|
|
2236
|
+
// removal. This emitter never had the scope or ADR-004 manual filters
|
|
2237
|
+
// either — why its count is 15 and the symlink trees' 13. Pre-existing.
|
|
2238
|
+
const rules = partition_rules_for_dir('.augment/rules', _iterdirSorted(src_rules)
|
|
2427
2239
|
.filter((p) => p.endsWith('.md') && _isFile(p))
|
|
2428
2240
|
.map((p) => path.basename(p))
|
|
2429
|
-
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
2241
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
2430
2242
|
for (const rule of rules) {
|
|
2431
2243
|
const target = path.join(dst_rules, rule);
|
|
2432
2244
|
if (_isSymlink(target) || _existsOrSymlink(target)) {
|