ai-ops-cli 0.2.6 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +170 -0
- package/README.md +109 -163
- package/data/context-layer/AGENTS.md +29 -0
- package/data/context-layer/CLAUDE.md +14 -0
- package/data/context-layer/GEMINI.md +14 -0
- package/data/context-layer/docs/agent/checks/impact-checklist.md +16 -0
- package/data/context-layer/docs/agent/checks/review-checklist.md +17 -0
- package/data/context-layer/docs/agent/maps/codebase-map.md +16 -0
- package/data/context-layer/docs/agent/rules/doc-update-rules.md +22 -0
- package/data/context-layer/docs/agent/rules/routing-rules.md +22 -0
- package/data/context-layer/docs/agent/rules/stop-rules.md +20 -0
- package/data/context-layer/docs/agent/workflow.md +25 -0
- package/data/context-layer/docs/business/business-rules.md +16 -0
- package/data/context-layer/docs/docs-status.md +14 -0
- package/data/packs/pack-registry.json +8 -0
- package/data/packs/spec-lifecycle/docs/specs/README.ko.md +26 -0
- package/data/packs/spec-lifecycle/docs/specs/README.md +26 -0
- package/data/packs/spec-lifecycle/docs/specs/baseline/.gitkeep +1 -0
- package/data/packs/spec-lifecycle/docs/specs/initial-build/.gitkeep +1 -0
- package/data/skills/README.ko.md +182 -0
- package/data/skills/README.md +27 -2
- package/data/skills/skill-registry.json +64 -16
- package/data/skills/task-skills/doc-impact-reviewer/SKILL.md +101 -0
- package/data/skills/task-skills/doc-impact-reviewer/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-baseline-sync/SKILL.md +134 -0
- package/data/skills/task-skills/spec-baseline-sync/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-baseline-sync/references/template.md +14 -0
- package/data/skills/task-skills/spec-product-01-idea-to-brief/SKILL.md +78 -0
- package/data/skills/task-skills/spec-product-01-idea-to-brief/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-product-01-idea-to-brief/references/template.md +36 -0
- package/data/skills/task-skills/spec-product-02-brief-to-technical-context/SKILL.md +91 -0
- package/data/skills/task-skills/spec-product-02-brief-to-technical-context/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-product-02-brief-to-technical-context/references/template.md +58 -0
- package/data/skills/task-skills/spec-product-03-brief-to-product-spec/SKILL.md +85 -0
- package/data/skills/task-skills/spec-product-03-brief-to-product-spec/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-product-03-brief-to-product-spec/references/template.md +41 -0
- package/data/skills/task-skills/spec-product-04-product-spec-to-ui-spec/SKILL.md +93 -0
- package/data/skills/task-skills/spec-product-04-product-spec-to-ui-spec/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-product-04-product-spec-to-ui-spec/references/stitch-prompt-template.md +41 -0
- package/data/skills/task-skills/spec-product-04-product-spec-to-ui-spec/references/ui-spec-template.md +39 -0
- package/data/skills/task-skills/spec-product-05-spec-to-work-packets/SKILL.md +157 -0
- package/data/skills/task-skills/spec-product-05-spec-to-work-packets/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-product-05-spec-to-work-packets/references/stitch-html-review.md +25 -0
- package/data/skills/task-skills/spec-product-05-spec-to-work-packets/references/work-packet-template.md +67 -0
- package/data/skills/task-skills/spec-shared-glossary-sync/SKILL.md +102 -0
- package/data/skills/task-skills/spec-shared-glossary-sync/agents/openai.yaml +6 -0
- package/data/skills/task-skills/spec-shared-glossary-sync/references/checklist.md +36 -0
- package/data/skills/task-skills/spec-shared-glossary-sync/references/template.md +58 -0
- package/data/subagents/README.ko.md +47 -0
- package/data/subagents/README.md +47 -0
- package/data/subagents/security-gate/PROMPT.md +18 -0
- package/data/subagents/security-gate/claude.frontmatter.yaml +8 -0
- package/data/subagents/security-gate/codex.frontmatter.toml +6 -0
- package/data/subagents/security-gate/gemini.frontmatter.yaml +6 -0
- package/data/subagents/security-reviewer/PROMPT.md +17 -0
- package/data/subagents/security-reviewer/claude.frontmatter.yaml +9 -0
- package/data/subagents/security-reviewer/codex.frontmatter.toml +6 -0
- package/data/subagents/security-reviewer/gemini.frontmatter.yaml +6 -0
- package/data/subagents/subagent-registry.json +14 -0
- package/dist/bin/index.js +2101 -1712
- package/dist/bin/index.js.map +1 -1
- package/package.json +2 -2
package/dist/bin/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
|
-
import * as
|
|
7
|
+
import * as p2 from "@clack/prompts";
|
|
8
8
|
|
|
9
9
|
// src/core/schemas/rule.schema.ts
|
|
10
10
|
import { z } from "zod";
|
|
@@ -46,17 +46,12 @@ var SKILL_KIND = {
|
|
|
46
46
|
REFERENCE: "reference",
|
|
47
47
|
TASK: "task"
|
|
48
48
|
};
|
|
49
|
-
var SKILL_SCOPE = {
|
|
50
|
-
PROJECT: "project",
|
|
51
|
-
USER: "user"
|
|
52
|
-
};
|
|
53
49
|
var SKILL_TOOL = {
|
|
54
50
|
CLAUDE_CODE: "claude-code",
|
|
55
51
|
CODEX: "codex",
|
|
56
52
|
GEMINI: "gemini"
|
|
57
53
|
};
|
|
58
54
|
var SkillKindSchema = z3.union([z3.literal(SKILL_KIND.REFERENCE), z3.literal(SKILL_KIND.TASK)]);
|
|
59
|
-
var SkillScopeSchema = z3.union([z3.literal(SKILL_SCOPE.PROJECT), z3.literal(SKILL_SCOPE.USER)]);
|
|
60
55
|
var SkillToolSchema = z3.union([
|
|
61
56
|
z3.literal(SKILL_TOOL.CLAUDE_CODE),
|
|
62
57
|
z3.literal(SKILL_TOOL.CODEX),
|
|
@@ -74,7 +69,6 @@ var InstalledSkillSchema = z3.object({
|
|
|
74
69
|
id: z3.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
75
70
|
kind: SkillKindSchema,
|
|
76
71
|
tools: z3.array(SkillToolSchema).min(1),
|
|
77
|
-
scope: SkillScopeSchema,
|
|
78
72
|
installed_paths: z3.array(z3.string().min(1)).min(1),
|
|
79
73
|
sourceHash: z3.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars")
|
|
80
74
|
}).strip();
|
|
@@ -87,7 +81,6 @@ var SkillCatalogEntrySchema = z4.object({
|
|
|
87
81
|
id: SkillIdSchema,
|
|
88
82
|
kind: SkillKindSchema,
|
|
89
83
|
supported_tools: z4.array(SkillToolSchema).min(1),
|
|
90
|
-
install_scopes: z4.array(SkillScopeSchema).min(1),
|
|
91
84
|
groups: z4.array(z4.string().min(1)),
|
|
92
85
|
included_in_presets: z4.array(z4.string().min(1)),
|
|
93
86
|
source_path: SkillCatalogPathSchema
|
|
@@ -113,43 +106,216 @@ var SkillRegistrySchema = z5.object({
|
|
|
113
106
|
generatedAt: z5.string().datetime({ offset: true })
|
|
114
107
|
}).strict();
|
|
115
108
|
|
|
116
|
-
// src/core/schemas/
|
|
109
|
+
// src/core/schemas/subagent.schema.ts
|
|
110
|
+
import { z as z7 } from "zod";
|
|
111
|
+
|
|
112
|
+
// src/core/schemas/project-layer.schema.ts
|
|
117
113
|
import { z as z6 } from "zod";
|
|
118
|
-
var
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
var ProjectLayerToolSchema = z6.enum(["claude-code", "codex", "gemini"]);
|
|
115
|
+
var ProjectLayerDocumentStatusSchema = z6.enum(["Active", "Reserved", "Draft", "Archived"]);
|
|
116
|
+
var ShortHashSchema = z6.string().regex(/^[a-f0-9]{6}$/, "hash must be 6 lowercase hex chars");
|
|
117
|
+
var isSafeProjectLayerPath = (value) => {
|
|
118
|
+
if (value.length === 0) return false;
|
|
119
|
+
if (value.includes("\0")) return false;
|
|
120
|
+
if (value.includes("\\")) return false;
|
|
121
|
+
if (value.startsWith("/")) return false;
|
|
122
|
+
if (/^[A-Za-z]:/.test(value)) return false;
|
|
123
|
+
const segments = value.split("/");
|
|
124
|
+
return segments.every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
|
|
125
|
+
};
|
|
126
|
+
var ProjectLayerPathSchema = z6.string().min(1).refine(isSafeProjectLayerPath, "path must be a safe project-relative path");
|
|
127
|
+
var ProjectLayerFrontmatterSchema = z6.object({
|
|
128
|
+
status: ProjectLayerDocumentStatusSchema,
|
|
129
|
+
layer: z6.string().min(1),
|
|
130
|
+
owner: z6.string().min(1),
|
|
131
|
+
read_when: z6.array(z6.string().min(1)).min(1),
|
|
132
|
+
update_when: z6.array(z6.string().min(1)).min(1)
|
|
133
|
+
}).strict();
|
|
134
|
+
var ProjectLayerManagedFileSchema = z6.object({
|
|
135
|
+
path: ProjectLayerPathSchema,
|
|
136
|
+
sourceHash: ShortHashSchema
|
|
137
|
+
}).strict();
|
|
138
|
+
var ProjectLayerProjectFileSchema = z6.object({
|
|
139
|
+
path: ProjectLayerPathSchema,
|
|
140
|
+
templateHash: ShortHashSchema,
|
|
141
|
+
created: z6.boolean()
|
|
142
|
+
}).strict();
|
|
143
|
+
var ProjectLayerPackFileRecordSchema = z6.object({
|
|
144
|
+
path: ProjectLayerPathSchema,
|
|
145
|
+
sourceHash: ShortHashSchema
|
|
146
|
+
}).strict();
|
|
147
|
+
var ProjectLayerPackRecordSchema = z6.object({
|
|
148
|
+
id: z6.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
149
|
+
sourceHash: ShortHashSchema,
|
|
150
|
+
documents: z6.array(ProjectLayerPackFileRecordSchema),
|
|
151
|
+
files: z6.array(ProjectLayerPackFileRecordSchema),
|
|
152
|
+
installedAt: z6.string().datetime({ offset: true })
|
|
153
|
+
}).strict();
|
|
154
|
+
var ProjectLayerManifestSchema = z6.object({
|
|
155
|
+
schemaVersion: z6.literal(1),
|
|
156
|
+
kind: z6.literal("project-operating-layer"),
|
|
157
|
+
tools: z6.array(ProjectLayerToolSchema).min(1),
|
|
158
|
+
managed_files: z6.array(ProjectLayerManagedFileSchema),
|
|
159
|
+
project_files: z6.array(ProjectLayerProjectFileSchema),
|
|
160
|
+
packs: z6.array(ProjectLayerPackRecordSchema).default([]),
|
|
161
|
+
settings: z6.record(z6.unknown()),
|
|
162
|
+
sourceHash: ShortHashSchema,
|
|
163
|
+
cliVersion: z6.string().min(1),
|
|
164
|
+
generatedAt: z6.string().datetime({ offset: true })
|
|
165
|
+
}).strict();
|
|
166
|
+
var ProjectLayerContextDocumentSchema = ProjectLayerFrontmatterSchema.extend({
|
|
167
|
+
path: ProjectLayerPathSchema,
|
|
168
|
+
contentHash: ShortHashSchema
|
|
169
|
+
}).strict();
|
|
170
|
+
var ProjectLayerContextIndexSchema = z6.object({
|
|
171
|
+
schemaVersion: z6.literal(1),
|
|
172
|
+
kind: z6.literal("context-layer-index"),
|
|
173
|
+
documents: z6.array(ProjectLayerContextDocumentSchema),
|
|
174
|
+
generatedAt: z6.string().datetime({ offset: true })
|
|
175
|
+
}).strict();
|
|
176
|
+
|
|
177
|
+
// src/core/subagent-paths.ts
|
|
178
|
+
import { join } from "path";
|
|
179
|
+
var SUBAGENT_TOOL_OUTPUTS = {
|
|
180
|
+
"claude-code": {
|
|
181
|
+
dir: ".claude/agents",
|
|
182
|
+
extension: ".md"
|
|
183
|
+
},
|
|
184
|
+
codex: {
|
|
185
|
+
dir: ".codex/agents",
|
|
186
|
+
extension: ".toml"
|
|
187
|
+
},
|
|
188
|
+
gemini: {
|
|
189
|
+
dir: ".gemini/agents",
|
|
190
|
+
extension: ".md"
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var buildSubagentRelativePath = (subagentId, toolId) => {
|
|
194
|
+
const output = SUBAGENT_TOOL_OUTPUTS[toolId];
|
|
195
|
+
return join(output.dir, `${subagentId}${output.extension}`);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// src/core/schemas/subagent.schema.ts
|
|
199
|
+
var SubagentIdSchema = z7.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case");
|
|
200
|
+
var SubagentMarkdownFrontmatterSchema = z7.object({
|
|
201
|
+
name: SubagentIdSchema,
|
|
202
|
+
description: z7.string().min(1)
|
|
203
|
+
}).passthrough();
|
|
204
|
+
var TomlValueSchema = z7.union([z7.string(), z7.number(), z7.boolean(), z7.array(z7.string())]);
|
|
205
|
+
var SubagentInstalledPathSchema = z7.string().min(1).refine(isSafeProjectLayerPath, "installed path must be safe relative path");
|
|
206
|
+
var CodexSubagentFrontmatterSchema = z7.object({
|
|
207
|
+
name: SubagentIdSchema,
|
|
208
|
+
description: z7.string().min(1),
|
|
209
|
+
skill_names: z7.array(SubagentIdSchema).optional()
|
|
210
|
+
}).catchall(TomlValueSchema);
|
|
211
|
+
var InstalledSubagentSchema = z7.object({
|
|
212
|
+
id: SubagentIdSchema,
|
|
213
|
+
tools: z7.array(SkillToolSchema).min(1),
|
|
214
|
+
installed_paths: z7.array(SubagentInstalledPathSchema).min(1),
|
|
215
|
+
sourceHash: z7.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars")
|
|
216
|
+
}).strip().superRefine((subagent, ctx) => {
|
|
217
|
+
const expectedPaths = new Set(subagent.tools.map((tool) => buildSubagentRelativePath(subagent.id, tool)));
|
|
218
|
+
const installedPaths = new Set(subagent.installed_paths);
|
|
219
|
+
if (installedPaths.size !== subagent.installed_paths.length) {
|
|
220
|
+
ctx.addIssue({
|
|
221
|
+
code: z7.ZodIssueCode.custom,
|
|
222
|
+
path: ["installed_paths"],
|
|
223
|
+
message: "installed_paths must not contain duplicates"
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (installedPaths.size !== expectedPaths.size) {
|
|
228
|
+
ctx.addIssue({
|
|
229
|
+
code: z7.ZodIssueCode.custom,
|
|
230
|
+
path: ["installed_paths"],
|
|
231
|
+
message: "installed_paths must match id and tools"
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
for (const installedPath of installedPaths) {
|
|
236
|
+
if (!expectedPaths.has(installedPath)) {
|
|
237
|
+
ctx.addIssue({
|
|
238
|
+
code: z7.ZodIssueCode.custom,
|
|
239
|
+
path: ["installed_paths"],
|
|
240
|
+
message: "installed_paths must match id and tools"
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// src/core/schemas/subagent-catalog.schema.ts
|
|
248
|
+
import { z as z8 } from "zod";
|
|
249
|
+
var SubagentCatalogPathSchema = z8.string().regex(
|
|
250
|
+
/^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)*$/,
|
|
251
|
+
"source_path must be relative kebab-case path"
|
|
252
|
+
);
|
|
253
|
+
var SubagentCatalogEntrySchema = z8.object({
|
|
254
|
+
id: SubagentIdSchema,
|
|
255
|
+
supported_tools: z8.array(SkillToolSchema).min(1),
|
|
256
|
+
source_path: SubagentCatalogPathSchema
|
|
257
|
+
}).strict();
|
|
258
|
+
var SubagentCatalogSchema = z8.object({
|
|
259
|
+
subagents: z8.array(SubagentCatalogEntrySchema)
|
|
260
|
+
}).strict();
|
|
261
|
+
|
|
262
|
+
// src/core/schemas/subagent-manifest.schema.ts
|
|
263
|
+
import { z as z9 } from "zod";
|
|
264
|
+
var SubagentManifestSchema = z9.object({
|
|
265
|
+
subagents: z9.array(InstalledSubagentSchema),
|
|
266
|
+
cliVersion: z9.string().optional(),
|
|
267
|
+
generatedAt: z9.string().datetime({ offset: true })
|
|
268
|
+
}).strict();
|
|
269
|
+
|
|
270
|
+
// src/core/schemas/pack.schema.ts
|
|
271
|
+
import { z as z10 } from "zod";
|
|
272
|
+
var PackIdSchema = z10.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case");
|
|
273
|
+
var PackSourcePathSchema = z10.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*(?:\/[a-z0-9]+(?:-[a-z0-9]+)*)*$/, "source_path must be relative kebab-case path");
|
|
274
|
+
var PackCatalogEntrySchema = z10.object({
|
|
275
|
+
id: PackIdSchema,
|
|
276
|
+
source_path: PackSourcePathSchema
|
|
277
|
+
}).strict();
|
|
278
|
+
var PackCatalogSchema = z10.object({
|
|
279
|
+
packs: z10.array(PackCatalogEntrySchema)
|
|
280
|
+
}).strict();
|
|
281
|
+
|
|
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()
|
|
122
288
|
}).strict();
|
|
123
|
-
var WorkspaceEntrySchema =
|
|
124
|
-
preset:
|
|
125
|
-
rules:
|
|
289
|
+
var WorkspaceEntrySchema = z11.object({
|
|
290
|
+
preset: z11.string().min(1),
|
|
291
|
+
rules: z11.array(z11.string().min(1))
|
|
126
292
|
}).strict();
|
|
127
|
-
var ManifestSchema =
|
|
128
|
-
tools:
|
|
129
|
-
scope:
|
|
293
|
+
var ManifestSchema = z11.object({
|
|
294
|
+
tools: z11.array(z11.string().min(1)).min(1),
|
|
295
|
+
scope: z11.literal("project"),
|
|
130
296
|
/** 비모노레포 단일 preset */
|
|
131
|
-
preset:
|
|
297
|
+
preset: z11.string().min(1).optional(),
|
|
132
298
|
/** 모노레포: workspace path → { preset, rules } */
|
|
133
|
-
workspaces:
|
|
134
|
-
installed_rules:
|
|
299
|
+
workspaces: z11.record(z11.string(), WorkspaceEntrySchema).optional(),
|
|
300
|
+
installed_rules: z11.array(z11.string().min(1)),
|
|
135
301
|
/** 실제 디스크에 쓰여진 파일 상대 경로 목록 (uninstall용). 기존 manifest 호환성 위해 optional */
|
|
136
|
-
installed_files:
|
|
302
|
+
installed_files: z11.array(z11.string().min(1)).optional(),
|
|
137
303
|
/** skill 설치 루트 디렉토리 목록 */
|
|
138
|
-
installed_skills:
|
|
304
|
+
installed_skills: z11.array(InstalledSkillSchema).optional(),
|
|
139
305
|
/** non-managed 파일에 섹션을 append한 경우 추적 (uninstall 시 섹션만 제거) */
|
|
140
|
-
appended_files:
|
|
306
|
+
appended_files: z11.array(z11.string().min(1)).optional(),
|
|
141
307
|
/** init 시 선택된 settings 항목 — update 시 재생성에 사용 */
|
|
142
308
|
settings: SettingsConfigSchema.optional(),
|
|
143
309
|
/** init/update 실행 시점의 CLI 패키지 버전 — 버전 변경 감지에 사용 */
|
|
144
|
-
cliVersion:
|
|
310
|
+
cliVersion: z11.string().optional(),
|
|
145
311
|
/** SSOT 데이터 파일들의 deterministic SHA-256 해시 (6자리 hex). diff/update 판단 기준 */
|
|
146
|
-
sourceHash:
|
|
147
|
-
generatedAt:
|
|
312
|
+
sourceHash: z11.string().regex(/^[a-f0-9]{6}$/, "sourceHash must be 6 lowercase hex chars"),
|
|
313
|
+
generatedAt: z11.string().datetime({ offset: true })
|
|
148
314
|
}).strict();
|
|
149
315
|
|
|
150
316
|
// src/core/loader.ts
|
|
151
317
|
import { readFileSync, readdirSync } from "fs";
|
|
152
|
-
import { join, resolve } from "path";
|
|
318
|
+
import { join as join2, resolve } from "path";
|
|
153
319
|
import { parse as parse2 } from "yaml";
|
|
154
320
|
|
|
155
321
|
// src/core/frontmatter.ts
|
|
@@ -165,68 +331,83 @@ var parseMarkdownFrontmatter = (content) => {
|
|
|
165
331
|
};
|
|
166
332
|
};
|
|
167
333
|
|
|
168
|
-
// src/core/
|
|
169
|
-
var
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!found) {
|
|
181
|
-
const suffix = context ? ` (from ${context})` : "";
|
|
182
|
-
throw new Error(`Rule not found: ${ruleId}${suffix}`);
|
|
334
|
+
// src/core/subagent-toml.ts
|
|
335
|
+
var parseTomlValue = (value) => {
|
|
336
|
+
const trimmed = value.trim();
|
|
337
|
+
if (trimmed.startsWith('"') || trimmed.startsWith("[")) {
|
|
338
|
+
const parsed = JSON.parse(trimmed);
|
|
339
|
+
if (typeof parsed === "string") {
|
|
340
|
+
return parsed;
|
|
341
|
+
}
|
|
342
|
+
if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) {
|
|
343
|
+
return parsed;
|
|
344
|
+
}
|
|
345
|
+
throw new Error(`Unsupported TOML value: ${value}`);
|
|
183
346
|
}
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
347
|
+
if (trimmed === "true") return true;
|
|
348
|
+
if (trimmed === "false") return false;
|
|
349
|
+
const numericValue = Number(trimmed);
|
|
350
|
+
if (Number.isFinite(numericValue)) {
|
|
351
|
+
return numericValue;
|
|
352
|
+
}
|
|
353
|
+
throw new Error(`Unsupported TOML value: ${value}`);
|
|
190
354
|
};
|
|
191
|
-
var
|
|
192
|
-
|
|
355
|
+
var parseFlatToml = (content) => {
|
|
356
|
+
const result = {};
|
|
357
|
+
const lines = content.split("\n");
|
|
358
|
+
for (const line of lines) {
|
|
359
|
+
const trimmed = line.trim();
|
|
360
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const match = trimmed.match(/^([A-Za-z0-9_-]+)\s*=\s*(.+)$/);
|
|
364
|
+
if (!match) {
|
|
365
|
+
throw new Error(`Unsupported TOML line: ${line}`);
|
|
366
|
+
}
|
|
367
|
+
const [, key, value] = match;
|
|
368
|
+
result[key] = parseTomlValue(value);
|
|
369
|
+
}
|
|
370
|
+
return result;
|
|
193
371
|
};
|
|
194
|
-
var
|
|
195
|
-
|
|
196
|
-
|
|
372
|
+
var renderTomlValue = (value) => {
|
|
373
|
+
if (typeof value === "string") {
|
|
374
|
+
return JSON.stringify(value);
|
|
375
|
+
}
|
|
376
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
377
|
+
return String(value);
|
|
378
|
+
}
|
|
379
|
+
return `[${value.map((item) => JSON.stringify(item)).join(", ")}]`;
|
|
197
380
|
};
|
|
381
|
+
var renderFlatToml = (entries) => entries.map(([key, value]) => `${key} = ${renderTomlValue(value)}`).join("\n");
|
|
382
|
+
|
|
383
|
+
// src/core/loader.ts
|
|
198
384
|
var loadSkillDirectoryFiles = (skillDir) => {
|
|
199
385
|
const files = [];
|
|
200
386
|
const walk = (relativeDir = "") => {
|
|
201
|
-
const absDir = relativeDir.length > 0 ?
|
|
387
|
+
const absDir = relativeDir.length > 0 ? join2(skillDir, relativeDir) : skillDir;
|
|
202
388
|
const entries = readdirSync(absDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
203
389
|
for (const entry of entries) {
|
|
204
|
-
const nextRelativePath = relativeDir.length > 0 ?
|
|
390
|
+
const nextRelativePath = relativeDir.length > 0 ? join2(relativeDir, entry.name) : entry.name;
|
|
205
391
|
if (entry.isDirectory()) {
|
|
206
392
|
walk(nextRelativePath);
|
|
207
393
|
continue;
|
|
208
394
|
}
|
|
209
395
|
files.push({
|
|
210
396
|
path: nextRelativePath,
|
|
211
|
-
content: readFileSync(
|
|
397
|
+
content: readFileSync(join2(skillDir, nextRelativePath), "utf-8")
|
|
212
398
|
});
|
|
213
399
|
}
|
|
214
400
|
};
|
|
215
401
|
walk();
|
|
216
402
|
return files;
|
|
217
403
|
};
|
|
218
|
-
var loadAllRules = (rulesDir) => {
|
|
219
|
-
const files = readdirSync(rulesDir).filter((f) => f.endsWith(".yaml")).sort();
|
|
220
|
-
const rules = files.map((f) => loadRuleFile(resolve(rulesDir, f)));
|
|
221
|
-
return sortRulesByPriority(rules);
|
|
222
|
-
};
|
|
223
404
|
var loadSkillCatalog = (skillsDir) => SkillCatalogSchema.parse(JSON.parse(readFileSync(resolve(skillsDir, "skill-registry.json"), "utf-8")));
|
|
224
405
|
var loadAllSkills = (skillsDir) => {
|
|
225
406
|
const catalog = loadSkillCatalog(skillsDir);
|
|
226
407
|
const entries = [...catalog.skills].sort((a, b) => a.id.localeCompare(b.id));
|
|
227
408
|
return entries.map((entry) => {
|
|
228
409
|
const directory = resolve(skillsDir, entry.source_path);
|
|
229
|
-
const skillMdPath =
|
|
410
|
+
const skillMdPath = join2(directory, "SKILL.md");
|
|
230
411
|
const rawSkillMd = readFileSync(skillMdPath, "utf-8");
|
|
231
412
|
const { frontmatter } = parseMarkdownFrontmatter(rawSkillMd);
|
|
232
413
|
const parsed = SkillFrontmatterSchema.parse(frontmatter);
|
|
@@ -242,7 +423,6 @@ var loadAllSkills = (skillsDir) => {
|
|
|
242
423
|
kind: entry.kind,
|
|
243
424
|
description: parsed.description,
|
|
244
425
|
supported_tools: [...entry.supported_tools],
|
|
245
|
-
install_scopes: [...entry.install_scopes],
|
|
246
426
|
groups: [...entry.groups],
|
|
247
427
|
included_in_presets: [...entry.included_in_presets],
|
|
248
428
|
directory,
|
|
@@ -250,191 +430,69 @@ var loadAllSkills = (skillsDir) => {
|
|
|
250
430
|
};
|
|
251
431
|
});
|
|
252
432
|
};
|
|
253
|
-
var
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// src/core/renderer.ts
|
|
260
|
-
import { join as join2 } from "path";
|
|
261
|
-
|
|
262
|
-
// src/core/tool-output.ts
|
|
263
|
-
var GLOBAL_CATEGORIES = ["persona", "communication", "philosophy", "convention", "standard"];
|
|
264
|
-
var CLAUDE_CODE_PATH_GLOBS = {
|
|
265
|
-
typescript: ["**/*.ts", "**/*.tsx"],
|
|
266
|
-
"react-typescript": ["**/*.tsx", "**/*.jsx"],
|
|
267
|
-
nextjs: ["**/app/**", "next.config.*", "**/middleware.ts"],
|
|
268
|
-
nestjs: ["**/*.module.ts", "**/*.controller.ts", "**/*.service.ts"],
|
|
269
|
-
"nestjs-graphql": ["**/*.resolver.ts"],
|
|
270
|
-
"graphql-core": ["**/*.graphql", "**/*.gql"],
|
|
271
|
-
"graphql-client-web": ["**/*.graphql", "**/*.gql", "**/*.tsx", "**/*.ts"],
|
|
272
|
-
"graphql-client-app": ["**/*.graphql", "**/*.gql", "lib/**/*.dart"],
|
|
273
|
-
"graphql-server": ["**/*.graphql", "**/*.gql", "**/*.resolver.ts"],
|
|
274
|
-
"prisma-postgresql": ["prisma/**", "**/*.prisma"],
|
|
275
|
-
"shadcn-ui": ["**/components/ui/**"],
|
|
276
|
-
flutter: ["lib/**/*.dart"],
|
|
277
|
-
python: ["**/*.py"],
|
|
278
|
-
fastapi: ["**/routers/**", "**/main.py"],
|
|
279
|
-
sqlalchemy: ["**/models/**/*.py", "alembic/**"],
|
|
280
|
-
"data-pipeline-python": ["**/pipelines/**", "**/etl/**"],
|
|
281
|
-
"ai-llm-python": ["**/agents/**", "**/chains/**"],
|
|
282
|
-
"libs-frontend-web": ["**/*.tsx", "**/*.ts"],
|
|
283
|
-
"libs-frontend-app": ["lib/**/*.dart"],
|
|
284
|
-
"libs-backend-ts": ["**/*.ts"],
|
|
285
|
-
"libs-backend-python": ["**/*.py"]
|
|
286
|
-
};
|
|
287
|
-
var TOOL_OUTPUT_MAP = {
|
|
288
|
-
"claude-code": {
|
|
289
|
-
mode: "multi-file",
|
|
290
|
-
rulesDir: ".claude/rules",
|
|
291
|
-
fileExtension: ".md",
|
|
292
|
-
// single: path-scoped (paths: frontmatter) / monorepo: hierarchical ({workspace}/CLAUDE.md)
|
|
293
|
-
contextStrategy: "hybrid"
|
|
294
|
-
},
|
|
295
|
-
codex: {
|
|
296
|
-
mode: "multi-file",
|
|
297
|
-
dir: "",
|
|
298
|
-
rootFileName: "AGENTS.md",
|
|
299
|
-
// global 룰
|
|
300
|
-
domainFileName: "AGENTS.override.md",
|
|
301
|
-
// domain 룰 (하위 폴더)
|
|
302
|
-
contextStrategy: "hierarchical"
|
|
303
|
-
// 루트 + 하위 폴더 JIT
|
|
304
|
-
},
|
|
305
|
-
gemini: {
|
|
306
|
-
mode: "multi-file",
|
|
307
|
-
dir: "",
|
|
308
|
-
rootFileName: "GEMINI.md",
|
|
309
|
-
// global 룰 (루트)
|
|
310
|
-
domainFileName: "GEMINI.md",
|
|
311
|
-
// domain 룰 (하위 폴더)
|
|
312
|
-
contextStrategy: "hierarchical"
|
|
313
|
-
// 루트 + 하위 폴더 JIT
|
|
433
|
+
var readRequiredTextFile = (filePath) => {
|
|
434
|
+
try {
|
|
435
|
+
return readFileSync(filePath, "utf-8");
|
|
436
|
+
} catch (error) {
|
|
437
|
+
const cause = error instanceof Error ? `: ${error.message}` : "";
|
|
438
|
+
throw new Error(`Required subagent source file is missing: ${filePath}${cause}`);
|
|
314
439
|
}
|
|
315
440
|
};
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
var renderDecisionTable = (entries) => {
|
|
320
|
-
const escape = (s) => s.replace(/\|/g, "|");
|
|
321
|
-
const hasAvoid = entries.some((e) => e.avoid !== void 0);
|
|
322
|
-
const header = hasAvoid ? "| When | Then | Avoid |\n|------|------|-------|" : "| When | Then |\n|------|------|";
|
|
323
|
-
const rows = entries.map((e) => {
|
|
324
|
-
const when = escape(e.when);
|
|
325
|
-
const then = escape(e.then);
|
|
326
|
-
if (hasAvoid) {
|
|
327
|
-
const avoid = e.avoid ? escape(e.avoid) : "";
|
|
328
|
-
return `| ${when} | ${then} | ${avoid} |`;
|
|
329
|
-
}
|
|
330
|
-
return `| ${when} | ${then} |`;
|
|
331
|
-
});
|
|
332
|
-
return [header, ...rows].join("\n");
|
|
333
|
-
};
|
|
334
|
-
var renderRuleToMarkdown = (rule) => {
|
|
335
|
-
const sections = [`# ${ruleIdToTitle(rule.id)}`];
|
|
336
|
-
if (rule.content.constraints.length > 0) {
|
|
337
|
-
sections.push("## Constraints");
|
|
338
|
-
sections.push(rule.content.constraints.map((c) => `- ${c}`).join("\n"));
|
|
339
|
-
}
|
|
340
|
-
if (rule.content.guidelines.length > 0) {
|
|
341
|
-
sections.push("## Guidelines");
|
|
342
|
-
sections.push(rule.content.guidelines.map((g) => `- ${g}`).join("\n"));
|
|
343
|
-
}
|
|
344
|
-
if (rule.content.decision_table && rule.content.decision_table.length > 0) {
|
|
345
|
-
sections.push("## Decision Table");
|
|
346
|
-
sections.push(renderDecisionTable(rule.content.decision_table));
|
|
347
|
-
}
|
|
348
|
-
return sections.join("\n\n");
|
|
349
|
-
};
|
|
350
|
-
var renderRulesToMarkdown = (rules) => rules.map((rule) => renderRuleToMarkdown(rule)).join("\n\n---\n\n");
|
|
351
|
-
var isGlobalRule = (rule) => GLOBAL_CATEGORIES.includes(rule.category);
|
|
352
|
-
var partitionRules = (rules) => {
|
|
353
|
-
const global = [];
|
|
354
|
-
const domain = [];
|
|
355
|
-
for (const rule of rules) {
|
|
356
|
-
if (isGlobalRule(rule)) {
|
|
357
|
-
global.push(rule);
|
|
358
|
-
} else {
|
|
359
|
-
domain.push(rule);
|
|
360
|
-
}
|
|
441
|
+
var assertSubagentFrontmatterName = (params) => {
|
|
442
|
+
if (params.name !== params.id) {
|
|
443
|
+
throw new Error(`Subagent ${params.tool} frontmatter name mismatch: ${params.id} != ${params.name}`);
|
|
361
444
|
}
|
|
362
|
-
return { global, domain };
|
|
363
445
|
};
|
|
364
|
-
var
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
446
|
+
var loadSubagentCatalog = (subagentsDir) => SubagentCatalogSchema.parse(JSON.parse(readFileSync(resolve(subagentsDir, "subagent-registry.json"), "utf-8")));
|
|
447
|
+
var loadAllSubagents = (subagentsDir) => {
|
|
448
|
+
const catalog = loadSubagentCatalog(subagentsDir);
|
|
449
|
+
const entries = [...catalog.subagents].sort((a, b) => a.id.localeCompare(b.id));
|
|
450
|
+
return entries.map((entry) => {
|
|
451
|
+
const directory = resolve(subagentsDir, entry.source_path);
|
|
452
|
+
const prompt = readRequiredTextFile(join2(directory, "PROMPT.md"));
|
|
453
|
+
const claudeRaw = readRequiredTextFile(join2(directory, "claude.frontmatter.yaml"));
|
|
454
|
+
const codexRaw = readRequiredTextFile(join2(directory, "codex.frontmatter.toml"));
|
|
455
|
+
const geminiRaw = readRequiredTextFile(join2(directory, "gemini.frontmatter.yaml"));
|
|
456
|
+
const claude = SubagentMarkdownFrontmatterSchema.parse(parse2(claudeRaw));
|
|
457
|
+
const codex = CodexSubagentFrontmatterSchema.parse(parseFlatToml(codexRaw));
|
|
458
|
+
const gemini = SubagentMarkdownFrontmatterSchema.parse(parse2(geminiRaw));
|
|
459
|
+
assertSubagentFrontmatterName({ id: entry.id, tool: "claude", name: claude.name });
|
|
460
|
+
assertSubagentFrontmatterName({ id: entry.id, tool: "codex", name: codex.name });
|
|
461
|
+
assertSubagentFrontmatterName({ id: entry.id, tool: "gemini", name: gemini.name });
|
|
462
|
+
return {
|
|
463
|
+
id: entry.id,
|
|
464
|
+
supported_tools: [...entry.supported_tools],
|
|
465
|
+
source_path: entry.source_path,
|
|
466
|
+
directory,
|
|
467
|
+
prompt,
|
|
468
|
+
frontmatter: {
|
|
469
|
+
claude: {
|
|
470
|
+
raw: claudeRaw,
|
|
471
|
+
parsed: claude
|
|
472
|
+
},
|
|
473
|
+
codex: {
|
|
474
|
+
raw: codexRaw,
|
|
475
|
+
parsed: codex
|
|
476
|
+
},
|
|
477
|
+
gemini: {
|
|
478
|
+
raw: geminiRaw,
|
|
479
|
+
parsed: gemini
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
});
|
|
370
484
|
};
|
|
371
|
-
var renderClaudeCodeRule = (rule) => {
|
|
372
|
-
const globs = CLAUDE_CODE_PATH_GLOBS[rule.id];
|
|
373
|
-
if (!isGlobalRule(rule) && globs !== void 0) {
|
|
374
|
-
return `${renderFrontmatter(globs)}
|
|
375
485
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return renderRuleToMarkdown(rule);
|
|
379
|
-
};
|
|
380
|
-
var renderForTool = (toolId, rules, workspaceMappings) => {
|
|
381
|
-
const config = TOOL_OUTPUT_MAP[toolId];
|
|
382
|
-
if (toolId === "claude-code") {
|
|
383
|
-
const { rulesDir, fileExtension } = config;
|
|
384
|
-
if (!workspaceMappings || workspaceMappings.length === 0) {
|
|
385
|
-
const files = rules.map((rule) => ({
|
|
386
|
-
relativePath: join2(rulesDir, `${rule.id}${fileExtension}`),
|
|
387
|
-
content: renderClaudeCodeRule(rule)
|
|
388
|
-
}));
|
|
389
|
-
return { tool: "claude-code", files };
|
|
390
|
-
}
|
|
391
|
-
const { global: global2, domain: domain2 } = partitionRules(rules);
|
|
392
|
-
const globalFiles = global2.map((rule) => ({
|
|
393
|
-
relativePath: join2(rulesDir, `${rule.id}${fileExtension}`),
|
|
394
|
-
content: renderRuleToMarkdown(rule)
|
|
395
|
-
// global은 frontmatter 불필요
|
|
396
|
-
}));
|
|
397
|
-
const workspaceFiles = [];
|
|
398
|
-
for (const ws of workspaceMappings) {
|
|
399
|
-
const wsRules = domain2.filter((r) => ws.ruleIds.includes(r.id));
|
|
400
|
-
if (wsRules.length === 0) continue;
|
|
401
|
-
workspaceFiles.push({
|
|
402
|
-
relativePath: join2(ws.path, "CLAUDE.md"),
|
|
403
|
-
content: renderRulesToMarkdown(wsRules)
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
return { tool: "claude-code", files: [...globalFiles, ...workspaceFiles] };
|
|
407
|
-
}
|
|
408
|
-
if (!workspaceMappings || workspaceMappings.length === 0) {
|
|
409
|
-
const rootContent2 = renderRulesToMarkdown(rules);
|
|
410
|
-
const domainFiles2 = [];
|
|
411
|
-
if (toolId === "codex") return { tool: "codex", rootContent: rootContent2, domainFiles: domainFiles2 };
|
|
412
|
-
return { tool: "gemini", rootContent: rootContent2, domainFiles: domainFiles2 };
|
|
413
|
-
}
|
|
414
|
-
const { global, domain } = partitionRules(rules);
|
|
415
|
-
const rootContent = renderRulesToMarkdown(global);
|
|
416
|
-
const domainFiles = [];
|
|
417
|
-
for (const ws of workspaceMappings) {
|
|
418
|
-
const wsRules = domain.filter((r) => ws.ruleIds.includes(r.id));
|
|
419
|
-
if (wsRules.length === 0) continue;
|
|
420
|
-
domainFiles.push({
|
|
421
|
-
workspacePath: ws.path,
|
|
422
|
-
content: renderRulesToMarkdown(wsRules)
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
if (toolId === "codex") {
|
|
426
|
-
return { tool: "codex", rootContent, domainFiles };
|
|
427
|
-
}
|
|
428
|
-
return { tool: "gemini", rootContent, domainFiles };
|
|
429
|
-
};
|
|
486
|
+
// src/core/renderer.ts
|
|
487
|
+
import { join as join3 } from "path";
|
|
430
488
|
|
|
431
489
|
// src/core/skill-renderer.ts
|
|
432
|
-
import { join as
|
|
490
|
+
import { join as join5 } from "path";
|
|
433
491
|
|
|
434
492
|
// src/core/source-hash.ts
|
|
435
493
|
import { createHash } from "crypto";
|
|
436
494
|
import { existsSync, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
437
|
-
import { dirname, join as
|
|
495
|
+
import { dirname, join as join4, resolve as resolve2 } from "path";
|
|
438
496
|
import { fileURLToPath } from "url";
|
|
439
497
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
440
498
|
var getCliVersion = () => {
|
|
@@ -447,68 +505,8 @@ var getCliVersion = () => {
|
|
|
447
505
|
}
|
|
448
506
|
};
|
|
449
507
|
var computeHash = (contents) => createHash("sha256").update(contents.join("")).digest("hex").slice(0, 6);
|
|
450
|
-
var loadSortedFileContents = (dirPath) => {
|
|
451
|
-
const files = readdirSync2(dirPath).filter((f) => f.endsWith(".yaml")).sort();
|
|
452
|
-
return files.map((f) => readFileSync2(resolve2(dirPath, f), "utf-8"));
|
|
453
|
-
};
|
|
454
|
-
var loadDirectoryContents = (baseDir) => {
|
|
455
|
-
const contents = [];
|
|
456
|
-
const walk = (relativeDir = "") => {
|
|
457
|
-
const absDir = relativeDir.length > 0 ? join3(baseDir, relativeDir) : baseDir;
|
|
458
|
-
const entries = readdirSync2(absDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
459
|
-
for (const entry of entries) {
|
|
460
|
-
const nextRelativePath = relativeDir.length > 0 ? join3(relativeDir, entry.name) : entry.name;
|
|
461
|
-
if (entry.isDirectory()) {
|
|
462
|
-
walk(nextRelativePath);
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
contents.push(`${nextRelativePath}:${readFileSync2(join3(baseDir, nextRelativePath), "utf-8")}`);
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
walk();
|
|
469
|
-
return contents;
|
|
470
|
-
};
|
|
471
|
-
var loadSkillTreeContents = (skillsDir) => {
|
|
472
|
-
const contents = [];
|
|
473
|
-
const registryPath = resolve2(skillsDir, "skill-registry.json");
|
|
474
|
-
if (existsSync(registryPath)) {
|
|
475
|
-
contents.push(`skill-registry.json:${readFileSync2(registryPath, "utf-8")}`);
|
|
476
|
-
}
|
|
477
|
-
for (const directoryName of ["reference-skills", "task-skills"]) {
|
|
478
|
-
const directoryPath = resolve2(skillsDir, directoryName);
|
|
479
|
-
if (!existsSync(directoryPath)) {
|
|
480
|
-
continue;
|
|
481
|
-
}
|
|
482
|
-
const directoryContents = loadDirectoryContents(directoryPath).map((content) => `${directoryName}/${content}`);
|
|
483
|
-
contents.push(...directoryContents);
|
|
484
|
-
}
|
|
485
|
-
return contents;
|
|
486
|
-
};
|
|
487
|
-
var computeSourceHash = (dataDir) => {
|
|
488
|
-
const ruleContents = loadSortedFileContents(resolve2(dataDir, "rules"));
|
|
489
|
-
const skillContents = loadSkillTreeContents(resolve2(dataDir, "skills"));
|
|
490
|
-
const presetsContent = readFileSync2(resolve2(dataDir, "presets.yaml"), "utf-8");
|
|
491
|
-
return computeHash([...ruleContents, ...skillContents, presetsContent]);
|
|
492
|
-
};
|
|
493
508
|
var computeInstalledSkillHash = (params) => computeHash([params.kind, params.description, ...[...params.tools].sort(), ...[...params.files].sort()]);
|
|
494
|
-
var
|
|
495
|
-
tools: [...params.tools],
|
|
496
|
-
scope: params.scope,
|
|
497
|
-
preset: params.preset,
|
|
498
|
-
workspaces: params.workspaces,
|
|
499
|
-
installed_rules: [...params.installedRules],
|
|
500
|
-
installed_files: params.installedFiles ? [...params.installedFiles] : void 0,
|
|
501
|
-
installed_skills: params.installedSkills ? [...params.installedSkills] : void 0,
|
|
502
|
-
appended_files: params.appendedFiles && params.appendedFiles.length > 0 ? [...params.appendedFiles] : void 0,
|
|
503
|
-
settings: params.settings ? {
|
|
504
|
-
claude: params.settings.claude ? [...params.settings.claude] : void 0,
|
|
505
|
-
gemini: params.settings.gemini ? [...params.settings.gemini] : void 0,
|
|
506
|
-
prettierignore: params.settings.prettierignore
|
|
507
|
-
} : void 0,
|
|
508
|
-
cliVersion: params.cliVersion,
|
|
509
|
-
sourceHash: params.sourceHash,
|
|
510
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
511
|
-
});
|
|
509
|
+
var computeInstalledSubagentHash = (params) => computeHash([params.id, params.prompt, ...[...params.tools].sort(), ...[...params.metadataFiles].sort()]);
|
|
512
510
|
|
|
513
511
|
// src/core/skill-renderer.ts
|
|
514
512
|
var AGENT_SKILLS_DIR = ".agents/skills";
|
|
@@ -516,10 +514,10 @@ var CLAUDE_SKILLS_DIR = ".claude/skills";
|
|
|
516
514
|
var buildRootDirs = (skillId, toolIds) => {
|
|
517
515
|
const dirs = [];
|
|
518
516
|
if (toolIds.some((toolId) => toolId === "codex" || toolId === "gemini")) {
|
|
519
|
-
dirs.push(
|
|
517
|
+
dirs.push(join5(AGENT_SKILLS_DIR, skillId));
|
|
520
518
|
}
|
|
521
519
|
if (toolIds.includes("claude-code")) {
|
|
522
|
-
dirs.push(
|
|
520
|
+
dirs.push(join5(CLAUDE_SKILLS_DIR, skillId));
|
|
523
521
|
}
|
|
524
522
|
return dirs;
|
|
525
523
|
};
|
|
@@ -541,7 +539,7 @@ var buildSkillInstallPlan = (params) => {
|
|
|
541
539
|
});
|
|
542
540
|
const packages = rootDirs.map((rootDir) => {
|
|
543
541
|
const files = params.skill.files.map((file) => ({
|
|
544
|
-
relativePath:
|
|
542
|
+
relativePath: join5(rootDir, file.path),
|
|
545
543
|
content: file.content
|
|
546
544
|
}));
|
|
547
545
|
return {
|
|
@@ -556,15 +554,137 @@ var buildSkillInstallPlan = (params) => {
|
|
|
556
554
|
id: params.skill.id,
|
|
557
555
|
kind: params.skill.kind,
|
|
558
556
|
tools: selectedTools,
|
|
559
|
-
scope: params.scope,
|
|
560
557
|
installed_paths: rootDirs,
|
|
561
558
|
sourceHash: skillHash
|
|
562
559
|
}
|
|
563
560
|
};
|
|
564
561
|
};
|
|
565
562
|
|
|
563
|
+
// src/core/subagent-renderer.ts
|
|
564
|
+
import { resolve as resolve3 } from "path";
|
|
565
|
+
var normalizeSelectedTools2 = (subagent, requestedTools) => {
|
|
566
|
+
const supportedToolSet = new Set(subagent.supported_tools);
|
|
567
|
+
return requestedTools.filter((toolId) => supportedToolSet.has(toolId));
|
|
568
|
+
};
|
|
569
|
+
var renderMarkdownSubagent = (params) => `---
|
|
570
|
+
${params.rawFrontmatter.trimEnd()}
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
${params.prompt.trimEnd()}
|
|
574
|
+
`;
|
|
575
|
+
var getCodexTomlEntries = (frontmatter) => Object.entries(frontmatter).filter(
|
|
576
|
+
(entry) => entry[0] !== "skill_names" && (typeof entry[1] === "string" || typeof entry[1] === "number" || typeof entry[1] === "boolean" || Array.isArray(entry[1]) && entry[1].every((item) => typeof item === "string"))
|
|
577
|
+
);
|
|
578
|
+
var renderCodexSubagent = (params) => {
|
|
579
|
+
const metadata = renderFlatToml(getCodexTomlEntries(params.frontmatter));
|
|
580
|
+
const skills = (params.frontmatter.skill_names ?? []).map((skillName) => {
|
|
581
|
+
const skillPath = resolve3(params.userBasePath, ".agents", "skills", skillName, "SKILL.md");
|
|
582
|
+
return `[[skills.config]]
|
|
583
|
+
path = ${JSON.stringify(skillPath)}
|
|
584
|
+
enabled = true`;
|
|
585
|
+
});
|
|
586
|
+
const sections = [
|
|
587
|
+
metadata,
|
|
588
|
+
`developer_instructions = ${JSON.stringify(params.prompt.trimEnd())}`,
|
|
589
|
+
...skills
|
|
590
|
+
].filter((section) => section.length > 0);
|
|
591
|
+
return sections.join("\n\n") + "\n";
|
|
592
|
+
};
|
|
593
|
+
var renderSubagentForTool = (params) => {
|
|
594
|
+
if (params.toolId === "claude-code") {
|
|
595
|
+
return renderMarkdownSubagent({
|
|
596
|
+
rawFrontmatter: params.subagent.frontmatter.claude.raw,
|
|
597
|
+
prompt: params.subagent.prompt
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
if (params.toolId === "gemini") {
|
|
601
|
+
return renderMarkdownSubagent({
|
|
602
|
+
rawFrontmatter: params.subagent.frontmatter.gemini.raw,
|
|
603
|
+
prompt: params.subagent.prompt
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return renderCodexSubagent({
|
|
607
|
+
frontmatter: params.subagent.frontmatter.codex.parsed,
|
|
608
|
+
prompt: params.subagent.prompt,
|
|
609
|
+
userBasePath: params.userBasePath
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
var getSelectedMetadataFiles = (subagent, selectedTools) => selectedTools.map((toolId) => {
|
|
613
|
+
if (toolId === "claude-code") return `claude:${subagent.frontmatter.claude.raw}`;
|
|
614
|
+
if (toolId === "gemini") return `gemini:${subagent.frontmatter.gemini.raw}`;
|
|
615
|
+
return `codex:${subagent.frontmatter.codex.raw}`;
|
|
616
|
+
});
|
|
617
|
+
var getStringArray = (value) => {
|
|
618
|
+
if (!Array.isArray(value)) {
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
621
|
+
return value.filter((item) => typeof item === "string");
|
|
622
|
+
};
|
|
623
|
+
var buildRequiredSubagentSkills = (params) => {
|
|
624
|
+
const required = [];
|
|
625
|
+
if (params.selectedTools.includes("codex")) {
|
|
626
|
+
for (const skillName of params.subagent.frontmatter.codex.parsed.skill_names ?? []) {
|
|
627
|
+
required.push({
|
|
628
|
+
tool: "codex",
|
|
629
|
+
skillName,
|
|
630
|
+
path: resolve3(params.userBasePath, ".agents", "skills", skillName, "SKILL.md")
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (params.selectedTools.includes("claude-code")) {
|
|
635
|
+
for (const skillName of getStringArray(params.subagent.frontmatter.claude.parsed["skills"])) {
|
|
636
|
+
required.push({
|
|
637
|
+
tool: "claude-code",
|
|
638
|
+
skillName,
|
|
639
|
+
path: resolve3(params.userBasePath, ".claude", "skills", skillName, "SKILL.md")
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return required;
|
|
644
|
+
};
|
|
645
|
+
var buildSubagentInstallPlan = (params) => {
|
|
646
|
+
const selectedTools = normalizeSelectedTools2(params.subagent, params.requestedTools);
|
|
647
|
+
if (selectedTools.length === 0) {
|
|
648
|
+
throw new Error(`Subagent ${params.subagent.id} does not support the requested tools`);
|
|
649
|
+
}
|
|
650
|
+
const files = selectedTools.map((toolId) => ({
|
|
651
|
+
relativePath: buildSubagentRelativePath(params.subagent.id, toolId),
|
|
652
|
+
content: renderSubagentForTool({
|
|
653
|
+
subagent: params.subagent,
|
|
654
|
+
toolId,
|
|
655
|
+
userBasePath: params.userBasePath
|
|
656
|
+
})
|
|
657
|
+
}));
|
|
658
|
+
const subagentHash = computeInstalledSubagentHash({
|
|
659
|
+
id: params.subagent.id,
|
|
660
|
+
tools: selectedTools,
|
|
661
|
+
prompt: params.subagent.prompt,
|
|
662
|
+
metadataFiles: getSelectedMetadataFiles(params.subagent, selectedTools)
|
|
663
|
+
});
|
|
664
|
+
return {
|
|
665
|
+
packages: [
|
|
666
|
+
{
|
|
667
|
+
subagentId: params.subagent.id,
|
|
668
|
+
files
|
|
669
|
+
}
|
|
670
|
+
],
|
|
671
|
+
installedSubagent: {
|
|
672
|
+
id: params.subagent.id,
|
|
673
|
+
tools: selectedTools,
|
|
674
|
+
installed_paths: files.map((file) => file.relativePath),
|
|
675
|
+
sourceHash: subagentHash
|
|
676
|
+
},
|
|
677
|
+
requiredSkills: buildRequiredSubagentSkills({
|
|
678
|
+
subagent: params.subagent,
|
|
679
|
+
selectedTools,
|
|
680
|
+
userBasePath: params.userBasePath
|
|
681
|
+
})
|
|
682
|
+
};
|
|
683
|
+
};
|
|
684
|
+
|
|
566
685
|
// src/core/managed-header.ts
|
|
567
686
|
var MANAGED_MARKER = "<!-- managed by ai-ops -->";
|
|
687
|
+
var META_PATTERN = /^<!-- sourceHash: ([a-f0-9]{6}) \| generatedAt: (.+) -->$/;
|
|
568
688
|
var SECTION_START = "<!-- ai-ops:start -->";
|
|
569
689
|
var SECTION_END = "<!-- ai-ops:end -->";
|
|
570
690
|
var hasLegacyHeader = (content) => content.includes(MANAGED_MARKER);
|
|
@@ -585,6 +705,15 @@ var stripAiOpsSection = (content) => {
|
|
|
585
705
|
const after = content.slice(endIdx + SECTION_END.length).trimStart();
|
|
586
706
|
return before + (after ? "\n\n" + after : "") + "\n";
|
|
587
707
|
};
|
|
708
|
+
var extractAiOpsSectionContent = (content) => {
|
|
709
|
+
const startIdx = content.indexOf(SECTION_START);
|
|
710
|
+
const endIdx = content.indexOf(SECTION_END);
|
|
711
|
+
if (startIdx === -1 || endIdx === -1) return null;
|
|
712
|
+
const section = content.slice(startIdx + SECTION_START.length, endIdx).trim();
|
|
713
|
+
const lines = section.split("\n");
|
|
714
|
+
const [, ...contentLines] = lines;
|
|
715
|
+
return contentLines.join("\n").trimStart();
|
|
716
|
+
};
|
|
588
717
|
var replaceAiOpsSection = (existing, newSection) => {
|
|
589
718
|
const startIdx = existing.indexOf(SECTION_START);
|
|
590
719
|
const endIdx = existing.indexOf(SECTION_END);
|
|
@@ -593,152 +722,33 @@ var replaceAiOpsSection = (existing, newSection) => {
|
|
|
593
722
|
const after = existing.slice(endIdx + SECTION_END.length).trimStart();
|
|
594
723
|
return [before, newSection, after].filter(Boolean).join("\n\n") + "\n";
|
|
595
724
|
};
|
|
725
|
+
var parseAiOpsMeta = (content) => {
|
|
726
|
+
const startIdx = content.indexOf(SECTION_START);
|
|
727
|
+
if (startIdx === -1) return null;
|
|
728
|
+
const lines = content.slice(startIdx).split("\n");
|
|
729
|
+
const metaLine = lines[1] ?? "";
|
|
730
|
+
const match = META_PATTERN.exec(metaLine);
|
|
731
|
+
if (!match) return null;
|
|
732
|
+
return { sourceHash: match[1], generatedAt: match[2] };
|
|
733
|
+
};
|
|
596
734
|
|
|
597
735
|
// src/core/manifest-io.ts
|
|
598
736
|
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
599
|
-
import { dirname as dirname2, join as
|
|
600
|
-
var MANIFEST_FILENAME = ".ai-ops-manifest.json";
|
|
601
|
-
var parseManifest = (json) => ManifestSchema.parse(JSON.parse(json));
|
|
602
|
-
var serializeManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
603
|
-
var resolveManifestPath = (basePath) => join5(basePath, MANIFEST_FILENAME);
|
|
604
|
-
var readManifest = (manifestPath) => {
|
|
605
|
-
let raw;
|
|
606
|
-
try {
|
|
607
|
-
raw = readFileSync3(manifestPath, "utf-8");
|
|
608
|
-
} catch {
|
|
609
|
-
return null;
|
|
610
|
-
}
|
|
611
|
-
return parseManifest(raw);
|
|
612
|
-
};
|
|
613
|
-
var writeManifest = (manifestPath, manifest) => {
|
|
614
|
-
mkdirSync(dirname2(manifestPath), { recursive: true });
|
|
615
|
-
writeFileSync(manifestPath, serializeManifest(manifest), "utf-8");
|
|
616
|
-
};
|
|
737
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
617
738
|
|
|
618
739
|
// src/core/manifest-resolution.ts
|
|
619
740
|
var LEGACY_SKILL_ID_MAP = {
|
|
620
741
|
"engineering-standards-pack": "backend-service-standards"
|
|
621
742
|
};
|
|
622
|
-
var LEGACY_EXTERNALIZED_RULE_SKILL_MAP = {
|
|
623
|
-
"engineering-standards": "backend-service-standards",
|
|
624
|
-
typescript: "typescript-language",
|
|
625
|
-
python: "python-language",
|
|
626
|
-
"react-typescript": "frontend-web-react-next-runtime",
|
|
627
|
-
nextjs: "frontend-web-react-next-runtime",
|
|
628
|
-
"libs-frontend-web": "frontend-web-react-next-runtime",
|
|
629
|
-
"shadcn-ui": "frontend-web-shadcn-ui",
|
|
630
|
-
flutter: "frontend-app-flutter-runtime",
|
|
631
|
-
"libs-frontend-app": "frontend-app-flutter-runtime",
|
|
632
|
-
nestjs: "backend-ts-nestjs-runtime",
|
|
633
|
-
"libs-backend-ts": "backend-ts-nestjs-runtime",
|
|
634
|
-
fastapi: "backend-python-fastapi-runtime",
|
|
635
|
-
"libs-backend-python": "backend-python-fastapi-runtime",
|
|
636
|
-
"graphql-core": "graphql-contract",
|
|
637
|
-
"graphql-client-web": "graphql-client-integration",
|
|
638
|
-
"graphql-client-app": "graphql-client-integration",
|
|
639
|
-
"graphql-server": "graphql-server-runtime",
|
|
640
|
-
"nestjs-graphql": "graphql-server-runtime",
|
|
641
|
-
"prisma-postgresql": "db-prisma-postgresql",
|
|
642
|
-
sqlalchemy: "db-sqlalchemy-postgresql",
|
|
643
|
-
"ai-llm-python": "ai-llm-python-runtime",
|
|
644
|
-
"data-pipeline-python": "data-pipeline-python-performance"
|
|
645
|
-
};
|
|
646
743
|
var resolveCanonicalSkillId = (skillId) => LEGACY_SKILL_ID_MAP[skillId] ?? skillId;
|
|
647
|
-
var resolveRulesFromIds = (ruleIds, allRules) => {
|
|
648
|
-
const ruleMap = new Map(allRules.map((rule) => [rule.id, rule]));
|
|
649
|
-
const seen = /* @__PURE__ */ new Set();
|
|
650
|
-
const resolved = ruleIds.flatMap((ruleId) => {
|
|
651
|
-
const rule = ruleMap.get(ruleId);
|
|
652
|
-
if (!rule || seen.has(rule.id)) {
|
|
653
|
-
return [];
|
|
654
|
-
}
|
|
655
|
-
seen.add(rule.id);
|
|
656
|
-
return [rule];
|
|
657
|
-
});
|
|
658
|
-
return [...resolved].sort((a, b) => b.priority - a.priority);
|
|
659
|
-
};
|
|
660
|
-
var resolvePresetById = (presetId, presets) => {
|
|
661
|
-
if (presetId === void 0) {
|
|
662
|
-
return void 0;
|
|
663
|
-
}
|
|
664
|
-
return presets.find((preset) => preset.id === presetId);
|
|
665
|
-
};
|
|
666
|
-
var resolveManifestRules = (params) => {
|
|
667
|
-
const { manifest, allRules, presets } = params;
|
|
668
|
-
if (manifest.workspaces) {
|
|
669
|
-
const resolvedWorkspaces = Object.fromEntries(
|
|
670
|
-
Object.entries(manifest.workspaces).map(([workspacePath, entry]) => {
|
|
671
|
-
const preset2 = resolvePresetById(entry.preset, presets);
|
|
672
|
-
const rules = preset2 ? resolvePresetRules(preset2, allRules) : resolveRulesFromIds(entry.rules, allRules);
|
|
673
|
-
return [
|
|
674
|
-
workspacePath,
|
|
675
|
-
{
|
|
676
|
-
preset: entry.preset,
|
|
677
|
-
rules: rules.map((rule) => rule.id)
|
|
678
|
-
}
|
|
679
|
-
];
|
|
680
|
-
})
|
|
681
|
-
);
|
|
682
|
-
const installedRules2 = resolveRulesFromIds(
|
|
683
|
-
Object.values(resolvedWorkspaces).flatMap((entry) => entry.rules),
|
|
684
|
-
allRules
|
|
685
|
-
);
|
|
686
|
-
return {
|
|
687
|
-
installedRules: installedRules2,
|
|
688
|
-
workspaces: resolvedWorkspaces
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
const preset = resolvePresetById(manifest.preset, presets);
|
|
692
|
-
const installedRules = preset ? resolvePresetRules(preset, allRules) : resolveRulesFromIds(manifest.installed_rules, allRules);
|
|
693
|
-
return {
|
|
694
|
-
installedRules
|
|
695
|
-
};
|
|
696
|
-
};
|
|
697
|
-
var resolveManifestProjectSkills = (params) => {
|
|
698
|
-
const { manifest, allSkills } = params;
|
|
699
|
-
const skillMap = new Map(allSkills.map((skill) => [skill.id, skill]));
|
|
700
|
-
const targets = [];
|
|
701
|
-
const seen = /* @__PURE__ */ new Set();
|
|
702
|
-
for (const installedSkill of manifest.installed_skills ?? []) {
|
|
703
|
-
const canonicalSkillId = resolveCanonicalSkillId(installedSkill.id);
|
|
704
|
-
const skill = skillMap.get(canonicalSkillId);
|
|
705
|
-
if (!skill) {
|
|
706
|
-
throw new Error(`Skill not found during manifest resolution: ${installedSkill.id}`);
|
|
707
|
-
}
|
|
708
|
-
if (seen.has(skill.id)) {
|
|
709
|
-
continue;
|
|
710
|
-
}
|
|
711
|
-
seen.add(skill.id);
|
|
712
|
-
targets.push({
|
|
713
|
-
skill,
|
|
714
|
-
requestedTools: [...installedSkill.tools]
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
for (const ruleId of manifest.installed_rules) {
|
|
718
|
-
const mappedSkillId = LEGACY_EXTERNALIZED_RULE_SKILL_MAP[ruleId];
|
|
719
|
-
if (!mappedSkillId || seen.has(mappedSkillId)) {
|
|
720
|
-
continue;
|
|
721
|
-
}
|
|
722
|
-
const skill = skillMap.get(mappedSkillId);
|
|
723
|
-
if (!skill) {
|
|
724
|
-
throw new Error(`Skill not found during legacy rule migration: ${mappedSkillId}`);
|
|
725
|
-
}
|
|
726
|
-
seen.add(mappedSkillId);
|
|
727
|
-
targets.push({
|
|
728
|
-
skill,
|
|
729
|
-
requestedTools: [...manifest.tools]
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
return targets;
|
|
733
|
-
};
|
|
734
744
|
|
|
735
745
|
// src/core/skill-registry-io.ts
|
|
736
746
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
737
|
-
import { dirname as dirname3, join as
|
|
747
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
738
748
|
var SKILL_REGISTRY_FILENAME = "skills-manifest.json";
|
|
739
749
|
var parseSkillRegistry = (json) => SkillRegistrySchema.parse(JSON.parse(json));
|
|
740
750
|
var serializeSkillRegistry = (registry) => JSON.stringify(registry, null, 2) + "\n";
|
|
741
|
-
var resolveSkillRegistryPath = (userBasePath) =>
|
|
751
|
+
var resolveSkillRegistryPath = (userBasePath) => join7(userBasePath, ".ai-ops", SKILL_REGISTRY_FILENAME);
|
|
742
752
|
var readSkillRegistry = (registryPath) => {
|
|
743
753
|
let raw;
|
|
744
754
|
try {
|
|
@@ -753,1245 +763,1356 @@ var writeSkillRegistry = (registryPath, registry) => {
|
|
|
753
763
|
writeFileSync2(registryPath, serializeSkillRegistry(registry), "utf-8");
|
|
754
764
|
};
|
|
755
765
|
|
|
756
|
-
// src/core/
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// src/core/install-plan.ts
|
|
770
|
-
import { join as join7 } from "path";
|
|
771
|
-
var CODEX_PLAN_BODY = "## Plan Snapshot (Plan mode only)\n\n- This rule applies only when Codex is running in `collaboration_mode=Plan`.\n- Before implementation (file edits/creates, installs, commits) and before leaving Plan mode, save the latest plan content to `.codex/plans/YYYYMMDD_<topic>.md`.\n- In `Default` mode, do not automatically create or update plan files.";
|
|
772
|
-
var buildInstallPlan = (params) => {
|
|
773
|
-
const { toolId, renderResult, meta } = params;
|
|
774
|
-
if (toolId === "claude-code" && renderResult.tool === "claude-code") {
|
|
775
|
-
return renderResult.files.map(({ relativePath, content }) => ({
|
|
776
|
-
relativePath,
|
|
777
|
-
content: wrapWithSection(content, meta)
|
|
778
|
-
}));
|
|
779
|
-
}
|
|
780
|
-
if (toolId === "codex" && renderResult.tool === "codex") {
|
|
781
|
-
const config = TOOL_OUTPUT_MAP["codex"];
|
|
782
|
-
const actions = [];
|
|
783
|
-
const rootContent = renderResult.rootContent ? renderResult.rootContent + "\n\n---\n\n" + CODEX_PLAN_BODY : CODEX_PLAN_BODY;
|
|
784
|
-
actions.push({
|
|
785
|
-
relativePath: join7(config.dir, config.rootFileName),
|
|
786
|
-
content: wrapWithSection(rootContent, meta)
|
|
787
|
-
});
|
|
788
|
-
for (const df of renderResult.domainFiles) {
|
|
789
|
-
actions.push({
|
|
790
|
-
relativePath: join7(df.workspacePath, config.domainFileName),
|
|
791
|
-
content: wrapWithSection(df.content, meta)
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
return actions;
|
|
795
|
-
}
|
|
796
|
-
if (toolId === "gemini" && renderResult.tool === "gemini") {
|
|
797
|
-
const config = TOOL_OUTPUT_MAP["gemini"];
|
|
798
|
-
const actions = [];
|
|
799
|
-
if (renderResult.rootContent) {
|
|
800
|
-
actions.push({
|
|
801
|
-
relativePath: join7(config.dir, config.rootFileName),
|
|
802
|
-
content: wrapWithSection(renderResult.rootContent, meta)
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
for (const df of renderResult.domainFiles) {
|
|
806
|
-
actions.push({
|
|
807
|
-
relativePath: join7(df.workspacePath, config.domainFileName),
|
|
808
|
-
content: wrapWithSection(df.content, meta)
|
|
809
|
-
});
|
|
810
|
-
}
|
|
811
|
-
return actions;
|
|
766
|
+
// src/core/subagent-manifest-io.ts
|
|
767
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
768
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
769
|
+
var SUBAGENT_MANIFEST_FILENAME = "subagents-manifest.json";
|
|
770
|
+
var parseSubagentManifest = (json) => SubagentManifestSchema.parse(JSON.parse(json));
|
|
771
|
+
var serializeSubagentManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
772
|
+
var resolveSubagentManifestPath = (userBasePath) => join8(userBasePath, ".ai-ops", SUBAGENT_MANIFEST_FILENAME);
|
|
773
|
+
var readSubagentManifest = (manifestPath) => {
|
|
774
|
+
let raw;
|
|
775
|
+
try {
|
|
776
|
+
raw = readFileSync5(manifestPath, "utf-8");
|
|
777
|
+
} catch {
|
|
778
|
+
return null;
|
|
812
779
|
}
|
|
813
|
-
return
|
|
780
|
+
return parseSubagentManifest(raw);
|
|
781
|
+
};
|
|
782
|
+
var writeSubagentManifest = (manifestPath, manifest) => {
|
|
783
|
+
mkdirSync3(dirname4(manifestPath), { recursive: true });
|
|
784
|
+
writeFileSync3(manifestPath, serializeSubagentManifest(manifest), "utf-8");
|
|
814
785
|
};
|
|
815
786
|
|
|
816
|
-
// src/
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
\uC774 \uB514\uB809\uD1A0\uB9AC\uB294 AI \uD611\uC5C5 \uAE30\uBC18 spec \uD30C\uC774\uD504\uB77C\uC778\uC744 \uAD00\uB9AC\uD569\uB2C8\uB2E4.
|
|
820
|
-
|
|
821
|
-
## \uB514\uB809\uD1A0\uB9AC \uAD6C\uC870
|
|
822
|
-
|
|
823
|
-
\`\`\`
|
|
824
|
-
specs/
|
|
825
|
-
\u251C\u2500\u2500 baseline/ # \uAE30\uC900 spec \uBB38\uC11C (\uCD08\uAE30 \uC694\uAD6C\uC0AC\uD56D, \uD655\uC815\uB41C \uC2A4\uD399)
|
|
826
|
-
\u2514\u2500\u2500 initial-build/ # \uCD08\uAE30 \uAD6C\uCD95 spec \uBB38\uC11C
|
|
827
|
-
\`\`\`
|
|
828
|
-
|
|
829
|
-
## \uC0AC\uC6A9 \uBC29\uBC95
|
|
830
|
-
|
|
831
|
-
### baseline
|
|
832
|
-
|
|
833
|
-
\uD504\uB85C\uC81D\uD2B8\uC758 \uCD08\uAE30 \uB610\uB294 \uD655\uC815\uB41C \uC2A4\uD399 \uBB38\uC11C\uB97C \`baseline/\` \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4.
|
|
834
|
-
|
|
835
|
-
- \uD30C\uC77C\uBA85: \`<feature-name>.md\` (kebab-case)
|
|
836
|
-
- \uB0B4\uC6A9: \uC694\uAD6C\uC0AC\uD56D, \uB3C4\uBA54\uC778 \uC6A9\uC5B4, \uC81C\uC57D \uC870\uAC74 \uB4F1
|
|
837
|
-
|
|
838
|
-
### initial-build
|
|
839
|
-
|
|
840
|
-
\uCD08\uAE30 \uAD6C\uCD95 \uBC94\uC704\uC640 \uAD6C\uD604 \uACC4\uD68D\uC744 \`initial-build/\` \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4.
|
|
841
|
-
|
|
842
|
-
- \uD30C\uC77C\uBA85: \`<YYYYMMDD>-<feature-name>.md\`
|
|
843
|
-
- \uB0B4\uC6A9: \uCD08\uAE30 \uAD6C\uD604 \uBC94\uC704, \uC0AC\uC6A9\uC790 \uD750\uB984, \uB370\uC774\uD130 \uBAA8\uB378, API \uACC4\uC57D, \uAC80\uC99D \uAE30\uC900 \uB4F1
|
|
844
|
-
`;
|
|
845
|
-
|
|
846
|
-
// src/core/spec-plan.ts
|
|
847
|
-
var buildSpecInitPlan = () => [
|
|
848
|
-
{ relativePath: "specs/README.md", content: SPEC_README_TEMPLATE },
|
|
849
|
-
{ relativePath: "specs/baseline/.gitkeep", content: "" },
|
|
850
|
-
{ relativePath: "specs/initial-build/.gitkeep", content: "" }
|
|
851
|
-
];
|
|
787
|
+
// src/core/install-plan.ts
|
|
788
|
+
import { join as join9 } from "path";
|
|
852
789
|
|
|
853
|
-
// src/core/
|
|
854
|
-
import {
|
|
855
|
-
|
|
856
|
-
const files = [];
|
|
857
|
-
const isMonorepo = manifest.workspaces !== void 0;
|
|
858
|
-
for (const toolId of manifest.tools) {
|
|
859
|
-
if (toolId === "claude-code") {
|
|
860
|
-
const config = TOOL_OUTPUT_MAP["claude-code"];
|
|
861
|
-
for (const ruleId of manifest.installed_rules) {
|
|
862
|
-
files.push(join8(config.rulesDir, `${ruleId}${config.fileExtension}`));
|
|
863
|
-
}
|
|
864
|
-
} else if (toolId === "codex") {
|
|
865
|
-
const config = TOOL_OUTPUT_MAP["codex"];
|
|
866
|
-
if (!isMonorepo) {
|
|
867
|
-
files.push(join8(config.dir, config.rootFileName));
|
|
868
|
-
files.push(join8(config.dir, config.domainFileName));
|
|
869
|
-
} else {
|
|
870
|
-
files.push(join8(config.dir, config.rootFileName));
|
|
871
|
-
for (const ws of Object.keys(manifest.workspaces ?? {})) {
|
|
872
|
-
files.push(join8(ws, config.domainFileName));
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
} else if (toolId === "gemini") {
|
|
876
|
-
const config = TOOL_OUTPUT_MAP["gemini"];
|
|
877
|
-
if (!isMonorepo) {
|
|
878
|
-
files.push(join8(config.dir, config.rootFileName));
|
|
879
|
-
} else {
|
|
880
|
-
files.push(join8(config.dir, config.rootFileName));
|
|
881
|
-
for (const ws of Object.keys(manifest.workspaces ?? {})) {
|
|
882
|
-
files.push(join8(ws, config.domainFileName));
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
return [...new Set(files)];
|
|
888
|
-
};
|
|
790
|
+
// src/core/project-layer.ts
|
|
791
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync3, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
792
|
+
import { dirname as dirname6, isAbsolute, join as join10, relative, resolve as resolve5 } from "path";
|
|
889
793
|
|
|
890
794
|
// src/core/paths.ts
|
|
891
|
-
import { dirname as
|
|
795
|
+
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
892
796
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
893
|
-
var __dirname2 =
|
|
894
|
-
var COMPILER_DATA_DIR =
|
|
797
|
+
var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
|
|
798
|
+
var COMPILER_DATA_DIR = resolve4(__dirname2, "..", "..", "data");
|
|
895
799
|
|
|
896
|
-
// src/
|
|
897
|
-
|
|
898
|
-
var
|
|
899
|
-
var
|
|
900
|
-
var
|
|
901
|
-
var
|
|
902
|
-
var
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
"
|
|
915
|
-
// Node.js / JS / TS
|
|
916
|
-
"pubspec.yaml",
|
|
917
|
-
// Flutter / Dart
|
|
918
|
-
"pyproject.toml",
|
|
919
|
-
// Python (modern)
|
|
920
|
-
"setup.py",
|
|
921
|
-
// Python (legacy)
|
|
922
|
-
"Cargo.toml",
|
|
923
|
-
// Rust
|
|
924
|
-
"go.mod"
|
|
925
|
-
// Go
|
|
800
|
+
// src/core/project-layer.ts
|
|
801
|
+
var PROJECT_LAYER_MANIFEST_RELATIVE_PATH = ".ai-ops/manifest.json";
|
|
802
|
+
var PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH = ".ai-ops/context-layer.json";
|
|
803
|
+
var CONTEXT_LAYER_DATA_DIR = join10(COMPILER_DATA_DIR, "context-layer");
|
|
804
|
+
var TOOL_ORDER = ["codex", "gemini", "claude-code"];
|
|
805
|
+
var DEFAULT_TOOLS = TOOL_ORDER;
|
|
806
|
+
var TEMPLATE_PATHS = [
|
|
807
|
+
"AGENTS.md",
|
|
808
|
+
"GEMINI.md",
|
|
809
|
+
"CLAUDE.md",
|
|
810
|
+
"docs/agent/workflow.md",
|
|
811
|
+
"docs/agent/rules/routing-rules.md",
|
|
812
|
+
"docs/agent/rules/doc-update-rules.md",
|
|
813
|
+
"docs/agent/rules/stop-rules.md",
|
|
814
|
+
"docs/agent/checks/impact-checklist.md",
|
|
815
|
+
"docs/agent/checks/review-checklist.md",
|
|
816
|
+
"docs/agent/maps/codebase-map.md",
|
|
817
|
+
"docs/business/business-rules.md",
|
|
818
|
+
"docs/docs-status.md"
|
|
926
819
|
];
|
|
927
|
-
var
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
820
|
+
var PROJECT_OWNED_PATHS = /* @__PURE__ */ new Set([
|
|
821
|
+
"docs/docs-status.md",
|
|
822
|
+
"docs/agent/maps/codebase-map.md",
|
|
823
|
+
"docs/business/business-rules.md"
|
|
824
|
+
]);
|
|
825
|
+
var RESERVED_DOCUMENT_WARNINGS = [
|
|
826
|
+
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
827
|
+
"Do not use this document as current decision-making evidence"
|
|
828
|
+
];
|
|
829
|
+
var resolveProjectLayerManifestPath = (basePath) => join10(basePath, PROJECT_LAYER_MANIFEST_RELATIVE_PATH);
|
|
830
|
+
var resolveProjectLayerContextIndexPath = (basePath) => join10(basePath, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH);
|
|
831
|
+
var resolveTemplatePath = (relativePath) => join10(CONTEXT_LAYER_DATA_DIR, relativePath);
|
|
832
|
+
var toRelativeDir = (relativePath) => dirname6(relativePath);
|
|
833
|
+
var resolveProjectLayerFilePath = (basePath, relativePath) => {
|
|
834
|
+
if (!isSafeProjectLayerPath(relativePath)) {
|
|
835
|
+
throw new Error(`Unsafe project layer path: ${relativePath}`);
|
|
836
|
+
}
|
|
837
|
+
const absoluteBasePath = resolve5(basePath);
|
|
838
|
+
const absolutePath = resolve5(absoluteBasePath, relativePath);
|
|
839
|
+
const relativeFromBase = relative(absoluteBasePath, absolutePath);
|
|
840
|
+
if (relativeFromBase === "" || relativeFromBase.startsWith("..") || isAbsolute(relativeFromBase)) {
|
|
841
|
+
throw new Error(`Unsafe project layer path: ${relativePath}`);
|
|
842
|
+
}
|
|
843
|
+
return absolutePath;
|
|
844
|
+
};
|
|
845
|
+
var parseProjectLayerManifest = (json) => ProjectLayerManifestSchema.parse(JSON.parse(json));
|
|
846
|
+
var serializeProjectLayerManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
847
|
+
var parseProjectLayerContextIndex = (json) => ProjectLayerContextIndexSchema.parse(JSON.parse(json));
|
|
848
|
+
var serializeProjectLayerContextIndex = (contextIndex) => JSON.stringify(contextIndex, null, 2) + "\n";
|
|
849
|
+
var parseProjectLayerFrontmatter = (content) => {
|
|
850
|
+
const { frontmatter } = parseMarkdownFrontmatter(content);
|
|
851
|
+
return ProjectLayerFrontmatterSchema.parse(frontmatter);
|
|
852
|
+
};
|
|
853
|
+
var parseProjectLayerDocument = (path, rawContent) => {
|
|
854
|
+
const managedContent = extractAiOpsSectionContent(rawContent);
|
|
855
|
+
const content = managedContent ?? rawContent;
|
|
856
|
+
const frontmatter = parseProjectLayerFrontmatter(content);
|
|
857
|
+
return {
|
|
858
|
+
path,
|
|
859
|
+
status: frontmatter.status,
|
|
860
|
+
layer: frontmatter.layer,
|
|
861
|
+
owner: frontmatter.owner,
|
|
862
|
+
read_when: frontmatter.read_when,
|
|
863
|
+
update_when: frontmatter.update_when,
|
|
864
|
+
contentHash: computeHash([content.trimEnd()]),
|
|
865
|
+
content
|
|
866
|
+
};
|
|
948
867
|
};
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
written.push(action.relativePath);
|
|
963
|
-
} else {
|
|
964
|
-
const existing = readFileSync5(absPath, "utf-8");
|
|
965
|
-
if (hasAiOpsSection(existing)) {
|
|
966
|
-
const updated = replaceAiOpsSection(existing, action.content);
|
|
967
|
-
writeFileSync3(absPath, updated, "utf-8");
|
|
968
|
-
const stripped = stripAiOpsSection(existing);
|
|
969
|
-
(stripped.trim().length > 0 ? appended : written).push(action.relativePath);
|
|
970
|
-
} else if (hasLegacyHeader(existing)) {
|
|
971
|
-
writeFileSync3(absPath, action.content + "\n", "utf-8");
|
|
972
|
-
written.push(action.relativePath);
|
|
973
|
-
} else {
|
|
974
|
-
const updated = existing.trimEnd() + "\n\n" + action.content + "\n";
|
|
975
|
-
writeFileSync3(absPath, updated, "utf-8");
|
|
976
|
-
appended.push(action.relativePath);
|
|
868
|
+
var parseDocsStatusEntries = (content) => {
|
|
869
|
+
const document = parseProjectLayerDocument("docs/docs-status.md", content);
|
|
870
|
+
const rows = document.content.split("\n").filter((line) => line.trim().startsWith("|")).map((line) => line.trim());
|
|
871
|
+
return rows.flatMap((line) => {
|
|
872
|
+
const cells = line.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
873
|
+
if (cells.length < 3) return [];
|
|
874
|
+
if (cells[0] === "path") return [];
|
|
875
|
+
if (cells[0].startsWith("---")) return [];
|
|
876
|
+
return [
|
|
877
|
+
{
|
|
878
|
+
path: cells[0],
|
|
879
|
+
status: cells[1],
|
|
880
|
+
owner: cells[2]
|
|
977
881
|
}
|
|
978
|
-
|
|
882
|
+
];
|
|
883
|
+
});
|
|
884
|
+
};
|
|
885
|
+
var resolveProjectLayerTools = (requestedTools) => {
|
|
886
|
+
if (requestedTools === void 0 || requestedTools.length === 0) {
|
|
887
|
+
return [...DEFAULT_TOOLS];
|
|
979
888
|
}
|
|
980
|
-
|
|
889
|
+
const parsedTools = requestedTools.map((tool) => ProjectLayerToolSchema.parse(tool));
|
|
890
|
+
const toolSet = new Set(parsedTools);
|
|
891
|
+
return TOOL_ORDER.filter((tool) => toolSet.has(tool));
|
|
981
892
|
};
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
const absPath = resolve6(basePath, file.relativePath);
|
|
995
|
-
mkdirSync4(dirname6(absPath), { recursive: true });
|
|
996
|
-
writeFileSync4(absPath, file.content + "\n", "utf-8");
|
|
997
|
-
}
|
|
998
|
-
writtenRoots.push(skillPackage.rootDir);
|
|
893
|
+
var shouldIncludeTemplate = (relativePath, tools) => {
|
|
894
|
+
if (relativePath === "GEMINI.md") return tools.includes("gemini");
|
|
895
|
+
if (relativePath === "CLAUDE.md") return tools.includes("claude-code");
|
|
896
|
+
return true;
|
|
897
|
+
};
|
|
898
|
+
var buildDocsStatusRows = (specs) => specs.map((spec) => `| ${spec.path} | ${spec.frontmatter.status} | ${spec.frontmatter.owner} |`).join("\n");
|
|
899
|
+
var includesReservedDocumentWarning = (content) => RESERVED_DOCUMENT_WARNINGS.some((warning) => content.includes(warning));
|
|
900
|
+
var loadTemplateSpec = (relativePath, content) => {
|
|
901
|
+
const frontmatter = parseProjectLayerFrontmatter(content);
|
|
902
|
+
const ownership = PROJECT_OWNED_PATHS.has(relativePath) ? "project" : "managed";
|
|
903
|
+
if (frontmatter.status === "Reserved" && !includesReservedDocumentWarning(content)) {
|
|
904
|
+
throw new Error(`Reserved template must include warning text: ${relativePath}`);
|
|
999
905
|
}
|
|
1000
|
-
return
|
|
906
|
+
return {
|
|
907
|
+
path: relativePath,
|
|
908
|
+
content,
|
|
909
|
+
ownership,
|
|
910
|
+
frontmatter,
|
|
911
|
+
contentHash: computeHash([content.trimEnd()])
|
|
912
|
+
};
|
|
1001
913
|
};
|
|
1002
|
-
var
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
914
|
+
var loadProjectLayerTemplateSpecs = (tools) => {
|
|
915
|
+
const selectedPaths = TEMPLATE_PATHS.filter((relativePath) => shouldIncludeTemplate(relativePath, tools));
|
|
916
|
+
const nonStatusSpecs = selectedPaths.filter((relativePath) => relativePath !== "docs/docs-status.md").map((relativePath) => loadTemplateSpec(relativePath, readFileSync6(resolveTemplatePath(relativePath), "utf-8")));
|
|
917
|
+
const statusTemplate = readFileSync6(resolveTemplatePath("docs/docs-status.md"), "utf-8");
|
|
918
|
+
const statusPlaceholderSpec = loadTemplateSpec("docs/docs-status.md", statusTemplate);
|
|
919
|
+
const specsForStatus = [...nonStatusSpecs, statusPlaceholderSpec].sort((a, b) => a.path.localeCompare(b.path));
|
|
920
|
+
const statusContent = statusTemplate.replace("{{documents_table}}", buildDocsStatusRows(specsForStatus));
|
|
921
|
+
const statusSpec = loadTemplateSpec("docs/docs-status.md", statusContent);
|
|
922
|
+
return [...nonStatusSpecs, statusSpec].sort((a, b) => a.path.localeCompare(b.path));
|
|
923
|
+
};
|
|
924
|
+
var computeProjectLayerSourceHash = (specs) => computeHash(specs.map((spec) => `${spec.path}:${spec.content}`));
|
|
925
|
+
var readProjectLayerManifest = (basePath) => {
|
|
926
|
+
try {
|
|
927
|
+
return parseProjectLayerManifest(readFileSync6(resolveProjectLayerManifestPath(basePath), "utf-8"));
|
|
928
|
+
} catch (error) {
|
|
929
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
throw error;
|
|
1009
933
|
}
|
|
1010
|
-
return removed;
|
|
1011
934
|
};
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
result[key] = deepMerge(result[key], value);
|
|
1024
|
-
} else {
|
|
1025
|
-
result[key] = value;
|
|
935
|
+
var writeProjectLayerManifest = (basePath, manifest) => {
|
|
936
|
+
const manifestPath = resolveProjectLayerManifestPath(basePath);
|
|
937
|
+
mkdirSync4(dirname6(manifestPath), { recursive: true });
|
|
938
|
+
writeFileSync4(manifestPath, serializeProjectLayerManifest(manifest), "utf-8");
|
|
939
|
+
};
|
|
940
|
+
var readProjectLayerContextIndex = (basePath) => {
|
|
941
|
+
try {
|
|
942
|
+
return parseProjectLayerContextIndex(readFileSync6(resolveProjectLayerContextIndexPath(basePath), "utf-8"));
|
|
943
|
+
} catch (error) {
|
|
944
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
945
|
+
return null;
|
|
1026
946
|
}
|
|
947
|
+
throw error;
|
|
1027
948
|
}
|
|
1028
|
-
return result;
|
|
1029
949
|
};
|
|
1030
|
-
var
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
950
|
+
var writeProjectLayerContextIndex = (basePath, contextIndex) => {
|
|
951
|
+
const contextIndexPath = resolveProjectLayerContextIndexPath(basePath);
|
|
952
|
+
mkdirSync4(dirname6(contextIndexPath), { recursive: true });
|
|
953
|
+
writeFileSync4(contextIndexPath, serializeProjectLayerContextIndex(contextIndex), "utf-8");
|
|
954
|
+
};
|
|
955
|
+
var installManagedFiles = (basePath, specs, meta) => {
|
|
956
|
+
const written = [];
|
|
957
|
+
const appended = [];
|
|
958
|
+
for (const spec of specs) {
|
|
959
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, spec.path);
|
|
960
|
+
const wrappedContent = wrapWithSection(spec.content, meta);
|
|
961
|
+
if (!existsSync2(absolutePath)) {
|
|
962
|
+
mkdirSync4(dirname6(absolutePath), { recursive: true });
|
|
963
|
+
writeFileSync4(absolutePath, wrappedContent + "\n", "utf-8");
|
|
964
|
+
written.push(spec.path);
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
const existing = readFileSync6(absolutePath, "utf-8");
|
|
968
|
+
if (hasAiOpsSection(existing)) {
|
|
969
|
+
writeFileSync4(absolutePath, replaceAiOpsSection(existing, wrappedContent), "utf-8");
|
|
970
|
+
const stripped = stripAiOpsSection(existing);
|
|
971
|
+
(stripped.trim().length > 0 ? appended : written).push(spec.path);
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
if (hasLegacyHeader(existing)) {
|
|
975
|
+
writeFileSync4(absolutePath, wrappedContent + "\n", "utf-8");
|
|
976
|
+
written.push(spec.path);
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
writeFileSync4(absolutePath, existing.trimEnd() + "\n\n" + wrappedContent + "\n", "utf-8");
|
|
980
|
+
appended.push(spec.path);
|
|
981
|
+
}
|
|
982
|
+
return { written, appended };
|
|
983
|
+
};
|
|
984
|
+
var installProjectFiles = (params) => {
|
|
985
|
+
const records = [];
|
|
986
|
+
const created = [];
|
|
987
|
+
const refreshed = [];
|
|
988
|
+
const preserved = [];
|
|
989
|
+
const previousByPath = new Map((params.previousProjectFiles ?? []).map((file) => [file.path, file]));
|
|
990
|
+
for (const spec of params.specs) {
|
|
991
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, spec.path);
|
|
992
|
+
const previous = previousByPath.get(spec.path);
|
|
993
|
+
if (!existsSync2(absolutePath)) {
|
|
994
|
+
mkdirSync4(dirname6(absolutePath), { recursive: true });
|
|
995
|
+
writeFileSync4(absolutePath, spec.content + "\n", "utf-8");
|
|
996
|
+
created.push(spec.path);
|
|
997
|
+
records.push({
|
|
998
|
+
path: spec.path,
|
|
999
|
+
templateHash: spec.contentHash,
|
|
1000
|
+
created: true
|
|
1001
|
+
});
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
const existingContent = readFileSync6(absolutePath, "utf-8").trimEnd();
|
|
1005
|
+
const existingHash = computeHash([existingContent]);
|
|
1006
|
+
if (previous?.created === true && existingHash === previous.templateHash) {
|
|
1007
|
+
if (existingHash !== spec.contentHash) {
|
|
1008
|
+
writeFileSync4(absolutePath, spec.content + "\n", "utf-8");
|
|
1009
|
+
refreshed.push(spec.path);
|
|
1038
1010
|
} else {
|
|
1039
|
-
|
|
1011
|
+
preserved.push(spec.path);
|
|
1040
1012
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1013
|
+
records.push({
|
|
1014
|
+
path: spec.path,
|
|
1015
|
+
templateHash: spec.contentHash,
|
|
1016
|
+
created: true
|
|
1017
|
+
});
|
|
1018
|
+
continue;
|
|
1043
1019
|
}
|
|
1020
|
+
preserved.push(spec.path);
|
|
1021
|
+
records.push({
|
|
1022
|
+
path: spec.path,
|
|
1023
|
+
templateHash: previous?.templateHash ?? spec.contentHash,
|
|
1024
|
+
created: previous?.created ?? false
|
|
1025
|
+
});
|
|
1044
1026
|
}
|
|
1045
|
-
return
|
|
1027
|
+
return { records, created, refreshed, preserved };
|
|
1028
|
+
};
|
|
1029
|
+
var buildContextIndexFromDisk = (params) => {
|
|
1030
|
+
const documents = params.documentPaths.map(
|
|
1031
|
+
(path) => parseProjectLayerDocument(path, readFileSync6(resolveProjectLayerFilePath(params.basePath, path), "utf-8"))
|
|
1032
|
+
);
|
|
1033
|
+
return ProjectLayerContextIndexSchema.parse({
|
|
1034
|
+
schemaVersion: 1,
|
|
1035
|
+
kind: "context-layer-index",
|
|
1036
|
+
documents: documents.map(({ content: _content, ...document }) => document),
|
|
1037
|
+
generatedAt: params.generatedAt
|
|
1038
|
+
});
|
|
1046
1039
|
};
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
var
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1040
|
+
var computeProjectFileHash = (basePath, relativePath) => computeHash([readFileSync6(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
|
|
1041
|
+
var collectDocumentPathsFromManifest = (manifest) => [
|
|
1042
|
+
...manifest.managed_files.map((file) => file.path),
|
|
1043
|
+
...manifest.project_files.map((file) => file.path),
|
|
1044
|
+
...manifest.packs.flatMap((pack) => pack.documents.map((file) => file.path))
|
|
1045
|
+
].sort();
|
|
1046
|
+
var buildDocsStatusRowsFromDisk = (params) => params.documentPaths.map((path) => {
|
|
1047
|
+
const document = parseProjectLayerDocument(path, readFileSync6(resolveProjectLayerFilePath(params.basePath, path), "utf-8"));
|
|
1048
|
+
return `| ${document.path} | ${document.status} | ${document.owner} |`;
|
|
1049
|
+
});
|
|
1050
|
+
var replaceDocsStatusRows = (content, rows) => {
|
|
1051
|
+
const lines = content.trimEnd().split("\n");
|
|
1052
|
+
const headerIndex = lines.findIndex((line) => line.trim() === "| path | status | owner |");
|
|
1053
|
+
const dividerIndex = headerIndex + 1;
|
|
1054
|
+
if (headerIndex < 0 || !lines[dividerIndex]?.trim().startsWith("| ---")) {
|
|
1055
|
+
throw new Error("docs/docs-status.md table header not found");
|
|
1056
|
+
}
|
|
1057
|
+
let tableEndIndex = dividerIndex + 1;
|
|
1058
|
+
while (tableEndIndex < lines.length && lines[tableEndIndex]?.trim().startsWith("|")) {
|
|
1059
|
+
tableEndIndex += 1;
|
|
1060
|
+
}
|
|
1061
|
+
return [...lines.slice(0, dividerIndex + 1), ...rows, ...lines.slice(tableEndIndex)].join("\n") + "\n";
|
|
1062
|
+
};
|
|
1063
|
+
var updateDocsStatusTable = (basePath, documentPaths) => {
|
|
1064
|
+
const docsStatusPath = "docs/docs-status.md";
|
|
1065
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, docsStatusPath);
|
|
1066
|
+
const beforeHash = computeProjectFileHash(basePath, docsStatusPath);
|
|
1067
|
+
const rows = buildDocsStatusRowsFromDisk({ basePath, documentPaths });
|
|
1068
|
+
const nextContent = replaceDocsStatusRows(readFileSync6(absolutePath, "utf-8"), rows);
|
|
1069
|
+
writeFileSync4(absolutePath, nextContent, "utf-8");
|
|
1070
|
+
return {
|
|
1071
|
+
beforeHash,
|
|
1072
|
+
afterHash: computeProjectFileHash(basePath, docsStatusPath)
|
|
1073
|
+
};
|
|
1074
|
+
};
|
|
1075
|
+
var updateDocsStatusProjectFileRecord = (params) => ProjectLayerManifestSchema.parse({
|
|
1076
|
+
...params.manifest,
|
|
1077
|
+
project_files: params.manifest.project_files.map((file) => {
|
|
1078
|
+
if (file.path !== "docs/docs-status.md" || !file.created || file.templateHash !== params.beforeHash) {
|
|
1079
|
+
return file;
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
...file,
|
|
1083
|
+
templateHash: params.afterHash
|
|
1084
|
+
};
|
|
1085
|
+
})
|
|
1086
|
+
});
|
|
1087
|
+
var refreshProjectLayerDerivedState = (params) => {
|
|
1088
|
+
const documentPaths = collectDocumentPathsFromManifest(params.manifest);
|
|
1089
|
+
const docsStatusHashes = updateDocsStatusTable(params.basePath, documentPaths);
|
|
1090
|
+
const manifest = updateDocsStatusProjectFileRecord({
|
|
1091
|
+
manifest: params.manifest,
|
|
1092
|
+
beforeHash: docsStatusHashes.beforeHash,
|
|
1093
|
+
afterHash: docsStatusHashes.afterHash
|
|
1062
1094
|
});
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1095
|
+
const contextIndex = buildContextIndexFromDisk({
|
|
1096
|
+
basePath: params.basePath,
|
|
1097
|
+
documentPaths,
|
|
1098
|
+
generatedAt: params.generatedAt
|
|
1099
|
+
});
|
|
1100
|
+
writeProjectLayerContextIndex(params.basePath, contextIndex);
|
|
1101
|
+
return {
|
|
1102
|
+
manifest,
|
|
1103
|
+
contextIndex
|
|
1104
|
+
};
|
|
1105
|
+
};
|
|
1106
|
+
var buildProjectLayerManifest = (params) => ProjectLayerManifestSchema.parse({
|
|
1107
|
+
schemaVersion: 1,
|
|
1108
|
+
kind: "project-operating-layer",
|
|
1109
|
+
tools: [...params.tools],
|
|
1110
|
+
managed_files: params.managedFiles.map((path) => ({
|
|
1111
|
+
path,
|
|
1112
|
+
sourceHash: params.sourceHash
|
|
1113
|
+
})),
|
|
1114
|
+
project_files: [...params.projectFiles],
|
|
1115
|
+
packs: [...params.packs],
|
|
1116
|
+
settings: params.settings ?? {},
|
|
1117
|
+
sourceHash: params.sourceHash,
|
|
1118
|
+
cliVersion: params.cliVersion,
|
|
1119
|
+
generatedAt: params.generatedAt
|
|
1120
|
+
});
|
|
1121
|
+
var retireUnselectedManagedFiles = (params) => {
|
|
1122
|
+
if (!params.previousManifest) return;
|
|
1123
|
+
const nextManagedPathSet = new Set(params.nextManagedPaths);
|
|
1124
|
+
for (const file of params.previousManifest.managed_files) {
|
|
1125
|
+
if (!nextManagedPathSet.has(file.path)) {
|
|
1126
|
+
removeManagedProjectFile(params.basePath, file.path);
|
|
1075
1127
|
}
|
|
1076
1128
|
}
|
|
1077
|
-
let merged = existing;
|
|
1078
|
-
for (const val of selectedValues) {
|
|
1079
|
-
const group = config.groups.find((g) => g.value === val);
|
|
1080
|
-
if (!group) continue;
|
|
1081
|
-
merged = deepMerge(merged, group.patch);
|
|
1082
|
-
}
|
|
1083
|
-
mkdirSync5(settingsDir, { recursive: true });
|
|
1084
|
-
writeFileSync5(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
1085
1129
|
};
|
|
1086
|
-
var
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1130
|
+
var installProjectLayer = (params) => {
|
|
1131
|
+
const previousManifest = params.previousManifest === void 0 ? readProjectLayerManifest(params.basePath) : params.previousManifest;
|
|
1132
|
+
const specs = loadProjectLayerTemplateSpecs(params.tools);
|
|
1133
|
+
const sourceHash = computeProjectLayerSourceHash(specs);
|
|
1134
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1135
|
+
const managedSpecs = specs.filter((spec) => spec.ownership === "managed");
|
|
1136
|
+
const projectSpecs = specs.filter((spec) => spec.ownership === "project");
|
|
1137
|
+
const managedPaths = managedSpecs.map((spec) => spec.path);
|
|
1138
|
+
retireUnselectedManagedFiles({
|
|
1139
|
+
basePath: params.basePath,
|
|
1140
|
+
previousManifest,
|
|
1141
|
+
nextManagedPaths: managedPaths
|
|
1142
|
+
});
|
|
1143
|
+
const managedResult = installManagedFiles(params.basePath, managedSpecs, { sourceHash, generatedAt });
|
|
1144
|
+
const projectResult = installProjectFiles({
|
|
1145
|
+
basePath: params.basePath,
|
|
1146
|
+
specs: projectSpecs,
|
|
1147
|
+
previousProjectFiles: previousManifest?.project_files
|
|
1148
|
+
});
|
|
1149
|
+
const provisionalManifest = buildProjectLayerManifest({
|
|
1150
|
+
tools: params.tools,
|
|
1151
|
+
managedFiles: managedPaths,
|
|
1152
|
+
projectFiles: projectResult.records,
|
|
1153
|
+
packs: previousManifest?.packs ?? [],
|
|
1154
|
+
sourceHash,
|
|
1155
|
+
cliVersion: getCliVersion(),
|
|
1156
|
+
generatedAt,
|
|
1157
|
+
settings: previousManifest?.settings
|
|
1158
|
+
});
|
|
1159
|
+
const { manifest, contextIndex } = refreshProjectLayerDerivedState({
|
|
1160
|
+
basePath: params.basePath,
|
|
1161
|
+
manifest: provisionalManifest,
|
|
1162
|
+
generatedAt
|
|
1163
|
+
});
|
|
1164
|
+
writeProjectLayerManifest(params.basePath, manifest);
|
|
1165
|
+
return {
|
|
1166
|
+
manifest,
|
|
1167
|
+
contextIndex,
|
|
1168
|
+
written: managedResult.written,
|
|
1169
|
+
appended: managedResult.appended,
|
|
1170
|
+
createdProjectFiles: projectResult.created,
|
|
1171
|
+
refreshedProjectFiles: projectResult.refreshed,
|
|
1172
|
+
preservedProjectFiles: projectResult.preserved
|
|
1173
|
+
};
|
|
1174
|
+
};
|
|
1175
|
+
var updateProjectLayer = (params) => {
|
|
1176
|
+
const specs = loadProjectLayerTemplateSpecs(params.manifest.tools);
|
|
1177
|
+
const sourceHash = computeProjectLayerSourceHash(specs);
|
|
1178
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1179
|
+
const managedSpecs = specs.filter((spec) => spec.ownership === "managed");
|
|
1180
|
+
const projectSpecs = specs.filter((spec) => spec.ownership === "project");
|
|
1181
|
+
const managedResult = installManagedFiles(params.basePath, managedSpecs, { sourceHash, generatedAt });
|
|
1182
|
+
const projectResult = installProjectFiles({
|
|
1183
|
+
basePath: params.basePath,
|
|
1184
|
+
specs: projectSpecs,
|
|
1185
|
+
previousProjectFiles: params.manifest.project_files
|
|
1186
|
+
});
|
|
1187
|
+
const provisionalManifest = buildProjectLayerManifest({
|
|
1188
|
+
tools: params.manifest.tools,
|
|
1189
|
+
managedFiles: managedSpecs.map((spec) => spec.path),
|
|
1190
|
+
projectFiles: projectResult.records,
|
|
1191
|
+
packs: params.manifest.packs,
|
|
1192
|
+
sourceHash,
|
|
1193
|
+
cliVersion: getCliVersion(),
|
|
1194
|
+
generatedAt,
|
|
1195
|
+
settings: params.manifest.settings
|
|
1196
|
+
});
|
|
1197
|
+
const { manifest, contextIndex } = refreshProjectLayerDerivedState({
|
|
1198
|
+
basePath: params.basePath,
|
|
1199
|
+
manifest: provisionalManifest,
|
|
1200
|
+
generatedAt
|
|
1201
|
+
});
|
|
1202
|
+
writeProjectLayerManifest(params.basePath, manifest);
|
|
1203
|
+
return {
|
|
1204
|
+
manifest,
|
|
1205
|
+
contextIndex,
|
|
1206
|
+
written: managedResult.written,
|
|
1207
|
+
appended: managedResult.appended,
|
|
1208
|
+
createdProjectFiles: projectResult.created,
|
|
1209
|
+
refreshedProjectFiles: projectResult.refreshed,
|
|
1210
|
+
preservedProjectFiles: projectResult.preserved
|
|
1211
|
+
};
|
|
1212
|
+
};
|
|
1213
|
+
var issue = (level, code, message) => ({
|
|
1214
|
+
level,
|
|
1215
|
+
code,
|
|
1216
|
+
message
|
|
1217
|
+
});
|
|
1218
|
+
var readDocumentSafely = (basePath, path) => {
|
|
1090
1219
|
try {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1220
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, path);
|
|
1221
|
+
if (!existsSync2(absolutePath)) {
|
|
1222
|
+
return issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${path}`);
|
|
1223
|
+
}
|
|
1224
|
+
return parseProjectLayerDocument(path, readFileSync6(absolutePath, "utf-8"));
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1227
|
+
return issue("error", "invalid-frontmatter", `${path} frontmatter \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
var buildContextIndexMap = (contextIndex) => new Map((contextIndex?.documents ?? []).map((document) => [document.path, document]));
|
|
1231
|
+
var compareArray = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1232
|
+
var compareContextDocument = (params) => {
|
|
1233
|
+
const indexed = params.indexed;
|
|
1234
|
+
if (indexed === void 0) {
|
|
1235
|
+
return [issue("error", "context-missing-document", `context-layer \uB204\uB77D: ${params.expected.path}`)];
|
|
1236
|
+
}
|
|
1237
|
+
const issues = [];
|
|
1238
|
+
const scalarKeys = ["status", "layer", "owner", "contentHash"];
|
|
1239
|
+
for (const key of scalarKeys) {
|
|
1240
|
+
if (params.expected[key] !== indexed[key]) {
|
|
1241
|
+
issues.push(
|
|
1242
|
+
issue("error", "context-document-mismatch", `${params.expected.path} context ${key} \uBD88\uC77C\uCE58`)
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1095
1245
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const group = config.groups.find((g) => g.value === val);
|
|
1099
|
-
if (!group) continue;
|
|
1100
|
-
result = deepRemoveKeys(result, group.patch);
|
|
1246
|
+
if (!compareArray(params.expected.read_when, indexed.read_when)) {
|
|
1247
|
+
issues.push(issue("error", "context-document-mismatch", `${params.expected.path} context read_when \uBD88\uC77C\uCE58`));
|
|
1101
1248
|
}
|
|
1102
|
-
if (
|
|
1103
|
-
|
|
1104
|
-
return "deleted";
|
|
1249
|
+
if (!compareArray(params.expected.update_when, indexed.update_when)) {
|
|
1250
|
+
issues.push(issue("error", "context-document-mismatch", `${params.expected.path} context update_when \uBD88\uC77C\uCE58`));
|
|
1105
1251
|
}
|
|
1106
|
-
|
|
1107
|
-
return "cleaned";
|
|
1252
|
+
return issues;
|
|
1108
1253
|
};
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
value: "ui",
|
|
1114
|
-
label: "UI \u2014 \uC904 \uBC88\uD638 \uC228\uAE30\uAE30",
|
|
1115
|
-
hint: "ui.showLineNumbers: false \u2014 \uCF54\uB4DC \uBCF5\uC0AC \uC2DC \uC904 \uBC88\uD638\uAC00 \uD3EC\uD568\uB418\uC9C0 \uC54A\uB3C4\uB85D \uBE44\uD65C\uC131\uD654",
|
|
1116
|
-
patch: { ui: { showLineNumbers: false } }
|
|
1117
|
-
},
|
|
1118
|
-
{
|
|
1119
|
-
value: "plan",
|
|
1120
|
-
label: "Plan \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uBC0F \uBAA8\uB378 \uB77C\uC6B0\uD305",
|
|
1121
|
-
hint: "general.plan.directory: .gemini/plans, modelRouting: true \u2014 AI \uACC4\uD68D\uC744 \uD30C\uC77C\uB85C \uC800\uC7A5\uD558\uACE0 \uD0DC\uC2A4\uD06C\uBCC4 \uCD5C\uC801 \uBAA8\uB378 \uC790\uB3D9 \uC120\uD0DD",
|
|
1122
|
-
patch: { general: { plan: { directory: ".gemini/plans", modelRouting: true } } }
|
|
1123
|
-
},
|
|
1124
|
-
{
|
|
1125
|
-
value: "sessionRetention",
|
|
1126
|
-
label: "Session Retention \u2014 \uC138\uC158 30\uC77C \uBCF4\uC874",
|
|
1127
|
-
hint: "general.sessionRetention.maxAge: 30d \u2014 \uC774\uC804 \uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C 30\uC77C\uAC04 \uC720\uC9C0",
|
|
1128
|
-
patch: { general: { sessionRetention: { maxAge: "30d" } } }
|
|
1129
|
-
},
|
|
1130
|
-
{
|
|
1131
|
-
value: "experimental",
|
|
1132
|
-
label: "Experimental \u2014 JIT \uCEE8\uD14D\uC2A4\uD2B8 + Plan \uAE30\uB2A5",
|
|
1133
|
-
hint: "experimental.jitContext: true, plan: true \u2014 \uC11C\uBE0C\uB514\uB809\uD1A0\uB9AC \uCEE8\uD14D\uC2A4\uD2B8 \uC9C0\uC5F0 \uB85C\uB529 \uBC0F \uACC4\uD68D \uAE30\uB2A5 \uC2E4\uD5D8\uC801 \uD65C\uC131\uD654",
|
|
1134
|
-
patch: { experimental: { jitContext: true, plan: true } }
|
|
1254
|
+
var compareDocsStatusEntry = (params) => {
|
|
1255
|
+
const entry = params.entry;
|
|
1256
|
+
if (entry === void 0) {
|
|
1257
|
+
return [issue("error", "docs-status-missing-document", `docs-status \uB204\uB77D: ${params.expected.path}`)];
|
|
1135
1258
|
}
|
|
1136
|
-
];
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
fileName: "settings.json",
|
|
1140
|
-
promptMessage: "Gemini CLI \uC124\uC815 \uD30C\uC77C(.gemini/settings.json)\uC744 \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
1141
|
-
groups: SETTING_GROUPS
|
|
1142
|
-
};
|
|
1143
|
-
var promptGeminiSettings = () => promptToolSettings(CONFIG);
|
|
1144
|
-
var installGeminiSettings = (basePath, selectedValues) => installToolSettings(basePath, selectedValues, CONFIG);
|
|
1145
|
-
var uninstallGeminiSettings = (basePath, selectedValues) => uninstallToolSettings(basePath, selectedValues, CONFIG);
|
|
1146
|
-
|
|
1147
|
-
// src/lib/claude-settings.ts
|
|
1148
|
-
var SETTING_GROUPS2 = [
|
|
1149
|
-
{
|
|
1150
|
-
value: "model",
|
|
1151
|
-
label: "Model \u2014 Plan \uBAA8\uB4DC \uBAA8\uB378",
|
|
1152
|
-
hint: "model: opusplan \u2014 Plan \uBAA8\uB4DC\uC5D0\uC11C Opus \uBAA8\uB378 \uC0AC\uC6A9",
|
|
1153
|
-
patch: { model: "opusplan" }
|
|
1154
|
-
},
|
|
1155
|
-
{
|
|
1156
|
-
value: "plansDirectory",
|
|
1157
|
-
label: "Plans Directory \u2014 \uACC4\uD68D \uD30C\uC77C \uC800\uC7A5 \uACBD\uB85C",
|
|
1158
|
-
hint: "plansDirectory: ./.claude/plans \u2014 \uACC4\uD68D \uD30C\uC77C\uC744 .claude/plans\uC5D0 \uC800\uC7A5",
|
|
1159
|
-
patch: { plansDirectory: "./.claude/plans" }
|
|
1259
|
+
const issues = [];
|
|
1260
|
+
if (entry.status !== params.expected.status) {
|
|
1261
|
+
issues.push(issue("error", "docs-status-mismatch", `${params.expected.path} docs-status status \uBD88\uC77C\uCE58`));
|
|
1160
1262
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
${
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
const
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1263
|
+
if (entry.owner !== params.expected.owner) {
|
|
1264
|
+
issues.push(issue("error", "docs-status-mismatch", `${params.expected.path} docs-status owner \uBD88\uC77C\uCE58`));
|
|
1265
|
+
}
|
|
1266
|
+
return issues;
|
|
1267
|
+
};
|
|
1268
|
+
var diffProjectLayer = (basePath) => {
|
|
1269
|
+
let manifest;
|
|
1270
|
+
try {
|
|
1271
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1274
|
+
return {
|
|
1275
|
+
currentSourceHash: null,
|
|
1276
|
+
issues: [issue("error", "invalid-manifest", `${PROJECT_LAYER_MANIFEST_RELATIVE_PATH} \uD30C\uC2F1 \uC2E4\uD328: ${reason}`)]
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
if (!manifest) {
|
|
1280
|
+
return {
|
|
1281
|
+
currentSourceHash: null,
|
|
1282
|
+
issues: [issue("error", "missing-manifest", `${PROJECT_LAYER_MANIFEST_RELATIVE_PATH}\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`)]
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
const specs = loadProjectLayerTemplateSpecs(manifest.tools);
|
|
1286
|
+
const currentSourceHash = computeProjectLayerSourceHash(specs);
|
|
1287
|
+
let contextIndex = null;
|
|
1288
|
+
const issues = [];
|
|
1289
|
+
try {
|
|
1290
|
+
contextIndex = readProjectLayerContextIndex(basePath);
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1293
|
+
issues.push(
|
|
1294
|
+
issue("error", "invalid-context-index", `${PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH} \uD30C\uC2F1 \uC2E4\uD328: ${reason}`)
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
const contextMap = buildContextIndexMap(contextIndex);
|
|
1298
|
+
const expectedManagedPaths = new Set(specs.filter((spec) => spec.ownership === "managed").map((spec) => spec.path));
|
|
1299
|
+
const manifestManagedPaths = new Set(manifest.managed_files.map((file) => file.path));
|
|
1300
|
+
if (manifest.sourceHash !== currentSourceHash) {
|
|
1301
|
+
issues.push(
|
|
1302
|
+
issue("warning", "source-hash-drift", `template sourceHash \uBCC0\uACBD: ${manifest.sourceHash} -> ${currentSourceHash}`)
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
if (contextIndex === null) {
|
|
1306
|
+
issues.push(issue("error", "missing-context-index", `${PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH}\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`));
|
|
1307
|
+
}
|
|
1308
|
+
for (const expectedPath of expectedManagedPaths) {
|
|
1309
|
+
if (!manifestManagedPaths.has(expectedPath)) {
|
|
1310
|
+
issues.push(issue("error", "manifest-missing-managed-file", `manifest managed_files \uB204\uB77D: ${expectedPath}`));
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
for (const file of manifest.managed_files) {
|
|
1314
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1315
|
+
if (!existsSync2(absolutePath)) {
|
|
1316
|
+
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1204
1317
|
continue;
|
|
1205
1318
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1319
|
+
const content = readFileSync6(absolutePath, "utf-8");
|
|
1320
|
+
const meta = parseAiOpsMeta(content);
|
|
1321
|
+
if (!meta) {
|
|
1322
|
+
issues.push(issue("error", "missing-managed-section", `managed section \uBA54\uD0C0 \uC5C6\uC74C: ${file.path}`));
|
|
1208
1323
|
continue;
|
|
1209
1324
|
}
|
|
1210
|
-
if (
|
|
1325
|
+
if (meta.sourceHash !== currentSourceHash) {
|
|
1326
|
+
issues.push(
|
|
1327
|
+
issue("warning", "managed-source-hash-drift", `${file.path} sourceHash \uBCC0\uACBD: ${meta.sourceHash} -> ${currentSourceHash}`)
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1211
1330
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
const
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
continue;
|
|
1331
|
+
for (const file of manifest.project_files) {
|
|
1332
|
+
if (!existsSync2(resolveProjectLayerFilePath(basePath, file.path))) {
|
|
1333
|
+
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
for (const pack of manifest.packs) {
|
|
1337
|
+
for (const file of [...pack.documents, ...pack.files]) {
|
|
1338
|
+
if (!existsSync2(resolveProjectLayerFilePath(basePath, file.path))) {
|
|
1339
|
+
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1340
|
+
}
|
|
1223
1341
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1342
|
+
}
|
|
1343
|
+
for (const path of collectDocumentPathsFromManifest(manifest)) {
|
|
1344
|
+
const document = readDocumentSafely(basePath, path);
|
|
1345
|
+
if ("code" in document) {
|
|
1346
|
+
issues.push(document);
|
|
1226
1347
|
continue;
|
|
1227
1348
|
}
|
|
1228
|
-
|
|
1349
|
+
issues.push(...compareContextDocument({ expected: document, indexed: contextMap.get(path) }));
|
|
1229
1350
|
}
|
|
1230
|
-
return
|
|
1351
|
+
return { currentSourceHash, issues };
|
|
1231
1352
|
};
|
|
1232
|
-
var
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
};
|
|
1240
|
-
var installPrettierIgnore = (basePath) => {
|
|
1241
|
-
const filePath = join12(basePath, ".prettierignore");
|
|
1242
|
-
const section = wrapSection(PRETTIER_IGNORE_CONTENT);
|
|
1243
|
-
if (!existsSync6(filePath)) {
|
|
1244
|
-
writeFileSync6(filePath, section + "\n", "utf-8");
|
|
1245
|
-
return;
|
|
1353
|
+
var auditProjectLayer = (basePath) => {
|
|
1354
|
+
const diffReport = diffProjectLayer(basePath);
|
|
1355
|
+
let manifest;
|
|
1356
|
+
try {
|
|
1357
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1358
|
+
} catch {
|
|
1359
|
+
return diffReport;
|
|
1246
1360
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
writeFileSync6(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
|
|
1250
|
-
return;
|
|
1361
|
+
if (!manifest) {
|
|
1362
|
+
return diffReport;
|
|
1251
1363
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1364
|
+
let contextIndex = null;
|
|
1365
|
+
try {
|
|
1366
|
+
contextIndex = readProjectLayerContextIndex(basePath);
|
|
1367
|
+
} catch {
|
|
1368
|
+
contextIndex = null;
|
|
1369
|
+
}
|
|
1370
|
+
const documentPaths = collectDocumentPathsFromManifest(manifest);
|
|
1371
|
+
const documentPathSet = new Set(documentPaths);
|
|
1372
|
+
const contextPathSet = new Set(contextIndex?.documents.map((document) => document.path) ?? []);
|
|
1373
|
+
const issues = [...diffReport.issues];
|
|
1374
|
+
const docsStatusPath = resolveProjectLayerFilePath(basePath, "docs/docs-status.md");
|
|
1375
|
+
if (!existsSync2(docsStatusPath)) {
|
|
1376
|
+
issues.push(issue("error", "missing-docs-status", "docs/docs-status.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
1377
|
+
return { currentSourceHash: diffReport.currentSourceHash, issues };
|
|
1378
|
+
}
|
|
1379
|
+
let docsStatusEntries = [];
|
|
1380
|
+
try {
|
|
1381
|
+
docsStatusEntries = parseDocsStatusEntries(readFileSync6(docsStatusPath, "utf-8"));
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1384
|
+
issues.push(issue("error", "invalid-docs-status", `docs/docs-status.md \uD30C\uC2F1 \uC2E4\uD328: ${reason}`));
|
|
1385
|
+
}
|
|
1386
|
+
const docsStatusMap = new Map(docsStatusEntries.map((entry) => [entry.path, entry]));
|
|
1387
|
+
for (const path of documentPaths) {
|
|
1388
|
+
const document = readDocumentSafely(basePath, path);
|
|
1389
|
+
if ("code" in document) {
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
issues.push(...compareDocsStatusEntry({ expected: document, entry: docsStatusMap.get(path) }));
|
|
1264
1393
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
};
|
|
1268
|
-
|
|
1269
|
-
// src/lib/skill-state.ts
|
|
1270
|
-
var resolveSkillScope = (params) => {
|
|
1271
|
-
if (params.scope !== void 0) {
|
|
1272
|
-
if (params.scope === "user") return "user";
|
|
1273
|
-
if (params.scope === "project") return "project";
|
|
1274
|
-
throw new Error(`Unsupported scope: ${params.scope}`);
|
|
1394
|
+
for (const entry of docsStatusEntries) {
|
|
1395
|
+
if (!documentPathSet.has(entry.path)) {
|
|
1396
|
+
issues.push(issue("warning", "docs-status-extra-document", `docs-status\uC5D0 manifest \uC678 \uBB38\uC11C\uAC00 \uC788\uC2B5\uB2C8\uB2E4: ${entry.path}`));
|
|
1397
|
+
}
|
|
1275
1398
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
};
|
|
1279
|
-
|
|
1280
|
-
if (params.requested === void 0 || params.requested.length === 0) {
|
|
1281
|
-
return [...params.supported];
|
|
1399
|
+
for (const contextPath of contextPathSet) {
|
|
1400
|
+
if (!documentPathSet.has(contextPath)) {
|
|
1401
|
+
issues.push(issue("warning", "context-extra-document", `context-layer\uC5D0 manifest \uC678 \uBB38\uC11C\uAC00 \uC788\uC2B5\uB2C8\uB2E4: ${contextPath}`));
|
|
1402
|
+
}
|
|
1282
1403
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1404
|
+
return { currentSourceHash: diffReport.currentSourceHash, issues };
|
|
1405
|
+
};
|
|
1406
|
+
function removeManagedProjectFile(basePath, relativePath) {
|
|
1407
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, relativePath);
|
|
1408
|
+
if (!existsSync2(absolutePath)) {
|
|
1409
|
+
return { deleted: [], cleaned: [], preserved: [], notFound: [relativePath] };
|
|
1410
|
+
}
|
|
1411
|
+
const content = readFileSync6(absolutePath, "utf-8");
|
|
1412
|
+
if (!hasAiOpsSection(content)) {
|
|
1413
|
+
return { deleted: [], cleaned: [], preserved: [relativePath], notFound: [] };
|
|
1414
|
+
}
|
|
1415
|
+
const stripped = stripAiOpsSection(content);
|
|
1416
|
+
if (stripped.trim().length === 0) {
|
|
1417
|
+
rmSync(absolutePath);
|
|
1418
|
+
return { deleted: [relativePath], cleaned: [], preserved: [], notFound: [] };
|
|
1419
|
+
}
|
|
1420
|
+
writeFileSync4(absolutePath, stripped, "utf-8");
|
|
1421
|
+
return { deleted: [], cleaned: [relativePath], preserved: [], notFound: [] };
|
|
1422
|
+
}
|
|
1423
|
+
var removeCreateOnlyProjectFile = (basePath, file) => {
|
|
1424
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1425
|
+
if (!existsSync2(absolutePath)) {
|
|
1426
|
+
return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
|
|
1427
|
+
}
|
|
1428
|
+
const content = readFileSync6(absolutePath, "utf-8").trimEnd();
|
|
1429
|
+
const currentHash = computeHash([content]);
|
|
1430
|
+
if (file.created && currentHash === file.templateHash) {
|
|
1431
|
+
rmSync(absolutePath);
|
|
1432
|
+
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1433
|
+
}
|
|
1434
|
+
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
1435
|
+
};
|
|
1436
|
+
var removePackOwnedFile = (basePath, file) => {
|
|
1437
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1438
|
+
if (!existsSync2(absolutePath)) {
|
|
1439
|
+
return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
|
|
1440
|
+
}
|
|
1441
|
+
const currentHash = computeHash([readFileSync6(absolutePath, "utf-8").trimEnd()]);
|
|
1442
|
+
if (currentHash === file.sourceHash) {
|
|
1443
|
+
rmSync(absolutePath);
|
|
1444
|
+
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1445
|
+
}
|
|
1446
|
+
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
1447
|
+
};
|
|
1448
|
+
var mergeRemoveResults = (results) => ({
|
|
1449
|
+
deleted: results.flatMap((result) => result.deleted),
|
|
1450
|
+
cleaned: results.flatMap((result) => result.cleaned),
|
|
1451
|
+
preserved: results.flatMap((result) => result.preserved),
|
|
1452
|
+
notFound: results.flatMap((result) => result.notFound)
|
|
1453
|
+
});
|
|
1454
|
+
var removeEmptyDirs = (basePath, relativePaths) => {
|
|
1455
|
+
const dirs = [...new Set(relativePaths.map(toRelativeDir).filter((dir) => dir !== "."))].sort(
|
|
1456
|
+
(a, b) => b.length - a.length
|
|
1457
|
+
);
|
|
1458
|
+
for (const dir of [...dirs, ".ai-ops"]) {
|
|
1459
|
+
const absoluteDir = resolveProjectLayerFilePath(basePath, dir);
|
|
1460
|
+
if (!existsSync2(absoluteDir)) continue;
|
|
1461
|
+
try {
|
|
1462
|
+
if (readdirSync3(absoluteDir).length === 0) {
|
|
1463
|
+
rmSync(absoluteDir, { recursive: true });
|
|
1464
|
+
}
|
|
1465
|
+
} catch {
|
|
1466
|
+
}
|
|
1287
1467
|
}
|
|
1288
|
-
return [...params.requested];
|
|
1289
|
-
};
|
|
1290
|
-
var TOOL_ORDER = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
1291
|
-
var mergeSkillTools = (params) => {
|
|
1292
|
-
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
1293
|
-
return TOOL_ORDER.filter((tool) => merged.has(tool));
|
|
1294
|
-
};
|
|
1295
|
-
var subtractSkillTools = (params) => {
|
|
1296
|
-
const installed = new Set(params.installed ?? []);
|
|
1297
|
-
return params.requested.filter((tool) => !installed.has(tool));
|
|
1298
|
-
};
|
|
1299
|
-
var upsertInstalledSkill = (installedSkills, nextSkill) => {
|
|
1300
|
-
const nextSkillId = resolveCanonicalSkillId(nextSkill.id);
|
|
1301
|
-
const remaining = installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== nextSkillId);
|
|
1302
|
-
return [...remaining, nextSkill];
|
|
1303
1468
|
};
|
|
1304
|
-
var
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1469
|
+
var uninstallProjectLayer = (basePath, manifest) => {
|
|
1470
|
+
const managedResults = manifest.managed_files.map((file) => removeManagedProjectFile(basePath, file.path));
|
|
1471
|
+
const projectResults = manifest.project_files.map((file) => removeCreateOnlyProjectFile(basePath, file));
|
|
1472
|
+
const packResults = manifest.packs.flatMap(
|
|
1473
|
+
(pack) => [...pack.documents, ...pack.files].map((file) => removePackOwnedFile(basePath, file))
|
|
1474
|
+
);
|
|
1475
|
+
const stateFiles = [PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH, PROJECT_LAYER_MANIFEST_RELATIVE_PATH];
|
|
1476
|
+
for (const stateFile of stateFiles) {
|
|
1477
|
+
rmSync(resolveProjectLayerFilePath(basePath, stateFile), { force: true });
|
|
1478
|
+
}
|
|
1479
|
+
const result = mergeRemoveResults([...managedResults, ...projectResults, ...packResults]);
|
|
1480
|
+
removeEmptyDirs(basePath, [...result.deleted, ...stateFiles]);
|
|
1481
|
+
return result;
|
|
1311
1482
|
};
|
|
1312
1483
|
|
|
1313
|
-
// src/
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1484
|
+
// src/core/pack.ts
|
|
1485
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
1486
|
+
import { dirname as dirname7, isAbsolute as isAbsolute2, join as join11, relative as relative2, resolve as resolve6 } from "path";
|
|
1487
|
+
var PACK_REGISTRY_FILENAME = "pack-registry.json";
|
|
1488
|
+
var SPEC_LIFECYCLE_PACK_ID = "spec-lifecycle";
|
|
1489
|
+
var PACK_INSTALL_ROOT = "docs/specs/";
|
|
1490
|
+
var RESERVED_DOCUMENT_WARNINGS2 = [
|
|
1491
|
+
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
1492
|
+
"Do not use this document as current decision-making evidence"
|
|
1318
1493
|
];
|
|
1319
|
-
var
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
}
|
|
1494
|
+
var DEFAULT_PACKS_DIR = join11(COMPILER_DATA_DIR, "packs");
|
|
1495
|
+
var includesReservedDocumentWarning2 = (content) => RESERVED_DOCUMENT_WARNINGS2.some((warning) => content.includes(warning));
|
|
1496
|
+
var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync7(join11(packsDir, PACK_REGISTRY_FILENAME), "utf-8")));
|
|
1497
|
+
var assertPackInstallPath = (path) => {
|
|
1498
|
+
if (!isSafeProjectLayerPath(path) || !path.startsWith(PACK_INSTALL_ROOT)) {
|
|
1499
|
+
throw new Error(`Unsafe pack path: ${path}`);
|
|
1500
|
+
}
|
|
1326
1501
|
};
|
|
1327
|
-
var
|
|
1328
|
-
|
|
1329
|
-
const
|
|
1330
|
-
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1502
|
+
var readPackSourceFiles = (packDir) => {
|
|
1503
|
+
const files = [];
|
|
1504
|
+
const walk = (relativeDir = "") => {
|
|
1505
|
+
const absoluteDir = relativeDir.length > 0 ? join11(packDir, relativeDir) : packDir;
|
|
1506
|
+
const entries = readdirSync4(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1507
|
+
for (const entry of entries) {
|
|
1508
|
+
const nextRelativePath = relativeDir.length > 0 ? join11(relativeDir, entry.name) : entry.name;
|
|
1509
|
+
if (entry.isDirectory()) {
|
|
1510
|
+
walk(nextRelativePath);
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
assertPackInstallPath(nextRelativePath);
|
|
1514
|
+
const content = readFileSync7(join11(packDir, nextRelativePath), "utf-8");
|
|
1515
|
+
files.push({
|
|
1516
|
+
path: nextRelativePath,
|
|
1517
|
+
content,
|
|
1518
|
+
sourceHash: computeHash([content.trimEnd()])
|
|
1336
1519
|
});
|
|
1337
|
-
continue;
|
|
1338
1520
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
existing: previous.requestedTools,
|
|
1343
|
-
requested: target.requestedTools
|
|
1344
|
-
})
|
|
1345
|
-
});
|
|
1346
|
-
}
|
|
1347
|
-
return [...merged.values()].sort((a, b) => a.skill.id.localeCompare(b.skill.id));
|
|
1521
|
+
};
|
|
1522
|
+
walk();
|
|
1523
|
+
return files;
|
|
1348
1524
|
};
|
|
1349
|
-
var
|
|
1350
|
-
|
|
1351
|
-
const
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
continue;
|
|
1356
|
-
}
|
|
1357
|
-
const supportedRequestedTools = resolveSupportedRequestedTools(skill, params.selectedTools);
|
|
1358
|
-
if (supportedRequestedTools.length === 0) {
|
|
1525
|
+
var splitPackSourceFiles = (files) => {
|
|
1526
|
+
const documents = [];
|
|
1527
|
+
const regularFiles = [];
|
|
1528
|
+
for (const file of files) {
|
|
1529
|
+
if (!file.path.endsWith(".md")) {
|
|
1530
|
+
regularFiles.push(file);
|
|
1359
1531
|
continue;
|
|
1360
1532
|
}
|
|
1361
|
-
const
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
installed: availableTools
|
|
1366
|
-
});
|
|
1367
|
-
if (requestedTools.length === 0) {
|
|
1368
|
-
globalSkills.push({
|
|
1369
|
-
skill,
|
|
1370
|
-
availableTools
|
|
1371
|
-
});
|
|
1372
|
-
continue;
|
|
1533
|
+
const { frontmatter } = parseMarkdownFrontmatter(file.content);
|
|
1534
|
+
const parsed = ProjectLayerFrontmatterSchema.parse(frontmatter);
|
|
1535
|
+
if (parsed.status === "Reserved" && !includesReservedDocumentWarning2(file.content)) {
|
|
1536
|
+
throw new Error(`Reserved pack document must include warning text: ${file.path}`);
|
|
1373
1537
|
}
|
|
1374
|
-
|
|
1375
|
-
skill,
|
|
1376
|
-
requestedTools,
|
|
1377
|
-
globalTools: availableTools
|
|
1378
|
-
});
|
|
1538
|
+
documents.push(file);
|
|
1379
1539
|
}
|
|
1380
|
-
return {
|
|
1381
|
-
globalSkills,
|
|
1382
|
-
installableSkills
|
|
1383
|
-
};
|
|
1540
|
+
return { documents, files: regularFiles };
|
|
1384
1541
|
};
|
|
1385
|
-
var
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
const { globalSkills, installableSkills } = partitionPresetSkills({
|
|
1400
|
-
preset,
|
|
1401
|
-
allSkills,
|
|
1402
|
-
selectedTools,
|
|
1403
|
-
globalInstalledSkills
|
|
1404
|
-
});
|
|
1405
|
-
if (globalSkills.length > 0) {
|
|
1406
|
-
const globalLines = globalSkills.map(
|
|
1407
|
-
({ skill, availableTools }) => ` \u2713 ${skill.id} (${formatToolList(availableTools)})`
|
|
1408
|
-
);
|
|
1409
|
-
p3.note(globalLines.join("\n"), `[${workspaceName}] already available globally`);
|
|
1410
|
-
}
|
|
1411
|
-
if (installableSkills.length === 0) {
|
|
1412
|
-
p3.note(" \uC0C8\uB85C \uC124\uCE58\uD560 reference skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", `[${workspaceName}] installable reference skills`);
|
|
1542
|
+
var loadAllPacks = (packsDir) => {
|
|
1543
|
+
const catalog = readPackCatalog(packsDir);
|
|
1544
|
+
const entries = [...catalog.packs].sort((a, b) => a.id.localeCompare(b.id));
|
|
1545
|
+
return entries.map((entry) => {
|
|
1546
|
+
if (entry.id !== SPEC_LIFECYCLE_PACK_ID) {
|
|
1547
|
+
throw new Error(`Unsupported pack id: ${entry.id}`);
|
|
1548
|
+
}
|
|
1549
|
+
const packDir = resolve6(packsDir, entry.source_path);
|
|
1550
|
+
const relativeFromPacks = relative2(resolve6(packsDir), packDir);
|
|
1551
|
+
if (relativeFromPacks.length === 0 || relativeFromPacks.startsWith("..") || isAbsolute2(relativeFromPacks)) {
|
|
1552
|
+
throw new Error(`Pack source path escapes packs dir: ${entry.source_path}`);
|
|
1553
|
+
}
|
|
1554
|
+
const files = readPackSourceFiles(packDir);
|
|
1555
|
+
const split = splitPackSourceFiles(files);
|
|
1413
1556
|
return {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1557
|
+
id: entry.id,
|
|
1558
|
+
sourceHash: computeHash(files.map((file) => `${file.path}:${file.content}`).sort()),
|
|
1559
|
+
documents: split.documents,
|
|
1560
|
+
files: split.files
|
|
1418
1561
|
};
|
|
1419
|
-
}
|
|
1420
|
-
const selectedSkillIds = await p3.multiselect({
|
|
1421
|
-
message: `[${workspaceName}] installable reference skills \uC120\uD0DD`,
|
|
1422
|
-
options: installableSkills.map(({ skill, requestedTools, globalTools }) => ({
|
|
1423
|
-
value: skill.id,
|
|
1424
|
-
label: skill.id,
|
|
1425
|
-
hint: globalTools.length > 0 ? `global: ${formatToolList(globalTools)} / install: ${formatToolList(requestedTools)}` : `${skill.description} / install: ${formatToolList(requestedTools)}`
|
|
1426
|
-
})),
|
|
1427
|
-
initialValues: installableSkills.map(({ skill }) => skill.id),
|
|
1428
|
-
required: false
|
|
1429
1562
|
});
|
|
1430
|
-
if (p3.isCancel(selectedSkillIds)) return null;
|
|
1431
|
-
const selectedSkillSet = new Set(selectedSkillIds);
|
|
1432
|
-
return {
|
|
1433
|
-
workspace: workspaceName,
|
|
1434
|
-
preset,
|
|
1435
|
-
finalRules,
|
|
1436
|
-
finalSkillTargets: installableSkills.filter(({ skill }) => selectedSkillSet.has(skill.id)).map(({ skill, requestedTools }) => ({
|
|
1437
|
-
skill,
|
|
1438
|
-
requestedTools
|
|
1439
|
-
}))
|
|
1440
|
-
};
|
|
1441
1563
|
};
|
|
1442
|
-
var
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1564
|
+
var resolvePackById = (packsDir, packId) => {
|
|
1565
|
+
const pack = loadAllPacks(packsDir).find((candidate) => candidate.id === packId);
|
|
1566
|
+
if (!pack) {
|
|
1567
|
+
throw new Error(`Unknown pack: ${packId}`);
|
|
1568
|
+
}
|
|
1569
|
+
return pack;
|
|
1570
|
+
};
|
|
1571
|
+
var serializePackFileContent = (content) => content.length === 0 ? "" : content.trimEnd() + "\n";
|
|
1572
|
+
var readProjectFileHash = (basePath, relativePath) => computeHash([readFileSync7(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
|
|
1573
|
+
var writePackFile = (basePath, file) => {
|
|
1574
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1575
|
+
mkdirSync5(dirname7(absolutePath), { recursive: true });
|
|
1576
|
+
writeFileSync5(absolutePath, serializePackFileContent(file.content), "utf-8");
|
|
1577
|
+
};
|
|
1578
|
+
var buildPackFileRecords = (files) => files.map((file) => ({
|
|
1579
|
+
path: file.path,
|
|
1580
|
+
sourceHash: file.sourceHash
|
|
1581
|
+
}));
|
|
1582
|
+
var buildPackRecord = (params) => ({
|
|
1583
|
+
id: params.pack.id,
|
|
1584
|
+
sourceHash: params.pack.sourceHash,
|
|
1585
|
+
documents: buildPackFileRecords(params.pack.documents),
|
|
1586
|
+
files: buildPackFileRecords(params.pack.files),
|
|
1587
|
+
installedAt: params.installedAt
|
|
1588
|
+
});
|
|
1589
|
+
var applyPackSourceFiles = (params) => {
|
|
1590
|
+
const written = [];
|
|
1591
|
+
const refreshed = [];
|
|
1592
|
+
const preserved = [];
|
|
1593
|
+
const deleted = [];
|
|
1594
|
+
const notFound = [];
|
|
1595
|
+
const sourceFiles = [...params.pack.documents, ...params.pack.files];
|
|
1596
|
+
const sourceByPath = new Map(sourceFiles.map((file) => [file.path, file]));
|
|
1597
|
+
const previousByPath = new Map(
|
|
1598
|
+
[...params.previousRecord?.documents ?? [], ...params.previousRecord?.files ?? []].map((file) => [
|
|
1599
|
+
file.path,
|
|
1600
|
+
file
|
|
1601
|
+
])
|
|
1602
|
+
);
|
|
1603
|
+
for (const file of sourceFiles) {
|
|
1604
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, file.path);
|
|
1605
|
+
const previous = previousByPath.get(file.path);
|
|
1606
|
+
if (!existsSync3(absolutePath)) {
|
|
1607
|
+
writePackFile(params.basePath, file);
|
|
1608
|
+
written.push(file.path);
|
|
1609
|
+
continue;
|
|
1481
1610
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
});
|
|
1486
|
-
if (p3.isCancel(isMonorepo)) {
|
|
1487
|
-
cancelInit();
|
|
1488
|
-
}
|
|
1489
|
-
const allRules = loadAllRules(rulesDir);
|
|
1490
|
-
const allSkills = loadAllSkills(skillsDir);
|
|
1491
|
-
const presets = loadPresets(resolvePresetsPath());
|
|
1492
|
-
const sourceHash = computeSourceHash(resolveCompilerDataDir());
|
|
1493
|
-
const globalInstalledSkills = readSkillRegistry(resolveSkillRegistryPath(userBasePath))?.skills ?? [];
|
|
1494
|
-
const mappings = [];
|
|
1495
|
-
if (!isMonorepo) {
|
|
1496
|
-
const mapping = await selectPresetAndFineTune(
|
|
1497
|
-
".",
|
|
1498
|
-
presets,
|
|
1499
|
-
allRules,
|
|
1500
|
-
allSkills,
|
|
1501
|
-
selectedTools,
|
|
1502
|
-
globalInstalledSkills
|
|
1503
|
-
) ?? cancelInit();
|
|
1504
|
-
mappings.push(mapping);
|
|
1505
|
-
} else {
|
|
1506
|
-
const candidates = listWorkspaceCandidates(basePath);
|
|
1507
|
-
const selectedWorkspaces = await p3.multiselect({
|
|
1508
|
-
message: "\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1509
|
-
options: candidates.map((candidate) => ({ value: candidate, label: candidate })),
|
|
1510
|
-
required: true
|
|
1511
|
-
});
|
|
1512
|
-
if (p3.isCancel(selectedWorkspaces)) {
|
|
1513
|
-
cancelInit();
|
|
1514
|
-
}
|
|
1515
|
-
for (const workspace of selectedWorkspaces) {
|
|
1516
|
-
const mapping = await selectPresetAndFineTune(
|
|
1517
|
-
workspace,
|
|
1518
|
-
presets,
|
|
1519
|
-
allRules,
|
|
1520
|
-
allSkills,
|
|
1521
|
-
selectedTools,
|
|
1522
|
-
globalInstalledSkills
|
|
1523
|
-
) ?? cancelInit();
|
|
1524
|
-
mappings.push(mapping);
|
|
1525
|
-
}
|
|
1611
|
+
if (previous === void 0) {
|
|
1612
|
+
preserved.push(file.path);
|
|
1613
|
+
continue;
|
|
1526
1614
|
}
|
|
1527
|
-
const
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
}
|
|
1532
|
-
const geminiSettingValues = selectedTools.includes("gemini") ? await promptGeminiSettings() : null;
|
|
1533
|
-
const resolvedGeminiSettingValues = isPromptCancelled(geminiSettingValues) ? cancelInit() : geminiSettingValues;
|
|
1534
|
-
const claudeSettingValues = selectedTools.includes("claude-code") ? await promptClaudeSettings() : null;
|
|
1535
|
-
const resolvedClaudeSettingValues = isPromptCancelled(claudeSettingValues) ? cancelInit() : claudeSettingValues;
|
|
1536
|
-
const wantPrettierIgnore = await promptPrettierIgnore();
|
|
1537
|
-
const resolvedWantPrettierIgnore = isPromptCancelled(wantPrettierIgnore) ? cancelInit() : wantPrettierIgnore;
|
|
1538
|
-
spinner3.start("\uADDC\uCE59 \uC124\uCE58 \uC911...");
|
|
1539
|
-
spinnerStarted = true;
|
|
1540
|
-
const meta = { sourceHash, generatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1541
|
-
const allInstalledFiles = [];
|
|
1542
|
-
const allAppended = [];
|
|
1543
|
-
const selectedRuleIds = deduplicateRules(mappings.flatMap((mapping) => mapping.finalRules)).map((rule) => rule.id);
|
|
1544
|
-
let projectInstalledSkills = [];
|
|
1545
|
-
if (selectedSkillTargets.length > 0 && skillScope !== null) {
|
|
1546
|
-
const skillBasePath = skillScope === "project" ? basePath : userBasePath;
|
|
1547
|
-
const installedSkills = selectedSkillTargets.map(({ skill, requestedTools }) => {
|
|
1548
|
-
const existingUserSkill = skillScope === "user" ? findInstalledSkill(globalInstalledSkills, skill.id) : void 0;
|
|
1549
|
-
const nextRequestedTools = skillScope === "user" ? mergeSkillTools({
|
|
1550
|
-
existing: existingUserSkill?.tools,
|
|
1551
|
-
requested: requestedTools
|
|
1552
|
-
}) : requestedTools;
|
|
1553
|
-
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1554
|
-
skill,
|
|
1555
|
-
requestedTools: nextRequestedTools,
|
|
1556
|
-
scope: skillScope
|
|
1557
|
-
});
|
|
1558
|
-
installSkillPackages(skillBasePath, packages);
|
|
1559
|
-
return installedSkill;
|
|
1560
|
-
});
|
|
1561
|
-
if (skillScope === "project") {
|
|
1562
|
-
projectInstalledSkills = installedSkills;
|
|
1563
|
-
} else {
|
|
1564
|
-
const registryPath = resolveSkillRegistryPath(skillBasePath);
|
|
1565
|
-
const previous = readSkillRegistry(registryPath);
|
|
1566
|
-
const nextSkills = installedSkills.reduce(
|
|
1567
|
-
(acc, installedSkill) => upsertInstalledSkill(acc, installedSkill),
|
|
1568
|
-
previous?.skills ?? []
|
|
1569
|
-
);
|
|
1570
|
-
writeSkillRegistry(registryPath, {
|
|
1571
|
-
skills: nextSkills,
|
|
1572
|
-
cliVersion: getCliVersion(),
|
|
1573
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1615
|
+
const currentHash = readProjectFileHash(params.basePath, file.path);
|
|
1616
|
+
if (currentHash !== previous.sourceHash) {
|
|
1617
|
+
preserved.push(file.path);
|
|
1618
|
+
continue;
|
|
1576
1619
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
const workspaceMappings = mappings.map((mapping) => ({
|
|
1581
|
-
path: mapping.workspace,
|
|
1582
|
-
ruleIds: mapping.finalRules.map((rule) => rule.id)
|
|
1583
|
-
}));
|
|
1584
|
-
const renderResult = renderForTool(toolId, allWorkspaceRules, workspaceMappings);
|
|
1585
|
-
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1586
|
-
const result = installFiles(basePath, actions, meta);
|
|
1587
|
-
allInstalledFiles.push(...result.written);
|
|
1588
|
-
allAppended.push(...result.appended);
|
|
1589
|
-
} else {
|
|
1590
|
-
const renderResult = renderForTool(toolId, mappings[0].finalRules);
|
|
1591
|
-
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1592
|
-
const result = installFiles(basePath, actions, meta);
|
|
1593
|
-
allInstalledFiles.push(...result.written);
|
|
1594
|
-
allAppended.push(...result.appended);
|
|
1595
|
-
}
|
|
1620
|
+
if (currentHash !== file.sourceHash) {
|
|
1621
|
+
writePackFile(params.basePath, file);
|
|
1622
|
+
refreshed.push(file.path);
|
|
1596
1623
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1624
|
+
}
|
|
1625
|
+
for (const previous of previousByPath.values()) {
|
|
1626
|
+
if (sourceByPath.has(previous.path)) {
|
|
1627
|
+
continue;
|
|
1599
1628
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1629
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, previous.path);
|
|
1630
|
+
if (!existsSync3(absolutePath)) {
|
|
1631
|
+
notFound.push(previous.path);
|
|
1632
|
+
continue;
|
|
1602
1633
|
}
|
|
1603
|
-
if (
|
|
1604
|
-
|
|
1634
|
+
if (readProjectFileHash(params.basePath, previous.path) === previous.sourceHash) {
|
|
1635
|
+
rmSync2(absolutePath);
|
|
1636
|
+
deleted.push(previous.path);
|
|
1637
|
+
} else {
|
|
1638
|
+
preserved.push(previous.path);
|
|
1605
1639
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
tools: selectedTools,
|
|
1619
|
-
scope: "project",
|
|
1620
|
-
preset: !isMonorepo ? mappings[0].preset.id : void 0,
|
|
1621
|
-
workspaces: workspacesRecord,
|
|
1622
|
-
installedRules: selectedRuleIds,
|
|
1623
|
-
installedFiles: allInstalledFiles,
|
|
1624
|
-
installedSkills: projectInstalledSkills,
|
|
1625
|
-
appendedFiles: allAppended,
|
|
1626
|
-
settings: resolvedClaudeSettingValues || resolvedGeminiSettingValues || resolvedWantPrettierIgnore ? {
|
|
1627
|
-
claude: resolvedClaudeSettingValues ? [...resolvedClaudeSettingValues] : void 0,
|
|
1628
|
-
gemini: resolvedGeminiSettingValues ? [...resolvedGeminiSettingValues] : void 0,
|
|
1629
|
-
prettierignore: resolvedWantPrettierIgnore || void 0
|
|
1630
|
-
} : void 0,
|
|
1631
|
-
cliVersion: getCliVersion(),
|
|
1632
|
-
sourceHash
|
|
1633
|
-
});
|
|
1634
|
-
writeManifest(resolveManifestPath(basePath), manifest);
|
|
1635
|
-
if (allAppended.length > 0) {
|
|
1636
|
-
p3.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 \uC139\uC158 \uCD94\uAC00\uB428 (\uB0B4\uC6A9 \uBCF4\uC874):
|
|
1637
|
-
${allAppended.map((file) => ` ${file}`).join("\n")}`);
|
|
1640
|
+
}
|
|
1641
|
+
return { written, refreshed, preserved, deleted, notFound };
|
|
1642
|
+
};
|
|
1643
|
+
var removePackFiles = (basePath, record) => {
|
|
1644
|
+
const deleted = [];
|
|
1645
|
+
const preserved = [];
|
|
1646
|
+
const notFound = [];
|
|
1647
|
+
for (const file of [...record.documents, ...record.files]) {
|
|
1648
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1649
|
+
if (!existsSync3(absolutePath)) {
|
|
1650
|
+
notFound.push(file.path);
|
|
1651
|
+
continue;
|
|
1638
1652
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1653
|
+
if (readProjectFileHash(basePath, file.path) === file.sourceHash) {
|
|
1654
|
+
rmSync2(absolutePath);
|
|
1655
|
+
deleted.push(file.path);
|
|
1656
|
+
} else {
|
|
1657
|
+
preserved.push(file.path);
|
|
1643
1658
|
}
|
|
1644
|
-
p3.outro("ai-ops init \uC644\uB8CC");
|
|
1645
|
-
} finally {
|
|
1646
|
-
process.off("SIGINT", handleSigint);
|
|
1647
1659
|
}
|
|
1660
|
+
return { written: [], refreshed: [], preserved, deleted, notFound };
|
|
1648
1661
|
};
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1662
|
+
var removeEmptyDirs2 = (basePath, relativePaths) => {
|
|
1663
|
+
const dirs = [...new Set(relativePaths.map((path) => dirname7(path)).filter((dir) => dir !== "."))].sort(
|
|
1664
|
+
(a, b) => b.length - a.length
|
|
1665
|
+
);
|
|
1666
|
+
for (const dir of dirs) {
|
|
1667
|
+
const absoluteDir = resolveProjectLayerFilePath(basePath, dir);
|
|
1668
|
+
if (!existsSync3(absoluteDir)) {
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
try {
|
|
1672
|
+
if (readdirSync4(absoluteDir).length === 0) {
|
|
1673
|
+
rmSync2(absoluteDir, { recursive: true });
|
|
1674
|
+
}
|
|
1675
|
+
} catch {
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
var requireProjectLayerManifest = (basePath) => {
|
|
1680
|
+
const manifest = readProjectLayerManifest(basePath);
|
|
1657
1681
|
if (!manifest) {
|
|
1658
|
-
|
|
1659
|
-
process.exit(1);
|
|
1682
|
+
throw new Error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1660
1683
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1684
|
+
return manifest;
|
|
1685
|
+
};
|
|
1686
|
+
var upsertPackRecord = (manifest, record, generatedAt) => ProjectLayerManifestSchema.parse({
|
|
1687
|
+
...manifest,
|
|
1688
|
+
packs: [...manifest.packs.filter((pack) => pack.id !== record.id), record],
|
|
1689
|
+
cliVersion: getCliVersion(),
|
|
1690
|
+
generatedAt
|
|
1691
|
+
});
|
|
1692
|
+
var removePackRecord = (manifest, packId, generatedAt) => ProjectLayerManifestSchema.parse({
|
|
1693
|
+
...manifest,
|
|
1694
|
+
packs: manifest.packs.filter((pack) => pack.id !== packId),
|
|
1695
|
+
cliVersion: getCliVersion(),
|
|
1696
|
+
generatedAt
|
|
1697
|
+
});
|
|
1698
|
+
var writeManifestWithDerivedState = (params) => {
|
|
1699
|
+
const derived = refreshProjectLayerDerivedState({
|
|
1700
|
+
basePath: params.basePath,
|
|
1701
|
+
manifest: params.manifest,
|
|
1702
|
+
generatedAt: params.generatedAt
|
|
1703
|
+
});
|
|
1704
|
+
writeProjectLayerManifest(params.basePath, derived.manifest);
|
|
1705
|
+
return derived;
|
|
1706
|
+
};
|
|
1707
|
+
var installProjectLayerPack = (params) => {
|
|
1708
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1709
|
+
const previousRecord = manifest.packs.find((pack2) => pack2.id === params.packId);
|
|
1710
|
+
if (previousRecord) {
|
|
1711
|
+
return updateProjectLayerPack(params);
|
|
1712
|
+
}
|
|
1713
|
+
const pack = resolvePackById(params.packsDir ?? DEFAULT_PACKS_DIR, params.packId);
|
|
1714
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1715
|
+
const applyResult = applyPackSourceFiles({ basePath: params.basePath, pack, previousRecord: null });
|
|
1716
|
+
const nextManifest = upsertPackRecord(manifest, buildPackRecord({ pack, installedAt }), installedAt);
|
|
1717
|
+
const derived = writeManifestWithDerivedState({
|
|
1718
|
+
basePath: params.basePath,
|
|
1719
|
+
manifest: nextManifest,
|
|
1720
|
+
generatedAt: installedAt
|
|
1673
1721
|
});
|
|
1674
|
-
|
|
1722
|
+
return { ...applyResult, manifest: derived.manifest, contextIndex: derived.contextIndex };
|
|
1723
|
+
};
|
|
1724
|
+
var updateProjectLayerPack = (params) => {
|
|
1725
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1726
|
+
const previousRecord = manifest.packs.find((pack2) => pack2.id === params.packId);
|
|
1727
|
+
if (!previousRecord) {
|
|
1728
|
+
throw new Error(`\uC124\uCE58\uB41C pack\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${params.packId}`);
|
|
1729
|
+
}
|
|
1730
|
+
const pack = resolvePackById(params.packsDir ?? DEFAULT_PACKS_DIR, params.packId);
|
|
1731
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1732
|
+
const applyResult = applyPackSourceFiles({ basePath: params.basePath, pack, previousRecord });
|
|
1733
|
+
const nextManifest = upsertPackRecord(
|
|
1675
1734
|
manifest,
|
|
1676
|
-
|
|
1735
|
+
buildPackRecord({ pack, installedAt: previousRecord.installedAt }),
|
|
1736
|
+
generatedAt
|
|
1737
|
+
);
|
|
1738
|
+
const derived = writeManifestWithDerivedState({
|
|
1739
|
+
basePath: params.basePath,
|
|
1740
|
+
manifest: nextManifest,
|
|
1741
|
+
generatedAt
|
|
1677
1742
|
});
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1743
|
+
removeEmptyDirs2(params.basePath, applyResult.deleted);
|
|
1744
|
+
return { ...applyResult, manifest: derived.manifest, contextIndex: derived.contextIndex };
|
|
1745
|
+
};
|
|
1746
|
+
var uninstallProjectLayerPack = (params) => {
|
|
1747
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1748
|
+
const previousRecord = manifest.packs.find((pack) => pack.id === params.packId);
|
|
1749
|
+
if (!previousRecord) {
|
|
1750
|
+
throw new Error(`\uC124\uCE58\uB41C pack\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${params.packId}`);
|
|
1751
|
+
}
|
|
1752
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1753
|
+
const applyResult = removePackFiles(params.basePath, previousRecord);
|
|
1754
|
+
const nextManifest = removePackRecord(manifest, params.packId, generatedAt);
|
|
1755
|
+
const derived = writeManifestWithDerivedState({
|
|
1756
|
+
basePath: params.basePath,
|
|
1757
|
+
manifest: nextManifest,
|
|
1758
|
+
generatedAt
|
|
1759
|
+
});
|
|
1760
|
+
removeEmptyDirs2(params.basePath, applyResult.deleted);
|
|
1761
|
+
return { ...applyResult, manifest: derived.manifest, contextIndex: derived.contextIndex };
|
|
1762
|
+
};
|
|
1763
|
+
var packIssue = (level, code, message) => ({
|
|
1764
|
+
level,
|
|
1765
|
+
code,
|
|
1766
|
+
message
|
|
1767
|
+
});
|
|
1768
|
+
var diffProjectLayerPack = (params) => {
|
|
1769
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1770
|
+
const targets = params.packId ? manifest.packs.filter((pack) => pack.id === params.packId) : manifest.packs;
|
|
1771
|
+
const issues = [];
|
|
1772
|
+
if (targets.length === 0) {
|
|
1773
|
+
return { issues: [packIssue("warning", "missing-pack", "\uBE44\uAD50\uD560 \uC124\uCE58\uB41C pack\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.")] };
|
|
1774
|
+
}
|
|
1775
|
+
for (const record of targets) {
|
|
1776
|
+
const pack = resolvePackById(params.packsDir ?? DEFAULT_PACKS_DIR, record.id);
|
|
1777
|
+
if (record.sourceHash !== pack.sourceHash) {
|
|
1778
|
+
issues.push(
|
|
1779
|
+
packIssue("warning", "pack-source-hash-drift", `${record.id} sourceHash \uBCC0\uACBD: ${record.sourceHash} -> ${pack.sourceHash}`)
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
for (const file of [...record.documents, ...record.files]) {
|
|
1783
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, file.path);
|
|
1784
|
+
if (!existsSync3(absolutePath)) {
|
|
1785
|
+
issues.push(packIssue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
return { issues };
|
|
1790
|
+
};
|
|
1791
|
+
|
|
1792
|
+
// src/core/uninstall-plan.ts
|
|
1793
|
+
import { join as join12 } from "path";
|
|
1794
|
+
|
|
1795
|
+
// src/lib/paths.ts
|
|
1796
|
+
import { join as join13 } from "path";
|
|
1797
|
+
var resolveSkillsDir = () => join13(COMPILER_DATA_DIR, "skills");
|
|
1798
|
+
var resolveSubagentsDir = () => join13(COMPILER_DATA_DIR, "subagents");
|
|
1799
|
+
var resolvePacksDir = () => join13(COMPILER_DATA_DIR, "packs");
|
|
1800
|
+
var resolveBasePath = () => process.cwd();
|
|
1801
|
+
var resolveUserBasePath = () => {
|
|
1802
|
+
const userBasePath = process.env.AI_OPS_HOME ?? process.env.HOME;
|
|
1803
|
+
if (!userBasePath) {
|
|
1804
|
+
throw new Error("AI_OPS_HOME or HOME is required for global asset commands");
|
|
1805
|
+
}
|
|
1806
|
+
return userBasePath;
|
|
1807
|
+
};
|
|
1808
|
+
|
|
1809
|
+
// src/commands/project-layer-errors.ts
|
|
1810
|
+
import * as p from "@clack/prompts";
|
|
1811
|
+
import { ZodError } from "zod";
|
|
1812
|
+
var formatProjectLayerCommandError = (error) => {
|
|
1813
|
+
if (error instanceof ZodError) {
|
|
1814
|
+
return error.issues.map((issue2) => {
|
|
1815
|
+
const path = issue2.path.length > 0 ? issue2.path.join(".") : "manifest";
|
|
1816
|
+
return `${path}: ${issue2.message}`;
|
|
1817
|
+
}).join("; ");
|
|
1818
|
+
}
|
|
1819
|
+
return error instanceof Error ? error.message : "unknown error";
|
|
1820
|
+
};
|
|
1821
|
+
var reportInvalidProjectLayerManifest = (params) => {
|
|
1822
|
+
const reason = formatProjectLayerCommandError(params.error);
|
|
1823
|
+
p.log.error(`[invalid-manifest] .ai-ops/manifest.json \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
|
|
1824
|
+
process.exitCode = 1;
|
|
1825
|
+
p.outro(params.outro);
|
|
1826
|
+
};
|
|
1827
|
+
var reportProjectLayerApplyError = (params) => {
|
|
1828
|
+
const reason = formatProjectLayerCommandError(params.error);
|
|
1829
|
+
p.log.error(`[project-layer-apply] project operating layer \uC801\uC6A9 \uC2E4\uD328: ${reason}`);
|
|
1830
|
+
process.exitCode = 1;
|
|
1831
|
+
p.outro(params.outro);
|
|
1832
|
+
};
|
|
1833
|
+
|
|
1834
|
+
// src/commands/init.ts
|
|
1835
|
+
var TOOL_OPTIONS = [
|
|
1836
|
+
{ value: "codex", label: "Codex" },
|
|
1837
|
+
{ value: "gemini", label: "Gemini CLI" },
|
|
1838
|
+
{ value: "claude-code", label: "Claude Code" }
|
|
1839
|
+
];
|
|
1840
|
+
var promptTools = async () => {
|
|
1841
|
+
const selectedTools = await p2.multiselect({
|
|
1842
|
+
message: "AI \uB3C4\uAD6C adapter\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1843
|
+
options: TOOL_OPTIONS,
|
|
1844
|
+
initialValues: TOOL_OPTIONS.map((option) => option.value),
|
|
1845
|
+
required: true
|
|
1683
1846
|
});
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1847
|
+
return p2.isCancel(selectedTools) ? null : resolveProjectLayerTools(selectedTools);
|
|
1848
|
+
};
|
|
1849
|
+
var initCommand = async (opts = {}) => {
|
|
1850
|
+
p2.intro("ai-ops init");
|
|
1851
|
+
const basePath = resolveBasePath();
|
|
1852
|
+
const tools = opts.tool && opts.tool.length > 0 ? resolveProjectLayerTools(opts.tool) : await promptTools();
|
|
1853
|
+
if (tools === null) {
|
|
1854
|
+
p2.cancel("\uCDE8\uC18C\uB428");
|
|
1855
|
+
process.exit(0);
|
|
1856
|
+
}
|
|
1857
|
+
let previousManifest;
|
|
1858
|
+
try {
|
|
1859
|
+
previousManifest = readProjectLayerManifest(basePath);
|
|
1860
|
+
} catch (error) {
|
|
1861
|
+
reportInvalidProjectLayerManifest({ error, outro: "ai-ops init \uC2E4\uD328" });
|
|
1687
1862
|
return;
|
|
1688
1863
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1696
|
-
skill,
|
|
1697
|
-
requestedTools,
|
|
1698
|
-
scope: "project"
|
|
1864
|
+
let result;
|
|
1865
|
+
try {
|
|
1866
|
+
result = installProjectLayer({
|
|
1867
|
+
basePath,
|
|
1868
|
+
tools,
|
|
1869
|
+
previousManifest
|
|
1699
1870
|
});
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const rulesToInstall = resolvedRules.installedRules;
|
|
1720
|
-
for (const toolIdStr of manifest.tools) {
|
|
1721
|
-
const toolId = toolIdStr;
|
|
1722
|
-
const renderResult = renderForTool(toolId, rulesToInstall);
|
|
1723
|
-
const actions = buildInstallPlan({ toolId, renderResult, meta });
|
|
1724
|
-
const r = installFiles(basePath, actions, meta);
|
|
1725
|
-
allInstalledFiles.push(...r.written);
|
|
1726
|
-
allAppended.push(...r.appended);
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
if (manifest.settings?.claude) {
|
|
1730
|
-
installClaudeSettings(basePath, manifest.settings.claude);
|
|
1731
|
-
}
|
|
1732
|
-
if (manifest.settings?.gemini) {
|
|
1733
|
-
installGeminiSettings(basePath, manifest.settings.gemini);
|
|
1734
|
-
}
|
|
1735
|
-
if (manifest.settings?.prettierignore) {
|
|
1736
|
-
installPrettierIgnore(basePath);
|
|
1737
|
-
}
|
|
1738
|
-
const newManifest = buildManifest({
|
|
1739
|
-
tools: manifest.tools,
|
|
1740
|
-
scope: manifest.scope,
|
|
1741
|
-
preset: manifest.preset,
|
|
1742
|
-
workspaces: resolvedRules.workspaces,
|
|
1743
|
-
installedRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1744
|
-
installedFiles: allInstalledFiles.length > 0 ? allInstalledFiles : manifest.installed_files,
|
|
1745
|
-
installedSkills,
|
|
1746
|
-
appendedFiles: allAppended.length > 0 ? allAppended : manifest.appended_files,
|
|
1747
|
-
settings: manifest.settings ? {
|
|
1748
|
-
claude: manifest.settings.claude,
|
|
1749
|
-
gemini: manifest.settings.gemini,
|
|
1750
|
-
prettierignore: manifest.settings.prettierignore
|
|
1751
|
-
} : void 0,
|
|
1752
|
-
cliVersion,
|
|
1753
|
-
sourceHash
|
|
1754
|
-
});
|
|
1755
|
-
writeManifest(manifestPath, newManifest);
|
|
1756
|
-
s.stop("\uADDC\uCE59 \uAC31\uC2E0 \uC644\uB8CC");
|
|
1757
|
-
p4.outro("ai-ops update \uC644\uB8CC");
|
|
1871
|
+
} catch (error) {
|
|
1872
|
+
reportProjectLayerApplyError({ error, outro: "ai-ops init \uC2E4\uD328" });
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
p2.log.success(`project operating layer \uC124\uCE58 \uC644\uB8CC: ${result.manifest.managed_files.length + result.manifest.project_files.length}\uAC1C \uD30C\uC77C`);
|
|
1876
|
+
p2.log.info(`\uB3C4\uAD6C adapter: ${result.manifest.tools.join(", ")}`);
|
|
1877
|
+
if (result.appended.length > 0) {
|
|
1878
|
+
p2.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 managed section \uCD94\uAC00:
|
|
1879
|
+
${result.appended.map((file) => ` ${file}`).join("\n")}`);
|
|
1880
|
+
}
|
|
1881
|
+
if (result.refreshedProjectFiles.length > 0) {
|
|
1882
|
+
p2.log.info(`unmodified project-owned \uD30C\uC77C \uAC31\uC2E0:
|
|
1883
|
+
${result.refreshedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1884
|
+
}
|
|
1885
|
+
if (result.preservedProjectFiles.length > 0) {
|
|
1886
|
+
p2.log.info(`\uAE30\uC874 project-owned \uD30C\uC77C \uBCF4\uC874:
|
|
1887
|
+
${result.preservedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1888
|
+
}
|
|
1889
|
+
p2.outro("ai-ops init \uC644\uB8CC");
|
|
1758
1890
|
};
|
|
1759
1891
|
|
|
1760
|
-
// src/commands/
|
|
1761
|
-
import * as
|
|
1762
|
-
var
|
|
1892
|
+
// src/commands/update.ts
|
|
1893
|
+
import * as p3 from "@clack/prompts";
|
|
1894
|
+
var updateCommand = async (opts) => {
|
|
1763
1895
|
const basePath = resolveBasePath();
|
|
1764
|
-
|
|
1765
|
-
|
|
1896
|
+
p3.intro("ai-ops update");
|
|
1897
|
+
let manifest;
|
|
1898
|
+
try {
|
|
1899
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
reportInvalidProjectLayerManifest({ error, outro: "ai-ops update \uC2E4\uD328" });
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1766
1904
|
if (!manifest) {
|
|
1767
|
-
|
|
1905
|
+
p3.log.error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1768
1906
|
process.exit(1);
|
|
1769
1907
|
}
|
|
1770
|
-
const
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
manifest,
|
|
1776
|
-
allRules,
|
|
1777
|
-
presets
|
|
1778
|
-
});
|
|
1779
|
-
const resolvedSkills = resolveManifestProjectSkills({
|
|
1780
|
-
manifest,
|
|
1781
|
-
allSkills
|
|
1782
|
-
});
|
|
1783
|
-
const result = computeDiff({
|
|
1784
|
-
previous: manifest,
|
|
1785
|
-
currentRules: resolvedRules.installedRules.map((rule) => rule.id),
|
|
1786
|
-
currentSourceHash: sourceHash
|
|
1787
|
-
});
|
|
1788
|
-
const skillLines = resolvedSkills.map(({ skill, requestedTools }) => {
|
|
1789
|
-
const { installedSkill: next } = buildSkillInstallPlan({
|
|
1790
|
-
skill,
|
|
1791
|
-
requestedTools,
|
|
1792
|
-
scope: "project"
|
|
1793
|
-
});
|
|
1794
|
-
const previous = (manifest.installed_skills ?? []).find((installedSkill) => installedSkill.id === skill.id);
|
|
1795
|
-
const previousHash = previous?.sourceHash ?? "legacy";
|
|
1796
|
-
const changed = previousHash !== next.sourceHash;
|
|
1797
|
-
return `- ${skill.id}: ${changed ? "changed" : "up-to-date"} (${previousHash} -> ${next.sourceHash})`;
|
|
1798
|
-
});
|
|
1799
|
-
if (result.status === "up-to-date") {
|
|
1800
|
-
p5.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
1801
|
-
} else {
|
|
1802
|
-
if (result.sourceChanged) {
|
|
1803
|
-
p5.log.warn(`\uC18C\uC2A4 \uBCC0\uACBD \uAC10\uC9C0: ${manifest.sourceHash} \u2192 ${sourceHash}`);
|
|
1804
|
-
}
|
|
1805
|
-
if (result.added.length > 0) {
|
|
1806
|
-
p5.log.info(`\uCD94\uAC00\uB41C \uADDC\uCE59: ${result.added.join(", ")}`);
|
|
1807
|
-
}
|
|
1808
|
-
if (result.removed.length > 0) {
|
|
1809
|
-
p5.log.info(`\uC81C\uAC70\uB41C \uADDC\uCE59: ${result.removed.join(", ")}`);
|
|
1810
|
-
}
|
|
1908
|
+
const diffReport = diffProjectLayer(basePath);
|
|
1909
|
+
if (diffReport.issues.length === 0 && !opts.force) {
|
|
1910
|
+
p3.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1911
|
+
p3.outro("ai-ops update \uC644\uB8CC");
|
|
1912
|
+
return;
|
|
1811
1913
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1914
|
+
let result;
|
|
1915
|
+
try {
|
|
1916
|
+
result = updateProjectLayer({ basePath, manifest });
|
|
1917
|
+
} catch (error) {
|
|
1918
|
+
reportProjectLayerApplyError({ error, outro: "ai-ops update \uC2E4\uD328" });
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
p3.log.success(`managed \uD30C\uC77C \uAC31\uC2E0: ${result.manifest.managed_files.length}\uAC1C`);
|
|
1922
|
+
if (result.createdProjectFiles.length > 0) {
|
|
1923
|
+
p3.log.info(`\uB204\uB77D\uB41C project-owned \uD30C\uC77C \uBCF5\uAD6C:
|
|
1924
|
+
${result.createdProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1925
|
+
}
|
|
1926
|
+
if (result.refreshedProjectFiles.length > 0) {
|
|
1927
|
+
p3.log.info(`unmodified project-owned \uD30C\uC77C \uAC31\uC2E0:
|
|
1928
|
+
${result.refreshedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1815
1929
|
}
|
|
1816
|
-
|
|
1930
|
+
if (result.preservedProjectFiles.length > 0) {
|
|
1931
|
+
p3.log.info(`project-owned \uD30C\uC77C \uBCF4\uC874:
|
|
1932
|
+
${result.preservedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1933
|
+
}
|
|
1934
|
+
p3.outro("ai-ops update \uC644\uB8CC");
|
|
1817
1935
|
};
|
|
1818
1936
|
|
|
1819
|
-
// src/commands/
|
|
1820
|
-
import * as
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
if (!existsSync7(absPath)) {
|
|
1834
|
-
notFound.push(rel);
|
|
1835
|
-
continue;
|
|
1836
|
-
}
|
|
1837
|
-
const content = readFileSync8(absPath, "utf-8");
|
|
1838
|
-
if (hasAiOpsSection(content)) {
|
|
1839
|
-
const stripped = stripAiOpsSection(content);
|
|
1840
|
-
if (stripped.trim().length === 0) {
|
|
1841
|
-
rmSync4(absPath);
|
|
1842
|
-
deleted.push(rel);
|
|
1843
|
-
} else {
|
|
1844
|
-
writeFileSync7(absPath, stripped, "utf-8");
|
|
1845
|
-
cleaned.push(rel);
|
|
1846
|
-
}
|
|
1847
|
-
} else if (hasLegacyHeader(content)) {
|
|
1848
|
-
rmSync4(absPath);
|
|
1849
|
-
deleted.push(rel);
|
|
1937
|
+
// src/commands/diff.ts
|
|
1938
|
+
import * as p4 from "@clack/prompts";
|
|
1939
|
+
var diffCommand = async () => {
|
|
1940
|
+
p4.intro("ai-ops diff");
|
|
1941
|
+
const report = diffProjectLayer(resolveBasePath());
|
|
1942
|
+
if (report.issues.length === 0) {
|
|
1943
|
+
p4.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
1944
|
+
p4.outro("ai-ops diff \uC644\uB8CC");
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
for (const item of report.issues) {
|
|
1948
|
+
const line = `[${item.code}] ${item.message}`;
|
|
1949
|
+
if (item.level === "error") {
|
|
1950
|
+
p4.log.error(line);
|
|
1850
1951
|
} else {
|
|
1851
|
-
|
|
1952
|
+
p4.log.warn(line);
|
|
1852
1953
|
}
|
|
1853
1954
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
var cleanEmptyDirs = (basePath, dirs) => {
|
|
1857
|
-
const removed = [];
|
|
1858
|
-
for (const dir of dirs) {
|
|
1859
|
-
const absDir = resolve7(basePath, dir);
|
|
1860
|
-
if (!existsSync7(absDir)) continue;
|
|
1861
|
-
try {
|
|
1862
|
-
const entries = readdirSync4(absDir);
|
|
1863
|
-
if (entries.length === 0) {
|
|
1864
|
-
rmSync4(absDir, { recursive: true });
|
|
1865
|
-
removed.push(dir);
|
|
1866
|
-
}
|
|
1867
|
-
} catch {
|
|
1868
|
-
}
|
|
1955
|
+
if (report.issues.some((item) => item.level === "error")) {
|
|
1956
|
+
process.exitCode = 1;
|
|
1869
1957
|
}
|
|
1870
|
-
|
|
1958
|
+
p4.outro("ai-ops diff \uC644\uB8CC");
|
|
1871
1959
|
};
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1960
|
+
|
|
1961
|
+
// src/commands/audit.ts
|
|
1962
|
+
import * as p5 from "@clack/prompts";
|
|
1963
|
+
var auditCommand = async () => {
|
|
1964
|
+
p5.intro("ai-ops audit");
|
|
1965
|
+
const report = auditProjectLayer(resolveBasePath());
|
|
1966
|
+
if (report.issues.length === 0) {
|
|
1967
|
+
p5.log.success("audit \uD1B5\uACFC. manifest, context-layer, frontmatter, docs-status\uAC00 \uC77C\uCE58\uD569\uB2C8\uB2E4.");
|
|
1968
|
+
p5.outro("ai-ops audit \uC644\uB8CC");
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
for (const item of report.issues) {
|
|
1972
|
+
const line = `[${item.code}] ${item.message}`;
|
|
1973
|
+
if (item.level === "error") {
|
|
1974
|
+
p5.log.error(line);
|
|
1975
|
+
} else {
|
|
1976
|
+
p5.log.warn(line);
|
|
1878
1977
|
}
|
|
1879
1978
|
}
|
|
1880
|
-
|
|
1979
|
+
if (report.issues.some((item) => item.level === "error")) {
|
|
1980
|
+
process.exitCode = 1;
|
|
1981
|
+
}
|
|
1982
|
+
p5.outro("ai-ops audit \uC644\uB8CC");
|
|
1881
1983
|
};
|
|
1882
1984
|
|
|
1883
1985
|
// src/commands/uninstall.ts
|
|
1884
|
-
|
|
1885
|
-
var uninstallCommand = async () => {
|
|
1986
|
+
import * as p6 from "@clack/prompts";
|
|
1987
|
+
var uninstallCommand = async (opts = {}) => {
|
|
1886
1988
|
const basePath = resolveBasePath();
|
|
1887
|
-
const manifestPath = resolveManifestPath(basePath);
|
|
1888
1989
|
p6.intro("ai-ops uninstall");
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
const targetFiles = [
|
|
1895
|
-
...manifest.installed_files ?? inferInstalledFiles(manifest),
|
|
1896
|
-
...manifest.appended_files ?? []
|
|
1897
|
-
].filter((f) => !SETTINGS_PATHS.has(f));
|
|
1898
|
-
const targetSkillDirs = (manifest.installed_skills ?? []).flatMap((skill) => skill.installed_paths);
|
|
1899
|
-
if (targetFiles.length === 0 && targetSkillDirs.length === 0) {
|
|
1900
|
-
p6.log.warn("\uC0AD\uC81C\uD560 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1901
|
-
p6.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1990
|
+
let manifest;
|
|
1991
|
+
try {
|
|
1992
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1993
|
+
} catch (error) {
|
|
1994
|
+
reportInvalidProjectLayerManifest({ error, outro: "ai-ops uninstall \uC2E4\uD328" });
|
|
1902
1995
|
return;
|
|
1903
1996
|
}
|
|
1904
|
-
if (
|
|
1905
|
-
p6.log.
|
|
1906
|
-
|
|
1997
|
+
if (!manifest) {
|
|
1998
|
+
p6.log.error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. project operating layer\uAC00 \uC124\uCE58\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
1999
|
+
process.exitCode = 1;
|
|
2000
|
+
p6.outro("ai-ops uninstall \uC2E4\uD328");
|
|
2001
|
+
return;
|
|
1907
2002
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2003
|
+
const targetFiles = [
|
|
2004
|
+
...manifest.managed_files.map((file) => file.path),
|
|
2005
|
+
...manifest.project_files.map((file) => file.path),
|
|
2006
|
+
".ai-ops/context-layer.json",
|
|
2007
|
+
".ai-ops/manifest.json"
|
|
2008
|
+
];
|
|
2009
|
+
p6.log.info(`\uCC98\uB9AC \uB300\uC0C1 (${targetFiles.length}\uAC1C):
|
|
2010
|
+
${targetFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
2011
|
+
if (!opts.yes) {
|
|
2012
|
+
const confirmed = await p6.confirm({
|
|
2013
|
+
message: "project operating layer\uB97C \uC81C\uAC70\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
2014
|
+
initialValue: false
|
|
2015
|
+
});
|
|
2016
|
+
if (p6.isCancel(confirmed) || !confirmed) {
|
|
2017
|
+
p6.cancel("\uCDE8\uC18C\uB428");
|
|
2018
|
+
process.exit(0);
|
|
2019
|
+
}
|
|
1913
2020
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
})
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
process.exit(0);
|
|
2021
|
+
let result;
|
|
2022
|
+
try {
|
|
2023
|
+
result = uninstallProjectLayer(basePath, manifest);
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
reportProjectLayerApplyError({ error, outro: "ai-ops uninstall \uC2E4\uD328" });
|
|
2026
|
+
return;
|
|
1921
2027
|
}
|
|
1922
|
-
const settingsMessages = [];
|
|
1923
|
-
if (manifest.settings?.claude) {
|
|
1924
|
-
const status = uninstallClaudeSettings(basePath, manifest.settings.claude);
|
|
1925
|
-
if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .claude/settings.local.json");
|
|
1926
|
-
else if (status === "cleaned")
|
|
1927
|
-
settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .claude/settings.local.json");
|
|
1928
|
-
}
|
|
1929
|
-
if (manifest.settings?.gemini) {
|
|
1930
|
-
const status = uninstallGeminiSettings(basePath, manifest.settings.gemini);
|
|
1931
|
-
if (status === "deleted") settingsMessages.push("\uC0AD\uC81C: .gemini/settings.json");
|
|
1932
|
-
else if (status === "cleaned") settingsMessages.push("ai-ops \uD0A4 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uC124\uC815 \uBCF4\uC874): .gemini/settings.json");
|
|
1933
|
-
}
|
|
1934
|
-
const prettierStatus = uninstallPrettierIgnore(basePath);
|
|
1935
|
-
if (prettierStatus === "deleted") settingsMessages.push("\uC0AD\uC81C: .prettierignore");
|
|
1936
|
-
else if (prettierStatus === "cleaned") settingsMessages.push("ai-ops \uC139\uC158 \uC81C\uAC70 (\uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874): .prettierignore");
|
|
1937
|
-
const result = removeFiles(basePath, targetFiles);
|
|
1938
|
-
const removedSkillDirs = removeDirectories(basePath, targetSkillDirs);
|
|
1939
|
-
const dirs = collectManagedDirs(targetFiles);
|
|
1940
|
-
const removedDirs = cleanEmptyDirs(basePath, dirs);
|
|
1941
|
-
rmSync5(manifestPath, { force: true });
|
|
1942
2028
|
if (result.deleted.length > 0) {
|
|
1943
|
-
p6.log.success(`\uC0AD\uC81C \uC644\uB8CC
|
|
1944
|
-
${result.deleted.map((
|
|
2029
|
+
p6.log.success(`\uC0AD\uC81C \uC644\uB8CC:
|
|
2030
|
+
${result.deleted.map((file) => ` ${file}`).join("\n")}`);
|
|
1945
2031
|
}
|
|
1946
2032
|
if (result.cleaned.length > 0) {
|
|
1947
|
-
p6.log.success(
|
|
1948
|
-
|
|
1949
|
-
${result.cleaned.map((f) => ` ${f}`).join("\n")}`
|
|
1950
|
-
);
|
|
2033
|
+
p6.log.success(`managed section \uC81C\uAC70, \uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874:
|
|
2034
|
+
${result.cleaned.map((file) => ` ${file}`).join("\n")}`);
|
|
1951
2035
|
}
|
|
1952
|
-
if (result.
|
|
1953
|
-
p6.log.
|
|
1954
|
-
|
|
1955
|
-
${result.skipped.map((f) => ` ${f}`).join("\n")}`
|
|
1956
|
-
);
|
|
2036
|
+
if (result.preserved.length > 0) {
|
|
2037
|
+
p6.log.info(`\uC218\uC815\uB418\uC5C8\uAC70\uB098 \uAE30\uC874\uC5D0 \uC788\uB358 project-owned \uD30C\uC77C \uBCF4\uC874:
|
|
2038
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
1957
2039
|
}
|
|
1958
2040
|
if (result.notFound.length > 0) {
|
|
1959
|
-
p6.log.info(`\uC774\uBBF8 \uC5C6\uC74C
|
|
1960
|
-
${result.notFound.map((
|
|
1961
|
-
}
|
|
1962
|
-
if (removedDirs.length > 0) {
|
|
1963
|
-
p6.log.info(`\uBE48 \uB514\uB809\uD1A0\uB9AC \uC815\uB9AC (${removedDirs.length}\uAC1C):
|
|
1964
|
-
${removedDirs.map((d) => ` ${d}`).join("\n")}`);
|
|
1965
|
-
}
|
|
1966
|
-
if (removedSkillDirs.length > 0) {
|
|
1967
|
-
p6.log.success(
|
|
1968
|
-
`skill \uB514\uB809\uD1A0\uB9AC \uC0AD\uC81C (${removedSkillDirs.length}\uAC1C):
|
|
1969
|
-
${removedSkillDirs.map((d) => ` ${d}`).join("\n")}`
|
|
1970
|
-
);
|
|
1971
|
-
}
|
|
1972
|
-
if (settingsMessages.length > 0) {
|
|
1973
|
-
p6.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
|
|
1974
|
-
${settingsMessages.map((m) => ` ${m}`).join("\n")}`);
|
|
2041
|
+
p6.log.info(`\uC774\uBBF8 \uC5C6\uC74C:
|
|
2042
|
+
${result.notFound.map((file) => ` ${file}`).join("\n")}`);
|
|
1975
2043
|
}
|
|
1976
|
-
p6.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
|
|
1977
2044
|
p6.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1978
2045
|
};
|
|
1979
2046
|
|
|
1980
2047
|
// src/commands/skill.ts
|
|
1981
2048
|
import * as p7 from "@clack/prompts";
|
|
1982
|
-
import { rmSync as
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
}
|
|
2049
|
+
import { rmSync as rmSync4 } from "fs";
|
|
2050
|
+
|
|
2051
|
+
// src/lib/skill-state.ts
|
|
2052
|
+
var resolveRequestedTools = (params) => {
|
|
2053
|
+
if (params.requested === void 0 || params.requested.length === 0) {
|
|
2054
|
+
return [...params.supported];
|
|
2055
|
+
}
|
|
2056
|
+
const supportedSet = new Set(params.supported);
|
|
2057
|
+
const invalid = params.requested.filter((tool) => !supportedSet.has(tool));
|
|
2058
|
+
if (invalid.length > 0) {
|
|
2059
|
+
throw new Error(`Unsupported tools requested: ${invalid.join(", ")}`);
|
|
2060
|
+
}
|
|
2061
|
+
return [...params.requested];
|
|
2062
|
+
};
|
|
2063
|
+
var TOOL_ORDER2 = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
2064
|
+
var mergeSkillTools = (params) => {
|
|
2065
|
+
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
2066
|
+
return TOOL_ORDER2.filter((tool) => merged.has(tool));
|
|
2067
|
+
};
|
|
2068
|
+
var upsertInstalledSkill = (installedSkills, nextSkill) => {
|
|
2069
|
+
const nextSkillId = resolveCanonicalSkillId(nextSkill.id);
|
|
2070
|
+
const remaining = installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== nextSkillId);
|
|
2071
|
+
return [...remaining, nextSkill];
|
|
2072
|
+
};
|
|
2073
|
+
var removeInstalledSkill = (installedSkills, skillId) => {
|
|
2074
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
2075
|
+
return installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== targetSkillId);
|
|
2076
|
+
};
|
|
2077
|
+
var findInstalledSkill = (installedSkills, skillId) => {
|
|
2078
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
2079
|
+
return installedSkills.find((skill) => resolveCanonicalSkillId(skill.id) === targetSkillId);
|
|
2080
|
+
};
|
|
2081
|
+
|
|
2082
|
+
// src/lib/skill-install.ts
|
|
2083
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2084
|
+
import { dirname as dirname8, resolve as resolve7 } from "path";
|
|
2085
|
+
var installSkillPackages = (basePath, packages) => {
|
|
2086
|
+
const writtenRoots = [];
|
|
2087
|
+
for (const skillPackage of packages) {
|
|
2088
|
+
const absRoot = resolve7(basePath, skillPackage.rootDir);
|
|
2089
|
+
if (existsSync4(absRoot)) {
|
|
2090
|
+
rmSync3(absRoot, { recursive: true, force: true });
|
|
2091
|
+
}
|
|
2092
|
+
for (const file of skillPackage.files) {
|
|
2093
|
+
const absPath = resolve7(basePath, file.relativePath);
|
|
2094
|
+
mkdirSync6(dirname8(absPath), { recursive: true });
|
|
2095
|
+
writeFileSync6(absPath, file.content + "\n", "utf-8");
|
|
2096
|
+
}
|
|
2097
|
+
writtenRoots.push(skillPackage.rootDir);
|
|
2098
|
+
}
|
|
2099
|
+
return writtenRoots;
|
|
2100
|
+
};
|
|
2101
|
+
var removeDirectories = (basePath, relativeDirs) => {
|
|
2102
|
+
const removed = [];
|
|
2103
|
+
for (const relativeDir of relativeDirs) {
|
|
2104
|
+
const absPath = resolve7(basePath, relativeDir);
|
|
2105
|
+
if (!existsSync4(absPath)) continue;
|
|
2106
|
+
rmSync3(absPath, { recursive: true, force: true });
|
|
2107
|
+
removed.push(relativeDir);
|
|
2108
|
+
}
|
|
2109
|
+
return removed;
|
|
1989
2110
|
};
|
|
2111
|
+
|
|
2112
|
+
// src/commands/skill.ts
|
|
1990
2113
|
var loadCompilerInputs = () => {
|
|
1991
|
-
const compilerDataDir = resolveCompilerDataDir();
|
|
1992
2114
|
return {
|
|
1993
2115
|
allSkills: loadAllSkills(resolveSkillsDir()),
|
|
1994
|
-
sourceHash: computeSourceHash(compilerDataDir),
|
|
1995
2116
|
cliVersion: getCliVersion()
|
|
1996
2117
|
};
|
|
1997
2118
|
};
|
|
@@ -2003,42 +2124,12 @@ var resolveSkillById = (skills, skillId) => {
|
|
|
2003
2124
|
}
|
|
2004
2125
|
return skill;
|
|
2005
2126
|
};
|
|
2006
|
-
var assertScopeAllowed = (skill, scope) => {
|
|
2007
|
-
if (!skill.install_scopes.includes(scope)) {
|
|
2008
|
-
throw new Error(`Skill ${skill.id} does not support ${scope} scope`);
|
|
2009
|
-
}
|
|
2010
|
-
};
|
|
2011
|
-
var writeProjectSkillState = (params) => {
|
|
2012
|
-
const manifestPath = resolveManifestPath(params.basePath);
|
|
2013
|
-
const previous = readManifest(manifestPath);
|
|
2014
|
-
const installedSkills = params.removeSkillId ? removeInstalledSkill(previous?.installed_skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.installed_skills ?? [], params.nextSkill) : previous?.installed_skills ?? [];
|
|
2015
|
-
const nextTools = params.nextSkill !== void 0 ? [.../* @__PURE__ */ new Set([...previous?.tools ?? [], ...params.nextSkill.tools])] : previous?.tools ?? [];
|
|
2016
|
-
const hasProjectState = (previous?.installed_rules.length ?? 0) > 0 || (previous?.installed_files?.length ?? 0) > 0 || (previous?.appended_files?.length ?? 0) > 0 || installedSkills.length > 0 || previous?.settings !== void 0;
|
|
2017
|
-
if (!hasProjectState) {
|
|
2018
|
-
rmSync6(manifestPath, { force: true });
|
|
2019
|
-
return;
|
|
2020
|
-
}
|
|
2021
|
-
const manifest = buildManifest({
|
|
2022
|
-
tools: nextTools.length > 0 ? nextTools : params.nextSkill?.tools ?? ["codex"],
|
|
2023
|
-
scope: "project",
|
|
2024
|
-
preset: previous?.preset,
|
|
2025
|
-
workspaces: previous?.workspaces,
|
|
2026
|
-
installedRules: previous?.installed_rules ?? [],
|
|
2027
|
-
installedFiles: previous?.installed_files,
|
|
2028
|
-
installedSkills,
|
|
2029
|
-
appendedFiles: previous?.appended_files,
|
|
2030
|
-
settings: previous?.settings,
|
|
2031
|
-
cliVersion: params.cliVersion,
|
|
2032
|
-
sourceHash: params.sourceHash
|
|
2033
|
-
});
|
|
2034
|
-
writeManifest(manifestPath, manifest);
|
|
2035
|
-
};
|
|
2036
2127
|
var writeUserSkillState = (params) => {
|
|
2037
2128
|
const registryPath = resolveSkillRegistryPath(params.basePath);
|
|
2038
2129
|
const previous = readSkillRegistry(registryPath);
|
|
2039
2130
|
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
2040
2131
|
if (skills.length === 0) {
|
|
2041
|
-
|
|
2132
|
+
rmSync4(registryPath, { force: true });
|
|
2042
2133
|
return;
|
|
2043
2134
|
}
|
|
2044
2135
|
writeSkillRegistry(registryPath, {
|
|
@@ -2047,20 +2138,14 @@ var writeUserSkillState = (params) => {
|
|
|
2047
2138
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2048
2139
|
});
|
|
2049
2140
|
};
|
|
2050
|
-
var readInstalledSkills = (
|
|
2051
|
-
if (scope === "project") {
|
|
2052
|
-
return (readManifest(resolveManifestPath(basePath))?.installed_skills ?? []).map((installedSkill) => ({
|
|
2053
|
-
...installedSkill,
|
|
2054
|
-
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2055
|
-
}));
|
|
2056
|
-
}
|
|
2141
|
+
var readInstalledSkills = (basePath) => {
|
|
2057
2142
|
return (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
|
|
2058
2143
|
...installedSkill,
|
|
2059
2144
|
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2060
2145
|
}));
|
|
2061
2146
|
};
|
|
2062
2147
|
var installSkill = (params) => {
|
|
2063
|
-
const installedSkills = readInstalledSkills(params.
|
|
2148
|
+
const installedSkills = readInstalledSkills(params.basePath);
|
|
2064
2149
|
const existingInstalledSkill = findInstalledSkill(installedSkills, params.skill.id);
|
|
2065
2150
|
const nextRequestedTools = mergeSkillTools({
|
|
2066
2151
|
existing: existingInstalledSkill?.tools,
|
|
@@ -2068,31 +2153,21 @@ var installSkill = (params) => {
|
|
|
2068
2153
|
});
|
|
2069
2154
|
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
2070
2155
|
skill: params.skill,
|
|
2071
|
-
requestedTools: nextRequestedTools
|
|
2072
|
-
scope: params.scope
|
|
2156
|
+
requestedTools: nextRequestedTools
|
|
2073
2157
|
});
|
|
2074
2158
|
installSkillPackages(params.basePath, packages);
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
nextSkill: installedSkill
|
|
2081
|
-
});
|
|
2082
|
-
} else {
|
|
2083
|
-
writeUserSkillState({
|
|
2084
|
-
basePath: params.basePath,
|
|
2085
|
-
cliVersion: params.cliVersion,
|
|
2086
|
-
nextSkill: installedSkill
|
|
2087
|
-
});
|
|
2088
|
-
}
|
|
2159
|
+
writeUserSkillState({
|
|
2160
|
+
basePath: params.basePath,
|
|
2161
|
+
cliVersion: params.cliVersion,
|
|
2162
|
+
nextSkill: installedSkill
|
|
2163
|
+
});
|
|
2089
2164
|
return installedSkill;
|
|
2090
2165
|
};
|
|
2091
|
-
var skillListCommand = async (
|
|
2092
|
-
const
|
|
2166
|
+
var skillListCommand = async () => {
|
|
2167
|
+
const basePath = resolveUserBasePath();
|
|
2093
2168
|
const { allSkills } = loadCompilerInputs();
|
|
2094
|
-
const installedSkills = readInstalledSkills(
|
|
2095
|
-
p7.intro(
|
|
2169
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2170
|
+
p7.intro("ai-ops skill list");
|
|
2096
2171
|
const sections = [
|
|
2097
2172
|
{ kind: "reference", title: "reference skills" },
|
|
2098
2173
|
{ kind: "task", title: "task skills" }
|
|
@@ -2100,7 +2175,7 @@ var skillListCommand = async (opts) => {
|
|
|
2100
2175
|
const lines = allSkills.filter((skill) => skill.kind === kind).map((skill) => {
|
|
2101
2176
|
const installed = findInstalledSkill(installedSkills, skill.id);
|
|
2102
2177
|
const suffix = installed ? `installed for ${installed.tools.join(", ")}` : "not installed";
|
|
2103
|
-
return `- ${skill.id}
|
|
2178
|
+
return `- ${skill.id} - ${suffix}`;
|
|
2104
2179
|
});
|
|
2105
2180
|
if (lines.length === 0) {
|
|
2106
2181
|
return null;
|
|
@@ -2112,29 +2187,26 @@ ${lines.join("\n")}`;
|
|
|
2112
2187
|
p7.outro("ai-ops skill list \uC644\uB8CC");
|
|
2113
2188
|
};
|
|
2114
2189
|
var skillInstallCommand = async (skillId, opts) => {
|
|
2115
|
-
const
|
|
2116
|
-
const { allSkills,
|
|
2190
|
+
const basePath = resolveUserBasePath();
|
|
2191
|
+
const { allSkills, cliVersion } = loadCompilerInputs();
|
|
2117
2192
|
const skill = resolveSkillById(allSkills, skillId);
|
|
2118
|
-
assertScopeAllowed(skill, scope);
|
|
2119
2193
|
const requestedTools = resolveRequestedTools({ requested: opts.tool, supported: skill.supported_tools });
|
|
2120
2194
|
p7.intro(`ai-ops skill install ${skillId}`);
|
|
2121
2195
|
const installedSkill = installSkill({
|
|
2122
2196
|
skill,
|
|
2123
2197
|
requestedTools,
|
|
2124
|
-
scope,
|
|
2125
2198
|
basePath,
|
|
2126
|
-
cliVersion
|
|
2127
|
-
sourceHash
|
|
2199
|
+
cliVersion
|
|
2128
2200
|
});
|
|
2129
2201
|
p7.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSkill.id} (${installedSkill.installed_paths.join(", ")})`);
|
|
2130
2202
|
p7.outro("ai-ops skill install \uC644\uB8CC");
|
|
2131
2203
|
};
|
|
2132
|
-
var skillDiffCommand = async (skillId
|
|
2133
|
-
const
|
|
2204
|
+
var skillDiffCommand = async (skillId) => {
|
|
2205
|
+
const basePath = resolveUserBasePath();
|
|
2134
2206
|
const { allSkills } = loadCompilerInputs();
|
|
2135
|
-
const installedSkills = readInstalledSkills(
|
|
2207
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2136
2208
|
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2137
|
-
p7.intro(
|
|
2209
|
+
p7.intro("ai-ops skill diff");
|
|
2138
2210
|
if (targets.length === 0) {
|
|
2139
2211
|
p7.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2140
2212
|
p7.outro("ai-ops skill diff \uC644\uB8CC");
|
|
@@ -2144,8 +2216,7 @@ var skillDiffCommand = async (skillId, opts) => {
|
|
|
2144
2216
|
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2145
2217
|
const { installedSkill: next } = buildSkillInstallPlan({
|
|
2146
2218
|
skill,
|
|
2147
|
-
requestedTools: installedSkill.tools
|
|
2148
|
-
scope
|
|
2219
|
+
requestedTools: installedSkill.tools
|
|
2149
2220
|
});
|
|
2150
2221
|
const changed = next.sourceHash !== installedSkill.sourceHash;
|
|
2151
2222
|
return `- ${installedSkill.id}: ${changed ? "changed" : "up-to-date"} (${installedSkill.sourceHash} -> ${next.sourceHash})`;
|
|
@@ -2153,12 +2224,12 @@ var skillDiffCommand = async (skillId, opts) => {
|
|
|
2153
2224
|
p7.log.info(lines.join("\n"));
|
|
2154
2225
|
p7.outro("ai-ops skill diff \uC644\uB8CC");
|
|
2155
2226
|
};
|
|
2156
|
-
var skillUpdateCommand = async (skillId
|
|
2157
|
-
const
|
|
2158
|
-
const { allSkills,
|
|
2159
|
-
const installedSkills = readInstalledSkills(
|
|
2227
|
+
var skillUpdateCommand = async (skillId) => {
|
|
2228
|
+
const basePath = resolveUserBasePath();
|
|
2229
|
+
const { allSkills, cliVersion } = loadCompilerInputs();
|
|
2230
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2160
2231
|
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2161
|
-
p7.intro(
|
|
2232
|
+
p7.intro("ai-ops skill update");
|
|
2162
2233
|
if (targets.length === 0) {
|
|
2163
2234
|
p7.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2164
2235
|
p7.outro("ai-ops skill update \uC644\uB8CC");
|
|
@@ -2168,57 +2239,29 @@ var skillUpdateCommand = async (skillId, opts) => {
|
|
|
2168
2239
|
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2169
2240
|
const { packages, installedSkill: next } = buildSkillInstallPlan({
|
|
2170
2241
|
skill,
|
|
2171
|
-
requestedTools: installedSkill.tools
|
|
2172
|
-
scope
|
|
2242
|
+
requestedTools: installedSkill.tools
|
|
2173
2243
|
});
|
|
2174
2244
|
installSkillPackages(basePath, packages);
|
|
2175
2245
|
return next;
|
|
2176
2246
|
});
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
)
|
|
2187
|
-
|
|
2188
|
-
manifestPath,
|
|
2189
|
-
buildManifest({
|
|
2190
|
-
tools: previous.tools,
|
|
2191
|
-
scope: previous.scope,
|
|
2192
|
-
preset: previous.preset,
|
|
2193
|
-
workspaces: previous.workspaces,
|
|
2194
|
-
installedRules: previous.installed_rules,
|
|
2195
|
-
installedFiles: previous.installed_files,
|
|
2196
|
-
installedSkills: [...untouched, ...nextInstalledSkills],
|
|
2197
|
-
appendedFiles: previous.appended_files,
|
|
2198
|
-
settings: previous.settings,
|
|
2199
|
-
cliVersion,
|
|
2200
|
-
sourceHash
|
|
2201
|
-
})
|
|
2202
|
-
);
|
|
2203
|
-
} else {
|
|
2204
|
-
const registryPath = resolveSkillRegistryPath(basePath);
|
|
2205
|
-
const previous = readSkillRegistry(registryPath);
|
|
2206
|
-
const untouched = (previous?.skills ?? []).filter(
|
|
2207
|
-
(installedSkill) => !nextInstalledSkills.some((next) => next.id === installedSkill.id)
|
|
2208
|
-
);
|
|
2209
|
-
writeSkillRegistry(registryPath, {
|
|
2210
|
-
skills: [...untouched, ...nextInstalledSkills],
|
|
2211
|
-
cliVersion,
|
|
2212
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2213
|
-
});
|
|
2214
|
-
}
|
|
2247
|
+
const registryPath = resolveSkillRegistryPath(basePath);
|
|
2248
|
+
const previous = readSkillRegistry(registryPath);
|
|
2249
|
+
const nextSkillIds = new Set(nextInstalledSkills.map((skill) => skill.id));
|
|
2250
|
+
const untouched = (previous?.skills ?? []).filter(
|
|
2251
|
+
(installedSkill) => !nextSkillIds.has(resolveCanonicalSkillId(installedSkill.id))
|
|
2252
|
+
);
|
|
2253
|
+
writeSkillRegistry(registryPath, {
|
|
2254
|
+
skills: [...untouched, ...nextInstalledSkills],
|
|
2255
|
+
cliVersion,
|
|
2256
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2257
|
+
});
|
|
2215
2258
|
p7.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSkills.map((skill) => skill.id).join(", ")}`);
|
|
2216
2259
|
p7.outro("ai-ops skill update \uC644\uB8CC");
|
|
2217
2260
|
};
|
|
2218
|
-
var skillUninstallCommand = async (skillId
|
|
2219
|
-
const
|
|
2220
|
-
const {
|
|
2221
|
-
const installedSkills = readInstalledSkills(
|
|
2261
|
+
var skillUninstallCommand = async (skillId) => {
|
|
2262
|
+
const basePath = resolveUserBasePath();
|
|
2263
|
+
const { cliVersion } = loadCompilerInputs();
|
|
2264
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2222
2265
|
const installedSkill = findInstalledSkill(installedSkills, skillId);
|
|
2223
2266
|
p7.intro(`ai-ops skill uninstall ${skillId}`);
|
|
2224
2267
|
if (!installedSkill) {
|
|
@@ -2227,70 +2270,416 @@ var skillUninstallCommand = async (skillId, opts) => {
|
|
|
2227
2270
|
return;
|
|
2228
2271
|
}
|
|
2229
2272
|
const removed = removeDirectories(basePath, installedSkill.installed_paths);
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
removeSkillId: skillId
|
|
2236
|
-
});
|
|
2237
|
-
} else {
|
|
2238
|
-
writeUserSkillState({
|
|
2239
|
-
basePath,
|
|
2240
|
-
cliVersion,
|
|
2241
|
-
removeSkillId: skillId
|
|
2242
|
-
});
|
|
2243
|
-
}
|
|
2273
|
+
writeUserSkillState({
|
|
2274
|
+
basePath,
|
|
2275
|
+
cliVersion,
|
|
2276
|
+
removeSkillId: skillId
|
|
2277
|
+
});
|
|
2244
2278
|
p7.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
|
|
2245
2279
|
p7.outro("ai-ops skill uninstall \uC644\uB8CC");
|
|
2246
2280
|
};
|
|
2247
2281
|
|
|
2248
|
-
// src/commands/
|
|
2249
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
2250
|
-
import { join as join13, dirname as dirname8 } from "path";
|
|
2282
|
+
// src/commands/subagent.ts
|
|
2251
2283
|
import * as p8 from "@clack/prompts";
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2284
|
+
import { existsSync as existsSync6, rmSync as rmSync6 } from "fs";
|
|
2285
|
+
|
|
2286
|
+
// src/lib/subagent-install.ts
|
|
2287
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync7, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
2288
|
+
import { dirname as dirname9, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve8 } from "path";
|
|
2289
|
+
var resolveInsideBasePath = (basePath, relativePath) => {
|
|
2290
|
+
const absBasePath = resolve8(basePath);
|
|
2291
|
+
const absPath = resolve8(absBasePath, relativePath);
|
|
2292
|
+
const fromBase = relative3(absBasePath, absPath);
|
|
2293
|
+
if (fromBase.length === 0 || fromBase.startsWith("..") || isAbsolute3(fromBase)) {
|
|
2294
|
+
throw new Error(`Subagent path escapes AI_OPS_HOME: ${relativePath}`);
|
|
2295
|
+
}
|
|
2296
|
+
return absPath;
|
|
2297
|
+
};
|
|
2298
|
+
var installSubagentPackages = (basePath, packages) => {
|
|
2299
|
+
const written = [];
|
|
2300
|
+
for (const subagentPackage of packages) {
|
|
2301
|
+
for (const file of subagentPackage.files) {
|
|
2302
|
+
const absPath = resolveInsideBasePath(basePath, file.relativePath);
|
|
2303
|
+
if (existsSync5(absPath)) {
|
|
2304
|
+
rmSync5(absPath, { recursive: true, force: true });
|
|
2305
|
+
}
|
|
2306
|
+
mkdirSync7(dirname9(absPath), { recursive: true });
|
|
2307
|
+
writeFileSync7(absPath, file.content.trimEnd() + "\n", "utf-8");
|
|
2308
|
+
written.push(file.relativePath);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
return written;
|
|
2312
|
+
};
|
|
2313
|
+
var removeSubagentFiles = (basePath, relativePaths) => {
|
|
2314
|
+
const removed = [];
|
|
2315
|
+
for (const relativePath of relativePaths) {
|
|
2316
|
+
const absPath = resolveInsideBasePath(basePath, relativePath);
|
|
2317
|
+
if (!existsSync5(absPath)) continue;
|
|
2318
|
+
rmSync5(absPath, { recursive: true, force: true });
|
|
2319
|
+
removed.push(relativePath);
|
|
2320
|
+
}
|
|
2321
|
+
return removed;
|
|
2322
|
+
};
|
|
2323
|
+
|
|
2324
|
+
// src/lib/subagent-state.ts
|
|
2325
|
+
var resolveRequestedSubagentTools = (params) => {
|
|
2326
|
+
if (params.requested === void 0 || params.requested.length === 0) {
|
|
2327
|
+
return [...params.supported];
|
|
2328
|
+
}
|
|
2329
|
+
const supportedSet = new Set(params.supported);
|
|
2330
|
+
const invalid = params.requested.filter((tool) => !supportedSet.has(tool));
|
|
2331
|
+
if (invalid.length > 0) {
|
|
2332
|
+
throw new Error(`Unsupported tools requested: ${invalid.join(", ")}`);
|
|
2333
|
+
}
|
|
2334
|
+
return [...params.requested];
|
|
2335
|
+
};
|
|
2336
|
+
var TOOL_ORDER3 = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
2337
|
+
var mergeSubagentTools = (params) => {
|
|
2338
|
+
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
2339
|
+
return TOOL_ORDER3.filter((tool) => merged.has(tool));
|
|
2340
|
+
};
|
|
2341
|
+
var upsertInstalledSubagent = (installedSubagents, nextSubagent) => {
|
|
2342
|
+
const remaining = installedSubagents.filter((subagent) => subagent.id !== nextSubagent.id);
|
|
2343
|
+
return [...remaining, nextSubagent];
|
|
2344
|
+
};
|
|
2345
|
+
var removeInstalledSubagent = (installedSubagents, subagentId) => installedSubagents.filter((subagent) => subagent.id !== subagentId);
|
|
2346
|
+
var findInstalledSubagent = (installedSubagents, subagentId) => installedSubagents.find((subagent) => subagent.id === subagentId);
|
|
2347
|
+
var resolveInstalledSubagentPaths = (installedSubagent) => installedSubagent.tools.map((tool) => buildSubagentRelativePath(installedSubagent.id, tool));
|
|
2348
|
+
|
|
2349
|
+
// src/commands/subagent.ts
|
|
2350
|
+
var loadCompilerInputs2 = () => {
|
|
2351
|
+
return {
|
|
2352
|
+
allSubagents: loadAllSubagents(resolveSubagentsDir()),
|
|
2353
|
+
cliVersion: getCliVersion()
|
|
2354
|
+
};
|
|
2355
|
+
};
|
|
2356
|
+
var resolveSubagentById = (subagents, subagentId) => {
|
|
2357
|
+
const subagent = subagents.find((candidate) => candidate.id === subagentId);
|
|
2358
|
+
if (!subagent) {
|
|
2359
|
+
throw new Error(`Unknown subagent: ${subagentId}`);
|
|
2360
|
+
}
|
|
2361
|
+
return subagent;
|
|
2362
|
+
};
|
|
2363
|
+
var writeUserSubagentState = (params) => {
|
|
2364
|
+
const manifestPath = resolveSubagentManifestPath(params.basePath);
|
|
2365
|
+
const previous = readSubagentManifest(manifestPath);
|
|
2366
|
+
const subagents = params.removeSubagentId ? removeInstalledSubagent(previous?.subagents ?? [], params.removeSubagentId) : params.nextSubagent ? upsertInstalledSubagent(previous?.subagents ?? [], params.nextSubagent) : previous?.subagents ?? [];
|
|
2367
|
+
if (subagents.length === 0) {
|
|
2368
|
+
rmSync6(manifestPath, { force: true });
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
writeSubagentManifest(manifestPath, {
|
|
2372
|
+
subagents,
|
|
2373
|
+
cliVersion: params.cliVersion,
|
|
2374
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2375
|
+
});
|
|
2376
|
+
};
|
|
2377
|
+
var readInstalledSubagents = (basePath) => readSubagentManifest(resolveSubagentManifestPath(basePath))?.subagents ?? [];
|
|
2378
|
+
var warnMissingSkills = (requiredSkills) => {
|
|
2379
|
+
const missing = requiredSkills.filter((skill) => !existsSync6(skill.path));
|
|
2380
|
+
if (missing.length === 0) {
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
p8.log.warn(
|
|
2384
|
+
[
|
|
2385
|
+
"\uD544\uC694\uD55C skill\uC774 \uC544\uC9C1 \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. subagent \uC124\uCE58\uB294 \uACC4\uC18D \uC9C4\uD589\uD569\uB2C8\uB2E4.",
|
|
2386
|
+
...missing.map((skill) => `- ${skill.tool}:${skill.skillName} (${skill.path})`)
|
|
2387
|
+
].join("\n")
|
|
2388
|
+
);
|
|
2389
|
+
};
|
|
2390
|
+
var installSubagent = (params) => {
|
|
2391
|
+
const installedSubagents = readInstalledSubagents(params.basePath);
|
|
2392
|
+
const existingInstalledSubagent = findInstalledSubagent(installedSubagents, params.subagent.id);
|
|
2393
|
+
const nextRequestedTools = mergeSubagentTools({
|
|
2394
|
+
existing: existingInstalledSubagent?.tools,
|
|
2395
|
+
requested: params.requestedTools
|
|
2396
|
+
});
|
|
2397
|
+
const { packages, installedSubagent, requiredSkills } = buildSubagentInstallPlan({
|
|
2398
|
+
subagent: params.subagent,
|
|
2399
|
+
requestedTools: nextRequestedTools,
|
|
2400
|
+
userBasePath: params.basePath
|
|
2401
|
+
});
|
|
2402
|
+
installSubagentPackages(params.basePath, packages);
|
|
2403
|
+
warnMissingSkills(requiredSkills);
|
|
2404
|
+
writeUserSubagentState({
|
|
2405
|
+
basePath: params.basePath,
|
|
2406
|
+
cliVersion: params.cliVersion,
|
|
2407
|
+
nextSubagent: installedSubagent
|
|
2408
|
+
});
|
|
2409
|
+
return installedSubagent;
|
|
2410
|
+
};
|
|
2411
|
+
var subagentListCommand = async () => {
|
|
2412
|
+
const basePath = resolveUserBasePath();
|
|
2413
|
+
const { allSubagents } = loadCompilerInputs2();
|
|
2414
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2415
|
+
p8.intro("ai-ops subagent list");
|
|
2416
|
+
const lines = allSubagents.map((subagent) => {
|
|
2417
|
+
const installed = findInstalledSubagent(installedSubagents, subagent.id);
|
|
2418
|
+
const suffix = installed ? `installed for ${installed.tools.join(", ")}` : "not installed";
|
|
2419
|
+
return `- ${subagent.id} - ${suffix}`;
|
|
2420
|
+
});
|
|
2421
|
+
p8.log.info(lines.join("\n"));
|
|
2422
|
+
p8.outro("ai-ops subagent list \uC644\uB8CC");
|
|
2423
|
+
};
|
|
2424
|
+
var subagentInstallCommand = async (subagentId, opts) => {
|
|
2425
|
+
const basePath = resolveUserBasePath();
|
|
2426
|
+
const { allSubagents, cliVersion } = loadCompilerInputs2();
|
|
2427
|
+
const subagent = resolveSubagentById(allSubagents, subagentId);
|
|
2428
|
+
const requestedTools = resolveRequestedSubagentTools({ requested: opts.tool, supported: subagent.supported_tools });
|
|
2429
|
+
p8.intro(`ai-ops subagent install ${subagentId}`);
|
|
2430
|
+
const installedSubagent = installSubagent({
|
|
2431
|
+
subagent,
|
|
2432
|
+
requestedTools,
|
|
2433
|
+
basePath,
|
|
2434
|
+
cliVersion
|
|
2435
|
+
});
|
|
2436
|
+
p8.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSubagent.id} (${installedSubagent.installed_paths.join(", ")})`);
|
|
2437
|
+
p8.outro("ai-ops subagent install \uC644\uB8CC");
|
|
2438
|
+
};
|
|
2439
|
+
var subagentDiffCommand = async (subagentId) => {
|
|
2440
|
+
const basePath = resolveUserBasePath();
|
|
2441
|
+
const { allSubagents } = loadCompilerInputs2();
|
|
2442
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2443
|
+
const targets = subagentId ? installedSubagents.filter((subagent) => subagent.id === subagentId) : installedSubagents;
|
|
2444
|
+
p8.intro("ai-ops subagent diff");
|
|
2445
|
+
if (targets.length === 0) {
|
|
2446
|
+
p8.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C subagent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2447
|
+
p8.outro("ai-ops subagent diff \uC644\uB8CC");
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
const lines = targets.map((installedSubagent) => {
|
|
2451
|
+
const subagent = resolveSubagentById(allSubagents, installedSubagent.id);
|
|
2452
|
+
const { installedSubagent: next } = buildSubagentInstallPlan({
|
|
2453
|
+
subagent,
|
|
2454
|
+
requestedTools: installedSubagent.tools,
|
|
2455
|
+
userBasePath: basePath
|
|
2456
|
+
});
|
|
2457
|
+
const changed = next.sourceHash !== installedSubagent.sourceHash;
|
|
2458
|
+
return `- ${installedSubagent.id}: ${changed ? "changed" : "up-to-date"} (${installedSubagent.sourceHash} -> ${next.sourceHash})`;
|
|
2459
|
+
});
|
|
2460
|
+
p8.log.info(lines.join("\n"));
|
|
2461
|
+
p8.outro("ai-ops subagent diff \uC644\uB8CC");
|
|
2462
|
+
};
|
|
2463
|
+
var subagentUpdateCommand = async (subagentId) => {
|
|
2464
|
+
const basePath = resolveUserBasePath();
|
|
2465
|
+
const { allSubagents, cliVersion } = loadCompilerInputs2();
|
|
2466
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2467
|
+
const targets = subagentId ? installedSubagents.filter((subagent) => subagent.id === subagentId) : installedSubagents;
|
|
2468
|
+
p8.intro("ai-ops subagent update");
|
|
2469
|
+
if (targets.length === 0) {
|
|
2470
|
+
p8.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C subagent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2471
|
+
p8.outro("ai-ops subagent update \uC644\uB8CC");
|
|
2472
|
+
return;
|
|
2473
|
+
}
|
|
2474
|
+
const nextInstalledSubagents = targets.map((installedSubagent) => {
|
|
2475
|
+
const subagent = resolveSubagentById(allSubagents, installedSubagent.id);
|
|
2476
|
+
const { packages, installedSubagent: next, requiredSkills } = buildSubagentInstallPlan({
|
|
2477
|
+
subagent,
|
|
2478
|
+
requestedTools: installedSubagent.tools,
|
|
2479
|
+
userBasePath: basePath
|
|
2480
|
+
});
|
|
2481
|
+
installSubagentPackages(basePath, packages);
|
|
2482
|
+
warnMissingSkills(requiredSkills);
|
|
2483
|
+
return next;
|
|
2484
|
+
});
|
|
2485
|
+
const manifestPath = resolveSubagentManifestPath(basePath);
|
|
2486
|
+
const previous = readSubagentManifest(manifestPath);
|
|
2487
|
+
const nextSubagentIds = new Set(nextInstalledSubagents.map((subagent) => subagent.id));
|
|
2488
|
+
const untouched = (previous?.subagents ?? []).filter((subagent) => !nextSubagentIds.has(subagent.id));
|
|
2489
|
+
writeSubagentManifest(manifestPath, {
|
|
2490
|
+
subagents: [...untouched, ...nextInstalledSubagents],
|
|
2491
|
+
cliVersion,
|
|
2492
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2493
|
+
});
|
|
2494
|
+
p8.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSubagents.map((subagent) => subagent.id).join(", ")}`);
|
|
2495
|
+
p8.outro("ai-ops subagent update \uC644\uB8CC");
|
|
2496
|
+
};
|
|
2497
|
+
var subagentUninstallCommand = async (subagentId) => {
|
|
2498
|
+
const basePath = resolveUserBasePath();
|
|
2499
|
+
const cliVersion = getCliVersion();
|
|
2500
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2501
|
+
const installedSubagent = findInstalledSubagent(installedSubagents, subagentId);
|
|
2502
|
+
p8.intro(`ai-ops subagent uninstall ${subagentId}`);
|
|
2503
|
+
if (!installedSubagent) {
|
|
2504
|
+
p8.log.warn("\uC124\uCE58\uB41C subagent\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
2505
|
+
p8.outro("ai-ops subagent uninstall \uC644\uB8CC");
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
const removed = removeSubagentFiles(basePath, resolveInstalledSubagentPaths(installedSubagent));
|
|
2509
|
+
writeUserSubagentState({
|
|
2510
|
+
basePath,
|
|
2511
|
+
cliVersion,
|
|
2512
|
+
removeSubagentId: subagentId
|
|
2513
|
+
});
|
|
2514
|
+
p8.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
|
|
2515
|
+
p8.outro("ai-ops subagent uninstall \uC644\uB8CC");
|
|
2516
|
+
};
|
|
2517
|
+
|
|
2518
|
+
// src/commands/pack.ts
|
|
2519
|
+
import * as p9 from "@clack/prompts";
|
|
2520
|
+
var readManifestForPackCommand = (basePath) => {
|
|
2521
|
+
try {
|
|
2522
|
+
return readProjectLayerManifest(basePath);
|
|
2523
|
+
} catch (error) {
|
|
2524
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
2525
|
+
p9.log.error(`.ai-ops/manifest.json \uD30C\uC2F1 \uC2E4\uD328: ${message}`);
|
|
2526
|
+
process.exitCode = 1;
|
|
2527
|
+
return null;
|
|
2528
|
+
}
|
|
2529
|
+
};
|
|
2530
|
+
var reportPackError = (error) => {
|
|
2531
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
2532
|
+
p9.log.error(message);
|
|
2533
|
+
process.exitCode = 1;
|
|
2534
|
+
};
|
|
2535
|
+
var packListCommand = async () => {
|
|
2536
|
+
const basePath = resolveBasePath();
|
|
2537
|
+
const packs = loadAllPacks(resolvePacksDir());
|
|
2538
|
+
const manifest = readManifestForPackCommand(basePath);
|
|
2539
|
+
const installedPackIds = new Set(manifest?.packs.map((pack) => pack.id) ?? []);
|
|
2540
|
+
p9.intro("ai-ops pack list");
|
|
2541
|
+
p9.log.info(
|
|
2542
|
+
packs.map((pack) => {
|
|
2543
|
+
const suffix = installedPackIds.has(pack.id) ? "installed" : "not installed";
|
|
2544
|
+
return `- ${pack.id} - ${suffix}`;
|
|
2545
|
+
}).join("\n")
|
|
2546
|
+
);
|
|
2547
|
+
p9.outro("ai-ops pack list \uC644\uB8CC");
|
|
2548
|
+
};
|
|
2549
|
+
var packInstallCommand = async (packId) => {
|
|
2550
|
+
const basePath = resolveBasePath();
|
|
2551
|
+
p9.intro(`ai-ops pack install ${packId}`);
|
|
2552
|
+
try {
|
|
2553
|
+
const result = installProjectLayerPack({ basePath, packId, packsDir: resolvePacksDir() });
|
|
2554
|
+
p9.log.success(`pack \uC124\uCE58 \uC644\uB8CC: ${packId}`);
|
|
2555
|
+
if (result.written.length > 0) {
|
|
2556
|
+
p9.log.info(`\uC0DD\uC131:
|
|
2557
|
+
${result.written.map((file) => ` ${file}`).join("\n")}`);
|
|
2558
|
+
}
|
|
2559
|
+
if (result.refreshed.length > 0) {
|
|
2560
|
+
p9.log.info(`\uAC31\uC2E0:
|
|
2561
|
+
${result.refreshed.map((file) => ` ${file}`).join("\n")}`);
|
|
2562
|
+
}
|
|
2563
|
+
if (result.preserved.length > 0) {
|
|
2564
|
+
p9.log.info(`\uBCF4\uC874:
|
|
2565
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
2566
|
+
}
|
|
2567
|
+
} catch (error) {
|
|
2568
|
+
reportPackError(error);
|
|
2569
|
+
}
|
|
2570
|
+
p9.outro("ai-ops pack install \uC644\uB8CC");
|
|
2571
|
+
};
|
|
2572
|
+
var packDiffCommand = async (packId) => {
|
|
2573
|
+
const basePath = resolveBasePath();
|
|
2574
|
+
p9.intro("ai-ops pack diff");
|
|
2575
|
+
try {
|
|
2576
|
+
const report = diffProjectLayerPack({ basePath, packId, packsDir: resolvePacksDir() });
|
|
2577
|
+
if (report.issues.length === 0) {
|
|
2578
|
+
p9.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. pack\uC774 \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
2579
|
+
p9.outro("ai-ops pack diff \uC644\uB8CC");
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
for (const item of report.issues) {
|
|
2583
|
+
const line = `[${item.code}] ${item.message}`;
|
|
2584
|
+
if (item.level === "error") {
|
|
2585
|
+
p9.log.error(line);
|
|
2586
|
+
} else {
|
|
2587
|
+
p9.log.warn(line);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
if (report.issues.some((item) => item.level === "error")) {
|
|
2591
|
+
process.exitCode = 1;
|
|
2592
|
+
}
|
|
2593
|
+
} catch (error) {
|
|
2594
|
+
reportPackError(error);
|
|
2595
|
+
}
|
|
2596
|
+
p9.outro("ai-ops pack diff \uC644\uB8CC");
|
|
2597
|
+
};
|
|
2598
|
+
var packUpdateCommand = async (packId) => {
|
|
2599
|
+
const basePath = resolveBasePath();
|
|
2600
|
+
p9.intro("ai-ops pack update");
|
|
2601
|
+
try {
|
|
2602
|
+
const manifest = readManifestForPackCommand(basePath);
|
|
2603
|
+
if (!manifest) {
|
|
2604
|
+
p9.log.error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
2605
|
+
process.exitCode = 1;
|
|
2606
|
+
p9.outro("ai-ops pack update \uC644\uB8CC");
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
const targetPackIds = packId ? [packId] : manifest.packs.map((pack) => pack.id);
|
|
2610
|
+
if (targetPackIds.length === 0) {
|
|
2611
|
+
p9.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C pack\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2612
|
+
p9.outro("ai-ops pack update \uC644\uB8CC");
|
|
2613
|
+
return;
|
|
2614
|
+
}
|
|
2615
|
+
for (const targetPackId of targetPackIds) {
|
|
2616
|
+
const result = updateProjectLayerPack({ basePath, packId: targetPackId, packsDir: resolvePacksDir() });
|
|
2617
|
+
p9.log.success(`pack \uAC31\uC2E0 \uC644\uB8CC: ${targetPackId}`);
|
|
2618
|
+
if (result.refreshed.length > 0) {
|
|
2619
|
+
p9.log.info(`\uAC31\uC2E0:
|
|
2620
|
+
${result.refreshed.map((file) => ` ${file}`).join("\n")}`);
|
|
2621
|
+
}
|
|
2622
|
+
if (result.preserved.length > 0) {
|
|
2623
|
+
p9.log.info(`\uBCF4\uC874:
|
|
2624
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
} catch (error) {
|
|
2628
|
+
reportPackError(error);
|
|
2258
2629
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2630
|
+
p9.outro("ai-ops pack update \uC644\uB8CC");
|
|
2631
|
+
};
|
|
2632
|
+
var packUninstallCommand = async (packId) => {
|
|
2633
|
+
const basePath = resolveBasePath();
|
|
2634
|
+
p9.intro(`ai-ops pack uninstall ${packId}`);
|
|
2635
|
+
try {
|
|
2636
|
+
const result = uninstallProjectLayerPack({ basePath, packId });
|
|
2637
|
+
p9.log.success(`pack \uC81C\uAC70 \uC644\uB8CC: ${packId}`);
|
|
2638
|
+
if (result.deleted.length > 0) {
|
|
2639
|
+
p9.log.info(`\uC0AD\uC81C:
|
|
2640
|
+
${result.deleted.map((file) => ` ${file}`).join("\n")}`);
|
|
2641
|
+
}
|
|
2642
|
+
if (result.preserved.length > 0) {
|
|
2643
|
+
p9.log.info(`\uBCF4\uC874:
|
|
2644
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
2645
|
+
}
|
|
2646
|
+
} catch (error) {
|
|
2647
|
+
reportPackError(error);
|
|
2265
2648
|
}
|
|
2266
|
-
|
|
2649
|
+
p9.outro("ai-ops pack uninstall \uC644\uB8CC");
|
|
2267
2650
|
};
|
|
2268
2651
|
|
|
2269
2652
|
// src/bin/index.ts
|
|
2270
2653
|
var program = new Command();
|
|
2271
|
-
program.name("ai-ops").description("AI
|
|
2272
|
-
program.command("init").description("
|
|
2273
|
-
program.command("update").description("
|
|
2274
|
-
program.command("diff").description("
|
|
2275
|
-
program.command("
|
|
2654
|
+
program.name("ai-ops").description("AI agent operating layer manager").version("0.1.0");
|
|
2655
|
+
program.command("init").description("project operating layer \uCD08\uAE30 \uC124\uCE58").option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C adapter \uC9C0\uC815 (codex|gemini|claude-code)").action((opts) => initCommand(opts));
|
|
2656
|
+
program.command("update").description("project operating layer \uAC31\uC2E0").option("--force", "\uBCC0\uACBD \uC5C6\uC5B4\uB3C4 \uAC15\uC81C \uC7AC\uC124\uCE58", false).action((opts) => updateCommand(opts));
|
|
2657
|
+
program.command("diff").description("project operating layer drift \uBE44\uAD50").action(() => diffCommand());
|
|
2658
|
+
program.command("audit").description("project operating layer \uC0C1\uD0DC \uAC80\uC0AC").action(() => auditCommand());
|
|
2659
|
+
program.command("uninstall").description("project operating layer \uC81C\uAC70").option("--yes", "\uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC81C\uAC70", false).action((opts) => uninstallCommand(opts));
|
|
2276
2660
|
var skillCommand = program.command("skill").description("\uC5D0\uC774\uC804\uD2B8 skill \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2277
|
-
var
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
);
|
|
2281
|
-
applySkillScopeOptions(skillCommand.command("install <skillId>").description("skill \uC124\uCE58")).action(
|
|
2661
|
+
var applySkillInstallOptions = (command) => command.option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C \uC9C0\uC815");
|
|
2662
|
+
skillCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C skill \uBAA9\uB85D").action(() => skillListCommand());
|
|
2663
|
+
applySkillInstallOptions(skillCommand.command("install <skillId>").description("skill \uC124\uCE58")).action(
|
|
2282
2664
|
(skillId, opts) => skillInstallCommand(skillId, opts)
|
|
2283
2665
|
);
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
);
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
);
|
|
2290
|
-
|
|
2291
|
-
(
|
|
2666
|
+
skillCommand.command("diff [skillId]").description("skill \uBCC0\uACBD \uBE44\uAD50").action((skillId) => skillDiffCommand(skillId));
|
|
2667
|
+
skillCommand.command("update [skillId]").description("skill \uAC31\uC2E0").action((skillId) => skillUpdateCommand(skillId));
|
|
2668
|
+
skillCommand.command("uninstall <skillId>").description("skill \uC81C\uAC70").action((skillId) => skillUninstallCommand(skillId));
|
|
2669
|
+
var subagentCommand = program.command("subagent").description("\uC5D0\uC774\uC804\uD2B8 subagent \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2670
|
+
var applySubagentInstallOptions = (command) => command.option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C \uC9C0\uC815");
|
|
2671
|
+
subagentCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C subagent \uBAA9\uB85D").action(() => subagentListCommand());
|
|
2672
|
+
applySubagentInstallOptions(subagentCommand.command("install <subagentId>").description("subagent \uC124\uCE58")).action(
|
|
2673
|
+
(subagentId, opts) => subagentInstallCommand(subagentId, opts)
|
|
2292
2674
|
);
|
|
2293
|
-
|
|
2294
|
-
|
|
2675
|
+
subagentCommand.command("diff [subagentId]").description("subagent \uBCC0\uACBD \uBE44\uAD50").action((subagentId) => subagentDiffCommand(subagentId));
|
|
2676
|
+
subagentCommand.command("update [subagentId]").description("subagent \uAC31\uC2E0").action((subagentId) => subagentUpdateCommand(subagentId));
|
|
2677
|
+
subagentCommand.command("uninstall <subagentId>").description("subagent \uC81C\uAC70").action((subagentId) => subagentUninstallCommand(subagentId));
|
|
2678
|
+
var packCommand = program.command("pack").description("optional project operating layer pack \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2679
|
+
packCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C pack \uBAA9\uB85D").action(() => packListCommand());
|
|
2680
|
+
packCommand.command("install <packId>").description("pack \uC124\uCE58").action((packId) => packInstallCommand(packId));
|
|
2681
|
+
packCommand.command("diff [packId]").description("pack \uBCC0\uACBD \uBE44\uAD50").action((packId) => packDiffCommand(packId));
|
|
2682
|
+
packCommand.command("update [packId]").description("pack \uAC31\uC2E0").action((packId) => packUpdateCommand(packId));
|
|
2683
|
+
packCommand.command("uninstall <packId>").description("pack \uC81C\uAC70").action((packId) => packUninstallCommand(packId));
|
|
2295
2684
|
program.parse();
|
|
2296
2685
|
//# sourceMappingURL=index.js.map
|