ai-ops-cli 1.1.1 → 1.3.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 +38 -14
- package/README.md +38 -14
- package/data/context-layer/AGENTS.md +1 -1
- package/data/context-layer/docs/agent/checks/impact-checklist.md +8 -6
- package/data/context-layer/docs/agent/rules/routing-rules.md +2 -1
- package/data/context-layer/docs/agent/workflow.md +22 -8
- package/data/integrations/integration-registry.json +44 -0
- package/data/skills/README.ko.md +12 -13
- package/data/skills/README.md +8 -9
- package/data/skills/skill-registry.json +7 -25
- package/data/skills/task-skills/context-promotion-review/SKILL.md +1 -1
- package/data/skills/task-skills/doc-impact-reviewer/SKILL.md +0 -1
- 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 +1414 -576
- package/dist/bin/index.js.map +1 -1
- package/package.json +2 -2
- package/data/context-layer/docs/agent/checks/review-checklist.md +0 -17
package/dist/bin/index.js
CHANGED
|
@@ -6,42 +6,8 @@ import { Command } from "commander";
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import * as p2 from "@clack/prompts";
|
|
8
8
|
|
|
9
|
-
// src/core/schemas/rule.schema.ts
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
var DecisionTableEntrySchema = z.object({
|
|
12
|
-
when: z.string().min(1),
|
|
13
|
-
then: z.string().min(1),
|
|
14
|
-
/** 조건부 규칙에서 회피해야 할 패턴 */
|
|
15
|
-
avoid: z.string().min(1).optional()
|
|
16
|
-
}).strict();
|
|
17
|
-
var RuleContentSchema = z.object({
|
|
18
|
-
/** Anti-pattern 규칙 ('하지 마라'). guidelines보다 항상 상단 렌더링 */
|
|
19
|
-
constraints: z.array(z.string().min(1)).default([]),
|
|
20
|
-
/** Positive 규칙 ('해라') */
|
|
21
|
-
guidelines: z.array(z.string().min(1)),
|
|
22
|
-
/** 조건부 규칙. when→then→avoid 구조 */
|
|
23
|
-
decision_table: z.array(DecisionTableEntrySchema).optional()
|
|
24
|
-
}).strict();
|
|
25
|
-
var RuleSchema = z.object({
|
|
26
|
-
id: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
27
|
-
category: z.string().min(1),
|
|
28
|
-
tags: z.array(z.string().min(1)),
|
|
29
|
-
/** 0-100. 높을수록 생성 파일 상단 배치 (U-shaped attention 최적화) */
|
|
30
|
-
priority: z.number().int().min(0).max(100),
|
|
31
|
-
supported_tools: z.array(z.string().min(1)).min(1).default(["claude-code", "codex", "gemini"]),
|
|
32
|
-
content: RuleContentSchema
|
|
33
|
-
}).strict();
|
|
34
|
-
|
|
35
|
-
// src/core/schemas/preset.schema.ts
|
|
36
|
-
import { z as z2 } from "zod";
|
|
37
|
-
var PresetSchema = z2.object({
|
|
38
|
-
id: z2.string().regex(/^[a-z][a-z0-9-]*$/).min(1),
|
|
39
|
-
description: z2.string().min(1),
|
|
40
|
-
rules: z2.array(z2.string().min(1)).min(1)
|
|
41
|
-
}).strict();
|
|
42
|
-
|
|
43
9
|
// src/core/schemas/skill.schema.ts
|
|
44
|
-
import { z
|
|
10
|
+
import { z } from "zod";
|
|
45
11
|
var SKILL_KIND = {
|
|
46
12
|
REFERENCE: "reference",
|
|
47
13
|
TASK: "task"
|
|
@@ -51,69 +17,68 @@ var SKILL_TOOL = {
|
|
|
51
17
|
CODEX: "codex",
|
|
52
18
|
GEMINI: "gemini"
|
|
53
19
|
};
|
|
54
|
-
var SkillKindSchema =
|
|
55
|
-
var SkillToolSchema =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
20
|
+
var SkillKindSchema = z.union([z.literal(SKILL_KIND.REFERENCE), z.literal(SKILL_KIND.TASK)]);
|
|
21
|
+
var SkillToolSchema = z.union([
|
|
22
|
+
z.literal(SKILL_TOOL.CLAUDE_CODE),
|
|
23
|
+
z.literal(SKILL_TOOL.CODEX),
|
|
24
|
+
z.literal(SKILL_TOOL.GEMINI)
|
|
59
25
|
]);
|
|
60
|
-
var SkillFileSchema =
|
|
61
|
-
path:
|
|
62
|
-
content:
|
|
26
|
+
var SkillFileSchema = z.object({
|
|
27
|
+
path: z.string().min(1),
|
|
28
|
+
content: z.string()
|
|
63
29
|
}).strict();
|
|
64
|
-
var SkillFrontmatterSchema =
|
|
65
|
-
name:
|
|
66
|
-
description:
|
|
30
|
+
var SkillFrontmatterSchema = z.object({
|
|
31
|
+
name: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "name must be kebab-case"),
|
|
32
|
+
description: z.string().min(1)
|
|
67
33
|
}).passthrough();
|
|
68
|
-
var InstalledSkillSchema =
|
|
69
|
-
id:
|
|
34
|
+
var InstalledSkillSchema = z.object({
|
|
35
|
+
id: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
70
36
|
kind: SkillKindSchema,
|
|
71
|
-
tools:
|
|
72
|
-
installed_paths:
|
|
73
|
-
sourceHash:
|
|
37
|
+
tools: z.array(SkillToolSchema).min(1),
|
|
38
|
+
installed_paths: z.array(z.string().min(1)).min(1),
|
|
39
|
+
sourceHash: z.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars")
|
|
74
40
|
}).strip();
|
|
75
41
|
|
|
76
42
|
// src/core/schemas/skill-catalog.schema.ts
|
|
77
|
-
import { z as
|
|
78
|
-
var SkillIdSchema =
|
|
79
|
-
var SkillCatalogPathSchema =
|
|
80
|
-
var SkillCatalogEntrySchema =
|
|
43
|
+
import { z as z2 } from "zod";
|
|
44
|
+
var SkillIdSchema = z2.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case");
|
|
45
|
+
var SkillCatalogPathSchema = z2.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)+$/, "source_path must be relative kebab-case path");
|
|
46
|
+
var SkillCatalogEntrySchema = z2.object({
|
|
81
47
|
id: SkillIdSchema,
|
|
82
48
|
kind: SkillKindSchema,
|
|
83
|
-
supported_tools:
|
|
84
|
-
groups:
|
|
85
|
-
included_in_presets: z4.array(z4.string().min(1)),
|
|
49
|
+
supported_tools: z2.array(SkillToolSchema).min(1),
|
|
50
|
+
groups: z2.array(z2.string().min(1)),
|
|
86
51
|
source_path: SkillCatalogPathSchema
|
|
87
52
|
}).strict().superRefine((entry, ctx) => {
|
|
88
53
|
const expectedPrefix = entry.kind === "reference" ? "reference-skills/" : "task-skills/";
|
|
89
54
|
if (!entry.source_path.startsWith(expectedPrefix)) {
|
|
90
55
|
ctx.addIssue({
|
|
91
|
-
code:
|
|
56
|
+
code: z2.ZodIssueCode.custom,
|
|
92
57
|
path: ["source_path"],
|
|
93
58
|
message: `source_path must start with ${expectedPrefix}`
|
|
94
59
|
});
|
|
95
60
|
}
|
|
96
61
|
});
|
|
97
|
-
var SkillCatalogSchema =
|
|
98
|
-
skills:
|
|
62
|
+
var SkillCatalogSchema = z2.object({
|
|
63
|
+
skills: z2.array(SkillCatalogEntrySchema)
|
|
99
64
|
}).strict();
|
|
100
65
|
|
|
101
66
|
// src/core/schemas/skill-registry.schema.ts
|
|
102
|
-
import { z as
|
|
103
|
-
var SkillRegistrySchema =
|
|
104
|
-
skills:
|
|
105
|
-
cliVersion:
|
|
106
|
-
generatedAt:
|
|
67
|
+
import { z as z3 } from "zod";
|
|
68
|
+
var SkillRegistrySchema = z3.object({
|
|
69
|
+
skills: z3.array(InstalledSkillSchema),
|
|
70
|
+
cliVersion: z3.string().optional(),
|
|
71
|
+
generatedAt: z3.string().datetime({ offset: true })
|
|
107
72
|
}).strict();
|
|
108
73
|
|
|
109
74
|
// src/core/schemas/subagent.schema.ts
|
|
110
|
-
import { z as
|
|
75
|
+
import { z as z5 } from "zod";
|
|
111
76
|
|
|
112
77
|
// src/core/schemas/project-layer.schema.ts
|
|
113
|
-
import { z as
|
|
114
|
-
var ProjectLayerToolSchema =
|
|
115
|
-
var ProjectLayerDocumentStatusSchema =
|
|
116
|
-
var ShortHashSchema =
|
|
78
|
+
import { z as z4 } from "zod";
|
|
79
|
+
var ProjectLayerToolSchema = z4.enum(["claude-code", "codex", "gemini"]);
|
|
80
|
+
var ProjectLayerDocumentStatusSchema = z4.enum(["Active", "Reserved", "Draft", "Archived"]);
|
|
81
|
+
var ShortHashSchema = z4.string().regex(/^[a-f0-9]{6}$/, "hash must be 6 lowercase hex chars");
|
|
117
82
|
var isSafeProjectLayerPath = (value) => {
|
|
118
83
|
if (value.length === 0) return false;
|
|
119
84
|
if (value.includes("\0")) return false;
|
|
@@ -123,55 +88,55 @@ var isSafeProjectLayerPath = (value) => {
|
|
|
123
88
|
const segments = value.split("/");
|
|
124
89
|
return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
125
90
|
};
|
|
126
|
-
var ProjectLayerPathSchema =
|
|
127
|
-
var ProjectLayerFrontmatterSchema =
|
|
91
|
+
var ProjectLayerPathSchema = z4.string().min(1).refine(isSafeProjectLayerPath, "path must be a safe project-relative path");
|
|
92
|
+
var ProjectLayerFrontmatterSchema = z4.object({
|
|
128
93
|
status: ProjectLayerDocumentStatusSchema,
|
|
129
|
-
layer:
|
|
130
|
-
owner:
|
|
131
|
-
read_when:
|
|
132
|
-
update_when:
|
|
94
|
+
layer: z4.string().min(1),
|
|
95
|
+
owner: z4.string().min(1),
|
|
96
|
+
read_when: z4.array(z4.string().min(1)).min(1),
|
|
97
|
+
update_when: z4.array(z4.string().min(1)).min(1)
|
|
133
98
|
}).strict();
|
|
134
|
-
var ProjectLayerManagedFileSchema =
|
|
99
|
+
var ProjectLayerManagedFileSchema = z4.object({
|
|
135
100
|
path: ProjectLayerPathSchema,
|
|
136
101
|
sourceHash: ShortHashSchema
|
|
137
102
|
}).strict();
|
|
138
|
-
var ProjectLayerProjectFileSchema =
|
|
103
|
+
var ProjectLayerProjectFileSchema = z4.object({
|
|
139
104
|
path: ProjectLayerPathSchema,
|
|
140
105
|
templateHash: ShortHashSchema,
|
|
141
|
-
created:
|
|
106
|
+
created: z4.boolean()
|
|
142
107
|
}).strict();
|
|
143
|
-
var ProjectLayerPackFileRecordSchema =
|
|
108
|
+
var ProjectLayerPackFileRecordSchema = z4.object({
|
|
144
109
|
path: ProjectLayerPathSchema,
|
|
145
110
|
sourceHash: ShortHashSchema
|
|
146
111
|
}).strict();
|
|
147
|
-
var ProjectLayerPackRecordSchema =
|
|
148
|
-
id:
|
|
112
|
+
var ProjectLayerPackRecordSchema = z4.object({
|
|
113
|
+
id: z4.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
149
114
|
sourceHash: ShortHashSchema,
|
|
150
|
-
documents:
|
|
151
|
-
files:
|
|
152
|
-
installedAt:
|
|
115
|
+
documents: z4.array(ProjectLayerPackFileRecordSchema),
|
|
116
|
+
files: z4.array(ProjectLayerPackFileRecordSchema),
|
|
117
|
+
installedAt: z4.string().datetime({ offset: true })
|
|
153
118
|
}).strict();
|
|
154
|
-
var ProjectLayerManifestSchema =
|
|
155
|
-
schemaVersion:
|
|
156
|
-
kind:
|
|
157
|
-
tools:
|
|
158
|
-
managed_files:
|
|
159
|
-
project_files:
|
|
160
|
-
packs:
|
|
161
|
-
settings:
|
|
119
|
+
var ProjectLayerManifestSchema = z4.object({
|
|
120
|
+
schemaVersion: z4.literal(1),
|
|
121
|
+
kind: z4.literal("project-operating-layer"),
|
|
122
|
+
tools: z4.array(ProjectLayerToolSchema).min(1),
|
|
123
|
+
managed_files: z4.array(ProjectLayerManagedFileSchema),
|
|
124
|
+
project_files: z4.array(ProjectLayerProjectFileSchema),
|
|
125
|
+
packs: z4.array(ProjectLayerPackRecordSchema).default([]),
|
|
126
|
+
settings: z4.record(z4.unknown()),
|
|
162
127
|
sourceHash: ShortHashSchema,
|
|
163
|
-
cliVersion:
|
|
164
|
-
generatedAt:
|
|
128
|
+
cliVersion: z4.string().min(1),
|
|
129
|
+
generatedAt: z4.string().datetime({ offset: true })
|
|
165
130
|
}).strict();
|
|
166
131
|
var ProjectLayerContextDocumentSchema = ProjectLayerFrontmatterSchema.extend({
|
|
167
132
|
path: ProjectLayerPathSchema,
|
|
168
133
|
contentHash: ShortHashSchema
|
|
169
134
|
}).strict();
|
|
170
|
-
var ProjectLayerContextIndexSchema =
|
|
171
|
-
schemaVersion:
|
|
172
|
-
kind:
|
|
173
|
-
documents:
|
|
174
|
-
generatedAt:
|
|
135
|
+
var ProjectLayerContextIndexSchema = z4.object({
|
|
136
|
+
schemaVersion: z4.literal(1),
|
|
137
|
+
kind: z4.literal("context-layer-index"),
|
|
138
|
+
documents: z4.array(ProjectLayerContextDocumentSchema),
|
|
139
|
+
generatedAt: z4.string().datetime({ offset: true })
|
|
175
140
|
}).strict();
|
|
176
141
|
|
|
177
142
|
// src/core/subagent-paths.ts
|
|
@@ -196,29 +161,29 @@ var buildSubagentRelativePath = (subagentId, toolId) => {
|
|
|
196
161
|
};
|
|
197
162
|
|
|
198
163
|
// src/core/schemas/subagent.schema.ts
|
|
199
|
-
var SubagentIdSchema =
|
|
200
|
-
var SubagentMarkdownFrontmatterSchema =
|
|
164
|
+
var SubagentIdSchema = z5.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case");
|
|
165
|
+
var SubagentMarkdownFrontmatterSchema = z5.object({
|
|
201
166
|
name: SubagentIdSchema,
|
|
202
|
-
description:
|
|
167
|
+
description: z5.string().min(1)
|
|
203
168
|
}).passthrough();
|
|
204
|
-
var TomlValueSchema =
|
|
205
|
-
var SubagentInstalledPathSchema =
|
|
206
|
-
var CodexSubagentFrontmatterSchema =
|
|
169
|
+
var TomlValueSchema = z5.union([z5.string(), z5.number(), z5.boolean(), z5.array(z5.string())]);
|
|
170
|
+
var SubagentInstalledPathSchema = z5.string().min(1).refine(isSafeProjectLayerPath, "installed path must be safe relative path");
|
|
171
|
+
var CodexSubagentFrontmatterSchema = z5.object({
|
|
207
172
|
name: SubagentIdSchema,
|
|
208
|
-
description:
|
|
209
|
-
skill_names:
|
|
173
|
+
description: z5.string().min(1),
|
|
174
|
+
skill_names: z5.array(SubagentIdSchema).optional()
|
|
210
175
|
}).catchall(TomlValueSchema);
|
|
211
|
-
var InstalledSubagentSchema =
|
|
176
|
+
var InstalledSubagentSchema = z5.object({
|
|
212
177
|
id: SubagentIdSchema,
|
|
213
|
-
tools:
|
|
214
|
-
installed_paths:
|
|
215
|
-
sourceHash:
|
|
178
|
+
tools: z5.array(SkillToolSchema).min(1),
|
|
179
|
+
installed_paths: z5.array(SubagentInstalledPathSchema).min(1),
|
|
180
|
+
sourceHash: z5.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars")
|
|
216
181
|
}).strip().superRefine((subagent, ctx) => {
|
|
217
182
|
const expectedPaths = new Set(subagent.tools.map((tool) => buildSubagentRelativePath(subagent.id, tool)));
|
|
218
183
|
const installedPaths = new Set(subagent.installed_paths);
|
|
219
184
|
if (installedPaths.size !== subagent.installed_paths.length) {
|
|
220
185
|
ctx.addIssue({
|
|
221
|
-
code:
|
|
186
|
+
code: z5.ZodIssueCode.custom,
|
|
222
187
|
path: ["installed_paths"],
|
|
223
188
|
message: "installed_paths must not contain duplicates"
|
|
224
189
|
});
|
|
@@ -226,7 +191,7 @@ var InstalledSubagentSchema = z7.object({
|
|
|
226
191
|
}
|
|
227
192
|
if (installedPaths.size !== expectedPaths.size) {
|
|
228
193
|
ctx.addIssue({
|
|
229
|
-
code:
|
|
194
|
+
code: z5.ZodIssueCode.custom,
|
|
230
195
|
path: ["installed_paths"],
|
|
231
196
|
message: "installed_paths must match id and tools"
|
|
232
197
|
});
|
|
@@ -235,7 +200,7 @@ var InstalledSubagentSchema = z7.object({
|
|
|
235
200
|
for (const installedPath of installedPaths) {
|
|
236
201
|
if (!expectedPaths.has(installedPath)) {
|
|
237
202
|
ctx.addIssue({
|
|
238
|
-
code:
|
|
203
|
+
code: z5.ZodIssueCode.custom,
|
|
239
204
|
path: ["installed_paths"],
|
|
240
205
|
message: "installed_paths must match id and tools"
|
|
241
206
|
});
|
|
@@ -245,27 +210,146 @@ var InstalledSubagentSchema = z7.object({
|
|
|
245
210
|
});
|
|
246
211
|
|
|
247
212
|
// src/core/schemas/subagent-catalog.schema.ts
|
|
248
|
-
import { z as
|
|
249
|
-
var SubagentCatalogPathSchema =
|
|
213
|
+
import { z as z6 } from "zod";
|
|
214
|
+
var SubagentCatalogPathSchema = z6.string().regex(
|
|
250
215
|
/^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)*$/,
|
|
251
216
|
"source_path must be relative kebab-case path"
|
|
252
217
|
);
|
|
253
|
-
var SubagentCatalogEntrySchema =
|
|
218
|
+
var SubagentCatalogEntrySchema = z6.object({
|
|
254
219
|
id: SubagentIdSchema,
|
|
255
|
-
supported_tools:
|
|
220
|
+
supported_tools: z6.array(SkillToolSchema).min(1),
|
|
256
221
|
source_path: SubagentCatalogPathSchema
|
|
257
222
|
}).strict();
|
|
258
|
-
var SubagentCatalogSchema =
|
|
259
|
-
subagents:
|
|
223
|
+
var SubagentCatalogSchema = z6.object({
|
|
224
|
+
subagents: z6.array(SubagentCatalogEntrySchema)
|
|
260
225
|
}).strict();
|
|
261
226
|
|
|
262
227
|
// src/core/schemas/subagent-manifest.schema.ts
|
|
228
|
+
import { z as z7 } from "zod";
|
|
229
|
+
var SubagentManifestSchema = z7.object({
|
|
230
|
+
subagents: z7.array(InstalledSubagentSchema),
|
|
231
|
+
cliVersion: z7.string().optional(),
|
|
232
|
+
generatedAt: z7.string().datetime({ offset: true })
|
|
233
|
+
}).strict();
|
|
234
|
+
|
|
235
|
+
// src/core/schemas/integration.schema.ts
|
|
236
|
+
import { z as z8 } from "zod";
|
|
237
|
+
var INTEGRATION_ID = {
|
|
238
|
+
CONTEXT_PROMOTION: "context-promotion",
|
|
239
|
+
PC: "pc"
|
|
240
|
+
};
|
|
241
|
+
var INTEGRATION_COMPONENT_TYPE = {
|
|
242
|
+
SKILL: "skill",
|
|
243
|
+
CODEX_HOOK: "codex-hook",
|
|
244
|
+
RECEIPT_CONFIG: "receipt-config"
|
|
245
|
+
};
|
|
246
|
+
var IntegrationIdSchema = z8.union([z8.literal(INTEGRATION_ID.CONTEXT_PROMOTION), z8.literal(INTEGRATION_ID.PC)]);
|
|
247
|
+
var IntegrationSkillComponentSchema = z8.object({
|
|
248
|
+
type: z8.literal(INTEGRATION_COMPONENT_TYPE.SKILL),
|
|
249
|
+
id: z8.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
250
|
+
tools: z8.array(SkillToolSchema).min(1),
|
|
251
|
+
owned: z8.boolean()
|
|
252
|
+
}).strict();
|
|
253
|
+
var IntegrationCodexHookComponentSchema = z8.object({
|
|
254
|
+
type: z8.literal(INTEGRATION_COMPONENT_TYPE.CODEX_HOOK),
|
|
255
|
+
id: IntegrationIdSchema,
|
|
256
|
+
command: z8.string().min(1),
|
|
257
|
+
owned: z8.boolean()
|
|
258
|
+
}).strict();
|
|
259
|
+
var IntegrationReceiptConfigComponentSchema = z8.object({
|
|
260
|
+
type: z8.literal(INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG),
|
|
261
|
+
id: z8.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/),
|
|
262
|
+
storagePath: z8.string().min(1),
|
|
263
|
+
owned: z8.boolean()
|
|
264
|
+
}).strict();
|
|
265
|
+
var IntegrationComponentSchema = z8.union([
|
|
266
|
+
IntegrationSkillComponentSchema,
|
|
267
|
+
IntegrationCodexHookComponentSchema,
|
|
268
|
+
IntegrationReceiptConfigComponentSchema
|
|
269
|
+
]);
|
|
270
|
+
var InstalledIntegrationSchema = z8.object({
|
|
271
|
+
id: IntegrationIdSchema,
|
|
272
|
+
components: z8.array(IntegrationComponentSchema),
|
|
273
|
+
installedAt: z8.string().min(1),
|
|
274
|
+
updatedAt: z8.string().min(1)
|
|
275
|
+
}).strict();
|
|
276
|
+
var IntegrationManifestSchema = z8.object({
|
|
277
|
+
schemaVersion: z8.literal(1),
|
|
278
|
+
kind: z8.literal("ai-ops-integrations-manifest"),
|
|
279
|
+
integrations: z8.array(InstalledIntegrationSchema),
|
|
280
|
+
cliVersion: z8.string().min(1),
|
|
281
|
+
generatedAt: z8.string().min(1)
|
|
282
|
+
}).strict();
|
|
283
|
+
|
|
284
|
+
// src/core/schemas/integration-catalog.schema.ts
|
|
263
285
|
import { z as z9 } from "zod";
|
|
264
|
-
var
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
286
|
+
var ComponentIdSchema = z9.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "component id must be kebab-case");
|
|
287
|
+
var IntegrationCatalogSkillComponentSchema = z9.object({
|
|
288
|
+
type: z9.literal(INTEGRATION_COMPONENT_TYPE.SKILL),
|
|
289
|
+
id: ComponentIdSchema,
|
|
290
|
+
tools: z9.array(z9.literal("codex")).min(1)
|
|
291
|
+
}).strict();
|
|
292
|
+
var IntegrationCatalogCodexHookComponentSchema = z9.object({
|
|
293
|
+
type: z9.literal(INTEGRATION_COMPONENT_TYPE.CODEX_HOOK),
|
|
294
|
+
id: IntegrationIdSchema
|
|
268
295
|
}).strict();
|
|
296
|
+
var IntegrationCatalogReceiptConfigComponentSchema = z9.object({
|
|
297
|
+
type: z9.literal(INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG),
|
|
298
|
+
id: ComponentIdSchema,
|
|
299
|
+
storage_path: z9.string().min(1)
|
|
300
|
+
}).strict();
|
|
301
|
+
var IntegrationCatalogComponentSchema = z9.union([
|
|
302
|
+
IntegrationCatalogSkillComponentSchema,
|
|
303
|
+
IntegrationCatalogCodexHookComponentSchema,
|
|
304
|
+
IntegrationCatalogReceiptConfigComponentSchema
|
|
305
|
+
]);
|
|
306
|
+
var IntegrationCatalogEntrySchema = z9.object({
|
|
307
|
+
id: IntegrationIdSchema,
|
|
308
|
+
description: z9.string().min(1),
|
|
309
|
+
components: z9.array(IntegrationCatalogComponentSchema).min(1)
|
|
310
|
+
}).strict().superRefine((entry, ctx) => {
|
|
311
|
+
const hasSkill = entry.components.some((component) => component.type === INTEGRATION_COMPONENT_TYPE.SKILL);
|
|
312
|
+
const hasCodexHook = entry.components.some((component) => component.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK);
|
|
313
|
+
const hasReceiptConfig = entry.components.some(
|
|
314
|
+
(component) => component.type === INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG
|
|
315
|
+
);
|
|
316
|
+
if (!hasSkill) {
|
|
317
|
+
ctx.addIssue({
|
|
318
|
+
code: z9.ZodIssueCode.custom,
|
|
319
|
+
path: ["components"],
|
|
320
|
+
message: `integration must declare a skill component: ${entry.id}`
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (!hasCodexHook) {
|
|
324
|
+
ctx.addIssue({
|
|
325
|
+
code: z9.ZodIssueCode.custom,
|
|
326
|
+
path: ["components"],
|
|
327
|
+
message: `integration must declare a codex-hook component: ${entry.id}`
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (!hasReceiptConfig) {
|
|
331
|
+
ctx.addIssue({
|
|
332
|
+
code: z9.ZodIssueCode.custom,
|
|
333
|
+
path: ["components"],
|
|
334
|
+
message: `integration must declare a receipt-config component: ${entry.id}`
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
var IntegrationCatalogSchema = z9.object({
|
|
339
|
+
integrations: z9.array(IntegrationCatalogEntrySchema)
|
|
340
|
+
}).strict().superRefine((catalog, ctx) => {
|
|
341
|
+
const seen = /* @__PURE__ */ new Set();
|
|
342
|
+
for (const [index, entry] of catalog.integrations.entries()) {
|
|
343
|
+
if (seen.has(entry.id)) {
|
|
344
|
+
ctx.addIssue({
|
|
345
|
+
code: z9.ZodIssueCode.custom,
|
|
346
|
+
path: ["integrations", index, "id"],
|
|
347
|
+
message: `duplicate integration id: ${entry.id}`
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
seen.add(entry.id);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
269
353
|
|
|
270
354
|
// src/core/schemas/pack.schema.ts
|
|
271
355
|
import { z as z10 } from "zod";
|
|
@@ -279,40 +363,6 @@ var PackCatalogSchema = z10.object({
|
|
|
279
363
|
packs: z10.array(PackCatalogEntrySchema)
|
|
280
364
|
}).strict();
|
|
281
365
|
|
|
282
|
-
// src/core/schemas/manifest.schema.ts
|
|
283
|
-
import { z as z11 } from "zod";
|
|
284
|
-
var SettingsConfigSchema = z11.object({
|
|
285
|
-
claude: z11.array(z11.string().min(1)).optional(),
|
|
286
|
-
gemini: z11.array(z11.string().min(1)).optional(),
|
|
287
|
-
prettierignore: z11.boolean().optional()
|
|
288
|
-
}).strict();
|
|
289
|
-
var WorkspaceEntrySchema = z11.object({
|
|
290
|
-
preset: z11.string().min(1),
|
|
291
|
-
rules: z11.array(z11.string().min(1))
|
|
292
|
-
}).strict();
|
|
293
|
-
var ManifestSchema = z11.object({
|
|
294
|
-
tools: z11.array(z11.string().min(1)).min(1),
|
|
295
|
-
scope: z11.literal("project"),
|
|
296
|
-
/** 비모노레포 단일 preset */
|
|
297
|
-
preset: z11.string().min(1).optional(),
|
|
298
|
-
/** 모노레포: workspace path → { preset, rules } */
|
|
299
|
-
workspaces: z11.record(z11.string(), WorkspaceEntrySchema).optional(),
|
|
300
|
-
installed_rules: z11.array(z11.string().min(1)),
|
|
301
|
-
/** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */
|
|
302
|
-
installed_files: z11.array(z11.string().min(1)).optional(),
|
|
303
|
-
/** skill 설치 루트 디렉토리 목록 */
|
|
304
|
-
installed_skills: z11.array(InstalledSkillSchema).optional(),
|
|
305
|
-
/** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */
|
|
306
|
-
appended_files: z11.array(z11.string().min(1)).optional(),
|
|
307
|
-
/** init 시 선택된 settings 항목 — update 시 재생성에 사용 */
|
|
308
|
-
settings: SettingsConfigSchema.optional(),
|
|
309
|
-
/** init/update 실행 시점의 CLI 패키지 버전 — 버전 변경 감지에 사용 */
|
|
310
|
-
cliVersion: z11.string().optional(),
|
|
311
|
-
/** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
|
|
312
|
-
sourceHash: z11.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
|
|
313
|
-
generatedAt: z11.string().datetime({ offset: true })
|
|
314
|
-
}).strict();
|
|
315
|
-
|
|
316
366
|
// src/core/loader.ts
|
|
317
367
|
import { readFileSync, readdirSync } from "fs";
|
|
318
368
|
import { join as join2, resolve } from "path";
|
|
@@ -424,7 +474,6 @@ var loadAllSkills = (skillsDir) => {
|
|
|
424
474
|
description: parsed.description,
|
|
425
475
|
supported_tools: [...entry.supported_tools],
|
|
426
476
|
groups: [...entry.groups],
|
|
427
|
-
included_in_presets: [...entry.included_in_presets],
|
|
428
477
|
directory,
|
|
429
478
|
files
|
|
430
479
|
};
|
|
@@ -444,6 +493,10 @@ var assertSubagentFrontmatterName = (params) => {
|
|
|
444
493
|
}
|
|
445
494
|
};
|
|
446
495
|
var loadSubagentCatalog = (subagentsDir) => SubagentCatalogSchema.parse(JSON.parse(readFileSync(resolve(subagentsDir, "subagent-registry.json"), "utf-8")));
|
|
496
|
+
var loadIntegrationCatalog = (integrationsDir) => IntegrationCatalogSchema.parse(
|
|
497
|
+
JSON.parse(readFileSync(resolve(integrationsDir, "integration-registry.json"), "utf-8"))
|
|
498
|
+
);
|
|
499
|
+
var loadAllIntegrations = (integrationsDir) => [...loadIntegrationCatalog(integrationsDir).integrations].sort((a, b) => a.id.localeCompare(b.id));
|
|
447
500
|
var loadAllSubagents = (subagentsDir) => {
|
|
448
501
|
const catalog = loadSubagentCatalog(subagentsDir);
|
|
449
502
|
const entries = [...catalog.subagents].sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -483,16 +536,13 @@ var loadAllSubagents = (subagentsDir) => {
|
|
|
483
536
|
});
|
|
484
537
|
};
|
|
485
538
|
|
|
486
|
-
// src/core/renderer.ts
|
|
487
|
-
import { join as join3 } from "path";
|
|
488
|
-
|
|
489
539
|
// src/core/skill-renderer.ts
|
|
490
|
-
import { join as
|
|
540
|
+
import { join as join4 } from "path";
|
|
491
541
|
|
|
492
542
|
// src/core/source-hash.ts
|
|
493
543
|
import { createHash } from "crypto";
|
|
494
544
|
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
495
|
-
import { dirname, join as
|
|
545
|
+
import { dirname, join as join3, resolve as resolve2 } from "path";
|
|
496
546
|
import { fileURLToPath } from "url";
|
|
497
547
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
498
548
|
var getCliVersion = () => {
|
|
@@ -514,10 +564,10 @@ var CLAUDE_SKILLS_DIR = ".claude/skills";
|
|
|
514
564
|
var buildRootDirs = (skillId, toolIds) => {
|
|
515
565
|
const dirs = [];
|
|
516
566
|
if (toolIds.some((toolId) => toolId === "codex" || toolId === "gemini")) {
|
|
517
|
-
dirs.push(
|
|
567
|
+
dirs.push(join4(AGENT_SKILLS_DIR, skillId));
|
|
518
568
|
}
|
|
519
569
|
if (toolIds.includes("claude-code")) {
|
|
520
|
-
dirs.push(
|
|
570
|
+
dirs.push(join4(CLAUDE_SKILLS_DIR, skillId));
|
|
521
571
|
}
|
|
522
572
|
return dirs;
|
|
523
573
|
};
|
|
@@ -539,7 +589,7 @@ var buildSkillInstallPlan = (params) => {
|
|
|
539
589
|
});
|
|
540
590
|
const packages = rootDirs.map((rootDir) => {
|
|
541
591
|
const files = params.skill.files.map((file) => ({
|
|
542
|
-
relativePath:
|
|
592
|
+
relativePath: join4(rootDir, file.path),
|
|
543
593
|
content: file.content
|
|
544
594
|
}));
|
|
545
595
|
return {
|
|
@@ -583,11 +633,9 @@ var renderCodexSubagent = (params) => {
|
|
|
583
633
|
path = ${JSON.stringify(skillPath)}
|
|
584
634
|
enabled = true`;
|
|
585
635
|
});
|
|
586
|
-
const sections = [
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
...skills
|
|
590
|
-
].filter((section) => section.length > 0);
|
|
636
|
+
const sections = [metadata, `developer_instructions = ${JSON.stringify(params.prompt.trimEnd())}`, ...skills].filter(
|
|
637
|
+
(section) => section.length > 0
|
|
638
|
+
);
|
|
591
639
|
return sections.join("\n\n") + "\n";
|
|
592
640
|
};
|
|
593
641
|
var renderSubagentForTool = (params) => {
|
|
@@ -732,64 +780,97 @@ var parseAiOpsMeta = (content) => {
|
|
|
732
780
|
return { sourceHash: match[1], generatedAt: match[2] };
|
|
733
781
|
};
|
|
734
782
|
|
|
735
|
-
// src/core/
|
|
783
|
+
// src/core/skill-registry-io.ts
|
|
736
784
|
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
737
|
-
import { dirname as dirname2, join as
|
|
738
|
-
|
|
739
|
-
// src/core/manifest-resolution.ts
|
|
785
|
+
import { dirname as dirname2, join as join5 } from "path";
|
|
786
|
+
var SKILL_REGISTRY_FILENAME = "skills-manifest.json";
|
|
740
787
|
var LEGACY_SKILL_ID_MAP = {
|
|
741
788
|
"engineering-standards-pack": "backend-service-standards"
|
|
742
789
|
};
|
|
743
790
|
var resolveCanonicalSkillId = (skillId) => LEGACY_SKILL_ID_MAP[skillId] ?? skillId;
|
|
744
|
-
|
|
745
|
-
// src/core/skill-registry-io.ts
|
|
746
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
747
|
-
import { dirname as dirname3, join as join7 } from "path";
|
|
748
|
-
var SKILL_REGISTRY_FILENAME = "skills-manifest.json";
|
|
749
791
|
var parseSkillRegistry = (json) => SkillRegistrySchema.parse(JSON.parse(json));
|
|
750
792
|
var serializeSkillRegistry = (registry) => JSON.stringify(registry, null, 2) + "\n";
|
|
751
|
-
var resolveSkillRegistryPath = (userBasePath) =>
|
|
793
|
+
var resolveSkillRegistryPath = (userBasePath) => join5(userBasePath, ".ai-ops", SKILL_REGISTRY_FILENAME);
|
|
752
794
|
var readSkillRegistry = (registryPath) => {
|
|
753
795
|
let raw;
|
|
754
796
|
try {
|
|
755
|
-
raw =
|
|
797
|
+
raw = readFileSync3(registryPath, "utf-8");
|
|
756
798
|
} catch {
|
|
757
799
|
return null;
|
|
758
800
|
}
|
|
759
801
|
return parseSkillRegistry(raw);
|
|
760
802
|
};
|
|
761
803
|
var writeSkillRegistry = (registryPath, registry) => {
|
|
762
|
-
|
|
763
|
-
|
|
804
|
+
mkdirSync(dirname2(registryPath), { recursive: true });
|
|
805
|
+
writeFileSync(registryPath, serializeSkillRegistry(registry), "utf-8");
|
|
764
806
|
};
|
|
765
807
|
|
|
766
808
|
// src/core/subagent-manifest-io.ts
|
|
767
|
-
import { mkdirSync as
|
|
768
|
-
import { dirname as
|
|
809
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
810
|
+
import { dirname as dirname3, join as join6 } from "path";
|
|
769
811
|
var SUBAGENT_MANIFEST_FILENAME = "subagents-manifest.json";
|
|
770
812
|
var parseSubagentManifest = (json) => SubagentManifestSchema.parse(JSON.parse(json));
|
|
771
813
|
var serializeSubagentManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
772
|
-
var resolveSubagentManifestPath = (userBasePath) =>
|
|
814
|
+
var resolveSubagentManifestPath = (userBasePath) => join6(userBasePath, ".ai-ops", SUBAGENT_MANIFEST_FILENAME);
|
|
773
815
|
var readSubagentManifest = (manifestPath) => {
|
|
774
816
|
let raw;
|
|
775
817
|
try {
|
|
776
|
-
raw =
|
|
818
|
+
raw = readFileSync4(manifestPath, "utf-8");
|
|
777
819
|
} catch {
|
|
778
820
|
return null;
|
|
779
821
|
}
|
|
780
822
|
return parseSubagentManifest(raw);
|
|
781
823
|
};
|
|
782
824
|
var writeSubagentManifest = (manifestPath, manifest) => {
|
|
783
|
-
|
|
784
|
-
|
|
825
|
+
mkdirSync2(dirname3(manifestPath), { recursive: true });
|
|
826
|
+
writeFileSync2(manifestPath, serializeSubagentManifest(manifest), "utf-8");
|
|
785
827
|
};
|
|
786
828
|
|
|
787
|
-
// src/core/
|
|
788
|
-
import {
|
|
829
|
+
// src/core/integration-manifest-io.ts
|
|
830
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
831
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
832
|
+
var INTEGRATION_MANIFEST_FILENAME = "integrations-manifest.json";
|
|
833
|
+
var parseIntegrationManifest = (json) => IntegrationManifestSchema.parse(JSON.parse(json));
|
|
834
|
+
var serializeIntegrationManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
835
|
+
var resolveIntegrationManifestPath = (userBasePath) => join7(userBasePath, ".ai-ops", INTEGRATION_MANIFEST_FILENAME);
|
|
836
|
+
var readIntegrationManifest = (manifestPath) => {
|
|
837
|
+
let raw;
|
|
838
|
+
try {
|
|
839
|
+
raw = readFileSync5(manifestPath, "utf-8");
|
|
840
|
+
} catch {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
return parseIntegrationManifest(raw);
|
|
844
|
+
};
|
|
845
|
+
var writeIntegrationManifest = (manifestPath, manifest) => {
|
|
846
|
+
mkdirSync3(dirname4(manifestPath), { recursive: true });
|
|
847
|
+
writeFileSync3(manifestPath, serializeIntegrationManifest(manifest), "utf-8");
|
|
848
|
+
};
|
|
849
|
+
var findInstalledIntegration = (integrations, integrationId) => integrations.find((integration) => integration.id === integrationId);
|
|
850
|
+
var upsertInstalledIntegration = (integrations, nextIntegration) => [
|
|
851
|
+
...integrations.filter((integration) => integration.id !== nextIntegration.id),
|
|
852
|
+
nextIntegration
|
|
853
|
+
];
|
|
854
|
+
var removeInstalledIntegration = (integrations, integrationId) => integrations.filter((integration) => integration.id !== integrationId);
|
|
855
|
+
var writeUserIntegrationState = (params) => {
|
|
856
|
+
const previous = readIntegrationManifest(params.manifestPath);
|
|
857
|
+
const integrations = params.removeIntegrationId ? removeInstalledIntegration(previous?.integrations ?? [], params.removeIntegrationId) : params.nextIntegration ? upsertInstalledIntegration(previous?.integrations ?? [], params.nextIntegration) : previous?.integrations ?? [];
|
|
858
|
+
if (integrations.length === 0) {
|
|
859
|
+
rmSync(params.manifestPath, { force: true });
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
writeIntegrationManifest(params.manifestPath, {
|
|
863
|
+
schemaVersion: 1,
|
|
864
|
+
kind: "ai-ops-integrations-manifest",
|
|
865
|
+
integrations,
|
|
866
|
+
cliVersion: params.cliVersion,
|
|
867
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
868
|
+
});
|
|
869
|
+
};
|
|
789
870
|
|
|
790
871
|
// src/core/project-layer.ts
|
|
791
|
-
import { existsSync, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync3, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
792
|
-
import { dirname as dirname6, isAbsolute, join as
|
|
872
|
+
import { existsSync, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
873
|
+
import { dirname as dirname6, isAbsolute, join as join8, relative, resolve as resolve5 } from "path";
|
|
793
874
|
|
|
794
875
|
// src/core/paths.ts
|
|
795
876
|
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
@@ -800,7 +881,7 @@ var COMPILER_DATA_DIR = resolve4(__dirname2, "..", "..", "data");
|
|
|
800
881
|
// src/core/project-layer.ts
|
|
801
882
|
var PROJECT_LAYER_MANIFEST_RELATIVE_PATH = ".ai-ops/manifest.json";
|
|
802
883
|
var PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH = ".ai-ops/context-layer.json";
|
|
803
|
-
var CONTEXT_LAYER_DATA_DIR =
|
|
884
|
+
var CONTEXT_LAYER_DATA_DIR = join8(COMPILER_DATA_DIR, "context-layer");
|
|
804
885
|
var TOOL_ORDER = ["codex", "gemini", "claude-code"];
|
|
805
886
|
var DEFAULT_TOOLS = TOOL_ORDER;
|
|
806
887
|
var TEMPLATE_PATHS = [
|
|
@@ -813,7 +894,6 @@ var TEMPLATE_PATHS = [
|
|
|
813
894
|
"docs/agent/rules/doc-update-rules.md",
|
|
814
895
|
"docs/agent/rules/stop-rules.md",
|
|
815
896
|
"docs/agent/checks/impact-checklist.md",
|
|
816
|
-
"docs/agent/checks/review-checklist.md",
|
|
817
897
|
"docs/agent/maps/codebase-map.md",
|
|
818
898
|
"docs/business/business-rules.md",
|
|
819
899
|
"docs/docs-status.md"
|
|
@@ -827,9 +907,9 @@ var RESERVED_DOCUMENT_WARNINGS = [
|
|
|
827
907
|
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
828
908
|
"Do not use this document as current decision-making evidence"
|
|
829
909
|
];
|
|
830
|
-
var resolveProjectLayerManifestPath = (basePath) =>
|
|
831
|
-
var resolveProjectLayerContextIndexPath = (basePath) =>
|
|
832
|
-
var resolveTemplatePath = (relativePath) =>
|
|
910
|
+
var resolveProjectLayerManifestPath = (basePath) => join8(basePath, PROJECT_LAYER_MANIFEST_RELATIVE_PATH);
|
|
911
|
+
var resolveProjectLayerContextIndexPath = (basePath) => join8(basePath, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH);
|
|
912
|
+
var resolveTemplatePath = (relativePath) => join8(CONTEXT_LAYER_DATA_DIR, relativePath);
|
|
833
913
|
var toRelativeDir = (relativePath) => dirname6(relativePath);
|
|
834
914
|
var resolveProjectLayerFilePath = (basePath, relativePath) => {
|
|
835
915
|
if (!isSafeProjectLayerPath(relativePath)) {
|
|
@@ -1441,7 +1521,7 @@ function removeManagedProjectFile(basePath, relativePath) {
|
|
|
1441
1521
|
}
|
|
1442
1522
|
const stripped = stripAiOpsSection(content);
|
|
1443
1523
|
if (stripped.trim().length === 0) {
|
|
1444
|
-
|
|
1524
|
+
rmSync2(absolutePath);
|
|
1445
1525
|
return { deleted: [relativePath], cleaned: [], preserved: [], notFound: [] };
|
|
1446
1526
|
}
|
|
1447
1527
|
writeFileSync4(absolutePath, stripped, "utf-8");
|
|
@@ -1455,7 +1535,7 @@ var removeCreateOnlyProjectFile = (basePath, file) => {
|
|
|
1455
1535
|
const content = readFileSync6(absolutePath, "utf-8").trimEnd();
|
|
1456
1536
|
const currentHash = computeHash([content]);
|
|
1457
1537
|
if (file.created && currentHash === file.templateHash) {
|
|
1458
|
-
|
|
1538
|
+
rmSync2(absolutePath);
|
|
1459
1539
|
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1460
1540
|
}
|
|
1461
1541
|
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
@@ -1467,7 +1547,7 @@ var removePackOwnedFile = (basePath, file) => {
|
|
|
1467
1547
|
}
|
|
1468
1548
|
const currentHash = computeHash([readFileSync6(absolutePath, "utf-8").trimEnd()]);
|
|
1469
1549
|
if (currentHash === file.sourceHash) {
|
|
1470
|
-
|
|
1550
|
+
rmSync2(absolutePath);
|
|
1471
1551
|
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1472
1552
|
}
|
|
1473
1553
|
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
@@ -1487,7 +1567,7 @@ var removeEmptyDirs = (basePath, relativePaths) => {
|
|
|
1487
1567
|
if (!existsSync(absoluteDir)) continue;
|
|
1488
1568
|
try {
|
|
1489
1569
|
if (readdirSync3(absoluteDir).length === 0) {
|
|
1490
|
-
|
|
1570
|
+
rmSync2(absoluteDir, { recursive: true });
|
|
1491
1571
|
}
|
|
1492
1572
|
} catch {
|
|
1493
1573
|
}
|
|
@@ -1501,7 +1581,7 @@ var uninstallProjectLayer = (basePath, manifest) => {
|
|
|
1501
1581
|
);
|
|
1502
1582
|
const stateFiles = [PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH, PROJECT_LAYER_MANIFEST_RELATIVE_PATH];
|
|
1503
1583
|
for (const stateFile of stateFiles) {
|
|
1504
|
-
|
|
1584
|
+
rmSync2(resolveProjectLayerFilePath(basePath, stateFile), { force: true });
|
|
1505
1585
|
}
|
|
1506
1586
|
const result = mergeRemoveResults([...managedResults, ...projectResults, ...packResults]);
|
|
1507
1587
|
removeEmptyDirs(basePath, [...result.deleted, ...stateFiles]);
|
|
@@ -1509,8 +1589,8 @@ var uninstallProjectLayer = (basePath, manifest) => {
|
|
|
1509
1589
|
};
|
|
1510
1590
|
|
|
1511
1591
|
// src/core/pack.ts
|
|
1512
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync4, rmSync as
|
|
1513
|
-
import { dirname as dirname7, isAbsolute as isAbsolute2, join as
|
|
1592
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync4, rmSync as rmSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
1593
|
+
import { dirname as dirname7, isAbsolute as isAbsolute2, join as join9, relative as relative2, resolve as resolve6 } from "path";
|
|
1514
1594
|
var PACK_REGISTRY_FILENAME = "pack-registry.json";
|
|
1515
1595
|
var SPEC_LIFECYCLE_PACK_ID = "spec-lifecycle";
|
|
1516
1596
|
var PACK_INSTALL_ROOT = "docs/specs/";
|
|
@@ -1518,9 +1598,9 @@ var RESERVED_DOCUMENT_WARNINGS2 = [
|
|
|
1518
1598
|
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
1519
1599
|
"Do not use this document as current decision-making evidence"
|
|
1520
1600
|
];
|
|
1521
|
-
var DEFAULT_PACKS_DIR =
|
|
1601
|
+
var DEFAULT_PACKS_DIR = join9(COMPILER_DATA_DIR, "packs");
|
|
1522
1602
|
var includesReservedDocumentWarning2 = (content) => RESERVED_DOCUMENT_WARNINGS2.some((warning) => content.includes(warning));
|
|
1523
|
-
var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync7(
|
|
1603
|
+
var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync7(join9(packsDir, PACK_REGISTRY_FILENAME), "utf-8")));
|
|
1524
1604
|
var assertPackInstallPath = (path) => {
|
|
1525
1605
|
if (!isSafeProjectLayerPath(path) || !path.startsWith(PACK_INSTALL_ROOT)) {
|
|
1526
1606
|
throw new Error(`Unsafe pack path: ${path}`);
|
|
@@ -1529,16 +1609,16 @@ var assertPackInstallPath = (path) => {
|
|
|
1529
1609
|
var readPackSourceFiles = (packDir) => {
|
|
1530
1610
|
const files = [];
|
|
1531
1611
|
const walk = (relativeDir = "") => {
|
|
1532
|
-
const absoluteDir = relativeDir.length > 0 ?
|
|
1612
|
+
const absoluteDir = relativeDir.length > 0 ? join9(packDir, relativeDir) : packDir;
|
|
1533
1613
|
const entries = readdirSync4(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1534
1614
|
for (const entry of entries) {
|
|
1535
|
-
const nextRelativePath = relativeDir.length > 0 ?
|
|
1615
|
+
const nextRelativePath = relativeDir.length > 0 ? join9(relativeDir, entry.name) : entry.name;
|
|
1536
1616
|
if (entry.isDirectory()) {
|
|
1537
1617
|
walk(nextRelativePath);
|
|
1538
1618
|
continue;
|
|
1539
1619
|
}
|
|
1540
1620
|
assertPackInstallPath(nextRelativePath);
|
|
1541
|
-
const content = readFileSync7(
|
|
1621
|
+
const content = readFileSync7(join9(packDir, nextRelativePath), "utf-8");
|
|
1542
1622
|
files.push({
|
|
1543
1623
|
path: nextRelativePath,
|
|
1544
1624
|
content,
|
|
@@ -1659,7 +1739,7 @@ var applyPackSourceFiles = (params) => {
|
|
|
1659
1739
|
continue;
|
|
1660
1740
|
}
|
|
1661
1741
|
if (readProjectFileHash(params.basePath, previous.path) === previous.sourceHash) {
|
|
1662
|
-
|
|
1742
|
+
rmSync3(absolutePath);
|
|
1663
1743
|
deleted.push(previous.path);
|
|
1664
1744
|
} else {
|
|
1665
1745
|
preserved.push(previous.path);
|
|
@@ -1678,7 +1758,7 @@ var removePackFiles = (basePath, record) => {
|
|
|
1678
1758
|
continue;
|
|
1679
1759
|
}
|
|
1680
1760
|
if (readProjectFileHash(basePath, file.path) === file.sourceHash) {
|
|
1681
|
-
|
|
1761
|
+
rmSync3(absolutePath);
|
|
1682
1762
|
deleted.push(file.path);
|
|
1683
1763
|
} else {
|
|
1684
1764
|
preserved.push(file.path);
|
|
@@ -1697,7 +1777,7 @@ var removeEmptyDirs2 = (basePath, relativePaths) => {
|
|
|
1697
1777
|
}
|
|
1698
1778
|
try {
|
|
1699
1779
|
if (readdirSync4(absoluteDir).length === 0) {
|
|
1700
|
-
|
|
1780
|
+
rmSync3(absoluteDir, { recursive: true });
|
|
1701
1781
|
}
|
|
1702
1782
|
} catch {
|
|
1703
1783
|
}
|
|
@@ -1816,99 +1896,315 @@ var diffProjectLayerPack = (params) => {
|
|
|
1816
1896
|
return { issues };
|
|
1817
1897
|
};
|
|
1818
1898
|
|
|
1819
|
-
// src/core/uninstall-plan.ts
|
|
1820
|
-
import { join as join12 } from "path";
|
|
1821
|
-
|
|
1822
1899
|
// src/core/context-promotion.ts
|
|
1823
1900
|
import { createHash as createHash2 } from "crypto";
|
|
1824
1901
|
import { execFileSync } from "child_process";
|
|
1825
1902
|
import { existsSync as existsSync3, mkdirSync as mkdirSync6, readFileSync as readFileSync8, statSync, writeFileSync as writeFileSync6 } from "fs";
|
|
1826
|
-
import { dirname as dirname8, join as
|
|
1903
|
+
import { dirname as dirname8, join as join10, resolve as resolve7 } from "path";
|
|
1827
1904
|
import { z as z12 } from "zod";
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
var
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1905
|
+
|
|
1906
|
+
// src/core/tool-use-hook.ts
|
|
1907
|
+
import { z as z11 } from "zod";
|
|
1908
|
+
var HookToolInputSchema = z11.object({
|
|
1909
|
+
command: z11.string().optional()
|
|
1910
|
+
}).passthrough();
|
|
1911
|
+
var ToolUseHookInputSchema = z11.object({
|
|
1912
|
+
hook_event_name: z11.string(),
|
|
1913
|
+
cwd: z11.string(),
|
|
1914
|
+
tool_name: z11.string().optional(),
|
|
1915
|
+
tool_input: z11.unknown().optional(),
|
|
1916
|
+
tool_response: z11.unknown().optional()
|
|
1917
|
+
}).passthrough();
|
|
1918
|
+
var SHELL_CONTROL_TOKENS = /* @__PURE__ */ new Set(["&&", "||", ";", "|", "(", ")"]);
|
|
1919
|
+
var SHELL_SCRIPT_FLAGS = /* @__PURE__ */ new Set(["-c", "-lc"]);
|
|
1920
|
+
var GIT_GLOBAL_OPTIONS_WITH_VALUE = /* @__PURE__ */ new Set([
|
|
1921
|
+
"-C",
|
|
1922
|
+
"-c",
|
|
1923
|
+
"--git-dir",
|
|
1924
|
+
"--work-tree",
|
|
1925
|
+
"--namespace",
|
|
1926
|
+
"--config-env",
|
|
1927
|
+
"--exec-path"
|
|
1845
1928
|
]);
|
|
1846
|
-
var
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1929
|
+
var basename = (token) => token.replace(/\\/g, "/").split("/").at(-1) ?? token;
|
|
1930
|
+
var isAssignmentToken = (token) => /^[A-Za-z_][A-Za-z0-9_]*=/.test(token);
|
|
1931
|
+
var tokenizeShellLike = (command) => {
|
|
1932
|
+
const tokens = [];
|
|
1933
|
+
let current = "";
|
|
1934
|
+
let quote = null;
|
|
1935
|
+
const pushCurrent = () => {
|
|
1936
|
+
if (current.length > 0) {
|
|
1937
|
+
tokens.push(current);
|
|
1938
|
+
current = "";
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
1942
|
+
const char = command[index];
|
|
1943
|
+
const nextChar = command[index + 1];
|
|
1944
|
+
if (quote) {
|
|
1945
|
+
if (char === quote) {
|
|
1946
|
+
quote = null;
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
current += char;
|
|
1950
|
+
continue;
|
|
1951
|
+
}
|
|
1952
|
+
if (char === '"' || char === "'") {
|
|
1953
|
+
quote = char;
|
|
1954
|
+
continue;
|
|
1955
|
+
}
|
|
1956
|
+
if (/\s/.test(char)) {
|
|
1957
|
+
pushCurrent();
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
if (char === "&" && nextChar === "&" || char === "|" && nextChar === "|") {
|
|
1961
|
+
pushCurrent();
|
|
1962
|
+
tokens.push(`${char}${nextChar}`);
|
|
1963
|
+
index += 1;
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
if (char === ";" || char === "|" || char === "(" || char === ")") {
|
|
1967
|
+
pushCurrent();
|
|
1968
|
+
tokens.push(char);
|
|
1969
|
+
continue;
|
|
1970
|
+
}
|
|
1971
|
+
current += char;
|
|
1877
1972
|
}
|
|
1973
|
+
pushCurrent();
|
|
1974
|
+
return tokens;
|
|
1878
1975
|
};
|
|
1879
|
-
var
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1976
|
+
var splitCommandSegments = (tokens) => {
|
|
1977
|
+
const segments = [];
|
|
1978
|
+
let current = [];
|
|
1979
|
+
for (const token of tokens) {
|
|
1980
|
+
if (SHELL_CONTROL_TOKENS.has(token)) {
|
|
1981
|
+
if (current.length > 0) {
|
|
1982
|
+
segments.push(current);
|
|
1983
|
+
current = [];
|
|
1984
|
+
}
|
|
1985
|
+
continue;
|
|
1986
|
+
}
|
|
1987
|
+
current.push(token);
|
|
1884
1988
|
}
|
|
1989
|
+
if (current.length > 0) {
|
|
1990
|
+
segments.push(current);
|
|
1991
|
+
}
|
|
1992
|
+
return segments;
|
|
1885
1993
|
};
|
|
1886
|
-
var
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
}
|
|
1896
|
-
const content = readFileSync8(absolutePath);
|
|
1897
|
-
return `${relativePath}:${createHash2("sha256").update(content).digest("hex")}`;
|
|
1898
|
-
} catch {
|
|
1899
|
-
throw new Error(`Unable to read untracked path for context promotion fingerprint: ${relativePath}`);
|
|
1994
|
+
var firstExecutableIndex = (segment) => {
|
|
1995
|
+
let index = 0;
|
|
1996
|
+
while (index < segment.length && isAssignmentToken(segment[index])) {
|
|
1997
|
+
index += 1;
|
|
1998
|
+
}
|
|
1999
|
+
if (segment[index] === "env") {
|
|
2000
|
+
index += 1;
|
|
2001
|
+
while (index < segment.length && isAssignmentToken(segment[index])) {
|
|
2002
|
+
index += 1;
|
|
1900
2003
|
}
|
|
1901
|
-
}
|
|
2004
|
+
}
|
|
2005
|
+
if (segment[index] === "command" || segment[index] === "sudo") {
|
|
2006
|
+
index += 1;
|
|
2007
|
+
}
|
|
2008
|
+
return index;
|
|
1902
2009
|
};
|
|
1903
|
-
var
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2010
|
+
var segmentInvokesGitCommit = (segment) => {
|
|
2011
|
+
const executableIndex = firstExecutableIndex(segment);
|
|
2012
|
+
if (executableIndex >= segment.length || basename(segment[executableIndex]) !== "git") {
|
|
2013
|
+
return false;
|
|
2014
|
+
}
|
|
2015
|
+
for (let index = executableIndex + 1; index < segment.length; index += 1) {
|
|
2016
|
+
const token = segment[index];
|
|
2017
|
+
if (GIT_GLOBAL_OPTIONS_WITH_VALUE.has(token)) {
|
|
2018
|
+
index += 1;
|
|
2019
|
+
continue;
|
|
2020
|
+
}
|
|
2021
|
+
if (token.startsWith("-")) {
|
|
2022
|
+
continue;
|
|
2023
|
+
}
|
|
2024
|
+
return token === "commit";
|
|
2025
|
+
}
|
|
2026
|
+
return false;
|
|
2027
|
+
};
|
|
2028
|
+
var segmentInvokesShellScriptWithGitCommit = (segment) => {
|
|
2029
|
+
const executableIndex = firstExecutableIndex(segment);
|
|
2030
|
+
const executable = segment[executableIndex];
|
|
2031
|
+
if (!executable || !["bash", "sh", "zsh"].includes(basename(executable))) {
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
for (let index = executableIndex + 1; index < segment.length - 1; index += 1) {
|
|
2035
|
+
if (SHELL_SCRIPT_FLAGS.has(segment[index]) && isGitCommitCommand(segment[index + 1])) {
|
|
2036
|
+
return true;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return false;
|
|
2040
|
+
};
|
|
2041
|
+
var isGitCommitCommand = (command) => {
|
|
2042
|
+
const segments = splitCommandSegments(tokenizeShellLike(command));
|
|
2043
|
+
return segments.some(
|
|
2044
|
+
(segment) => segmentInvokesGitCommit(segment) || segmentInvokesShellScriptWithGitCommit(segment)
|
|
2045
|
+
);
|
|
2046
|
+
};
|
|
2047
|
+
var isJsonRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2048
|
+
var numberField = (record, keys) => {
|
|
2049
|
+
for (const key of keys) {
|
|
2050
|
+
const value = record[key];
|
|
2051
|
+
if (typeof value === "number") {
|
|
2052
|
+
return value;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
return null;
|
|
2056
|
+
};
|
|
2057
|
+
var booleanField = (record, keys) => {
|
|
2058
|
+
for (const key of keys) {
|
|
2059
|
+
const value = record[key];
|
|
2060
|
+
if (typeof value === "boolean") {
|
|
2061
|
+
return value;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
return null;
|
|
2065
|
+
};
|
|
2066
|
+
var GIT_COMMIT_FAILURE_OUTPUT_PATTERNS = [
|
|
2067
|
+
/(^|\n)\s*fatal:/i,
|
|
2068
|
+
/(^|\n)\s*error:/i,
|
|
2069
|
+
/(^|\n)\s*nothing to commit\b/i,
|
|
2070
|
+
/(^|\n)\s*no changes added to commit\b/i,
|
|
2071
|
+
/(^|\n).*aborting commit\b/i,
|
|
2072
|
+
/(^|\n).*commit failed\b/i,
|
|
2073
|
+
/(^|\n).*failed to .*commit\b/i,
|
|
2074
|
+
/(^|\n).*command failed\b/i,
|
|
2075
|
+
/(^|\n).*non-zero exit\b/i,
|
|
2076
|
+
/(^|\n).*exit (code|status)\s+[1-9]\d*\b/i,
|
|
2077
|
+
/(^|\n).*exited with code\s+[1-9]\d*\b/i,
|
|
2078
|
+
/(^|\n).*hook.*(failed|declined|error|exit(?:ed)? with code|non-zero)/i
|
|
2079
|
+
];
|
|
2080
|
+
var GIT_COMMIT_SUCCESS_OUTPUT_PATTERN = /(^|\n)\[[^\]\n]+ [a-f0-9]{7,40}\]/i;
|
|
2081
|
+
var stringIndicatesGitCommitSuccess = (output) => GIT_COMMIT_SUCCESS_OUTPUT_PATTERN.test(output);
|
|
2082
|
+
var stringIndicatesGitCommitFailureOrSuccess = (output) => stringIndicatesGitCommitSuccess(output) ? false : GIT_COMMIT_FAILURE_OUTPUT_PATTERNS.some((pattern) => pattern.test(output)) ? true : null;
|
|
2083
|
+
var recordStringFieldsIndicateGitCommitFailure = (record) => ["message", "output", "stdout", "stderr", "error", "combinedOutput"].some((key) => {
|
|
2084
|
+
const value = record[key];
|
|
2085
|
+
return typeof value === "string" && stringIndicatesGitCommitFailureOrSuccess(value) === true;
|
|
2086
|
+
});
|
|
2087
|
+
var toolResponseIndicatesFailure = (toolResponse) => {
|
|
2088
|
+
if (typeof toolResponse === "string") {
|
|
2089
|
+
return stringIndicatesGitCommitFailureOrSuccess(toolResponse) === true;
|
|
2090
|
+
}
|
|
2091
|
+
if (!isJsonRecord(toolResponse)) {
|
|
2092
|
+
return false;
|
|
2093
|
+
}
|
|
2094
|
+
const success = booleanField(toolResponse, ["success", "ok"]);
|
|
2095
|
+
if (success === false) {
|
|
2096
|
+
return true;
|
|
2097
|
+
}
|
|
2098
|
+
const exitCode = numberField(toolResponse, ["exit_code", "exitCode", "status", "code"]);
|
|
2099
|
+
if (exitCode !== null && exitCode !== 0) {
|
|
2100
|
+
return true;
|
|
2101
|
+
}
|
|
2102
|
+
return recordStringFieldsIndicateGitCommitFailure(toolResponse);
|
|
2103
|
+
};
|
|
2104
|
+
var parseSuccessfulGitCommitPostToolUseHook = (hookInput) => {
|
|
2105
|
+
const parsed = ToolUseHookInputSchema.safeParse(hookInput);
|
|
2106
|
+
if (!parsed.success) {
|
|
2107
|
+
return null;
|
|
2108
|
+
}
|
|
2109
|
+
if (parsed.data.hook_event_name !== "PostToolUse" || parsed.data.tool_name !== "Bash") {
|
|
2110
|
+
return null;
|
|
2111
|
+
}
|
|
2112
|
+
const toolInput = HookToolInputSchema.safeParse(parsed.data.tool_input);
|
|
2113
|
+
const command = toolInput.success ? toolInput.data.command ?? "" : "";
|
|
2114
|
+
if (!isGitCommitCommand(command) || toolResponseIndicatesFailure(parsed.data.tool_response)) {
|
|
2115
|
+
return null;
|
|
2116
|
+
}
|
|
2117
|
+
return {
|
|
2118
|
+
cwd: parsed.data.cwd,
|
|
2119
|
+
command
|
|
2120
|
+
};
|
|
2121
|
+
};
|
|
2122
|
+
|
|
2123
|
+
// src/core/context-promotion.ts
|
|
2124
|
+
var CONTEXT_PROMOTION_DECISION = {
|
|
2125
|
+
PROMOTED: "promoted",
|
|
2126
|
+
NO_PROMOTION: "no-promotion"
|
|
2127
|
+
};
|
|
2128
|
+
var CONTEXT_PROMOTION_SCOPE = {
|
|
2129
|
+
CORE: "core",
|
|
2130
|
+
PROJECT_LOCAL: "project-local",
|
|
2131
|
+
GLOBAL: "global"
|
|
2132
|
+
};
|
|
2133
|
+
var ContextPromotionDecisionSchema = z12.union([
|
|
2134
|
+
z12.literal(CONTEXT_PROMOTION_DECISION.PROMOTED),
|
|
2135
|
+
z12.literal(CONTEXT_PROMOTION_DECISION.NO_PROMOTION)
|
|
2136
|
+
]);
|
|
2137
|
+
var ContextPromotionScopeSchema = z12.union([
|
|
2138
|
+
z12.literal(CONTEXT_PROMOTION_SCOPE.CORE),
|
|
2139
|
+
z12.literal(CONTEXT_PROMOTION_SCOPE.PROJECT_LOCAL),
|
|
2140
|
+
z12.literal(CONTEXT_PROMOTION_SCOPE.GLOBAL)
|
|
2141
|
+
]);
|
|
2142
|
+
var ContextPromotionReceiptSchema = z12.object({
|
|
2143
|
+
fingerprint: z12.string().regex(/^[a-f0-9]{16}$/),
|
|
2144
|
+
commitHash: z12.string().regex(/^(NO_HEAD|[a-f0-9]{40})$/).optional(),
|
|
2145
|
+
decision: ContextPromotionDecisionSchema,
|
|
2146
|
+
scopes: z12.array(ContextPromotionScopeSchema),
|
|
2147
|
+
targets: z12.array(z12.string().min(1)),
|
|
2148
|
+
summary: z12.string().min(1),
|
|
2149
|
+
resolvedAt: z12.string().min(1)
|
|
2150
|
+
}).strict();
|
|
2151
|
+
var ContextPromotionReceiptIndexSchema = z12.object({
|
|
2152
|
+
schemaVersion: z12.literal(1),
|
|
2153
|
+
kind: z12.literal("context-promotion-receipts"),
|
|
2154
|
+
projectKey: z12.string().regex(/^[a-f0-9]{12}$/),
|
|
2155
|
+
projectRoot: z12.string().min(1),
|
|
2156
|
+
receipts: z12.array(ContextPromotionReceiptSchema)
|
|
2157
|
+
}).strict();
|
|
2158
|
+
var RECEIPT_INDEX_FILENAME = "receipts-index.json";
|
|
2159
|
+
var DEFAULT_PRUNE_MAX = 50;
|
|
2160
|
+
var hashHex = (parts, length) => createHash2("sha256").update(parts.join("\0")).digest("hex").slice(0, length);
|
|
2161
|
+
var buildContextPromotionProjectKey = (gitRoot) => hashHex([resolve7(gitRoot)], 12);
|
|
2162
|
+
var runGit = (cwd, args) => execFileSync("git", [...args], {
|
|
2163
|
+
cwd,
|
|
2164
|
+
encoding: "utf-8",
|
|
2165
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
2166
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2167
|
+
});
|
|
2168
|
+
var resolveContextPromotionGitRoot = (cwd) => {
|
|
2169
|
+
try {
|
|
2170
|
+
return runGit(cwd, ["rev-parse", "--show-toplevel"]).trim();
|
|
2171
|
+
} catch {
|
|
2172
|
+
return null;
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
var readGitHead = (gitRoot) => {
|
|
2176
|
+
try {
|
|
2177
|
+
return runGit(gitRoot, ["rev-parse", "--verify", "HEAD"]).trim();
|
|
2178
|
+
} catch {
|
|
2179
|
+
return "NO_HEAD";
|
|
2180
|
+
}
|
|
2181
|
+
};
|
|
2182
|
+
var readUntrackedFingerprintParts = (gitRoot) => {
|
|
2183
|
+
const raw = runGit(gitRoot, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
2184
|
+
const paths = raw.split("\0").filter((path) => path.length > 0).sort((a, b) => a.localeCompare(b));
|
|
2185
|
+
return paths.map((relativePath) => {
|
|
2186
|
+
const absolutePath = join10(gitRoot, relativePath);
|
|
2187
|
+
try {
|
|
2188
|
+
const stat = statSync(absolutePath);
|
|
2189
|
+
if (!stat.isFile()) {
|
|
2190
|
+
return `${relativePath}:non-file`;
|
|
2191
|
+
}
|
|
2192
|
+
const content = readFileSync8(absolutePath);
|
|
2193
|
+
return `${relativePath}:${createHash2("sha256").update(content).digest("hex")}`;
|
|
2194
|
+
} catch {
|
|
2195
|
+
throw new Error(`Unable to read untracked path for context promotion fingerprint: ${relativePath}`);
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
};
|
|
2199
|
+
var readTrackedWorkingTreeFingerprintParts = (gitRoot) => {
|
|
2200
|
+
const rawDiff = runGit(gitRoot, ["diff", "--raw", "-z"]);
|
|
2201
|
+
const rawNames = runGit(gitRoot, ["diff", "--name-only", "-z"]);
|
|
2202
|
+
const paths = rawNames.split("\0").filter((path) => path.length > 0).sort((a, b) => a.localeCompare(b));
|
|
2203
|
+
return [
|
|
2204
|
+
`raw:${rawDiff}`,
|
|
2205
|
+
...paths.map((relativePath) => {
|
|
2206
|
+
const absolutePath = join10(gitRoot, relativePath);
|
|
2207
|
+
if (!existsSync3(absolutePath)) {
|
|
1912
2208
|
return `${relativePath}:deleted`;
|
|
1913
2209
|
}
|
|
1914
2210
|
const stat = statSync(absolutePath);
|
|
@@ -1933,14 +2229,7 @@ var computeContextPromotionFingerprint = (gitRoot) => hashHex(
|
|
|
1933
2229
|
],
|
|
1934
2230
|
16
|
|
1935
2231
|
);
|
|
1936
|
-
var resolveContextPromotionReceiptIndexPath = (params) =>
|
|
1937
|
-
params.userBasePath,
|
|
1938
|
-
".ai-ops",
|
|
1939
|
-
"context-promotion",
|
|
1940
|
-
"projects",
|
|
1941
|
-
params.projectKey,
|
|
1942
|
-
RECEIPT_INDEX_FILENAME
|
|
1943
|
-
);
|
|
2232
|
+
var resolveContextPromotionReceiptIndexPath = (params) => join10(params.userBasePath, ".ai-ops", "context-promotion", "projects", params.projectKey, RECEIPT_INDEX_FILENAME);
|
|
1944
2233
|
var parseContextPromotionReceiptIndex = (json) => ContextPromotionReceiptIndexSchema.parse(JSON.parse(json));
|
|
1945
2234
|
var serializeContextPromotionReceiptIndex = (index) => JSON.stringify(index, null, 2) + "\n";
|
|
1946
2235
|
var readContextPromotionReceiptIndex = (indexPath) => {
|
|
@@ -2000,7 +2289,7 @@ var pruneContextPromotionReceipts = (params) => {
|
|
|
2000
2289
|
writeContextPromotionReceiptIndex(params.indexPath, nextIndex);
|
|
2001
2290
|
return nextIndex;
|
|
2002
2291
|
};
|
|
2003
|
-
var hasContextPromotionLayer = (gitRoot) => existsSync3(
|
|
2292
|
+
var hasContextPromotionLayer = (gitRoot) => existsSync3(join10(gitRoot, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH));
|
|
2004
2293
|
var getContextPromotionStatus = (params) => {
|
|
2005
2294
|
const cwd = resolve7(params.cwd);
|
|
2006
2295
|
const gitRoot = resolveContextPromotionGitRoot(cwd);
|
|
@@ -2087,143 +2376,6 @@ var resolveContextPromotion = (params) => {
|
|
|
2087
2376
|
});
|
|
2088
2377
|
return getContextPromotionStatus({ cwd: params.cwd, userBasePath: params.userBasePath });
|
|
2089
2378
|
};
|
|
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
2379
|
var buildContextPromotionReviewPrompt = (status) => {
|
|
2228
2380
|
const projectRoot = status.gitRoot ?? status.cwd;
|
|
2229
2381
|
const cdCommand = `cd ${JSON.stringify(projectRoot)}`;
|
|
@@ -2279,107 +2431,388 @@ var buildPostToolUseOutput = (prompt) => ({
|
|
|
2279
2431
|
additionalContext: prompt
|
|
2280
2432
|
}
|
|
2281
2433
|
});
|
|
2282
|
-
var
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2434
|
+
var evaluateContextPromotionPostToolUseHook = (params) => {
|
|
2435
|
+
const hookInput = parseSuccessfulGitCommitPostToolUseHook(params.hookInput);
|
|
2436
|
+
if (!hookInput) {
|
|
2437
|
+
return null;
|
|
2438
|
+
}
|
|
2439
|
+
let status;
|
|
2440
|
+
try {
|
|
2441
|
+
status = getContextPromotionStatus({
|
|
2442
|
+
cwd: hookInput.cwd,
|
|
2443
|
+
userBasePath: params.userBasePath
|
|
2444
|
+
});
|
|
2445
|
+
} catch (error) {
|
|
2446
|
+
return buildPostToolUseOutput(buildContextPromotionStatusFailurePrompt(hookInput.cwd, error));
|
|
2447
|
+
}
|
|
2448
|
+
if (!status.hasContextLayer || status.receipt) {
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
return buildPostToolUseOutput(buildContextPromotionReviewPrompt(status));
|
|
2452
|
+
};
|
|
2453
|
+
|
|
2454
|
+
// src/core/pc-integration.ts
|
|
2455
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
2456
|
+
import { existsSync as existsSync4, readFileSync as readFileSync9, readdirSync as readdirSync5 } from "fs";
|
|
2457
|
+
import { join as join11, resolve as resolve8, sep } from "path";
|
|
2458
|
+
var normalizePath = (path) => resolve8(path.replace(/^~(?=$|\/)/, process.env.HOME ?? "~"));
|
|
2459
|
+
var pathContains = (parentPath, childPath) => {
|
|
2460
|
+
const parent = normalizePath(parentPath);
|
|
2461
|
+
const child = normalizePath(childPath);
|
|
2462
|
+
return child === parent || child.startsWith(`${parent}${sep}`);
|
|
2463
|
+
};
|
|
2464
|
+
var normalizeFieldValue = (value) => {
|
|
2465
|
+
const trimmed = value?.trim() ?? "";
|
|
2466
|
+
if (trimmed.length === 0 || ["none", "null", "-", "<empty>"].includes(trimmed.toLowerCase())) {
|
|
2467
|
+
return null;
|
|
2468
|
+
}
|
|
2469
|
+
return trimmed.replace(/^`|`$/g, "");
|
|
2470
|
+
};
|
|
2471
|
+
var extractSection = (content, headings) => {
|
|
2472
|
+
const headingPattern = headings.map((heading) => heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
2473
|
+
const match = new RegExp(`^##\\s+(?:${headingPattern})\\s*$`, "mu").exec(content);
|
|
2474
|
+
if (!match) {
|
|
2475
|
+
return "";
|
|
2476
|
+
}
|
|
2477
|
+
const start = match.index + match[0].length;
|
|
2478
|
+
const rest = content.slice(start);
|
|
2479
|
+
const nextHeading = /^##\s+/mu.exec(rest);
|
|
2480
|
+
return nextHeading ? rest.slice(0, nextHeading.index) : rest;
|
|
2481
|
+
};
|
|
2482
|
+
var parseListField = (content, labels) => {
|
|
2483
|
+
for (const label of labels) {
|
|
2484
|
+
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2485
|
+
const match = new RegExp(`^-\\s+${escapedLabel}:\\s*(.+)$`, "mu").exec(content);
|
|
2486
|
+
const value = normalizeFieldValue(match?.[1] ?? null);
|
|
2487
|
+
if (value) {
|
|
2287
2488
|
return value;
|
|
2288
2489
|
}
|
|
2289
2490
|
}
|
|
2290
|
-
return null;
|
|
2491
|
+
return null;
|
|
2492
|
+
};
|
|
2493
|
+
var readTextFileOrNull = (filePath) => {
|
|
2494
|
+
try {
|
|
2495
|
+
return readFileSync9(filePath, "utf-8");
|
|
2496
|
+
} catch {
|
|
2497
|
+
return null;
|
|
2498
|
+
}
|
|
2499
|
+
};
|
|
2500
|
+
var runGit2 = (cwd, args) => execFileSync2("git", [...args], {
|
|
2501
|
+
cwd,
|
|
2502
|
+
encoding: "utf-8",
|
|
2503
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2504
|
+
}).trim();
|
|
2505
|
+
var resolveGitRoot = (cwd) => {
|
|
2506
|
+
try {
|
|
2507
|
+
return runGit2(cwd, ["rev-parse", "--show-toplevel"]);
|
|
2508
|
+
} catch {
|
|
2509
|
+
return null;
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
var readGitHead2 = (cwd) => {
|
|
2513
|
+
try {
|
|
2514
|
+
return runGit2(cwd, ["rev-parse", "--verify", "HEAD"]);
|
|
2515
|
+
} catch {
|
|
2516
|
+
return null;
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
var listWorkspaceStatePaths = (contextRoot) => {
|
|
2520
|
+
const workspacesDir = join11(contextRoot, "workspaces");
|
|
2521
|
+
if (!existsSync4(workspacesDir)) {
|
|
2522
|
+
return [];
|
|
2523
|
+
}
|
|
2524
|
+
return readdirSync5(workspacesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join11(workspacesDir, entry.name, "workspace-state.md")).filter((statePath) => existsSync4(statePath)).sort((a, b) => a.localeCompare(b));
|
|
2525
|
+
};
|
|
2526
|
+
var parseWorkspaceCandidate = (statePath) => {
|
|
2527
|
+
const content = readTextFileOrNull(statePath);
|
|
2528
|
+
if (!content) {
|
|
2529
|
+
return null;
|
|
2530
|
+
}
|
|
2531
|
+
const workspaceRoot = parseListField(content, ["\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB8E8\uD2B8", "Workspace Root"]);
|
|
2532
|
+
if (!workspaceRoot) {
|
|
2533
|
+
return null;
|
|
2534
|
+
}
|
|
2535
|
+
const activeSection = extractSection(content, ["\uD65C\uC131 Workstream", "Active Workstream"]);
|
|
2536
|
+
const activeWorkstreamId = parseListField(activeSection, ["ID", "Workstream ID", "Active Workstream"]);
|
|
2537
|
+
const workspaceDir = resolve8(statePath, "..");
|
|
2538
|
+
const id = parseListField(content, ["\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 ID", "Workspace ID"]) ?? workspaceDir.split(sep).at(-1) ?? "unknown";
|
|
2539
|
+
return {
|
|
2540
|
+
id,
|
|
2541
|
+
statePath,
|
|
2542
|
+
workspaceDir,
|
|
2543
|
+
workspaceRoot,
|
|
2544
|
+
activeWorkstreamId
|
|
2545
|
+
};
|
|
2546
|
+
};
|
|
2547
|
+
var findMatchingWorkspace = (params) => {
|
|
2548
|
+
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);
|
|
2549
|
+
return candidates[0] ?? null;
|
|
2550
|
+
};
|
|
2551
|
+
var parseRepoEntry = (entryPath) => {
|
|
2552
|
+
const content = readTextFileOrNull(entryPath);
|
|
2553
|
+
if (!content) {
|
|
2554
|
+
return null;
|
|
2555
|
+
}
|
|
2556
|
+
const id = parseListField(content, ["\uC5D4\uD2B8\uB9AC ID", "Entry ID"]);
|
|
2557
|
+
if (!id) {
|
|
2558
|
+
return null;
|
|
2559
|
+
}
|
|
2560
|
+
return {
|
|
2561
|
+
id,
|
|
2562
|
+
path: parseListField(content, ["\uACBD\uB85C", "Path"]),
|
|
2563
|
+
gitRoot: parseListField(content, ["Git \uB8E8\uD2B8", "Git Root"])
|
|
2564
|
+
};
|
|
2565
|
+
};
|
|
2566
|
+
var findCurrentEntry = (params) => {
|
|
2567
|
+
const reposDir = join11(params.workspaceDir, "repos");
|
|
2568
|
+
if (!existsSync4(reposDir)) {
|
|
2569
|
+
return null;
|
|
2570
|
+
}
|
|
2571
|
+
const entries = readdirSync5(reposDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => parseRepoEntry(join11(reposDir, entry.name))).filter((entry) => entry !== null).filter((entry) => {
|
|
2572
|
+
const paths = [entry.path, entry.gitRoot].filter((path) => path !== null);
|
|
2573
|
+
return paths.some((path) => pathContains(path, params.cwd));
|
|
2574
|
+
}).sort((a, b) => {
|
|
2575
|
+
const aLength = Math.max(0, ...[a.path, a.gitRoot].map((path) => path ? normalizePath(path).length : 0));
|
|
2576
|
+
const bLength = Math.max(0, ...[b.path, b.gitRoot].map((path) => path ? normalizePath(path).length : 0));
|
|
2577
|
+
return bLength - aLength;
|
|
2578
|
+
});
|
|
2579
|
+
return entries[0] ?? null;
|
|
2580
|
+
};
|
|
2581
|
+
var parseWorkstreamScopeEntryIds = (content) => {
|
|
2582
|
+
const scopeSection = extractSection(content, ["\uBC94\uC704", "Scope"]);
|
|
2583
|
+
if (scopeSection.length === 0) {
|
|
2584
|
+
return [];
|
|
2585
|
+
}
|
|
2586
|
+
const lines = scopeSection.split("\n");
|
|
2587
|
+
const ids = [];
|
|
2588
|
+
let inEntryBlock = false;
|
|
2589
|
+
for (const line of lines) {
|
|
2590
|
+
if (/^-\s*(엔트리|Entries|Entry):/.test(line)) {
|
|
2591
|
+
inEntryBlock = true;
|
|
2592
|
+
const inlineValue = normalizeFieldValue(line.split(":").slice(1).join(":"));
|
|
2593
|
+
if (inlineValue && !inlineValue.includes("<")) {
|
|
2594
|
+
ids.push(...inlineValue.split(",").map((value) => value.trim().replace(/^`|`$/g, "")));
|
|
2595
|
+
}
|
|
2596
|
+
continue;
|
|
2597
|
+
}
|
|
2598
|
+
if (inEntryBlock && /^-\s+\S/.test(line)) {
|
|
2599
|
+
inEntryBlock = false;
|
|
2600
|
+
}
|
|
2601
|
+
if (!inEntryBlock) {
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
const nestedMatch = /^\s+-\s+`?([a-z0-9]+(?:-[a-z0-9]+)*)`?/.exec(line);
|
|
2605
|
+
if (nestedMatch) {
|
|
2606
|
+
ids.push(nestedMatch[1]);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
return [...new Set(ids.filter((id) => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id)))];
|
|
2291
2610
|
};
|
|
2292
|
-
var
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
return value;
|
|
2297
|
-
}
|
|
2611
|
+
var parseLastConfirmedCommitHash = (params) => {
|
|
2612
|
+
const section = extractSection(params.content, ["\uB9C8\uC9C0\uB9C9 \uD655\uC778 Commit", "Last Confirmed Commit"]);
|
|
2613
|
+
if (section.length === 0) {
|
|
2614
|
+
return null;
|
|
2298
2615
|
}
|
|
2299
|
-
|
|
2616
|
+
const escapedEntryId = params.entryId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2617
|
+
const match = new RegExp(`^-\\s+\`?${escapedEntryId}\`?:\\s*([a-f0-9]{40})\\b`, "imu").exec(section);
|
|
2618
|
+
return match?.[1] ?? null;
|
|
2300
2619
|
};
|
|
2301
|
-
var
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
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;
|
|
2620
|
+
var getPcHandoffStatus = (params) => {
|
|
2621
|
+
const cwd = normalizePath(params.cwd);
|
|
2622
|
+
const contextRoot = normalizePath(params.contextRoot);
|
|
2623
|
+
if (!existsSync4(contextRoot)) {
|
|
2624
|
+
return {
|
|
2625
|
+
cwd,
|
|
2626
|
+
contextRoot,
|
|
2627
|
+
workspaceId: null,
|
|
2628
|
+
workspaceRoot: null,
|
|
2629
|
+
activeWorkstreamId: null,
|
|
2630
|
+
activeWorkstreamPath: null,
|
|
2631
|
+
currentEntryId: null,
|
|
2632
|
+
lastConfirmedCommitHash: null,
|
|
2633
|
+
ready: false,
|
|
2634
|
+
skipReason: "pc context root not found"
|
|
2635
|
+
};
|
|
2325
2636
|
}
|
|
2326
|
-
|
|
2327
|
-
|
|
2637
|
+
const workspace = findMatchingWorkspace({ cwd, contextRoot });
|
|
2638
|
+
if (!workspace) {
|
|
2639
|
+
return {
|
|
2640
|
+
cwd,
|
|
2641
|
+
contextRoot,
|
|
2642
|
+
workspaceId: null,
|
|
2643
|
+
workspaceRoot: null,
|
|
2644
|
+
activeWorkstreamId: null,
|
|
2645
|
+
activeWorkstreamPath: null,
|
|
2646
|
+
currentEntryId: null,
|
|
2647
|
+
lastConfirmedCommitHash: null,
|
|
2648
|
+
ready: false,
|
|
2649
|
+
skipReason: "matching pc workspace not found"
|
|
2650
|
+
};
|
|
2328
2651
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2652
|
+
if (!workspace.activeWorkstreamId) {
|
|
2653
|
+
return {
|
|
2654
|
+
cwd,
|
|
2655
|
+
contextRoot,
|
|
2656
|
+
workspaceId: workspace.id,
|
|
2657
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2658
|
+
activeWorkstreamId: null,
|
|
2659
|
+
activeWorkstreamPath: null,
|
|
2660
|
+
currentEntryId: null,
|
|
2661
|
+
lastConfirmedCommitHash: null,
|
|
2662
|
+
ready: false,
|
|
2663
|
+
skipReason: "active pc workstream not selected"
|
|
2664
|
+
};
|
|
2332
2665
|
}
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2335
|
-
|
|
2666
|
+
const activeWorkstreamPath = join11(workspace.workspaceDir, "workstreams", `${workspace.activeWorkstreamId}.md`);
|
|
2667
|
+
const activeWorkstreamContent = readTextFileOrNull(activeWorkstreamPath);
|
|
2668
|
+
if (!activeWorkstreamContent) {
|
|
2669
|
+
return {
|
|
2670
|
+
cwd,
|
|
2671
|
+
contextRoot,
|
|
2672
|
+
workspaceId: workspace.id,
|
|
2673
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2674
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2675
|
+
activeWorkstreamPath,
|
|
2676
|
+
currentEntryId: null,
|
|
2677
|
+
lastConfirmedCommitHash: null,
|
|
2678
|
+
ready: false,
|
|
2679
|
+
skipReason: "active pc workstream file not found"
|
|
2680
|
+
};
|
|
2336
2681
|
}
|
|
2337
|
-
|
|
2682
|
+
const currentEntry = findCurrentEntry({ cwd, workspaceDir: workspace.workspaceDir });
|
|
2683
|
+
if (!currentEntry) {
|
|
2684
|
+
return {
|
|
2685
|
+
cwd,
|
|
2686
|
+
contextRoot,
|
|
2687
|
+
workspaceId: workspace.id,
|
|
2688
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2689
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2690
|
+
activeWorkstreamPath,
|
|
2691
|
+
currentEntryId: null,
|
|
2692
|
+
lastConfirmedCommitHash: null,
|
|
2693
|
+
ready: false,
|
|
2694
|
+
skipReason: "current repo is not registered in pc workspace"
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2697
|
+
const lastConfirmedCommitHash = parseLastConfirmedCommitHash({
|
|
2698
|
+
content: activeWorkstreamContent,
|
|
2699
|
+
entryId: currentEntry.id
|
|
2700
|
+
});
|
|
2701
|
+
const scopeEntryIds = parseWorkstreamScopeEntryIds(activeWorkstreamContent);
|
|
2702
|
+
if (scopeEntryIds.length > 0 && !scopeEntryIds.includes(currentEntry.id)) {
|
|
2703
|
+
return {
|
|
2704
|
+
cwd,
|
|
2705
|
+
contextRoot,
|
|
2706
|
+
workspaceId: workspace.id,
|
|
2707
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2708
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2709
|
+
activeWorkstreamPath,
|
|
2710
|
+
currentEntryId: currentEntry.id,
|
|
2711
|
+
lastConfirmedCommitHash,
|
|
2712
|
+
ready: false,
|
|
2713
|
+
skipReason: "current repo is outside the active pc workstream scope"
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
return {
|
|
2717
|
+
cwd,
|
|
2718
|
+
contextRoot,
|
|
2719
|
+
workspaceId: workspace.id,
|
|
2720
|
+
workspaceRoot: workspace.workspaceRoot,
|
|
2721
|
+
activeWorkstreamId: workspace.activeWorkstreamId,
|
|
2722
|
+
activeWorkstreamPath,
|
|
2723
|
+
currentEntryId: currentEntry.id,
|
|
2724
|
+
lastConfirmedCommitHash,
|
|
2725
|
+
ready: true,
|
|
2726
|
+
skipReason: null
|
|
2727
|
+
};
|
|
2338
2728
|
};
|
|
2339
|
-
var
|
|
2340
|
-
|
|
2341
|
-
|
|
2729
|
+
var buildPcDonePrompt = (params) => [
|
|
2730
|
+
"A successful git commit just created a new HEAD commit.",
|
|
2731
|
+
"",
|
|
2732
|
+
"Run `$pc:done` now to record the handoff for the active personal project context.",
|
|
2733
|
+
"",
|
|
2734
|
+
"Important guardrails:",
|
|
2735
|
+
"- Do not create or initialize a new pc context from this hook.",
|
|
2736
|
+
"- If `$pc:done` cannot match the prepared workspace, active workstream, or current repo scope, skip and briefly say why.",
|
|
2737
|
+
"- If the active workstream already records this HEAD as the last confirmed commit, skip without writing another handoff.",
|
|
2738
|
+
"- Do not modify the product repo for this hook; `$pc:done` may only update `~/.personal-project-contexts/` and commit that context repo.",
|
|
2739
|
+
"- Use the just-created HEAD commit as the newest evidence for completed work and the next first action.",
|
|
2740
|
+
"",
|
|
2741
|
+
`Project git root: ${params.gitRoot}`,
|
|
2742
|
+
`HEAD: ${params.head}`,
|
|
2743
|
+
`pc context root: ${params.status.contextRoot}`,
|
|
2744
|
+
`pc workspace: ${params.status.workspaceId ?? "unknown"} (${params.status.workspaceRoot ?? "unknown"})`,
|
|
2745
|
+
`active workstream: ${params.status.activeWorkstreamId ?? "unknown"}`,
|
|
2746
|
+
`current entry: ${params.status.currentEntryId ?? "unknown"}`,
|
|
2747
|
+
`last confirmed commit: ${params.status.lastConfirmedCommitHash ?? "none"}`
|
|
2748
|
+
].join("\n");
|
|
2749
|
+
var buildPostToolUseOutput2 = (prompt) => ({
|
|
2750
|
+
decision: "block",
|
|
2751
|
+
reason: prompt,
|
|
2752
|
+
hookSpecificOutput: {
|
|
2753
|
+
hookEventName: "PostToolUse",
|
|
2754
|
+
additionalContext: prompt
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
var evaluatePcPostToolUseHook = (params) => {
|
|
2758
|
+
const gitCommitHook = parseSuccessfulGitCommitPostToolUseHook(params.hookInput);
|
|
2759
|
+
if (!gitCommitHook) {
|
|
2342
2760
|
return null;
|
|
2343
2761
|
}
|
|
2344
|
-
|
|
2762
|
+
const gitRoot = resolveGitRoot(gitCommitHook.cwd);
|
|
2763
|
+
if (!gitRoot) {
|
|
2345
2764
|
return null;
|
|
2346
2765
|
}
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
if (!isGitCommitCommand(command) || toolResponseIndicatesFailure(hookInput.data.tool_response)) {
|
|
2766
|
+
const head = readGitHead2(gitRoot);
|
|
2767
|
+
if (!head) {
|
|
2350
2768
|
return null;
|
|
2351
2769
|
}
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
} catch (error) {
|
|
2359
|
-
return buildPostToolUseOutput(buildContextPromotionStatusFailurePrompt(hookInput.data.cwd, error));
|
|
2770
|
+
const status = getPcHandoffStatus({
|
|
2771
|
+
cwd: gitCommitHook.cwd,
|
|
2772
|
+
contextRoot: params.contextRoot
|
|
2773
|
+
});
|
|
2774
|
+
if (!status.ready) {
|
|
2775
|
+
return null;
|
|
2360
2776
|
}
|
|
2361
|
-
if (
|
|
2777
|
+
if (status.lastConfirmedCommitHash === head) {
|
|
2362
2778
|
return null;
|
|
2363
2779
|
}
|
|
2364
|
-
return
|
|
2780
|
+
return buildPostToolUseOutput2(buildPcDonePrompt({ status, head, gitRoot }));
|
|
2365
2781
|
};
|
|
2366
2782
|
|
|
2367
2783
|
// src/core/codex-hook.ts
|
|
2368
|
-
import { existsSync as
|
|
2369
|
-
import { dirname as dirname9, join as
|
|
2784
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
2785
|
+
import { dirname as dirname9, join as join12 } from "path";
|
|
2370
2786
|
var CONTEXT_PROMOTION_HOOK_ID = "context-promotion";
|
|
2371
2787
|
var CONTEXT_PROMOTION_HOOK_COMMAND_MARKER = "context-promotion hook post-tool-use";
|
|
2372
2788
|
var CONTEXT_PROMOTION_LEGACY_HOOK_COMMAND_MARKER = "context-promotion hook pre-tool-use";
|
|
2373
2789
|
var CONTEXT_PROMOTION_DEFAULT_HOOK_COMMAND = `ai-ops ${CONTEXT_PROMOTION_HOOK_COMMAND_MARKER}`;
|
|
2790
|
+
var PC_HOOK_ID = "pc";
|
|
2791
|
+
var PC_HOOK_COMMAND_MARKER = "integration hook post-tool-use pc";
|
|
2792
|
+
var PC_DEFAULT_HOOK_COMMAND = `ai-ops ${PC_HOOK_COMMAND_MARKER}`;
|
|
2374
2793
|
var PRE_TOOL_USE_EVENT = "PreToolUse";
|
|
2375
2794
|
var POST_TOOL_USE_EVENT = "PostToolUse";
|
|
2376
2795
|
var BASH_MATCHER = "^Bash$";
|
|
2796
|
+
var CONTEXT_PROMOTION_CODEX_HOOK = {
|
|
2797
|
+
id: CONTEXT_PROMOTION_HOOK_ID,
|
|
2798
|
+
commandMarker: CONTEXT_PROMOTION_HOOK_COMMAND_MARKER,
|
|
2799
|
+
legacyCommandMarkers: [CONTEXT_PROMOTION_LEGACY_HOOK_COMMAND_MARKER],
|
|
2800
|
+
defaultCommand: CONTEXT_PROMOTION_DEFAULT_HOOK_COMMAND,
|
|
2801
|
+
statusMessage: "Checking context promotion review"
|
|
2802
|
+
};
|
|
2803
|
+
var PC_CODEX_HOOK = {
|
|
2804
|
+
id: PC_HOOK_ID,
|
|
2805
|
+
commandMarker: PC_HOOK_COMMAND_MARKER,
|
|
2806
|
+
legacyCommandMarkers: [],
|
|
2807
|
+
defaultCommand: PC_DEFAULT_HOOK_COMMAND,
|
|
2808
|
+
statusMessage: "Checking pc handoff"
|
|
2809
|
+
};
|
|
2377
2810
|
var isJsonRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2378
2811
|
var readJsonRecord = (filePath) => {
|
|
2379
|
-
if (!
|
|
2812
|
+
if (!existsSync5(filePath)) {
|
|
2380
2813
|
return {};
|
|
2381
2814
|
}
|
|
2382
|
-
const parsed = JSON.parse(
|
|
2815
|
+
const parsed = JSON.parse(readFileSync10(filePath, "utf-8"));
|
|
2383
2816
|
if (!isJsonRecord2(parsed)) {
|
|
2384
2817
|
throw new Error("hooks.json must contain a JSON object");
|
|
2385
2818
|
}
|
|
@@ -2402,34 +2835,34 @@ var getArray = (record, key) => {
|
|
|
2402
2835
|
const existing = record[key];
|
|
2403
2836
|
return Array.isArray(existing) ? existing : [];
|
|
2404
2837
|
};
|
|
2405
|
-
var
|
|
2838
|
+
var handlerMatchesDefinition = (definition) => (handler) => isJsonRecord2(handler) && typeof handler.command === "string" && [definition.commandMarker, ...definition.legacyCommandMarkers].some((marker) => handler.command.includes(marker));
|
|
2406
2839
|
var handlerMatchesCommand = (handler, command) => isJsonRecord2(handler) && handler.command === command;
|
|
2407
|
-
var
|
|
2408
|
-
var
|
|
2409
|
-
var
|
|
2840
|
+
var groupHasDefinitionHook = (definition) => (group) => isJsonRecord2(group) && getArray(group, "hooks").some(handlerMatchesDefinition(definition));
|
|
2841
|
+
var groupHasCurrentDefinitionHook = (group, command) => isJsonRecord2(group) && getArray(group, "hooks").some((handler) => handlerMatchesCommand(handler, command));
|
|
2842
|
+
var countDefinitionHandlers = (groups, definition) => groups.reduce((count, group) => {
|
|
2410
2843
|
if (!isJsonRecord2(group)) {
|
|
2411
2844
|
return count;
|
|
2412
2845
|
}
|
|
2413
|
-
return count + getArray(group, "hooks").filter(
|
|
2846
|
+
return count + getArray(group, "hooks").filter(handlerMatchesDefinition(definition)).length;
|
|
2414
2847
|
}, 0);
|
|
2415
|
-
var
|
|
2848
|
+
var configHasDefinitionHook = (config, definition) => {
|
|
2416
2849
|
const hooks = config.hooks;
|
|
2417
2850
|
if (!isJsonRecord2(hooks)) {
|
|
2418
2851
|
return false;
|
|
2419
2852
|
}
|
|
2420
|
-
return getArray(hooks, POST_TOOL_USE_EVENT).some(
|
|
2853
|
+
return getArray(hooks, POST_TOOL_USE_EVENT).some(groupHasDefinitionHook(definition));
|
|
2421
2854
|
};
|
|
2422
|
-
var
|
|
2855
|
+
var configHasOnlyCurrentDefinitionHook = (config, definition, command) => {
|
|
2423
2856
|
const hooks = config.hooks;
|
|
2424
2857
|
if (!isJsonRecord2(hooks)) {
|
|
2425
2858
|
return false;
|
|
2426
2859
|
}
|
|
2427
|
-
const hasLegacy = getArray(hooks, PRE_TOOL_USE_EVENT).some(
|
|
2860
|
+
const hasLegacy = getArray(hooks, PRE_TOOL_USE_EVENT).some(groupHasDefinitionHook(definition));
|
|
2428
2861
|
const postGroups = getArray(hooks, POST_TOOL_USE_EVENT);
|
|
2429
|
-
const hasCurrent = postGroups.some((group) =>
|
|
2430
|
-
return hasCurrent && !hasLegacy &&
|
|
2862
|
+
const hasCurrent = postGroups.some((group) => groupHasCurrentDefinitionHook(group, command));
|
|
2863
|
+
return hasCurrent && !hasLegacy && countDefinitionHandlers(postGroups, definition) === 1;
|
|
2431
2864
|
};
|
|
2432
|
-
var
|
|
2865
|
+
var removeDefinitionHooksFromEvent = (hooks, eventName, definition) => {
|
|
2433
2866
|
const previousGroups = getArray(hooks, eventName);
|
|
2434
2867
|
let removed = false;
|
|
2435
2868
|
const nextGroups = previousGroups.map((group) => {
|
|
@@ -2438,7 +2871,7 @@ var removeContextPromotionHooksFromEvent = (hooks, eventName) => {
|
|
|
2438
2871
|
}
|
|
2439
2872
|
const previousHandlers = getArray(group, "hooks");
|
|
2440
2873
|
const nextHandlers = previousHandlers.filter((handler) => {
|
|
2441
|
-
const matches =
|
|
2874
|
+
const matches = handlerMatchesDefinition(definition)(handler);
|
|
2442
2875
|
if (matches) {
|
|
2443
2876
|
removed = true;
|
|
2444
2877
|
}
|
|
@@ -2462,21 +2895,29 @@ var removeContextPromotionHooksFromEvent = (hooks, eventName) => {
|
|
|
2462
2895
|
}
|
|
2463
2896
|
return true;
|
|
2464
2897
|
};
|
|
2465
|
-
var resolveCodexHooksPath = (codexHomePath) =>
|
|
2466
|
-
var
|
|
2467
|
-
const command = overrideCommand?.trim() ??
|
|
2468
|
-
if (!command.includes(
|
|
2469
|
-
throw new Error(
|
|
2898
|
+
var resolveCodexHooksPath = (codexHomePath) => join12(codexHomePath, "hooks.json");
|
|
2899
|
+
var buildCodexHookCommand = (params) => {
|
|
2900
|
+
const command = params.overrideCommand?.trim() ?? params.definition.defaultCommand;
|
|
2901
|
+
if (!command.includes(params.definition.commandMarker)) {
|
|
2902
|
+
throw new Error(`${params.definition.id} hook command must include: ${params.definition.commandMarker}`);
|
|
2470
2903
|
}
|
|
2471
2904
|
return command;
|
|
2472
2905
|
};
|
|
2906
|
+
var buildContextPromotionHookCommand = (overrideCommand) => buildCodexHookCommand({
|
|
2907
|
+
definition: CONTEXT_PROMOTION_CODEX_HOOK,
|
|
2908
|
+
overrideCommand
|
|
2909
|
+
});
|
|
2910
|
+
var inspectCodexHook = (params) => ({
|
|
2911
|
+
hooksPath: params.hooksPath,
|
|
2912
|
+
installed: configHasDefinitionHook(readJsonRecord(params.hooksPath), params.definition)
|
|
2913
|
+
});
|
|
2473
2914
|
var inspectContextPromotionHook = (hooksPath) => ({
|
|
2474
2915
|
hooksPath,
|
|
2475
|
-
installed:
|
|
2916
|
+
installed: inspectCodexHook({ hooksPath, definition: CONTEXT_PROMOTION_CODEX_HOOK }).installed
|
|
2476
2917
|
});
|
|
2477
|
-
var
|
|
2918
|
+
var installCodexHook = (params) => {
|
|
2478
2919
|
const config = readJsonRecord(params.hooksPath);
|
|
2479
|
-
if (
|
|
2920
|
+
if (configHasOnlyCurrentDefinitionHook(config, params.definition, params.command)) {
|
|
2480
2921
|
return {
|
|
2481
2922
|
hooksPath: params.hooksPath,
|
|
2482
2923
|
installed: true,
|
|
@@ -2484,8 +2925,8 @@ var installContextPromotionHook = (params) => {
|
|
|
2484
2925
|
};
|
|
2485
2926
|
}
|
|
2486
2927
|
const hooks = getOrCreateRecord(config, "hooks");
|
|
2487
|
-
|
|
2488
|
-
|
|
2928
|
+
removeDefinitionHooksFromEvent(hooks, PRE_TOOL_USE_EVENT, params.definition);
|
|
2929
|
+
removeDefinitionHooksFromEvent(hooks, POST_TOOL_USE_EVENT, params.definition);
|
|
2489
2930
|
const existingGroups = getArray(hooks, POST_TOOL_USE_EVENT);
|
|
2490
2931
|
const nextGroup = {
|
|
2491
2932
|
matcher: BASH_MATCHER,
|
|
@@ -2494,7 +2935,7 @@ var installContextPromotionHook = (params) => {
|
|
|
2494
2935
|
type: "command",
|
|
2495
2936
|
command: params.command,
|
|
2496
2937
|
timeout: 30,
|
|
2497
|
-
statusMessage:
|
|
2938
|
+
statusMessage: params.definition.statusMessage
|
|
2498
2939
|
}
|
|
2499
2940
|
]
|
|
2500
2941
|
};
|
|
@@ -2506,32 +2947,42 @@ var installContextPromotionHook = (params) => {
|
|
|
2506
2947
|
changed: true
|
|
2507
2948
|
};
|
|
2508
2949
|
};
|
|
2509
|
-
var
|
|
2510
|
-
|
|
2950
|
+
var installContextPromotionHook = (params) => installCodexHook({
|
|
2951
|
+
hooksPath: params.hooksPath,
|
|
2952
|
+
definition: CONTEXT_PROMOTION_CODEX_HOOK,
|
|
2953
|
+
command: params.command
|
|
2954
|
+
});
|
|
2955
|
+
var uninstallCodexHook = (params) => {
|
|
2956
|
+
const config = readJsonRecord(params.hooksPath);
|
|
2511
2957
|
const hooks = config.hooks;
|
|
2512
2958
|
if (!isJsonRecord2(hooks)) {
|
|
2513
|
-
return { hooksPath, removed: false, changed: false };
|
|
2959
|
+
return { hooksPath: params.hooksPath, removed: false, changed: false };
|
|
2514
2960
|
}
|
|
2515
|
-
const removedLegacy =
|
|
2516
|
-
const removedCurrent =
|
|
2961
|
+
const removedLegacy = removeDefinitionHooksFromEvent(hooks, PRE_TOOL_USE_EVENT, params.definition);
|
|
2962
|
+
const removedCurrent = removeDefinitionHooksFromEvent(hooks, POST_TOOL_USE_EVENT, params.definition);
|
|
2517
2963
|
const removed = removedLegacy || removedCurrent;
|
|
2518
2964
|
if (!removed) {
|
|
2519
|
-
return { hooksPath, removed: false, changed: false };
|
|
2965
|
+
return { hooksPath: params.hooksPath, removed: false, changed: false };
|
|
2520
2966
|
}
|
|
2521
|
-
writeJsonRecord(hooksPath, config);
|
|
2522
|
-
return { hooksPath, removed: true, changed: true };
|
|
2967
|
+
writeJsonRecord(params.hooksPath, config);
|
|
2968
|
+
return { hooksPath: params.hooksPath, removed: true, changed: true };
|
|
2523
2969
|
};
|
|
2970
|
+
var uninstallContextPromotionHook = (hooksPath) => uninstallCodexHook({
|
|
2971
|
+
hooksPath,
|
|
2972
|
+
definition: CONTEXT_PROMOTION_CODEX_HOOK
|
|
2973
|
+
});
|
|
2524
2974
|
|
|
2525
2975
|
// src/lib/paths.ts
|
|
2526
|
-
import { join as
|
|
2527
|
-
var resolveSkillsDir = () =>
|
|
2528
|
-
var resolveSubagentsDir = () =>
|
|
2529
|
-
var resolvePacksDir = () =>
|
|
2976
|
+
import { join as join13 } from "path";
|
|
2977
|
+
var resolveSkillsDir = () => join13(COMPILER_DATA_DIR, "skills");
|
|
2978
|
+
var resolveSubagentsDir = () => join13(COMPILER_DATA_DIR, "subagents");
|
|
2979
|
+
var resolvePacksDir = () => join13(COMPILER_DATA_DIR, "packs");
|
|
2980
|
+
var resolveIntegrationsDir = () => join13(COMPILER_DATA_DIR, "integrations");
|
|
2530
2981
|
var resolveBasePath = () => process.cwd();
|
|
2531
2982
|
var resolveUserBasePath = () => {
|
|
2532
2983
|
const userBasePath = process.env.AI_OPS_HOME ?? process.env.HOME;
|
|
2533
2984
|
if (!userBasePath) {
|
|
2534
|
-
throw new Error("AI_OPS_HOME or HOME is required for global
|
|
2985
|
+
throw new Error("AI_OPS_HOME or HOME is required for user/global component commands");
|
|
2535
2986
|
}
|
|
2536
2987
|
return userBasePath;
|
|
2537
2988
|
};
|
|
@@ -2776,7 +3227,7 @@ ${result.notFound.map((file) => ` ${file}`).join("\n")}`);
|
|
|
2776
3227
|
|
|
2777
3228
|
// src/commands/skill.ts
|
|
2778
3229
|
import * as p7 from "@clack/prompts";
|
|
2779
|
-
import { rmSync as
|
|
3230
|
+
import { rmSync as rmSync5 } from "fs";
|
|
2780
3231
|
|
|
2781
3232
|
// src/lib/skill-state.ts
|
|
2782
3233
|
var resolveRequestedTools = (params) => {
|
|
@@ -2810,17 +3261,17 @@ var findInstalledSkill = (installedSkills, skillId) => {
|
|
|
2810
3261
|
};
|
|
2811
3262
|
|
|
2812
3263
|
// src/lib/skill-install.ts
|
|
2813
|
-
import { existsSync as
|
|
2814
|
-
import { dirname as dirname10, resolve as
|
|
3264
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync8, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "fs";
|
|
3265
|
+
import { dirname as dirname10, resolve as resolve9 } from "path";
|
|
2815
3266
|
var installSkillPackages = (basePath, packages) => {
|
|
2816
3267
|
const writtenRoots = [];
|
|
2817
3268
|
for (const skillPackage of packages) {
|
|
2818
|
-
const absRoot =
|
|
2819
|
-
if (
|
|
2820
|
-
|
|
3269
|
+
const absRoot = resolve9(basePath, skillPackage.rootDir);
|
|
3270
|
+
if (existsSync6(absRoot)) {
|
|
3271
|
+
rmSync4(absRoot, { recursive: true, force: true });
|
|
2821
3272
|
}
|
|
2822
3273
|
for (const file of skillPackage.files) {
|
|
2823
|
-
const absPath =
|
|
3274
|
+
const absPath = resolve9(basePath, file.relativePath);
|
|
2824
3275
|
mkdirSync8(dirname10(absPath), { recursive: true });
|
|
2825
3276
|
writeFileSync8(absPath, file.content + "\n", "utf-8");
|
|
2826
3277
|
}
|
|
@@ -2831,9 +3282,9 @@ var installSkillPackages = (basePath, packages) => {
|
|
|
2831
3282
|
var removeDirectories = (basePath, relativeDirs) => {
|
|
2832
3283
|
const removed = [];
|
|
2833
3284
|
for (const relativeDir of relativeDirs) {
|
|
2834
|
-
const absPath =
|
|
2835
|
-
if (!
|
|
2836
|
-
|
|
3285
|
+
const absPath = resolve9(basePath, relativeDir);
|
|
3286
|
+
if (!existsSync6(absPath)) continue;
|
|
3287
|
+
rmSync4(absPath, { recursive: true, force: true });
|
|
2837
3288
|
removed.push(relativeDir);
|
|
2838
3289
|
}
|
|
2839
3290
|
return removed;
|
|
@@ -2859,7 +3310,7 @@ var writeUserSkillState = (params) => {
|
|
|
2859
3310
|
const previous = readSkillRegistry(registryPath);
|
|
2860
3311
|
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
2861
3312
|
if (skills.length === 0) {
|
|
2862
|
-
|
|
3313
|
+
rmSync5(registryPath, { force: true });
|
|
2863
3314
|
return;
|
|
2864
3315
|
}
|
|
2865
3316
|
writeSkillRegistry(registryPath, {
|
|
@@ -3011,14 +3462,14 @@ var skillUninstallCommand = async (skillId) => {
|
|
|
3011
3462
|
|
|
3012
3463
|
// src/commands/subagent.ts
|
|
3013
3464
|
import * as p8 from "@clack/prompts";
|
|
3014
|
-
import { existsSync as
|
|
3465
|
+
import { existsSync as existsSync8, rmSync as rmSync7 } from "fs";
|
|
3015
3466
|
|
|
3016
3467
|
// src/lib/subagent-install.ts
|
|
3017
|
-
import { existsSync as
|
|
3018
|
-
import { dirname as dirname11, isAbsolute as isAbsolute3, relative as relative3, resolve as
|
|
3468
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync9, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "fs";
|
|
3469
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve10 } from "path";
|
|
3019
3470
|
var resolveInsideBasePath = (basePath, relativePath) => {
|
|
3020
|
-
const absBasePath =
|
|
3021
|
-
const absPath =
|
|
3471
|
+
const absBasePath = resolve10(basePath);
|
|
3472
|
+
const absPath = resolve10(absBasePath, relativePath);
|
|
3022
3473
|
const fromBase = relative3(absBasePath, absPath);
|
|
3023
3474
|
if (fromBase.length === 0 || fromBase.startsWith("..") || isAbsolute3(fromBase)) {
|
|
3024
3475
|
throw new Error(`Subagent path escapes AI_OPS_HOME: ${relativePath}`);
|
|
@@ -3030,8 +3481,8 @@ var installSubagentPackages = (basePath, packages) => {
|
|
|
3030
3481
|
for (const subagentPackage of packages) {
|
|
3031
3482
|
for (const file of subagentPackage.files) {
|
|
3032
3483
|
const absPath = resolveInsideBasePath(basePath, file.relativePath);
|
|
3033
|
-
if (
|
|
3034
|
-
|
|
3484
|
+
if (existsSync7(absPath)) {
|
|
3485
|
+
rmSync6(absPath, { recursive: true, force: true });
|
|
3035
3486
|
}
|
|
3036
3487
|
mkdirSync9(dirname11(absPath), { recursive: true });
|
|
3037
3488
|
writeFileSync9(absPath, file.content.trimEnd() + "\n", "utf-8");
|
|
@@ -3044,8 +3495,8 @@ var removeSubagentFiles = (basePath, relativePaths) => {
|
|
|
3044
3495
|
const removed = [];
|
|
3045
3496
|
for (const relativePath of relativePaths) {
|
|
3046
3497
|
const absPath = resolveInsideBasePath(basePath, relativePath);
|
|
3047
|
-
if (!
|
|
3048
|
-
|
|
3498
|
+
if (!existsSync7(absPath)) continue;
|
|
3499
|
+
rmSync6(absPath, { recursive: true, force: true });
|
|
3049
3500
|
removed.push(relativePath);
|
|
3050
3501
|
}
|
|
3051
3502
|
return removed;
|
|
@@ -3095,7 +3546,7 @@ var writeUserSubagentState = (params) => {
|
|
|
3095
3546
|
const previous = readSubagentManifest(manifestPath);
|
|
3096
3547
|
const subagents = params.removeSubagentId ? removeInstalledSubagent(previous?.subagents ?? [], params.removeSubagentId) : params.nextSubagent ? upsertInstalledSubagent(previous?.subagents ?? [], params.nextSubagent) : previous?.subagents ?? [];
|
|
3097
3548
|
if (subagents.length === 0) {
|
|
3098
|
-
|
|
3549
|
+
rmSync7(manifestPath, { force: true });
|
|
3099
3550
|
return;
|
|
3100
3551
|
}
|
|
3101
3552
|
writeSubagentManifest(manifestPath, {
|
|
@@ -3106,7 +3557,7 @@ var writeUserSubagentState = (params) => {
|
|
|
3106
3557
|
};
|
|
3107
3558
|
var readInstalledSubagents = (basePath) => readSubagentManifest(resolveSubagentManifestPath(basePath))?.subagents ?? [];
|
|
3108
3559
|
var warnMissingSkills = (requiredSkills) => {
|
|
3109
|
-
const missing = requiredSkills.filter((skill) => !
|
|
3560
|
+
const missing = requiredSkills.filter((skill) => !existsSync8(skill.path));
|
|
3110
3561
|
if (missing.length === 0) {
|
|
3111
3562
|
return;
|
|
3112
3563
|
}
|
|
@@ -3417,13 +3868,13 @@ var parseMax = (max) => {
|
|
|
3417
3868
|
}
|
|
3418
3869
|
return parsed;
|
|
3419
3870
|
};
|
|
3420
|
-
var readStdin = async () => new Promise((
|
|
3871
|
+
var readStdin = async () => new Promise((resolve11, reject) => {
|
|
3421
3872
|
let raw = "";
|
|
3422
3873
|
process.stdin.setEncoding("utf-8");
|
|
3423
3874
|
process.stdin.on("data", (chunk) => {
|
|
3424
3875
|
raw += chunk;
|
|
3425
3876
|
});
|
|
3426
|
-
process.stdin.on("end", () =>
|
|
3877
|
+
process.stdin.on("end", () => resolve11(raw));
|
|
3427
3878
|
process.stdin.on("error", reject);
|
|
3428
3879
|
});
|
|
3429
3880
|
var reportContextPromotionError = (error) => {
|
|
@@ -3524,8 +3975,8 @@ var contextPromotionPostToolUseHookCommand = async () => {
|
|
|
3524
3975
|
|
|
3525
3976
|
// src/commands/codex-hook.ts
|
|
3526
3977
|
import * as p11 from "@clack/prompts";
|
|
3527
|
-
import { existsSync as
|
|
3528
|
-
import { join as
|
|
3978
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3979
|
+
import { join as join14 } from "path";
|
|
3529
3980
|
var CONTEXT_PROMOTION_REVIEW_SKILL_ID = "context-promotion-review";
|
|
3530
3981
|
var resolveCodexHomePath = () => {
|
|
3531
3982
|
const codexHome = process.env.CODEX_HOME;
|
|
@@ -3563,7 +4014,7 @@ var resolveContextPromotionReviewSkill = () => {
|
|
|
3563
4014
|
};
|
|
3564
4015
|
var hasInstalledContextPromotionReviewSkill = (basePath) => {
|
|
3565
4016
|
const installedSkill = findInstalledSkill(readInstalledSkills2(basePath), CONTEXT_PROMOTION_REVIEW_SKILL_ID);
|
|
3566
|
-
return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true &&
|
|
4017
|
+
return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true && existsSync9(join14(basePath, ".agents/skills/context-promotion-review/SKILL.md"));
|
|
3567
4018
|
};
|
|
3568
4019
|
var ensureContextPromotionReviewSkill = (basePath) => {
|
|
3569
4020
|
const skill = resolveContextPromotionReviewSkill();
|
|
@@ -3577,7 +4028,7 @@ var ensureContextPromotionReviewSkill = (basePath) => {
|
|
|
3577
4028
|
skill,
|
|
3578
4029
|
requestedTools
|
|
3579
4030
|
});
|
|
3580
|
-
const alreadyInstalled = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) &&
|
|
4031
|
+
const alreadyInstalled = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) && existsSync9(join14(basePath, ".agents/skills/context-promotion-review/SKILL.md"));
|
|
3581
4032
|
if (alreadyInstalled) {
|
|
3582
4033
|
return { changed: false, installedSkill };
|
|
3583
4034
|
}
|
|
@@ -3638,6 +4089,386 @@ var codexHookUninstallCommand = async (hookId) => {
|
|
|
3638
4089
|
p11.outro("ai-ops codex-hook uninstall \uC644\uB8CC");
|
|
3639
4090
|
};
|
|
3640
4091
|
|
|
4092
|
+
// src/commands/integration.ts
|
|
4093
|
+
import * as p12 from "@clack/prompts";
|
|
4094
|
+
import { existsSync as existsSync10, rmSync as rmSync8 } from "fs";
|
|
4095
|
+
import { join as join15 } from "path";
|
|
4096
|
+
var CODEX_HOOK_DEFINITIONS = [CONTEXT_PROMOTION_CODEX_HOOK, PC_CODEX_HOOK];
|
|
4097
|
+
var resolveCodexHomePath2 = () => {
|
|
4098
|
+
const codexHome = process.env.CODEX_HOME;
|
|
4099
|
+
if (codexHome && codexHome.length > 0) {
|
|
4100
|
+
return codexHome;
|
|
4101
|
+
}
|
|
4102
|
+
const home = process.env.HOME;
|
|
4103
|
+
if (!home) {
|
|
4104
|
+
throw new Error("CODEX_HOME or HOME is required for Codex hook commands");
|
|
4105
|
+
}
|
|
4106
|
+
return `${home}/.codex`;
|
|
4107
|
+
};
|
|
4108
|
+
var resolvePersonalContextRoot = () => {
|
|
4109
|
+
const home = process.env.HOME;
|
|
4110
|
+
if (!home) {
|
|
4111
|
+
throw new Error("HOME is required for pc integration commands");
|
|
4112
|
+
}
|
|
4113
|
+
return `${home}/.personal-project-contexts`;
|
|
4114
|
+
};
|
|
4115
|
+
var resolveCodexHookDefinition = (hookId) => {
|
|
4116
|
+
const hookDefinition = CODEX_HOOK_DEFINITIONS.find((definition) => definition.id === hookId);
|
|
4117
|
+
if (!hookDefinition) {
|
|
4118
|
+
throw new Error(`Unknown Codex hook for integration: ${hookId}`);
|
|
4119
|
+
}
|
|
4120
|
+
return hookDefinition;
|
|
4121
|
+
};
|
|
4122
|
+
var resolveCatalogSkillComponent = (entry) => {
|
|
4123
|
+
const component = entry.components.find((candidate) => candidate.type === INTEGRATION_COMPONENT_TYPE.SKILL);
|
|
4124
|
+
if (!component || component.type !== INTEGRATION_COMPONENT_TYPE.SKILL) {
|
|
4125
|
+
throw new Error(`Integration catalog entry must declare a skill component: ${entry.id}`);
|
|
4126
|
+
}
|
|
4127
|
+
return component;
|
|
4128
|
+
};
|
|
4129
|
+
var resolveCatalogHookComponent = (entry) => {
|
|
4130
|
+
const component = entry.components.find((candidate) => candidate.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK);
|
|
4131
|
+
if (!component || component.type !== INTEGRATION_COMPONENT_TYPE.CODEX_HOOK) {
|
|
4132
|
+
throw new Error(`Integration catalog entry must declare a codex-hook component: ${entry.id}`);
|
|
4133
|
+
}
|
|
4134
|
+
return component;
|
|
4135
|
+
};
|
|
4136
|
+
var resolveCatalogReceiptConfigComponents = (entry) => entry.components.filter(
|
|
4137
|
+
(component) => component.type === INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG
|
|
4138
|
+
);
|
|
4139
|
+
var loadIntegrationDefinitions = () => loadAllIntegrations(resolveIntegrationsDir()).map((entry) => {
|
|
4140
|
+
const hookComponent = resolveCatalogHookComponent(entry);
|
|
4141
|
+
return {
|
|
4142
|
+
...entry,
|
|
4143
|
+
skillComponent: resolveCatalogSkillComponent(entry),
|
|
4144
|
+
hookComponent,
|
|
4145
|
+
receiptConfigComponents: resolveCatalogReceiptConfigComponents(entry),
|
|
4146
|
+
hookDefinition: resolveCodexHookDefinition(hookComponent.id)
|
|
4147
|
+
};
|
|
4148
|
+
});
|
|
4149
|
+
var parseIntegrationId = (integrationId) => {
|
|
4150
|
+
const definition = loadIntegrationDefinitions().find((candidate) => candidate.id === integrationId);
|
|
4151
|
+
if (!definition) {
|
|
4152
|
+
throw new Error(`Unknown integration: ${integrationId}`);
|
|
4153
|
+
}
|
|
4154
|
+
return definition.id;
|
|
4155
|
+
};
|
|
4156
|
+
var resolveIntegrationDefinition = (integrationId) => {
|
|
4157
|
+
const definition = loadIntegrationDefinitions().find((candidate) => candidate.id === integrationId);
|
|
4158
|
+
if (!definition) {
|
|
4159
|
+
throw new Error(`Unknown integration: ${integrationId}`);
|
|
4160
|
+
}
|
|
4161
|
+
return definition;
|
|
4162
|
+
};
|
|
4163
|
+
var reportIntegrationError = (error) => {
|
|
4164
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
4165
|
+
p12.log.error(message);
|
|
4166
|
+
process.exitCode = 1;
|
|
4167
|
+
};
|
|
4168
|
+
var readStdin2 = async () => new Promise((resolve11, reject) => {
|
|
4169
|
+
let raw = "";
|
|
4170
|
+
process.stdin.setEncoding("utf-8");
|
|
4171
|
+
process.stdin.on("data", (chunk) => {
|
|
4172
|
+
raw += chunk;
|
|
4173
|
+
});
|
|
4174
|
+
process.stdin.on("end", () => resolve11(raw));
|
|
4175
|
+
process.stdin.on("error", reject);
|
|
4176
|
+
});
|
|
4177
|
+
var readInstalledSkills3 = (basePath) => (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
|
|
4178
|
+
...installedSkill,
|
|
4179
|
+
id: resolveCanonicalSkillId(installedSkill.id)
|
|
4180
|
+
}));
|
|
4181
|
+
var resolveSkillById2 = (skillId) => {
|
|
4182
|
+
const skill = loadAllSkills(resolveSkillsDir()).find((candidate) => candidate.id === skillId);
|
|
4183
|
+
if (!skill) {
|
|
4184
|
+
throw new Error(`Unknown skill: ${skillId}`);
|
|
4185
|
+
}
|
|
4186
|
+
return skill;
|
|
4187
|
+
};
|
|
4188
|
+
var hasInstalledCodexSkill = (params) => {
|
|
4189
|
+
const installedSkill = findInstalledSkill(readInstalledSkills3(params.basePath), params.skillId);
|
|
4190
|
+
return installedSkill?.tools.includes(SKILL_TOOL.CODEX) === true && existsSync10(join15(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
|
|
4191
|
+
};
|
|
4192
|
+
var writeUserSkillState2 = (params) => {
|
|
4193
|
+
const registryPath = resolveSkillRegistryPath(params.basePath);
|
|
4194
|
+
const previous = readSkillRegistry(registryPath);
|
|
4195
|
+
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
4196
|
+
if (skills.length === 0) {
|
|
4197
|
+
rmSync8(registryPath, { force: true });
|
|
4198
|
+
return;
|
|
4199
|
+
}
|
|
4200
|
+
writeSkillRegistry(registryPath, {
|
|
4201
|
+
skills,
|
|
4202
|
+
cliVersion: params.cliVersion,
|
|
4203
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4204
|
+
});
|
|
4205
|
+
};
|
|
4206
|
+
var ensureSkillComponent = (params) => {
|
|
4207
|
+
const skill = resolveSkillById2(params.skillId);
|
|
4208
|
+
const installedSkills = readInstalledSkills3(params.basePath);
|
|
4209
|
+
const existingInstalledSkill = findInstalledSkill(installedSkills, skill.id);
|
|
4210
|
+
const requestedTools = mergeSkillTools({
|
|
4211
|
+
existing: existingInstalledSkill?.tools,
|
|
4212
|
+
requested: [SKILL_TOOL.CODEX]
|
|
4213
|
+
});
|
|
4214
|
+
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
4215
|
+
skill,
|
|
4216
|
+
requestedTools
|
|
4217
|
+
});
|
|
4218
|
+
const alreadyCurrent = existingInstalledSkill?.sourceHash === installedSkill.sourceHash && existingInstalledSkill.tools.includes(SKILL_TOOL.CODEX) && existsSync10(join15(params.basePath, ".agents/skills", params.skillId, "SKILL.md"));
|
|
4219
|
+
if (alreadyCurrent) {
|
|
4220
|
+
return {
|
|
4221
|
+
type: INTEGRATION_COMPONENT_TYPE.SKILL,
|
|
4222
|
+
id: params.skillId,
|
|
4223
|
+
tools: [SKILL_TOOL.CODEX],
|
|
4224
|
+
owned: params.previouslyOwned
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
installSkillPackages(params.basePath, packages);
|
|
4228
|
+
writeUserSkillState2({
|
|
4229
|
+
basePath: params.basePath,
|
|
4230
|
+
cliVersion: params.cliVersion,
|
|
4231
|
+
nextSkill: installedSkill
|
|
4232
|
+
});
|
|
4233
|
+
return {
|
|
4234
|
+
type: INTEGRATION_COMPONENT_TYPE.SKILL,
|
|
4235
|
+
id: params.skillId,
|
|
4236
|
+
tools: [SKILL_TOOL.CODEX],
|
|
4237
|
+
owned: true
|
|
4238
|
+
};
|
|
4239
|
+
};
|
|
4240
|
+
var ensureHookComponent = (params) => {
|
|
4241
|
+
const command = buildCodexHookCommand({
|
|
4242
|
+
definition: params.definition,
|
|
4243
|
+
overrideCommand: params.command
|
|
4244
|
+
});
|
|
4245
|
+
const installedBefore = inspectCodexHook({
|
|
4246
|
+
hooksPath: params.hooksPath,
|
|
4247
|
+
definition: params.definition
|
|
4248
|
+
}).installed;
|
|
4249
|
+
const result = installCodexHook({
|
|
4250
|
+
hooksPath: params.hooksPath,
|
|
4251
|
+
definition: params.definition,
|
|
4252
|
+
command
|
|
4253
|
+
});
|
|
4254
|
+
return {
|
|
4255
|
+
type: INTEGRATION_COMPONENT_TYPE.CODEX_HOOK,
|
|
4256
|
+
id: params.hookId,
|
|
4257
|
+
command,
|
|
4258
|
+
owned: params.previouslyOwned || result.changed || !installedBefore
|
|
4259
|
+
};
|
|
4260
|
+
};
|
|
4261
|
+
var buildReceiptConfigComponents = (components) => components.map((component) => ({
|
|
4262
|
+
type: INTEGRATION_COMPONENT_TYPE.RECEIPT_CONFIG,
|
|
4263
|
+
id: component.id,
|
|
4264
|
+
storagePath: component.storage_path,
|
|
4265
|
+
owned: false
|
|
4266
|
+
}));
|
|
4267
|
+
var componentWasOwned = (params) => params.previous?.components.some(
|
|
4268
|
+
(component) => component.type === params.type && component.id === params.id && component.owned
|
|
4269
|
+
) ?? false;
|
|
4270
|
+
var buildInstalledIntegration = (params) => {
|
|
4271
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4272
|
+
return {
|
|
4273
|
+
id: params.definition.id,
|
|
4274
|
+
components: [...params.components],
|
|
4275
|
+
installedAt: params.previous?.installedAt ?? now,
|
|
4276
|
+
updatedAt: now
|
|
4277
|
+
};
|
|
4278
|
+
};
|
|
4279
|
+
var removeOwnedSkill = (params) => {
|
|
4280
|
+
const installedSkill = findInstalledSkill(readInstalledSkills3(params.basePath), params.skillId);
|
|
4281
|
+
if (!installedSkill) {
|
|
4282
|
+
return [];
|
|
4283
|
+
}
|
|
4284
|
+
const removed = removeDirectories(params.basePath, installedSkill.installed_paths);
|
|
4285
|
+
writeUserSkillState2({
|
|
4286
|
+
basePath: params.basePath,
|
|
4287
|
+
cliVersion: params.cliVersion,
|
|
4288
|
+
removeSkillId: params.skillId
|
|
4289
|
+
});
|
|
4290
|
+
return removed;
|
|
4291
|
+
};
|
|
4292
|
+
var formatComponentStatus = (component) => {
|
|
4293
|
+
const owner = component.owned ? "owned" : "pre-existing";
|
|
4294
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.SKILL) {
|
|
4295
|
+
return `skill:${component.id} (${owner})`;
|
|
4296
|
+
}
|
|
4297
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK) {
|
|
4298
|
+
return `codex-hook:${component.id} (${owner})`;
|
|
4299
|
+
}
|
|
4300
|
+
return `receipt-config:${component.id} (${owner})`;
|
|
4301
|
+
};
|
|
4302
|
+
var integrationListCommand = async () => {
|
|
4303
|
+
p12.intro("ai-ops integration list");
|
|
4304
|
+
try {
|
|
4305
|
+
const manifest = readIntegrationManifest(resolveIntegrationManifestPath(resolveUserBasePath()));
|
|
4306
|
+
const installed = new Set((manifest?.integrations ?? []).map((integration) => integration.id));
|
|
4307
|
+
const lines = loadIntegrationDefinitions().map((definition) => {
|
|
4308
|
+
const suffix = installed.has(definition.id) ? "installed" : "not installed";
|
|
4309
|
+
return `- ${definition.id} - ${suffix} - ${definition.description}`;
|
|
4310
|
+
});
|
|
4311
|
+
p12.log.info(lines.join("\n"));
|
|
4312
|
+
} catch (error) {
|
|
4313
|
+
reportIntegrationError(error);
|
|
4314
|
+
}
|
|
4315
|
+
p12.outro("ai-ops integration list \uC644\uB8CC");
|
|
4316
|
+
};
|
|
4317
|
+
var integrationInstallCommand = async (integrationId, opts = {}) => {
|
|
4318
|
+
p12.intro(`ai-ops integration install ${integrationId}`);
|
|
4319
|
+
try {
|
|
4320
|
+
const definition = resolveIntegrationDefinition(integrationId);
|
|
4321
|
+
const basePath = resolveUserBasePath();
|
|
4322
|
+
const cliVersion = getCliVersion();
|
|
4323
|
+
const manifestPath = resolveIntegrationManifestPath(basePath);
|
|
4324
|
+
const previous = findInstalledIntegration(readIntegrationManifest(manifestPath)?.integrations ?? [], definition.id);
|
|
4325
|
+
const skillComponent = ensureSkillComponent({
|
|
4326
|
+
basePath,
|
|
4327
|
+
cliVersion,
|
|
4328
|
+
skillId: definition.skillComponent.id,
|
|
4329
|
+
previouslyOwned: componentWasOwned({
|
|
4330
|
+
previous,
|
|
4331
|
+
type: INTEGRATION_COMPONENT_TYPE.SKILL,
|
|
4332
|
+
id: definition.skillComponent.id
|
|
4333
|
+
})
|
|
4334
|
+
});
|
|
4335
|
+
const hookComponent = ensureHookComponent({
|
|
4336
|
+
hooksPath: resolveCodexHooksPath(resolveCodexHomePath2()),
|
|
4337
|
+
hookId: definition.hookComponent.id,
|
|
4338
|
+
definition: definition.hookDefinition,
|
|
4339
|
+
command: opts.command,
|
|
4340
|
+
previouslyOwned: componentWasOwned({
|
|
4341
|
+
previous,
|
|
4342
|
+
type: INTEGRATION_COMPONENT_TYPE.CODEX_HOOK,
|
|
4343
|
+
id: definition.hookComponent.id
|
|
4344
|
+
})
|
|
4345
|
+
});
|
|
4346
|
+
const installedIntegration = buildInstalledIntegration({
|
|
4347
|
+
definition,
|
|
4348
|
+
previous,
|
|
4349
|
+
components: [skillComponent, hookComponent, ...buildReceiptConfigComponents(definition.receiptConfigComponents)]
|
|
4350
|
+
});
|
|
4351
|
+
writeUserIntegrationState({
|
|
4352
|
+
manifestPath,
|
|
4353
|
+
cliVersion,
|
|
4354
|
+
nextIntegration: installedIntegration
|
|
4355
|
+
});
|
|
4356
|
+
p12.log.success(`integration \uC124\uCE58 \uC644\uB8CC: ${definition.id}`);
|
|
4357
|
+
p12.log.info(installedIntegration.components.map(formatComponentStatus).join("\n"));
|
|
4358
|
+
} catch (error) {
|
|
4359
|
+
reportIntegrationError(error);
|
|
4360
|
+
}
|
|
4361
|
+
p12.outro("ai-ops integration install \uC644\uB8CC");
|
|
4362
|
+
};
|
|
4363
|
+
var integrationStatusCommand = async (integrationId) => {
|
|
4364
|
+
p12.intro(`ai-ops integration status ${integrationId}`);
|
|
4365
|
+
try {
|
|
4366
|
+
const definition = resolveIntegrationDefinition(integrationId);
|
|
4367
|
+
const basePath = resolveUserBasePath();
|
|
4368
|
+
const manifest = readIntegrationManifest(resolveIntegrationManifestPath(basePath));
|
|
4369
|
+
const installedIntegration = findInstalledIntegration(manifest?.integrations ?? [], definition.id);
|
|
4370
|
+
const hookStatus = inspectCodexHook({
|
|
4371
|
+
hooksPath: resolveCodexHooksPath(resolveCodexHomePath2()),
|
|
4372
|
+
definition: definition.hookDefinition
|
|
4373
|
+
});
|
|
4374
|
+
const lines = [
|
|
4375
|
+
`integration installed: ${installedIntegration ? "yes" : "no"}`,
|
|
4376
|
+
`skill installed: ${hasInstalledCodexSkill({ basePath, skillId: definition.skillComponent.id }) ? "yes" : "no"}`,
|
|
4377
|
+
`hook installed: ${hookStatus.installed ? "yes" : "no"}`,
|
|
4378
|
+
`hooks file: ${hookStatus.hooksPath}`
|
|
4379
|
+
];
|
|
4380
|
+
if (definition.id === INTEGRATION_ID.PC) {
|
|
4381
|
+
const pcStatus = getPcHandoffStatus({
|
|
4382
|
+
cwd: resolveBasePath(),
|
|
4383
|
+
contextRoot: resolvePersonalContextRoot()
|
|
4384
|
+
});
|
|
4385
|
+
lines.push(
|
|
4386
|
+
`pc context ready: ${pcStatus.ready ? "yes" : "no"}`,
|
|
4387
|
+
`pc skip reason: ${pcStatus.skipReason ?? "none"}`,
|
|
4388
|
+
`pc workspace: ${pcStatus.workspaceId ?? "not found"}`,
|
|
4389
|
+
`pc active workstream: ${pcStatus.activeWorkstreamId ?? "not found"}`,
|
|
4390
|
+
`pc last confirmed commit: ${pcStatus.lastConfirmedCommitHash ?? "not found"}`
|
|
4391
|
+
);
|
|
4392
|
+
}
|
|
4393
|
+
if (installedIntegration) {
|
|
4394
|
+
lines.push(`owned components: ${installedIntegration.components.map(formatComponentStatus).join(", ")}`);
|
|
4395
|
+
}
|
|
4396
|
+
p12.log.info(lines.join("\n"));
|
|
4397
|
+
} catch (error) {
|
|
4398
|
+
reportIntegrationError(error);
|
|
4399
|
+
}
|
|
4400
|
+
p12.outro("ai-ops integration status \uC644\uB8CC");
|
|
4401
|
+
};
|
|
4402
|
+
var integrationUninstallCommand = async (integrationId) => {
|
|
4403
|
+
p12.intro(`ai-ops integration uninstall ${integrationId}`);
|
|
4404
|
+
try {
|
|
4405
|
+
const definition = resolveIntegrationDefinition(integrationId);
|
|
4406
|
+
const basePath = resolveUserBasePath();
|
|
4407
|
+
const cliVersion = getCliVersion();
|
|
4408
|
+
const manifestPath = resolveIntegrationManifestPath(basePath);
|
|
4409
|
+
const installedIntegration = findInstalledIntegration(
|
|
4410
|
+
readIntegrationManifest(manifestPath)?.integrations ?? [],
|
|
4411
|
+
definition.id
|
|
4412
|
+
);
|
|
4413
|
+
if (!installedIntegration) {
|
|
4414
|
+
p12.log.warn("\uC124\uCE58\uB41C integration manifest entry\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4415
|
+
p12.outro("ai-ops integration uninstall \uC644\uB8CC");
|
|
4416
|
+
return;
|
|
4417
|
+
}
|
|
4418
|
+
const removed = [];
|
|
4419
|
+
for (const component of installedIntegration.components) {
|
|
4420
|
+
if (!component.owned) {
|
|
4421
|
+
continue;
|
|
4422
|
+
}
|
|
4423
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.SKILL) {
|
|
4424
|
+
removed.push(...removeOwnedSkill({ basePath, cliVersion, skillId: component.id }));
|
|
4425
|
+
}
|
|
4426
|
+
if (component.type === INTEGRATION_COMPONENT_TYPE.CODEX_HOOK) {
|
|
4427
|
+
const result = uninstallCodexHook({
|
|
4428
|
+
hooksPath: resolveCodexHooksPath(resolveCodexHomePath2()),
|
|
4429
|
+
definition: definition.hookDefinition
|
|
4430
|
+
});
|
|
4431
|
+
if (result.removed) {
|
|
4432
|
+
removed.push(result.hooksPath);
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
}
|
|
4436
|
+
writeUserIntegrationState({
|
|
4437
|
+
manifestPath,
|
|
4438
|
+
cliVersion,
|
|
4439
|
+
removeIntegrationId: definition.id
|
|
4440
|
+
});
|
|
4441
|
+
p12.log.success(removed.length > 0 ? `\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}` : "\uC81C\uAC70\uD560 owned component \uC5C6\uC74C");
|
|
4442
|
+
} catch (error) {
|
|
4443
|
+
reportIntegrationError(error);
|
|
4444
|
+
}
|
|
4445
|
+
p12.outro("ai-ops integration uninstall \uC644\uB8CC");
|
|
4446
|
+
};
|
|
4447
|
+
var integrationPostToolUseHookCommand = async (integrationId) => {
|
|
4448
|
+
try {
|
|
4449
|
+
const id = parseIntegrationId(integrationId);
|
|
4450
|
+
const raw = await readStdin2();
|
|
4451
|
+
const hookInput = raw.trim().length > 0 ? JSON.parse(raw) : {};
|
|
4452
|
+
if (id !== INTEGRATION_ID.PC) {
|
|
4453
|
+
return;
|
|
4454
|
+
}
|
|
4455
|
+
const output = evaluatePcPostToolUseHook({
|
|
4456
|
+
hookInput,
|
|
4457
|
+
contextRoot: resolvePersonalContextRoot()
|
|
4458
|
+
});
|
|
4459
|
+
if (output) {
|
|
4460
|
+
process.stdout.write(JSON.stringify(output) + "\n");
|
|
4461
|
+
}
|
|
4462
|
+
} catch (error) {
|
|
4463
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
4464
|
+
process.stdout.write(
|
|
4465
|
+
JSON.stringify({
|
|
4466
|
+
systemMessage: `ai-ops integration hook skipped: ${message}`
|
|
4467
|
+
}) + "\n"
|
|
4468
|
+
);
|
|
4469
|
+
}
|
|
4470
|
+
};
|
|
4471
|
+
|
|
3641
4472
|
// src/bin/index.ts
|
|
3642
4473
|
var program = new Command();
|
|
3643
4474
|
program.name("ai-ops").description("AI agent operating layer manager").version(getCliVersion());
|
|
@@ -3683,5 +4514,12 @@ var codexHookCommand = program.command("codex-hook").description("Codex hooks \u
|
|
|
3683
4514
|
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
4515
|
codexHookCommand.command("status <hookId>").description("Codex hook \uC124\uCE58 \uC0C1\uD0DC \uD655\uC778").action((hookId) => codexHookStatusCommand(hookId));
|
|
3685
4516
|
codexHookCommand.command("uninstall <hookId>").description("Codex hook \uC81C\uAC70").action((hookId) => codexHookUninstallCommand(hookId));
|
|
4517
|
+
var integrationCommand = program.command("integration").description("user/global runtime integration \uC124\uCE58/\uC870\uD68C/\uC81C\uAC70");
|
|
4518
|
+
integrationCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C integration \uBAA9\uB85D").action(() => integrationListCommand());
|
|
4519
|
+
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));
|
|
4520
|
+
integrationCommand.command("status <integrationId>").description("integration \uC124\uCE58 \uC0C1\uD0DC \uD655\uC778").action((integrationId) => integrationStatusCommand(integrationId));
|
|
4521
|
+
integrationCommand.command("uninstall <integrationId>").description("integration \uC81C\uAC70").action((integrationId) => integrationUninstallCommand(integrationId));
|
|
4522
|
+
var integrationHookCommand = integrationCommand.command("hook").description("integration hook \uB0B4\uBD80 \uBA85\uB839");
|
|
4523
|
+
integrationHookCommand.command("post-tool-use <integrationId>").description("Codex PostToolUse integration hook entrypoint").action((integrationId) => integrationPostToolUseHookCommand(integrationId));
|
|
3686
4524
|
program.parse();
|
|
3687
4525
|
//# sourceMappingURL=index.js.map
|