agconf 0.2.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.
@@ -0,0 +1,653 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/core/skill-metadata.ts
4
+ import { createHash as createHash2 } from "crypto";
5
+ import * as fs from "fs/promises";
6
+ import * as path from "path";
7
+ import fg from "fast-glob";
8
+
9
+ // src/config/schema.ts
10
+ import { z } from "zod";
11
+ var CURRENT_CONFIG_VERSION = "1.0.0";
12
+ var CanonicalMetaSchema = z.object({
13
+ /** Unique identifier for this canonical source (e.g., "acme-standards") */
14
+ name: z.string().min(1),
15
+ /** Organization name for display purposes */
16
+ organization: z.string().optional(),
17
+ /** Description of what this canonical repository provides */
18
+ description: z.string().optional()
19
+ });
20
+ var CanonicalPathsSchema = z.object({
21
+ /** Path to the global instructions file (e.g., "instructions/AGENTS.md") */
22
+ instructions: z.string().default("instructions/AGENTS.md"),
23
+ /** Path to the skills directory (e.g., "skills") */
24
+ skills_dir: z.string().default("skills"),
25
+ /** Path to the rules directory (e.g., "rules") - optional for backward compat */
26
+ rules_dir: z.string().optional()
27
+ });
28
+ var MarkersConfigSchema = z.object({
29
+ /**
30
+ * Prefix for marker comments in managed files.
31
+ * Generates markers like: <!-- {prefix}:global:start -->
32
+ * Default: "agent-conf"
33
+ */
34
+ prefix: z.string().default("agent-conf")
35
+ });
36
+ var MergeConfigSchema = z.object({
37
+ /** Whether to preserve repository-specific content blocks during sync */
38
+ preserve_repo_content: z.boolean().default(true)
39
+ });
40
+ var CanonicalRepoConfigSchema = z.object({
41
+ /** Schema version in semver format (e.g., "1.0.0") */
42
+ version: z.string().regex(/^\d+\.\d+\.\d+$/, "Version must be in semver format (e.g., 1.0.0)"),
43
+ meta: CanonicalMetaSchema,
44
+ content: CanonicalPathsSchema.default({}),
45
+ /** List of supported target agents (e.g., ["claude", "codex"]) */
46
+ targets: z.array(z.string()).default(["claude"]),
47
+ markers: MarkersConfigSchema.default({}),
48
+ merge: MergeConfigSchema.default({})
49
+ });
50
+ var SourceConfigSchema = z.object({
51
+ /** URL of the canonical repository (e.g., "https://github.com/acme/agent-standards") */
52
+ url: z.string().url().optional(),
53
+ /** GitHub repository in owner/repo format (alternative to url) */
54
+ repository: z.string().optional(),
55
+ /** Git ref to sync from (branch, tag, or commit) */
56
+ ref: z.string().optional(),
57
+ /** Priority for conflict resolution (higher wins). Default: 0 */
58
+ priority: z.number().default(0)
59
+ });
60
+ var DownstreamConfigSchema = z.object({
61
+ /**
62
+ * List of canonical sources to sync from.
63
+ * For MVP, only single source is supported; multi-source is future work.
64
+ */
65
+ sources: z.array(SourceConfigSchema).min(1).optional(),
66
+ /** Override which targets to sync to (defaults to source repo's targets) */
67
+ targets: z.array(z.string()).optional()
68
+ });
69
+ var ResolvedConfigSchema = z.object({
70
+ /** Name of the canonical source */
71
+ name: z.string(),
72
+ /** Organization name */
73
+ organization: z.string().optional(),
74
+ /** Path to instructions file within source */
75
+ instructionsPath: z.string(),
76
+ /** Path to skills directory within source */
77
+ skillsDir: z.string(),
78
+ /** Path to rules directory within source - optional for backward compat */
79
+ rulesDir: z.string().optional(),
80
+ /** Marker prefix for managed content */
81
+ markerPrefix: z.string(),
82
+ /** Target agents to sync to */
83
+ targets: z.array(z.string()),
84
+ /** Whether to preserve repo-specific content during merge */
85
+ preserveRepoContent: z.boolean(),
86
+ /** CLI name for hooks and workflows */
87
+ cliName: z.string().default("agent-conf"),
88
+ /** Directory name for lockfile and config (e.g., ".agent-conf") */
89
+ configDir: z.string().default(".agent-conf"),
90
+ /** Lockfile name */
91
+ lockfileName: z.string().default("lockfile.json")
92
+ });
93
+ function getMarkers(prefix) {
94
+ return {
95
+ globalStart: `<!-- ${prefix}:global:start -->`,
96
+ globalEnd: `<!-- ${prefix}:global:end -->`,
97
+ repoStart: `<!-- ${prefix}:repo:start -->`,
98
+ repoEnd: `<!-- ${prefix}:repo:end -->`
99
+ };
100
+ }
101
+ function getMetadataKeys(prefix) {
102
+ const keyPrefix = prefix.replace(/-/g, "_");
103
+ return {
104
+ managed: `${keyPrefix}_managed`,
105
+ contentHash: `${keyPrefix}_content_hash`
106
+ };
107
+ }
108
+
109
+ // src/core/markers.ts
110
+ import { createHash } from "crypto";
111
+ var DEFAULT_MARKER_PREFIX = "agent-conf";
112
+ function getMarkers2(prefix = DEFAULT_MARKER_PREFIX) {
113
+ return getMarkers(prefix);
114
+ }
115
+ function parseAgentsMd(content, options = {}) {
116
+ const { prefix = DEFAULT_MARKER_PREFIX } = options;
117
+ const markers = getMarkers2(prefix);
118
+ return parseWithMarkers(content, markers);
119
+ }
120
+ function parseWithMarkers(content, markers) {
121
+ const globalStartIdx = content.indexOf(markers.globalStart);
122
+ const globalEndIdx = content.indexOf(markers.globalEnd);
123
+ const repoStartIdx = content.indexOf(markers.repoStart);
124
+ const repoEndIdx = content.indexOf(markers.repoEnd);
125
+ const hasMarkers = globalStartIdx !== -1 || repoStartIdx !== -1;
126
+ let globalBlock = null;
127
+ let repoBlock = null;
128
+ if (globalStartIdx !== -1 && globalEndIdx !== -1 && globalEndIdx > globalStartIdx) {
129
+ const startContent = globalStartIdx + markers.globalStart.length;
130
+ globalBlock = content.slice(startContent, globalEndIdx).trim();
131
+ }
132
+ if (repoStartIdx !== -1 && repoEndIdx !== -1 && repoEndIdx > repoStartIdx) {
133
+ const startContent = repoStartIdx + markers.repoStart.length;
134
+ repoBlock = content.slice(startContent, repoEndIdx).trim();
135
+ }
136
+ return { globalBlock, repoBlock, hasMarkers };
137
+ }
138
+ function computeGlobalBlockHash(content) {
139
+ const hash = createHash("sha256").update(content.trim()).digest("hex");
140
+ return `sha256:${hash.slice(0, 12)}`;
141
+ }
142
+ function buildGlobalBlock(content, metadata, options = {}) {
143
+ const { prefix = DEFAULT_MARKER_PREFIX, cliName = "agent-conf" } = options;
144
+ const markers = getMarkers2(prefix);
145
+ const contentHash = metadata.contentHash ?? computeGlobalBlockHash(content);
146
+ const lines = [
147
+ markers.globalStart,
148
+ `<!-- DO NOT EDIT THIS SECTION - Managed by ${cliName} CLI -->`,
149
+ `<!-- Content hash: ${contentHash} -->`,
150
+ "",
151
+ content.trim(),
152
+ "",
153
+ markers.globalEnd
154
+ ];
155
+ return lines.join("\n");
156
+ }
157
+ function buildRepoBlock(content, options = {}) {
158
+ const { prefix = DEFAULT_MARKER_PREFIX } = options;
159
+ const markers = getMarkers2(prefix);
160
+ const lines = [
161
+ markers.repoStart,
162
+ "<!-- Repository-specific instructions below -->",
163
+ "",
164
+ content?.trim() ?? "",
165
+ "",
166
+ markers.repoEnd
167
+ ];
168
+ return lines.join("\n");
169
+ }
170
+ function buildAgentsMd(globalContent, repoContent, metadata, options = {}) {
171
+ const globalBlock = buildGlobalBlock(globalContent, metadata, options);
172
+ const repoBlock = buildRepoBlock(repoContent, options);
173
+ return `${globalBlock}
174
+
175
+ ${repoBlock}
176
+ `;
177
+ }
178
+ function extractRepoBlockContent(parsed) {
179
+ if (!parsed.repoBlock) return null;
180
+ const lines = parsed.repoBlock.split("\n");
181
+ const filtered = lines.filter((line) => {
182
+ const trimmed = line.trim();
183
+ return trimmed !== "<!-- Repository-specific instructions below -->";
184
+ });
185
+ const result = filtered.join("\n").trim();
186
+ return result || null;
187
+ }
188
+ function stripMetadataComments(globalBlock) {
189
+ const lines = globalBlock.split("\n");
190
+ const filteredLines = lines.filter((line) => {
191
+ const trimmed = line.trim();
192
+ if (trimmed.startsWith("<!-- DO NOT EDIT")) return false;
193
+ if (trimmed.startsWith("<!-- Source:")) return false;
194
+ if (trimmed.startsWith("<!-- Last synced:")) return false;
195
+ if (trimmed.startsWith("<!-- Content hash:")) return false;
196
+ return true;
197
+ });
198
+ return filteredLines.join("\n").trim();
199
+ }
200
+ function parseGlobalBlockMetadata(globalBlock) {
201
+ const result = {};
202
+ const sourceMatch = globalBlock.match(/<!--\s*Source:\s*(.+?)\s*-->/);
203
+ if (sourceMatch?.[1]) {
204
+ result.source = sourceMatch[1];
205
+ }
206
+ const syncedMatch = globalBlock.match(/<!--\s*Last synced:\s*(.+?)\s*-->/);
207
+ if (syncedMatch?.[1]) {
208
+ result.syncedAt = syncedMatch[1];
209
+ }
210
+ const hashMatch = globalBlock.match(/<!--\s*Content hash:\s*(.+?)\s*-->/);
211
+ if (hashMatch?.[1]) {
212
+ result.contentHash = hashMatch[1];
213
+ }
214
+ return result;
215
+ }
216
+ function hasGlobalBlockChanges(agentsMdContent, options = {}) {
217
+ const parsed = parseAgentsMd(agentsMdContent, options);
218
+ if (!parsed.globalBlock) {
219
+ return false;
220
+ }
221
+ const metadata = parseGlobalBlockMetadata(parsed.globalBlock);
222
+ if (!metadata.contentHash) {
223
+ return false;
224
+ }
225
+ const content = stripMetadataComments(parsed.globalBlock);
226
+ const currentHash = computeGlobalBlockHash(content);
227
+ return metadata.contentHash !== currentHash;
228
+ }
229
+ function isAgentsMdManaged(agentsMdContent, options = {}) {
230
+ const parsed = parseAgentsMd(agentsMdContent, options);
231
+ return parsed.hasMarkers && parsed.globalBlock !== null;
232
+ }
233
+ function parseRulesSection(agentsMdContent, options = {}) {
234
+ const { prefix = DEFAULT_MARKER_PREFIX } = options;
235
+ const rulesStartMarker = `<!-- ${prefix}:rules:start -->`;
236
+ const rulesEndMarker = `<!-- ${prefix}:rules:end -->`;
237
+ const startIdx = agentsMdContent.indexOf(rulesStartMarker);
238
+ const endIdx = agentsMdContent.indexOf(rulesEndMarker);
239
+ if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
240
+ return { content: null, hasMarkers: false };
241
+ }
242
+ const content = agentsMdContent.slice(startIdx + rulesStartMarker.length, endIdx).trim();
243
+ return { content, hasMarkers: true };
244
+ }
245
+ function parseRulesSectionMetadata(rulesSection) {
246
+ const result = {};
247
+ const hashMatch = rulesSection.match(/<!--\s*Content hash:\s*(.+?)\s*-->/);
248
+ if (hashMatch?.[1]) {
249
+ result.contentHash = hashMatch[1];
250
+ }
251
+ const countMatch = rulesSection.match(/<!--\s*Rule count:\s*(\d+)\s*-->/);
252
+ if (countMatch?.[1]) {
253
+ result.ruleCount = Number.parseInt(countMatch[1], 10);
254
+ }
255
+ return result;
256
+ }
257
+ function stripRulesSectionMetadata(rulesSection) {
258
+ const lines = rulesSection.split("\n");
259
+ const filteredLines = lines.filter((line) => {
260
+ const trimmed = line.trim();
261
+ if (trimmed.startsWith("<!-- DO NOT EDIT")) return false;
262
+ if (trimmed.startsWith("<!-- Content hash:")) return false;
263
+ if (trimmed.startsWith("<!-- Rule count:")) return false;
264
+ return true;
265
+ });
266
+ return filteredLines.join("\n").trim();
267
+ }
268
+ function computeRulesSectionHash(content) {
269
+ const hash = createHash("sha256").update(content.trim()).digest("hex");
270
+ return `sha256:${hash.slice(0, 12)}`;
271
+ }
272
+ function hasRulesSectionChanges(agentsMdContent, options = {}) {
273
+ const parsed = parseRulesSection(agentsMdContent, options);
274
+ if (!parsed.content) {
275
+ return false;
276
+ }
277
+ const metadata = parseRulesSectionMetadata(parsed.content);
278
+ if (!metadata.contentHash) {
279
+ return false;
280
+ }
281
+ const content = stripRulesSectionMetadata(parsed.content);
282
+ const currentHash = computeRulesSectionHash(content);
283
+ return metadata.contentHash !== currentHash;
284
+ }
285
+
286
+ // src/core/skill-metadata.ts
287
+ var DEFAULT_METADATA_PREFIX = "agent-conf";
288
+ function getMetadataKeyNames(prefix = DEFAULT_METADATA_PREFIX) {
289
+ return getMetadataKeys(prefix);
290
+ }
291
+ var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
292
+ function parseFrontmatter(content) {
293
+ const match = content.match(FRONTMATTER_REGEX);
294
+ if (!match || !match[1]) {
295
+ return { frontmatter: {}, body: content, raw: "" };
296
+ }
297
+ const rawYaml = match[1];
298
+ const body = content.slice(match[0].length);
299
+ const frontmatter = parseSimpleYaml(rawYaml);
300
+ return { frontmatter, body, raw: rawYaml };
301
+ }
302
+ function parseSimpleYaml(yaml) {
303
+ const result = {};
304
+ const lines = yaml.split("\n");
305
+ let currentKey = null;
306
+ let nestedObject = null;
307
+ for (const line of lines) {
308
+ if (line.trim() === "") continue;
309
+ if (line.startsWith(" ") && currentKey && nestedObject !== null) {
310
+ const nestedMatch = line.match(/^\s+(\w+):\s*["']?(.*)["']?$/);
311
+ if (nestedMatch?.[1] && nestedMatch[2] !== void 0) {
312
+ const key = nestedMatch[1];
313
+ const value = nestedMatch[2];
314
+ nestedObject[key] = value.replace(/^["']|["']$/g, "");
315
+ }
316
+ continue;
317
+ }
318
+ if (currentKey && nestedObject !== null) {
319
+ result[currentKey] = nestedObject;
320
+ nestedObject = null;
321
+ }
322
+ const match = line.match(/^(\w+):\s*(.*)$/);
323
+ if (match?.[1] && match[2] !== void 0) {
324
+ const key = match[1];
325
+ const value = match[2];
326
+ currentKey = key;
327
+ if (value.trim() === "") {
328
+ nestedObject = {};
329
+ } else {
330
+ result[key] = value.replace(/^["']|["']$/g, "");
331
+ }
332
+ }
333
+ }
334
+ if (currentKey && nestedObject !== null) {
335
+ result[currentKey] = nestedObject;
336
+ }
337
+ return result;
338
+ }
339
+ function serializeFrontmatter(frontmatter) {
340
+ const lines = [];
341
+ for (const [key, value] of Object.entries(frontmatter)) {
342
+ if (typeof value === "object" && value !== null) {
343
+ lines.push(`${key}:`);
344
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
345
+ const quotedValue = needsQuoting(String(nestedValue)) ? `"${String(nestedValue)}"` : String(nestedValue);
346
+ lines.push(` ${nestedKey}: ${quotedValue}`);
347
+ }
348
+ } else {
349
+ const strValue = String(value);
350
+ if (strValue.length > 80 || strValue.includes("\n")) {
351
+ lines.push(`${key}: ${strValue}`);
352
+ } else {
353
+ lines.push(`${key}: ${strValue}`);
354
+ }
355
+ }
356
+ }
357
+ return lines.join("\n");
358
+ }
359
+ function validateSkillFrontmatter(content, skillName, filePath) {
360
+ const { frontmatter } = parseFrontmatter(content);
361
+ const errors = [];
362
+ if (Object.keys(frontmatter).length === 0) {
363
+ errors.push("Missing frontmatter (must have --- delimiters)");
364
+ } else {
365
+ if (!frontmatter.name) {
366
+ errors.push("Missing required field: name");
367
+ }
368
+ if (!frontmatter.description) {
369
+ errors.push("Missing required field: description");
370
+ }
371
+ }
372
+ if (errors.length > 0) {
373
+ return { skillName, path: filePath, errors };
374
+ }
375
+ return null;
376
+ }
377
+ function needsQuoting(value) {
378
+ return value.includes(":") || value.includes("#") || value.includes("@") || value === "true" || value === "false" || /^\d+$/.test(value);
379
+ }
380
+ function computeContentHash(content, options = {}) {
381
+ const stripped = stripManagedMetadata(content, options);
382
+ const hash = createHash2("sha256").update(stripped).digest("hex");
383
+ return `sha256:${hash.slice(0, 12)}`;
384
+ }
385
+ function stripManagedMetadata(content, options = {}) {
386
+ const { metadataPrefix = DEFAULT_METADATA_PREFIX } = options;
387
+ const { frontmatter, body } = parseFrontmatter(content);
388
+ if (Object.keys(frontmatter).length === 0) {
389
+ return content;
390
+ }
391
+ const keyPrefix = `${metadataPrefix.replace(/-/g, "_")}_`;
392
+ if (frontmatter.metadata && typeof frontmatter.metadata === "object") {
393
+ const metadata = frontmatter.metadata;
394
+ const cleanedMetadata = {};
395
+ for (const [key, value] of Object.entries(metadata)) {
396
+ if (!key.startsWith(keyPrefix)) {
397
+ cleanedMetadata[key] = value;
398
+ }
399
+ }
400
+ if (Object.keys(cleanedMetadata).length > 0) {
401
+ frontmatter.metadata = cleanedMetadata;
402
+ } else {
403
+ delete frontmatter.metadata;
404
+ }
405
+ }
406
+ const yamlContent = serializeFrontmatter(frontmatter);
407
+ return `---
408
+ ${yamlContent}
409
+ ---
410
+ ${body}`;
411
+ }
412
+ function addManagedMetadata(content, options = {}) {
413
+ const { metadataPrefix = DEFAULT_METADATA_PREFIX } = options;
414
+ const { frontmatter, body } = parseFrontmatter(content);
415
+ const contentHash = computeContentHash(content, options);
416
+ if (!frontmatter.metadata || typeof frontmatter.metadata !== "object") {
417
+ frontmatter.metadata = {};
418
+ }
419
+ const metadata = frontmatter.metadata;
420
+ const keys = getMetadataKeyNames(metadataPrefix);
421
+ metadata[keys.managed] = "true";
422
+ metadata[keys.contentHash] = contentHash;
423
+ const yamlContent = serializeFrontmatter(frontmatter);
424
+ return `---
425
+ ${yamlContent}
426
+ ---
427
+ ${body}`;
428
+ }
429
+ function hasManualChanges(content, options = {}) {
430
+ const { metadataPrefix = DEFAULT_METADATA_PREFIX } = options;
431
+ const { frontmatter } = parseFrontmatter(content);
432
+ if (!frontmatter.metadata || typeof frontmatter.metadata !== "object") {
433
+ return false;
434
+ }
435
+ const metadata = frontmatter.metadata;
436
+ const keys = getMetadataKeyNames(metadataPrefix);
437
+ const storedHash = metadata[keys.contentHash];
438
+ if (!storedHash) {
439
+ return false;
440
+ }
441
+ const currentHash = computeContentHash(content, options);
442
+ return storedHash !== currentHash;
443
+ }
444
+ function isManaged(content, options = {}) {
445
+ const { metadataPrefix = DEFAULT_METADATA_PREFIX } = options;
446
+ const { frontmatter } = parseFrontmatter(content);
447
+ if (!frontmatter.metadata || typeof frontmatter.metadata !== "object") {
448
+ return false;
449
+ }
450
+ const metadata = frontmatter.metadata;
451
+ const keys = getMetadataKeyNames(metadataPrefix);
452
+ return metadata[keys.managed] === "true";
453
+ }
454
+ async function checkSkillFiles(targetDir, targets = ["claude"], options = {}) {
455
+ const results = [];
456
+ for (const target of targets) {
457
+ const skillsDir = path.join(targetDir, `.${target}`, "skills");
458
+ try {
459
+ await fs.access(skillsDir);
460
+ } catch {
461
+ continue;
462
+ }
463
+ const skillFiles = await fg("*/SKILL.md", {
464
+ cwd: skillsDir,
465
+ absolute: false
466
+ });
467
+ for (const skillFile of skillFiles) {
468
+ const fullPath = path.join(skillsDir, skillFile);
469
+ const skillName = path.dirname(skillFile);
470
+ const relativePath = path.join(`.${target}`, "skills", skillFile);
471
+ try {
472
+ const content = await fs.readFile(fullPath, "utf-8");
473
+ const fileIsManaged = isManaged(content, options);
474
+ const hasChanges = fileIsManaged && hasManualChanges(content, options);
475
+ results.push({
476
+ path: relativePath,
477
+ skillName,
478
+ isManaged: fileIsManaged,
479
+ hasChanges
480
+ });
481
+ } catch (_error) {
482
+ }
483
+ }
484
+ }
485
+ return results;
486
+ }
487
+ async function getModifiedSkillFiles(targetDir, targets = ["claude"], options = {}) {
488
+ const allFiles = await checkSkillFiles(targetDir, targets, options);
489
+ return allFiles.filter((f) => f.hasChanges);
490
+ }
491
+ async function checkRuleFiles(targetDir, targets = ["claude"], options = {}) {
492
+ const results = [];
493
+ for (const target of targets) {
494
+ const rulesDir = path.join(targetDir, `.${target}`, "rules");
495
+ try {
496
+ await fs.access(rulesDir);
497
+ } catch {
498
+ continue;
499
+ }
500
+ const ruleFiles = await fg("**/*.md", {
501
+ cwd: rulesDir,
502
+ absolute: false
503
+ });
504
+ for (const ruleFile of ruleFiles) {
505
+ const fullPath = path.join(rulesDir, ruleFile);
506
+ const relativePath = path.join(`.${target}`, "rules", ruleFile);
507
+ try {
508
+ const content = await fs.readFile(fullPath, "utf-8");
509
+ const fileIsManaged = isManaged(content, options);
510
+ const hasChanges = fileIsManaged && hasManualChanges(content, options);
511
+ const { frontmatter } = parseFrontmatter(content);
512
+ const metadata = frontmatter.metadata;
513
+ const keyPrefix = (options.metadataPrefix || "agent-conf").replace(/-/g, "_");
514
+ const rulePath = metadata?.[`${keyPrefix}_source_path`] || ruleFile;
515
+ results.push({
516
+ path: relativePath,
517
+ rulePath,
518
+ isManaged: fileIsManaged,
519
+ hasChanges
520
+ });
521
+ } catch (_error) {
522
+ }
523
+ }
524
+ }
525
+ return results;
526
+ }
527
+ async function getModifiedRuleFiles(targetDir, targets = ["claude"], options = {}) {
528
+ const allFiles = await checkRuleFiles(targetDir, targets, options);
529
+ return allFiles.filter((f) => f.hasChanges);
530
+ }
531
+ async function checkAgentsMd(targetDir, options = {}) {
532
+ const agentsMdPath = path.join(targetDir, "AGENTS.md");
533
+ try {
534
+ const content = await fs.readFile(agentsMdPath, "utf-8");
535
+ const managed = isAgentsMdManaged(content, options);
536
+ if (!managed) {
537
+ return null;
538
+ }
539
+ const hasChanges = hasGlobalBlockChanges(content, options);
540
+ const parsed = parseAgentsMd(content, options);
541
+ let source;
542
+ let syncedAt;
543
+ if (parsed.globalBlock) {
544
+ const metadata = parseGlobalBlockMetadata(parsed.globalBlock);
545
+ source = metadata.source;
546
+ syncedAt = metadata.syncedAt;
547
+ }
548
+ const result = {
549
+ path: "AGENTS.md",
550
+ type: "agents",
551
+ isManaged: managed,
552
+ hasChanges
553
+ };
554
+ if (source !== void 0) result.source = source;
555
+ if (syncedAt !== void 0) result.syncedAt = syncedAt;
556
+ return result;
557
+ } catch {
558
+ return null;
559
+ }
560
+ }
561
+ async function checkAllManagedFiles(targetDir, targets = ["claude"], options = {}) {
562
+ const results = [];
563
+ const markerOptions = options.markerPrefix ? { prefix: options.markerPrefix } : {};
564
+ const metadataOptions = options.metadataPrefix ? { metadataPrefix: options.metadataPrefix } : {};
565
+ const agentsMdResult = await checkAgentsMd(targetDir, markerOptions);
566
+ if (agentsMdResult) {
567
+ results.push(agentsMdResult);
568
+ }
569
+ const rulesSectionResult = await checkAgentsMdRulesSection(targetDir, markerOptions);
570
+ if (rulesSectionResult) {
571
+ results.push(rulesSectionResult);
572
+ }
573
+ const skillFiles = await checkSkillFiles(targetDir, targets, metadataOptions);
574
+ for (const skill of skillFiles) {
575
+ if (skill.isManaged) {
576
+ results.push({
577
+ path: skill.path,
578
+ type: "skill",
579
+ skillName: skill.skillName,
580
+ isManaged: skill.isManaged,
581
+ hasChanges: skill.hasChanges
582
+ });
583
+ }
584
+ }
585
+ const ruleFiles = await checkRuleFiles(targetDir, targets, metadataOptions);
586
+ for (const rule of ruleFiles) {
587
+ if (rule.isManaged) {
588
+ results.push({
589
+ path: rule.path,
590
+ type: "rule",
591
+ rulePath: rule.rulePath,
592
+ isManaged: rule.isManaged,
593
+ hasChanges: rule.hasChanges
594
+ });
595
+ }
596
+ }
597
+ return results;
598
+ }
599
+ async function checkAgentsMdRulesSection(targetDir, options = {}) {
600
+ const agentsMdPath = path.join(targetDir, "AGENTS.md");
601
+ try {
602
+ const content = await fs.readFile(agentsMdPath, "utf-8");
603
+ const parsed = parseRulesSection(content, options);
604
+ if (!parsed.hasMarkers || !parsed.content) {
605
+ return null;
606
+ }
607
+ const hasChanges = hasRulesSectionChanges(content, options);
608
+ return {
609
+ path: "AGENTS.md",
610
+ type: "rules-section",
611
+ isManaged: true,
612
+ hasChanges
613
+ };
614
+ } catch {
615
+ return null;
616
+ }
617
+ }
618
+ async function getModifiedManagedFiles(targetDir, targets = ["claude"], options = {}) {
619
+ const allFiles = await checkAllManagedFiles(targetDir, targets, options);
620
+ return allFiles.filter((f) => f.hasChanges);
621
+ }
622
+
623
+ export {
624
+ CURRENT_CONFIG_VERSION,
625
+ CanonicalRepoConfigSchema,
626
+ parseAgentsMd,
627
+ computeGlobalBlockHash,
628
+ buildAgentsMd,
629
+ extractRepoBlockContent,
630
+ stripMetadataComments,
631
+ parseGlobalBlockMetadata,
632
+ parseRulesSection,
633
+ parseRulesSectionMetadata,
634
+ stripRulesSectionMetadata,
635
+ computeRulesSectionHash,
636
+ getMetadataKeyNames,
637
+ parseFrontmatter,
638
+ validateSkillFrontmatter,
639
+ computeContentHash,
640
+ stripManagedMetadata,
641
+ addManagedMetadata,
642
+ hasManualChanges,
643
+ isManaged,
644
+ checkSkillFiles,
645
+ getModifiedSkillFiles,
646
+ checkRuleFiles,
647
+ getModifiedRuleFiles,
648
+ checkAgentsMd,
649
+ checkAllManagedFiles,
650
+ checkAgentsMdRulesSection,
651
+ getModifiedManagedFiles
652
+ };
653
+ //# sourceMappingURL=chunk-B53WKCQU.js.map