ai-ops-cli 1.1.0 → 1.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.
- package/README.ko.md +37 -12
- package/README.md +37 -12
- package/data/context-layer/docs/agent/rules/routing-rules.md +2 -1
- package/data/context-layer/docs/agent/workflow.md +1 -1
- package/data/integrations/integration-registry.json +44 -0
- package/data/skills/skill-registry.json +8 -0
- package/data/skills/task-skills/pc/SKILL.md +266 -0
- package/data/skills/task-skills/pc/agents/openai.yaml +6 -0
- package/data/skills/task-skills/pc/references/templates.md +264 -0
- package/dist/bin/index.js +1325 -399
- package/dist/bin/index.js.map +1 -1
- package/package.json +2 -2
package/dist/bin/index.js
CHANGED
|
@@ -267,50 +267,169 @@ var SubagentManifestSchema = z9.object({
|
|
|
267
267
|
generatedAt: z9.string().datetime({ offset: true })
|
|
268
268
|
}).strict();
|
|
269
269
|
|
|
270
|
-
// src/core/schemas/
|
|
270
|
+
// src/core/schemas/integration.schema.ts
|
|
271
271
|
import { z as z10 } from "zod";
|
|
272
|
-
var
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
var INTEGRATION_ID = {
|
|
273
|
+
CONTEXT_PROMOTION: "context-promotion",
|
|
274
|
+
PC: "pc"
|
|
275
|
+
};
|
|
276
|
+
var INTEGRATION_COMPONENT_TYPE = {
|
|
277
|
+
SKILL: "skill",
|
|
278
|
+
CODEX_HOOK: "codex-hook",
|
|
279
|
+
RECEIPT_CONFIG: "receipt-config"
|
|
280
|
+
};
|
|
281
|
+
var IntegrationIdSchema = z10.union([z10.literal(INTEGRATION_ID.CONTEXT_PROMOTION), z10.literal(INTEGRATION_ID.PC)]);
|
|
282
|
+
var IntegrationSkillComponentSchema = z10.object({
|
|
283
|
+
type: z10.literal(INTEGRATION_COMPONENT_TYPE.SKILL),
|
|
284
|
+
id: z10.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
285
|
+
tools: z10.array(SkillToolSchema).min(1),
|
|
286
|
+
owned: z10.boolean()
|
|
287
|
+
}).strict();
|
|
288
|
+
var IntegrationCodexHookComponentSchema = z10.object({
|
|
289
|
+
type: z10.literal(INTEGRATION_COMPONENT_TYPE.CODEX_HOOK),
|
|
290
|
+
id: IntegrationIdSchema,
|
|
291
|
+
command: z10.string().min(1),
|
|
292
|
+
owned: z10.boolean()
|
|
293
|
+
}).strict();
|
|
294
|
+
var IntegrationReceiptConfigComponentSchema = z10.object({
|
|
295
|
+
type: z10.literal(INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG),
|
|
296
|
+
id: z10.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
297
|
+
storagePath: z10.string().min(1),
|
|
298
|
+
owned: z10.boolean()
|
|
299
|
+
}).strict();
|
|
300
|
+
var IntegrationComponentSchema = z10.union([
|
|
301
|
+
IntegrationSkillComponentSchema,
|
|
302
|
+
IntegrationCodexHookComponentSchema,
|
|
303
|
+
IntegrationReceiptConfigComponentSchema
|
|
304
|
+
]);
|
|
305
|
+
var InstalledIntegrationSchema = z10.object({
|
|
306
|
+
id: IntegrationIdSchema,
|
|
307
|
+
components: z10.array(IntegrationComponentSchema),
|
|
308
|
+
installedAt: z10.string().min(1),
|
|
309
|
+
updatedAt: z10.string().min(1)
|
|
310
|
+
}).strict();
|
|
311
|
+
var IntegrationManifestSchema = z10.object({
|
|
312
|
+
schemaVersion: z10.literal(1),
|
|
313
|
+
kind: z10.literal("ai-ops-integrations-manifest"),
|
|
314
|
+
integrations: z10.array(InstalledIntegrationSchema),
|
|
315
|
+
cliVersion: z10.string().min(1),
|
|
316
|
+
generatedAt: z10.string().min(1)
|
|
317
|
+
}).strict();
|
|
318
|
+
|
|
319
|
+
// src/core/schemas/integration-catalog.schema.ts
|
|
320
|
+
import { z as z11 } from "zod";
|
|
321
|
+
var ComponentIdSchema = z11.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "component id must be kebab-case");
|
|
322
|
+
var IntegrationCatalogSkillComponentSchema = z11.object({
|
|
323
|
+
type: z11.literal(INTEGRATION_COMPONENT_TYPE.SKILL),
|
|
324
|
+
id: ComponentIdSchema,
|
|
325
|
+
tools: z11.array(z11.literal("codex")).min(1)
|
|
326
|
+
}).strict();
|
|
327
|
+
var IntegrationCatalogCodexHookComponentSchema = z11.object({
|
|
328
|
+
type: z11.literal(INTEGRATION_COMPONENT_TYPE.CODEX_HOOK),
|
|
329
|
+
id: IntegrationIdSchema
|
|
330
|
+
}).strict();
|
|
331
|
+
var IntegrationCatalogReceiptConfigComponentSchema = z11.object({
|
|
332
|
+
type: z11.literal(INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG),
|
|
333
|
+
id: ComponentIdSchema,
|
|
334
|
+
storage_path: z11.string().min(1)
|
|
335
|
+
}).strict();
|
|
336
|
+
var IntegrationCatalogComponentSchema = z11.union([
|
|
337
|
+
IntegrationCatalogSkillComponentSchema,
|
|
338
|
+
IntegrationCatalogCodexHookComponentSchema,
|
|
339
|
+
IntegrationCatalogReceiptConfigComponentSchema
|
|
340
|
+
]);
|
|
341
|
+
var IntegrationCatalogEntrySchema = z11.object({
|
|
342
|
+
id: IntegrationIdSchema,
|
|
343
|
+
description: z11.string().min(1),
|
|
344
|
+
components: z11.array(IntegrationCatalogComponentSchema).min(1)
|
|
345
|
+
}).strict().superRefine((entry, ctx) => {
|
|
346
|
+
const hasSkill = entry.components.some((component) => component.type === INTEGRATION_COMPONENT_TYPE.SKILL);
|
|
347
|
+
const hasCodexHook = entry.components.some((component) => component.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK);
|
|
348
|
+
const hasReceiptConfig = entry.components.some(
|
|
349
|
+
(component) => component.type === INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG
|
|
350
|
+
);
|
|
351
|
+
if (!hasSkill) {
|
|
352
|
+
ctx.addIssue({
|
|
353
|
+
code: z11.ZodIssueCode.custom,
|
|
354
|
+
path: ["components"],
|
|
355
|
+
message: `integration must declare a skill component: ${entry.id}`
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (!hasCodexHook) {
|
|
359
|
+
ctx.addIssue({
|
|
360
|
+
code: z11.ZodIssueCode.custom,
|
|
361
|
+
path: ["components"],
|
|
362
|
+
message: `integration must declare a codex-hook component: ${entry.id}`
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
if (!hasReceiptConfig) {
|
|
366
|
+
ctx.addIssue({
|
|
367
|
+
code: z11.ZodIssueCode.custom,
|
|
368
|
+
path: ["components"],
|
|
369
|
+
message: `integration must declare a receipt-config component: ${entry.id}`
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
var IntegrationCatalogSchema = z11.object({
|
|
374
|
+
integrations: z11.array(IntegrationCatalogEntrySchema)
|
|
375
|
+
}).strict().superRefine((catalog, ctx) => {
|
|
376
|
+
const seen = /* @__PURE__ */ new Set();
|
|
377
|
+
for (const [index, entry] of catalog.integrations.entries()) {
|
|
378
|
+
if (seen.has(entry.id)) {
|
|
379
|
+
ctx.addIssue({
|
|
380
|
+
code: z11.ZodIssueCode.custom,
|
|
381
|
+
path: ["integrations", index, "id"],
|
|
382
|
+
message: `duplicate integration id: ${entry.id}`
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
seen.add(entry.id);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// src/core/schemas/pack.schema.ts
|
|
390
|
+
import { z as z12 } from "zod";
|
|
391
|
+
var PackIdSchema = z12.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case");
|
|
392
|
+
var PackSourcePathSchema = z12.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)*$/, "source_path must be relative kebab-case path");
|
|
393
|
+
var PackCatalogEntrySchema = z12.object({
|
|
275
394
|
id: PackIdSchema,
|
|
276
395
|
source_path: PackSourcePathSchema
|
|
277
396
|
}).strict();
|
|
278
|
-
var PackCatalogSchema =
|
|
279
|
-
packs:
|
|
397
|
+
var PackCatalogSchema = z12.object({
|
|
398
|
+
packs: z12.array(PackCatalogEntrySchema)
|
|
280
399
|
}).strict();
|
|
281
400
|
|
|
282
401
|
// src/core/schemas/manifest.schema.ts
|
|
283
|
-
import { z as
|
|
284
|
-
var SettingsConfigSchema =
|
|
285
|
-
claude:
|
|
286
|
-
gemini:
|
|
287
|
-
prettierignore:
|
|
402
|
+
import { z as z13 } from "zod";
|
|
403
|
+
var SettingsConfigSchema = z13.object({
|
|
404
|
+
claude: z13.array(z13.string().min(1)).optional(),
|
|
405
|
+
gemini: z13.array(z13.string().min(1)).optional(),
|
|
406
|
+
prettierignore: z13.boolean().optional()
|
|
288
407
|
}).strict();
|
|
289
|
-
var WorkspaceEntrySchema =
|
|
290
|
-
preset:
|
|
291
|
-
rules:
|
|
408
|
+
var WorkspaceEntrySchema = z13.object({
|
|
409
|
+
preset: z13.string().min(1),
|
|
410
|
+
rules: z13.array(z13.string().min(1))
|
|
292
411
|
}).strict();
|
|
293
|
-
var ManifestSchema =
|
|
294
|
-
tools:
|
|
295
|
-
scope:
|
|
412
|
+
var ManifestSchema = z13.object({
|
|
413
|
+
tools: z13.array(z13.string().min(1)).min(1),
|
|
414
|
+
scope: z13.literal("project"),
|
|
296
415
|
/** 비모노레포 단일 preset */
|
|
297
|
-
preset:
|
|
416
|
+
preset: z13.string().min(1).optional(),
|
|
298
417
|
/** 모노레포: workspace path → { preset, rules } */
|
|
299
|
-
workspaces:
|
|
300
|
-
installed_rules:
|
|
418
|
+
workspaces: z13.record(z13.string(), WorkspaceEntrySchema).optional(),
|
|
419
|
+
installed_rules: z13.array(z13.string().min(1)),
|
|
301
420
|
/** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */
|
|
302
|
-
installed_files:
|
|
421
|
+
installed_files: z13.array(z13.string().min(1)).optional(),
|
|
303
422
|
/** skill 설치 루트 디렉토리 목록 */
|
|
304
|
-
installed_skills:
|
|
423
|
+
installed_skills: z13.array(InstalledSkillSchema).optional(),
|
|
305
424
|
/** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */
|
|
306
|
-
appended_files:
|
|
425
|
+
appended_files: z13.array(z13.string().min(1)).optional(),
|
|
307
426
|
/** init 시 선택된 settings 항목 — update 시 재생성에 사용 */
|
|
308
427
|
settings: SettingsConfigSchema.optional(),
|
|
309
428
|
/** init/update 실행 시점의 CLI 패키지 버전 — 버전 변경 감지에 사용 */
|
|
310
|
-
cliVersion:
|
|
429
|
+
cliVersion: z13.string().optional(),
|
|
311
430
|
/** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
|
|
312
|
-
sourceHash:
|
|
313
|
-
generatedAt:
|
|
431
|
+
sourceHash: z13.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
|
|
432
|
+
generatedAt: z13.string().datetime({ offset: true })
|
|
314
433
|
}).strict();
|
|
315
434
|
|
|
316
435
|
// src/core/loader.ts
|
|
@@ -444,6 +563,10 @@ var assertSubagentFrontmatterName = (params) => {
|
|
|
444
563
|
}
|
|
445
564
|
};
|
|
446
565
|
var loadSubagentCatalog = (subagentsDir) => SubagentCatalogSchema.parse(JSON.parse(readFileSync(resolve(subagentsDir, "subagent-registry.json"), "utf-8")));
|
|
566
|
+
var loadIntegrationCatalog = (integrationsDir) => IntegrationCatalogSchema.parse(
|
|
567
|
+
JSON.parse(readFileSync(resolve(integrationsDir, "integration-registry.json"), "utf-8"))
|
|
568
|
+
);
|
|
569
|
+
var loadAllIntegrations = (integrationsDir) => [...loadIntegrationCatalog(integrationsDir).integrations].sort((a, b) => a.id.localeCompare(b.id));
|
|
447
570
|
var loadAllSubagents = (subagentsDir) => {
|
|
448
571
|
const catalog = loadSubagentCatalog(subagentsDir);
|
|
449
572
|
const entries = [...catalog.subagents].sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -784,23 +907,65 @@ var writeSubagentManifest = (manifestPath, manifest) => {
|
|
|
784
907
|
writeFileSync3(manifestPath, serializeSubagentManifest(manifest), "utf-8");
|
|
785
908
|
};
|
|
786
909
|
|
|
910
|
+
// src/core/integration-manifest-io.ts
|
|
911
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
912
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
913
|
+
var INTEGRATION_MANIFEST_FILENAME = "integrations-manifest.json";
|
|
914
|
+
var parseIntegrationManifest = (json) => IntegrationManifestSchema.parse(JSON.parse(json));
|
|
915
|
+
var serializeIntegrationManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
916
|
+
var resolveIntegrationManifestPath = (userBasePath) => join9(userBasePath, ".ai-ops", INTEGRATION_MANIFEST_FILENAME);
|
|
917
|
+
var readIntegrationManifest = (manifestPath) => {
|
|
918
|
+
let raw;
|
|
919
|
+
try {
|
|
920
|
+
raw = readFileSync6(manifestPath, "utf-8");
|
|
921
|
+
} catch {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
return parseIntegrationManifest(raw);
|
|
925
|
+
};
|
|
926
|
+
var writeIntegrationManifest = (manifestPath, manifest) => {
|
|
927
|
+
mkdirSync4(dirname5(manifestPath), { recursive: true });
|
|
928
|
+
writeFileSync4(manifestPath, serializeIntegrationManifest(manifest), "utf-8");
|
|
929
|
+
};
|
|
930
|
+
var findInstalledIntegration = (integrations, integrationId) => integrations.find((integration) => integration.id === integrationId);
|
|
931
|
+
var upsertInstalledIntegration = (integrations, nextIntegration) => [
|
|
932
|
+
...integrations.filter((integration) => integration.id !== nextIntegration.id),
|
|
933
|
+
nextIntegration
|
|
934
|
+
];
|
|
935
|
+
var removeInstalledIntegration = (integrations, integrationId) => integrations.filter((integration) => integration.id !== integrationId);
|
|
936
|
+
var writeUserIntegrationState = (params) => {
|
|
937
|
+
const previous = readIntegrationManifest(params.manifestPath);
|
|
938
|
+
const integrations = params.removeIntegrationId ? removeInstalledIntegration(previous?.integrations ?? [], params.removeIntegrationId) : params.nextIntegration ? upsertInstalledIntegration(previous?.integrations ?? [], params.nextIntegration) : previous?.integrations ?? [];
|
|
939
|
+
if (integrations.length === 0) {
|
|
940
|
+
rmSync(params.manifestPath, { force: true });
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
writeIntegrationManifest(params.manifestPath, {
|
|
944
|
+
schemaVersion: 1,
|
|
945
|
+
kind: "ai-ops-integrations-manifest",
|
|
946
|
+
integrations,
|
|
947
|
+
cliVersion: params.cliVersion,
|
|
948
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
|
|
787
952
|
// src/core/install-plan.ts
|
|
788
|
-
import { join as
|
|
953
|
+
import { join as join10 } from "path";
|
|
789
954
|
|
|
790
955
|
// src/core/project-layer.ts
|
|
791
|
-
import { existsSync, mkdirSync as
|
|
792
|
-
import { dirname as
|
|
956
|
+
import { existsSync, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
957
|
+
import { dirname as dirname7, isAbsolute, join as join11, relative, resolve as resolve5 } from "path";
|
|
793
958
|
|
|
794
959
|
// src/core/paths.ts
|
|
795
|
-
import { dirname as
|
|
960
|
+
import { dirname as dirname6, resolve as resolve4 } from "path";
|
|
796
961
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
797
|
-
var __dirname2 =
|
|
962
|
+
var __dirname2 = dirname6(fileURLToPath2(import.meta.url));
|
|
798
963
|
var COMPILER_DATA_DIR = resolve4(__dirname2, "..", "..", "data");
|
|
799
964
|
|
|
800
965
|
// src/core/project-layer.ts
|
|
801
966
|
var PROJECT_LAYER_MANIFEST_RELATIVE_PATH = ".ai-ops/manifest.json";
|
|
802
967
|
var PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH = ".ai-ops/context-layer.json";
|
|
803
|
-
var CONTEXT_LAYER_DATA_DIR =
|
|
968
|
+
var CONTEXT_LAYER_DATA_DIR = join11(COMPILER_DATA_DIR, "context-layer");
|
|
804
969
|
var TOOL_ORDER = ["codex", "gemini", "claude-code"];
|
|
805
970
|
var DEFAULT_TOOLS = TOOL_ORDER;
|
|
806
971
|
var TEMPLATE_PATHS = [
|
|
@@ -827,10 +992,10 @@ var RESERVED_DOCUMENT_WARNINGS = [
|
|
|
827
992
|
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
828
993
|
"Do not use this document as current decision-making evidence"
|
|
829
994
|
];
|
|
830
|
-
var resolveProjectLayerManifestPath = (basePath) =>
|
|
831
|
-
var resolveProjectLayerContextIndexPath = (basePath) =>
|
|
832
|
-
var resolveTemplatePath = (relativePath) =>
|
|
833
|
-
var toRelativeDir = (relativePath) =>
|
|
995
|
+
var resolveProjectLayerManifestPath = (basePath) => join11(basePath, PROJECT_LAYER_MANIFEST_RELATIVE_PATH);
|
|
996
|
+
var resolveProjectLayerContextIndexPath = (basePath) => join11(basePath, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH);
|
|
997
|
+
var resolveTemplatePath = (relativePath) => join11(CONTEXT_LAYER_DATA_DIR, relativePath);
|
|
998
|
+
var toRelativeDir = (relativePath) => dirname7(relativePath);
|
|
834
999
|
var resolveProjectLayerFilePath = (basePath, relativePath) => {
|
|
835
1000
|
if (!isSafeProjectLayerPath(relativePath)) {
|
|
836
1001
|
throw new Error(`Unsafe project layer path: ${relativePath}`);
|
|
@@ -945,8 +1110,8 @@ var loadTemplateSpec = (relativePath, content) => {
|
|
|
945
1110
|
};
|
|
946
1111
|
var loadProjectLayerTemplateSpecs = (tools) => {
|
|
947
1112
|
const selectedPaths = TEMPLATE_PATHS.filter((relativePath) => shouldIncludeTemplate(relativePath, tools));
|
|
948
|
-
const nonStatusSpecs = selectedPaths.filter((relativePath) => relativePath !== "docs/docs-status.md").map((relativePath) => loadTemplateSpec(relativePath,
|
|
949
|
-
const statusTemplate =
|
|
1113
|
+
const nonStatusSpecs = selectedPaths.filter((relativePath) => relativePath !== "docs/docs-status.md").map((relativePath) => loadTemplateSpec(relativePath, readFileSync7(resolveTemplatePath(relativePath), "utf-8")));
|
|
1114
|
+
const statusTemplate = readFileSync7(resolveTemplatePath("docs/docs-status.md"), "utf-8");
|
|
950
1115
|
const statusPlaceholderSpec = loadTemplateSpec("docs/docs-status.md", statusTemplate);
|
|
951
1116
|
const specsForStatus = [...nonStatusSpecs, statusPlaceholderSpec].sort((a, b) => a.path.localeCompare(b.path));
|
|
952
1117
|
const statusContent = statusTemplate.replace("{{documents_table}}", buildDocsStatusRows(specsForStatus));
|
|
@@ -956,7 +1121,7 @@ var loadProjectLayerTemplateSpecs = (tools) => {
|
|
|
956
1121
|
var computeProjectLayerSourceHash = (specs) => computeHash(specs.map((spec) => `${spec.path}:${spec.content}`));
|
|
957
1122
|
var readProjectLayerManifest = (basePath) => {
|
|
958
1123
|
try {
|
|
959
|
-
return parseProjectLayerManifest(
|
|
1124
|
+
return parseProjectLayerManifest(readFileSync7(resolveProjectLayerManifestPath(basePath), "utf-8"));
|
|
960
1125
|
} catch (error) {
|
|
961
1126
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
962
1127
|
return null;
|
|
@@ -966,12 +1131,12 @@ var readProjectLayerManifest = (basePath) => {
|
|
|
966
1131
|
};
|
|
967
1132
|
var writeProjectLayerManifest = (basePath, manifest) => {
|
|
968
1133
|
const manifestPath = resolveProjectLayerManifestPath(basePath);
|
|
969
|
-
|
|
970
|
-
|
|
1134
|
+
mkdirSync5(dirname7(manifestPath), { recursive: true });
|
|
1135
|
+
writeFileSync5(manifestPath, serializeProjectLayerManifest(manifest), "utf-8");
|
|
971
1136
|
};
|
|
972
1137
|
var readProjectLayerContextIndex = (basePath) => {
|
|
973
1138
|
try {
|
|
974
|
-
return parseProjectLayerContextIndex(
|
|
1139
|
+
return parseProjectLayerContextIndex(readFileSync7(resolveProjectLayerContextIndexPath(basePath), "utf-8"));
|
|
975
1140
|
} catch (error) {
|
|
976
1141
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
977
1142
|
return null;
|
|
@@ -981,8 +1146,8 @@ var readProjectLayerContextIndex = (basePath) => {
|
|
|
981
1146
|
};
|
|
982
1147
|
var writeProjectLayerContextIndex = (basePath, contextIndex) => {
|
|
983
1148
|
const contextIndexPath = resolveProjectLayerContextIndexPath(basePath);
|
|
984
|
-
|
|
985
|
-
|
|
1149
|
+
mkdirSync5(dirname7(contextIndexPath), { recursive: true });
|
|
1150
|
+
writeFileSync5(contextIndexPath, serializeProjectLayerContextIndex(contextIndex), "utf-8");
|
|
986
1151
|
};
|
|
987
1152
|
var installManagedFiles = (basePath, specs, meta) => {
|
|
988
1153
|
const written = [];
|
|
@@ -991,24 +1156,24 @@ var installManagedFiles = (basePath, specs, meta) => {
|
|
|
991
1156
|
const absolutePath = resolveProjectLayerFilePath(basePath, spec.path);
|
|
992
1157
|
const wrappedContent = wrapWithSection(spec.content, meta);
|
|
993
1158
|
if (!existsSync(absolutePath)) {
|
|
994
|
-
|
|
995
|
-
|
|
1159
|
+
mkdirSync5(dirname7(absolutePath), { recursive: true });
|
|
1160
|
+
writeFileSync5(absolutePath, wrappedContent + "\n", "utf-8");
|
|
996
1161
|
written.push(spec.path);
|
|
997
1162
|
continue;
|
|
998
1163
|
}
|
|
999
|
-
const existing =
|
|
1164
|
+
const existing = readFileSync7(absolutePath, "utf-8");
|
|
1000
1165
|
if (hasAiOpsSection(existing)) {
|
|
1001
|
-
|
|
1166
|
+
writeFileSync5(absolutePath, replaceAiOpsSection(existing, wrappedContent), "utf-8");
|
|
1002
1167
|
const stripped = stripAiOpsSection(existing);
|
|
1003
1168
|
(stripped.trim().length > 0 ? appended : written).push(spec.path);
|
|
1004
1169
|
continue;
|
|
1005
1170
|
}
|
|
1006
1171
|
if (hasLegacyHeader(existing)) {
|
|
1007
|
-
|
|
1172
|
+
writeFileSync5(absolutePath, wrappedContent + "\n", "utf-8");
|
|
1008
1173
|
written.push(spec.path);
|
|
1009
1174
|
continue;
|
|
1010
1175
|
}
|
|
1011
|
-
|
|
1176
|
+
writeFileSync5(absolutePath, existing.trimEnd() + "\n\n" + wrappedContent + "\n", "utf-8");
|
|
1012
1177
|
appended.push(spec.path);
|
|
1013
1178
|
}
|
|
1014
1179
|
return { written, appended };
|
|
@@ -1023,8 +1188,8 @@ var installProjectFiles = (params) => {
|
|
|
1023
1188
|
const absolutePath = resolveProjectLayerFilePath(params.basePath, spec.path);
|
|
1024
1189
|
const previous = previousByPath.get(spec.path);
|
|
1025
1190
|
if (!existsSync(absolutePath)) {
|
|
1026
|
-
|
|
1027
|
-
|
|
1191
|
+
mkdirSync5(dirname7(absolutePath), { recursive: true });
|
|
1192
|
+
writeFileSync5(absolutePath, spec.content + "\n", "utf-8");
|
|
1028
1193
|
created.push(spec.path);
|
|
1029
1194
|
records.push({
|
|
1030
1195
|
path: spec.path,
|
|
@@ -1033,11 +1198,11 @@ var installProjectFiles = (params) => {
|
|
|
1033
1198
|
});
|
|
1034
1199
|
continue;
|
|
1035
1200
|
}
|
|
1036
|
-
const existingContent =
|
|
1201
|
+
const existingContent = readFileSync7(absolutePath, "utf-8").trimEnd();
|
|
1037
1202
|
const existingHash = computeHash([existingContent]);
|
|
1038
1203
|
if (previous?.created === true && existingHash === previous.templateHash) {
|
|
1039
1204
|
if (existingHash !== spec.contentHash) {
|
|
1040
|
-
|
|
1205
|
+
writeFileSync5(absolutePath, spec.content + "\n", "utf-8");
|
|
1041
1206
|
refreshed.push(spec.path);
|
|
1042
1207
|
} else {
|
|
1043
1208
|
preserved.push(spec.path);
|
|
@@ -1060,7 +1225,7 @@ var installProjectFiles = (params) => {
|
|
|
1060
1225
|
};
|
|
1061
1226
|
var buildContextIndexFromDisk = (params) => {
|
|
1062
1227
|
const documents = params.documentPaths.map(
|
|
1063
|
-
(path) => parseProjectLayerDocument(path,
|
|
1228
|
+
(path) => parseProjectLayerDocument(path, readFileSync7(resolveProjectLayerFilePath(params.basePath, path), "utf-8"))
|
|
1064
1229
|
);
|
|
1065
1230
|
return ProjectLayerContextIndexSchema.parse({
|
|
1066
1231
|
schemaVersion: 1,
|
|
@@ -1069,14 +1234,14 @@ var buildContextIndexFromDisk = (params) => {
|
|
|
1069
1234
|
generatedAt: params.generatedAt
|
|
1070
1235
|
});
|
|
1071
1236
|
};
|
|
1072
|
-
var computeProjectFileHash = (basePath, relativePath) => computeHash([
|
|
1237
|
+
var computeProjectFileHash = (basePath, relativePath) => computeHash([readFileSync7(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
|
|
1073
1238
|
var collectDocumentPathsFromManifest = (manifest) => [
|
|
1074
1239
|
...manifest.managed_files.map((file) => file.path),
|
|
1075
1240
|
...manifest.project_files.map((file) => file.path),
|
|
1076
1241
|
...manifest.packs.flatMap((pack) => pack.documents.map((file) => file.path))
|
|
1077
1242
|
].sort();
|
|
1078
1243
|
var buildDocsStatusRowsFromDisk = (params) => params.documentPaths.map((path) => {
|
|
1079
|
-
const document = parseProjectLayerDocument(path,
|
|
1244
|
+
const document = parseProjectLayerDocument(path, readFileSync7(resolveProjectLayerFilePath(params.basePath, path), "utf-8"));
|
|
1080
1245
|
return `| ${document.path} | ${document.status} | ${document.owner} |`;
|
|
1081
1246
|
});
|
|
1082
1247
|
var replaceDocsStatusRows = (content, rows) => {
|
|
@@ -1092,8 +1257,8 @@ var updateDocsStatusTable = (basePath, documentPaths) => {
|
|
|
1092
1257
|
const absolutePath = resolveProjectLayerFilePath(basePath, docsStatusPath);
|
|
1093
1258
|
const beforeHash = computeProjectFileHash(basePath, docsStatusPath);
|
|
1094
1259
|
const rows = buildDocsStatusRowsFromDisk({ basePath, documentPaths });
|
|
1095
|
-
const nextContent = replaceDocsStatusRows(
|
|
1096
|
-
|
|
1260
|
+
const nextContent = replaceDocsStatusRows(readFileSync7(absolutePath, "utf-8"), rows);
|
|
1261
|
+
writeFileSync5(absolutePath, nextContent, "utf-8");
|
|
1097
1262
|
return {
|
|
1098
1263
|
beforeHash,
|
|
1099
1264
|
afterHash: computeProjectFileHash(basePath, docsStatusPath)
|
|
@@ -1248,7 +1413,7 @@ var readDocumentSafely = (basePath, path) => {
|
|
|
1248
1413
|
if (!existsSync(absolutePath)) {
|
|
1249
1414
|
return issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${path}`);
|
|
1250
1415
|
}
|
|
1251
|
-
return parseProjectLayerDocument(path,
|
|
1416
|
+
return parseProjectLayerDocument(path, readFileSync7(absolutePath, "utf-8"));
|
|
1252
1417
|
} catch (error) {
|
|
1253
1418
|
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1254
1419
|
return issue("error", "invalid-frontmatter", `${path} frontmatter \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
|
|
@@ -1343,7 +1508,7 @@ var diffProjectLayer = (basePath) => {
|
|
|
1343
1508
|
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1344
1509
|
continue;
|
|
1345
1510
|
}
|
|
1346
|
-
const content =
|
|
1511
|
+
const content = readFileSync7(absolutePath, "utf-8");
|
|
1347
1512
|
const meta = parseAiOpsMeta(content);
|
|
1348
1513
|
if (!meta) {
|
|
1349
1514
|
issues.push(issue("error", "missing-managed-section", `managed section \uBA54\uD0C0 \uC5C6\uC74C: ${file.path}`));
|
|
@@ -1405,7 +1570,7 @@ var auditProjectLayer = (basePath) => {
|
|
|
1405
1570
|
}
|
|
1406
1571
|
let docsStatusEntries = [];
|
|
1407
1572
|
try {
|
|
1408
|
-
docsStatusEntries = parseDocsStatusEntries(
|
|
1573
|
+
docsStatusEntries = parseDocsStatusEntries(readFileSync7(docsStatusPath, "utf-8"));
|
|
1409
1574
|
} catch (error) {
|
|
1410
1575
|
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1411
1576
|
issues.push(issue("error", "invalid-docs-status", `docs/docs-status.md \uD30C\uC2F1 \uC2E4\uD328: ${reason}`));
|
|
@@ -1435,16 +1600,16 @@ function removeManagedProjectFile(basePath, relativePath) {
|
|
|
1435
1600
|
if (!existsSync(absolutePath)) {
|
|
1436
1601
|
return { deleted: [], cleaned: [], preserved: [], notFound: [relativePath] };
|
|
1437
1602
|
}
|
|
1438
|
-
const content =
|
|
1603
|
+
const content = readFileSync7(absolutePath, "utf-8");
|
|
1439
1604
|
if (!hasAiOpsSection(content)) {
|
|
1440
1605
|
return { deleted: [], cleaned: [], preserved: [relativePath], notFound: [] };
|
|
1441
1606
|
}
|
|
1442
1607
|
const stripped = stripAiOpsSection(content);
|
|
1443
1608
|
if (stripped.trim().length === 0) {
|
|
1444
|
-
|
|
1609
|
+
rmSync2(absolutePath);
|
|
1445
1610
|
return { deleted: [relativePath], cleaned: [], preserved: [], notFound: [] };
|
|
1446
1611
|
}
|
|
1447
|
-
|
|
1612
|
+
writeFileSync5(absolutePath, stripped, "utf-8");
|
|
1448
1613
|
return { deleted: [], cleaned: [relativePath], preserved: [], notFound: [] };
|
|
1449
1614
|
}
|
|
1450
1615
|
var removeCreateOnlyProjectFile = (basePath, file) => {
|
|
@@ -1452,10 +1617,10 @@ var removeCreateOnlyProjectFile = (basePath, file) => {
|
|
|
1452
1617
|
if (!existsSync(absolutePath)) {
|
|
1453
1618
|
return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
|
|
1454
1619
|
}
|
|
1455
|
-
const content =
|
|
1620
|
+
const content = readFileSync7(absolutePath, "utf-8").trimEnd();
|
|
1456
1621
|
const currentHash = computeHash([content]);
|
|
1457
1622
|
if (file.created && currentHash === file.templateHash) {
|
|
1458
|
-
|
|
1623
|
+
rmSync2(absolutePath);
|
|
1459
1624
|
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1460
1625
|
}
|
|
1461
1626
|
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
@@ -1465,9 +1630,9 @@ var removePackOwnedFile = (basePath, file) => {
|
|
|
1465
1630
|
if (!existsSync(absolutePath)) {
|
|
1466
1631
|
return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
|
|
1467
1632
|
}
|
|
1468
|
-
const currentHash = computeHash([
|
|
1633
|
+
const currentHash = computeHash([readFileSync7(absolutePath, "utf-8").trimEnd()]);
|
|
1469
1634
|
if (currentHash === file.sourceHash) {
|
|
1470
|
-
|
|
1635
|
+
rmSync2(absolutePath);
|
|
1471
1636
|
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1472
1637
|
}
|
|
1473
1638
|
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
@@ -1487,7 +1652,7 @@ var removeEmptyDirs = (basePath, relativePaths) => {
|
|
|
1487
1652
|
if (!existsSync(absoluteDir)) continue;
|
|
1488
1653
|
try {
|
|
1489
1654
|
if (readdirSync3(absoluteDir).length === 0) {
|
|
1490
|
-
|
|
1655
|
+
rmSync2(absoluteDir, { recursive: true });
|
|
1491
1656
|
}
|
|
1492
1657
|
} catch {
|
|
1493
1658
|
}
|
|
@@ -1501,7 +1666,7 @@ var uninstallProjectLayer = (basePath, manifest) => {
|
|
|
1501
1666
|
);
|
|
1502
1667
|
const stateFiles = [PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH, PROJECT_LAYER_MANIFEST_RELATIVE_PATH];
|
|
1503
1668
|
for (const stateFile of stateFiles) {
|
|
1504
|
-
|
|
1669
|
+
rmSync2(resolveProjectLayerFilePath(basePath, stateFile), { force: true });
|
|
1505
1670
|
}
|
|
1506
1671
|
const result = mergeRemoveResults([...managedResults, ...projectResults, ...packResults]);
|
|
1507
1672
|
removeEmptyDirs(basePath, [...result.deleted, ...stateFiles]);
|
|
@@ -1509,8 +1674,8 @@ var uninstallProjectLayer = (basePath, manifest) => {
|
|
|
1509
1674
|
};
|
|
1510
1675
|
|
|
1511
1676
|
// src/core/pack.ts
|
|
1512
|
-
import { existsSync as existsSync2, mkdirSync as
|
|
1513
|
-
import { dirname as
|
|
1677
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync6, readFileSync as readFileSync8, readdirSync as readdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
1678
|
+
import { dirname as dirname8, isAbsolute as isAbsolute2, join as join12, relative as relative2, resolve as resolve6 } from "path";
|
|
1514
1679
|
var PACK_REGISTRY_FILENAME = "pack-registry.json";
|
|
1515
1680
|
var SPEC_LIFECYCLE_PACK_ID = "spec-lifecycle";
|
|
1516
1681
|
var PACK_INSTALL_ROOT = "docs/specs/";
|
|
@@ -1518,9 +1683,9 @@ var RESERVED_DOCUMENT_WARNINGS2 = [
|
|
|
1518
1683
|
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
1519
1684
|
"Do not use this document as current decision-making evidence"
|
|
1520
1685
|
];
|
|
1521
|
-
var DEFAULT_PACKS_DIR =
|
|
1686
|
+
var DEFAULT_PACKS_DIR = join12(COMPILER_DATA_DIR, "packs");
|
|
1522
1687
|
var includesReservedDocumentWarning2 = (content) => RESERVED_DOCUMENT_WARNINGS2.some((warning) => content.includes(warning));
|
|
1523
|
-
var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(
|
|
1688
|
+
var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync8(join12(packsDir, PACK_REGISTRY_FILENAME), "utf-8")));
|
|
1524
1689
|
var assertPackInstallPath = (path) => {
|
|
1525
1690
|
if (!isSafeProjectLayerPath(path) || !path.startsWith(PACK_INSTALL_ROOT)) {
|
|
1526
1691
|
throw new Error(`Unsafe pack path: ${path}`);
|
|
@@ -1529,16 +1694,16 @@ var assertPackInstallPath = (path) => {
|
|
|
1529
1694
|
var readPackSourceFiles = (packDir) => {
|
|
1530
1695
|
const files = [];
|
|
1531
1696
|
const walk = (relativeDir = "") => {
|
|
1532
|
-
const absoluteDir = relativeDir.length > 0 ?
|
|
1697
|
+
const absoluteDir = relativeDir.length > 0 ? join12(packDir, relativeDir) : packDir;
|
|
1533
1698
|
const entries = readdirSync4(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1534
1699
|
for (const entry of entries) {
|
|
1535
|
-
const nextRelativePath = relativeDir.length > 0 ?
|
|
1700
|
+
const nextRelativePath = relativeDir.length > 0 ? join12(relativeDir, entry.name) : entry.name;
|
|
1536
1701
|
if (entry.isDirectory()) {
|
|
1537
1702
|
walk(nextRelativePath);
|
|
1538
1703
|
continue;
|
|
1539
1704
|
}
|
|
1540
1705
|
assertPackInstallPath(nextRelativePath);
|
|
1541
|
-
const content =
|
|
1706
|
+
const content = readFileSync8(join12(packDir, nextRelativePath), "utf-8");
|
|
1542
1707
|
files.push({
|
|
1543
1708
|
path: nextRelativePath,
|
|
1544
1709
|
content,
|
|
@@ -1596,11 +1761,11 @@ var resolvePackById = (packsDir, packId) => {
|
|
|
1596
1761
|
return pack;
|
|
1597
1762
|
};
|
|
1598
1763
|
var serializePackFileContent = (content) => content.length === 0 ? "" : content.trimEnd() + "\n";
|
|
1599
|
-
var readProjectFileHash = (basePath, relativePath) => computeHash([
|
|
1764
|
+
var readProjectFileHash = (basePath, relativePath) => computeHash([readFileSync8(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
|
|
1600
1765
|
var writePackFile = (basePath, file) => {
|
|
1601
1766
|
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1602
|
-
|
|
1603
|
-
|
|
1767
|
+
mkdirSync6(dirname8(absolutePath), { recursive: true });
|
|
1768
|
+
writeFileSync6(absolutePath, serializePackFileContent(file.content), "utf-8");
|
|
1604
1769
|
};
|
|
1605
1770
|
var buildPackFileRecords = (files) => files.map((file) => ({
|
|
1606
1771
|
path: file.path,
|
|
@@ -1659,7 +1824,7 @@ var applyPackSourceFiles = (params) => {
|
|
|
1659
1824
|
continue;
|
|
1660
1825
|
}
|
|
1661
1826
|
if (readProjectFileHash(params.basePath, previous.path) === previous.sourceHash) {
|
|
1662
|
-
|
|
1827
|
+
rmSync3(absolutePath);
|
|
1663
1828
|
deleted.push(previous.path);
|
|
1664
1829
|
} else {
|
|
1665
1830
|
preserved.push(previous.path);
|
|
@@ -1678,7 +1843,7 @@ var removePackFiles = (basePath, record) => {
|
|
|
1678
1843
|
continue;
|
|
1679
1844
|
}
|
|
1680
1845
|
if (readProjectFileHash(basePath, file.path) === file.sourceHash) {
|
|
1681
|
-
|
|
1846
|
+
rmSync3(absolutePath);
|
|
1682
1847
|
deleted.push(file.path);
|
|
1683
1848
|
} else {
|
|
1684
1849
|
preserved.push(file.path);
|
|
@@ -1687,7 +1852,7 @@ var removePackFiles = (basePath, record) => {
|
|
|
1687
1852
|
return { written: [], refreshed: [], preserved, deleted, notFound };
|
|
1688
1853
|
};
|
|
1689
1854
|
var removeEmptyDirs2 = (basePath, relativePaths) => {
|
|
1690
|
-
const dirs = [...new Set(relativePaths.map((path) =>
|
|
1855
|
+
const dirs = [...new Set(relativePaths.map((path) => dirname8(path)).filter((dir) => dir !== "."))].sort(
|
|
1691
1856
|
(a, b) => b.length - a.length
|
|
1692
1857
|
);
|
|
1693
1858
|
for (const dir of dirs) {
|
|
@@ -1697,7 +1862,7 @@ var removeEmptyDirs2 = (basePath, relativePaths) => {
|
|
|
1697
1862
|
}
|
|
1698
1863
|
try {
|
|
1699
1864
|
if (readdirSync4(absoluteDir).length === 0) {
|
|
1700
|
-
|
|
1865
|
+
rmSync3(absoluteDir, { recursive: true });
|
|
1701
1866
|
}
|
|
1702
1867
|
} catch {
|
|
1703
1868
|
}
|
|
@@ -1817,14 +1982,233 @@ var diffProjectLayerPack = (params) => {
|
|
|
1817
1982
|
};
|
|
1818
1983
|
|
|
1819
1984
|
// src/core/uninstall-plan.ts
|
|
1820
|
-
import { join as
|
|
1985
|
+
import { join as join13 } from "path";
|
|
1821
1986
|
|
|
1822
1987
|
// src/core/context-promotion.ts
|
|
1823
1988
|
import { createHash as createHash2 } from "crypto";
|
|
1824
1989
|
import { execFileSync } from "child_process";
|
|
1825
|
-
import { existsSync as existsSync3, mkdirSync as
|
|
1826
|
-
import { dirname as
|
|
1827
|
-
import { z as
|
|
1990
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync7, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
1991
|
+
import { dirname as dirname9, join as join14, resolve as resolve7 } from "path";
|
|
1992
|
+
import { z as z15 } from "zod";
|
|
1993
|
+
|
|
1994
|
+
// src/core/tool-use-hook.ts
|
|
1995
|
+
import { z as z14 } from "zod";
|
|
1996
|
+
var HookToolInputSchema = z14.object({
|
|
1997
|
+
command: z14.string().optional()
|
|
1998
|
+
}).passthrough();
|
|
1999
|
+
var ToolUseHookInputSchema = z14.object({
|
|
2000
|
+
hook_event_name: z14.string(),
|
|
2001
|
+
cwd: z14.string(),
|
|
2002
|
+
tool_name: z14.string().optional(),
|
|
2003
|
+
tool_input: z14.unknown().optional(),
|
|
2004
|
+
tool_response: z14.unknown().optional()
|
|
2005
|
+
}).passthrough();
|
|
2006
|
+
var SHELL_CONTROL_TOKENS = /* @__PURE__ */ new Set(["&&", "||", ";", "|", "(", ")"]);
|
|
2007
|
+
var SHELL_SCRIPT_FLAGS = /* @__PURE__ */ new Set(["-c", "-lc"]);
|
|
2008
|
+
var GIT_GLOBAL_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
2009
|
+
"-C",
|
|
2010
|
+
"-c",
|
|
2011
|
+
"--git-dir",
|
|
2012
|
+
"--work-tree",
|
|
2013
|
+
"--namespace",
|
|
2014
|
+
"--config-env",
|
|
2015
|
+
"--exec-path"
|
|
2016
|
+
]);
|
|
2017
|
+
var basename = (token) => token.replace(/\\/g, "/").split("/").at(-1) ?? token;
|
|
2018
|
+
var isAssignmentToken = (token) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(token);
|
|
2019
|
+
var tokenizeShellLike = (command) => {
|
|
2020
|
+
const tokens = [];
|
|
2021
|
+
let current = "";
|
|
2022
|
+
let quote = null;
|
|
2023
|
+
const pushCurrent = () => {
|
|
2024
|
+
if (current.length > 0) {
|
|
2025
|
+
tokens.push(current);
|
|
2026
|
+
current = "";
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
2029
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
2030
|
+
const char = command[index];
|
|
2031
|
+
const nextChar = command[index + 1];
|
|
2032
|
+
if (quote) {
|
|
2033
|
+
if (char === quote) {
|
|
2034
|
+
quote = null;
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
current += char;
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
if (char === '"' || char === "'") {
|
|
2041
|
+
quote = char;
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
2044
|
+
if (/\s/.test(char)) {
|
|
2045
|
+
pushCurrent();
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
if (char === "&" && nextChar === "&" || char === "|" && nextChar === "|") {
|
|
2049
|
+
pushCurrent();
|
|
2050
|
+
tokens.push(`${char}${nextChar}`);
|
|
2051
|
+
index += 1;
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
2054
|
+
if (char === ";" || char === "|" || char === "(" || char === ")") {
|
|
2055
|
+
pushCurrent();
|
|
2056
|
+
tokens.push(char);
|
|
2057
|
+
continue;
|
|
2058
|
+
}
|
|
2059
|
+
current += char;
|
|
2060
|
+
}
|
|
2061
|
+
pushCurrent();
|
|
2062
|
+
return tokens;
|
|
2063
|
+
};
|
|
2064
|
+
var splitCommandSegments = (tokens) => {
|
|
2065
|
+
const segments = [];
|
|
2066
|
+
let current = [];
|
|
2067
|
+
for (const token of tokens) {
|
|
2068
|
+
if (SHELL_CONTROL_TOKENS.has(token)) {
|
|
2069
|
+
if (current.length > 0) {
|
|
2070
|
+
segments.push(current);
|
|
2071
|
+
current = [];
|
|
2072
|
+
}
|
|
2073
|
+
continue;
|
|
2074
|
+
}
|
|
2075
|
+
current.push(token);
|
|
2076
|
+
}
|
|
2077
|
+
if (current.length > 0) {
|
|
2078
|
+
segments.push(current);
|
|
2079
|
+
}
|
|
2080
|
+
return segments;
|
|
2081
|
+
};
|
|
2082
|
+
var firstExecutableIndex = (segment) => {
|
|
2083
|
+
let index = 0;
|
|
2084
|
+
while (index < segment.length && isAssignmentToken(segment[index])) {
|
|
2085
|
+
index += 1;
|
|
2086
|
+
}
|
|
2087
|
+
if (segment[index] === "env") {
|
|
2088
|
+
index += 1;
|
|
2089
|
+
while (index < segment.length && isAssignmentToken(segment[index])) {
|
|
2090
|
+
index += 1;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
if (segment[index] === "command" || segment[index] === "sudo") {
|
|
2094
|
+
index += 1;
|
|
2095
|
+
}
|
|
2096
|
+
return index;
|
|
2097
|
+
};
|
|
2098
|
+
var segmentInvokesGitCommit = (segment) => {
|
|
2099
|
+
const executableIndex = firstExecutableIndex(segment);
|
|
2100
|
+
if (executableIndex >= segment.length || basename(segment[executableIndex]) !== "git") {
|
|
2101
|
+
return false;
|
|
2102
|
+
}
|
|
2103
|
+
for (let index = executableIndex + 1; index < segment.length; index += 1) {
|
|
2104
|
+
const token = segment[index];
|
|
2105
|
+
if (GIT_GLOBAL_OPTIONS_WITH_VALUE.has(token)) {
|
|
2106
|
+
index += 1;
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
if (token.startsWith("-")) {
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2112
|
+
return token === "commit";
|
|
2113
|
+
}
|
|
2114
|
+
return false;
|
|
2115
|
+
};
|
|
2116
|
+
var segmentInvokesShellScriptWithGitCommit = (segment) => {
|
|
2117
|
+
const executableIndex = firstExecutableIndex(segment);
|
|
2118
|
+
const executable = segment[executableIndex];
|
|
2119
|
+
if (!executable || !["bash", "sh", "zsh"].includes(basename(executable))) {
|
|
2120
|
+
return false;
|
|
2121
|
+
}
|
|
2122
|
+
for (let index = executableIndex + 1; index < segment.length - 1; index += 1) {
|
|
2123
|
+
if (SHELL_SCRIPT_FLAGS.has(segment[index]) && isGitCommitCommand(segment[index + 1])) {
|
|
2124
|
+
return true;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
return false;
|
|
2128
|
+
};
|
|
2129
|
+
var isGitCommitCommand = (command) => {
|
|
2130
|
+
const segments = splitCommandSegments(tokenizeShellLike(command));
|
|
2131
|
+
return segments.some(
|
|
2132
|
+
(segment) => segmentInvokesGitCommit(segment) || segmentInvokesShellScriptWithGitCommit(segment)
|
|
2133
|
+
);
|
|
2134
|
+
};
|
|
2135
|
+
var isJsonRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2136
|
+
var numberField = (record, keys) => {
|
|
2137
|
+
for (const key of keys) {
|
|
2138
|
+
const value = record[key];
|
|
2139
|
+
if (typeof value === "number") {
|
|
2140
|
+
return value;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
return null;
|
|
2144
|
+
};
|
|
2145
|
+
var booleanField = (record, keys) => {
|
|
2146
|
+
for (const key of keys) {
|
|
2147
|
+
const value = record[key];
|
|
2148
|
+
if (typeof value === "boolean") {
|
|
2149
|
+
return value;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
return null;
|
|
2153
|
+
};
|
|
2154
|
+
var GIT_COMMIT_FAILURE_OUTPUT_PATTERNS = [
|
|
2155
|
+
/(^|\n)\s*fatal:/i,
|
|
2156
|
+
/(^|\n)\s*error:/i,
|
|
2157
|
+
/(^|\n)\s*nothing to commit\b/i,
|
|
2158
|
+
/(^|\n)\s*no changes added to commit\b/i,
|
|
2159
|
+
/(^|\n).*aborting commit\b/i,
|
|
2160
|
+
/(^|\n).*commit failed\b/i,
|
|
2161
|
+
/(^|\n).*failed to .*commit\b/i,
|
|
2162
|
+
/(^|\n).*command failed\b/i,
|
|
2163
|
+
/(^|\n).*non-zero exit\b/i,
|
|
2164
|
+
/(^|\n).*exit (code|status)\s+[1-9]\d*\b/i,
|
|
2165
|
+
/(^|\n).*exited with code\s+[1-9]\d*\b/i,
|
|
2166
|
+
/(^|\n).*hook.*(failed|declined|error|exit(?:ed)? with code|non-zero)/i
|
|
2167
|
+
];
|
|
2168
|
+
var GIT_COMMIT_SUCCESS_OUTPUT_PATTERN = /(^|\n)\[[^\]\n]+ [a-f0-9]{7,40}\]/i;
|
|
2169
|
+
var stringIndicatesGitCommitSuccess = (output) => GIT_COMMIT_SUCCESS_OUTPUT_PATTERN.test(output);
|
|
2170
|
+
var stringIndicatesGitCommitFailureOrSuccess = (output) => stringIndicatesGitCommitSuccess(output) ? false : GIT_COMMIT_FAILURE_OUTPUT_PATTERNS.some((pattern) => pattern.test(output)) ? true : null;
|
|
2171
|
+
var recordStringFieldsIndicateGitCommitFailure = (record) => ["message", "output", "stdout", "stderr", "error", "combinedOutput"].some((key) => {
|
|
2172
|
+
const value = record[key];
|
|
2173
|
+
return typeof value === "string" && stringIndicatesGitCommitFailureOrSuccess(value) === true;
|
|
2174
|
+
});
|
|
2175
|
+
var toolResponseIndicatesFailure = (toolResponse) => {
|
|
2176
|
+
if (typeof toolResponse === "string") {
|
|
2177
|
+
return stringIndicatesGitCommitFailureOrSuccess(toolResponse) === true;
|
|
2178
|
+
}
|
|
2179
|
+
if (!isJsonRecord(toolResponse)) {
|
|
2180
|
+
return false;
|
|
2181
|
+
}
|
|
2182
|
+
const success = booleanField(toolResponse, ["success", "ok"]);
|
|
2183
|
+
if (success === false) {
|
|
2184
|
+
return true;
|
|
2185
|
+
}
|
|
2186
|
+
const exitCode = numberField(toolResponse, ["exit_code", "exitCode", "status", "code"]);
|
|
2187
|
+
if (exitCode !== null && exitCode !== 0) {
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
return recordStringFieldsIndicateGitCommitFailure(toolResponse);
|
|
2191
|
+
};
|
|
2192
|
+
var parseSuccessfulGitCommitPostToolUseHook = (hookInput) => {
|
|
2193
|
+
const parsed = ToolUseHookInputSchema.safeParse(hookInput);
|
|
2194
|
+
if (!parsed.success) {
|
|
2195
|
+
return null;
|
|
2196
|
+
}
|
|
2197
|
+
if (parsed.data.hook_event_name !== "PostToolUse" || parsed.data.tool_name !== "Bash") {
|
|
2198
|
+
return null;
|
|
2199
|
+
}
|
|
2200
|
+
const toolInput = HookToolInputSchema.safeParse(parsed.data.tool_input);
|
|
2201
|
+
const command = toolInput.success ? toolInput.data.command ?? "" : "";
|
|
2202
|
+
if (!isGitCommitCommand(command) || toolResponseIndicatesFailure(parsed.data.tool_response)) {
|
|
2203
|
+
return null;
|
|
2204
|
+
}
|
|
2205
|
+
return {
|
|
2206
|
+
cwd: parsed.data.cwd,
|
|
2207
|
+
command
|
|
2208
|
+
};
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
// src/core/context-promotion.ts
|
|
1828
2212
|
var CONTEXT_PROMOTION_DECISION = {
|
|
1829
2213
|
PROMOTED: "promoted",
|
|
1830
2214
|
NO_PROMOTION: "no-promotion"
|
|
@@ -1834,30 +2218,30 @@ var CONTEXT_PROMOTION_SCOPE = {
|
|
|
1834
2218
|
PROJECT_LOCAL: "project-local",
|
|
1835
2219
|
GLOBAL: "global"
|
|
1836
2220
|
};
|
|
1837
|
-
var ContextPromotionDecisionSchema =
|
|
1838
|
-
|
|
1839
|
-
|
|
2221
|
+
var ContextPromotionDecisionSchema = z15.union([
|
|
2222
|
+
z15.literal(CONTEXT_PROMOTION_DECISION.PROMOTED),
|
|
2223
|
+
z15.literal(CONTEXT_PROMOTION_DECISION.NO_PROMOTION)
|
|
1840
2224
|
]);
|
|
1841
|
-
var ContextPromotionScopeSchema =
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
2225
|
+
var ContextPromotionScopeSchema = z15.union([
|
|
2226
|
+
z15.literal(CONTEXT_PROMOTION_SCOPE.CORE),
|
|
2227
|
+
z15.literal(CONTEXT_PROMOTION_SCOPE.PROJECT_LOCAL),
|
|
2228
|
+
z15.literal(CONTEXT_PROMOTION_SCOPE.GLOBAL)
|
|
1845
2229
|
]);
|
|
1846
|
-
var ContextPromotionReceiptSchema =
|
|
1847
|
-
fingerprint:
|
|
1848
|
-
commitHash:
|
|
2230
|
+
var ContextPromotionReceiptSchema = z15.object({
|
|
2231
|
+
fingerprint: z15.string().regex(/^[a-f0-9]{16}$/),
|
|
2232
|
+
commitHash: z15.string().regex(/^(NO_HEAD|[a-f0-9]{40})$/).optional(),
|
|
1849
2233
|
decision: ContextPromotionDecisionSchema,
|
|
1850
|
-
scopes:
|
|
1851
|
-
targets:
|
|
1852
|
-
summary:
|
|
1853
|
-
resolvedAt:
|
|
2234
|
+
scopes: z15.array(ContextPromotionScopeSchema),
|
|
2235
|
+
targets: z15.array(z15.string().min(1)),
|
|
2236
|
+
summary: z15.string().min(1),
|
|
2237
|
+
resolvedAt: z15.string().min(1)
|
|
1854
2238
|
}).strict();
|
|
1855
|
-
var ContextPromotionReceiptIndexSchema =
|
|
1856
|
-
schemaVersion:
|
|
1857
|
-
kind:
|
|
1858
|
-
projectKey:
|
|
1859
|
-
projectRoot:
|
|
1860
|
-
receipts:
|
|
2239
|
+
var ContextPromotionReceiptIndexSchema = z15.object({
|
|
2240
|
+
schemaVersion: z15.literal(1),
|
|
2241
|
+
kind: z15.literal("context-promotion-receipts"),
|
|
2242
|
+
projectKey: z15.string().regex(/^[a-f0-9]{12}$/),
|
|
2243
|
+
projectRoot: z15.string().min(1),
|
|
2244
|
+
receipts: z15.array(ContextPromotionReceiptSchema)
|
|
1861
2245
|
}).strict();
|
|
1862
2246
|
var RECEIPT_INDEX_FILENAME = "receipts-index.json";
|
|
1863
2247
|
var DEFAULT_PRUNE_MAX = 50;
|
|
@@ -1887,13 +2271,13 @@ var readUntrackedFingerprintParts = (gitRoot) => {
|
|
|
1887
2271
|
const raw = runGit(gitRoot, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
1888
2272
|
const paths = raw.split("\0").filter((path) => path.length > 0).sort((a, b) => a.localeCompare(b));
|
|
1889
2273
|
return paths.map((relativePath) => {
|
|
1890
|
-
const absolutePath =
|
|
2274
|
+
const absolutePath = join14(gitRoot, relativePath);
|
|
1891
2275
|
try {
|
|
1892
2276
|
const stat = statSync(absolutePath);
|
|
1893
2277
|
if (!stat.isFile()) {
|
|
1894
2278
|
return `${relativePath}:non-file`;
|
|
1895
2279
|
}
|
|
1896
|
-
const content =
|
|
2280
|
+
const content = readFileSync9(absolutePath);
|
|
1897
2281
|
return `${relativePath}:${createHash2("sha256").update(content).digest("hex")}`;
|
|
1898
2282
|
} catch {
|
|
1899
2283
|
throw new Error(`Unable to read untracked path for context promotion fingerprint: ${relativePath}`);
|
|
@@ -1907,7 +2291,7 @@ var readTrackedWorkingTreeFingerprintParts = (gitRoot) => {
|
|
|
1907
2291
|
return [
|
|
1908
2292
|
`raw:${rawDiff}`,
|
|
1909
2293
|
...paths.map((relativePath) => {
|
|
1910
|
-
const absolutePath =
|
|
2294
|
+
const absolutePath = join14(gitRoot, relativePath);
|
|
1911
2295
|
if (!existsSync3(absolutePath)) {
|
|
1912
2296
|
return `${relativePath}:deleted`;
|
|
1913
2297
|
}
|
|
@@ -1915,7 +2299,7 @@ var readTrackedWorkingTreeFingerprintParts = (gitRoot) => {
|
|
|
1915
2299
|
if (!stat.isFile()) {
|
|
1916
2300
|
return `${relativePath}:non-file`;
|
|
1917
2301
|
}
|
|
1918
|
-
const content =
|
|
2302
|
+
const content = readFileSync9(absolutePath);
|
|
1919
2303
|
return `${relativePath}:${createHash2("sha256").update(content).digest("hex")}`;
|
|
1920
2304
|
})
|
|
1921
2305
|
];
|
|
@@ -1933,26 +2317,19 @@ var computeContextPromotionFingerprint = (gitRoot) => hashHex(
|
|
|
1933
2317
|
],
|
|
1934
2318
|
16
|
|
1935
2319
|
);
|
|
1936
|
-
var resolveContextPromotionReceiptIndexPath = (params) =>
|
|
1937
|
-
params.userBasePath,
|
|
1938
|
-
".ai-ops",
|
|
1939
|
-
"context-promotion",
|
|
1940
|
-
"projects",
|
|
1941
|
-
params.projectKey,
|
|
1942
|
-
RECEIPT_INDEX_FILENAME
|
|
1943
|
-
);
|
|
2320
|
+
var resolveContextPromotionReceiptIndexPath = (params) => join14(params.userBasePath, ".ai-ops", "context-promotion", "projects", params.projectKey, RECEIPT_INDEX_FILENAME);
|
|
1944
2321
|
var parseContextPromotionReceiptIndex = (json) => ContextPromotionReceiptIndexSchema.parse(JSON.parse(json));
|
|
1945
2322
|
var serializeContextPromotionReceiptIndex = (index) => JSON.stringify(index, null, 2) + "\n";
|
|
1946
2323
|
var readContextPromotionReceiptIndex = (indexPath) => {
|
|
1947
2324
|
try {
|
|
1948
|
-
return parseContextPromotionReceiptIndex(
|
|
2325
|
+
return parseContextPromotionReceiptIndex(readFileSync9(indexPath, "utf-8"));
|
|
1949
2326
|
} catch {
|
|
1950
2327
|
return null;
|
|
1951
2328
|
}
|
|
1952
2329
|
};
|
|
1953
2330
|
var writeContextPromotionReceiptIndex = (indexPath, index) => {
|
|
1954
|
-
|
|
1955
|
-
|
|
2331
|
+
mkdirSync7(dirname9(indexPath), { recursive: true });
|
|
2332
|
+
writeFileSync7(indexPath, serializeContextPromotionReceiptIndex(index), "utf-8");
|
|
1956
2333
|
};
|
|
1957
2334
|
var buildEmptyReceiptIndex = (params) => ({
|
|
1958
2335
|
schemaVersion: 1,
|
|
@@ -2000,7 +2377,7 @@ var pruneContextPromotionReceipts = (params) => {
|
|
|
2000
2377
|
writeContextPromotionReceiptIndex(params.indexPath, nextIndex);
|
|
2001
2378
|
return nextIndex;
|
|
2002
2379
|
};
|
|
2003
|
-
var hasContextPromotionLayer = (gitRoot) => existsSync3(
|
|
2380
|
+
var hasContextPromotionLayer = (gitRoot) => existsSync3(join14(gitRoot, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH));
|
|
2004
2381
|
var getContextPromotionStatus = (params) => {
|
|
2005
2382
|
const cwd = resolve7(params.cwd);
|
|
2006
2383
|
const gitRoot = resolveContextPromotionGitRoot(cwd);
|
|
@@ -2087,143 +2464,6 @@ var resolveContextPromotion = (params) => {
|
|
|
2087
2464
|
});
|
|
2088
2465
|
return getContextPromotionStatus({ cwd: params.cwd, userBasePath: params.userBasePath });
|
|
2089
2466
|
};
|
|
2090
|
-
var HookToolInputSchema = z12.object({
|
|
2091
|
-
command: z12.string().optional()
|
|
2092
|
-
}).passthrough();
|
|
2093
|
-
var ToolUseHookInputSchema = z12.object({
|
|
2094
|
-
hook_event_name: z12.string(),
|
|
2095
|
-
cwd: z12.string(),
|
|
2096
|
-
tool_name: z12.string().optional(),
|
|
2097
|
-
tool_input: z12.unknown().optional(),
|
|
2098
|
-
tool_response: z12.unknown().optional()
|
|
2099
|
-
}).passthrough();
|
|
2100
|
-
var SHELL_CONTROL_TOKENS = /* @__PURE__ */ new Set(["&&", "||", ";", "|", "(", ")"]);
|
|
2101
|
-
var SHELL_SCRIPT_FLAGS = /* @__PURE__ */ new Set(["-c", "-lc"]);
|
|
2102
|
-
var GIT_GLOBAL_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
2103
|
-
"-C",
|
|
2104
|
-
"-c",
|
|
2105
|
-
"--git-dir",
|
|
2106
|
-
"--work-tree",
|
|
2107
|
-
"--namespace",
|
|
2108
|
-
"--config-env",
|
|
2109
|
-
"--exec-path"
|
|
2110
|
-
]);
|
|
2111
|
-
var basename = (token) => token.replace(/\\/g, "/").split("/").at(-1) ?? token;
|
|
2112
|
-
var isAssignmentToken = (token) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(token);
|
|
2113
|
-
var tokenizeShellLike = (command) => {
|
|
2114
|
-
const tokens = [];
|
|
2115
|
-
let current = "";
|
|
2116
|
-
let quote = null;
|
|
2117
|
-
const pushCurrent = () => {
|
|
2118
|
-
if (current.length > 0) {
|
|
2119
|
-
tokens.push(current);
|
|
2120
|
-
current = "";
|
|
2121
|
-
}
|
|
2122
|
-
};
|
|
2123
|
-
for (let index = 0; index < command.length; index += 1) {
|
|
2124
|
-
const char = command[index];
|
|
2125
|
-
const nextChar = command[index + 1];
|
|
2126
|
-
if (quote) {
|
|
2127
|
-
if (char === quote) {
|
|
2128
|
-
quote = null;
|
|
2129
|
-
continue;
|
|
2130
|
-
}
|
|
2131
|
-
current += char;
|
|
2132
|
-
continue;
|
|
2133
|
-
}
|
|
2134
|
-
if (char === '"' || char === "'") {
|
|
2135
|
-
quote = char;
|
|
2136
|
-
continue;
|
|
2137
|
-
}
|
|
2138
|
-
if (/\s/.test(char)) {
|
|
2139
|
-
pushCurrent();
|
|
2140
|
-
continue;
|
|
2141
|
-
}
|
|
2142
|
-
if (char === "&" && nextChar === "&" || char === "|" && nextChar === "|") {
|
|
2143
|
-
pushCurrent();
|
|
2144
|
-
tokens.push(`${char}${nextChar}`);
|
|
2145
|
-
index += 1;
|
|
2146
|
-
continue;
|
|
2147
|
-
}
|
|
2148
|
-
if (char === ";" || char === "|" || char === "(" || char === ")") {
|
|
2149
|
-
pushCurrent();
|
|
2150
|
-
tokens.push(char);
|
|
2151
|
-
continue;
|
|
2152
|
-
}
|
|
2153
|
-
current += char;
|
|
2154
|
-
}
|
|
2155
|
-
pushCurrent();
|
|
2156
|
-
return tokens;
|
|
2157
|
-
};
|
|
2158
|
-
var splitCommandSegments = (tokens) => {
|
|
2159
|
-
const segments = [];
|
|
2160
|
-
let current = [];
|
|
2161
|
-
for (const token of tokens) {
|
|
2162
|
-
if (SHELL_CONTROL_TOKENS.has(token)) {
|
|
2163
|
-
if (current.length > 0) {
|
|
2164
|
-
segments.push(current);
|
|
2165
|
-
current = [];
|
|
2166
|
-
}
|
|
2167
|
-
continue;
|
|
2168
|
-
}
|
|
2169
|
-
current.push(token);
|
|
2170
|
-
}
|
|
2171
|
-
if (current.length > 0) {
|
|
2172
|
-
segments.push(current);
|
|
2173
|
-
}
|
|
2174
|
-
return segments;
|
|
2175
|
-
};
|
|
2176
|
-
var firstExecutableIndex = (segment) => {
|
|
2177
|
-
let index = 0;
|
|
2178
|
-
while (index < segment.length && isAssignmentToken(segment[index])) {
|
|
2179
|
-
index += 1;
|
|
2180
|
-
}
|
|
2181
|
-
if (segment[index] === "env") {
|
|
2182
|
-
index += 1;
|
|
2183
|
-
while (index < segment.length && isAssignmentToken(segment[index])) {
|
|
2184
|
-
index += 1;
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
if (segment[index] === "command" || segment[index] === "sudo") {
|
|
2188
|
-
index += 1;
|
|
2189
|
-
}
|
|
2190
|
-
return index;
|
|
2191
|
-
};
|
|
2192
|
-
var segmentInvokesGitCommit = (segment) => {
|
|
2193
|
-
const executableIndex = firstExecutableIndex(segment);
|
|
2194
|
-
if (executableIndex >= segment.length || basename(segment[executableIndex]) !== "git") {
|
|
2195
|
-
return false;
|
|
2196
|
-
}
|
|
2197
|
-
for (let index = executableIndex + 1; index < segment.length; index += 1) {
|
|
2198
|
-
const token = segment[index];
|
|
2199
|
-
if (GIT_GLOBAL_OPTIONS_WITH_VALUE.has(token)) {
|
|
2200
|
-
index += 1;
|
|
2201
|
-
continue;
|
|
2202
|
-
}
|
|
2203
|
-
if (token.startsWith("-")) {
|
|
2204
|
-
continue;
|
|
2205
|
-
}
|
|
2206
|
-
return token === "commit";
|
|
2207
|
-
}
|
|
2208
|
-
return false;
|
|
2209
|
-
};
|
|
2210
|
-
var segmentInvokesShellScriptWithGitCommit = (segment) => {
|
|
2211
|
-
const executableIndex = firstExecutableIndex(segment);
|
|
2212
|
-
const executable = segment[executableIndex];
|
|
2213
|
-
if (!executable || !["bash", "sh", "zsh"].includes(basename(executable))) {
|
|
2214
|
-
return false;
|
|
2215
|
-
}
|
|
2216
|
-
for (let index = executableIndex + 1; index < segment.length - 1; index += 1) {
|
|
2217
|
-
if (SHELL_SCRIPT_FLAGS.has(segment[index]) && isGitCommitCommand(segment[index + 1])) {
|
|
2218
|
-
return true;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
return false;
|
|
2222
|
-
};
|
|
2223
|
-
var isGitCommitCommand = (command) => {
|
|
2224
|
-
const segments = splitCommandSegments(tokenizeShellLike(command));
|
|
2225
|
-
return segments.some((segment) => segmentInvokesGitCommit(segment) || segmentInvokesShellScriptWithGitCommit(segment));
|
|
2226
|
-
};
|
|
2227
2467
|
var buildContextPromotionReviewPrompt = (status) => {
|
|
2228
2468
|
const projectRoot = status.gitRoot ?? status.cwd;
|
|
2229
2469
|
const cdCommand = `cd ${JSON.stringify(projectRoot)}`;
|
|
@@ -2279,115 +2519,396 @@ var buildPostToolUseOutput = (prompt) => ({
|
|
|
2279
2519
|
additionalContext: prompt
|
|
2280
2520
|
}
|
|
2281
2521
|
});
|
|
2282
|
-
var
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2522
|
+
var evaluateContextPromotionPostToolUseHook = (params) => {
|
|
2523
|
+
const hookInput = parseSuccessfulGitCommitPostToolUseHook(params.hookInput);
|
|
2524
|
+
if (!hookInput) {
|
|
2525
|
+
return null;
|
|
2526
|
+
}
|
|
2527
|
+
let status;
|
|
2528
|
+
try {
|
|
2529
|
+
status = getContextPromotionStatus({
|
|
2530
|
+
cwd: hookInput.cwd,
|
|
2531
|
+
userBasePath: params.userBasePath
|
|
2532
|
+
});
|
|
2533
|
+
} catch (error) {
|
|
2534
|
+
return buildPostToolUseOutput(buildContextPromotionStatusFailurePrompt(hookInput.cwd, error));
|
|
2535
|
+
}
|
|
2536
|
+
if (!status.hasContextLayer || status.receipt) {
|
|
2537
|
+
return null;
|
|
2538
|
+
}
|
|
2539
|
+
return buildPostToolUseOutput(buildContextPromotionReviewPrompt(status));
|
|
2540
|
+
};
|
|
2541
|
+
|
|
2542
|
+
// src/core/pc-integration.ts
|
|
2543
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2544
|
+
import { existsSync as existsSync4, readFileSync as readFileSync10, readdirSync as readdirSync5 } from "fs";
|
|
2545
|
+
import { join as join15, resolve as resolve8, sep } from "path";
|
|
2546
|
+
var normalizePath = (path) => resolve8(path.replace(/^~(?=$|\/)/, process.env.HOME ?? "~"));
|
|
2547
|
+
var pathContains = (parentPath, childPath) => {
|
|
2548
|
+
const parent = normalizePath(parentPath);
|
|
2549
|
+
const child = normalizePath(childPath);
|
|
2550
|
+
return child === parent || child.startsWith(`${parent}${sep}`);
|
|
2551
|
+
};
|
|
2552
|
+
var normalizeFieldValue = (value) => {
|
|
2553
|
+
const trimmed = value?.trim() ?? "";
|
|
2554
|
+
if (trimmed.length === 0 || ["none", "null", "-", "<empty>"].includes(trimmed.toLowerCase())) {
|
|
2555
|
+
return null;
|
|
2556
|
+
}
|
|
2557
|
+
return trimmed.replace(/^`|`$/g, "");
|
|
2558
|
+
};
|
|
2559
|
+
var extractSection = (content, headings) => {
|
|
2560
|
+
const headingPattern = headings.map((heading) => heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
2561
|
+
const match = new RegExp(`^##\\s+(?:${headingPattern})\\s*$`, "mu").exec(content);
|
|
2562
|
+
if (!match) {
|
|
2563
|
+
return "";
|
|
2564
|
+
}
|
|
2565
|
+
const start = match.index + match[0].length;
|
|
2566
|
+
const rest = content.slice(start);
|
|
2567
|
+
const nextHeading = /^##\s+/mu.exec(rest);
|
|
2568
|
+
return nextHeading ? rest.slice(0, nextHeading.index) : rest;
|
|
2569
|
+
};
|
|
2570
|
+
var parseListField = (content, labels) => {
|
|
2571
|
+
for (const label of labels) {
|
|
2572
|
+
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2573
|
+
const match = new RegExp(`^-\\s+${escapedLabel}:\\s*(.+)$`, "mu").exec(content);
|
|
2574
|
+
const value = normalizeFieldValue(match?.[1] ?? null);
|
|
2575
|
+
if (value) {
|
|
2287
2576
|
return value;
|
|
2288
2577
|
}
|
|
2289
2578
|
}
|
|
2290
2579
|
return null;
|
|
2291
2580
|
};
|
|
2292
|
-
var
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2581
|
+
var readTextFileOrNull = (filePath) => {
|
|
2582
|
+
try {
|
|
2583
|
+
return readFileSync10(filePath, "utf-8");
|
|
2584
|
+
} catch {
|
|
2585
|
+
return null;
|
|
2586
|
+
}
|
|
2587
|
+
};
|
|
2588
|
+
var runGit2 = (cwd, args) => execFileSync2("git", [...args], {
|
|
2589
|
+
cwd,
|
|
2590
|
+
encoding: "utf-8",
|
|
2591
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2592
|
+
}).trim();
|
|
2593
|
+
var resolveGitRoot = (cwd) => {
|
|
2594
|
+
try {
|
|
2595
|
+
return runGit2(cwd, ["rev-parse", "--show-toplevel"]);
|
|
2596
|
+
} catch {
|
|
2597
|
+
return null;
|
|
2598
|
+
}
|
|
2599
|
+
};
|
|
2600
|
+
var readGitHead2 = (cwd) => {
|
|
2601
|
+
try {
|
|
2602
|
+
return runGit2(cwd, ["rev-parse", "--verify", "HEAD"]);
|
|
2603
|
+
} catch {
|
|
2604
|
+
return null;
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
var listWorkspaceStatePaths = (contextRoot) => {
|
|
2608
|
+
const workspacesDir = join15(contextRoot, "workspaces");
|
|
2609
|
+
if (!existsSync4(workspacesDir)) {
|
|
2610
|
+
return [];
|
|
2611
|
+
}
|
|
2612
|
+
return readdirSync5(workspacesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join15(workspacesDir, entry.name, "workspace-state.md")).filter((statePath) => existsSync4(statePath)).sort((a, b) => a.localeCompare(b));
|
|
2613
|
+
};
|
|
2614
|
+
var parseWorkspaceCandidate = (statePath) => {
|
|
2615
|
+
const content = readTextFileOrNull(statePath);
|
|
2616
|
+
if (!content) {
|
|
2617
|
+
return null;
|
|
2618
|
+
}
|
|
2619
|
+
const workspaceRoot = parseListField(content, ["\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8", "Workspace Root"]);
|
|
2620
|
+
if (!workspaceRoot) {
|
|
2621
|
+
return null;
|
|
2622
|
+
}
|
|
2623
|
+
const activeSection = extractSection(content, ["\uD65C\uC131 Workstream", "Active Workstream"]);
|
|
2624
|
+
const activeWorkstreamId = parseListField(activeSection, ["ID", "Workstream ID", "Active Workstream"]);
|
|
2625
|
+
const workspaceDir = resolve8(statePath, "..");
|
|
2626
|
+
const id = parseListField(content, ["\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 ID", "Workspace ID"]) ?? workspaceDir.split(sep).at(-1) ?? "unknown";
|
|
2627
|
+
return {
|
|
2628
|
+
id,
|
|
2629
|
+
statePath,
|
|
2630
|
+
workspaceDir,
|
|
2631
|
+
workspaceRoot,
|
|
2632
|
+
activeWorkstreamId
|
|
2633
|
+
};
|
|
2634
|
+
};
|
|
2635
|
+
var findMatchingWorkspace = (params) => {
|
|
2636
|
+
const candidates = listWorkspaceStatePaths(params.contextRoot).map(parseWorkspaceCandidate).filter((candidate) => candidate !== null).filter((candidate) => pathContains(candidate.workspaceRoot, params.cwd)).sort((a, b) => normalizePath(b.workspaceRoot).length - normalizePath(a.workspaceRoot).length);
|
|
2637
|
+
return candidates[0] ?? null;
|
|
2638
|
+
};
|
|
2639
|
+
var parseRepoEntry = (entryPath) => {
|
|
2640
|
+
const content = readTextFileOrNull(entryPath);
|
|
2641
|
+
if (!content) {
|
|
2642
|
+
return null;
|
|
2643
|
+
}
|
|
2644
|
+
const id = parseListField(content, ["\uC5D4\uD2B8\uB9AC ID", "Entry ID"]);
|
|
2645
|
+
if (!id) {
|
|
2646
|
+
return null;
|
|
2647
|
+
}
|
|
2648
|
+
return {
|
|
2649
|
+
id,
|
|
2650
|
+
path: parseListField(content, ["\uACBD\uB85C", "Path"]),
|
|
2651
|
+
gitRoot: parseListField(content, ["Git \uB8E8\uD2B8", "Git Root"])
|
|
2652
|
+
};
|
|
2653
|
+
};
|
|
2654
|
+
var findCurrentEntry = (params) => {
|
|
2655
|
+
const reposDir = join15(params.workspaceDir, "repos");
|
|
2656
|
+
if (!existsSync4(reposDir)) {
|
|
2657
|
+
return null;
|
|
2658
|
+
}
|
|
2659
|
+
const entries = readdirSync5(reposDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => parseRepoEntry(join15(reposDir, entry.name))).filter((entry) => entry !== null).filter((entry) => {
|
|
2660
|
+
const paths = [entry.path, entry.gitRoot].filter((path) => path !== null);
|
|
2661
|
+
return paths.some((path) => pathContains(path, params.cwd));
|
|
2662
|
+
}).sort((a, b) => {
|
|
2663
|
+
const aLength = Math.max(0, ...[a.path, a.gitRoot].map((path) => path ? normalizePath(path).length : 0));
|
|
2664
|
+
const bLength = Math.max(0, ...[b.path, b.gitRoot].map((path) => path ? normalizePath(path).length : 0));
|
|
2665
|
+
return bLength - aLength;
|
|
2666
|
+
});
|
|
2667
|
+
return entries[0] ?? null;
|
|
2668
|
+
};
|
|
2669
|
+
var parseWorkstreamScopeEntryIds = (content) => {
|
|
2670
|
+
const scopeSection = extractSection(content, ["\uBC94\uC704", "Scope"]);
|
|
2671
|
+
if (scopeSection.length === 0) {
|
|
2672
|
+
return [];
|
|
2673
|
+
}
|
|
2674
|
+
const lines = scopeSection.split("\n");
|
|
2675
|
+
const ids = [];
|
|
2676
|
+
let inEntryBlock = false;
|
|
2677
|
+
for (const line of lines) {
|
|
2678
|
+
if (/^-\s*(엔트리|Entries|Entry):/.test(line)) {
|
|
2679
|
+
inEntryBlock = true;
|
|
2680
|
+
const inlineValue = normalizeFieldValue(line.split(":").slice(1).join(":"));
|
|
2681
|
+
if (inlineValue && !inlineValue.includes("<")) {
|
|
2682
|
+
ids.push(...inlineValue.split(",").map((value) => value.trim().replace(/^`|`$/g, "")));
|
|
2683
|
+
}
|
|
2684
|
+
continue;
|
|
2685
|
+
}
|
|
2686
|
+
if (inEntryBlock && /^-\s+\S/.test(line)) {
|
|
2687
|
+
inEntryBlock = false;
|
|
2688
|
+
}
|
|
2689
|
+
if (!inEntryBlock) {
|
|
2690
|
+
continue;
|
|
2691
|
+
}
|
|
2692
|
+
const nestedMatch = /^\s+-\s+`?([a-z0-9]+(?:-[a-z0-9]+)*)`?/.exec(line);
|
|
2693
|
+
if (nestedMatch) {
|
|
2694
|
+
ids.push(nestedMatch[1]);
|
|
2297
2695
|
}
|
|
2298
2696
|
}
|
|
2299
|
-
return
|
|
2697
|
+
return [...new Set(ids.filter((id) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id)))];
|
|
2300
2698
|
};
|
|
2301
|
-
var
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
/(^|\n)\s*no changes added to commit\b/i,
|
|
2306
|
-
/(^|\n).*aborting commit\b/i,
|
|
2307
|
-
/(^|\n).*commit failed\b/i,
|
|
2308
|
-
/(^|\n).*failed to .*commit\b/i,
|
|
2309
|
-
/(^|\n).*command failed\b/i,
|
|
2310
|
-
/(^|\n).*non-zero exit\b/i,
|
|
2311
|
-
/(^|\n).*exit (code|status)\s+[1-9]\d*\b/i,
|
|
2312
|
-
/(^|\n).*exited with code\s+[1-9]\d*\b/i,
|
|
2313
|
-
/(^|\n).*hook.*(failed|declined|error|exit(?:ed)? with code|non-zero)/i
|
|
2314
|
-
];
|
|
2315
|
-
var GIT_COMMIT_SUCCESS_OUTPUT_PATTERN = /(^|\n)\[[^\]\n]+ [a-f0-9]{7,40}\]/i;
|
|
2316
|
-
var stringIndicatesGitCommitSuccess = (output) => GIT_COMMIT_SUCCESS_OUTPUT_PATTERN.test(output);
|
|
2317
|
-
var stringIndicatesGitCommitFailureOrSuccess = (output) => stringIndicatesGitCommitSuccess(output) ? false : GIT_COMMIT_FAILURE_OUTPUT_PATTERNS.some((pattern) => pattern.test(output)) ? true : null;
|
|
2318
|
-
var recordStringFieldsIndicateGitCommitFailure = (record) => ["message", "output", "stdout", "stderr", "error", "combinedOutput"].some((key) => {
|
|
2319
|
-
const value = record[key];
|
|
2320
|
-
return typeof value === "string" && stringIndicatesGitCommitFailureOrSuccess(value) === true;
|
|
2321
|
-
});
|
|
2322
|
-
var toolResponseIndicatesFailure = (toolResponse) => {
|
|
2323
|
-
if (typeof toolResponse === "string") {
|
|
2324
|
-
return stringIndicatesGitCommitFailureOrSuccess(toolResponse) === true;
|
|
2699
|
+
var parseLastConfirmedCommitHash = (params) => {
|
|
2700
|
+
const section = extractSection(params.content, ["\uB9C8\uC9C0\uB9C9 \uD655\uC778 Commit", "Last Confirmed Commit"]);
|
|
2701
|
+
if (section.length === 0) {
|
|
2702
|
+
return null;
|
|
2325
2703
|
}
|
|
2326
|
-
|
|
2327
|
-
|
|
2704
|
+
const escapedEntryId = params.entryId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2705
|
+
const match = new RegExp(`^-\\s+\`?${escapedEntryId}\`?:\\s*([a-f0-9]{40})\\b`, "imu").exec(section);
|
|
2706
|
+
return match?.[1] ?? null;
|
|
2707
|
+
};
|
|
2708
|
+
var getPcHandoffStatus = (params) => {
|
|
2709
|
+
const cwd = normalizePath(params.cwd);
|
|
2710
|
+
const contextRoot = normalizePath(params.contextRoot);
|
|
2711
|
+
if (!existsSync4(contextRoot)) {
|
|
2712
|
+
return {
|
|
2713
|
+
cwd,
|
|
2714
|
+
contextRoot,
|
|
2715
|
+
workspaceId: null,
|
|
2716
|
+
workspaceRoot: null,
|
|
2717
|
+
activeWorkstreamId: null,
|
|
2718
|
+
activeWorkstreamPath: null,
|
|
2719
|
+
currentEntryId: null,
|
|
2720
|
+
lastConfirmedCommitHash: null,
|
|
2721
|
+
ready: false,
|
|
2722
|
+
skipReason: "pc context root not found"
|
|
2723
|
+
};
|
|
2328
2724
|
}
|
|
2329
|
-
const
|
|
2330
|
-
if (
|
|
2331
|
-
return
|
|
2725
|
+
const workspace = findMatchingWorkspace({ cwd, contextRoot });
|
|
2726
|
+
if (!workspace) {
|
|
2727
|
+
return {
|
|
2728
|
+
cwd,
|
|
2729
|
+
contextRoot,
|
|
2730
|
+
workspaceId: null,
|
|
2731
|
+
workspaceRoot: null,
|
|
2732
|
+
activeWorkstreamId: null,
|
|
2733
|
+
activeWorkstreamPath: null,
|
|
2734
|
+
currentEntryId: null,
|
|
2735
|
+
lastConfirmedCommitHash: null,
|
|
2736
|
+
ready: false,
|
|
2737
|
+
skipReason: "matching pc workspace not found"
|
|
2738
|
+
};
|
|
2332
2739
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2740
|
+
if (!workspace.activeWorkstreamId) {
|
|
2741
|
+
return {
|
|
2742
|
+
cwd,
|
|
2743
|
+
contextRoot,
|
|
2744
|
+
workspaceId: workspace.id,
|
|
2745
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2746
|
+
activeWorkstreamId: null,
|
|
2747
|
+
activeWorkstreamPath: null,
|
|
2748
|
+
currentEntryId: null,
|
|
2749
|
+
lastConfirmedCommitHash: null,
|
|
2750
|
+
ready: false,
|
|
2751
|
+
skipReason: "active pc workstream not selected"
|
|
2752
|
+
};
|
|
2336
2753
|
}
|
|
2337
|
-
|
|
2754
|
+
const activeWorkstreamPath = join15(workspace.workspaceDir, "workstreams", `${workspace.activeWorkstreamId}.md`);
|
|
2755
|
+
const activeWorkstreamContent = readTextFileOrNull(activeWorkstreamPath);
|
|
2756
|
+
if (!activeWorkstreamContent) {
|
|
2757
|
+
return {
|
|
2758
|
+
cwd,
|
|
2759
|
+
contextRoot,
|
|
2760
|
+
workspaceId: workspace.id,
|
|
2761
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2762
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2763
|
+
activeWorkstreamPath,
|
|
2764
|
+
currentEntryId: null,
|
|
2765
|
+
lastConfirmedCommitHash: null,
|
|
2766
|
+
ready: false,
|
|
2767
|
+
skipReason: "active pc workstream file not found"
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
const currentEntry = findCurrentEntry({ cwd, workspaceDir: workspace.workspaceDir });
|
|
2771
|
+
if (!currentEntry) {
|
|
2772
|
+
return {
|
|
2773
|
+
cwd,
|
|
2774
|
+
contextRoot,
|
|
2775
|
+
workspaceId: workspace.id,
|
|
2776
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2777
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2778
|
+
activeWorkstreamPath,
|
|
2779
|
+
currentEntryId: null,
|
|
2780
|
+
lastConfirmedCommitHash: null,
|
|
2781
|
+
ready: false,
|
|
2782
|
+
skipReason: "current repo is not registered in pc workspace"
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
const lastConfirmedCommitHash = parseLastConfirmedCommitHash({
|
|
2786
|
+
content: activeWorkstreamContent,
|
|
2787
|
+
entryId: currentEntry.id
|
|
2788
|
+
});
|
|
2789
|
+
const scopeEntryIds = parseWorkstreamScopeEntryIds(activeWorkstreamContent);
|
|
2790
|
+
if (scopeEntryIds.length > 0 && !scopeEntryIds.includes(currentEntry.id)) {
|
|
2791
|
+
return {
|
|
2792
|
+
cwd,
|
|
2793
|
+
contextRoot,
|
|
2794
|
+
workspaceId: workspace.id,
|
|
2795
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2796
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2797
|
+
activeWorkstreamPath,
|
|
2798
|
+
currentEntryId: currentEntry.id,
|
|
2799
|
+
lastConfirmedCommitHash,
|
|
2800
|
+
ready: false,
|
|
2801
|
+
skipReason: "current repo is outside the active pc workstream scope"
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
return {
|
|
2805
|
+
cwd,
|
|
2806
|
+
contextRoot,
|
|
2807
|
+
workspaceId: workspace.id,
|
|
2808
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2809
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2810
|
+
activeWorkstreamPath,
|
|
2811
|
+
currentEntryId: currentEntry.id,
|
|
2812
|
+
lastConfirmedCommitHash,
|
|
2813
|
+
ready: true,
|
|
2814
|
+
skipReason: null
|
|
2815
|
+
};
|
|
2338
2816
|
};
|
|
2339
|
-
var
|
|
2340
|
-
|
|
2341
|
-
|
|
2817
|
+
var buildPcDonePrompt = (params) => [
|
|
2818
|
+
"A successful git commit just created a new HEAD commit.",
|
|
2819
|
+
"",
|
|
2820
|
+
"Run `$pc:done` now to record the handoff for the active personal project context.",
|
|
2821
|
+
"",
|
|
2822
|
+
"Important guardrails:",
|
|
2823
|
+
"- Do not create or initialize a new pc context from this hook.",
|
|
2824
|
+
"- If `$pc:done` cannot match the prepared workspace, active workstream, or current repo scope, skip and briefly say why.",
|
|
2825
|
+
"- If the active workstream already records this HEAD as the last confirmed commit, skip without writing another handoff.",
|
|
2826
|
+
"- Do not modify the product repo for this hook; `$pc:done` may only update `~/.personal-project-contexts/` and commit that context repo.",
|
|
2827
|
+
"- Use the just-created HEAD commit as the newest evidence for completed work and the next first action.",
|
|
2828
|
+
"",
|
|
2829
|
+
`Project git root: ${params.gitRoot}`,
|
|
2830
|
+
`HEAD: ${params.head}`,
|
|
2831
|
+
`pc context root: ${params.status.contextRoot}`,
|
|
2832
|
+
`pc workspace: ${params.status.workspaceId ?? "unknown"} (${params.status.workspaceRoot ?? "unknown"})`,
|
|
2833
|
+
`active workstream: ${params.status.activeWorkstreamId ?? "unknown"}`,
|
|
2834
|
+
`current entry: ${params.status.currentEntryId ?? "unknown"}`,
|
|
2835
|
+
`last confirmed commit: ${params.status.lastConfirmedCommitHash ?? "none"}`
|
|
2836
|
+
].join("\n");
|
|
2837
|
+
var buildPostToolUseOutput2 = (prompt) => ({
|
|
2838
|
+
decision: "block",
|
|
2839
|
+
reason: prompt,
|
|
2840
|
+
hookSpecificOutput: {
|
|
2841
|
+
hookEventName: "PostToolUse",
|
|
2842
|
+
additionalContext: prompt
|
|
2843
|
+
}
|
|
2844
|
+
});
|
|
2845
|
+
var evaluatePcPostToolUseHook = (params) => {
|
|
2846
|
+
const gitCommitHook = parseSuccessfulGitCommitPostToolUseHook(params.hookInput);
|
|
2847
|
+
if (!gitCommitHook) {
|
|
2342
2848
|
return null;
|
|
2343
2849
|
}
|
|
2344
|
-
|
|
2850
|
+
const gitRoot = resolveGitRoot(gitCommitHook.cwd);
|
|
2851
|
+
if (!gitRoot) {
|
|
2345
2852
|
return null;
|
|
2346
2853
|
}
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
if (!isGitCommitCommand(command) || toolResponseIndicatesFailure(hookInput.data.tool_response)) {
|
|
2854
|
+
const head = readGitHead2(gitRoot);
|
|
2855
|
+
if (!head) {
|
|
2350
2856
|
return null;
|
|
2351
2857
|
}
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
} catch (error) {
|
|
2359
|
-
return buildPostToolUseOutput(buildContextPromotionStatusFailurePrompt(hookInput.data.cwd, error));
|
|
2858
|
+
const status = getPcHandoffStatus({
|
|
2859
|
+
cwd: gitCommitHook.cwd,
|
|
2860
|
+
contextRoot: params.contextRoot
|
|
2861
|
+
});
|
|
2862
|
+
if (!status.ready) {
|
|
2863
|
+
return null;
|
|
2360
2864
|
}
|
|
2361
|
-
if (
|
|
2865
|
+
if (status.lastConfirmedCommitHash === head) {
|
|
2362
2866
|
return null;
|
|
2363
2867
|
}
|
|
2364
|
-
return
|
|
2868
|
+
return buildPostToolUseOutput2(buildPcDonePrompt({ status, head, gitRoot }));
|
|
2365
2869
|
};
|
|
2366
2870
|
|
|
2367
2871
|
// src/core/codex-hook.ts
|
|
2368
|
-
import { existsSync as
|
|
2369
|
-
import { dirname as
|
|
2872
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
2873
|
+
import { dirname as dirname10, join as join16 } from "path";
|
|
2370
2874
|
var CONTEXT_PROMOTION_HOOK_ID = "context-promotion";
|
|
2371
2875
|
var CONTEXT_PROMOTION_HOOK_COMMAND_MARKER = "context-promotion hook post-tool-use";
|
|
2372
2876
|
var CONTEXT_PROMOTION_LEGACY_HOOK_COMMAND_MARKER = "context-promotion hook pre-tool-use";
|
|
2373
2877
|
var CONTEXT_PROMOTION_DEFAULT_HOOK_COMMAND = `ai-ops ${CONTEXT_PROMOTION_HOOK_COMMAND_MARKER}`;
|
|
2878
|
+
var PC_HOOK_ID = "pc";
|
|
2879
|
+
var PC_HOOK_COMMAND_MARKER = "integration hook post-tool-use pc";
|
|
2880
|
+
var PC_DEFAULT_HOOK_COMMAND = `ai-ops ${PC_HOOK_COMMAND_MARKER}`;
|
|
2374
2881
|
var PRE_TOOL_USE_EVENT = "PreToolUse";
|
|
2375
2882
|
var POST_TOOL_USE_EVENT = "PostToolUse";
|
|
2376
2883
|
var BASH_MATCHER = "^Bash$";
|
|
2884
|
+
var CONTEXT_PROMOTION_CODEX_HOOK = {
|
|
2885
|
+
id: CONTEXT_PROMOTION_HOOK_ID,
|
|
2886
|
+
commandMarker: CONTEXT_PROMOTION_HOOK_COMMAND_MARKER,
|
|
2887
|
+
legacyCommandMarkers: [CONTEXT_PROMOTION_LEGACY_HOOK_COMMAND_MARKER],
|
|
2888
|
+
defaultCommand: CONTEXT_PROMOTION_DEFAULT_HOOK_COMMAND,
|
|
2889
|
+
statusMessage: "Checking context promotion review"
|
|
2890
|
+
};
|
|
2891
|
+
var PC_CODEX_HOOK = {
|
|
2892
|
+
id: PC_HOOK_ID,
|
|
2893
|
+
commandMarker: PC_HOOK_COMMAND_MARKER,
|
|
2894
|
+
legacyCommandMarkers: [],
|
|
2895
|
+
defaultCommand: PC_DEFAULT_HOOK_COMMAND,
|
|
2896
|
+
statusMessage: "Checking pc handoff"
|
|
2897
|
+
};
|
|
2377
2898
|
var isJsonRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2378
2899
|
var readJsonRecord = (filePath) => {
|
|
2379
|
-
if (!
|
|
2900
|
+
if (!existsSync5(filePath)) {
|
|
2380
2901
|
return {};
|
|
2381
2902
|
}
|
|
2382
|
-
const parsed = JSON.parse(
|
|
2903
|
+
const parsed = JSON.parse(readFileSync11(filePath, "utf-8"));
|
|
2383
2904
|
if (!isJsonRecord2(parsed)) {
|
|
2384
2905
|
throw new Error("hooks.json must contain a JSON object");
|
|
2385
2906
|
}
|
|
2386
2907
|
return parsed;
|
|
2387
2908
|
};
|
|
2388
2909
|
var writeJsonRecord = (filePath, value) => {
|
|
2389
|
-
|
|
2390
|
-
|
|
2910
|
+
mkdirSync8(dirname10(filePath), { recursive: true });
|
|
2911
|
+
writeFileSync8(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
2391
2912
|
};
|
|
2392
2913
|
var getOrCreateRecord = (record, key) => {
|
|
2393
2914
|
const existing = record[key];
|
|
@@ -2402,34 +2923,34 @@ var getArray = (record, key) => {
|
|
|
2402
2923
|
const existing = record[key];
|
|
2403
2924
|
return Array.isArray(existing) ? existing : [];
|
|
2404
2925
|
};
|
|
2405
|
-
var
|
|
2926
|
+
var handlerMatchesDefinition = (definition) => (handler) => isJsonRecord2(handler) && typeof handler.command === "string" && [definition.commandMarker, ...definition.legacyCommandMarkers].some((marker) => handler.command.includes(marker));
|
|
2406
2927
|
var handlerMatchesCommand = (handler, command) => isJsonRecord2(handler) && handler.command === command;
|
|
2407
|
-
var
|
|
2408
|
-
var
|
|
2409
|
-
var
|
|
2928
|
+
var groupHasDefinitionHook = (definition) => (group) => isJsonRecord2(group) && getArray(group, "hooks").some(handlerMatchesDefinition(definition));
|
|
2929
|
+
var groupHasCurrentDefinitionHook = (group, command) => isJsonRecord2(group) && getArray(group, "hooks").some((handler) => handlerMatchesCommand(handler, command));
|
|
2930
|
+
var countDefinitionHandlers = (groups, definition) => groups.reduce((count, group) => {
|
|
2410
2931
|
if (!isJsonRecord2(group)) {
|
|
2411
2932
|
return count;
|
|
2412
2933
|
}
|
|
2413
|
-
return count + getArray(group, "hooks").filter(
|
|
2934
|
+
return count + getArray(group, "hooks").filter(handlerMatchesDefinition(definition)).length;
|
|
2414
2935
|
}, 0);
|
|
2415
|
-
var
|
|
2936
|
+
var configHasDefinitionHook = (config, definition) => {
|
|
2416
2937
|
const hooks = config.hooks;
|
|
2417
2938
|
if (!isJsonRecord2(hooks)) {
|
|
2418
2939
|
return false;
|
|
2419
2940
|
}
|
|
2420
|
-
return getArray(hooks, POST_TOOL_USE_EVENT).some(
|
|
2941
|
+
return getArray(hooks, POST_TOOL_USE_EVENT).some(groupHasDefinitionHook(definition));
|
|
2421
2942
|
};
|
|
2422
|
-
var
|
|
2943
|
+
var configHasOnlyCurrentDefinitionHook = (config, definition, command) => {
|
|
2423
2944
|
const hooks = config.hooks;
|
|
2424
2945
|
if (!isJsonRecord2(hooks)) {
|
|
2425
2946
|
return false;
|
|
2426
2947
|
}
|
|
2427
|
-
const hasLegacy = getArray(hooks, PRE_TOOL_USE_EVENT).some(
|
|
2948
|
+
const hasLegacy = getArray(hooks, PRE_TOOL_USE_EVENT).some(groupHasDefinitionHook(definition));
|
|
2428
2949
|
const postGroups = getArray(hooks, POST_TOOL_USE_EVENT);
|
|
2429
|
-
const hasCurrent = postGroups.some((group) =>
|
|
2430
|
-
return hasCurrent && !hasLegacy &&
|
|
2950
|
+
const hasCurrent = postGroups.some((group) => groupHasCurrentDefinitionHook(group, command));
|
|
2951
|
+
return hasCurrent && !hasLegacy && countDefinitionHandlers(postGroups, definition) === 1;
|
|
2431
2952
|
};
|
|
2432
|
-
var
|
|
2953
|
+
var removeDefinitionHooksFromEvent = (hooks, eventName, definition) => {
|
|
2433
2954
|
const previousGroups = getArray(hooks, eventName);
|
|
2434
2955
|
let removed = false;
|
|
2435
2956
|
const nextGroups = previousGroups.map((group) => {
|
|
@@ -2438,7 +2959,7 @@ var removeContextPromotionHooksFromEvent = (hooks, eventName) => {
|
|
|
2438
2959
|
}
|
|
2439
2960
|
const previousHandlers = getArray(group, "hooks");
|
|
2440
2961
|
const nextHandlers = previousHandlers.filter((handler) => {
|
|
2441
|
-
const matches =
|
|
2962
|
+
const matches = handlerMatchesDefinition(definition)(handler);
|
|
2442
2963
|
if (matches) {
|
|
2443
2964
|
removed = true;
|
|
2444
2965
|
}
|
|
@@ -2462,21 +2983,29 @@ var removeContextPromotionHooksFromEvent = (hooks, eventName) => {
|
|
|
2462
2983
|
}
|
|
2463
2984
|
return true;
|
|
2464
2985
|
};
|
|
2465
|
-
var resolveCodexHooksPath = (codexHomePath) =>
|
|
2466
|
-
var
|
|
2467
|
-
const command = overrideCommand?.trim() ??
|
|
2468
|
-
if (!command.includes(
|
|
2469
|
-
throw new Error(
|
|
2986
|
+
var resolveCodexHooksPath = (codexHomePath) => join16(codexHomePath, "hooks.json");
|
|
2987
|
+
var buildCodexHookCommand = (params) => {
|
|
2988
|
+
const command = params.overrideCommand?.trim() ?? params.definition.defaultCommand;
|
|
2989
|
+
if (!command.includes(params.definition.commandMarker)) {
|
|
2990
|
+
throw new Error(`${params.definition.id} hook command must include: ${params.definition.commandMarker}`);
|
|
2470
2991
|
}
|
|
2471
2992
|
return command;
|
|
2472
2993
|
};
|
|
2994
|
+
var buildContextPromotionHookCommand = (overrideCommand) => buildCodexHookCommand({
|
|
2995
|
+
definition: CONTEXT_PROMOTION_CODEX_HOOK,
|
|
2996
|
+
overrideCommand
|
|
2997
|
+
});
|
|
2998
|
+
var inspectCodexHook = (params) => ({
|
|
2999
|
+
hooksPath: params.hooksPath,
|
|
3000
|
+
installed: configHasDefinitionHook(readJsonRecord(params.hooksPath), params.definition)
|
|
3001
|
+
});
|
|
2473
3002
|
var inspectContextPromotionHook = (hooksPath) => ({
|
|
2474
3003
|
hooksPath,
|
|
2475
|
-
installed:
|
|
3004
|
+
installed: inspectCodexHook({ hooksPath, definition: CONTEXT_PROMOTION_CODEX_HOOK }).installed
|
|
2476
3005
|
});
|
|
2477
|
-
var
|
|
3006
|
+
var installCodexHook = (params) => {
|
|
2478
3007
|
const config = readJsonRecord(params.hooksPath);
|
|
2479
|
-
if (
|
|
3008
|
+
if (configHasOnlyCurrentDefinitionHook(config, params.definition, params.command)) {
|
|
2480
3009
|
return {
|
|
2481
3010
|
hooksPath: params.hooksPath,
|
|
2482
3011
|
installed: true,
|
|
@@ -2484,8 +3013,8 @@ var installContextPromotionHook = (params) => {
|
|
|
2484
3013
|
};
|
|
2485
3014
|
}
|
|
2486
3015
|
const hooks = getOrCreateRecord(config, "hooks");
|
|
2487
|
-
|
|
2488
|
-
|
|
3016
|
+
removeDefinitionHooksFromEvent(hooks, PRE_TOOL_USE_EVENT, params.definition);
|
|
3017
|
+
removeDefinitionHooksFromEvent(hooks, POST_TOOL_USE_EVENT, params.definition);
|
|
2489
3018
|
const existingGroups = getArray(hooks, POST_TOOL_USE_EVENT);
|
|
2490
3019
|
const nextGroup = {
|
|
2491
3020
|
matcher: BASH_MATCHER,
|
|
@@ -2494,7 +3023,7 @@ var installContextPromotionHook = (params) => {
|
|
|
2494
3023
|
type: "command",
|
|
2495
3024
|
command: params.command,
|
|
2496
3025
|
timeout: 30,
|
|
2497
|
-
statusMessage:
|
|
3026
|
+
statusMessage: params.definition.statusMessage
|
|
2498
3027
|
}
|
|
2499
3028
|
]
|
|
2500
3029
|
};
|
|
@@ -2506,32 +3035,42 @@ var installContextPromotionHook = (params) => {
|
|
|
2506
3035
|
changed: true
|
|
2507
3036
|
};
|
|
2508
3037
|
};
|
|
2509
|
-
var
|
|
2510
|
-
|
|
3038
|
+
var installContextPromotionHook = (params) => installCodexHook({
|
|
3039
|
+
hooksPath: params.hooksPath,
|
|
3040
|
+
definition: CONTEXT_PROMOTION_CODEX_HOOK,
|
|
3041
|
+
command: params.command
|
|
3042
|
+
});
|
|
3043
|
+
var uninstallCodexHook = (params) => {
|
|
3044
|
+
const config = readJsonRecord(params.hooksPath);
|
|
2511
3045
|
const hooks = config.hooks;
|
|
2512
3046
|
if (!isJsonRecord2(hooks)) {
|
|
2513
|
-
return { hooksPath, removed: false, changed: false };
|
|
3047
|
+
return { hooksPath: params.hooksPath, removed: false, changed: false };
|
|
2514
3048
|
}
|
|
2515
|
-
const removedLegacy =
|
|
2516
|
-
const removedCurrent =
|
|
3049
|
+
const removedLegacy = removeDefinitionHooksFromEvent(hooks, PRE_TOOL_USE_EVENT, params.definition);
|
|
3050
|
+
const removedCurrent = removeDefinitionHooksFromEvent(hooks, POST_TOOL_USE_EVENT, params.definition);
|
|
2517
3051
|
const removed = removedLegacy || removedCurrent;
|
|
2518
3052
|
if (!removed) {
|
|
2519
|
-
return { hooksPath, removed: false, changed: false };
|
|
3053
|
+
return { hooksPath: params.hooksPath, removed: false, changed: false };
|
|
2520
3054
|
}
|
|
2521
|
-
writeJsonRecord(hooksPath, config);
|
|
2522
|
-
return { hooksPath, removed: true, changed: true };
|
|
3055
|
+
writeJsonRecord(params.hooksPath, config);
|
|
3056
|
+
return { hooksPath: params.hooksPath, removed: true, changed: true };
|
|
2523
3057
|
};
|
|
3058
|
+
var uninstallContextPromotionHook = (hooksPath) => uninstallCodexHook({
|
|
3059
|
+
hooksPath,
|
|
3060
|
+
definition: CONTEXT_PROMOTION_CODEX_HOOK
|
|
3061
|
+
});
|
|
2524
3062
|
|
|
2525
3063
|
// src/lib/paths.ts
|
|
2526
|
-
import { join as
|
|
2527
|
-
var resolveSkillsDir = () =>
|
|
2528
|
-
var resolveSubagentsDir = () =>
|
|
2529
|
-
var resolvePacksDir = () =>
|
|
3064
|
+
import { join as join17 } from "path";
|
|
3065
|
+
var resolveSkillsDir = () => join17(COMPILER_DATA_DIR, "skills");
|
|
3066
|
+
var resolveSubagentsDir = () => join17(COMPILER_DATA_DIR, "subagents");
|
|
3067
|
+
var resolvePacksDir = () => join17(COMPILER_DATA_DIR, "packs");
|
|
3068
|
+
var resolveIntegrationsDir = () => join17(COMPILER_DATA_DIR, "integrations");
|
|
2530
3069
|
var resolveBasePath = () => process.cwd();
|
|
2531
3070
|
var resolveUserBasePath = () => {
|
|
2532
3071
|
const userBasePath = process.env.AI_OPS_HOME ?? process.env.HOME;
|
|
2533
3072
|
if (!userBasePath) {
|
|
2534
|
-
throw new Error("AI_OPS_HOME or HOME is required for global
|
|
3073
|
+
throw new Error("AI_OPS_HOME or HOME is required for user/global component commands");
|
|
2535
3074
|
}
|
|
2536
3075
|
return userBasePath;
|
|
2537
3076
|
};
|
|
@@ -2776,7 +3315,7 @@ ${result.notFound.map((file) => ` ${file}`).join("\n")}`);
|
|
|
2776
3315
|
|
|
2777
3316
|
// src/commands/skill.ts
|
|
2778
3317
|
import * as p7 from "@clack/prompts";
|
|
2779
|
-
import { rmSync as
|
|
3318
|
+
import { rmSync as rmSync5 } from "fs";
|
|
2780
3319
|
|
|
2781
3320
|
// src/lib/skill-state.ts
|
|
2782
3321
|
var resolveRequestedTools = (params) => {
|
|
@@ -2810,19 +3349,19 @@ var findInstalledSkill = (installedSkills, skillId) => {
|
|
|
2810
3349
|
};
|
|
2811
3350
|
|
|
2812
3351
|
// src/lib/skill-install.ts
|
|
2813
|
-
import { existsSync as
|
|
2814
|
-
import { dirname as
|
|
3352
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync9, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
3353
|
+
import { dirname as dirname11, resolve as resolve9 } from "path";
|
|
2815
3354
|
var installSkillPackages = (basePath, packages) => {
|
|
2816
3355
|
const writtenRoots = [];
|
|
2817
3356
|
for (const skillPackage of packages) {
|
|
2818
|
-
const absRoot =
|
|
2819
|
-
if (
|
|
2820
|
-
|
|
3357
|
+
const absRoot = resolve9(basePath, skillPackage.rootDir);
|
|
3358
|
+
if (existsSync6(absRoot)) {
|
|
3359
|
+
rmSync4(absRoot, { recursive: true, force: true });
|
|
2821
3360
|
}
|
|
2822
3361
|
for (const file of skillPackage.files) {
|
|
2823
|
-
const absPath =
|
|
2824
|
-
|
|
2825
|
-
|
|
3362
|
+
const absPath = resolve9(basePath, file.relativePath);
|
|
3363
|
+
mkdirSync9(dirname11(absPath), { recursive: true });
|
|
3364
|
+
writeFileSync9(absPath, file.content + "\n", "utf-8");
|
|
2826
3365
|
}
|
|
2827
3366
|
writtenRoots.push(skillPackage.rootDir);
|
|
2828
3367
|
}
|
|
@@ -2831,9 +3370,9 @@ var installSkillPackages = (basePath, packages) => {
|
|
|
2831
3370
|
var removeDirectories = (basePath, relativeDirs) => {
|
|
2832
3371
|
const removed = [];
|
|
2833
3372
|
for (const relativeDir of relativeDirs) {
|
|
2834
|
-
const absPath =
|
|
2835
|
-
if (!
|
|
2836
|
-
|
|
3373
|
+
const absPath = resolve9(basePath, relativeDir);
|
|
3374
|
+
if (!existsSync6(absPath)) continue;
|
|
3375
|
+
rmSync4(absPath, { recursive: true, force: true });
|
|
2837
3376
|
removed.push(relativeDir);
|
|
2838
3377
|
}
|
|
2839
3378
|
return removed;
|
|
@@ -2859,7 +3398,7 @@ var writeUserSkillState = (params) => {
|
|
|
2859
3398
|
const previous = readSkillRegistry(registryPath);
|
|
2860
3399
|
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
2861
3400
|
if (skills.length === 0) {
|
|
2862
|
-
|
|
3401
|
+
rmSync5(registryPath, { force: true });
|
|
2863
3402
|
return;
|
|
2864
3403
|
}
|
|
2865
3404
|
writeSkillRegistry(registryPath, {
|
|
@@ -3011,14 +3550,14 @@ var skillUninstallCommand = async (skillId) => {
|
|
|
3011
3550
|
|
|
3012
3551
|
// src/commands/subagent.ts
|
|
3013
3552
|
import * as p8 from "@clack/prompts";
|
|
3014
|
-
import { existsSync as
|
|
3553
|
+
import { existsSync as existsSync8, rmSync as rmSync7 } from "fs";
|
|
3015
3554
|
|
|
3016
3555
|
// src/lib/subagent-install.ts
|
|
3017
|
-
import { existsSync as
|
|
3018
|
-
import { dirname as
|
|
3556
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync10, rmSync as rmSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
3557
|
+
import { dirname as dirname12, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve10 } from "path";
|
|
3019
3558
|
var resolveInsideBasePath = (basePath, relativePath) => {
|
|
3020
|
-
const absBasePath =
|
|
3021
|
-
const absPath =
|
|
3559
|
+
const absBasePath = resolve10(basePath);
|
|
3560
|
+
const absPath = resolve10(absBasePath, relativePath);
|
|
3022
3561
|
const fromBase = relative3(absBasePath, absPath);
|
|
3023
3562
|
if (fromBase.length === 0 || fromBase.startsWith("..") || isAbsolute3(fromBase)) {
|
|
3024
3563
|
throw new Error(`Subagent path escapes AI_OPS_HOME: ${relativePath}`);
|
|
@@ -3030,11 +3569,11 @@ var installSubagentPackages = (basePath, packages) => {
|
|
|
3030
3569
|
for (const subagentPackage of packages) {
|
|
3031
3570
|
for (const file of subagentPackage.files) {
|
|
3032
3571
|
const absPath = resolveInsideBasePath(basePath, file.relativePath);
|
|
3033
|
-
if (
|
|
3034
|
-
|
|
3572
|
+
if (existsSync7(absPath)) {
|
|
3573
|
+
rmSync6(absPath, { recursive: true, force: true });
|
|
3035
3574
|
}
|
|
3036
|
-
|
|
3037
|
-
|
|
3575
|
+
mkdirSync10(dirname12(absPath), { recursive: true });
|
|
3576
|
+
writeFileSync10(absPath, file.content.trimEnd() + "\n", "utf-8");
|
|
3038
3577
|
written.push(file.relativePath);
|
|
3039
3578
|
}
|
|
3040
3579
|
}
|
|
@@ -3044,8 +3583,8 @@ var removeSubagentFiles = (basePath, relativePaths) => {
|
|
|
3044
3583
|
const removed = [];
|
|
3045
3584
|
for (const relativePath of relativePaths) {
|
|
3046
3585
|
const absPath = resolveInsideBasePath(basePath, relativePath);
|
|
3047
|
-
if (!
|
|
3048
|
-
|
|
3586
|
+
if (!existsSync7(absPath)) continue;
|
|
3587
|
+
rmSync6(absPath, { recursive: true, force: true });
|
|
3049
3588
|
removed.push(relativePath);
|
|
3050
3589
|
}
|
|
3051
3590
|
return removed;
|
|
@@ -3095,7 +3634,7 @@ var writeUserSubagentState = (params) => {
|
|
|
3095
3634
|
const previous = readSubagentManifest(manifestPath);
|
|
3096
3635
|
const subagents = params.removeSubagentId ? removeInstalledSubagent(previous?.subagents ?? [], params.removeSubagentId) : params.nextSubagent ? upsertInstalledSubagent(previous?.subagents ?? [], params.nextSubagent) : previous?.subagents ?? [];
|
|
3097
3636
|
if (subagents.length === 0) {
|
|
3098
|
-
|
|
3637
|
+
rmSync7(manifestPath, { force: true });
|
|
3099
3638
|
return;
|
|
3100
3639
|
}
|
|
3101
3640
|
writeSubagentManifest(manifestPath, {
|
|
@@ -3106,7 +3645,7 @@ var writeUserSubagentState = (params) => {
|
|
|
3106
3645
|
};
|
|
3107
3646
|
var readInstalledSubagents = (basePath) => readSubagentManifest(resolveSubagentManifestPath(basePath))?.subagents ?? [];
|
|
3108
3647
|
var warnMissingSkills = (requiredSkills) => {
|
|
3109
|
-
const missing = requiredSkills.filter((skill) => !
|
|
3648
|
+
const missing = requiredSkills.filter((skill) => !existsSync8(skill.path));
|
|
3110
3649
|
if (missing.length === 0) {
|
|
3111
3650
|
return;
|
|
3112
3651
|
}
|
|
@@ -3417,13 +3956,13 @@ var parseMax = (max) => {
|
|
|
3417
3956
|
}
|
|
3418
3957
|
return parsed;
|
|
3419
3958
|
};
|
|
3420
|
-
var readStdin = async () => new Promise((
|
|
3959
|
+
var readStdin = async () => new Promise((resolve11, reject) => {
|
|
3421
3960
|
let raw = "";
|
|
3422
3961
|
process.stdin.setEncoding("utf-8");
|
|
3423
3962
|
process.stdin.on("data", (chunk) => {
|
|
3424
3963
|
raw += chunk;
|
|
3425
3964
|
});
|
|
3426
|
-
process.stdin.on("end", () =>
|
|
3965
|
+
process.stdin.on("end", () => resolve11(raw));
|
|
3427
3966
|
process.stdin.on("error", reject);
|
|
3428
3967
|
});
|
|
3429
3968
|
var reportContextPromotionError = (error) => {
|
|
@@ -3524,8 +4063,8 @@ var contextPromotionPostToolUseHookCommand = async () => {
|
|
|
3524
4063
|
|
|
3525
4064
|
// src/commands/codex-hook.ts
|
|
3526
4065
|
import * as p11 from "@clack/prompts";
|
|
3527
|
-
import { existsSync as
|
|
3528
|
-
import { join as
|
|
4066
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4067
|
+
import { join as join18 } from "path";
|
|
3529
4068
|
var CONTEXT_PROMOTION_REVIEW_SKILL_ID = "context-promotion-review";
|
|
3530
4069
|
var resolveCodexHomePath = () => {
|
|
3531
4070
|
const codexHome = process.env.CODEX_HOME;
|
|
@@ -3563,7 +4102,7 @@ var resolveContextPromotionReviewSkill = () => {
|
|
|
3563
4102
|
};
|
|
3564
4103
|
var hasInstalledContextPromotionReviewSkill = (basePath) => {
|
|
3565
4104
|
const installedSkill = findInstalledSkill(readInstalledSkills2(basePath), CONTEXT_PROMOTION_REVIEW_SKILL_ID);
|
|
3566
|
-
return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true &&
|
|
4105
|
+
return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true && existsSync9(join18(basePath, ".agents/skills/context-promotion-review/SKILL.md"));
|
|
3567
4106
|
};
|
|
3568
4107
|
var ensureContextPromotionReviewSkill = (basePath) => {
|
|
3569
4108
|
const skill = resolveContextPromotionReviewSkill();
|
|
@@ -3577,7 +4116,7 @@ var ensureContextPromotionReviewSkill = (basePath) => {
|
|
|
3577
4116
|
skill,
|
|
3578
4117
|
requestedTools
|
|
3579
4118
|
});
|
|
3580
|
-
const alreadyInstalled = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) &&
|
|
4119
|
+
const alreadyInstalled = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) && existsSync9(join18(basePath, ".agents/skills/context-promotion-review/SKILL.md"));
|
|
3581
4120
|
if (alreadyInstalled) {
|
|
3582
4121
|
return { changed: false, installedSkill };
|
|
3583
4122
|
}
|
|
@@ -3638,9 +4177,389 @@ var codexHookUninstallCommand = async (hookId) => {
|
|
|
3638
4177
|
p11.outro("ai-ops codex-hook uninstall \uC644\uB8CC");
|
|
3639
4178
|
};
|
|
3640
4179
|
|
|
4180
|
+
// src/commands/integration.ts
|
|
4181
|
+
import * as p12 from "@clack/prompts";
|
|
4182
|
+
import { existsSync as existsSync10, rmSync as rmSync8 } from "fs";
|
|
4183
|
+
import { join as join19 } from "path";
|
|
4184
|
+
var CODEX_HOOK_DEFINITIONS = [CONTEXT_PROMOTION_CODEX_HOOK, PC_CODEX_HOOK];
|
|
4185
|
+
var resolveCodexHomePath2 = () => {
|
|
4186
|
+
const codexHome = process.env.CODEX_HOME;
|
|
4187
|
+
if (codexHome && codexHome.length > 0) {
|
|
4188
|
+
return codexHome;
|
|
4189
|
+
}
|
|
4190
|
+
const home = process.env.HOME;
|
|
4191
|
+
if (!home) {
|
|
4192
|
+
throw new Error("CODEX_HOME or HOME is required for Codex hook commands");
|
|
4193
|
+
}
|
|
4194
|
+
return `${home}/.codex`;
|
|
4195
|
+
};
|
|
4196
|
+
var resolvePersonalContextRoot = () => {
|
|
4197
|
+
const home = process.env.HOME;
|
|
4198
|
+
if (!home) {
|
|
4199
|
+
throw new Error("HOME is required for pc integration commands");
|
|
4200
|
+
}
|
|
4201
|
+
return `${home}/.personal-project-contexts`;
|
|
4202
|
+
};
|
|
4203
|
+
var resolveCodexHookDefinition = (hookId) => {
|
|
4204
|
+
const hookDefinition = CODEX_HOOK_DEFINITIONS.find((definition) => definition.id === hookId);
|
|
4205
|
+
if (!hookDefinition) {
|
|
4206
|
+
throw new Error(`Unknown Codex hook for integration: ${hookId}`);
|
|
4207
|
+
}
|
|
4208
|
+
return hookDefinition;
|
|
4209
|
+
};
|
|
4210
|
+
var resolveCatalogSkillComponent = (entry) => {
|
|
4211
|
+
const component = entry.components.find((candidate) => candidate.type === INTEGRATION_COMPONENT_TYPE.SKILL);
|
|
4212
|
+
if (!component || component.type !== INTEGRATION_COMPONENT_TYPE.SKILL) {
|
|
4213
|
+
throw new Error(`Integration catalog entry must declare a skill component: ${entry.id}`);
|
|
4214
|
+
}
|
|
4215
|
+
return component;
|
|
4216
|
+
};
|
|
4217
|
+
var resolveCatalogHookComponent = (entry) => {
|
|
4218
|
+
const component = entry.components.find((candidate) => candidate.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK);
|
|
4219
|
+
if (!component || component.type !== INTEGRATION_COMPONENT_TYPE.CODEX_HOOK) {
|
|
4220
|
+
throw new Error(`Integration catalog entry must declare a codex-hook component: ${entry.id}`);
|
|
4221
|
+
}
|
|
4222
|
+
return component;
|
|
4223
|
+
};
|
|
4224
|
+
var resolveCatalogReceiptConfigComponents = (entry) => entry.components.filter(
|
|
4225
|
+
(component) => component.type === INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG
|
|
4226
|
+
);
|
|
4227
|
+
var loadIntegrationDefinitions = () => loadAllIntegrations(resolveIntegrationsDir()).map((entry) => {
|
|
4228
|
+
const hookComponent = resolveCatalogHookComponent(entry);
|
|
4229
|
+
return {
|
|
4230
|
+
...entry,
|
|
4231
|
+
skillComponent: resolveCatalogSkillComponent(entry),
|
|
4232
|
+
hookComponent,
|
|
4233
|
+
receiptConfigComponents: resolveCatalogReceiptConfigComponents(entry),
|
|
4234
|
+
hookDefinition: resolveCodexHookDefinition(hookComponent.id)
|
|
4235
|
+
};
|
|
4236
|
+
});
|
|
4237
|
+
var parseIntegrationId = (integrationId) => {
|
|
4238
|
+
const definition = loadIntegrationDefinitions().find((candidate) => candidate.id === integrationId);
|
|
4239
|
+
if (!definition) {
|
|
4240
|
+
throw new Error(`Unknown integration: ${integrationId}`);
|
|
4241
|
+
}
|
|
4242
|
+
return definition.id;
|
|
4243
|
+
};
|
|
4244
|
+
var resolveIntegrationDefinition = (integrationId) => {
|
|
4245
|
+
const definition = loadIntegrationDefinitions().find((candidate) => candidate.id === integrationId);
|
|
4246
|
+
if (!definition) {
|
|
4247
|
+
throw new Error(`Unknown integration: ${integrationId}`);
|
|
4248
|
+
}
|
|
4249
|
+
return definition;
|
|
4250
|
+
};
|
|
4251
|
+
var reportIntegrationError = (error) => {
|
|
4252
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
4253
|
+
p12.log.error(message);
|
|
4254
|
+
process.exitCode = 1;
|
|
4255
|
+
};
|
|
4256
|
+
var readStdin2 = async () => new Promise((resolve11, reject) => {
|
|
4257
|
+
let raw = "";
|
|
4258
|
+
process.stdin.setEncoding("utf-8");
|
|
4259
|
+
process.stdin.on("data", (chunk) => {
|
|
4260
|
+
raw += chunk;
|
|
4261
|
+
});
|
|
4262
|
+
process.stdin.on("end", () => resolve11(raw));
|
|
4263
|
+
process.stdin.on("error", reject);
|
|
4264
|
+
});
|
|
4265
|
+
var readInstalledSkills3 = (basePath) => (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
|
|
4266
|
+
...installedSkill,
|
|
4267
|
+
id: resolveCanonicalSkillId(installedSkill.id)
|
|
4268
|
+
}));
|
|
4269
|
+
var resolveSkillById2 = (skillId) => {
|
|
4270
|
+
const skill = loadAllSkills(resolveSkillsDir()).find((candidate) => candidate.id === skillId);
|
|
4271
|
+
if (!skill) {
|
|
4272
|
+
throw new Error(`Unknown skill: ${skillId}`);
|
|
4273
|
+
}
|
|
4274
|
+
return skill;
|
|
4275
|
+
};
|
|
4276
|
+
var hasInstalledCodexSkill = (params) => {
|
|
4277
|
+
const installedSkill = findInstalledSkill(readInstalledSkills3(params.basePath), params.skillId);
|
|
4278
|
+
return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true && existsSync10(join19(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
|
|
4279
|
+
};
|
|
4280
|
+
var writeUserSkillState2 = (params) => {
|
|
4281
|
+
const registryPath = resolveSkillRegistryPath(params.basePath);
|
|
4282
|
+
const previous = readSkillRegistry(registryPath);
|
|
4283
|
+
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
4284
|
+
if (skills.length === 0) {
|
|
4285
|
+
rmSync8(registryPath, { force: true });
|
|
4286
|
+
return;
|
|
4287
|
+
}
|
|
4288
|
+
writeSkillRegistry(registryPath, {
|
|
4289
|
+
skills,
|
|
4290
|
+
cliVersion: params.cliVersion,
|
|
4291
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4292
|
+
});
|
|
4293
|
+
};
|
|
4294
|
+
var ensureSkillComponent = (params) => {
|
|
4295
|
+
const skill = resolveSkillById2(params.skillId);
|
|
4296
|
+
const installedSkills = readInstalledSkills3(params.basePath);
|
|
4297
|
+
const existingInstalledSkill = findInstalledSkill(installedSkills, skill.id);
|
|
4298
|
+
const requestedTools = mergeSkillTools({
|
|
4299
|
+
existing: existingInstalledSkill?.tools,
|
|
4300
|
+
requested: [SKILL_TOOL.CODEX]
|
|
4301
|
+
});
|
|
4302
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
4303
|
+
skill,
|
|
4304
|
+
requestedTools
|
|
4305
|
+
});
|
|
4306
|
+
const alreadyCurrent = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) && existsSync10(join19(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
|
|
4307
|
+
if (alreadyCurrent) {
|
|
4308
|
+
return {
|
|
4309
|
+
type: INTEGRATION_COMPONENT_TYPE.SKILL,
|
|
4310
|
+
id: params.skillId,
|
|
4311
|
+
tools: [SKILL_TOOL.CODEX],
|
|
4312
|
+
owned: params.previouslyOwned
|
|
4313
|
+
};
|
|
4314
|
+
}
|
|
4315
|
+
installSkillPackages(params.basePath, packages);
|
|
4316
|
+
writeUserSkillState2({
|
|
4317
|
+
basePath: params.basePath,
|
|
4318
|
+
cliVersion: params.cliVersion,
|
|
4319
|
+
nextSkill: installedSkill
|
|
4320
|
+
});
|
|
4321
|
+
return {
|
|
4322
|
+
type: INTEGRATION_COMPONENT_TYPE.SKILL,
|
|
4323
|
+
id: params.skillId,
|
|
4324
|
+
tools: [SKILL_TOOL.CODEX],
|
|
4325
|
+
owned: true
|
|
4326
|
+
};
|
|
4327
|
+
};
|
|
4328
|
+
var ensureHookComponent = (params) => {
|
|
4329
|
+
const command = buildCodexHookCommand({
|
|
4330
|
+
definition: params.definition,
|
|
4331
|
+
overrideCommand: params.command
|
|
4332
|
+
});
|
|
4333
|
+
const installedBefore = inspectCodexHook({
|
|
4334
|
+
hooksPath: params.hooksPath,
|
|
4335
|
+
definition: params.definition
|
|
4336
|
+
}).installed;
|
|
4337
|
+
const result = installCodexHook({
|
|
4338
|
+
hooksPath: params.hooksPath,
|
|
4339
|
+
definition: params.definition,
|
|
4340
|
+
command
|
|
4341
|
+
});
|
|
4342
|
+
return {
|
|
4343
|
+
type: INTEGRATION_COMPONENT_TYPE.CODEX_HOOK,
|
|
4344
|
+
id: params.hookId,
|
|
4345
|
+
command,
|
|
4346
|
+
owned: params.previouslyOwned || result.changed || !installedBefore
|
|
4347
|
+
};
|
|
4348
|
+
};
|
|
4349
|
+
var buildReceiptConfigComponents = (components) => components.map((component) => ({
|
|
4350
|
+
type: INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG,
|
|
4351
|
+
id: component.id,
|
|
4352
|
+
storagePath: component.storage_path,
|
|
4353
|
+
owned: false
|
|
4354
|
+
}));
|
|
4355
|
+
var componentWasOwned = (params) => params.previous?.components.some(
|
|
4356
|
+
(component) => component.type === params.type && component.id === params.id && component.owned
|
|
4357
|
+
) ?? false;
|
|
4358
|
+
var buildInstalledIntegration = (params) => {
|
|
4359
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4360
|
+
return {
|
|
4361
|
+
id: params.definition.id,
|
|
4362
|
+
components: [...params.components],
|
|
4363
|
+
installedAt: params.previous?.installedAt ?? now,
|
|
4364
|
+
updatedAt: now
|
|
4365
|
+
};
|
|
4366
|
+
};
|
|
4367
|
+
var removeOwnedSkill = (params) => {
|
|
4368
|
+
const installedSkill = findInstalledSkill(readInstalledSkills3(params.basePath), params.skillId);
|
|
4369
|
+
if (!installedSkill) {
|
|
4370
|
+
return [];
|
|
4371
|
+
}
|
|
4372
|
+
const removed = removeDirectories(params.basePath, installedSkill.installed_paths);
|
|
4373
|
+
writeUserSkillState2({
|
|
4374
|
+
basePath: params.basePath,
|
|
4375
|
+
cliVersion: params.cliVersion,
|
|
4376
|
+
removeSkillId: params.skillId
|
|
4377
|
+
});
|
|
4378
|
+
return removed;
|
|
4379
|
+
};
|
|
4380
|
+
var formatComponentStatus = (component) => {
|
|
4381
|
+
const owner = component.owned ? "owned" : "pre-existing";
|
|
4382
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.SKILL) {
|
|
4383
|
+
return `skill:${component.id} (${owner})`;
|
|
4384
|
+
}
|
|
4385
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK) {
|
|
4386
|
+
return `codex-hook:${component.id} (${owner})`;
|
|
4387
|
+
}
|
|
4388
|
+
return `receipt-config:${component.id} (${owner})`;
|
|
4389
|
+
};
|
|
4390
|
+
var integrationListCommand = async () => {
|
|
4391
|
+
p12.intro("ai-ops integration list");
|
|
4392
|
+
try {
|
|
4393
|
+
const manifest = readIntegrationManifest(resolveIntegrationManifestPath(resolveUserBasePath()));
|
|
4394
|
+
const installed = new Set((manifest?.integrations ?? []).map((integration) => integration.id));
|
|
4395
|
+
const lines = loadIntegrationDefinitions().map((definition) => {
|
|
4396
|
+
const suffix = installed.has(definition.id) ? "installed" : "not installed";
|
|
4397
|
+
return `- ${definition.id} - ${suffix} - ${definition.description}`;
|
|
4398
|
+
});
|
|
4399
|
+
p12.log.info(lines.join("\n"));
|
|
4400
|
+
} catch (error) {
|
|
4401
|
+
reportIntegrationError(error);
|
|
4402
|
+
}
|
|
4403
|
+
p12.outro("ai-ops integration list \uC644\uB8CC");
|
|
4404
|
+
};
|
|
4405
|
+
var integrationInstallCommand = async (integrationId, opts = {}) => {
|
|
4406
|
+
p12.intro(`ai-ops integration install ${integrationId}`);
|
|
4407
|
+
try {
|
|
4408
|
+
const definition = resolveIntegrationDefinition(integrationId);
|
|
4409
|
+
const basePath = resolveUserBasePath();
|
|
4410
|
+
const cliVersion = getCliVersion();
|
|
4411
|
+
const manifestPath = resolveIntegrationManifestPath(basePath);
|
|
4412
|
+
const previous = findInstalledIntegration(readIntegrationManifest(manifestPath)?.integrations ?? [], definition.id);
|
|
4413
|
+
const skillComponent = ensureSkillComponent({
|
|
4414
|
+
basePath,
|
|
4415
|
+
cliVersion,
|
|
4416
|
+
skillId: definition.skillComponent.id,
|
|
4417
|
+
previouslyOwned: componentWasOwned({
|
|
4418
|
+
previous,
|
|
4419
|
+
type: INTEGRATION_COMPONENT_TYPE.SKILL,
|
|
4420
|
+
id: definition.skillComponent.id
|
|
4421
|
+
})
|
|
4422
|
+
});
|
|
4423
|
+
const hookComponent = ensureHookComponent({
|
|
4424
|
+
hooksPath: resolveCodexHooksPath(resolveCodexHomePath2()),
|
|
4425
|
+
hookId: definition.hookComponent.id,
|
|
4426
|
+
definition: definition.hookDefinition,
|
|
4427
|
+
command: opts.command,
|
|
4428
|
+
previouslyOwned: componentWasOwned({
|
|
4429
|
+
previous,
|
|
4430
|
+
type: INTEGRATION_COMPONENT_TYPE.CODEX_HOOK,
|
|
4431
|
+
id: definition.hookComponent.id
|
|
4432
|
+
})
|
|
4433
|
+
});
|
|
4434
|
+
const installedIntegration = buildInstalledIntegration({
|
|
4435
|
+
definition,
|
|
4436
|
+
previous,
|
|
4437
|
+
components: [skillComponent, hookComponent, ...buildReceiptConfigComponents(definition.receiptConfigComponents)]
|
|
4438
|
+
});
|
|
4439
|
+
writeUserIntegrationState({
|
|
4440
|
+
manifestPath,
|
|
4441
|
+
cliVersion,
|
|
4442
|
+
nextIntegration: installedIntegration
|
|
4443
|
+
});
|
|
4444
|
+
p12.log.success(`integration \uC124\uCE58 \uC644\uB8CC: ${definition.id}`);
|
|
4445
|
+
p12.log.info(installedIntegration.components.map(formatComponentStatus).join("\n"));
|
|
4446
|
+
} catch (error) {
|
|
4447
|
+
reportIntegrationError(error);
|
|
4448
|
+
}
|
|
4449
|
+
p12.outro("ai-ops integration install \uC644\uB8CC");
|
|
4450
|
+
};
|
|
4451
|
+
var integrationStatusCommand = async (integrationId) => {
|
|
4452
|
+
p12.intro(`ai-ops integration status ${integrationId}`);
|
|
4453
|
+
try {
|
|
4454
|
+
const definition = resolveIntegrationDefinition(integrationId);
|
|
4455
|
+
const basePath = resolveUserBasePath();
|
|
4456
|
+
const manifest = readIntegrationManifest(resolveIntegrationManifestPath(basePath));
|
|
4457
|
+
const installedIntegration = findInstalledIntegration(manifest?.integrations ?? [], definition.id);
|
|
4458
|
+
const hookStatus = inspectCodexHook({
|
|
4459
|
+
hooksPath: resolveCodexHooksPath(resolveCodexHomePath2()),
|
|
4460
|
+
definition: definition.hookDefinition
|
|
4461
|
+
});
|
|
4462
|
+
const lines = [
|
|
4463
|
+
`integration installed: ${installedIntegration ? "yes" : "no"}`,
|
|
4464
|
+
`skill installed: ${hasInstalledCodexSkill({ basePath, skillId: definition.skillComponent.id }) ? "yes" : "no"}`,
|
|
4465
|
+
`hook installed: ${hookStatus.installed ? "yes" : "no"}`,
|
|
4466
|
+
`hooks file: ${hookStatus.hooksPath}`
|
|
4467
|
+
];
|
|
4468
|
+
if (definition.id === INTEGRATION_ID.PC) {
|
|
4469
|
+
const pcStatus = getPcHandoffStatus({
|
|
4470
|
+
cwd: resolveBasePath(),
|
|
4471
|
+
contextRoot: resolvePersonalContextRoot()
|
|
4472
|
+
});
|
|
4473
|
+
lines.push(
|
|
4474
|
+
`pc context ready: ${pcStatus.ready ? "yes" : "no"}`,
|
|
4475
|
+
`pc skip reason: ${pcStatus.skipReason ?? "none"}`,
|
|
4476
|
+
`pc workspace: ${pcStatus.workspaceId ?? "not found"}`,
|
|
4477
|
+
`pc active workstream: ${pcStatus.activeWorkstreamId ?? "not found"}`,
|
|
4478
|
+
`pc last confirmed commit: ${pcStatus.lastConfirmedCommitHash ?? "not found"}`
|
|
4479
|
+
);
|
|
4480
|
+
}
|
|
4481
|
+
if (installedIntegration) {
|
|
4482
|
+
lines.push(`owned components: ${installedIntegration.components.map(formatComponentStatus).join(", ")}`);
|
|
4483
|
+
}
|
|
4484
|
+
p12.log.info(lines.join("\n"));
|
|
4485
|
+
} catch (error) {
|
|
4486
|
+
reportIntegrationError(error);
|
|
4487
|
+
}
|
|
4488
|
+
p12.outro("ai-ops integration status \uC644\uB8CC");
|
|
4489
|
+
};
|
|
4490
|
+
var integrationUninstallCommand = async (integrationId) => {
|
|
4491
|
+
p12.intro(`ai-ops integration uninstall ${integrationId}`);
|
|
4492
|
+
try {
|
|
4493
|
+
const definition = resolveIntegrationDefinition(integrationId);
|
|
4494
|
+
const basePath = resolveUserBasePath();
|
|
4495
|
+
const cliVersion = getCliVersion();
|
|
4496
|
+
const manifestPath = resolveIntegrationManifestPath(basePath);
|
|
4497
|
+
const installedIntegration = findInstalledIntegration(
|
|
4498
|
+
readIntegrationManifest(manifestPath)?.integrations ?? [],
|
|
4499
|
+
definition.id
|
|
4500
|
+
);
|
|
4501
|
+
if (!installedIntegration) {
|
|
4502
|
+
p12.log.warn("\uC124\uCE58\uB41C integration manifest entry\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4503
|
+
p12.outro("ai-ops integration uninstall \uC644\uB8CC");
|
|
4504
|
+
return;
|
|
4505
|
+
}
|
|
4506
|
+
const removed = [];
|
|
4507
|
+
for (const component of installedIntegration.components) {
|
|
4508
|
+
if (!component.owned) {
|
|
4509
|
+
continue;
|
|
4510
|
+
}
|
|
4511
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.SKILL) {
|
|
4512
|
+
removed.push(...removeOwnedSkill({ basePath, cliVersion, skillId: component.id }));
|
|
4513
|
+
}
|
|
4514
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK) {
|
|
4515
|
+
const result = uninstallCodexHook({
|
|
4516
|
+
hooksPath: resolveCodexHooksPath(resolveCodexHomePath2()),
|
|
4517
|
+
definition: definition.hookDefinition
|
|
4518
|
+
});
|
|
4519
|
+
if (result.removed) {
|
|
4520
|
+
removed.push(result.hooksPath);
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
writeUserIntegrationState({
|
|
4525
|
+
manifestPath,
|
|
4526
|
+
cliVersion,
|
|
4527
|
+
removeIntegrationId: definition.id
|
|
4528
|
+
});
|
|
4529
|
+
p12.log.success(removed.length > 0 ? `\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}` : "\uC81C\uAC70\uD560 owned component \uC5C6\uC74C");
|
|
4530
|
+
} catch (error) {
|
|
4531
|
+
reportIntegrationError(error);
|
|
4532
|
+
}
|
|
4533
|
+
p12.outro("ai-ops integration uninstall \uC644\uB8CC");
|
|
4534
|
+
};
|
|
4535
|
+
var integrationPostToolUseHookCommand = async (integrationId) => {
|
|
4536
|
+
try {
|
|
4537
|
+
const id = parseIntegrationId(integrationId);
|
|
4538
|
+
const raw = await readStdin2();
|
|
4539
|
+
const hookInput = raw.trim().length > 0 ? JSON.parse(raw) : {};
|
|
4540
|
+
if (id !== INTEGRATION_ID.PC) {
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
const output = evaluatePcPostToolUseHook({
|
|
4544
|
+
hookInput,
|
|
4545
|
+
contextRoot: resolvePersonalContextRoot()
|
|
4546
|
+
});
|
|
4547
|
+
if (output) {
|
|
4548
|
+
process.stdout.write(JSON.stringify(output) + "\n");
|
|
4549
|
+
}
|
|
4550
|
+
} catch (error) {
|
|
4551
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
4552
|
+
process.stdout.write(
|
|
4553
|
+
JSON.stringify({
|
|
4554
|
+
systemMessage: `ai-ops integration hook skipped: ${message}`
|
|
4555
|
+
}) + "\n"
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
};
|
|
4559
|
+
|
|
3641
4560
|
// src/bin/index.ts
|
|
3642
4561
|
var program = new Command();
|
|
3643
|
-
program.name("ai-ops").description("AI agent operating layer manager").version(
|
|
4562
|
+
program.name("ai-ops").description("AI agent operating layer manager").version(getCliVersion());
|
|
3644
4563
|
program.command("init").description("project operating layer \uCD08\uAE30 \uC124\uCE58").option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C adapter \uC9C0\uC815 (codex|gemini|claude-code)").action((opts) => initCommand(opts));
|
|
3645
4564
|
program.command("update").description("project operating layer \uAC31\uC2E0").option("--force", "\uBCC0\uACBD \uC5C6\uC5B4\uB3C4 \uAC15\uC81C \uC7AC\uC124\uCE58", false).action((opts) => updateCommand(opts));
|
|
3646
4565
|
program.command("diff").description("project operating layer drift \uBE44\uAD50").action(() => diffCommand());
|
|
@@ -3683,5 +4602,12 @@ var codexHookCommand = program.command("codex-hook").description("Codex hooks \u
|
|
|
3683
4602
|
codexHookCommand.command("install <hookId>").description("Codex hook \uC124\uCE58").option("--command <command>", "hook\uC5D0 \uC800\uC7A5\uD560 context-promotion \uC2E4\uD589 \uBA85\uB839").action((hookId, opts) => codexHookInstallCommand(hookId, opts));
|
|
3684
4603
|
codexHookCommand.command("status <hookId>").description("Codex hook \uC124\uCE58 \uC0C1\uD0DC \uD655\uC778").action((hookId) => codexHookStatusCommand(hookId));
|
|
3685
4604
|
codexHookCommand.command("uninstall <hookId>").description("Codex hook \uC81C\uAC70").action((hookId) => codexHookUninstallCommand(hookId));
|
|
4605
|
+
var integrationCommand = program.command("integration").description("user/global runtime integration \uC124\uCE58/\uC870\uD68C/\uC81C\uAC70");
|
|
4606
|
+
integrationCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C integration \uBAA9\uB85D").action(() => integrationListCommand());
|
|
4607
|
+
integrationCommand.command("install <integrationId>").description("integration \uC124\uCE58").option("--command <command>", "Codex hook\uC5D0 \uC800\uC7A5\uD560 \uC2E4\uD589 \uBA85\uB839").action((integrationId, opts) => integrationInstallCommand(integrationId, opts));
|
|
4608
|
+
integrationCommand.command("status <integrationId>").description("integration \uC124\uCE58 \uC0C1\uD0DC \uD655\uC778").action((integrationId) => integrationStatusCommand(integrationId));
|
|
4609
|
+
integrationCommand.command("uninstall <integrationId>").description("integration \uC81C\uAC70").action((integrationId) => integrationUninstallCommand(integrationId));
|
|
4610
|
+
var integrationHookCommand = integrationCommand.command("hook").description("integration hook \uB0B4\uBD80 \uBA85\uB839");
|
|
4611
|
+
integrationHookCommand.command("post-tool-use <integrationId>").description("Codex PostToolUse integration hook entrypoint").action((integrationId) => integrationPostToolUseHookCommand(integrationId));
|
|
3686
4612
|
program.parse();
|
|
3687
4613
|
//# sourceMappingURL=index.js.map
|