@event4u/agent-config 9.10.0 → 9.11.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 +59 -0
- package/README.md +12 -15
- package/agents/templates/.ai-council.yml.example +481 -0
- package/agents/templates/.ai-video.xml.example +247 -0
- package/dist/agent-src/rules/design-fidelity.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +15 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +10 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +30 -30
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +20 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +20 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +179 -0
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +155 -4
- package/dist/agent-src/templates/scripts/work_engine/state.ts +21 -4
- package/dist/cli/initRouting.js +74 -11
- package/dist/cli/initRouting.js.map +1 -1
- package/dist/cli/main.js +14 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/cli-delegate/chunk-4SV3XGFA.js +6910 -0
- package/dist/cli-delegate/chunk-736JCWLM.js +1104 -0
- package/dist/cli-delegate/chunk-7DSAMZFU.js +356 -0
- package/dist/cli-delegate/chunk-7HQ52DHU.js +74 -0
- package/dist/cli-delegate/chunk-BJGXZNPM.js +266 -0
- package/dist/cli-delegate/chunk-FTAISGDQ.js +117 -0
- package/dist/cli-delegate/chunk-GME3Q44L.js +82 -0
- package/dist/cli-delegate/chunk-GRRPEPRN.js +336 -0
- package/dist/cli-delegate/chunk-HJ6DAJHR.js +502 -0
- package/dist/cli-delegate/chunk-IR7P7JWA.js +872 -0
- package/dist/cli-delegate/chunk-J55WI3RR.js +47 -0
- package/dist/cli-delegate/chunk-MCJYCQPF.js +35 -0
- package/dist/cli-delegate/chunk-NDJLNC6Q.js +169 -0
- package/dist/cli-delegate/chunk-OMIVVCG2.js +26 -0
- package/dist/cli-delegate/chunk-OY53J6II.js +7336 -0
- package/dist/cli-delegate/chunk-RDGRHAAB.js +259 -0
- package/dist/cli-delegate/chunk-TFG57JJM.js +1969 -0
- package/dist/cli-delegate/chunk-XX6TOPD7.js +715 -0
- package/dist/cli-delegate/chunk-YMFO6FDN.js +10603 -0
- package/dist/cli-delegate/cmd_analyze_session.js +294 -0
- package/dist/cli-delegate/cmd_conformance.js +646 -0
- package/dist/cli-delegate/cmd_converge.js +326 -0
- package/dist/cli-delegate/cmd_doctor.js +144 -0
- package/dist/cli-delegate/cmd_explain.js +2508 -0
- package/dist/cli-delegate/cmd_export.js +296 -0
- package/dist/cli-delegate/cmd_fleet.js +332 -0
- package/dist/cli-delegate/cmd_memory_get.js +3878 -0
- package/dist/cli-delegate/cmd_migrate.js +11 -0
- package/dist/cli-delegate/cmd_preflight.js +270 -0
- package/dist/cli-delegate/cmd_prune.js +444 -0
- package/dist/cli-delegate/cmd_refresh.js +28 -0
- package/dist/cli-delegate/cmd_settings_check.js +574 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +223 -0
- package/dist/cli-delegate/cmd_sync.js +264 -0
- package/dist/cli-delegate/cmd_uninstall.js +656 -0
- package/dist/cli-delegate/cmd_update.js +425 -0
- package/dist/cli-delegate/cmd_upgrade.js +444 -0
- package/dist/cli-delegate/cmd_validate.js +305 -0
- package/dist/cli-delegate/cmd_versions.js +370 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +14 -7
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +2 -2
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +12 -7
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +4 -3
- package/dist/install/emit_host_rules_cli.js +2 -1
- package/dist/install/emit_host_rules_cli.js.map +1 -1
- package/dist/install/install.mjs +410 -378
- package/dist/install/rule_scope.js +5 -1
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/install/toolDetection.js +40 -12
- package/dist/install/toolDetection.js.map +1 -1
- package/dist/install/wizard-plan.js +2 -2
- package/dist/install/wizard-plan.js.map +1 -1
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1614 -765
- package/dist/router.json +1 -1
- package/package.json +4 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +30 -30
- package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +88 -33
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +20 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +20 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +179 -0
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +155 -4
- package/src/agent-src/templates/scripts/work_engine/state.ts +21 -4
- package/src/config/discovery/packs.yml +7 -0
- package/src/config/evaluator-budgets.json +1 -1
- package/src/config/pack-size-budget.json +27 -0
- package/src/install/emit_host_rules_cli.ts +2 -1
- package/src/install/rule_scope.ts +5 -1
- package/src/install/toolDetection.ts +39 -8
- package/src/install/wizard-plan.ts +2 -2
- package/src/scripts/_cli/cmd_conformance.ts +2 -1
- package/src/scripts/_cli/cmd_converge.ts +2 -1
- package/src/scripts/_cli/cmd_doctor.ts +214 -13
- package/src/scripts/_cli/cmd_export.ts +2 -1
- package/src/scripts/_cli/cmd_fleet.ts +2 -1
- package/src/scripts/_cli/cmd_migrate.ts +2 -1
- package/src/scripts/_cli/cmd_preflight.ts +2 -1
- package/src/scripts/_cli/cmd_refresh.ts +2 -1
- package/src/scripts/_cli/cmd_update.ts +2 -1
- package/src/scripts/_cli/cmd_upgrade.ts +2 -1
- package/src/scripts/_cli/cmd_versions.ts +2 -1
- package/src/scripts/_cli/detection_report.ts +480 -0
- package/src/scripts/_dispatch.bash +52 -14
- package/src/scripts/_lib/changelog_eras.ts +103 -9
- package/src/scripts/_lib/environment_detector.ts +441 -0
- package/src/scripts/_lib/package_root.ts +72 -0
- package/src/scripts/ai_council/config.ts +15 -7
- package/src/scripts/ai_council/modes.ts +68 -2
- package/src/scripts/ai_council/transport_resolver.ts +331 -0
- package/src/scripts/check_bundle_path_leakage.ts +10 -4
- package/src/scripts/check_dependency_floors.ts +153 -0
- package/src/scripts/check_pack_size.ts +188 -0
- package/src/scripts/council_cli.ts +6 -2
- package/src/scripts/evaluator_umbrella.sh +34 -1
- package/src/scripts/lint_eval_fixture_citations.ts +113 -0
- package/src/scripts/lint_ui_stack_bundles.ts +170 -0
|
@@ -0,0 +1,1969 @@
|
|
|
1
|
+
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
artefact_roots,
|
|
4
|
+
iter_all_sources,
|
|
5
|
+
resolve_logical,
|
|
6
|
+
strip_source_prefix
|
|
7
|
+
} from "./chunk-BJGXZNPM.js";
|
|
8
|
+
import {
|
|
9
|
+
MODEL_TIER_RE,
|
|
10
|
+
TIER_TO_CLAUDE_MODEL,
|
|
11
|
+
build_claude_hook_matrix,
|
|
12
|
+
is_claude_builtin_name
|
|
13
|
+
} from "./chunk-7DSAMZFU.js";
|
|
14
|
+
import {
|
|
15
|
+
load_agent_settings,
|
|
16
|
+
project_settings_path
|
|
17
|
+
} from "./chunk-HJ6DAJHR.js";
|
|
18
|
+
import {
|
|
19
|
+
require_dist
|
|
20
|
+
} from "./chunk-OY53J6II.js";
|
|
21
|
+
import {
|
|
22
|
+
__toESM
|
|
23
|
+
} from "./chunk-J55WI3RR.js";
|
|
24
|
+
|
|
25
|
+
// src/install/rule_scope.ts
|
|
26
|
+
import * as fs2 from "node:fs";
|
|
27
|
+
import * as path2 from "node:path";
|
|
28
|
+
|
|
29
|
+
// src/scripts/condense.ts
|
|
30
|
+
var YAML = __toESM(require_dist(), 1);
|
|
31
|
+
import * as fs from "node:fs";
|
|
32
|
+
import * as os from "node:os";
|
|
33
|
+
import * as path from "node:path";
|
|
34
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
35
|
+
|
|
36
|
+
// src/scripts/_lib/compile_time_toggles.ts
|
|
37
|
+
var COMPILE_TIME_TOGGLES = {
|
|
38
|
+
// `speak` defaults FALSE (dormant) per docs/adrs/telegraph/0002 — the absent-key
|
|
39
|
+
// fallback used to be `true`, which silently contradicted ADR 0001 (accepted,
|
|
40
|
+
// "default off until bench"). Measured basis: median vs_terse −9.27% (API) /
|
|
41
|
+
// −5.47% (exact cl100k_base) — telegraph emits MORE than a plain "be terse".
|
|
42
|
+
// Opt back in with an explicit `telegraph.speak: true` once an output-side bench
|
|
43
|
+
// clears the kill-criterion bar. `enabled` keeps its true default: it is the
|
|
44
|
+
// family master switch, and flipping it would silence sibling telegraph settings
|
|
45
|
+
// that were never measured negative.
|
|
46
|
+
//
|
|
47
|
+
// This predicate deliberately does NOT read `speak_scope`. That key disables the
|
|
48
|
+
// rule's *behaviour* and never removed its token cost — the trap the measurement
|
|
49
|
+
// exposed.
|
|
50
|
+
"telegraph-speak": (s) => {
|
|
51
|
+
const tg = s["telegraph"] ?? {};
|
|
52
|
+
const enabled = tg["enabled"] === void 0 ? true : tg["enabled"];
|
|
53
|
+
const speak = tg["speak"] === void 0 ? false : tg["speak"];
|
|
54
|
+
return Boolean(enabled) && Boolean(speak);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
function rule_is_compile_enabled(rule_id, settings) {
|
|
58
|
+
const predicate = COMPILE_TIME_TOGGLES[rule_id];
|
|
59
|
+
return predicate === void 0 ? true : predicate(settings);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/scripts/_lib/script_output.ts
|
|
63
|
+
var VALID_LEVELS = ["silent", "minimal", "verbose"];
|
|
64
|
+
var DEFAULT_LEVEL = "minimal";
|
|
65
|
+
var ENV_VAR = "AGENT_SCRIPT_VERBOSITY";
|
|
66
|
+
var ENV_ALIAS = "SCRIPT_OUTPUT_VERBOSE";
|
|
67
|
+
var SETTINGS_FILE = ".agent-settings.yml";
|
|
68
|
+
var _resolved_level = null;
|
|
69
|
+
var _pending_summary = [];
|
|
70
|
+
function _read_settings_level(settings_path) {
|
|
71
|
+
const data = load_agent_settings({ project_path: settings_path });
|
|
72
|
+
const section = data["verbosity"];
|
|
73
|
+
if (typeof section !== "object" || section === null || Array.isArray(section)) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const value = section["script_output"];
|
|
77
|
+
if (typeof value === "string" && VALID_LEVELS.includes(value)) {
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
function resolve_level(settings_path = null) {
|
|
83
|
+
if (_resolved_level !== null) {
|
|
84
|
+
return _resolved_level;
|
|
85
|
+
}
|
|
86
|
+
const env_value = (process.env[ENV_VAR] ?? "").trim().toLowerCase();
|
|
87
|
+
if (VALID_LEVELS.includes(env_value)) {
|
|
88
|
+
_resolved_level = env_value;
|
|
89
|
+
} else if ((process.env[ENV_ALIAS] ?? "").trim() === "1") {
|
|
90
|
+
_resolved_level = "verbose";
|
|
91
|
+
} else {
|
|
92
|
+
const p = settings_path ?? SETTINGS_FILE;
|
|
93
|
+
_resolved_level = _read_settings_level(p) ?? DEFAULT_LEVEL;
|
|
94
|
+
}
|
|
95
|
+
process.env[ENV_VAR] = _resolved_level;
|
|
96
|
+
return _resolved_level;
|
|
97
|
+
}
|
|
98
|
+
function info(message) {
|
|
99
|
+
if (resolve_level() === "verbose") {
|
|
100
|
+
process.stdout.write(`${message}
|
|
101
|
+
`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function success(message) {
|
|
105
|
+
const level = resolve_level();
|
|
106
|
+
if (level === "verbose") {
|
|
107
|
+
process.stdout.write(`${message}
|
|
108
|
+
`);
|
|
109
|
+
} else if (level === "minimal") {
|
|
110
|
+
_pending_summary.push(message);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function flush_summary(headline = null) {
|
|
114
|
+
const level = resolve_level();
|
|
115
|
+
if (level !== "minimal" || _pending_summary.length === 0) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (headline) {
|
|
119
|
+
process.stdout.write(`${headline}
|
|
120
|
+
`);
|
|
121
|
+
} else {
|
|
122
|
+
process.stdout.write(`${_pending_summary[_pending_summary.length - 1]}
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
_pending_summary.length = 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/scripts/condense.ts
|
|
129
|
+
var _SLUG_PREFIX_RE = /^slug_prefix:\s*"?([a-z][a-z0-9-]*)"?\s*$/m;
|
|
130
|
+
function _yamlParse(text) {
|
|
131
|
+
try {
|
|
132
|
+
const data = YAML.parse(text, { version: "1.1" });
|
|
133
|
+
return data === void 0 ? null : data;
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function _exists(p) {
|
|
139
|
+
try {
|
|
140
|
+
fs.lstatSync(p);
|
|
141
|
+
fs.statSync(p);
|
|
142
|
+
return true;
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function _existsOrSymlink(p) {
|
|
148
|
+
try {
|
|
149
|
+
fs.lstatSync(p);
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function _isDir(p) {
|
|
156
|
+
try {
|
|
157
|
+
return fs.statSync(p).isDirectory();
|
|
158
|
+
} catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function _isFile(p) {
|
|
163
|
+
try {
|
|
164
|
+
return fs.statSync(p).isFile();
|
|
165
|
+
} catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function _isSymlink(p) {
|
|
170
|
+
try {
|
|
171
|
+
return fs.lstatSync(p).isSymbolicLink();
|
|
172
|
+
} catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function _isDirNoFollow(p) {
|
|
177
|
+
try {
|
|
178
|
+
return fs.lstatSync(p).isDirectory();
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function _readText(p) {
|
|
184
|
+
return fs.readFileSync(p, "utf-8");
|
|
185
|
+
}
|
|
186
|
+
function _writeText(p, text) {
|
|
187
|
+
fs.writeFileSync(p, text, "utf-8");
|
|
188
|
+
}
|
|
189
|
+
function _mkdirp(p) {
|
|
190
|
+
fs.mkdirSync(p, { recursive: true });
|
|
191
|
+
}
|
|
192
|
+
function _iterdirSorted(p) {
|
|
193
|
+
let names;
|
|
194
|
+
try {
|
|
195
|
+
names = fs.readdirSync(p);
|
|
196
|
+
} catch {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
names.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
200
|
+
return names.map((n) => path.join(p, n));
|
|
201
|
+
}
|
|
202
|
+
function _rglobSorted(root, pattern) {
|
|
203
|
+
const out = [];
|
|
204
|
+
const suffix = pattern === "*" ? null : pattern.slice(1);
|
|
205
|
+
const walk = (dir) => {
|
|
206
|
+
let entries;
|
|
207
|
+
try {
|
|
208
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
209
|
+
} catch {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
for (const ent of entries) {
|
|
213
|
+
const full = path.join(dir, ent.name);
|
|
214
|
+
const matches = suffix === null ? true : ent.name.endsWith(suffix);
|
|
215
|
+
if (matches) {
|
|
216
|
+
out.push(full);
|
|
217
|
+
}
|
|
218
|
+
if (ent.isDirectory() || ent.isSymbolicLink() && _isDir(full)) {
|
|
219
|
+
walk(full);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
walk(root);
|
|
224
|
+
out.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
function _relativeToPosix(child, root) {
|
|
228
|
+
const rel = path.relative(root, child);
|
|
229
|
+
return rel.split(path.sep).join("/");
|
|
230
|
+
}
|
|
231
|
+
function _isUnder(child, root) {
|
|
232
|
+
const rel = path.relative(root, child);
|
|
233
|
+
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
234
|
+
}
|
|
235
|
+
function _posixParts(p) {
|
|
236
|
+
return p.split("/").filter((s) => s.length > 0);
|
|
237
|
+
}
|
|
238
|
+
function _lstrip(s) {
|
|
239
|
+
return s.replace(/^\s+/u, "");
|
|
240
|
+
}
|
|
241
|
+
function _strip(s) {
|
|
242
|
+
return s.replace(/^\s+/u, "").replace(/\s+$/u, "");
|
|
243
|
+
}
|
|
244
|
+
function _lstripNewlines(s) {
|
|
245
|
+
return s.replace(/^\n+/, "");
|
|
246
|
+
}
|
|
247
|
+
function _stripChars(s, chars) {
|
|
248
|
+
let start = 0;
|
|
249
|
+
let end = s.length;
|
|
250
|
+
while (start < end && chars.includes(s[start])) start += 1;
|
|
251
|
+
while (end > start && chars.includes(s[end - 1])) end -= 1;
|
|
252
|
+
return s.slice(start, end);
|
|
253
|
+
}
|
|
254
|
+
var _HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
255
|
+
var _DEFAULT_PROJECT_ROOT = path.resolve(_HERE, "..", "..");
|
|
256
|
+
function _deriveState(root) {
|
|
257
|
+
return {
|
|
258
|
+
PROJECT_ROOT: root,
|
|
259
|
+
SOURCE_DIR: path.join(root, ".agent-src.uncondensed"),
|
|
260
|
+
TARGET_DIR: path.join(root, "dist/agent-src"),
|
|
261
|
+
AUGMENT_DIR: path.join(root, ".augment"),
|
|
262
|
+
SETTINGS_FILE: project_settings_path(root),
|
|
263
|
+
RULES_SOURCE: path.join(root, "dist/agent-src", "rules"),
|
|
264
|
+
SKILLS_SOURCE: path.join(root, "dist/agent-src", "skills"),
|
|
265
|
+
COMMANDS_SOURCE: path.join(root, "dist/agent-src", "commands"),
|
|
266
|
+
PERSONAS_SOURCE: path.join(root, "dist/agent-src", "personas"),
|
|
267
|
+
USER_TYPES_SOURCE: path.join(root, "dist/agent-src", "user-types"),
|
|
268
|
+
CLAUDE_SKILLS_DIR: path.join(root, ".claude", "skills"),
|
|
269
|
+
// ADR-109: subagents project from src/ directly (NOT dist/) — a subagent
|
|
270
|
+
// is an executable system prompt Claude Code runs verbatim; telegraph
|
|
271
|
+
// condensation would alter its behaviour, and it is not routed through the
|
|
272
|
+
// pack/discovery condensation flow.
|
|
273
|
+
CLAUDE_AGENTS_DIR: path.join(root, ".claude", "agents"),
|
|
274
|
+
SUBAGENTS_SOURCE: path.join(root, "src", "subagents"),
|
|
275
|
+
PLUGIN_SKILLS_DIR: path.join(root, ".claude-plugin", "skills"),
|
|
276
|
+
iter_all_sources,
|
|
277
|
+
resolve_logical,
|
|
278
|
+
artefact_roots
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
var MODULE_STATE = _deriveState(_DEFAULT_PROJECT_ROOT);
|
|
282
|
+
function _iter_sources() {
|
|
283
|
+
return MODULE_STATE.iter_all_sources();
|
|
284
|
+
}
|
|
285
|
+
function _resolve_source(relative2) {
|
|
286
|
+
return MODULE_STATE.resolve_logical(relative2);
|
|
287
|
+
}
|
|
288
|
+
function _any_source_root_exists() {
|
|
289
|
+
return [...MODULE_STATE.artefact_roots()].length > 0;
|
|
290
|
+
}
|
|
291
|
+
function _active_tools() {
|
|
292
|
+
const tools_file = path.join(MODULE_STATE.PROJECT_ROOT, "agents", ".agent-tools.yml");
|
|
293
|
+
if (!_exists(tools_file)) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
let data = _yamlParse(_readText(tools_file));
|
|
297
|
+
if (data === null || data === void 0) {
|
|
298
|
+
data = {};
|
|
299
|
+
}
|
|
300
|
+
const tools = typeof data === "object" && data !== null && !Array.isArray(data) ? data["tools"] : null;
|
|
301
|
+
if (!Array.isArray(tools)) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return new Set(tools.filter((t) => typeof t === "string"));
|
|
305
|
+
}
|
|
306
|
+
function _tool_active(tool_id) {
|
|
307
|
+
const active = _active_tools();
|
|
308
|
+
return active === null ? true : active.has(tool_id);
|
|
309
|
+
}
|
|
310
|
+
var COPY_AS_IS = /* @__PURE__ */ new Set(["README.md"]);
|
|
311
|
+
var COPY_AS_IS_DIRS = /* @__PURE__ */ new Set(["ghostwriter"]);
|
|
312
|
+
function _read_augment_rules_use_symlinks() {
|
|
313
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
314
|
+
const augment = data["augment"];
|
|
315
|
+
if (typeof augment !== "object" || augment === null || Array.isArray(augment)) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
const value = augment["rules_use_symlinks"];
|
|
319
|
+
if (typeof value === "boolean") {
|
|
320
|
+
return value;
|
|
321
|
+
}
|
|
322
|
+
if (typeof value === "string") {
|
|
323
|
+
return ["true", "yes", "on", "1"].includes(value.trim().toLowerCase());
|
|
324
|
+
}
|
|
325
|
+
if (typeof value === "number") {
|
|
326
|
+
return value === 1;
|
|
327
|
+
}
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
var USER_SCOPE_RULE_DIRS = {
|
|
331
|
+
".claude/rules": path.join(".claude", "rules")
|
|
332
|
+
};
|
|
333
|
+
function _read_projection_scope_dedup() {
|
|
334
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
335
|
+
const projection = data["projection"];
|
|
336
|
+
if (typeof projection !== "object" || projection === null || Array.isArray(projection)) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
const value = projection["scope_dedup"];
|
|
340
|
+
if (typeof value === "boolean") {
|
|
341
|
+
return value;
|
|
342
|
+
}
|
|
343
|
+
if (typeof value === "string") {
|
|
344
|
+
return ["true", "yes", "on", "1"].includes(value.trim().toLowerCase());
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
function _dedupable_rules(tool_dir, rules, userHome) {
|
|
349
|
+
const relative2 = USER_SCOPE_RULE_DIRS[tool_dir];
|
|
350
|
+
if (relative2 === void 0) {
|
|
351
|
+
return /* @__PURE__ */ new Set();
|
|
352
|
+
}
|
|
353
|
+
let userDirStat;
|
|
354
|
+
const userDir = path.join(userHome, relative2);
|
|
355
|
+
try {
|
|
356
|
+
userDirStat = fs.statSync(userDir);
|
|
357
|
+
} catch {
|
|
358
|
+
return /* @__PURE__ */ new Set();
|
|
359
|
+
}
|
|
360
|
+
if (!userDirStat.isDirectory()) {
|
|
361
|
+
return /* @__PURE__ */ new Set();
|
|
362
|
+
}
|
|
363
|
+
if ((userDirStat.mode & 2) !== 0) {
|
|
364
|
+
_print(` \u26A0\uFE0F ${tool_dir}: user-scope rules dir is world-writable \u2014 scope-dedup skipped`);
|
|
365
|
+
return /* @__PURE__ */ new Set();
|
|
366
|
+
}
|
|
367
|
+
const skip = /* @__PURE__ */ new Set();
|
|
368
|
+
for (const rule of rules) {
|
|
369
|
+
const twin = path.join(userDir, rule);
|
|
370
|
+
const source = path.join(MODULE_STATE.RULES_SOURCE, rule);
|
|
371
|
+
try {
|
|
372
|
+
if (!_isFile(twin) || !_isFile(source)) continue;
|
|
373
|
+
if (fs.readFileSync(twin).equals(fs.readFileSync(source))) {
|
|
374
|
+
skip.add(rule);
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return skip;
|
|
380
|
+
}
|
|
381
|
+
function _lean_projection_mode() {
|
|
382
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
383
|
+
const lean = data["lean_projection"];
|
|
384
|
+
if (typeof lean === "object" && lean !== null && !Array.isArray(lean) && String(lean["mode"] ?? "").trim().toLowerCase() === "thin") {
|
|
385
|
+
return "thin";
|
|
386
|
+
}
|
|
387
|
+
return "eager-all";
|
|
388
|
+
}
|
|
389
|
+
function apply_path_rewriter(relative_path) {
|
|
390
|
+
const target = path.join(MODULE_STATE.TARGET_DIR, relative_path);
|
|
391
|
+
if (!_exists(target) || !relative_path.endsWith(".md")) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
const original = _readText(target);
|
|
395
|
+
const rewritten = _rewrite_paths(original, relative_path);
|
|
396
|
+
if (rewritten === original) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
_writeText(target, rewritten);
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
function is_projection_stale(relative2) {
|
|
403
|
+
const source = _resolve_source(relative2);
|
|
404
|
+
if (source === null) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
if (skip_compile_disabled_rule(relative2)) {
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
const target = path.join(MODULE_STATE.TARGET_DIR, relative2);
|
|
411
|
+
if (!_exists(target)) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
return _readText(target) !== _rewrite_paths(_readText(source), relative2);
|
|
415
|
+
}
|
|
416
|
+
function list_changed_md(_source_dir) {
|
|
417
|
+
const changed = [];
|
|
418
|
+
const seen = /* @__PURE__ */ new Set();
|
|
419
|
+
for (const [, relative2] of _iter_sources()) {
|
|
420
|
+
if (seen.has(relative2)) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
seen.add(relative2);
|
|
424
|
+
const source = _resolve_source(relative2);
|
|
425
|
+
if (source === null || !should_condense(source)) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (is_projection_stale(relative2)) {
|
|
429
|
+
changed.push(relative2);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return changed;
|
|
433
|
+
}
|
|
434
|
+
function should_condense(filepath) {
|
|
435
|
+
if (path.extname(filepath) !== ".md") {
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
if (COPY_AS_IS.has(path.basename(filepath))) {
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
let rel_parts = filepath.split(path.sep).filter((s) => s.length > 0);
|
|
442
|
+
for (const root of MODULE_STATE.artefact_roots()) {
|
|
443
|
+
if (filepath !== root && !_isUnder(filepath, root)) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
rel_parts = _relativeToPosix(filepath, root).split("/");
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
if (rel_parts.length > 0 && COPY_AS_IS_DIRS.has(rel_parts[0])) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
function copy_file(source, target) {
|
|
455
|
+
_mkdirp(path.dirname(target));
|
|
456
|
+
fs.copyFileSync(source, target);
|
|
457
|
+
try {
|
|
458
|
+
const st = fs.statSync(source);
|
|
459
|
+
fs.utimesSync(target, st.atime, st.mtime);
|
|
460
|
+
fs.chmodSync(target, st.mode);
|
|
461
|
+
} catch {
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function cleanup_stale(_source_dir, target_dir) {
|
|
465
|
+
let deleted = 0;
|
|
466
|
+
if (!_exists(target_dir)) {
|
|
467
|
+
return 0;
|
|
468
|
+
}
|
|
469
|
+
for (const target_file of _rglobSorted(target_dir, "*")) {
|
|
470
|
+
if (_isDir(target_file)) {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
const relative2 = _relativeToPosix(target_file, target_dir);
|
|
474
|
+
if (_resolve_source(relative2) === null) {
|
|
475
|
+
_print(` Deleting stale: ${relative2}`);
|
|
476
|
+
fs.unlinkSync(target_file);
|
|
477
|
+
deleted += 1;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const dirs = _rglobSorted(target_dir, "*").filter((p) => _isDir(p)).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
|
|
481
|
+
for (const dirpath of dirs) {
|
|
482
|
+
try {
|
|
483
|
+
if (fs.readdirSync(dirpath).length === 0) {
|
|
484
|
+
fs.rmdirSync(dirpath);
|
|
485
|
+
_print(` Removing empty dir: ${_relativeToPosix(dirpath, target_dir)}`);
|
|
486
|
+
}
|
|
487
|
+
} catch {
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return deleted;
|
|
491
|
+
}
|
|
492
|
+
var COPY_MD_VERBATIM = true;
|
|
493
|
+
function skip_compile_disabled_rule(relative2) {
|
|
494
|
+
const posix = relative2.split(path.sep).join("/");
|
|
495
|
+
if (!posix.startsWith("rules/") || !posix.endsWith(".md")) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
const rule_id = path.basename(posix, ".md");
|
|
499
|
+
const settings = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
500
|
+
return !rule_is_compile_enabled(rule_id, settings);
|
|
501
|
+
}
|
|
502
|
+
function sync_non_md(_source_dir, target_dir) {
|
|
503
|
+
let copied = 0;
|
|
504
|
+
const seen = /* @__PURE__ */ new Set();
|
|
505
|
+
for (const [source_file, relative2] of _iter_sources()) {
|
|
506
|
+
if (!COPY_MD_VERBATIM && should_condense(source_file)) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (seen.has(relative2)) {
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (skip_compile_disabled_rule(relative2)) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
seen.add(relative2);
|
|
516
|
+
const target_file = path.join(target_dir, relative2);
|
|
517
|
+
copy_file(source_file, target_file);
|
|
518
|
+
if (COPY_MD_VERBATIM && relative2.endsWith(".md")) {
|
|
519
|
+
apply_path_rewriter(relative2);
|
|
520
|
+
}
|
|
521
|
+
_print(` Copied: ${relative2}`);
|
|
522
|
+
copied += 1;
|
|
523
|
+
}
|
|
524
|
+
return copied;
|
|
525
|
+
}
|
|
526
|
+
function list_md_files(_source_dir) {
|
|
527
|
+
const files = [];
|
|
528
|
+
const seen = /* @__PURE__ */ new Set();
|
|
529
|
+
for (const [source_file, relative2] of _iter_sources()) {
|
|
530
|
+
if (!should_condense(source_file)) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (seen.has(relative2)) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
seen.add(relative2);
|
|
537
|
+
files.push(relative2);
|
|
538
|
+
}
|
|
539
|
+
return files.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
540
|
+
}
|
|
541
|
+
function check_sync(_source_dir, target_dir) {
|
|
542
|
+
const missing = [];
|
|
543
|
+
const stale = [];
|
|
544
|
+
const seen = /* @__PURE__ */ new Set();
|
|
545
|
+
for (const [, relative2] of _iter_sources()) {
|
|
546
|
+
if (seen.has(relative2)) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
seen.add(relative2);
|
|
550
|
+
if (skip_compile_disabled_rule(relative2)) {
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (!_exists(path.join(target_dir, relative2))) {
|
|
554
|
+
missing.push(relative2);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (_exists(target_dir)) {
|
|
558
|
+
for (const target_file of _rglobSorted(target_dir, "*")) {
|
|
559
|
+
if (_isDir(target_file)) {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
const relative2 = _relativeToPosix(target_file, target_dir);
|
|
563
|
+
if (_resolve_source(relative2) === null || skip_compile_disabled_rule(relative2)) {
|
|
564
|
+
stale.push(relative2);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return [missing, stale];
|
|
569
|
+
}
|
|
570
|
+
var TOOL_DIRS = {
|
|
571
|
+
".claude/rules": "../../dist/agent-src/rules",
|
|
572
|
+
".cursor/rules": "../../dist/agent-src/rules",
|
|
573
|
+
".clinerules": "../dist/agent-src/rules"
|
|
574
|
+
};
|
|
575
|
+
var PERSONA_TOOL_DIRS = {
|
|
576
|
+
".claude/personas": "../../dist/agent-src/personas",
|
|
577
|
+
".cursor/personas": "../../dist/agent-src/personas"
|
|
578
|
+
};
|
|
579
|
+
var USER_TYPE_TOOL_DIRS = {
|
|
580
|
+
".claude/user-types": "../../dist/agent-src/user-types",
|
|
581
|
+
".cursor/user-types": "../../dist/agent-src/user-types"
|
|
582
|
+
};
|
|
583
|
+
var _DIR_TOOL_ID = {
|
|
584
|
+
".claude/rules": "claude-code",
|
|
585
|
+
".cursor/rules": "cursor",
|
|
586
|
+
".clinerules": "cline",
|
|
587
|
+
".claude/personas": "claude-code",
|
|
588
|
+
".cursor/personas": "cursor",
|
|
589
|
+
".claude/user-types": "claude-code",
|
|
590
|
+
".cursor/user-types": "cursor"
|
|
591
|
+
};
|
|
592
|
+
function _filter_tool_dirs(mapping) {
|
|
593
|
+
const out = {};
|
|
594
|
+
for (const [d, p] of Object.entries(mapping)) {
|
|
595
|
+
if (_tool_active(_DIR_TOOL_ID[d] ?? "claude-code")) {
|
|
596
|
+
out[d] = p;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return out;
|
|
600
|
+
}
|
|
601
|
+
function strip_frontmatter(content) {
|
|
602
|
+
if (content.startsWith("---")) {
|
|
603
|
+
const end = content.indexOf("---", 3);
|
|
604
|
+
if (end !== -1) {
|
|
605
|
+
content = _lstripNewlines(content.slice(end + 3));
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return content;
|
|
609
|
+
}
|
|
610
|
+
var _LEGACY_SRC_PREFIX = ".agent-src.uncondensed/";
|
|
611
|
+
var _PROJECTED_SRC_PREFIX = "dist/agent-src/";
|
|
612
|
+
var _LEGACY_PROJECTED_SRC_PREFIX = ".agent-src/";
|
|
613
|
+
var _FM_LIST_ITEM_RE = /^(\s*-\s*)(["']?)([^"'\n]+?\.md)(["']?)\s*$/;
|
|
614
|
+
var _FM_PATH_PREFIX_RE = /^(\s*(?:-\s+)?path_prefix:\s*)(["']?)([^"'\n]+?)(["']?)\s*$/;
|
|
615
|
+
var _BODY_DOCS_RE = /\.\.\/\.\.\/(docs\/(?:guidelines|contracts)\/[^)\s]+\.md)/g;
|
|
616
|
+
var _FM_PLAIN_LIST_RE = /^\s*-\s*(["']?)([^"'\n]+?)\1\s*$/;
|
|
617
|
+
var _HRR_BANNER_MARKER = "<!-- agent-config:human-review-banner -->";
|
|
618
|
+
function _depth_prefix(source_relative_path) {
|
|
619
|
+
const parts = _posixParts(source_relative_path);
|
|
620
|
+
const depth = Math.max(parts.length - 1, 1);
|
|
621
|
+
return "../".repeat(depth);
|
|
622
|
+
}
|
|
623
|
+
function _split_frontmatter(content) {
|
|
624
|
+
if (!content.startsWith("---\n")) {
|
|
625
|
+
return [null, content];
|
|
626
|
+
}
|
|
627
|
+
const end = content.indexOf("\n---\n", 4);
|
|
628
|
+
if (end === -1) {
|
|
629
|
+
return [null, content];
|
|
630
|
+
}
|
|
631
|
+
const fm_text = content.slice(4, end);
|
|
632
|
+
const body = content.slice(end + "\n---\n".length);
|
|
633
|
+
return [fm_text.split("\n"), body];
|
|
634
|
+
}
|
|
635
|
+
function _rewrite_load_context_value(value, prefix) {
|
|
636
|
+
if (value.startsWith("../") || value.startsWith("./") || value.startsWith("/")) {
|
|
637
|
+
return value;
|
|
638
|
+
}
|
|
639
|
+
if (value.startsWith(_LEGACY_SRC_PREFIX)) {
|
|
640
|
+
return prefix + value.slice(_LEGACY_SRC_PREFIX.length);
|
|
641
|
+
}
|
|
642
|
+
if (value.startsWith(_PROJECTED_SRC_PREFIX)) {
|
|
643
|
+
return prefix + value.slice(_PROJECTED_SRC_PREFIX.length);
|
|
644
|
+
}
|
|
645
|
+
if (value.startsWith(_LEGACY_PROJECTED_SRC_PREFIX)) {
|
|
646
|
+
return prefix + value.slice(_LEGACY_PROJECTED_SRC_PREFIX.length);
|
|
647
|
+
}
|
|
648
|
+
return prefix + value;
|
|
649
|
+
}
|
|
650
|
+
function _rewrite_path_prefix_value(value) {
|
|
651
|
+
return value;
|
|
652
|
+
}
|
|
653
|
+
function _rewrite_frontmatter_lines(lines, prefix) {
|
|
654
|
+
let in_load_context = false;
|
|
655
|
+
const out = [];
|
|
656
|
+
for (const line of lines) {
|
|
657
|
+
const bare = _lstrip(line);
|
|
658
|
+
if (bare.startsWith("load_context:") || bare.startsWith("load_context_eager:")) {
|
|
659
|
+
in_load_context = true;
|
|
660
|
+
out.push(line);
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (in_load_context) {
|
|
664
|
+
const m = _FM_LIST_ITEM_RE.exec(line);
|
|
665
|
+
if (m) {
|
|
666
|
+
const indent = m[1] ?? "";
|
|
667
|
+
const q1 = m[2] ?? "";
|
|
668
|
+
const value = m[3] ?? "";
|
|
669
|
+
const q2 = m[4] ?? "";
|
|
670
|
+
out.push(`${indent}${q1}${_rewrite_load_context_value(value, prefix)}${q2}`);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
in_load_context = false;
|
|
674
|
+
}
|
|
675
|
+
const pm = _FM_PATH_PREFIX_RE.exec(line);
|
|
676
|
+
if (pm) {
|
|
677
|
+
const head = pm[1] ?? "";
|
|
678
|
+
const q1 = pm[2] ?? "";
|
|
679
|
+
const value = pm[3] ?? "";
|
|
680
|
+
const q2 = pm[4] ?? "";
|
|
681
|
+
out.push(`${head}${q1}${_rewrite_path_prefix_value(value)}${q2}`);
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
out.push(line);
|
|
685
|
+
}
|
|
686
|
+
return out;
|
|
687
|
+
}
|
|
688
|
+
function _rewrite_body_links(body, prefix) {
|
|
689
|
+
return body.replace(_BODY_DOCS_RE, (_m, tail) => prefix + tail);
|
|
690
|
+
}
|
|
691
|
+
function _parse_trust_and_owner(fm_lines) {
|
|
692
|
+
let level = "core";
|
|
693
|
+
let hrr = false;
|
|
694
|
+
const packs = [];
|
|
695
|
+
const workspaces = [];
|
|
696
|
+
let in_trust = false;
|
|
697
|
+
let in_packs = false;
|
|
698
|
+
let in_workspaces = false;
|
|
699
|
+
for (const line of fm_lines) {
|
|
700
|
+
const stripped = _lstrip(line);
|
|
701
|
+
const indent = line.length - stripped.length;
|
|
702
|
+
if (indent === 0 && stripped.endsWith(":")) {
|
|
703
|
+
const key = stripped.slice(0, -1);
|
|
704
|
+
in_trust = key === "trust";
|
|
705
|
+
in_packs = key === "packs";
|
|
706
|
+
in_workspaces = key === "workspaces";
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
if (in_trust && stripped.startsWith("level:")) {
|
|
710
|
+
const after = stripped.slice(stripped.indexOf(":") + 1);
|
|
711
|
+
level = _stripChars(_stripChars(_strip(after), '"'), "'");
|
|
712
|
+
} else if (in_trust && stripped.startsWith("human_review_required:")) {
|
|
713
|
+
const after = stripped.slice(stripped.indexOf(":") + 1);
|
|
714
|
+
hrr = _strip(after).toLowerCase() === "true";
|
|
715
|
+
} else if (in_packs || in_workspaces) {
|
|
716
|
+
const m = _FM_PLAIN_LIST_RE.exec(line);
|
|
717
|
+
if (m) {
|
|
718
|
+
const value = _strip(m[2] ?? "");
|
|
719
|
+
(in_packs ? packs : workspaces).push(value);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
let owner = "unknown";
|
|
724
|
+
if (packs.length > 0) {
|
|
725
|
+
owner = packs[0].split("-")[0];
|
|
726
|
+
} else if (workspaces.length > 0) {
|
|
727
|
+
owner = workspaces[0];
|
|
728
|
+
}
|
|
729
|
+
return [level, hrr, owner];
|
|
730
|
+
}
|
|
731
|
+
function _inject_hrr_banner(body, level, owner) {
|
|
732
|
+
if (body.includes(_HRR_BANNER_MARKER)) {
|
|
733
|
+
return body;
|
|
734
|
+
}
|
|
735
|
+
const banner = `${_HRR_BANNER_MARKER}
|
|
736
|
+
> HUMAN REVIEW REQUIRED \xB7 trust: ${level} \xB7 owner: ${owner}
|
|
737
|
+
|
|
738
|
+
`;
|
|
739
|
+
return banner + _lstripNewlines(body);
|
|
740
|
+
}
|
|
741
|
+
function _rewrite_paths(content, source_relative_path) {
|
|
742
|
+
const prefix = _depth_prefix(source_relative_path);
|
|
743
|
+
const [fm_lines, bodyInitial] = _split_frontmatter(content);
|
|
744
|
+
let body = _rewrite_body_links(bodyInitial, prefix);
|
|
745
|
+
if (fm_lines === null) {
|
|
746
|
+
return body;
|
|
747
|
+
}
|
|
748
|
+
const new_fm = _rewrite_frontmatter_lines(fm_lines, prefix);
|
|
749
|
+
const [level, hrr, owner] = _parse_trust_and_owner(fm_lines);
|
|
750
|
+
if (hrr && level) {
|
|
751
|
+
body = _inject_hrr_banner(body, level, owner);
|
|
752
|
+
}
|
|
753
|
+
return "---\n" + new_fm.join("\n") + "\n---\n" + body;
|
|
754
|
+
}
|
|
755
|
+
function _read_projection_list(key) {
|
|
756
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
757
|
+
const proj = data["projection"];
|
|
758
|
+
const value = typeof proj === "object" && proj !== null && !Array.isArray(proj) ? proj[key] : null;
|
|
759
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
760
|
+
return value.map((v) => String(v));
|
|
761
|
+
}
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
function _read_rule_workspaces() {
|
|
765
|
+
return _read_projection_list("rule_workspaces");
|
|
766
|
+
}
|
|
767
|
+
function _read_rule_packs() {
|
|
768
|
+
return _read_projection_list("rule_packs");
|
|
769
|
+
}
|
|
770
|
+
function rule_in_scope(source_path, scope, pack_scope = null, role_scope = null) {
|
|
771
|
+
if (scope === null && pack_scope === null && role_scope === null) {
|
|
772
|
+
return true;
|
|
773
|
+
}
|
|
774
|
+
const [meta] = _parse_frontmatter(_readText(source_path));
|
|
775
|
+
if (meta["type"] === "always" || meta["alwaysApply"] === true) {
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
const axis = (key, configured) => {
|
|
779
|
+
if (configured === null) {
|
|
780
|
+
return true;
|
|
781
|
+
}
|
|
782
|
+
const values = Array.isArray(meta[key]) ? meta[key].map((w) => String(w)) : [];
|
|
783
|
+
if (values.length === 0) {
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
786
|
+
return values.some((v) => configured.includes(v));
|
|
787
|
+
};
|
|
788
|
+
return axis("workspaces", scope) && axis("packs", pack_scope) && axis("roles", role_scope);
|
|
789
|
+
}
|
|
790
|
+
function _scoped_rule_basenames() {
|
|
791
|
+
const scope = _read_rule_workspaces();
|
|
792
|
+
const pack_scope = _read_rule_packs();
|
|
793
|
+
return _iterdirSorted(MODULE_STATE.RULES_SOURCE).filter((p) => p.endsWith(".md") && _isFile(p)).filter((p) => rule_in_scope(p, scope, pack_scope)).filter((p) => !skip_compile_disabled_rule(`rules/${path.basename(p)}`)).map((p) => path.basename(p)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
794
|
+
}
|
|
795
|
+
function generate_rule_symlinks() {
|
|
796
|
+
const rules = _scoped_rule_basenames();
|
|
797
|
+
const tool_dirs = _filter_tool_dirs(TOOL_DIRS);
|
|
798
|
+
let thin_files = null;
|
|
799
|
+
if (_lean_projection_mode() === "thin") {
|
|
800
|
+
throw new Error(
|
|
801
|
+
"lean_projection.mode=thin requires project_thin_rules (not ported in condense.ts)"
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
const dedup_on = _read_projection_scope_dedup();
|
|
805
|
+
const user_home = process.env["HOME"] ?? os.homedir();
|
|
806
|
+
const skipped_per_tool = {};
|
|
807
|
+
let total = 0;
|
|
808
|
+
for (const [tool_dir, rel_prefix] of Object.entries(tool_dirs)) {
|
|
809
|
+
const target_dir = path.join(MODULE_STATE.PROJECT_ROOT, tool_dir);
|
|
810
|
+
const skip = dedup_on ? _dedupable_rules(tool_dir, rules, user_home) : /* @__PURE__ */ new Set();
|
|
811
|
+
skipped_per_tool[tool_dir] = skip;
|
|
812
|
+
_mkdirp(target_dir);
|
|
813
|
+
for (const item of _iterdirSorted(target_dir)) {
|
|
814
|
+
const name = path.basename(item);
|
|
815
|
+
if (_isSymlink(item) && (!rules.includes(name) || skip.has(name)) && name !== "README.md") {
|
|
816
|
+
fs.unlinkSync(item);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
for (const rule of rules) {
|
|
820
|
+
if (skip.has(rule)) {
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
const link = path.join(target_dir, rule);
|
|
824
|
+
if (_existsOrSymlink(link)) {
|
|
825
|
+
fs.unlinkSync(link);
|
|
826
|
+
}
|
|
827
|
+
if (thin_files !== null) {
|
|
828
|
+
_writeText(link, thin_files[rule]);
|
|
829
|
+
} else {
|
|
830
|
+
fs.symlinkSync(path.join(rel_prefix, rule), link);
|
|
831
|
+
}
|
|
832
|
+
total += 1;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
const source_count = rules.length;
|
|
836
|
+
for (const tool_dir of Object.keys(tool_dirs)) {
|
|
837
|
+
const target_dir = path.join(MODULE_STATE.PROJECT_ROOT, tool_dir);
|
|
838
|
+
const emitted = new Set(rules);
|
|
839
|
+
const tool_count = _iterdirSorted(target_dir).filter((f) => emitted.has(path.basename(f))).length;
|
|
840
|
+
const skipped = (skipped_per_tool[tool_dir] ?? /* @__PURE__ */ new Set()).size;
|
|
841
|
+
const expected = source_count - skipped;
|
|
842
|
+
if (tool_count !== expected) {
|
|
843
|
+
_print(` \u26A0\uFE0F ${tool_dir}: ${tool_count} rules (expected ${expected})`);
|
|
844
|
+
}
|
|
845
|
+
if (skipped > 0) {
|
|
846
|
+
_print(
|
|
847
|
+
` \u2139\uFE0F ${tool_dir}: ${skipped} rule(s) skipped \u2014 byte-identical twin already installed at user scope`
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
info(
|
|
852
|
+
` \u2705 Created ${total} rule symlinks across ${Object.keys(tool_dirs).length} tool directories (${source_count} rules each)`
|
|
853
|
+
);
|
|
854
|
+
return total;
|
|
855
|
+
}
|
|
856
|
+
function generate_windsurfrules() {
|
|
857
|
+
const rules = _scoped_rule_basenames();
|
|
858
|
+
const parts = ["# Auto-generated from dist/agent-src/rules/ \u2014 do not edit directly\n"];
|
|
859
|
+
for (const rule of rules) {
|
|
860
|
+
const p = path.join(MODULE_STATE.RULES_SOURCE, rule);
|
|
861
|
+
const content = strip_frontmatter(_readText(p));
|
|
862
|
+
parts.push(`---
|
|
863
|
+
|
|
864
|
+
${_strip(content)}
|
|
865
|
+
`);
|
|
866
|
+
}
|
|
867
|
+
const output = path.join(MODULE_STATE.PROJECT_ROOT, ".windsurfrules");
|
|
868
|
+
_writeText(output, parts.join("\n") + "\n");
|
|
869
|
+
info(` \u2705 Generated .windsurfrules (${rules.length} rules)`);
|
|
870
|
+
return rules.length;
|
|
871
|
+
}
|
|
872
|
+
function _CURSOR_RULES_MDC_DIR() {
|
|
873
|
+
return path.join(MODULE_STATE.PROJECT_ROOT, ".cursor", "rules");
|
|
874
|
+
}
|
|
875
|
+
function _WINDSURF_RULES_DIR() {
|
|
876
|
+
return path.join(MODULE_STATE.PROJECT_ROOT, ".windsurf", "rules");
|
|
877
|
+
}
|
|
878
|
+
function _WINDSURF_WORKFLOWS_DIR() {
|
|
879
|
+
return path.join(MODULE_STATE.PROJECT_ROOT, ".windsurf", "workflows");
|
|
880
|
+
}
|
|
881
|
+
function _CURSOR_COMMANDS_DIR() {
|
|
882
|
+
return path.join(MODULE_STATE.PROJECT_ROOT, ".cursor", "commands");
|
|
883
|
+
}
|
|
884
|
+
function _parse_frontmatter(content) {
|
|
885
|
+
if (!content.startsWith("---")) {
|
|
886
|
+
return [{}, content];
|
|
887
|
+
}
|
|
888
|
+
const end = content.indexOf("\n---", 3);
|
|
889
|
+
if (end === -1) {
|
|
890
|
+
return [{}, content];
|
|
891
|
+
}
|
|
892
|
+
const raw = _strip(content.slice(3, end));
|
|
893
|
+
const body = _lstripNewlines(content.slice(end + 4));
|
|
894
|
+
let meta = _yamlParse(raw);
|
|
895
|
+
if (meta === null || meta === void 0) {
|
|
896
|
+
meta = {};
|
|
897
|
+
}
|
|
898
|
+
if (typeof meta === "object" && meta !== null && !Array.isArray(meta)) {
|
|
899
|
+
return [meta, body];
|
|
900
|
+
}
|
|
901
|
+
return [{}, body];
|
|
902
|
+
}
|
|
903
|
+
function _yaml_scalar(value) {
|
|
904
|
+
return JSON.stringify(value);
|
|
905
|
+
}
|
|
906
|
+
function derive_trigger_globs(meta) {
|
|
907
|
+
const triggers = meta["triggers"];
|
|
908
|
+
if (!Array.isArray(triggers)) {
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
const globs = [];
|
|
912
|
+
for (const t of triggers) {
|
|
913
|
+
if (t === null || typeof t !== "object" || Array.isArray(t)) continue;
|
|
914
|
+
const obj = t;
|
|
915
|
+
if (typeof obj["file_pattern"] === "string" && obj["file_pattern"]) {
|
|
916
|
+
globs.push(obj["file_pattern"]);
|
|
917
|
+
} else if (typeof obj["path_prefix"] === "string" && obj["path_prefix"]) {
|
|
918
|
+
const prefix = obj["path_prefix"];
|
|
919
|
+
globs.push(prefix.endsWith("/") ? `${prefix}**` : `${prefix}**`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return [...new Set(globs)];
|
|
923
|
+
}
|
|
924
|
+
function _emit_cursor_mdc(source, target) {
|
|
925
|
+
const [meta, body] = _parse_frontmatter(_readText(source));
|
|
926
|
+
const description = _strip(String(meta["description"] ?? "").replace(/\n/g, " "));
|
|
927
|
+
const always_apply = Boolean(meta["alwaysApply"] || meta["type"] === "always");
|
|
928
|
+
const globs = always_apply ? [] : derive_trigger_globs(meta);
|
|
929
|
+
const lines = [
|
|
930
|
+
"---",
|
|
931
|
+
`description: ${_yaml_scalar(description)}`,
|
|
932
|
+
`globs: ${globs.join(",")}`,
|
|
933
|
+
`alwaysApply: ${always_apply ? "true" : "false"}`,
|
|
934
|
+
"---",
|
|
935
|
+
"",
|
|
936
|
+
_pyRstrip(body) + "\n"
|
|
937
|
+
];
|
|
938
|
+
_mkdirp(path.dirname(target));
|
|
939
|
+
_writeText(target, lines.join("\n"));
|
|
940
|
+
}
|
|
941
|
+
function _emit_windsurf_rule(source, target) {
|
|
942
|
+
const [meta, body] = _parse_frontmatter(_readText(source));
|
|
943
|
+
const description = _strip(String(meta["description"] ?? "").replace(/\n/g, " "));
|
|
944
|
+
const always_apply = Boolean(meta["alwaysApply"] || meta["type"] === "always");
|
|
945
|
+
const globs = always_apply ? [] : derive_trigger_globs(meta);
|
|
946
|
+
const trigger = always_apply ? "always_on" : globs.length > 0 ? "glob" : "model_decision";
|
|
947
|
+
const lines = [
|
|
948
|
+
"---",
|
|
949
|
+
`trigger: ${trigger}`,
|
|
950
|
+
`description: ${_yaml_scalar(description)}`,
|
|
951
|
+
`globs: ${globs.join(",")}`,
|
|
952
|
+
"---",
|
|
953
|
+
"",
|
|
954
|
+
_pyRstrip(body) + "\n"
|
|
955
|
+
];
|
|
956
|
+
_mkdirp(path.dirname(target));
|
|
957
|
+
_writeText(target, lines.join("\n"));
|
|
958
|
+
}
|
|
959
|
+
function _pyRstrip(s) {
|
|
960
|
+
return s.replace(/\s+$/u, "");
|
|
961
|
+
}
|
|
962
|
+
function _clean_modern_dir(target_dir, valid_names) {
|
|
963
|
+
if (!_exists(target_dir)) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
for (const item of _iterdirSorted(target_dir)) {
|
|
967
|
+
const name = path.basename(item);
|
|
968
|
+
if (name === "README.md") {
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
if (!valid_names.has(name)) {
|
|
972
|
+
if (_isDir(item) && !_isSymlink(item)) {
|
|
973
|
+
fs.rmSync(item, { recursive: true, force: true });
|
|
974
|
+
} else {
|
|
975
|
+
fs.unlinkSync(item);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
function generate_cursor_mdc_rules() {
|
|
981
|
+
const scope = _read_rule_workspaces();
|
|
982
|
+
const pack_scope = _read_rule_packs();
|
|
983
|
+
const rules = _rglobSorted(MODULE_STATE.RULES_SOURCE, "*.md").filter((p) => _isFile(p)).filter((p) => rule_in_scope(p, scope, pack_scope));
|
|
984
|
+
const stems = rules.map((r) => path.basename(r, ".md"));
|
|
985
|
+
const valid = /* @__PURE__ */ new Set([
|
|
986
|
+
...stems.map((s) => `${s}.mdc`),
|
|
987
|
+
...rules.map((r) => path.basename(r))
|
|
988
|
+
]);
|
|
989
|
+
_clean_modern_dir(_CURSOR_RULES_MDC_DIR(), valid);
|
|
990
|
+
for (const rule of rules) {
|
|
991
|
+
_emit_cursor_mdc(rule, path.join(_CURSOR_RULES_MDC_DIR(), `${path.basename(rule, ".md")}.mdc`));
|
|
992
|
+
}
|
|
993
|
+
info(` \u2705 Wrote ${rules.length} \`.cursor/rules/*.mdc\` files`);
|
|
994
|
+
return rules.length;
|
|
995
|
+
}
|
|
996
|
+
function generate_windsurf_modern_rules() {
|
|
997
|
+
const scope = _read_rule_workspaces();
|
|
998
|
+
const pack_scope = _read_rule_packs();
|
|
999
|
+
const rules = _rglobSorted(MODULE_STATE.RULES_SOURCE, "*.md").filter((p) => _isFile(p)).filter((p) => rule_in_scope(p, scope, pack_scope));
|
|
1000
|
+
const valid = new Set(rules.map((r) => path.basename(r)));
|
|
1001
|
+
_clean_modern_dir(_WINDSURF_RULES_DIR(), valid);
|
|
1002
|
+
for (const rule of rules) {
|
|
1003
|
+
_emit_windsurf_rule(rule, path.join(_WINDSURF_RULES_DIR(), path.basename(rule)));
|
|
1004
|
+
}
|
|
1005
|
+
info(` \u2705 Wrote ${rules.length} \`.windsurf/rules/*.md\` files`);
|
|
1006
|
+
return rules.length;
|
|
1007
|
+
}
|
|
1008
|
+
function generate_cursor_commands(active_command_slugs = null) {
|
|
1009
|
+
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, "src", "domains"))) {
|
|
1010
|
+
return 0;
|
|
1011
|
+
}
|
|
1012
|
+
const cmds = [...iterCommands()].filter(
|
|
1013
|
+
([, slug]) => active_command_slugs === null || active_command_slugs.has(slug)
|
|
1014
|
+
);
|
|
1015
|
+
const valid = new Set(cmds.map(([, slug]) => `${slug}.md`));
|
|
1016
|
+
_clean_modern_dir(_CURSOR_COMMANDS_DIR(), valid);
|
|
1017
|
+
_mkdirp(_CURSOR_COMMANDS_DIR());
|
|
1018
|
+
let count = 0;
|
|
1019
|
+
for (const [source_file, slug] of cmds) {
|
|
1020
|
+
const link = path.join(_CURSOR_COMMANDS_DIR(), `${slug}.md`);
|
|
1021
|
+
if (_existsOrSymlink(link)) {
|
|
1022
|
+
fs.unlinkSync(link);
|
|
1023
|
+
}
|
|
1024
|
+
const rel = path.join("../..", _relativeToPosix(source_file, MODULE_STATE.PROJECT_ROOT));
|
|
1025
|
+
fs.symlinkSync(rel, link);
|
|
1026
|
+
count += 1;
|
|
1027
|
+
}
|
|
1028
|
+
info(` \u2705 Linked ${count} \`.cursor/commands/*.md\` files`);
|
|
1029
|
+
return count;
|
|
1030
|
+
}
|
|
1031
|
+
function generate_windsurf_workflows(active_command_slugs = null) {
|
|
1032
|
+
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, "src", "domains"))) {
|
|
1033
|
+
return 0;
|
|
1034
|
+
}
|
|
1035
|
+
const cmds = [...iterCommands()].filter(
|
|
1036
|
+
([, slug]) => active_command_slugs === null || active_command_slugs.has(slug)
|
|
1037
|
+
);
|
|
1038
|
+
const valid = new Set(cmds.map(([, slug]) => `${slug}.md`));
|
|
1039
|
+
_clean_modern_dir(_WINDSURF_WORKFLOWS_DIR(), valid);
|
|
1040
|
+
_mkdirp(_WINDSURF_WORKFLOWS_DIR());
|
|
1041
|
+
let count = 0;
|
|
1042
|
+
for (const [source_file, slug] of cmds) {
|
|
1043
|
+
const link = path.join(_WINDSURF_WORKFLOWS_DIR(), `${slug}.md`);
|
|
1044
|
+
if (_existsOrSymlink(link)) {
|
|
1045
|
+
fs.unlinkSync(link);
|
|
1046
|
+
}
|
|
1047
|
+
const rel = path.join("../..", _relativeToPosix(source_file, MODULE_STATE.PROJECT_ROOT));
|
|
1048
|
+
fs.symlinkSync(rel, link);
|
|
1049
|
+
count += 1;
|
|
1050
|
+
}
|
|
1051
|
+
info(` \u2705 Linked ${count} \`.windsurf/workflows/*.md\` files`);
|
|
1052
|
+
return count;
|
|
1053
|
+
}
|
|
1054
|
+
function generate_gemini_md() {
|
|
1055
|
+
const link = path.join(MODULE_STATE.PROJECT_ROOT, "GEMINI.md");
|
|
1056
|
+
if (_existsOrSymlink(link)) {
|
|
1057
|
+
fs.unlinkSync(link);
|
|
1058
|
+
}
|
|
1059
|
+
fs.symlinkSync("AGENTS.md", link);
|
|
1060
|
+
info(" \u2705 Created GEMINI.md \u2192 AGENTS.md symlink");
|
|
1061
|
+
}
|
|
1062
|
+
function* iterCommands() {
|
|
1063
|
+
const src_domains = path.join(MODULE_STATE.PROJECT_ROOT, "src", "domains");
|
|
1064
|
+
if (!_isDir(src_domains)) {
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
for (const source_file of _rglobSorted(src_domains, "command.md")) {
|
|
1068
|
+
if (!_isFile(source_file)) {
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
const rel = _relativeToPosix(source_file, MODULE_STATE.PROJECT_ROOT);
|
|
1072
|
+
let slug = _command_path_to_slug(rel);
|
|
1073
|
+
if (!slug) {
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
const pack_id = _relativeToPosix(source_file, src_domains).split("/")[0];
|
|
1077
|
+
const prefix = _domains_slug_prefix(pack_id);
|
|
1078
|
+
if (prefix && slug !== prefix && !slug.startsWith(prefix + "-")) {
|
|
1079
|
+
slug = `${prefix}-${slug}`;
|
|
1080
|
+
}
|
|
1081
|
+
yield [source_file, slug];
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function _read_model_auto_switch() {
|
|
1085
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
1086
|
+
const model = data["model"];
|
|
1087
|
+
const value = typeof model === "object" && model !== null && !Array.isArray(model) ? model["auto_switch"] : null;
|
|
1088
|
+
if (typeof value === "string" && ["auto", "suggest", "off"].includes(value.trim().toLowerCase())) {
|
|
1089
|
+
return value.trim().toLowerCase();
|
|
1090
|
+
}
|
|
1091
|
+
return "suggest";
|
|
1092
|
+
}
|
|
1093
|
+
function _read_projection_mode() {
|
|
1094
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
1095
|
+
const proj = data["projection"];
|
|
1096
|
+
const value = typeof proj === "object" && proj !== null && !Array.isArray(proj) ? proj["mode"] : null;
|
|
1097
|
+
if (typeof value === "string" && ["legacy-all", "scoped"].includes(value.trim().toLowerCase())) {
|
|
1098
|
+
return value.trim().toLowerCase();
|
|
1099
|
+
}
|
|
1100
|
+
return "legacy-all";
|
|
1101
|
+
}
|
|
1102
|
+
function _domains_slug_prefix(pack_id) {
|
|
1103
|
+
const manifest = path.join(MODULE_STATE.PROJECT_ROOT, "src", "domains", pack_id, "pack.yaml");
|
|
1104
|
+
if (!_isFile(manifest)) {
|
|
1105
|
+
return "";
|
|
1106
|
+
}
|
|
1107
|
+
const m = _SLUG_PREFIX_RE.exec(_readText(manifest));
|
|
1108
|
+
return m ? m[1] : "";
|
|
1109
|
+
}
|
|
1110
|
+
function _command_path_to_slug(manifest_path) {
|
|
1111
|
+
const logical = strip_source_prefix(manifest_path);
|
|
1112
|
+
if (logical && logical.startsWith("commands/")) {
|
|
1113
|
+
return _posixParts(logical.slice("commands/".length).replace(/\.md$/, "")).join("-");
|
|
1114
|
+
}
|
|
1115
|
+
const parts = manifest_path.split(path.sep).filter((s) => s.length > 0);
|
|
1116
|
+
const i = parts.indexOf("commands");
|
|
1117
|
+
return parts.slice(i + 1).join("/").replace(/\.md$/, "").split("/").join("-");
|
|
1118
|
+
}
|
|
1119
|
+
function _resolve_active_predicates() {
|
|
1120
|
+
if (_read_projection_mode() !== "scoped") {
|
|
1121
|
+
return [null, null];
|
|
1122
|
+
}
|
|
1123
|
+
throw new Error("projection.mode=scoped requires the config package (not ported in condense.ts)");
|
|
1124
|
+
}
|
|
1125
|
+
function _model_tier(skill_md) {
|
|
1126
|
+
if (!_exists(skill_md)) {
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
const text = fs.readFileSync(skill_md).toString("utf-8");
|
|
1130
|
+
if (!text.startsWith("---\n")) {
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
const end = text.indexOf("\n---\n", 4);
|
|
1134
|
+
if (end === -1) {
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
const m = MODEL_TIER_RE.exec(text.slice(4, end));
|
|
1138
|
+
return m ? m[1] : null;
|
|
1139
|
+
}
|
|
1140
|
+
function _render_native_model_md(src_md, tier) {
|
|
1141
|
+
const text = _readText(src_md);
|
|
1142
|
+
const model = TIER_TO_CLAUDE_MODEL[tier];
|
|
1143
|
+
return text.replace(MODEL_TIER_RE, `model: ${model}`);
|
|
1144
|
+
}
|
|
1145
|
+
function generate_claude_skills(active_skill_names = null) {
|
|
1146
|
+
if (!_exists(MODULE_STATE.SKILLS_SOURCE)) {
|
|
1147
|
+
process.stderr.write(" \u26A0\uFE0F dist/agent-src/skills/ not found \u2014 skipping skills\n");
|
|
1148
|
+
return 0;
|
|
1149
|
+
}
|
|
1150
|
+
let skills = _iterdirSorted(MODULE_STATE.SKILLS_SOURCE).filter((p) => _isDir(p)).map((p) => path.basename(p)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1151
|
+
if (active_skill_names !== null) {
|
|
1152
|
+
skills = skills.filter((s) => active_skill_names.has(s));
|
|
1153
|
+
}
|
|
1154
|
+
const skill_set = new Set(skills);
|
|
1155
|
+
const command_slugs = new Set([...iterCommands()].map(([, slug]) => slug));
|
|
1156
|
+
_mkdirp(MODULE_STATE.CLAUDE_SKILLS_DIR);
|
|
1157
|
+
const auto = _read_model_auto_switch() === "auto";
|
|
1158
|
+
for (const item of _iterdirSorted(MODULE_STATE.CLAUDE_SKILLS_DIR)) {
|
|
1159
|
+
const name = path.basename(item);
|
|
1160
|
+
if (skill_set.has(name) || command_slugs.has(name) || name === "README.md") {
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
if (_isSymlink(item)) {
|
|
1164
|
+
fs.unlinkSync(item);
|
|
1165
|
+
} else if (_isDirNoFollow(item)) {
|
|
1166
|
+
const skill_md = path.join(item, "SKILL.md");
|
|
1167
|
+
if (_isFile(skill_md) && !_isSymlink(skill_md)) {
|
|
1168
|
+
fs.rmSync(item, { recursive: true, force: true });
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
let count = 0;
|
|
1173
|
+
let rendered = 0;
|
|
1174
|
+
for (const skill of skills) {
|
|
1175
|
+
const link = path.join(MODULE_STATE.CLAUDE_SKILLS_DIR, skill);
|
|
1176
|
+
const src_dir = path.join(MODULE_STATE.SKILLS_SOURCE, skill);
|
|
1177
|
+
const value = auto ? _model_tier(path.join(src_dir, "SKILL.md")) : null;
|
|
1178
|
+
if (_isSymlink(link)) {
|
|
1179
|
+
fs.unlinkSync(link);
|
|
1180
|
+
} else if (_isDirNoFollow(link)) {
|
|
1181
|
+
fs.rmSync(link, { recursive: true, force: true });
|
|
1182
|
+
} else if (_existsOrSymlink(link)) {
|
|
1183
|
+
fs.unlinkSync(link);
|
|
1184
|
+
}
|
|
1185
|
+
if (value !== null && value in TIER_TO_CLAUDE_MODEL) {
|
|
1186
|
+
_mkdirp(link);
|
|
1187
|
+
for (const entry of _iterdirSorted(src_dir)) {
|
|
1188
|
+
const entryName = path.basename(entry);
|
|
1189
|
+
if (entryName === "SKILL.md") {
|
|
1190
|
+
_writeText(
|
|
1191
|
+
path.join(link, "SKILL.md"),
|
|
1192
|
+
_render_native_model_md(entry, value)
|
|
1193
|
+
);
|
|
1194
|
+
} else {
|
|
1195
|
+
fs.symlinkSync(
|
|
1196
|
+
path.join("../../../dist/agent-src/skills", skill, entryName),
|
|
1197
|
+
path.join(link, entryName)
|
|
1198
|
+
);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
rendered += 1;
|
|
1202
|
+
} else {
|
|
1203
|
+
fs.symlinkSync(path.join("../../dist/agent-src/skills", skill), link);
|
|
1204
|
+
}
|
|
1205
|
+
count += 1;
|
|
1206
|
+
}
|
|
1207
|
+
const suffix = rendered ? ` (${rendered} rendered with native model:)` : "";
|
|
1208
|
+
info(` \u2705 Created ${count} skill entries in .claude/skills/${suffix}`);
|
|
1209
|
+
return count;
|
|
1210
|
+
}
|
|
1211
|
+
function generate_claude_commands(active_command_slugs = null) {
|
|
1212
|
+
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, "src", "domains"))) {
|
|
1213
|
+
process.stderr.write(" \u26A0\uFE0F src/domains/ not found \u2014 skipping commands\n");
|
|
1214
|
+
return 0;
|
|
1215
|
+
}
|
|
1216
|
+
_mkdirp(MODULE_STATE.CLAUDE_SKILLS_DIR);
|
|
1217
|
+
let skill_names = /* @__PURE__ */ new Set();
|
|
1218
|
+
if (_exists(MODULE_STATE.SKILLS_SOURCE)) {
|
|
1219
|
+
skill_names = new Set(
|
|
1220
|
+
_iterdirSorted(MODULE_STATE.SKILLS_SOURCE).filter((p) => _isDir(p)).map((p) => path.basename(p))
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
const current_slugs = /* @__PURE__ */ new Set();
|
|
1224
|
+
let count = 0;
|
|
1225
|
+
let skipped = 0;
|
|
1226
|
+
let reserved = 0;
|
|
1227
|
+
let rendered = 0;
|
|
1228
|
+
const auto = _read_model_auto_switch() === "auto";
|
|
1229
|
+
for (const [source_file, slug] of iterCommands()) {
|
|
1230
|
+
if (active_command_slugs !== null && !active_command_slugs.has(slug)) {
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
if (skill_names.has(slug)) {
|
|
1234
|
+
skipped += 1;
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
if (is_claude_builtin_name(slug)) {
|
|
1238
|
+
reserved += 1;
|
|
1239
|
+
continue;
|
|
1240
|
+
}
|
|
1241
|
+
current_slugs.add(slug);
|
|
1242
|
+
const skill_dir = path.join(MODULE_STATE.CLAUDE_SKILLS_DIR, slug);
|
|
1243
|
+
_mkdirp(skill_dir);
|
|
1244
|
+
const skill_file = path.join(skill_dir, "SKILL.md");
|
|
1245
|
+
if (_existsOrSymlink(skill_file)) {
|
|
1246
|
+
fs.unlinkSync(skill_file);
|
|
1247
|
+
}
|
|
1248
|
+
const rel_path = _relativeToPosix(source_file, MODULE_STATE.PROJECT_ROOT);
|
|
1249
|
+
const value = auto ? _model_tier(source_file) : null;
|
|
1250
|
+
if (value !== null && value in TIER_TO_CLAUDE_MODEL) {
|
|
1251
|
+
_writeText(skill_file, _render_native_model_md(source_file, value));
|
|
1252
|
+
rendered += 1;
|
|
1253
|
+
} else {
|
|
1254
|
+
const rel_target = path.join("../../..", rel_path);
|
|
1255
|
+
fs.symlinkSync(rel_target, skill_file);
|
|
1256
|
+
}
|
|
1257
|
+
count += 1;
|
|
1258
|
+
}
|
|
1259
|
+
let removed_dirs = 0;
|
|
1260
|
+
for (const item of _iterdirSorted(MODULE_STATE.CLAUDE_SKILLS_DIR)) {
|
|
1261
|
+
if (!_isDirNoFollow(item) || _isSymlink(item)) {
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
const name = path.basename(item);
|
|
1265
|
+
if (skill_names.has(name) || current_slugs.has(name)) {
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
const skill_md = path.join(item, "SKILL.md");
|
|
1269
|
+
if (_isSymlink(skill_md) || _isFile(skill_md)) {
|
|
1270
|
+
const entries = _iterdirSorted(item);
|
|
1271
|
+
if (entries.length === 1 && path.basename(entries[0]) === "SKILL.md") {
|
|
1272
|
+
fs.unlinkSync(skill_md);
|
|
1273
|
+
fs.rmdirSync(item);
|
|
1274
|
+
removed_dirs += 1;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
let msg = ` \u2705 Created ${count} command entries in .claude/skills/`;
|
|
1279
|
+
if (rendered) {
|
|
1280
|
+
msg += ` (${rendered} rendered with native model:)`;
|
|
1281
|
+
}
|
|
1282
|
+
if (skipped) {
|
|
1283
|
+
msg += ` (${skipped} skipped \u2014 same-name skill exists)`;
|
|
1284
|
+
}
|
|
1285
|
+
if (reserved) {
|
|
1286
|
+
msg += ` (${reserved} withheld \u2014 Claude Code built-in name)`;
|
|
1287
|
+
}
|
|
1288
|
+
if (removed_dirs) {
|
|
1289
|
+
msg += ` (${removed_dirs} stale dirs removed)`;
|
|
1290
|
+
}
|
|
1291
|
+
info(msg);
|
|
1292
|
+
return count;
|
|
1293
|
+
}
|
|
1294
|
+
function generate_claude_subagents() {
|
|
1295
|
+
if (!_isDir(MODULE_STATE.SUBAGENTS_SOURCE)) {
|
|
1296
|
+
return 0;
|
|
1297
|
+
}
|
|
1298
|
+
_mkdirp(MODULE_STATE.CLAUDE_AGENTS_DIR);
|
|
1299
|
+
const sources = _rglobSorted(MODULE_STATE.SUBAGENTS_SOURCE, "*.md").filter(
|
|
1300
|
+
(p) => _isFile(p) && !path.basename(p).startsWith("_")
|
|
1301
|
+
);
|
|
1302
|
+
const current = /* @__PURE__ */ new Set();
|
|
1303
|
+
let count = 0;
|
|
1304
|
+
const defensePath = path.join(MODULE_STATE.SUBAGENTS_SOURCE, "_prompt-defense.md");
|
|
1305
|
+
const defense = _isFile(defensePath) ? _readText(defensePath).trim() : "";
|
|
1306
|
+
for (const src of sources) {
|
|
1307
|
+
const stem = path.basename(src, ".md");
|
|
1308
|
+
const text = _readText(src);
|
|
1309
|
+
if (!text.startsWith("---\n")) {
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
const end = text.indexOf("\n---\n", 4);
|
|
1313
|
+
if (end === -1) {
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
const fmBlock = text.slice(4, end);
|
|
1317
|
+
const body = text.slice(end + 5);
|
|
1318
|
+
const fm = _yamlParse(fmBlock);
|
|
1319
|
+
if (fm === null) {
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
const name = typeof fm["name"] === "string" ? fm["name"] : stem;
|
|
1323
|
+
const description = typeof fm["description"] === "string" ? fm["description"] : "";
|
|
1324
|
+
const toolsRaw = fm["tools"];
|
|
1325
|
+
const tools = Array.isArray(toolsRaw) ? toolsRaw.map((t) => String(t)).join(", ") : String(toolsRaw ?? "");
|
|
1326
|
+
const tier = typeof fm["model_tier"] === "string" ? fm["model_tier"] : "inherit";
|
|
1327
|
+
const model = tier === "inherit" ? "inherit" : TIER_TO_CLAUDE_MODEL[tier] ?? "inherit";
|
|
1328
|
+
const effort = typeof fm["effort"] === "string" ? fm["effort"] : null;
|
|
1329
|
+
const guardedBody = defense !== "" && !body.includes("prompt-defense-preamble") ? `
|
|
1330
|
+
${defense}
|
|
1331
|
+
${body}` : body;
|
|
1332
|
+
const out = `---
|
|
1333
|
+
name: ${name}
|
|
1334
|
+
description: ${description}
|
|
1335
|
+
tools: ${tools}
|
|
1336
|
+
model: ${model}
|
|
1337
|
+
` + (effort !== null ? `effort: ${effort}
|
|
1338
|
+
` : "") + "---\n" + guardedBody;
|
|
1339
|
+
const target = path.join(MODULE_STATE.CLAUDE_AGENTS_DIR, `${name}.md`);
|
|
1340
|
+
_writeText(target, out);
|
|
1341
|
+
current.add(`${name}.md`);
|
|
1342
|
+
count += 1;
|
|
1343
|
+
}
|
|
1344
|
+
for (const item of _iterdirSorted(MODULE_STATE.CLAUDE_AGENTS_DIR)) {
|
|
1345
|
+
const base = path.basename(item);
|
|
1346
|
+
if (base === "README.md" || current.has(base)) {
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
if (_isFile(item) && base.endsWith(".md") && !_isSymlink(item)) {
|
|
1350
|
+
fs.unlinkSync(item);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
info(` \u2705 Created ${count} subagent entries in .claude/agents/`);
|
|
1354
|
+
return count;
|
|
1355
|
+
}
|
|
1356
|
+
function generate_subagent_host_contexts() {
|
|
1357
|
+
if (!_isDir(MODULE_STATE.SUBAGENTS_SOURCE)) {
|
|
1358
|
+
return 0;
|
|
1359
|
+
}
|
|
1360
|
+
const sources = _rglobSorted(MODULE_STATE.SUBAGENTS_SOURCE, "*.md").filter((p) => _isFile(p));
|
|
1361
|
+
const rendered = [];
|
|
1362
|
+
for (const src of sources) {
|
|
1363
|
+
const stem = path.basename(src, ".md");
|
|
1364
|
+
const text = _readText(src);
|
|
1365
|
+
if (!text.startsWith("---\n")) {
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
const end = text.indexOf("\n---\n", 4);
|
|
1369
|
+
if (end === -1) {
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
const fm = _yamlParse(text.slice(4, end));
|
|
1373
|
+
if (fm === null) {
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const body = text.slice(end + 5);
|
|
1377
|
+
const name = typeof fm["name"] === "string" ? fm["name"] : stem;
|
|
1378
|
+
const description = typeof fm["description"] === "string" ? fm["description"] : "";
|
|
1379
|
+
const trustRaw = fm["trust"];
|
|
1380
|
+
const trust = typeof trustRaw === "string" ? trustRaw : typeof trustRaw === "object" && trustRaw !== null && typeof trustRaw["level"] === "string" ? trustRaw["level"] : "unknown";
|
|
1381
|
+
const lifecycle = typeof fm["lifecycle"] === "string" ? fm["lifecycle"] : "unknown";
|
|
1382
|
+
const tier = typeof fm["model_tier"] === "string" ? fm["model_tier"] : "inherit";
|
|
1383
|
+
const toolsRaw = fm["tools"];
|
|
1384
|
+
const tools = Array.isArray(toolsRaw) ? toolsRaw.map((t) => String(t)).join(", ") : String(toolsRaw ?? "");
|
|
1385
|
+
const out = `---
|
|
1386
|
+
description: ${description}
|
|
1387
|
+
---
|
|
1388
|
+
|
|
1389
|
+
# Subagent (passive reference): ${name}
|
|
1390
|
+
|
|
1391
|
+
> Passive subagent reference. This host has **no native subagent dispatch** \u2014
|
|
1392
|
+
> there is no \`@${name}\` here. Load this discipline manually and apply it.
|
|
1393
|
+
> Governance parity (trust / lifecycle / model tier) is preserved below;
|
|
1394
|
+
> feature parity is not (ADR-109 \xA74).
|
|
1395
|
+
|
|
1396
|
+
- trust: ${trust}
|
|
1397
|
+
- lifecycle: ${lifecycle}
|
|
1398
|
+
- model tier: ${tier}
|
|
1399
|
+
` + (tools ? `- tools: ${tools}
|
|
1400
|
+
` : "") + "\n" + _pyRstrip(body) + "\n";
|
|
1401
|
+
rendered.push({ name, text: out });
|
|
1402
|
+
}
|
|
1403
|
+
const targets = [
|
|
1404
|
+
{
|
|
1405
|
+
active: _tool_active("cursor"),
|
|
1406
|
+
dir: path.join(MODULE_STATE.PROJECT_ROOT, ".cursor", "subagents"),
|
|
1407
|
+
file: (n) => `${n}.md`,
|
|
1408
|
+
owns: (b) => b.endsWith(".md")
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
active: _tool_active("windsurf"),
|
|
1412
|
+
dir: path.join(MODULE_STATE.PROJECT_ROOT, ".windsurf", "subagents"),
|
|
1413
|
+
file: (n) => `${n}.md`,
|
|
1414
|
+
owns: (b) => b.endsWith(".md")
|
|
1415
|
+
},
|
|
1416
|
+
{
|
|
1417
|
+
active: _tool_active("cline"),
|
|
1418
|
+
dir: path.join(MODULE_STATE.PROJECT_ROOT, ".clinerules"),
|
|
1419
|
+
file: (n) => `${n}.subagent.md`,
|
|
1420
|
+
owns: (b) => b.endsWith(".subagent.md")
|
|
1421
|
+
}
|
|
1422
|
+
];
|
|
1423
|
+
let count = 0;
|
|
1424
|
+
for (const t of targets) {
|
|
1425
|
+
if (!t.active) {
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
const current = new Set(rendered.map((r) => t.file(r.name)));
|
|
1429
|
+
_mkdirp(t.dir);
|
|
1430
|
+
for (const r of rendered) {
|
|
1431
|
+
_writeText(path.join(t.dir, t.file(r.name)), r.text);
|
|
1432
|
+
count += 1;
|
|
1433
|
+
}
|
|
1434
|
+
if (_exists(t.dir)) {
|
|
1435
|
+
for (const item of _iterdirSorted(t.dir)) {
|
|
1436
|
+
const base = path.basename(item);
|
|
1437
|
+
if (base === "README.md" || current.has(base)) {
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
if (_isFile(item) && !_isSymlink(item) && t.owns(base)) {
|
|
1441
|
+
fs.unlinkSync(item);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
info(` \u2705 Projected ${count} passive subagent context file(s) to non-Claude-Code hosts`);
|
|
1447
|
+
return count;
|
|
1448
|
+
}
|
|
1449
|
+
var PLUGIN_POINTER_SLUG = "install-agent-config";
|
|
1450
|
+
var PLUGIN_POINTER_SKILL_BODY = `---
|
|
1451
|
+
name: install-agent-config
|
|
1452
|
+
description: >-
|
|
1453
|
+
Install or upgrade the event4u agent-config suite. This marketplace plugin
|
|
1454
|
+
is a bootstrap shim \u2014 it ships hooks plus this pointer only; ALL skills,
|
|
1455
|
+
rules, and commands are installed via \`npx -y @event4u/agent-config init\`.
|
|
1456
|
+
Use when the user asks to install, set up, or upgrade agent-config, or
|
|
1457
|
+
wonders why this plugin contains no other skills.
|
|
1458
|
+
---
|
|
1459
|
+
|
|
1460
|
+
# Install agent-config
|
|
1461
|
+
|
|
1462
|
+
This plugin is a **bootstrap shim**. It exists so the marketplace listing
|
|
1463
|
+
stays discoverable and existing plugin installs keep working hooks \u2014 the
|
|
1464
|
+
content surface (skills, rules, commands, personas) is distributed as a
|
|
1465
|
+
file projection, not through this plugin.
|
|
1466
|
+
|
|
1467
|
+
## Install (canonical, one command)
|
|
1468
|
+
|
|
1469
|
+
\`\`\`bash
|
|
1470
|
+
npx -y @event4u/agent-config init
|
|
1471
|
+
\`\`\`
|
|
1472
|
+
|
|
1473
|
+
The installer writes the full content projection, registers the dispatcher
|
|
1474
|
+
hooks as a managed block in \`~/.claude/settings.json\` (byte-identical to
|
|
1475
|
+
this plugin's hooks, so nothing double-fires), and keeps everything
|
|
1476
|
+
upgradeable via \`agent-config upgrade\`.
|
|
1477
|
+
|
|
1478
|
+
## Already installed both?
|
|
1479
|
+
|
|
1480
|
+
Run \`agent-config doctor\` to check for duplicate surfaces, and
|
|
1481
|
+
\`agent-config converge\` to clean up consented duplicates.
|
|
1482
|
+
`;
|
|
1483
|
+
function generate_plugin_command_skills() {
|
|
1484
|
+
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, "src", "domains"))) {
|
|
1485
|
+
return 0;
|
|
1486
|
+
}
|
|
1487
|
+
_mkdirp(MODULE_STATE.PLUGIN_SKILLS_DIR);
|
|
1488
|
+
const pointer_dir = path.join(MODULE_STATE.PLUGIN_SKILLS_DIR, PLUGIN_POINTER_SLUG);
|
|
1489
|
+
_mkdirp(pointer_dir);
|
|
1490
|
+
const pointer_file = path.join(pointer_dir, "SKILL.md");
|
|
1491
|
+
if (_isSymlink(pointer_file)) {
|
|
1492
|
+
fs.unlinkSync(pointer_file);
|
|
1493
|
+
}
|
|
1494
|
+
if (!_isFile(pointer_file) || fs.readFileSync(pointer_file, "utf-8") !== PLUGIN_POINTER_SKILL_BODY) {
|
|
1495
|
+
fs.writeFileSync(pointer_file, PLUGIN_POINTER_SKILL_BODY);
|
|
1496
|
+
}
|
|
1497
|
+
let removed_dirs = 0;
|
|
1498
|
+
for (const item of _iterdirSorted(MODULE_STATE.PLUGIN_SKILLS_DIR)) {
|
|
1499
|
+
if (!_isDirNoFollow(item) || _isSymlink(item)) {
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
if (path.basename(item) === PLUGIN_POINTER_SLUG) {
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
const entries = _iterdirSorted(item);
|
|
1506
|
+
const all_symlinks = entries.length > 0 && entries.every((e) => _isSymlink(e));
|
|
1507
|
+
const single_skill_md = entries.length === 1 && path.basename(entries[0]) === "SKILL.md" && _isFile(entries[0]);
|
|
1508
|
+
if (all_symlinks || single_skill_md) {
|
|
1509
|
+
for (const e of entries) {
|
|
1510
|
+
fs.unlinkSync(e);
|
|
1511
|
+
}
|
|
1512
|
+
fs.rmdirSync(item);
|
|
1513
|
+
removed_dirs += 1;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
let msg = " \u2705 Plugin skills: bootstrap shim (1 pointer skill) in .claude-plugin/skills/";
|
|
1517
|
+
if (removed_dirs) {
|
|
1518
|
+
msg += ` (${removed_dirs} content entries pruned)`;
|
|
1519
|
+
}
|
|
1520
|
+
info(msg);
|
|
1521
|
+
return 1;
|
|
1522
|
+
}
|
|
1523
|
+
function generate_persona_symlinks() {
|
|
1524
|
+
if (!_exists(MODULE_STATE.PERSONAS_SOURCE)) {
|
|
1525
|
+
_print(" \u26A0\uFE0F dist/agent-src/personas/ not found \u2014 skipping personas");
|
|
1526
|
+
return 0;
|
|
1527
|
+
}
|
|
1528
|
+
const personas = _rglobSorted(MODULE_STATE.PERSONAS_SOURCE, "*.md").filter((p) => _isFile(p) && path.basename(p, ".md") !== "README").map((p) => path.basename(p)).filter((name) => _isFile(path.join(MODULE_STATE.PERSONAS_SOURCE, name)));
|
|
1529
|
+
const direct = _iterdirSorted(MODULE_STATE.PERSONAS_SOURCE).filter((p) => p.endsWith(".md") && _isFile(p) && path.basename(p, ".md") !== "README").map((p) => path.basename(p)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1530
|
+
void personas;
|
|
1531
|
+
const list = direct;
|
|
1532
|
+
const tool_dirs = _filter_tool_dirs(PERSONA_TOOL_DIRS);
|
|
1533
|
+
let total = 0;
|
|
1534
|
+
for (const [tool_dir, rel_prefix] of Object.entries(tool_dirs)) {
|
|
1535
|
+
const target_dir = path.join(MODULE_STATE.PROJECT_ROOT, tool_dir);
|
|
1536
|
+
_mkdirp(target_dir);
|
|
1537
|
+
for (const item of _iterdirSorted(target_dir)) {
|
|
1538
|
+
const name = path.basename(item);
|
|
1539
|
+
if (_isSymlink(item) && !list.includes(name) && name !== "README.md") {
|
|
1540
|
+
fs.unlinkSync(item);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
for (const persona of list) {
|
|
1544
|
+
const link = path.join(target_dir, persona);
|
|
1545
|
+
const target = path.join(rel_prefix, persona);
|
|
1546
|
+
if (_existsOrSymlink(link)) {
|
|
1547
|
+
fs.unlinkSync(link);
|
|
1548
|
+
}
|
|
1549
|
+
fs.symlinkSync(target, link);
|
|
1550
|
+
total += 1;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
info(
|
|
1554
|
+
` \u2705 Created ${total} persona symlinks across ${Object.keys(tool_dirs).length} tool directories (${list.length} personas each)`
|
|
1555
|
+
);
|
|
1556
|
+
return total;
|
|
1557
|
+
}
|
|
1558
|
+
function generate_user_type_symlinks() {
|
|
1559
|
+
if (!_exists(MODULE_STATE.USER_TYPES_SOURCE)) {
|
|
1560
|
+
_print(" \u26A0\uFE0F dist/agent-src/user-types/ not found \u2014 skipping user-types");
|
|
1561
|
+
return 0;
|
|
1562
|
+
}
|
|
1563
|
+
const user_types = _iterdirSorted(MODULE_STATE.USER_TYPES_SOURCE).filter((p) => p.endsWith(".md") && _isFile(p) && path.basename(p, ".md") !== "README").map((p) => path.basename(p)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1564
|
+
const tool_dirs = _filter_tool_dirs(USER_TYPE_TOOL_DIRS);
|
|
1565
|
+
let total = 0;
|
|
1566
|
+
for (const [tool_dir, rel_prefix] of Object.entries(tool_dirs)) {
|
|
1567
|
+
const target_dir = path.join(MODULE_STATE.PROJECT_ROOT, tool_dir);
|
|
1568
|
+
_mkdirp(target_dir);
|
|
1569
|
+
for (const item of _iterdirSorted(target_dir)) {
|
|
1570
|
+
const name = path.basename(item);
|
|
1571
|
+
if (_isSymlink(item) && !user_types.includes(name) && name !== "README.md") {
|
|
1572
|
+
fs.unlinkSync(item);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
for (const user_type of user_types) {
|
|
1576
|
+
const link = path.join(target_dir, user_type);
|
|
1577
|
+
const target = path.join(rel_prefix, user_type);
|
|
1578
|
+
if (_existsOrSymlink(link)) {
|
|
1579
|
+
fs.unlinkSync(link);
|
|
1580
|
+
}
|
|
1581
|
+
fs.symlinkSync(target, link);
|
|
1582
|
+
total += 1;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
info(
|
|
1586
|
+
` \u2705 Created ${total} user-type symlinks across ${Object.keys(tool_dirs).length} tool directories (${user_types.length} user-types each)`
|
|
1587
|
+
);
|
|
1588
|
+
return total;
|
|
1589
|
+
}
|
|
1590
|
+
function generate_plugin_hooks() {
|
|
1591
|
+
const manifest_path = path.join(
|
|
1592
|
+
MODULE_STATE.PROJECT_ROOT,
|
|
1593
|
+
"src",
|
|
1594
|
+
"scripts",
|
|
1595
|
+
"hook_manifest.yaml"
|
|
1596
|
+
);
|
|
1597
|
+
if (!_exists(manifest_path)) {
|
|
1598
|
+
process.stderr.write(" \u26A0\uFE0F scripts/hook_manifest.yaml not found \u2014 skipping plugin hooks\n");
|
|
1599
|
+
return 0;
|
|
1600
|
+
}
|
|
1601
|
+
const matrix = build_claude_hook_matrix(manifest_path);
|
|
1602
|
+
const hooks = {};
|
|
1603
|
+
for (const [native, command] of Object.entries(matrix)) {
|
|
1604
|
+
hooks[native] = [{ hooks: [{ type: "command", command }] }];
|
|
1605
|
+
}
|
|
1606
|
+
const hooks_dir = path.join(MODULE_STATE.PROJECT_ROOT, "hooks");
|
|
1607
|
+
_mkdirp(hooks_dir);
|
|
1608
|
+
const out = path.join(hooks_dir, "hooks.json");
|
|
1609
|
+
_writeText(out, _jsonDumpsHooks({ hooks }) + "\n");
|
|
1610
|
+
info(` \u2705 Generated hooks/hooks.json (${Object.keys(hooks).length} Claude plugin hooks)`);
|
|
1611
|
+
return Object.keys(hooks).length;
|
|
1612
|
+
}
|
|
1613
|
+
function _jsonDumpsHooks(obj) {
|
|
1614
|
+
return _pyJson(obj, 0);
|
|
1615
|
+
}
|
|
1616
|
+
function _pyJson(value, indent) {
|
|
1617
|
+
const pad = " ".repeat(indent);
|
|
1618
|
+
const padInner = " ".repeat(indent + 1);
|
|
1619
|
+
if (Array.isArray(value)) {
|
|
1620
|
+
if (value.length === 0) {
|
|
1621
|
+
return "[]";
|
|
1622
|
+
}
|
|
1623
|
+
const items = value.map((v) => padInner + _pyJson(v, indent + 1));
|
|
1624
|
+
return `[
|
|
1625
|
+
${items.join(",\n")}
|
|
1626
|
+
${pad}]`;
|
|
1627
|
+
}
|
|
1628
|
+
if (value !== null && typeof value === "object") {
|
|
1629
|
+
const entries = Object.entries(value);
|
|
1630
|
+
if (entries.length === 0) {
|
|
1631
|
+
return "{}";
|
|
1632
|
+
}
|
|
1633
|
+
const items = entries.map(([k, v]) => `${padInner}${JSON.stringify(k)}: ${_pyJson(v, indent + 1)}`);
|
|
1634
|
+
return `{
|
|
1635
|
+
${items.join(",\n")}
|
|
1636
|
+
${pad}}`;
|
|
1637
|
+
}
|
|
1638
|
+
return JSON.stringify(value);
|
|
1639
|
+
}
|
|
1640
|
+
function _generate_tools_inner(cmd_slugs, skill_names) {
|
|
1641
|
+
info("\u{1F527} Generating multi-agent tool directories...\n");
|
|
1642
|
+
const rules = generate_rule_symlinks();
|
|
1643
|
+
const windsurfrules = _tool_active("windsurf") ? generate_windsurfrules() : 0;
|
|
1644
|
+
if (_tool_active("gemini")) {
|
|
1645
|
+
generate_gemini_md();
|
|
1646
|
+
}
|
|
1647
|
+
const skills = _tool_active("claude-code") ? generate_claude_skills(skill_names) : 0;
|
|
1648
|
+
const commands = _tool_active("claude-code") ? generate_claude_commands(cmd_slugs) : 0;
|
|
1649
|
+
const subagents = _tool_active("claude-code") ? generate_claude_subagents() : 0;
|
|
1650
|
+
const subagent_contexts = generate_subagent_host_contexts();
|
|
1651
|
+
const plugin_cmd_skills = _tool_active("claude-code") ? generate_plugin_command_skills() : 0;
|
|
1652
|
+
const plugin_hooks = _tool_active("claude-code") ? generate_plugin_hooks() : 0;
|
|
1653
|
+
const personas = generate_persona_symlinks();
|
|
1654
|
+
const user_types = generate_user_type_symlinks();
|
|
1655
|
+
const cursor_mdc = _tool_active("cursor") ? generate_cursor_mdc_rules() : 0;
|
|
1656
|
+
const windsurf_modern = _tool_active("windsurf") ? generate_windsurf_modern_rules() : 0;
|
|
1657
|
+
const cursor_cmds = _tool_active("cursor") ? generate_cursor_commands(cmd_slugs) : 0;
|
|
1658
|
+
const windsurf_wf = _tool_active("windsurf") ? generate_windsurf_workflows(cmd_slugs) : 0;
|
|
1659
|
+
const summary = `\u2705 generate-tools \u2014 rules=${rules} skills=${skills} commands=${commands} subagents=${subagents} subagent_contexts=${subagent_contexts} plugin_cmd_skills=${plugin_cmd_skills} plugin_hooks=${plugin_hooks} personas=${personas} user_types=${user_types} cursor_mdc=${cursor_mdc} windsurf_rules=${windsurf_modern} cursor_commands=${cursor_cmds} windsurf_workflows=${windsurf_wf} windsurfrules=${windsurfrules}`;
|
|
1660
|
+
if (resolve_level() === "verbose") {
|
|
1661
|
+
_print(`
|
|
1662
|
+
${summary}`);
|
|
1663
|
+
} else {
|
|
1664
|
+
success(summary);
|
|
1665
|
+
flush_summary();
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function generate_tools() {
|
|
1669
|
+
const [cmd_slugs, skill_names] = _resolve_active_predicates();
|
|
1670
|
+
const scoped = cmd_slugs !== null;
|
|
1671
|
+
try {
|
|
1672
|
+
_generate_tools_inner(cmd_slugs, skill_names);
|
|
1673
|
+
} catch (e) {
|
|
1674
|
+
if (scoped) {
|
|
1675
|
+
info(" \u26A0\uFE0F scoped projection failed \u2014 restoring full (legacy-all) projection");
|
|
1676
|
+
_generate_tools_inner(null, null);
|
|
1677
|
+
}
|
|
1678
|
+
throw e;
|
|
1679
|
+
}
|
|
1680
|
+
if (!scoped) {
|
|
1681
|
+
info(
|
|
1682
|
+
" \u2139\uFE0F Profile mode available \u2014 focused surface. Run `agent-config use --profile=developer`."
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
var AUGMENT_SYMLINK_DIRS = [
|
|
1687
|
+
"skills",
|
|
1688
|
+
"commands",
|
|
1689
|
+
"guidelines",
|
|
1690
|
+
"personas",
|
|
1691
|
+
"user-types",
|
|
1692
|
+
"templates",
|
|
1693
|
+
"contexts",
|
|
1694
|
+
"scripts"
|
|
1695
|
+
];
|
|
1696
|
+
var AUGMENT_SYMLINK_FILES = ["README.md"];
|
|
1697
|
+
function project_to_augment() {
|
|
1698
|
+
if (!_exists(MODULE_STATE.TARGET_DIR)) {
|
|
1699
|
+
_print(` \u26A0\uFE0F ${path.basename(MODULE_STATE.TARGET_DIR)}/ not found \u2014 nothing to project`);
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
_mkdirp(MODULE_STATE.AUGMENT_DIR);
|
|
1703
|
+
const use_symlinks = _read_augment_rules_use_symlinks();
|
|
1704
|
+
const src_rules = path.join(MODULE_STATE.TARGET_DIR, "rules");
|
|
1705
|
+
const dst_rules = path.join(MODULE_STATE.AUGMENT_DIR, "rules");
|
|
1706
|
+
_mkdirp(dst_rules);
|
|
1707
|
+
const existing = new Set(
|
|
1708
|
+
_iterdirSorted(dst_rules).filter((f) => _isFile(f) || _isSymlink(f)).map((f) => path.basename(f))
|
|
1709
|
+
);
|
|
1710
|
+
const current = /* @__PURE__ */ new Set();
|
|
1711
|
+
let written = 0;
|
|
1712
|
+
if (_exists(src_rules)) {
|
|
1713
|
+
const rules = _iterdirSorted(src_rules).filter((p) => p.endsWith(".md") && _isFile(p)).map((p) => path.basename(p)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1714
|
+
for (const rule of rules) {
|
|
1715
|
+
const target = path.join(dst_rules, rule);
|
|
1716
|
+
if (_isSymlink(target) || _existsOrSymlink(target)) {
|
|
1717
|
+
fs.unlinkSync(target);
|
|
1718
|
+
}
|
|
1719
|
+
if (use_symlinks) {
|
|
1720
|
+
fs.symlinkSync(path.join("..", "..", "dist/agent-src", "rules", rule), target);
|
|
1721
|
+
} else {
|
|
1722
|
+
copy_file(path.join(src_rules, rule), target);
|
|
1723
|
+
}
|
|
1724
|
+
current.add(rule);
|
|
1725
|
+
written += 1;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
let removed_rules = 0;
|
|
1729
|
+
for (const name of existing) {
|
|
1730
|
+
if (!current.has(name)) {
|
|
1731
|
+
fs.unlinkSync(path.join(dst_rules, name));
|
|
1732
|
+
removed_rules += 1;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
const mode_label = use_symlinks ? "Symlinked" : "Copied";
|
|
1736
|
+
_print(
|
|
1737
|
+
` \u2705 ${mode_label} ${written} rules to .augment/rules/` + (removed_rules ? ` (${removed_rules} stale removed)` : "")
|
|
1738
|
+
);
|
|
1739
|
+
for (const sub of AUGMENT_SYMLINK_DIRS) {
|
|
1740
|
+
const dst = path.join(MODULE_STATE.AUGMENT_DIR, sub);
|
|
1741
|
+
if (_isSymlink(dst) || _existsOrSymlink(dst)) {
|
|
1742
|
+
if (_isDir(dst) && !_isSymlink(dst)) {
|
|
1743
|
+
fs.rmSync(dst, { recursive: true, force: true });
|
|
1744
|
+
} else {
|
|
1745
|
+
fs.unlinkSync(dst);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
const src = path.join(MODULE_STATE.TARGET_DIR, sub);
|
|
1749
|
+
if (_exists(src)) {
|
|
1750
|
+
fs.symlinkSync(path.join("..", "dist/agent-src", sub), dst);
|
|
1751
|
+
_print(` \u2705 Symlinked .augment/${sub} \u2192 ../dist/agent-src/${sub}`);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
for (const name of AUGMENT_SYMLINK_FILES) {
|
|
1755
|
+
const dst = path.join(MODULE_STATE.AUGMENT_DIR, name);
|
|
1756
|
+
const src = path.join(MODULE_STATE.TARGET_DIR, name);
|
|
1757
|
+
if (_isSymlink(dst) || _existsOrSymlink(dst)) {
|
|
1758
|
+
fs.unlinkSync(dst);
|
|
1759
|
+
}
|
|
1760
|
+
if (_exists(src)) {
|
|
1761
|
+
fs.symlinkSync(path.join("..", "dist/agent-src", name), dst);
|
|
1762
|
+
_print(` \u2705 Symlinked .augment/${name} \u2192 ../dist/agent-src/${name}`);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
const known = /* @__PURE__ */ new Set([...AUGMENT_SYMLINK_DIRS, ...AUGMENT_SYMLINK_FILES, "rules", "state"]);
|
|
1766
|
+
for (const item of _iterdirSorted(MODULE_STATE.AUGMENT_DIR)) {
|
|
1767
|
+
const name = path.basename(item);
|
|
1768
|
+
if (known.has(name)) {
|
|
1769
|
+
continue;
|
|
1770
|
+
}
|
|
1771
|
+
if (_isSymlink(item) || _isFile(item)) {
|
|
1772
|
+
fs.unlinkSync(item);
|
|
1773
|
+
_print(` \u{1F5D1}\uFE0F Removed stale .augment/${name}`);
|
|
1774
|
+
} else if (_isDir(item)) {
|
|
1775
|
+
fs.rmSync(item, { recursive: true, force: true });
|
|
1776
|
+
_print(` \u{1F5D1}\uFE0F Removed stale .augment/${name}/`);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
function clean_tools() {
|
|
1781
|
+
const targets = [
|
|
1782
|
+
path.join(MODULE_STATE.PROJECT_ROOT, ".claude"),
|
|
1783
|
+
path.join(MODULE_STATE.PROJECT_ROOT, ".cursor"),
|
|
1784
|
+
path.join(MODULE_STATE.PROJECT_ROOT, ".clinerules"),
|
|
1785
|
+
path.join(MODULE_STATE.PROJECT_ROOT, ".windsurf"),
|
|
1786
|
+
path.join(MODULE_STATE.PROJECT_ROOT, ".windsurfrules"),
|
|
1787
|
+
path.join(MODULE_STATE.PROJECT_ROOT, "GEMINI.md")
|
|
1788
|
+
];
|
|
1789
|
+
for (const t of targets) {
|
|
1790
|
+
if (_isDirNoFollow(t) && !_isSymlink(t)) {
|
|
1791
|
+
fs.rmSync(t, { recursive: true, force: true });
|
|
1792
|
+
_print(` \u{1F5D1}\uFE0F Removed ${_relativeToPosix(t, MODULE_STATE.PROJECT_ROOT)}`);
|
|
1793
|
+
} else if (_existsOrSymlink(t)) {
|
|
1794
|
+
fs.unlinkSync(t);
|
|
1795
|
+
_print(` \u{1F5D1}\uFE0F Removed ${_relativeToPosix(t, MODULE_STATE.PROJECT_ROOT)}`);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
_print("\u2705 All generated tool files cleaned");
|
|
1799
|
+
}
|
|
1800
|
+
function _print(line) {
|
|
1801
|
+
process.stdout.write(`${line}
|
|
1802
|
+
`);
|
|
1803
|
+
}
|
|
1804
|
+
function main(argv = process.argv.slice(2)) {
|
|
1805
|
+
if (!_exists(MODULE_STATE.SOURCE_DIR) && !_any_source_root_exists()) {
|
|
1806
|
+
_print(
|
|
1807
|
+
`\u274C No source directory found (looked at the artefact roots under src/ and ${MODULE_STATE.SOURCE_DIR})`
|
|
1808
|
+
);
|
|
1809
|
+
return 1;
|
|
1810
|
+
}
|
|
1811
|
+
const arg = argv.length > 0 ? argv[0] : "--sync";
|
|
1812
|
+
if (arg === "--list") {
|
|
1813
|
+
const files = list_md_files(MODULE_STATE.SOURCE_DIR);
|
|
1814
|
+
_print(`\u{1F4C4} ${files.length} .md files total:
|
|
1815
|
+
`);
|
|
1816
|
+
for (const f of files) {
|
|
1817
|
+
_print(` ${f}`);
|
|
1818
|
+
}
|
|
1819
|
+
} else if (arg === "--changed") {
|
|
1820
|
+
const changed = list_changed_md(MODULE_STATE.SOURCE_DIR);
|
|
1821
|
+
if (changed.length === 0) {
|
|
1822
|
+
_print("\u2705 Every .md projection matches its source");
|
|
1823
|
+
return 0;
|
|
1824
|
+
}
|
|
1825
|
+
_print(`\u{1F4DD} ${changed.length} .md projection(s) out of date \u2014 run 'task sync':
|
|
1826
|
+
`);
|
|
1827
|
+
for (const f of changed) {
|
|
1828
|
+
_print(` ${f}`);
|
|
1829
|
+
}
|
|
1830
|
+
} else if (arg === "--check") {
|
|
1831
|
+
const [missing, stale] = check_sync(MODULE_STATE.SOURCE_DIR, MODULE_STATE.TARGET_DIR);
|
|
1832
|
+
if (missing.length === 0 && stale.length === 0) {
|
|
1833
|
+
_print("\u2705 dist/agent-src/ is in sync with the artefact roots under src/");
|
|
1834
|
+
return 0;
|
|
1835
|
+
}
|
|
1836
|
+
if (missing.length > 0) {
|
|
1837
|
+
_print(`\u274C Missing in dist/agent-src/ (${missing.length}):`);
|
|
1838
|
+
for (const f of missing) {
|
|
1839
|
+
_print(` ${f}`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
if (stale.length > 0) {
|
|
1843
|
+
_print(`\u274C Stale in dist/agent-src/ (${stale.length}):`);
|
|
1844
|
+
for (const f of stale) {
|
|
1845
|
+
_print(` ${f}`);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
_print(`
|
|
1849
|
+
Run 'task sync' \u2014 the projection is a deterministic copy, so it needs no agent step.`);
|
|
1850
|
+
return 1;
|
|
1851
|
+
} else if (arg === "--sync") {
|
|
1852
|
+
_print(`Source: the artefact roots under src/`);
|
|
1853
|
+
_print(`Target: ${MODULE_STATE.TARGET_DIR}
|
|
1854
|
+
`);
|
|
1855
|
+
_print("--- Syncing non-.md files ---");
|
|
1856
|
+
const copied = sync_non_md(MODULE_STATE.SOURCE_DIR, MODULE_STATE.TARGET_DIR);
|
|
1857
|
+
_print(`
|
|
1858
|
+
--- Cleanup stale files ---`);
|
|
1859
|
+
const deleted = cleanup_stale(MODULE_STATE.SOURCE_DIR, MODULE_STATE.TARGET_DIR);
|
|
1860
|
+
const changed = list_changed_md(MODULE_STATE.SOURCE_DIR);
|
|
1861
|
+
_print(`
|
|
1862
|
+
\u2705 Done: ${copied} copied, ${deleted} stale deleted`);
|
|
1863
|
+
if (changed.length > 0) {
|
|
1864
|
+
_print(`\u274C ${changed.length} .md projection(s) still out of date after sync (run --changed)`);
|
|
1865
|
+
} else {
|
|
1866
|
+
_print(`\u2705 Every .md projection matches its source`);
|
|
1867
|
+
}
|
|
1868
|
+
_print(`
|
|
1869
|
+
--- Projecting dist/agent-src/ \u2192 .augment/ ---`);
|
|
1870
|
+
project_to_augment();
|
|
1871
|
+
} else if (arg === "--generate-tools") {
|
|
1872
|
+
generate_tools();
|
|
1873
|
+
} else if (arg === "--clean-tools") {
|
|
1874
|
+
clean_tools();
|
|
1875
|
+
} else if (arg === "--project-augment") {
|
|
1876
|
+
project_to_augment();
|
|
1877
|
+
} else {
|
|
1878
|
+
_print(
|
|
1879
|
+
"Usage: condense [--sync|--list|--changed|--check|--generate-tools|--clean-tools|--project-augment]"
|
|
1880
|
+
);
|
|
1881
|
+
return 1;
|
|
1882
|
+
}
|
|
1883
|
+
return 0;
|
|
1884
|
+
}
|
|
1885
|
+
function _isCliEntry() {
|
|
1886
|
+
if (process.argv[1] === void 0) {
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
1890
|
+
if (import.meta.url === argvUrl) {
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
try {
|
|
1894
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
1895
|
+
const argv = fs.realpathSync(path.resolve(process.argv[1]));
|
|
1896
|
+
return here === argv;
|
|
1897
|
+
} catch {
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
var isMain = _isCliEntry();
|
|
1902
|
+
if (isMain) {
|
|
1903
|
+
process.exit(main());
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
// src/install/rule_scope.ts
|
|
1907
|
+
var COMPAT_ALWAYS_EXCLUDED = ["source-of-truth.md"];
|
|
1908
|
+
var LEGACY_ALL = { workspaces: null, packs: null };
|
|
1909
|
+
function _list(value) {
|
|
1910
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
1911
|
+
return value.map((v) => String(v));
|
|
1912
|
+
}
|
|
1913
|
+
return null;
|
|
1914
|
+
}
|
|
1915
|
+
function ruleScopeFromSettings(settings) {
|
|
1916
|
+
const proj = settings["projection"];
|
|
1917
|
+
if (typeof proj !== "object" || proj === null || Array.isArray(proj)) {
|
|
1918
|
+
return LEGACY_ALL;
|
|
1919
|
+
}
|
|
1920
|
+
const p = proj;
|
|
1921
|
+
return {
|
|
1922
|
+
workspaces: _list(p["rule_workspaces"]),
|
|
1923
|
+
packs: _list(p["rule_packs"]),
|
|
1924
|
+
roles: _list(p["rule_roles"])
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
function ruleFileArrives(sourcePath, scope) {
|
|
1928
|
+
if (!sourcePath.endsWith(".md")) {
|
|
1929
|
+
return true;
|
|
1930
|
+
}
|
|
1931
|
+
const basename3 = path2.basename(sourcePath);
|
|
1932
|
+
if (COMPAT_ALWAYS_EXCLUDED.includes(basename3)) {
|
|
1933
|
+
return false;
|
|
1934
|
+
}
|
|
1935
|
+
return rule_in_scope(sourcePath, scope.workspaces, scope.packs, scope.roles ?? null);
|
|
1936
|
+
}
|
|
1937
|
+
function excludedRuleBasenames(rulesDir, scope) {
|
|
1938
|
+
let entries;
|
|
1939
|
+
try {
|
|
1940
|
+
entries = fs2.readdirSync(rulesDir).sort();
|
|
1941
|
+
} catch {
|
|
1942
|
+
return [];
|
|
1943
|
+
}
|
|
1944
|
+
const out = [];
|
|
1945
|
+
for (const name of entries) {
|
|
1946
|
+
if (!name.endsWith(".md")) {
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
const full = path2.join(rulesDir, name);
|
|
1950
|
+
try {
|
|
1951
|
+
if (!fs2.statSync(full).isFile()) {
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1954
|
+
} catch {
|
|
1955
|
+
continue;
|
|
1956
|
+
}
|
|
1957
|
+
if (!ruleFileArrives(full, scope)) {
|
|
1958
|
+
out.push(name);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return out;
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
export {
|
|
1965
|
+
LEGACY_ALL,
|
|
1966
|
+
ruleScopeFromSettings,
|
|
1967
|
+
ruleFileArrives,
|
|
1968
|
+
excludedRuleBasenames
|
|
1969
|
+
};
|