@event4u/agent-config 14.20.0 → 14.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +441 -0
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/analyze/inbox.md +381 -6
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +251 -34
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +15 -0
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-BIXEDQPZ.js} +7 -1
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +19 -5
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +27 -3
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +60 -29
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +1522 -1008
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +10 -1
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +39376 -38902
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/dist/scripts/condense.js +32 -25
- package/docs/MIGRATION.md +49 -1
- package/docs/contracts/settings-classes.md +1 -1
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/llms.txt +14 -14
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/continuity-surface.json +509 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/config/release-gate-locality.yml +4 -3
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +1 -1
- package/src/scripts/_lib/host_capability.ts +38 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +168 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_branch_work_committed.ts +234 -0
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_pack_size.ts +263 -24
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/code_graph/build.ts +138 -15
- package/src/scripts/code_graph/cli.ts +76 -8
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +121 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +247 -6
- package/src/scripts/code_graph/types.ts +66 -1
- package/src/scripts/code_graph/validate.ts +23 -0
- package/src/scripts/condense.ts +34 -22
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +29 -39
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/handoff_context_hook.ts +79 -0
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +94 -12
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
- package/src/scripts/hooks/concern_registry.ts +6 -2
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/rule_inject_hook.ts +1 -1
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +134 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/lint_roadmap_blockers.ts +51 -1
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tools.ts +2 -56
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release.ts +26 -71
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/release_publication.ts +34 -29
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/self_review_gate.ts +409 -15
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/vendor/grammars/README.md +61 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Every shipped skill, and the entry paths that can reach it.
|
|
4
|
+
*
|
|
5
|
+
* `road-to-skill-menu-economy` step 1.1. The roadmap's lever is COUNT ON THE
|
|
6
|
+
* MENU — the description corpus already sits at the schema cap, so there is no
|
|
7
|
+
* byte-shaving budget to spend (K2). Deciding which skills may leave the model
|
|
8
|
+
* menu needs to know, per skill, whether anything other than the menu can
|
|
9
|
+
* reach it. This report answers that mechanically and publishes the rule it
|
|
10
|
+
* used, so a later reader can disagree with the rule rather than with a number.
|
|
11
|
+
*
|
|
12
|
+
* WHAT IT IS NOT
|
|
13
|
+
* --------------
|
|
14
|
+
* It is a REPORT, deliberately: no threshold, no exit code that carries a
|
|
15
|
+
* verdict, and a `report_` prefix so `_lib/gate_population.ts` classifies it
|
|
16
|
+
* out of the gate population rather than by whoever last edited a manifest.
|
|
17
|
+
*
|
|
18
|
+
* It also does NOT decide "sole entry path", and that limit is load-bearing
|
|
19
|
+
* rather than an apology. A command reference proves a command CAN reach the
|
|
20
|
+
* skill; it cannot prove the menu never does. The roadmap's own Risk 1 is
|
|
21
|
+
* precisely this — marking a skill `user-invocable: false` is correct only if
|
|
22
|
+
* the command or flow path really is the only one — so this report produces
|
|
23
|
+
* the evidence for that judgement and never the judgement.
|
|
24
|
+
*
|
|
25
|
+
* THE CLASSIFICATION RULE, stated because it is the whole content
|
|
26
|
+
* -------------------------------------------------------------
|
|
27
|
+
* For each `src/skills/<name>/SKILL.md`, three signals are read:
|
|
28
|
+
*
|
|
29
|
+
* command_refs files under `src/domains/**\/command.md` that reference the
|
|
30
|
+
* skill in one of three DISAMBIGUATING shapes — `skill:<name>`,
|
|
31
|
+
* a `skills/<name>` path, or the bare name in backticks. A
|
|
32
|
+
* plain word-boundary match is deliberately NOT used: skill
|
|
33
|
+
* names like `security`, `database` and `docker` are ordinary
|
|
34
|
+
* English and would hit on every prose mention.
|
|
35
|
+
* flow_refs files under `src/flows/` whose own `skills:` list names it.
|
|
36
|
+
* Flows carry YAML name lists rather than the shapes above, so
|
|
37
|
+
* the command regex read 0 for all 299 on the first pass — a
|
|
38
|
+
* false zero, and the reason `flowSkillNames` exists.
|
|
39
|
+
* menu_present true unless the frontmatter carries `user-invocable: false`
|
|
40
|
+
* or `disable-model-invocation: true` — the two menu-economy
|
|
41
|
+
* keys the roadmap measures as nearly unused.
|
|
42
|
+
*
|
|
43
|
+
* and the row is classified:
|
|
44
|
+
*
|
|
45
|
+
* both command_refs > 0 AND flow_refs > 0
|
|
46
|
+
* command-only command_refs > 0, flow_refs = 0
|
|
47
|
+
* flow-only flow_refs > 0, command_refs = 0
|
|
48
|
+
* model-routed neither, and the skill is on the menu — the menu is then the
|
|
49
|
+
* only entry path this repository can point at
|
|
50
|
+
* orphan neither, and the skill is off the menu: nothing in the tree
|
|
51
|
+
* reaches it. Reported, never deleted (K3).
|
|
52
|
+
*
|
|
53
|
+
* `command-only` and `flow-only` name a CANDIDATE for `user-invocable: false`,
|
|
54
|
+
* never a decision — see the sole-entry limit above.
|
|
55
|
+
*
|
|
56
|
+
* Usage:
|
|
57
|
+
* ./scripts-run src/scripts/report_skill_menu_census # stdout summary
|
|
58
|
+
* ./scripts-run src/scripts/report_skill_menu_census --emit # write the artifact
|
|
59
|
+
* ./scripts-run src/scripts/report_skill_menu_census --emit --pin <sha>
|
|
60
|
+
*
|
|
61
|
+
* Deterministic: rows sort by skill name, no timestamp is written, and the pin
|
|
62
|
+
* is an argument rather than "now", so a re-run at the same pin over the same
|
|
63
|
+
* tree is byte-identical.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
import { execFileSync } from 'node:child_process';
|
|
67
|
+
import * as fs from 'node:fs';
|
|
68
|
+
import * as path from 'node:path';
|
|
69
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
70
|
+
|
|
71
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
72
|
+
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
73
|
+
export const ARTIFACT_REL = path.join('agents', 'evidence', 'analysis', 'skill-menu-census-2026-09.md');
|
|
74
|
+
|
|
75
|
+
export type EntryPath = 'both' | 'command-only' | 'flow-only' | 'model-routed' | 'orphan';
|
|
76
|
+
|
|
77
|
+
export interface SkillRow {
|
|
78
|
+
readonly name: string;
|
|
79
|
+
readonly commandRefs: number;
|
|
80
|
+
readonly flowRefs: number;
|
|
81
|
+
readonly menuPresent: boolean;
|
|
82
|
+
readonly hasTriggerCorpus: boolean;
|
|
83
|
+
readonly entryPath: EntryPath;
|
|
84
|
+
/** One command or flow file, for an operator to start from. Empty when none. */
|
|
85
|
+
readonly firstRef: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Escape a skill name for use inside a RegExp. Names are kebab-case, so this is cheap insurance. */
|
|
89
|
+
function escapeRe(s: string): string {
|
|
90
|
+
return s.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The three disambiguating reference shapes, as one regex.
|
|
95
|
+
*
|
|
96
|
+
* A bare word-boundary match was tried first and rejected on inspection: 299
|
|
97
|
+
* skill names include `security`, `database`, `docker`, `performance` and
|
|
98
|
+
* `mode`, each of which appears in ordinary command prose. A reference that
|
|
99
|
+
* cannot be told from a sentence is not evidence of an entry path.
|
|
100
|
+
*/
|
|
101
|
+
export function referenceRe(skill: string): RegExp {
|
|
102
|
+
const n = escapeRe(skill);
|
|
103
|
+
const tick = String.fromCharCode(96);
|
|
104
|
+
return new RegExp(
|
|
105
|
+
'skill:\\s*' + n + '(?![a-z0-9-])' +
|
|
106
|
+
'|skills/' + n + '(?:[/\\s' + tick + ')\\]]|$)' +
|
|
107
|
+
'|' + tick + n + tick,
|
|
108
|
+
'u',
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Every file under `dir` matching `pred`, sorted, as repo-relative paths. */
|
|
113
|
+
export function walk(root: string, dir: string, pred: (rel: string) => boolean): string[] {
|
|
114
|
+
const out: string[] = [];
|
|
115
|
+
const abs = path.join(root, dir);
|
|
116
|
+
if (!fs.existsSync(abs)) return out;
|
|
117
|
+
const stack: string[] = [dir];
|
|
118
|
+
while (stack.length > 0) {
|
|
119
|
+
const cur = stack.pop() as string;
|
|
120
|
+
let entries: fs.Dirent[];
|
|
121
|
+
try {
|
|
122
|
+
entries = fs.readdirSync(path.join(root, cur), { withFileTypes: true });
|
|
123
|
+
} catch {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
for (const e of entries) {
|
|
127
|
+
const rel = path.join(cur, e.name);
|
|
128
|
+
if (e.isDirectory()) stack.push(rel);
|
|
129
|
+
else if (e.isFile() && pred(rel)) out.push(rel);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return out.sort();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Frontmatter values this report needs. Hand-parsed: only two boolean keys matter. */
|
|
136
|
+
export function menuFlags(text: string): { userInvocable: boolean | null; disableModel: boolean | null } {
|
|
137
|
+
const head = text.slice(0, 4000);
|
|
138
|
+
const ui = /^user-invocable:\s*(true|false)\s*$/mu.exec(head);
|
|
139
|
+
const dm = /^disable-model-invocation:\s*(true|false)\s*$/mu.exec(head);
|
|
140
|
+
return {
|
|
141
|
+
userInvocable: ui === null ? null : ui[1] === 'true',
|
|
142
|
+
disableModel: dm === null ? null : dm[1] === 'true',
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The skill names a flow file declares, from its own `skills:` lists.
|
|
148
|
+
*
|
|
149
|
+
* Flows do NOT use the three reference shapes commands use — they carry YAML
|
|
150
|
+
* lists of bare names (`skills: [code-review, adversarial-review]`, or a block
|
|
151
|
+
* sequence). Running the command-shaped regex over them returned flow_refs = 0
|
|
152
|
+
* for all 299 skills on the first pass, which was a false zero and is why this
|
|
153
|
+
* parser exists rather than a wider word-boundary match: a bare name is safe
|
|
154
|
+
* HERE precisely because the `skills:` key bounds where it may be read.
|
|
155
|
+
*/
|
|
156
|
+
export function flowSkillNames(text: string): Set<string> {
|
|
157
|
+
const out = new Set<string>();
|
|
158
|
+
const lines = text.split('\n');
|
|
159
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
160
|
+
const line = lines[i] as string;
|
|
161
|
+
const inline = /^\s*skills:\s*\[([^\]]*)\]\s*$/u.exec(line);
|
|
162
|
+
if (inline !== null) {
|
|
163
|
+
for (const raw of (inline[1] as string).split(',')) {
|
|
164
|
+
const n = raw.trim().replace(/^['"]|['"]$/gu, '');
|
|
165
|
+
if (n !== '') out.add(n);
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (!/^\s*skills:\s*$/u.test(line)) continue;
|
|
170
|
+
const indent = (/^\s*/u.exec(line) as RegExpExecArray)[0].length;
|
|
171
|
+
for (let j = i + 1; j < lines.length; j += 1) {
|
|
172
|
+
const item = /^(\s*)-\s*(\S.*?)\s*$/u.exec(lines[j] as string);
|
|
173
|
+
if (item === null || (item[1] as string).length <= indent) break;
|
|
174
|
+
out.add((item[2] as string).replace(/^['"]|['"]$/gu, ''));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function classify(commandRefs: number, flowRefs: number, menuPresent: boolean): EntryPath {
|
|
181
|
+
if (commandRefs > 0 && flowRefs > 0) return 'both';
|
|
182
|
+
if (commandRefs > 0) return 'command-only';
|
|
183
|
+
if (flowRefs > 0) return 'flow-only';
|
|
184
|
+
return menuPresent ? 'model-routed' : 'orphan';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function census(root: string): SkillRow[] {
|
|
188
|
+
const skillsDir = path.join(root, 'src', 'skills');
|
|
189
|
+
const names = fs
|
|
190
|
+
.readdirSync(skillsDir, { withFileTypes: true })
|
|
191
|
+
.filter((e) => e.isDirectory() && fs.existsSync(path.join(skillsDir, e.name, 'SKILL.md')))
|
|
192
|
+
.map((e) => e.name)
|
|
193
|
+
.sort();
|
|
194
|
+
|
|
195
|
+
const commandFiles = walk(root, path.join('src', 'domains'), (rel) => path.basename(rel) === 'command.md');
|
|
196
|
+
const flowFiles = walk(root, path.join('src', 'flows'), () => true);
|
|
197
|
+
const commandText = commandFiles.map((rel) => [rel, fs.readFileSync(path.join(root, rel), 'utf-8')] as const);
|
|
198
|
+
const flowDecl = flowFiles.map(
|
|
199
|
+
(rel) => [rel, flowSkillNames(fs.readFileSync(path.join(root, rel), 'utf-8'))] as const,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const rows: SkillRow[] = [];
|
|
203
|
+
for (const name of names) {
|
|
204
|
+
const skillMd = path.join(skillsDir, name, 'SKILL.md');
|
|
205
|
+
const text = fs.readFileSync(skillMd, 'utf-8');
|
|
206
|
+
const flags = menuFlags(text);
|
|
207
|
+
const menuPresent = flags.userInvocable !== false && flags.disableModel !== true;
|
|
208
|
+
const re = referenceRe(name);
|
|
209
|
+
const cmdHits = commandText.filter(([, t]) => re.test(t)).map(([rel]) => rel);
|
|
210
|
+
const flowHits = flowDecl.filter(([, names]) => names.has(name)).map(([rel]) => rel);
|
|
211
|
+
rows.push({
|
|
212
|
+
name,
|
|
213
|
+
commandRefs: cmdHits.length,
|
|
214
|
+
flowRefs: flowHits.length,
|
|
215
|
+
menuPresent,
|
|
216
|
+
hasTriggerCorpus: fs.existsSync(path.join(skillsDir, name, 'evals', 'triggers.json')),
|
|
217
|
+
entryPath: classify(cmdHits.length, flowHits.length, menuPresent),
|
|
218
|
+
firstRef: cmdHits[0] ?? flowHits[0] ?? '',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return rows;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function tally(rows: readonly SkillRow[]): Record<EntryPath, number> {
|
|
225
|
+
const t: Record<EntryPath, number> = {
|
|
226
|
+
both: 0,
|
|
227
|
+
'command-only': 0,
|
|
228
|
+
'flow-only': 0,
|
|
229
|
+
'model-routed': 0,
|
|
230
|
+
orphan: 0,
|
|
231
|
+
};
|
|
232
|
+
for (const r of rows) t[r.entryPath] += 1;
|
|
233
|
+
return t;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function renderArtifact(rows: readonly SkillRow[], pin: string): string {
|
|
237
|
+
const t = tally(rows);
|
|
238
|
+
const candidates = t['command-only'] + t['flow-only'];
|
|
239
|
+
const L: string[] = [];
|
|
240
|
+
L.push('<!-- evidence-type: analysis -->');
|
|
241
|
+
L.push('# Skill menu census — every skill, and what can reach it');
|
|
242
|
+
L.push('');
|
|
243
|
+
L.push(`Pinned to commit \`${pin}\`. Generated by \`./scripts-run src/scripts/report_skill_menu_census --emit --pin ${pin}\`;`);
|
|
244
|
+
L.push('re-running it at the same pin over the same tree is byte-identical, which is what');
|
|
245
|
+
L.push('`road-to-skill-menu-economy` step 1.1 asks to be checkable.');
|
|
246
|
+
L.push('');
|
|
247
|
+
L.push('## The rule this census applied');
|
|
248
|
+
L.push('');
|
|
249
|
+
L.push('**Commands.** A `src/domains/**/command.md` file counts as reaching a skill when it');
|
|
250
|
+
L.push('references it in one of three disambiguating shapes — `skill:<name>`, a `skills/<name>`');
|
|
251
|
+
L.push('path, or the bare name in backticks. A plain word-boundary match is NOT used: names');
|
|
252
|
+
L.push('like `security`, `database` and `docker` are ordinary English and would hit on every');
|
|
253
|
+
L.push('prose mention. The `skill:<name>` shape is closed with a negative lookahead rather');
|
|
254
|
+
L.push('than a word boundary: `\\b` sits between `w` and `-`, so `skill:code-review-lens` was a');
|
|
255
|
+
L.push('hit for `code-review` and a skill would have inherited a longer sibling\'s references.');
|
|
256
|
+
L.push('Tightening it moved no figure in this corpus, which is recorded rather than assumed.');
|
|
257
|
+
L.push('');
|
|
258
|
+
L.push('**Flows.** A `src/flows/*.yaml` file counts when its own `skills:` list names the skill.');
|
|
259
|
+
L.push('Flows carry YAML name lists rather than the shapes above, so applying the command regex');
|
|
260
|
+
L.push('to them returned `flow_refs = 0` for all 299 skills on the first pass. That was a false');
|
|
261
|
+
L.push('zero, corrected before publication; a bare name is safe here precisely because the');
|
|
262
|
+
L.push('`skills:` key bounds where it may be read.');
|
|
263
|
+
L.push('');
|
|
264
|
+
L.push('**Menu.** `menu_present` is true unless the frontmatter carries `user-invocable: false`');
|
|
265
|
+
L.push('or `disable-model-invocation: true`.');
|
|
266
|
+
L.push('');
|
|
267
|
+
L.push('| Class | Meaning |');
|
|
268
|
+
L.push('|---|---|');
|
|
269
|
+
L.push('| `both` | reachable from a command AND from a flow |');
|
|
270
|
+
L.push('| `command-only` | a command references it; no flow does |');
|
|
271
|
+
L.push('| `flow-only` | a flow references it; no command does |');
|
|
272
|
+
L.push('| `model-routed` | neither, and it is on the menu — the menu is the only entry path this tree can point at |');
|
|
273
|
+
L.push('| `orphan` | neither, and it is off the menu: nothing here reaches it |');
|
|
274
|
+
L.push('');
|
|
275
|
+
L.push('## What this census does NOT establish');
|
|
276
|
+
L.push('');
|
|
277
|
+
L.push('It does not decide **sole entry path**. A command reference proves a command CAN');
|
|
278
|
+
L.push('reach the skill; nothing here proves the menu never does. `command-only` and');
|
|
279
|
+
L.push('`flow-only` are therefore CANDIDATES for `user-invocable: false` and never the');
|
|
280
|
+
L.push('marking decision — the roadmap\'s Risk 1 is exactly this misclassification, and its');
|
|
281
|
+
L.push('stated mitigation is a successful command invocation per marked skill, which is a');
|
|
282
|
+
L.push('step 1.2 obligation and not something a static scan can discharge.');
|
|
283
|
+
L.push('');
|
|
284
|
+
L.push('An `orphan` row is a question, not a deletion list (K3).');
|
|
285
|
+
L.push('');
|
|
286
|
+
L.push('## Totals');
|
|
287
|
+
L.push('');
|
|
288
|
+
L.push('| Class | Count |');
|
|
289
|
+
L.push('|---|---:|');
|
|
290
|
+
for (const k of ['both', 'command-only', 'flow-only', 'model-routed', 'orphan'] as EntryPath[]) {
|
|
291
|
+
L.push(`| \`${k}\` | ${String(t[k])} |`);
|
|
292
|
+
}
|
|
293
|
+
L.push(`| **total** | **${String(rows.length)}** |`);
|
|
294
|
+
L.push('');
|
|
295
|
+
L.push(`Menu-removal candidates (\`command-only\` + \`flow-only\`): **${String(candidates)}**.`);
|
|
296
|
+
L.push('');
|
|
297
|
+
L.push('## Rows');
|
|
298
|
+
L.push('');
|
|
299
|
+
L.push('| Skill | Entry path | cmd refs | flow refs | on menu | triggers.json | first reference |');
|
|
300
|
+
L.push('|---|---|---:|---:|:-:|:-:|---|');
|
|
301
|
+
for (const r of rows) {
|
|
302
|
+
L.push(
|
|
303
|
+
`| \`${r.name}\` | \`${r.entryPath}\` | ${String(r.commandRefs)} | ${String(r.flowRefs)} | ` +
|
|
304
|
+
`${r.menuPresent ? 'yes' : 'no'} | ${r.hasTriggerCorpus ? 'yes' : 'no'} | ` +
|
|
305
|
+
`${r.firstRef === '' ? '—' : `\`${r.firstRef}\``} |`,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
L.push('');
|
|
309
|
+
return L.join('\n');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function headSha(root: string): string {
|
|
313
|
+
return execFileSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf-8' }).trim();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function main(argv: readonly string[]): number {
|
|
317
|
+
const root = REPO_ROOT;
|
|
318
|
+
const emit = argv.includes('--emit');
|
|
319
|
+
const pinIdx = argv.indexOf('--pin');
|
|
320
|
+
const pin = pinIdx !== -1 && pinIdx + 1 < argv.length ? (argv[pinIdx + 1] as string) : headSha(root);
|
|
321
|
+
const rows = census(root);
|
|
322
|
+
const t = tally(rows);
|
|
323
|
+
if (emit) {
|
|
324
|
+
const out = path.join(root, ARTIFACT_REL);
|
|
325
|
+
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
326
|
+
fs.writeFileSync(out, renderArtifact(rows, pin), 'utf-8');
|
|
327
|
+
process.stdout.write(`wrote ${ARTIFACT_REL} · ${String(rows.length)} row(s) · pin ${pin}\n`);
|
|
328
|
+
}
|
|
329
|
+
process.stdout.write(`scanned: ${String(rows.length)} skill(s)\n`);
|
|
330
|
+
for (const k of ['both', 'command-only', 'flow-only', 'model-routed', 'orphan'] as EntryPath[]) {
|
|
331
|
+
process.stdout.write(` ${k.padEnd(14)} ${String(t[k]).padStart(4)}\n`);
|
|
332
|
+
}
|
|
333
|
+
process.stdout.write(
|
|
334
|
+
` menu-removal candidates (command-only + flow-only): ${String(t['command-only'] + t['flow-only'])}\n`,
|
|
335
|
+
);
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
340
|
+
process.exit(main(process.argv.slice(2)));
|
|
341
|
+
}
|