ai-ops-cli 1.4.1 → 1.5.1

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/dist/bin/index.js CHANGED
@@ -1246,6 +1246,25 @@ var replaceOrAppendBlock = (content, start, end, block) => {
1246
1246
  const separator = content.trim().length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
1247
1247
  return `${content}${separator}${cleanBlock}`;
1248
1248
  };
1249
+ var insertBlockBeforeFirstTable = (content, block) => {
1250
+ const cleanBlock = block.endsWith("\n") ? block : `${block}
1251
+ `;
1252
+ if (content.trim().length === 0) {
1253
+ return cleanBlock;
1254
+ }
1255
+ const lines = content.split("\n");
1256
+ const firstTableIndex = lines.findIndex(
1257
+ (line) => !line.trimStart().startsWith("#") && /^\s*\[[^\]]+\]\s*(?:#.*)?$/.test(line)
1258
+ );
1259
+ if (firstTableIndex < 0) {
1260
+ const separator = content.endsWith("\n") ? "\n" : "\n\n";
1261
+ return `${content}${separator}${cleanBlock}`;
1262
+ }
1263
+ const before = lines.slice(0, firstTableIndex).join("\n").trimEnd();
1264
+ const after = lines.slice(firstTableIndex).join("\n").trimStart();
1265
+ return `${[before, cleanBlock.trimEnd(), after].filter((section) => section.length > 0).join("\n\n")}
1266
+ `;
1267
+ };
1249
1268
  var quoteTomlString = (value) => JSON.stringify(value);
