ai-ops-cli 1.5.0 → 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.
@@ -32,6 +32,14 @@ update_when:
32
32
  - project-owned 문서가 비어 있거나 stale하면 실제 코드, 설정, schema, runtime 파일을 우선한다.
33
33
  - 문서 상태가 애매하면 `docs/docs-status.md`와 각 문서 frontmatter를 함께 확인한다.
34
34
 
35
+ ## Reference-Backed Implementation
36
+
37
+ reference 문서에 근거해 구현할 때는 문서의 핵심 제약을 먼저 검증 가능한 acceptance condition으로 바꾼다.
38
+
39
+ - `must`, `required`, `top-level`, `cannot mix`, `does not compose` 같은 강제 제약은 그냥 읽고 넘어가지 않는다.
40
+ - config, schema, parser, runtime이 직접 해석하는 구조와 permission, sandbox, credential, network, filesystem boundary는 테스트, fixture, smoke command, audit check 중 하나로 고정한다.
41
+ - 문서의 중요한 문장을 테스트 이름으로 바꾼다.
42
+
35
43
  ## 보존 원칙
36
44
 
37
45
  - 사용자 변경으로 보이는 diff는 되돌리지 않는다.
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));
@@ -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,