1250
1269
  var readActiveStringAssignment = (content, key) => {
1251
1270
  for (const line of content.split("\n")) {
@@ -1259,6 +1278,22 @@ var readActiveStringAssignment = (content, key) => {
1259
1278
  }
1260
1279
  return null;
1261
1280
  };
1281
+ var readTopLevelStringAssignment = (content, key) => {
1282
+ for (const line of content.split("\n")) {
1283
+ const trimmed = line.trim();
1284
+ if (trimmed.length === 0 || trimmed.startsWith("#")) {
1285
+ continue;
1286
+ }
1287
+ if (/^\[[^\]]+\]\s*(?:#.*)?$/.test(trimmed)) {
1288
+ return null;
1289
+ }
1290
+ const match = new RegExp(`^\\s*${key}\\s*=\\s*["']([^"']+)["']`).exec(line);
1291
+ if (match) {
1292
+ return match[1];
1293
+ }
1294
+ }
1295
+ return null;
1296
+ };
1262
1297
  var hasActiveTable = (content, tableName) => {
1263
1298
  const tablePattern = new RegExp(`^\\s*\\[${escapeRegExp(tableName)}\\]\\s*(?:#.*)?$`);
1264
1299
  return content.split("\n").some((line) => !line.trimStart().startsWith("#") && tablePattern.test(line));
@@ -1409,12 +1444,12 @@ var buildPermissionProfileBlock = (paths, includeDefaultPermissions) => [
1409
1444
  `${quoteTomlString(paths.personalContextRoot)} = "write"`,
1410
1445
  `${quoteTomlString(join10(paths.userBasePath, ".ai-ops", "context-promotion"))} = "write"`,
1411
1446
  "",
1412
- `[permissions.${SAFE_LOCAL_CODEX_PERMISSION_NAME}.filesystem.":workspace_roots"]`,
1447
+ `[permissions.${SAFE_LOCAL_CODEX_PERMISSION_NAME}.filesystem.":project_roots"]`,
1413
1448
  '"." = "write"',
1414
1449
  '".git" = "read"',
1415
1450
  '".codex" = "read"',
1416
1451
  '".codex/plans" = "write"',
1417
- '"**/*.env" = "deny"',
1452
+ '"**/*.env" = "none"',
1418
1453
  "",
1419
1454
  `[permissions.${SAFE_LOCAL_CODEX_PERMISSION_NAME}.network]`,
1420
1455
  "enabled = false",
@@ -1450,7 +1485,7 @@ var cleanupLegacySandboxConfig = (content) => removeLegacyManagedSandboxWorkspac
1450
1485
  var editConfigForInstall = (content, paths) => {
1451
1486
  const withoutCurrentProfileBlock = stripBlock(content, PROFILE_BLOCK_START, PROFILE_BLOCK_END);
1452
1487
  const withoutLegacy = cleanupLegacySandboxConfig(withoutCurrentProfileBlock);
1453
- const activeDefaultPermissions = readActiveStringAssignment(withoutLegacy, "default_permissions");
1488
+ const activeDefaultPermissions = readTopLevelStringAssignment(withoutLegacy, "default_permissions");
1454
1489
  if (readActiveStringAssignment(withoutLegacy, "sandbox_mode") || hasActiveTable(withoutLegacy, "sandbox_workspace_write")) {
1455
1490
  return {
1456
1491
  content,
@@ -1475,12 +1510,9 @@ var editConfigForInstall = (content, paths) => {
1475
1510
  conflict: CONFIG_CONFLICT_EXISTING_PROFILE
1476
1511
  };
1477
1512
  }
1478
- const nextContent = replaceOrAppendBlock(
1479
- withoutLegacy,
1480
- PROFILE_BLOCK_START,
1481
- PROFILE_BLOCK_END,
1482
- buildPermissionProfileBlock(paths, activeDefaultPermissions !== SAFE_LOCAL_CODEX_PERMISSION_NAME)
1483
- );
1513
+ const shouldWriteDefaultPermissions = activeDefaultPermissions !== SAFE_LOCAL_CODEX_PERMISSION_NAME;
1514
+ const profileBlock = buildPermissionProfileBlock(paths, shouldWriteDefaultPermissions);
1515
+ const nextContent = shouldWriteDefaultPermissions ? insertBlockBeforeFirstTable(withoutLegacy, profileBlock) : replaceOrAppendBlock(withoutLegacy, PROFILE_BLOCK_START, PROFILE_BLOCK_END, profileBlock);
1484
1516
  return {
1485
1517
  content: nextContent,
1486
1518
  installed: true,
@@ -2082,13 +2114,14 @@ var pruneContextPromotionReceipts = (params) => {
2082
2114
  };
2083
2115
 
2084
2116
  // src/features/context-promotion/status.ts
2085
- import { existsSync as existsSync9 } from "fs";
2117
+ import { existsSync as existsSync10 } from "fs";
2086
2118
  import { join as join16, resolve as resolve8 } from "path";
2087
2119
 
2088
2120
  // src/features/project-layer/constants.ts
2089
2121
  import { join as join13 } from "path";
2090
2122
  var PROJECT_LAYER_MANIFEST_RELATIVE_PATH = ".ai-ops/manifest.json";
2091
2123
  var PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH = ".ai-ops/context-layer.json";
2124
+ var CUSTOM_PROJECT_RULES_DIR = "docs/agent/project-rules";
2092
2125
  var CONTEXT_LAYER_DATA_DIR = join13(COMPILER_DATA_DIR, "context-layer");
2093
2126
  var TOOL_ORDER2 = ["codex", "gemini", "claude-code"];
2094
2127
  var DEFAULT_TOOLS = TOOL_ORDER2;
@@ -2125,7 +2158,7 @@ var resolveProjectLayerManifestPath = (basePath) => join14(basePath, PROJECT_LAY
2125
2158
  var resolveProjectLayerContextIndexPath = (basePath) => join14(basePath, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH);
2126
2159
  var resolveTemplatePath = (relativePath) => join14(CONTEXT_LAYER_DATA_DIR, relativePath);
2127
2160
  var toRelativeDir = (relativePath) => dirname8(relativePath);
2128
- var resolveProjectLayerFilePath2 = (basePath, relativePath) => {
2161
+ var resolveProjectLayerFilePath = (basePath, relativePath) => {
2129
2162
  if (!isSafeProjectLayerPath(relativePath)) {
2130
2163
  throw new Error(`Unsafe project layer path: ${relativePath}`);
2131
2164
  }
@@ -2258,18 +2291,86 @@ var loadProjectLayerTemplateSpecs = (tools) => {
2258
2291
  };
2259
2292
  var computeProjectLayerSourceHash = (specs) => computeHash(specs.map((spec) => `${spec.path}:${spec.content}`));
2260
2293
 
2294
+ // src/features/project-layer/custom-project-rules.ts
2295
+ import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
2296
+ var isMarkdownPath = (path) => path.endsWith(".md");
2297
+ var hasMarkdownFrontmatter = (content) => content.startsWith("---\n");
2298
+ var assertCustomProjectRuleContract = (params) => {
2299
+ if (params.owner !== "project") {
2300
+ throw new Error(`${params.path} owner\uB294 project\uC5EC\uC57C \uD569\uB2C8\uB2E4. \uD604\uC7AC \uAC12: ${params.owner}`);
2301
+ }
2302
+ if (params.layer !== "agent") {
2303
+ throw new Error(`${params.path} layer\uB294 agent\uC5EC\uC57C \uD569\uB2C8\uB2E4. \uD604\uC7AC \uAC12: ${params.layer}`);
2304
+ }
2305
+ };
2306
+ var isCustomProjectRulePath = (path) => path.startsWith(`${CUSTOM_PROJECT_RULES_DIR}/`) && isMarkdownPath(path);
2307
+ var collectMarkdownPaths = (params) => {
2308
+ const absoluteDir = resolveProjectLayerFilePath(params.basePath, params.relativeDir);
2309
+ if (!existsSync5(absoluteDir)) {
2310
+ return [];
2311
+ }
2312
+ return readdirSync3(absoluteDir, { withFileTypes: true }).flatMap((entry) => {
2313
+ const relativePath = `${params.relativeDir}/${entry.name}`;
2314
+ if (entry.isDirectory()) {
2315
+ return collectMarkdownPaths({ basePath: params.basePath, relativeDir: relativePath });
2316
+ }
2317
+ return entry.isFile() && isMarkdownPath(relativePath) ? [relativePath] : [];
2318
+ });
2319
+ };
2320
+ var discoverCustomProjectRuleFiles = (basePath) => collectMarkdownPaths({ basePath, relativeDir: CUSTOM_PROJECT_RULES_DIR }).sort((left, right) => left.localeCompare(right)).flatMap((path) => {
2321
+ const content = readFileSync9(resolveProjectLayerFilePath(basePath, path), "utf-8");
2322
+ if (!hasMarkdownFrontmatter(content)) {
2323
+ return [];
2324
+ }
2325
+ try {
2326
+ const document = parseProjectLayerDocument(path, content);
2327
+ assertCustomProjectRuleContract({
2328
+ path,
2329
+ owner: document.owner,
2330
+ layer: document.layer
2331
+ });
2332
+ return [
2333
+ {
2334
+ path,
2335
+ templateHash: document.contentHash,
2336
+ created: false
2337
+ }
2338
+ ];
2339
+ } catch (error) {
2340
+ const reason = error instanceof Error ? error.message : "unknown error";
2341
+ throw new Error(`${path} frontmatter \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
2342
+ }
2343
+ });
2344
+ var syncCustomProjectRuleFiles = (params) => {
2345
+ const customFiles = discoverCustomProjectRuleFiles(params.basePath);
2346
+ const customPathSet = new Set(customFiles.map((file) => file.path));
2347
+ const projectFilesByPath = /* @__PURE__ */ new Map();
2348
+ for (const file of params.manifest.project_files) {
2349
+ if (!isCustomProjectRulePath(file.path) || customPathSet.has(file.path)) {
2350
+ projectFilesByPath.set(file.path, file);
2351
+ }
2352
+ }
2353
+ for (const file of customFiles) {
2354
+ projectFilesByPath.set(file.path, file);
2355
+ }
2356
+ return ProjectLayerManifestSchema.parse({
2357
+ ...params.manifest,
2358
+ project_files: [...projectFilesByPath.values()].sort((left, right) => left.path.localeCompare(right.path))
2359
+ });
2360
+ };
2361
+
2261
2362
  // src/features/project-layer/state-io.ts
2262
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
2363
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
2263
2364
  import { dirname as dirname9 } from "path";
2264
2365
 
2265
2366
  // src/features/project-layer/docs-status.logic.ts
2266
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
2367
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
2267
2368
  var docsStatusIssue = (code, message) => ({
2268
2369
  level: "error",
2269
2370
  code,
2270
2371
  message
2271
2372
  });
2272
- var computeDocsStatusFileHash = (basePath, relativePath) => computeHash([readFileSync9(resolveProjectLayerFilePath2(basePath, relativePath), "utf-8").trimEnd()]);
2373
+ var computeDocsStatusFileHash = (basePath, relativePath) => computeHash([readFileSync10(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
2273
2374
  var parseMarkdownTableCells = (line) => {
2274
2375
  const trimmed = line.trim();
2275
2376
  if (!trimmed.startsWith("|") || !trimmed.endsWith("|")) {
@@ -2319,7 +2420,7 @@ var parseDocsStatusEntries = (content) => {
2319
2420
  });
2320
2421
  };
2321
2422
  var buildDocsStatusRowsFromDisk = (params) => params.documentPaths.map((path) => {
2322
- const document = parseProjectLayerDocument(path, readFileSync9(resolveProjectLayerFilePath2(params.basePath, path), "utf-8"));
2423
+ const document = parseProjectLayerDocument(path, readFileSync10(resolveProjectLayerFilePath(params.basePath, path), "utf-8"));
2323
2424
  return `| ${document.path} | ${document.status} | ${document.owner} |`;
2324
2425
  });
2325
2426
  var replaceDocsStatusRows = (content, rows) => {
@@ -2332,10 +2433,10 @@ var replaceDocsStatusRows = (content, rows) => {
2332
2433
  };
2333
2434
  var updateDocsStatusTable = (basePath, documentPaths) => {
2334
2435
  const docsStatusPath = "docs/docs-status.md";
2335
- const absolutePath = resolveProjectLayerFilePath2(basePath, docsStatusPath);
2436
+ const absolutePath = resolveProjectLayerFilePath(basePath, docsStatusPath);
2336
2437
  const beforeHash = computeDocsStatusFileHash(basePath, docsStatusPath);
2337
2438
  const rows = buildDocsStatusRowsFromDisk({ basePath, documentPaths });
2338
- const nextContent = replaceDocsStatusRows(readFileSync9(absolutePath, "utf-8"), rows);
2439
+ const nextContent = replaceDocsStatusRows(readFileSync10(absolutePath, "utf-8"), rows);
2339
2440
  writeFileSync6(absolutePath, nextContent, "utf-8");
2340
2441
  return {
2341
2442
  beforeHash,
@@ -2372,7 +2473,7 @@ var compareDocsStatusEntry = (params) => {
2372
2473
  // src/features/project-layer/state-io.ts
2373
2474
  var readProjectLayerManifest = (basePath) => {
2374
2475
  try {
2375
- return parseProjectLayerManifest(readFileSync10(resolveProjectLayerManifestPath(basePath), "utf-8"));
2476
+ return parseProjectLayerManifest(readFileSync11(resolveProjectLayerManifestPath(basePath), "utf-8"));
2376
2477
  } catch (error) {
2377
2478
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2378
2479
  return null;
@@ -2387,7 +2488,7 @@ var writeProjectLayerManifest = (basePath, manifest) => {
2387
2488
  };
2388
2489
  var readProjectLayerContextIndex = (basePath) => {
2389
2490
  try {
2390
- return parseProjectLayerContextIndex(readFileSync10(resolveProjectLayerContextIndexPath(basePath), "utf-8"));
2491
+ return parseProjectLayerContextIndex(readFileSync11(resolveProjectLayerContextIndexPath(basePath), "utf-8"));
2391
2492
  } catch (error) {
2392
2493
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
2393
2494
  return null;
@@ -2402,7 +2503,7 @@ var writeProjectLayerContextIndex = (basePath, contextIndex) => {
2402
2503
  };
2403
2504
  var buildContextIndexFromDisk = (params) => {
2404
2505
  const documents = params.documentPaths.map(
2405
- (path) => parseProjectLayerDocument(path, readFileSync10(resolveProjectLayerFilePath2(params.basePath, path), "utf-8"))
2506
+ (path) => parseProjectLayerDocument(path, readFileSync11(resolveProjectLayerFilePath(params.basePath, path), "utf-8"))
2406
2507
  );
2407
2508
  return ProjectLayerContextIndexSchema.parse({
2408
2509
  schemaVersion: 1,
@@ -2417,10 +2518,14 @@ var collectDocumentPathsFromManifest = (manifest) => [
2417
2518
  ...manifest.packs.flatMap((pack) => pack.documents.map((file) => file.path))
2418
2519
  ].sort();
2419
2520
  var refreshProjectLayerDerivedState = (params) => {
2420
- const documentPaths = collectDocumentPathsFromManifest(params.manifest);
2521
+ const manifestWithCustomRules = syncCustomProjectRuleFiles({
2522
+ basePath: params.basePath,
2523
+ manifest: params.manifest
2524
+ });
2525
+ const documentPaths = collectDocumentPathsFromManifest(manifestWithCustomRules);
2421
2526
  const docsStatusHashes = updateDocsStatusTable(params.basePath, documentPaths);
2422
2527
  const manifest = updateDocsStatusProjectFileRecord({
2423
- manifest: params.manifest,
2528
+ manifest: manifestWithCustomRules,
2424
2529
  beforeHash: docsStatusHashes.beforeHash,
2425
2530
  afterHash: docsStatusHashes.afterHash
2426
2531
  });
@@ -2437,17 +2542,17 @@ var refreshProjectLayerDerivedState = (params) => {
2437
2542
  };
2438
2543
 
2439
2544
  // src/features/project-layer/lifecycle.logic.ts
2440
- import { existsSync as existsSync6, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
2545
+ import { existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
2441
2546
  import { dirname as dirname10 } from "path";
2442
2547
 
2443
2548
  // src/features/project-layer/uninstall.logic.ts
2444
- import { existsSync as existsSync5, readFileSync as readFileSync11, readdirSync as readdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
2549
+ import { existsSync as existsSync6, readFileSync as readFileSync12, readdirSync as readdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
2445
2550
  function removeManagedProjectFile(basePath, relativePath) {
2446
- const absolutePath = resolveProjectLayerFilePath2(basePath, relativePath);
2447
- if (!existsSync5(absolutePath)) {
2551
+ const absolutePath = resolveProjectLayerFilePath(basePath, relativePath);
2552
+ if (!existsSync6(absolutePath)) {
2448
2553
  return { deleted: [], cleaned: [], preserved: [], notFound: [relativePath] };
2449
2554
  }
2450
- const content = readFileSync11(absolutePath, "utf-8");
2555
+ const content = readFileSync12(absolutePath, "utf-8");
2451
2556
  if (!hasAiOpsSection(content)) {
2452
2557
  return { deleted: [], cleaned: [], preserved: [relativePath], notFound: [] };
2453
2558
  }
@@ -2460,11 +2565,11 @@ function removeManagedProjectFile(basePath, relativePath) {
2460
2565
  return { deleted: [], cleaned: [relativePath], preserved: [], notFound: [] };
2461
2566
  }
2462
2567
  var removeCreateOnlyProjectFile = (basePath, file) => {
2463
- const absolutePath = resolveProjectLayerFilePath2(basePath, file.path);
2464
- if (!existsSync5(absolutePath)) {
2568
+ const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
2569
+ if (!existsSync6(absolutePath)) {
2465
2570
  return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
2466
2571
  }
2467
- const content = readFileSync11(absolutePath, "utf-8").trimEnd();
2572
+ const content = readFileSync12(absolutePath, "utf-8").trimEnd();
2468
2573
  const currentHash = computeHash([content]);
2469
2574
  if (file.created && currentHash === file.templateHash) {
2470
2575
  rmSync2(absolutePath);
@@ -2473,11 +2578,11 @@ var removeCreateOnlyProjectFile = (basePath, file) => {
2473
2578
  return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
2474
2579
  };
2475
2580
  var removePackOwnedFile = (basePath, file) => {
2476
- const absolutePath = resolveProjectLayerFilePath2(basePath, file.path);
2477
- if (!existsSync5(absolutePath)) {
2581
+ const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
2582
+ if (!existsSync6(absolutePath)) {
2478
2583
  return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
2479
2584
  }
2480
- const currentHash = computeHash([readFileSync11(absolutePath, "utf-8").trimEnd()]);
2585
+ const currentHash = computeHash([readFileSync12(absolutePath, "utf-8").trimEnd()]);
2481
2586
  if (currentHash === file.sourceHash) {
2482
2587
  rmSync2(absolutePath);
2483
2588
  return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
@@ -2495,10 +2600,10 @@ var removeEmptyDirs = (basePath, relativePaths) => {
2495
2600
  (a, b) => b.length - a.length
2496
2601
  );
2497
2602
  for (const dir of [...dirs, ".ai-ops"]) {
2498
- const absoluteDir = resolveProjectLayerFilePath2(basePath, dir);
2499
- if (!existsSync5(absoluteDir)) continue;
2603
+ const absoluteDir = resolveProjectLayerFilePath(basePath, dir);
2604
+ if (!existsSync6(absoluteDir)) continue;
2500
2605
  try {
2501
- if (readdirSync3(absoluteDir).length === 0) {
2606
+ if (readdirSync4(absoluteDir).length === 0) {
2502
2607
  rmSync2(absoluteDir, { recursive: true });
2503
2608
  }
2504
2609
  } catch {
@@ -2513,7 +2618,7 @@ var uninstallProjectLayer = (basePath, manifest) => {
2513
2618
  );
2514
2619
  const stateFiles = [PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH, PROJECT_LAYER_MANIFEST_RELATIVE_PATH];
2515
2620
  for (const stateFile of stateFiles) {
2516
- rmSync2(resolveProjectLayerFilePath2(basePath, stateFile), { force: true });
2621
+ rmSync2(resolveProjectLayerFilePath(basePath, stateFile), { force: true });
2517
2622
  }
2518
2623
  const result = mergeRemoveResults([...managedResults, ...projectResults, ...packResults]);
2519
2624
  removeEmptyDirs(basePath, [...result.deleted, ...stateFiles]);
@@ -2525,15 +2630,15 @@ var installManagedFiles = (basePath, specs, meta) => {
2525
2630
  const written = [];
2526
2631
  const appended = [];
2527
2632
  for (const spec of specs) {
2528
- const absolutePath = resolveProjectLayerFilePath2(basePath, spec.path);
2633
+ const absolutePath = resolveProjectLayerFilePath(basePath, spec.path);
2529
2634
  const wrappedContent = wrapWithSection(spec.content, meta);
2530
- if (!existsSync6(absolutePath)) {
2635
+ if (!existsSync7(absolutePath)) {
2531
2636
  mkdirSync7(dirname10(absolutePath), { recursive: true });
2532
2637
  writeFileSync9(absolutePath, wrappedContent + "\n", "utf-8");
2533
2638
  written.push(spec.path);
2534
2639
  continue;
2535
2640
  }
2536
- const existing = readFileSync12(absolutePath, "utf-8");
2641
+ const existing = readFileSync13(absolutePath, "utf-8");
2537
2642
  if (hasAiOpsSection(existing)) {
2538
2643
  writeFileSync9(absolutePath, replaceAiOpsSection(existing, wrappedContent), "utf-8");
2539
2644
  const stripped = stripAiOpsSection(existing);
@@ -2557,9 +2662,9 @@ var installProjectFiles = (params) => {
2557
2662
  const preserved = [];
2558
2663
  const previousByPath = new Map((params.previousProjectFiles ?? []).map((file) => [file.path, file]));
2559
2664
  for (const spec of params.specs) {
2560
- const absolutePath = resolveProjectLayerFilePath2(params.basePath, spec.path);
2665
+ const absolutePath = resolveProjectLayerFilePath(params.basePath, spec.path);
2561
2666
  const previous = previousByPath.get(spec.path);
2562
- if (!existsSync6(absolutePath)) {
2667
+ if (!existsSync7(absolutePath)) {
2563
2668
  mkdirSync7(dirname10(absolutePath), { recursive: true });
2564
2669
  writeFileSync9(absolutePath, spec.content + "\n", "utf-8");
2565
2670
  created.push(spec.path);
@@ -2570,7 +2675,7 @@ var installProjectFiles = (params) => {
2570
2675
  });
2571
2676
  continue;
2572
2677
  }
2573
- const existingContent = readFileSync12(absolutePath, "utf-8").trimEnd();
2678
+ const existingContent = readFileSync13(absolutePath, "utf-8").trimEnd();
2574
2679
  const existingHash = computeHash([existingContent]);
2575
2680
  if (previous?.created === true && existingHash === previous.templateHash) {
2576
2681
  if (existingHash !== spec.contentHash) {
@@ -2704,7 +2809,7 @@ var updateProjectLayer = (params) => {
2704
2809
  };
2705
2810
 
2706
2811
  // src/features/project-layer/audit.logic.ts
2707
- import { existsSync as existsSync7, readFileSync as readFileSync13 } from "fs";
2812
+ import { existsSync as existsSync8, readFileSync as readFileSync14 } from "fs";
2708
2813
  var issue = (level, code, message) => ({
2709
2814
  level,
2710
2815
  code,
@@ -2712,11 +2817,11 @@ var issue = (level, code, message) => ({
2712
2817
  });
2713
2818
  var readDocumentSafely = (basePath, path) => {
2714
2819
  try {
2715
- const absolutePath = resolveProjectLayerFilePath2(basePath, path);
2716
- if (!existsSync7(absolutePath)) {
2820
+ const absolutePath = resolveProjectLayerFilePath(basePath, path);
2821
+ if (!existsSync8(absolutePath)) {
2717
2822
  return issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${path}`);
2718
2823
  }
2719
- return parseProjectLayerDocument(path, readFileSync13(absolutePath, "utf-8"));
2824
+ return parseProjectLayerDocument(path, readFileSync14(absolutePath, "utf-8"));
2720
2825
  } catch (error) {
2721
2826
  const reason = error instanceof Error ? error.message : "unknown error";
2722
2827
  return issue("error", "invalid-frontmatter", `${path} frontmatter \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
@@ -2724,6 +2829,10 @@ var readDocumentSafely = (basePath, path) => {
2724
2829
  };
2725
2830
  var buildContextIndexMap = (contextIndex) => new Map((contextIndex?.documents ?? []).map((document) => [document.path, document]));
2726
2831
  var compareArray = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
2832
+ var compareProjectFileRecords = (left, right) => left.length === right.length && left.every((file, index) => {
2833
+ const other = right[index];
2834
+ return other !== void 0 && file.path === other.path && file.templateHash === other.templateHash && file.created === other.created;
2835
+ });
2727
2836
  var compareContextDocument = (params) => {
2728
2837
  const indexed = params.indexed;
2729
2838
  if (indexed === void 0) {
@@ -2767,6 +2876,13 @@ var diffProjectLayer = (basePath) => {
2767
2876
  const currentSourceHash = computeProjectLayerSourceHash(specs);
2768
2877
  let contextIndex = null;
2769
2878
  const issues = [];
2879
+ let syncedManifest = manifest;
2880
+ try {
2881
+ syncedManifest = syncCustomProjectRuleFiles({ basePath, manifest });
2882
+ } catch (error) {
2883
+ const reason = error instanceof Error ? error.message : "unknown error";
2884
+ issues.push(issue("error", "invalid-custom-project-rule", reason));
2885
+ }
2770
2886
  try {
2771
2887
  contextIndex = readProjectLayerContextIndex(basePath);
2772
2888
  } catch (error) {
@@ -2786,18 +2902,27 @@ var diffProjectLayer = (basePath) => {
2786
2902
  if (contextIndex === null) {
2787
2903
  issues.push(issue("error", "missing-context-index", `${PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH}\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`));
2788
2904
  }
2905
+ if (!compareProjectFileRecords(manifest.project_files, syncedManifest.project_files)) {
2906
+ issues.push(
2907
+ issue(
2908
+ "warning",
2909
+ "custom-project-rules-drift",
2910
+ "`docs/agent/project-rules/**/*.md` discovery \uACB0\uACFC\uAC00 manifest\uC640 \uB2E4\uB985\uB2C8\uB2E4. `ai-ops update`\uB85C \uB3D9\uAE30\uD654\uD558\uC138\uC694."
2911
+ )
2912
+ );
2913
+ }
2789
2914
  for (const expectedPath of expectedManagedPaths) {
2790
2915
  if (!manifestManagedPaths.has(expectedPath)) {
2791
2916
  issues.push(issue("error", "manifest-missing-managed-file", `manifest managed_files \uB204\uB77D: ${expectedPath}`));
2792
2917
  }
2793
2918
  }
2794
2919
  for (const file of manifest.managed_files) {
2795
- const absolutePath = resolveProjectLayerFilePath2(basePath, file.path);
2796
- if (!existsSync7(absolutePath)) {
2920
+ const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
2921
+ if (!existsSync8(absolutePath)) {
2797
2922
  issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
2798
2923
  continue;
2799
2924
  }
2800
- const content = readFileSync13(absolutePath, "utf-8");
2925
+ const content = readFileSync14(absolutePath, "utf-8");
2801
2926
  const meta = parseAiOpsMeta(content);
2802
2927
  if (!meta) {
2803
2928
  issues.push(issue("error", "missing-managed-section", `managed section \uBA54\uD0C0 \uC5C6\uC74C: ${file.path}`));
@@ -2809,19 +2934,19 @@ var diffProjectLayer = (basePath) => {
2809
2934
  );
2810
2935
  }
2811
2936
  }
2812
- for (const file of manifest.project_files) {
2813
- if (!existsSync7(resolveProjectLayerFilePath2(basePath, file.path))) {
2937
+ for (const file of syncedManifest.project_files) {
2938
+ if (!existsSync8(resolveProjectLayerFilePath(basePath, file.path))) {
2814
2939
  issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
2815
2940
  }
2816
2941
  }
2817
- for (const pack of manifest.packs) {
2942
+ for (const pack of syncedManifest.packs) {
2818
2943
  for (const file of [...pack.documents, ...pack.files]) {
2819
- if (!existsSync7(resolveProjectLayerFilePath2(basePath, file.path))) {
2944
+ if (!existsSync8(resolveProjectLayerFilePath(basePath, file.path))) {
2820
2945
  issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
2821
2946
  }
2822
2947
  }
2823
2948
  }
2824
- for (const path of collectDocumentPathsFromManifest(manifest)) {
2949
+ for (const path of collectDocumentPathsFromManifest(syncedManifest)) {
2825
2950
  const document = readDocumentSafely(basePath, path);
2826
2951
  if ("code" in document) {
2827
2952
  issues.push(document);
@@ -2848,18 +2973,24 @@ var auditProjectLayer = (basePath) => {
2848
2973
  } catch {
2849
2974
  contextIndex = null;
2850
2975
  }
2851
- const documentPaths = collectDocumentPathsFromManifest(manifest);
2976
+ let syncedManifest = manifest;
2977
+ try {
2978
+ syncedManifest = syncCustomProjectRuleFiles({ basePath, manifest });
2979
+ } catch {
2980
+ return diffReport;
2981
+ }
2982
+ const documentPaths = collectDocumentPathsFromManifest(syncedManifest);
2852
2983
  const documentPathSet = new Set(documentPaths);
2853
2984
  const contextPathSet = new Set(contextIndex?.documents.map((document) => document.path) ?? []);
2854
2985
  const issues = [...diffReport.issues];
2855
- const docsStatusPath = resolveProjectLayerFilePath2(basePath, "docs/docs-status.md");
2856
- if (!existsSync7(docsStatusPath)) {
2986
+ const docsStatusPath = resolveProjectLayerFilePath(basePath, "docs/docs-status.md");
2987
+ if (!existsSync8(docsStatusPath)) {
2857
2988
  issues.push(issue("error", "missing-docs-status", "docs/docs-status.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
2858
2989
  return { currentSourceHash: diffReport.currentSourceHash, issues };
2859
2990
  }
2860
2991
  let docsStatusEntries = [];
2861
2992
  try {
2862
- docsStatusEntries = parseDocsStatusEntries(readFileSync13(docsStatusPath, "utf-8"));
2993
+ docsStatusEntries = parseDocsStatusEntries(readFileSync14(docsStatusPath, "utf-8"));
2863
2994
  } catch (error) {
2864
2995
  const reason = error instanceof Error ? error.message : "unknown error";
2865
2996
  issues.push(issue("error", "invalid-docs-status", `docs/docs-status.md \uD30C\uC2F1 \uC2E4\uD328: ${reason}`));
@@ -2886,11 +3017,11 @@ var auditProjectLayer = (basePath) => {
2886
3017
  };
2887
3018
 
2888
3019
  // src/features/project-layer/pack.logic.ts
2889
- import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync15, readdirSync as readdirSync5, rmSync as rmSync3, writeFileSync as writeFileSync10 } from "fs";
3020
+ import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync16, readdirSync as readdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync10 } from "fs";
2890
3021
  import { dirname as dirname11 } from "path";
2891
3022
 
2892
3023
  // src/features/project-layer/pack-source.logic.ts
2893
- import { readFileSync as readFileSync14, readdirSync as readdirSync4 } from "fs";
3024
+ import { readFileSync as readFileSync15, readdirSync as readdirSync5 } from "fs";
2894
3025
  import { isAbsolute as isAbsolute2, join as join15, relative as relative2, resolve as resolve7 } from "path";
2895
3026
  var PACK_REGISTRY_FILENAME = "pack-registry.json";
2896
3027
  var SPEC_LIFECYCLE_PACK_ID = "spec-lifecycle";
@@ -2901,7 +3032,7 @@ var RESERVED_DOCUMENT_WARNINGS2 = [
2901
3032
  ];
2902
3033
  var DEFAULT_PACKS_DIR = join15(COMPILER_DATA_DIR, "packs");
2903
3034
  var includesReservedDocumentWarning2 = (content) => RESERVED_DOCUMENT_WARNINGS2.some((warning) => content.includes(warning));
2904
- var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync14(join15(packsDir, PACK_REGISTRY_FILENAME), "utf-8")));
3035
+ var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync15(join15(packsDir, PACK_REGISTRY_FILENAME), "utf-8")));
2905
3036
  var assertPackInstallPath = (path) => {
2906
3037
  if (!isSafeProjectLayerPath(path) || !path.startsWith(PACK_INSTALL_ROOT)) {
2907
3038
  throw new Error(`Unsafe pack path: ${path}`);
@@ -2911,7 +3042,7 @@ var readPackSourceFiles = (packDir) => {
2911
3042
  const files = [];
2912
3043
  const walk = (relativeDir = "") => {
2913
3044
  const absoluteDir = relativeDir.length > 0 ? join15(packDir, relativeDir) : packDir;
2914
- const entries = readdirSync4(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
3045
+ const entries = readdirSync5(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
2915
3046
  for (const entry of entries) {
2916
3047
  const nextRelativePath = relativeDir.length > 0 ? join15(relativeDir, entry.name) : entry.name;
2917
3048
  if (entry.isDirectory()) {
@@ -2919,7 +3050,7 @@ var readPackSourceFiles = (packDir) => {
2919
3050
  continue;
2920
3051
  }
2921
3052
  assertPackInstallPath(nextRelativePath);
2922
- const content = readFileSync14(join15(packDir, nextRelativePath), "utf-8");
3053
+ const content = readFileSync15(join15(packDir, nextRelativePath), "utf-8");
2923
3054
  files.push({
2924
3055
  path: nextRelativePath,
2925
3056
  content,
@@ -2979,9 +3110,9 @@ var resolvePackById = (packsDir, packId) => {
2979
3110
 
2980
3111
  // src/features/project-layer/pack.logic.ts
2981
3112
  var serializePackFileContent = (content) => content.length === 0 ? "" : content.trimEnd() + "\n";
2982
- var readProjectFileHash = (basePath, relativePath) => computeHash([readFileSync15(resolveProjectLayerFilePath2(basePath, relativePath), "utf-8").trimEnd()]);
3113
+ var readProjectFileHash = (basePath, relativePath) => computeHash([readFileSync16(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
2983
3114
  var writePackFile = (basePath, file) => {
2984
- const absolutePath = resolveProjectLayerFilePath2(basePath, file.path);
3115
+ const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
2985
3116
  mkdirSync8(dirname11(absolutePath), { recursive: true });
2986
3117
  writeFileSync10(absolutePath, serializePackFileContent(file.content), "utf-8");
2987
3118
  };
@@ -3011,9 +3142,9 @@ var applyPackSourceFiles = (params) => {
3011
3142
  ])
3012
3143
  );
3013
3144
  for (const file of sourceFiles) {
3014
- const absolutePath = resolveProjectLayerFilePath2(params.basePath, file.path);
3145
+ const absolutePath = resolveProjectLayerFilePath(params.basePath, file.path);
3015
3146
  const previous = previousByPath.get(file.path);
3016
- if (!existsSync8(absolutePath)) {
3147
+ if (!existsSync9(absolutePath)) {
3017
3148
  writePackFile(params.basePath, file);
3018
3149
  written.push(file.path);
3019
3150
  continue;
@@ -3036,8 +3167,8 @@ var applyPackSourceFiles = (params) => {
3036
3167
  if (sourceByPath.has(previous.path)) {
3037
3168
  continue;
3038
3169
  }
3039
- const absolutePath = resolveProjectLayerFilePath2(params.basePath, previous.path);
3040
- if (!existsSync8(absolutePath)) {
3170
+ const absolutePath = resolveProjectLayerFilePath(params.basePath, previous.path);
3171
+ if (!existsSync9(absolutePath)) {
3041
3172
  notFound.push(previous.path);
3042
3173
  continue;
3043
3174
  }
@@ -3055,8 +3186,8 @@ var removePackFiles = (basePath, record) => {
3055
3186
  const preserved = [];
3056
3187
  const notFound = [];
3057
3188
  for (const file of [...record.documents, ...record.files]) {
3058
- const absolutePath = resolveProjectLayerFilePath2(basePath, file.path);
3059
- if (!existsSync8(absolutePath)) {
3189
+ const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
3190
+ if (!existsSync9(absolutePath)) {
3060
3191
  notFound.push(file.path);
3061
3192
  continue;
3062
3193
  }
@@ -3074,12 +3205,12 @@ var removeEmptyDirs2 = (basePath, relativePaths) => {
3074
3205
  (a, b) => b.length - a.length
3075
3206
  );
3076
3207
  for (const dir of dirs) {
3077
- const absoluteDir = resolveProjectLayerFilePath2(basePath, dir);
3078
- if (!existsSync8(absoluteDir)) {
3208
+ const absoluteDir = resolveProjectLayerFilePath(basePath, dir);
3209
+ if (!existsSync9(absoluteDir)) {
3079
3210
  continue;
3080
3211
  }
3081
3212
  try {
3082
- if (readdirSync5(absoluteDir).length === 0) {
3213
+ if (readdirSync6(absoluteDir).length === 0) {
3083
3214
  rmSync3(absoluteDir, { recursive: true });
3084
3215
  }
3085
3216
  } catch {
@@ -3190,8 +3321,8 @@ var diffProjectLayerPack = (params) => {
3190
3321
  );
3191
3322
  }
3192
3323
  for (const file of [...record.documents, ...record.files]) {
3193
- const absolutePath = resolveProjectLayerFilePath2(params.basePath, file.path);
3194
- if (!existsSync8(absolutePath)) {
3324
+ const absolutePath = resolveProjectLayerFilePath(params.basePath, file.path);
3325
+ if (!existsSync9(absolutePath)) {
3195
3326
  issues.push(packIssue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
3196
3327
  }
3197
3328
  }
@@ -3200,7 +3331,7 @@ var diffProjectLayerPack = (params) => {
3200
3331
  };
3201
3332
 
3202
3333
  // src/features/context-promotion/status.ts
3203
- var hasContextPromotionLayer = (gitRoot) => existsSync9(join16(gitRoot, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH));
3334
+ var hasContextPromotionLayer = (gitRoot) => existsSync10(join16(gitRoot, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH));
3204
3335
  var getContextPromotionStatus = (params) => {
3205
3336
  const cwd = resolve8(params.cwd);
3206
3337
  const gitRoot = resolveContextPromotionGitRoot(cwd);
@@ -3528,11 +3659,11 @@ import * as p5 from "@clack/prompts";
3528
3659
 
3529
3660
  // src/features/pc/status.ts
3530
3661
  import { execFileSync as execFileSync2 } from "child_process";
3531
- import { existsSync as existsSync10, readdirSync as readdirSync6 } from "fs";
3662
+ import { existsSync as existsSync11, readdirSync as readdirSync7 } from "fs";
3532
3663
  import { join as join17, resolve as resolve10, sep as sep2 } from "path";
3533
3664
 
3534
3665
  // src/features/pc/markdown.ts
3535
- import { readFileSync as readFileSync16 } from "fs";
3666
+ import { readFileSync as readFileSync17 } from "fs";
3536
3667
  import { resolve as resolve9, sep } from "path";
3537
3668
  var normalizePath = (path) => resolve9(path.replace(/^~(?=$|\/)/, process.env.HOME ?? "~"));
3538
3669
  var pathContains = (parentPath, childPath) => {
@@ -3571,7 +3702,7 @@ var parseListField = (content, labels) => {
3571
3702
  };
3572
3703
  var readTextFileOrNull = (filePath) => {
3573
3704
  try {
3574
- return readFileSync16(filePath, "utf-8");
3705
+ return readFileSync17(filePath, "utf-8");
3575
3706
  } catch {
3576
3707
  return null;
3577
3708
  }
@@ -3599,10 +3730,10 @@ var readGitHead2 = (cwd) => {
3599
3730
  };
3600
3731
  var listWorkspaceStatePaths = (contextRoot) => {
3601
3732
  const workspacesDir = join17(contextRoot, "workspaces");
3602
- if (!existsSync10(workspacesDir)) {
3733
+ if (!existsSync11(workspacesDir)) {
3603
3734
  return [];
3604
3735
  }
3605
- return readdirSync6(workspacesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join17(workspacesDir, entry.name, "workspace-state.md")).filter((statePath) => existsSync10(statePath)).sort((a, b) => a.localeCompare(b));
3736
+ return readdirSync7(workspacesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join17(workspacesDir, entry.name, "workspace-state.md")).filter((statePath) => existsSync11(statePath)).sort((a, b) => a.localeCompare(b));
3606
3737
  };
3607
3738
  var parseWorkspaceCandidate = (statePath) => {
3608
3739
  const content = readTextFileOrNull(statePath);
@@ -3646,10 +3777,10 @@ var parseRepoEntry = (entryPath) => {
3646
3777
  };
3647
3778
  var findCurrentEntry = (params) => {
3648
3779
  const reposDir = join17(params.workspaceDir, "repos");
3649
- if (!existsSync10(reposDir)) {
3780
+ if (!existsSync11(reposDir)) {
3650
3781
  return null;
3651
3782
  }
3652
- const entries = readdirSync6(reposDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => parseRepoEntry(join17(reposDir, entry.name))).filter((entry) => entry !== null).filter((entry) => {
3783
+ const entries = readdirSync7(reposDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => parseRepoEntry(join17(reposDir, entry.name))).filter((entry) => entry !== null).filter((entry) => {
3653
3784
  const paths = [entry.path, entry.gitRoot].filter((path) => path !== null);
3654
3785
  return paths.some((path) => pathContains(path, params.cwd));
3655
3786
  }).sort((a, b) => {
@@ -3701,7 +3832,7 @@ var parseLastConfirmedCommitHash = (params) => {
3701
3832
  var getPcHandoffStatus = (params) => {
3702
3833
  const cwd = normalizePath(params.cwd);
3703
3834
  const contextRoot = normalizePath(params.contextRoot);
3704
- if (!existsSync10(contextRoot)) {
3835
+ if (!existsSync11(contextRoot)) {
3705
3836
  return {
3706
3837
  cwd,
3707
3838
  contextRoot,
@@ -3864,7 +3995,7 @@ var evaluatePcPostToolUseHook = (params) => {
3864
3995
  };
3865
3996
 
3866
3997
  // src/features/integrations/manifest-io.ts
3867
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync17, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
3998
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "fs";
3868
3999
  import { dirname as dirname12, join as join18 } from "path";
3869
4000
  var INTEGRATION_MANIFEST_FILENAME = "integrations-manifest.json";
3870
4001
  var parseIntegrationManifest = (json) => IntegrationManifestSchema.parse(JSON.parse(json));
@@ -3873,7 +4004,7 @@ var resolveIntegrationManifestPath = (userBasePath) => join18(userBasePath, ".ai
3873
4004
  var readIntegrationManifest = (manifestPath) => {
3874
4005
  let raw;
3875
4006
  try {
3876
- raw = readFileSync17(manifestPath, "utf-8");
4007
+ raw = readFileSync18(manifestPath, "utf-8");
3877
4008
  } catch {
3878
4009
  return null;
3879
4010
  }
@@ -3906,7 +4037,7 @@ var writeUserIntegrationState = (params) => {
3906
4037
  };
3907
4038
 
3908
4039
  // src/features/integrations/components.ts
3909
- import { existsSync as existsSync11, rmSync as rmSync5 } from "fs";
4040
+ import { existsSync as existsSync12, rmSync as rmSync5 } from "fs";
3910
4041
  import { join as join19 } from "path";
3911
4042
  var readInstalledSkills2 = (basePath) => (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
3912
4043
  ...installedSkill,
@@ -3921,7 +4052,7 @@ var resolveSkillById = (skillId) => {
3921
4052
  };
3922
4053
  var hasInstalledCodexSkill = (params) => {
3923
4054
  const installedSkill = findInstalledSkill(readInstalledSkills2(params.basePath), params.skillId);
3924
- return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true && existsSync11(join19(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
4055
+ return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true && existsSync12(join19(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
3925
4056
  };
3926
4057
  var writeUserSkillState = (params) => {
3927
4058
  const registryPath = resolveSkillRegistryPath(params.basePath);
@@ -3949,7 +4080,7 @@ var ensureSkillComponent = (params) => {
3949
4080
  skill,
3950
4081
  requestedTools
3951
4082
  });
3952
- const alreadyCurrent = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) && existsSync11(join19(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
4083
+ const alreadyCurrent = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) && existsSync12(join19(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
3953
4084
  if (alreadyCurrent) {
3954
4085
  return {
3955
4086
  type: INTEGRATION_COMPONENT_TYPE.SKILL,
@@ -4884,7 +5015,7 @@ var registerSkillCommands = (program) => {
4884
5015
  import { join as join23 } from "path";
4885
5016
 
4886
5017
  // src/features/studio/project-snapshot.ts
4887
- import { existsSync as existsSync12, readFileSync as readFileSync18 } from "fs";
5018
+ import { existsSync as existsSync13, readFileSync as readFileSync19 } from "fs";
4888
5019
  import { resolve as resolve11 } from "path";
4889
5020
  import { z as z14 } from "zod";
4890
5021
 
@@ -4951,7 +5082,9 @@ var AUDIT_ISSUE_SOURCES_BY_CODE = {
4951
5082
  "invalid-frontmatter": "frontmatter",
4952
5083
  "missing-managed-section": "managed-section",
4953
5084
  "source-hash-drift": "source-hash",
4954
- "managed-source-hash-drift": "source-hash"
5085
+ "managed-source-hash-drift": "source-hash",
5086
+ "invalid-custom-project-rule": "frontmatter",
5087
+ "custom-project-rules-drift": "manifest"
4955
5088
  };
4956
5089
  var AUDIT_ISSUE_ACTION_LABELS_BY_SOURCE = {
4957
5090
  manifest: "Review manifest record",
@@ -4998,6 +5131,10 @@ var extractTrailingIssuePath = (message) => {
4998
5131
  const [firstToken] = trailingSegment.trim().split(/\s+/);
4999
5132
  return firstToken === void 0 ? null : parsePathLikeToken(firstToken);
5000
5133
  };
5134
+ var extractLeadingIssuePath = (message) => {
5135
+ const [firstToken] = message.trim().split(/\s+/);
5136
+ return firstToken === void 0 ? null : parsePathLikeToken(firstToken);
5137
+ };
5001
5138
  var resolveIssueSource = (issue2) => AUDIT_ISSUE_SOURCES_BY_CODE[issue2.code] ?? "unknown";
5002
5139
  var resolveIssueAffectedPath = (params) => {
5003
5140
  if (params.issue.code === "missing-manifest" || params.issue.code === "invalid-manifest") {
@@ -5012,6 +5149,9 @@ var resolveIssueAffectedPath = (params) => {
5012
5149
  if (params.issue.code === "source-hash-drift") {
5013
5150
  return null;
5014
5151
  }
5152
+ if (params.issue.code === "invalid-custom-project-rule") {
5153
+ return extractLeadingIssuePath(params.issue.message);
5154
+ }
5015
5155
  const knownPath = findKnownPathInMessage(params.issue.message, params.knownPaths);
5016
5156
  if (knownPath !== null) {
5017
5157
  return knownPath;
@@ -5051,7 +5191,7 @@ var RecoverableContextIndexSchema = z14.object({
5051
5191
  });
5052
5192
  var readProjectManifestSnapshot = (basePath) => {
5053
5193
  const manifestPath = resolveProjectLayerManifestPath(basePath);
5054
- if (!existsSync12(manifestPath)) {
5194
+ if (!existsSync13(manifestPath)) {
5055
5195
  return {
5056
5196
  source: createMissingSourceState(PROJECT_LAYER_MANIFEST_RELATIVE_PATH),
5057
5197
  manifest: null
@@ -5082,14 +5222,14 @@ var readProjectManifestSnapshot = (basePath) => {
5082
5222
  };
5083
5223
  var readProjectContextIndexSnapshot = (basePath) => {
5084
5224
  const contextIndexPath = resolveProjectLayerContextIndexPath(basePath);
5085
- if (!existsSync12(contextIndexPath)) {
5225
+ if (!existsSync13(contextIndexPath)) {
5086
5226
  return {
5087
5227
  source: createMissingSourceState(PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH),
5088
5228
  contextIndex: null
5089
5229
  };
5090
5230
  }
5091
5231
  try {
5092
- const parsedJson = JSON.parse(readFileSync18(contextIndexPath, "utf-8"));
5232
+ const parsedJson = JSON.parse(readFileSync19(contextIndexPath, "utf-8"));
5093
5233
  const strictContextIndex = ProjectLayerContextIndexSchema.safeParse(parsedJson);
5094
5234
  if (strictContextIndex.success) {
5095
5235
  return {
@@ -5146,12 +5286,12 @@ var readProjectContextIndexSnapshot = (basePath) => {
5146
5286
  }
5147
5287
  };
5148
5288
  var readDocsStatusSourceState = (basePath) => {
5149
- const docsStatusPath = resolveProjectLayerFilePath2(basePath, DOCS_STATUS_RELATIVE_PATH);
5150
- if (!existsSync12(docsStatusPath)) {
5289
+ const docsStatusPath = resolveProjectLayerFilePath(basePath, DOCS_STATUS_RELATIVE_PATH);
5290
+ if (!existsSync13(docsStatusPath)) {
5151
5291
  return createMissingSourceState(DOCS_STATUS_RELATIVE_PATH);
5152
5292
  }
5153
5293
  try {
5154
- parseProjectLayerDocument(DOCS_STATUS_RELATIVE_PATH, readFileSync18(docsStatusPath, "utf-8"));
5294
+ parseProjectLayerDocument(DOCS_STATUS_RELATIVE_PATH, readFileSync19(docsStatusPath, "utf-8"));
5155
5295
  return buildSourceState({
5156
5296
  path: DOCS_STATUS_RELATIVE_PATH,
5157
5297
  exists: true,
@@ -5182,7 +5322,7 @@ var buildDocumentReadError = (code, message) => `${code}: ${message}`;
5182
5322
  var buildProjectDocumentSnapshot = (params) => {
5183
5323
  let absolutePath;
5184
5324
  try {
5185
- absolutePath = resolveProjectLayerFilePath2(params.basePath, params.indexed.path);
5325
+ absolutePath = resolveProjectLayerFilePath(params.basePath, params.indexed.path);
5186
5326
  } catch (error) {
5187
5327
  return {
5188
5328
  path: params.indexed.path,
@@ -5200,7 +5340,7 @@ var buildProjectDocumentSnapshot = (params) => {
5200
5340
  readError: buildDocumentReadError("unsafe-path", getErrorMessage(error))
5201
5341
  };
5202
5342
  }
5203
- if (!existsSync12(absolutePath)) {
5343
+ if (!existsSync13(absolutePath)) {
5204
5344
  return {
5205
5345
  path: params.indexed.path,
5206
5346
  status: params.indexed.status,
@@ -5218,7 +5358,7 @@ var buildProjectDocumentSnapshot = (params) => {
5218
5358
  };
5219
5359
  }
5220
5360
  try {
5221
- const document = parseProjectLayerDocument(params.indexed.path, readFileSync18(absolutePath, "utf-8"));
5361
+ const document = parseProjectLayerDocument(params.indexed.path, readFileSync19(absolutePath, "utf-8"));
5222
5362
  return {
5223
5363
  path: params.indexed.path,
5224
5364
  status: params.indexed.status,
@@ -5309,11 +5449,11 @@ var buildProjectSnapshot = (basePath) => {
5309
5449
  };
5310
5450
 
5311
5451
  // src/features/studio/runtime-snapshot.ts
5312
- import { existsSync as existsSync13, readFileSync as readFileSync20 } from "fs";
5452
+ import { existsSync as existsSync14, readFileSync as readFileSync21 } from "fs";
5313
5453
  import { join as join22 } from "path";
5314
5454
 
5315
5455
  // src/features/subagents/manifest-io.ts
5316
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync19, writeFileSync as writeFileSync12 } from "fs";
5456
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
5317
5457
  import { dirname as dirname13, join as join21 } from "path";
5318
5458
  var SUBAGENT_MANIFEST_FILENAME = "subagents-manifest.json";
5319
5459
  var parseSubagentManifest = (json) => SubagentManifestSchema.parse(JSON.parse(json));
@@ -5322,7 +5462,7 @@ var resolveSubagentManifestPath = (userBasePath) => join21(userBasePath, ".ai-op
5322
5462
  var readSubagentManifest = (manifestPath) => {
5323
5463
  let raw;
5324
5464
  try {
5325
- raw = readFileSync19(manifestPath, "utf-8");
5465
+ raw = readFileSync20(manifestPath, "utf-8");
5326
5466
  } catch {
5327
5467
  return null;
5328
5468
  }
@@ -5352,7 +5492,7 @@ var readRuntimeManifest = (params) => {
5352
5492
  value: null
5353
5493
  };
5354
5494
  }
5355
- if (!existsSync13(params.manifestPath)) {
5495
+ if (!existsSync14(params.manifestPath)) {
5356
5496
  return {
5357
5497
  source: createMissingSourceState(params.manifestPath),
5358
5498
  value: null
@@ -5390,11 +5530,11 @@ var readHooksSourceState = (codexHomePath, unavailableReason) => {
5390
5530
  });
5391
5531
  }
5392
5532
  const hooksPath = resolveCodexHooksPath(codexHomePath);
5393
- if (!existsSync13(hooksPath)) {
5533
+ if (!existsSync14(hooksPath)) {
5394
5534
  return createMissingSourceState(hooksPath);
5395
5535
  }
5396
5536
  try {
5397
- const parsed = JSON.parse(readFileSync20(hooksPath, "utf-8"));
5537
+ const parsed = JSON.parse(readFileSync21(hooksPath, "utf-8"));
5398
5538
  if (!isJsonRecord4(parsed)) {
5399
5539
  return buildSourceState({
5400
5540
  path: hooksPath,
@@ -5455,7 +5595,7 @@ var buildInstalledPathStates = (params) => {
5455
5595
  }
5456
5596
  return params.installedPaths.map((path) => ({
5457
5597
  path,
5458
- exists: existsSync13(join22(params.userBasePath ?? "", path))
5598
+ exists: existsSync14(join22(params.userBasePath ?? "", path))
5459
5599
  }));
5460
5600
  };
5461
5601
  var buildInstalledSkillMap = (installedSkills) => new Map(
@@ -5659,7 +5799,7 @@ var registerStudioCommands = (program) => {
5659
5799
 
5660
5800
  // src/features/subagents/commands.ts
5661
5801
  import * as p14 from "@clack/prompts";
5662
- import { existsSync as existsSync15, rmSync as rmSync8 } from "fs";
5802
+ import { existsSync as existsSync16, rmSync as rmSync8 } from "fs";
5663
5803
 
5664
5804
  // src/features/subagents/renderer.ts
5665
5805
  import { resolve as resolve12 } from "path";
@@ -5782,7 +5922,7 @@ var buildSubagentInstallPlan = (params) => {
5782
5922
  };
5783
5923
 
5784
5924
  // src/features/subagents/install-files.ts
5785
- import { existsSync as existsSync14, mkdirSync as mkdirSync11, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
5925
+ import { existsSync as existsSync15, mkdirSync as mkdirSync11, rmSync as rmSync7, writeFileSync as writeFileSync13 } from "fs";
5786
5926
  import { dirname as dirname14, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve13 } from "path";
5787
5927
  var resolveInsideBasePath = (basePath, relativePath) => {
5788
5928
  const absBasePath = resolve13(basePath);
@@ -5798,7 +5938,7 @@ var installSubagentPackages = (basePath, packages) => {
5798
5938
  for (const subagentPackage of packages) {
5799
5939
  for (const file of subagentPackage.files) {
5800
5940
  const absPath = resolveInsideBasePath(basePath, file.relativePath);
5801
- if (existsSync14(absPath)) {
5941
+ if (existsSync15(absPath)) {
5802
5942
  rmSync7(absPath, { recursive: true, force: true });
5803
5943
  }
5804
5944
  mkdirSync11(dirname14(absPath), { recursive: true });
@@ -5812,7 +5952,7 @@ var removeSubagentFiles = (basePath, relativePaths) => {
5812
5952
  const removed = [];
5813
5953
  for (const relativePath of relativePaths) {
5814
5954
  const absPath = resolveInsideBasePath(basePath, relativePath);
5815
- if (!existsSync14(absPath)) continue;
5955
+ if (!existsSync15(absPath)) continue;
5816
5956
  rmSync7(absPath, { recursive: true, force: true });
5817
5957
  removed.push(relativePath);
5818
5958
  }
@@ -5874,7 +6014,7 @@ var writeUserSubagentState = (params) => {
5874
6014
  };
5875
6015
  var readInstalledSubagents = (basePath) => readSubagentManifest(resolveSubagentManifestPath(basePath))?.subagents ?? [];
5876
6016
  var warnMissingSkills = (requiredSkills) => {
5877
- const missing = requiredSkills.filter((skill) => !existsSync15(skill.path));
6017
+ const missing = requiredSkills.filter((skill) => !existsSync16(skill.path));
5878
6018
  if (missing.length === 0) {
5879
6019
  return;
5880
6020
  }