ai-ops-cli 0.2.6 → 1.0.2
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 +195 -0
- package/README.md +126 -155
- package/data/context-layer/AGENTS.md +30 -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/00-agent-baseline.md +47 -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 +28 -0
- package/data/packs/spec-lifecycle/docs/specs/README.md +28 -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 +184 -0
- package/data/skills/README.md +29 -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 +49 -0
- package/data/subagents/README.md +49 -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 +2103 -1713
- package/dist/bin/index.js.map +1 -1
- package/package.json +2 -2
- package/data/presets.yaml +0 -35
- package/data/rules/code-philosophy.yaml +0 -35
- package/data/rules/communication.yaml +0 -12
- package/data/rules/naming-convention.yaml +0 -10
- package/data/rules/plan-mode.yaml +0 -32
- package/data/rules/role-persona.yaml +0 -14
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
|
-
import {
|
|
437
|
-
import { dirname, join as
|
|
494
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
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,1357 @@ 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, 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
|
-
|
|
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/00-agent-baseline.md",
|
|
812
|
+
"docs/agent/rules/routing-rules.md",
|
|
813
|
+
"docs/agent/rules/doc-update-rules.md",
|
|
814
|
+
"docs/agent/rules/stop-rules.md",
|
|
815
|
+
"docs/agent/checks/impact-checklist.md",
|
|
816
|
+
"docs/agent/checks/review-checklist.md",
|
|
817
|
+
"docs/agent/maps/codebase-map.md",
|
|
818
|
+
"docs/business/business-rules.md",
|
|
819
|
+
"docs/docs-status.md"
|
|
926
820
|
];
|
|
927
|
-
var
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
821
|
+
var PROJECT_OWNED_PATHS = /* @__PURE__ */ new Set([
|
|
822
|
+
"docs/docs-status.md",
|
|
823
|
+
"docs/agent/maps/codebase-map.md",
|
|
824
|
+
"docs/business/business-rules.md"
|
|
825
|
+
]);
|
|
826
|
+
var RESERVED_DOCUMENT_WARNINGS = [
|
|
827
|
+
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
828
|
+
"Do not use this document as current decision-making evidence"
|
|
829
|
+
];
|
|
830
|
+
var resolveProjectLayerManifestPath = (basePath) => join10(basePath, PROJECT_LAYER_MANIFEST_RELATIVE_PATH);
|
|
831
|
+
var resolveProjectLayerContextIndexPath = (basePath) => join10(basePath, PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH);
|
|
832
|
+
var resolveTemplatePath = (relativePath) => join10(CONTEXT_LAYER_DATA_DIR, relativePath);
|
|
833
|
+
var toRelativeDir = (relativePath) => dirname6(relativePath);
|
|
834
|
+
var resolveProjectLayerFilePath = (basePath, relativePath) => {
|
|
835
|
+
if (!isSafeProjectLayerPath(relativePath)) {
|
|
836
|
+
throw new Error(`Unsafe project layer path: ${relativePath}`);
|
|
837
|
+
}
|
|
838
|
+
const absoluteBasePath = resolve5(basePath);
|
|
839
|
+
const absolutePath = resolve5(absoluteBasePath, relativePath);
|
|
840
|
+
const relativeFromBase = relative(absoluteBasePath, absolutePath);
|
|
841
|
+
if (relativeFromBase === "" || relativeFromBase.startsWith("..") || isAbsolute(relativeFromBase)) {
|
|
842
|
+
throw new Error(`Unsafe project layer path: ${relativePath}`);
|
|
843
|
+
}
|
|
844
|
+
return absolutePath;
|
|
845
|
+
};
|
|
846
|
+
var parseProjectLayerManifest = (json) => ProjectLayerManifestSchema.parse(JSON.parse(json));
|
|
847
|
+
var serializeProjectLayerManifest = (manifest) => JSON.stringify(manifest, null, 2) + "\n";
|
|
848
|
+
var parseProjectLayerContextIndex = (json) => ProjectLayerContextIndexSchema.parse(JSON.parse(json));
|
|
849
|
+
var serializeProjectLayerContextIndex = (contextIndex) => JSON.stringify(contextIndex, null, 2) + "\n";
|
|
850
|
+
var parseProjectLayerFrontmatter = (content) => {
|
|
851
|
+
const { frontmatter } = parseMarkdownFrontmatter(content);
|
|
852
|
+
return ProjectLayerFrontmatterSchema.parse(frontmatter);
|
|
853
|
+
};
|
|
854
|
+
var parseProjectLayerDocument = (path, rawContent) => {
|
|
855
|
+
const managedContent = extractAiOpsSectionContent(rawContent);
|
|
856
|
+
const content = managedContent ?? rawContent;
|
|
857
|
+
const frontmatter = parseProjectLayerFrontmatter(content);
|
|
858
|
+
return {
|
|
859
|
+
path,
|
|
860
|
+
status: frontmatter.status,
|
|
861
|
+
layer: frontmatter.layer,
|
|
862
|
+
owner: frontmatter.owner,
|
|
863
|
+
read_when: frontmatter.read_when,
|
|
864
|
+
update_when: frontmatter.update_when,
|
|
865
|
+
contentHash: computeHash([content.trimEnd()]),
|
|
866
|
+
content
|
|
867
|
+
};
|
|
948
868
|
};
|
|
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);
|
|
869
|
+
var parseDocsStatusEntries = (content) => {
|
|
870
|
+
const document = parseProjectLayerDocument("docs/docs-status.md", content);
|
|
871
|
+
const rows = document.content.split("\n").filter((line) => line.trim().startsWith("|")).map((line) => line.trim());
|
|
872
|
+
return rows.flatMap((line) => {
|
|
873
|
+
const cells = line.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
874
|
+
if (cells.length < 3) return [];
|
|
875
|
+
if (cells[0] === "path") return [];
|
|
876
|
+
if (cells[0].startsWith("---")) return [];
|
|
877
|
+
return [
|
|
878
|
+
{
|
|
879
|
+
path: cells[0],
|
|
880
|
+
status: cells[1],
|
|
881
|
+
owner: cells[2]
|
|
977
882
|
}
|
|
978
|
-
|
|
883
|
+
];
|
|
884
|
+
});
|
|
885
|
+
};
|
|
886
|
+
var resolveProjectLayerTools = (requestedTools) => {
|
|
887
|
+
if (requestedTools === void 0 || requestedTools.length === 0) {
|
|
888
|
+
return [...DEFAULT_TOOLS];
|
|
979
889
|
}
|
|
980
|
-
|
|
890
|
+
const parsedTools = requestedTools.map((tool) => ProjectLayerToolSchema.parse(tool));
|
|
891
|
+
const toolSet = new Set(parsedTools);
|
|
892
|
+
return TOOL_ORDER.filter((tool) => toolSet.has(tool));
|
|
981
893
|
};
|
|
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);
|
|
894
|
+
var shouldIncludeTemplate = (relativePath, tools) => {
|
|
895
|
+
if (relativePath === "GEMINI.md") return tools.includes("gemini");
|
|
896
|
+
if (relativePath === "CLAUDE.md") return tools.includes("claude-code");
|
|
897
|
+
return true;
|
|
898
|
+
};
|
|
899
|
+
var buildDocsStatusRows = (specs) => specs.map((spec) => `| ${spec.path} | ${spec.frontmatter.status} | ${spec.frontmatter.owner} |`).join("\n");
|
|
900
|
+
var includesReservedDocumentWarning = (content) => RESERVED_DOCUMENT_WARNINGS.some((warning) => content.includes(warning));
|
|
901
|
+
var loadTemplateSpec = (relativePath, content) => {
|
|
902
|
+
const frontmatter = parseProjectLayerFrontmatter(content);
|
|
903
|
+
const ownership = PROJECT_OWNED_PATHS.has(relativePath) ? "project" : "managed";
|
|
904
|
+
if (frontmatter.status === "Reserved" && !includesReservedDocumentWarning(content)) {
|
|
905
|
+
throw new Error(`Reserved template must include warning text: ${relativePath}`);
|
|
999
906
|
}
|
|
1000
|
-
return
|
|
907
|
+
return {
|
|
908
|
+
path: relativePath,
|
|
909
|
+
content,
|
|
910
|
+
ownership,
|
|
911
|
+
frontmatter,
|
|
912
|
+
contentHash: computeHash([content.trimEnd()])
|
|
913
|
+
};
|
|
1001
914
|
};
|
|
1002
|
-
var
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
915
|
+
var loadProjectLayerTemplateSpecs = (tools) => {
|
|
916
|
+
const selectedPaths = TEMPLATE_PATHS.filter((relativePath) => shouldIncludeTemplate(relativePath, tools));
|
|
917
|
+
const nonStatusSpecs = selectedPaths.filter((relativePath) => relativePath !== "docs/docs-status.md").map((relativePath) => loadTemplateSpec(relativePath, readFileSync6(resolveTemplatePath(relativePath), "utf-8")));
|
|
918
|
+
const statusTemplate = readFileSync6(resolveTemplatePath("docs/docs-status.md"), "utf-8");
|
|
919
|
+
const statusPlaceholderSpec = loadTemplateSpec("docs/docs-status.md", statusTemplate);
|
|
920
|
+
const specsForStatus = [...nonStatusSpecs, statusPlaceholderSpec].sort((a, b) => a.path.localeCompare(b.path));
|
|
921
|
+
const statusContent = statusTemplate.replace("{{documents_table}}", buildDocsStatusRows(specsForStatus));
|
|
922
|
+
const statusSpec = loadTemplateSpec("docs/docs-status.md", statusContent);
|
|
923
|
+
return [...nonStatusSpecs, statusSpec].sort((a, b) => a.path.localeCompare(b.path));
|
|
924
|
+
};
|
|
925
|
+
var computeProjectLayerSourceHash = (specs) => computeHash(specs.map((spec) => `${spec.path}:${spec.content}`));
|
|
926
|
+
var readProjectLayerManifest = (basePath) => {
|
|
927
|
+
try {
|
|
928
|
+
return parseProjectLayerManifest(readFileSync6(resolveProjectLayerManifestPath(basePath), "utf-8"));
|
|
929
|
+
} catch (error) {
|
|
930
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
throw error;
|
|
1009
934
|
}
|
|
1010
|
-
return removed;
|
|
1011
935
|
};
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
result[key] = deepMerge(result[key], value);
|
|
1024
|
-
} else {
|
|
1025
|
-
result[key] = value;
|
|
936
|
+
var writeProjectLayerManifest = (basePath, manifest) => {
|
|
937
|
+
const manifestPath = resolveProjectLayerManifestPath(basePath);
|
|
938
|
+
mkdirSync4(dirname6(manifestPath), { recursive: true });
|
|
939
|
+
writeFileSync4(manifestPath, serializeProjectLayerManifest(manifest), "utf-8");
|
|
940
|
+
};
|
|
941
|
+
var readProjectLayerContextIndex = (basePath) => {
|
|
942
|
+
try {
|
|
943
|
+
return parseProjectLayerContextIndex(readFileSync6(resolveProjectLayerContextIndexPath(basePath), "utf-8"));
|
|
944
|
+
} catch (error) {
|
|
945
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
946
|
+
return null;
|
|
1026
947
|
}
|
|
948
|
+
throw error;
|
|
1027
949
|
}
|
|
1028
|
-
return result;
|
|
1029
950
|
};
|
|
1030
|
-
var
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
951
|
+
var writeProjectLayerContextIndex = (basePath, contextIndex) => {
|
|
952
|
+
const contextIndexPath = resolveProjectLayerContextIndexPath(basePath);
|
|
953
|
+
mkdirSync4(dirname6(contextIndexPath), { recursive: true });
|
|
954
|
+
writeFileSync4(contextIndexPath, serializeProjectLayerContextIndex(contextIndex), "utf-8");
|
|
955
|
+
};
|
|
956
|
+
var installManagedFiles = (basePath, specs, meta) => {
|
|
957
|
+
const written = [];
|
|
958
|
+
const appended = [];
|
|
959
|
+
for (const spec of specs) {
|
|
960
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, spec.path);
|
|
961
|
+
const wrappedContent = wrapWithSection(spec.content, meta);
|
|
962
|
+
if (!existsSync(absolutePath)) {
|
|
963
|
+
mkdirSync4(dirname6(absolutePath), { recursive: true });
|
|
964
|
+
writeFileSync4(absolutePath, wrappedContent + "\n", "utf-8");
|
|
965
|
+
written.push(spec.path);
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
const existing = readFileSync6(absolutePath, "utf-8");
|
|
969
|
+
if (hasAiOpsSection(existing)) {
|
|
970
|
+
writeFileSync4(absolutePath, replaceAiOpsSection(existing, wrappedContent), "utf-8");
|
|
971
|
+
const stripped = stripAiOpsSection(existing);
|
|
972
|
+
(stripped.trim().length > 0 ? appended : written).push(spec.path);
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
if (hasLegacyHeader(existing)) {
|
|
976
|
+
writeFileSync4(absolutePath, wrappedContent + "\n", "utf-8");
|
|
977
|
+
written.push(spec.path);
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
writeFileSync4(absolutePath, existing.trimEnd() + "\n\n" + wrappedContent + "\n", "utf-8");
|
|
981
|
+
appended.push(spec.path);
|
|
982
|
+
}
|
|
983
|
+
return { written, appended };
|
|
984
|
+
};
|
|
985
|
+
var installProjectFiles = (params) => {
|
|
986
|
+
const records = [];
|
|
987
|
+
const created = [];
|
|
988
|
+
const refreshed = [];
|
|
989
|
+
const preserved = [];
|
|
990
|
+
const previousByPath = new Map((params.previousProjectFiles ?? []).map((file) => [file.path, file]));
|
|
991
|
+
for (const spec of params.specs) {
|
|
992
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, spec.path);
|
|
993
|
+
const previous = previousByPath.get(spec.path);
|
|
994
|
+
if (!existsSync(absolutePath)) {
|
|
995
|
+
mkdirSync4(dirname6(absolutePath), { recursive: true });
|
|
996
|
+
writeFileSync4(absolutePath, spec.content + "\n", "utf-8");
|
|
997
|
+
created.push(spec.path);
|
|
998
|
+
records.push({
|
|
999
|
+
path: spec.path,
|
|
1000
|
+
templateHash: spec.contentHash,
|
|
1001
|
+
created: true
|
|
1002
|
+
});
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
const existingContent = readFileSync6(absolutePath, "utf-8").trimEnd();
|
|
1006
|
+
const existingHash = computeHash([existingContent]);
|
|
1007
|
+
if (previous?.created === true && existingHash === previous.templateHash) {
|
|
1008
|
+
if (existingHash !== spec.contentHash) {
|
|
1009
|
+
writeFileSync4(absolutePath, spec.content + "\n", "utf-8");
|
|
1010
|
+
refreshed.push(spec.path);
|
|
1038
1011
|
} else {
|
|
1039
|
-
|
|
1012
|
+
preserved.push(spec.path);
|
|
1040
1013
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1014
|
+
records.push({
|
|
1015
|
+
path: spec.path,
|
|
1016
|
+
templateHash: spec.contentHash,
|
|
1017
|
+
created: true
|
|
1018
|
+
});
|
|
1019
|
+
continue;
|
|
1043
1020
|
}
|
|
1021
|
+
preserved.push(spec.path);
|
|
1022
|
+
records.push({
|
|
1023
|
+
path: spec.path,
|
|
1024
|
+
templateHash: previous?.templateHash ?? spec.contentHash,
|
|
1025
|
+
created: previous?.created ?? false
|
|
1026
|
+
});
|
|
1044
1027
|
}
|
|
1045
|
-
return
|
|
1028
|
+
return { records, created, refreshed, preserved };
|
|
1029
|
+
};
|
|
1030
|
+
var buildContextIndexFromDisk = (params) => {
|
|
1031
|
+
const documents = params.documentPaths.map(
|
|
1032
|
+
(path) => parseProjectLayerDocument(path, readFileSync6(resolveProjectLayerFilePath(params.basePath, path), "utf-8"))
|
|
1033
|
+
);
|
|
1034
|
+
return ProjectLayerContextIndexSchema.parse({
|
|
1035
|
+
schemaVersion: 1,
|
|
1036
|
+
kind: "context-layer-index",
|
|
1037
|
+
documents: documents.map(({ content: _content, ...document }) => document),
|
|
1038
|
+
generatedAt: params.generatedAt
|
|
1039
|
+
});
|
|
1046
1040
|
};
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
var
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1041
|
+
var computeProjectFileHash = (basePath, relativePath) => computeHash([readFileSync6(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
|
|
1042
|
+
var collectDocumentPathsFromManifest = (manifest) => [
|
|
1043
|
+
...manifest.managed_files.map((file) => file.path),
|
|
1044
|
+
...manifest.project_files.map((file) => file.path),
|
|
1045
|
+
...manifest.packs.flatMap((pack) => pack.documents.map((file) => file.path))
|
|
1046
|
+
].sort();
|
|
1047
|
+
var buildDocsStatusRowsFromDisk = (params) => params.documentPaths.map((path) => {
|
|
1048
|
+
const document = parseProjectLayerDocument(path, readFileSync6(resolveProjectLayerFilePath(params.basePath, path), "utf-8"));
|
|
1049
|
+
return `| ${document.path} | ${document.status} | ${document.owner} |`;
|
|
1050
|
+
});
|
|
1051
|
+
var replaceDocsStatusRows = (content, rows) => {
|
|
1052
|
+
const lines = content.trimEnd().split("\n");
|
|
1053
|
+
const headerIndex = lines.findIndex((line) => line.trim() === "| path | status | owner |");
|
|
1054
|
+
const dividerIndex = headerIndex + 1;
|
|
1055
|
+
if (headerIndex < 0 || !lines[dividerIndex]?.trim().startsWith("| ---")) {
|
|
1056
|
+
throw new Error("docs/docs-status.md table header not found");
|
|
1057
|
+
}
|
|
1058
|
+
let tableEndIndex = dividerIndex + 1;
|
|
1059
|
+
while (tableEndIndex < lines.length && lines[tableEndIndex]?.trim().startsWith("|")) {
|
|
1060
|
+
tableEndIndex += 1;
|
|
1061
|
+
}
|
|
1062
|
+
return [...lines.slice(0, dividerIndex + 1), ...rows, ...lines.slice(tableEndIndex)].join("\n") + "\n";
|
|
1063
|
+
};
|
|
1064
|
+
var updateDocsStatusTable = (basePath, documentPaths) => {
|
|
1065
|
+
const docsStatusPath = "docs/docs-status.md";
|
|
1066
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, docsStatusPath);
|
|
1067
|
+
const beforeHash = computeProjectFileHash(basePath, docsStatusPath);
|
|
1068
|
+
const rows = buildDocsStatusRowsFromDisk({ basePath, documentPaths });
|
|
1069
|
+
const nextContent = replaceDocsStatusRows(readFileSync6(absolutePath, "utf-8"), rows);
|
|
1070
|
+
writeFileSync4(absolutePath, nextContent, "utf-8");
|
|
1071
|
+
return {
|
|
1072
|
+
beforeHash,
|
|
1073
|
+
afterHash: computeProjectFileHash(basePath, docsStatusPath)
|
|
1074
|
+
};
|
|
1075
|
+
};
|
|
1076
|
+
var updateDocsStatusProjectFileRecord = (params) => ProjectLayerManifestSchema.parse({
|
|
1077
|
+
...params.manifest,
|
|
1078
|
+
project_files: params.manifest.project_files.map((file) => {
|
|
1079
|
+
if (file.path !== "docs/docs-status.md" || !file.created || file.templateHash !== params.beforeHash) {
|
|
1080
|
+
return file;
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
...file,
|
|
1084
|
+
templateHash: params.afterHash
|
|
1085
|
+
};
|
|
1086
|
+
})
|
|
1087
|
+
});
|
|
1088
|
+
var refreshProjectLayerDerivedState = (params) => {
|
|
1089
|
+
const documentPaths = collectDocumentPathsFromManifest(params.manifest);
|
|
1090
|
+
const docsStatusHashes = updateDocsStatusTable(params.basePath, documentPaths);
|
|
1091
|
+
const manifest = updateDocsStatusProjectFileRecord({
|
|
1092
|
+
manifest: params.manifest,
|
|
1093
|
+
beforeHash: docsStatusHashes.beforeHash,
|
|
1094
|
+
afterHash: docsStatusHashes.afterHash
|
|
1062
1095
|
});
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1096
|
+
const contextIndex = buildContextIndexFromDisk({
|
|
1097
|
+
basePath: params.basePath,
|
|
1098
|
+
documentPaths,
|
|
1099
|
+
generatedAt: params.generatedAt
|
|
1100
|
+
});
|
|
1101
|
+
writeProjectLayerContextIndex(params.basePath, contextIndex);
|
|
1102
|
+
return {
|
|
1103
|
+
manifest,
|
|
1104
|
+
contextIndex
|
|
1105
|
+
};
|
|
1106
|
+
};
|
|
1107
|
+
var buildProjectLayerManifest = (params) => ProjectLayerManifestSchema.parse({
|
|
1108
|
+
schemaVersion: 1,
|
|
1109
|
+
kind: "project-operating-layer",
|
|
1110
|
+
tools: [...params.tools],
|
|
1111
|
+
managed_files: params.managedFiles.map((path) => ({
|
|
1112
|
+
path,
|
|
1113
|
+
sourceHash: params.sourceHash
|
|
1114
|
+
})),
|
|
1115
|
+
project_files: [...params.projectFiles],
|
|
1116
|
+
packs: [...params.packs],
|
|
1117
|
+
settings: params.settings ?? {},
|
|
1118
|
+
sourceHash: params.sourceHash,
|
|
1119
|
+
cliVersion: params.cliVersion,
|
|
1120
|
+
generatedAt: params.generatedAt
|
|
1121
|
+
});
|
|
1122
|
+
var retireUnselectedManagedFiles = (params) => {
|
|
1123
|
+
if (!params.previousManifest) return;
|
|
1124
|
+
const nextManagedPathSet = new Set(params.nextManagedPaths);
|
|
1125
|
+
for (const file of params.previousManifest.managed_files) {
|
|
1126
|
+
if (!nextManagedPathSet.has(file.path)) {
|
|
1127
|
+
removeManagedProjectFile(params.basePath, file.path);
|
|
1075
1128
|
}
|
|
1076
1129
|
}
|
|
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
1130
|
};
|
|
1086
|
-
var
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1131
|
+
var installProjectLayer = (params) => {
|
|
1132
|
+
const previousManifest = params.previousManifest === void 0 ? readProjectLayerManifest(params.basePath) : params.previousManifest;
|
|
1133
|
+
const specs = loadProjectLayerTemplateSpecs(params.tools);
|
|
1134
|
+
const sourceHash = computeProjectLayerSourceHash(specs);
|
|
1135
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1136
|
+
const managedSpecs = specs.filter((spec) => spec.ownership === "managed");
|
|
1137
|
+
const projectSpecs = specs.filter((spec) => spec.ownership === "project");
|
|
1138
|
+
const managedPaths = managedSpecs.map((spec) => spec.path);
|
|
1139
|
+
retireUnselectedManagedFiles({
|
|
1140
|
+
basePath: params.basePath,
|
|
1141
|
+
previousManifest,
|
|
1142
|
+
nextManagedPaths: managedPaths
|
|
1143
|
+
});
|
|
1144
|
+
const managedResult = installManagedFiles(params.basePath, managedSpecs, { sourceHash, generatedAt });
|
|
1145
|
+
const projectResult = installProjectFiles({
|
|
1146
|
+
basePath: params.basePath,
|
|
1147
|
+
specs: projectSpecs,
|
|
1148
|
+
previousProjectFiles: previousManifest?.project_files
|
|
1149
|
+
});
|
|
1150
|
+
const provisionalManifest = buildProjectLayerManifest({
|
|
1151
|
+
tools: params.tools,
|
|
1152
|
+
managedFiles: managedPaths,
|
|
1153
|
+
projectFiles: projectResult.records,
|
|
1154
|
+
packs: previousManifest?.packs ?? [],
|
|
1155
|
+
sourceHash,
|
|
1156
|
+
cliVersion: getCliVersion(),
|
|
1157
|
+
generatedAt,
|
|
1158
|
+
settings: previousManifest?.settings
|
|
1159
|
+
});
|
|
1160
|
+
const { manifest, contextIndex } = refreshProjectLayerDerivedState({
|
|
1161
|
+
basePath: params.basePath,
|
|
1162
|
+
manifest: provisionalManifest,
|
|
1163
|
+
generatedAt
|
|
1164
|
+
});
|
|
1165
|
+
writeProjectLayerManifest(params.basePath, manifest);
|
|
1166
|
+
return {
|
|
1167
|
+
manifest,
|
|
1168
|
+
contextIndex,
|
|
1169
|
+
written: managedResult.written,
|
|
1170
|
+
appended: managedResult.appended,
|
|
1171
|
+
createdProjectFiles: projectResult.created,
|
|
1172
|
+
refreshedProjectFiles: projectResult.refreshed,
|
|
1173
|
+
preservedProjectFiles: projectResult.preserved
|
|
1174
|
+
};
|
|
1175
|
+
};
|
|
1176
|
+
var updateProjectLayer = (params) => {
|
|
1177
|
+
const specs = loadProjectLayerTemplateSpecs(params.manifest.tools);
|
|
1178
|
+
const sourceHash = computeProjectLayerSourceHash(specs);
|
|
1179
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1180
|
+
const managedSpecs = specs.filter((spec) => spec.ownership === "managed");
|
|
1181
|
+
const projectSpecs = specs.filter((spec) => spec.ownership === "project");
|
|
1182
|
+
const managedResult = installManagedFiles(params.basePath, managedSpecs, { sourceHash, generatedAt });
|
|
1183
|
+
const projectResult = installProjectFiles({
|
|
1184
|
+
basePath: params.basePath,
|
|
1185
|
+
specs: projectSpecs,
|
|
1186
|
+
previousProjectFiles: params.manifest.project_files
|
|
1187
|
+
});
|
|
1188
|
+
const provisionalManifest = buildProjectLayerManifest({
|
|
1189
|
+
tools: params.manifest.tools,
|
|
1190
|
+
managedFiles: managedSpecs.map((spec) => spec.path),
|
|
1191
|
+
projectFiles: projectResult.records,
|
|
1192
|
+
packs: params.manifest.packs,
|
|
1193
|
+
sourceHash,
|
|
1194
|
+
cliVersion: getCliVersion(),
|
|
1195
|
+
generatedAt,
|
|
1196
|
+
settings: params.manifest.settings
|
|
1197
|
+
});
|
|
1198
|
+
const { manifest, contextIndex } = refreshProjectLayerDerivedState({
|
|
1199
|
+
basePath: params.basePath,
|
|
1200
|
+
manifest: provisionalManifest,
|
|
1201
|
+
generatedAt
|
|
1202
|
+
});
|
|
1203
|
+
writeProjectLayerManifest(params.basePath, manifest);
|
|
1204
|
+
return {
|
|
1205
|
+
manifest,
|
|
1206
|
+
contextIndex,
|
|
1207
|
+
written: managedResult.written,
|
|
1208
|
+
appended: managedResult.appended,
|
|
1209
|
+
createdProjectFiles: projectResult.created,
|
|
1210
|
+
refreshedProjectFiles: projectResult.refreshed,
|
|
1211
|
+
preservedProjectFiles: projectResult.preserved
|
|
1212
|
+
};
|
|
1213
|
+
};
|
|
1214
|
+
var issue = (level, code, message) => ({
|
|
1215
|
+
level,
|
|
1216
|
+
code,
|
|
1217
|
+
message
|
|
1218
|
+
});
|
|
1219
|
+
var readDocumentSafely = (basePath, path) => {
|
|
1090
1220
|
try {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1221
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, path);
|
|
1222
|
+
if (!existsSync(absolutePath)) {
|
|
1223
|
+
return issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${path}`);
|
|
1224
|
+
}
|
|
1225
|
+
return parseProjectLayerDocument(path, readFileSync6(absolutePath, "utf-8"));
|
|
1226
|
+
} catch (error) {
|
|
1227
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1228
|
+
return issue("error", "invalid-frontmatter", `${path} frontmatter \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
var buildContextIndexMap = (contextIndex) => new Map((contextIndex?.documents ?? []).map((document) => [document.path, document]));
|
|
1232
|
+
var compareArray = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1233
|
+
var compareContextDocument = (params) => {
|
|
1234
|
+
const indexed = params.indexed;
|
|
1235
|
+
if (indexed === void 0) {
|
|
1236
|
+
return [issue("error", "context-missing-document", `context-layer \uB204\uB77D: ${params.expected.path}`)];
|
|
1237
|
+
}
|
|
1238
|
+
const issues = [];
|
|
1239
|
+
const scalarKeys = ["status", "layer", "owner", "contentHash"];
|
|
1240
|
+
for (const key of scalarKeys) {
|
|
1241
|
+
if (params.expected[key] !== indexed[key]) {
|
|
1242
|
+
issues.push(
|
|
1243
|
+
issue("error", "context-document-mismatch", `${params.expected.path} context ${key} \uBD88\uC77C\uCE58`)
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1095
1246
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const group = config.groups.find((g) => g.value === val);
|
|
1099
|
-
if (!group) continue;
|
|
1100
|
-
result = deepRemoveKeys(result, group.patch);
|
|
1247
|
+
if (!compareArray(params.expected.read_when, indexed.read_when)) {
|
|
1248
|
+
issues.push(issue("error", "context-document-mismatch", `${params.expected.path} context read_when \uBD88\uC77C\uCE58`));
|
|
1101
1249
|
}
|
|
1102
|
-
if (
|
|
1103
|
-
|
|
1104
|
-
return "deleted";
|
|
1250
|
+
if (!compareArray(params.expected.update_when, indexed.update_when)) {
|
|
1251
|
+
issues.push(issue("error", "context-document-mismatch", `${params.expected.path} context update_when \uBD88\uC77C\uCE58`));
|
|
1105
1252
|
}
|
|
1106
|
-
|
|
1107
|
-
return "cleaned";
|
|
1253
|
+
return issues;
|
|
1108
1254
|
};
|
|
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 } }
|
|
1255
|
+
var compareDocsStatusEntry = (params) => {
|
|
1256
|
+
const entry = params.entry;
|
|
1257
|
+
if (entry === void 0) {
|
|
1258
|
+
return [issue("error", "docs-status-missing-document", `docs-status \uB204\uB77D: ${params.expected.path}`)];
|
|
1135
1259
|
}
|
|
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" }
|
|
1260
|
+
const issues = [];
|
|
1261
|
+
if (entry.status !== params.expected.status) {
|
|
1262
|
+
issues.push(issue("error", "docs-status-mismatch", `${params.expected.path} docs-status status \uBD88\uC77C\uCE58`));
|
|
1160
1263
|
}
|
|
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
|
-
|
|
1264
|
+
if (entry.owner !== params.expected.owner) {
|
|
1265
|
+
issues.push(issue("error", "docs-status-mismatch", `${params.expected.path} docs-status owner \uBD88\uC77C\uCE58`));
|
|
1266
|
+
}
|
|
1267
|
+
return issues;
|
|
1268
|
+
};
|
|
1269
|
+
var diffProjectLayer = (basePath) => {
|
|
1270
|
+
let manifest;
|
|
1271
|
+
try {
|
|
1272
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1275
|
+
return {
|
|
1276
|
+
currentSourceHash: null,
|
|
1277
|
+
issues: [issue("error", "invalid-manifest", `${PROJECT_LAYER_MANIFEST_RELATIVE_PATH} \uD30C\uC2F1 \uC2E4\uD328: ${reason}`)]
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
if (!manifest) {
|
|
1281
|
+
return {
|
|
1282
|
+
currentSourceHash: null,
|
|
1283
|
+
issues: [issue("error", "missing-manifest", `${PROJECT_LAYER_MANIFEST_RELATIVE_PATH}\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`)]
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
const specs = loadProjectLayerTemplateSpecs(manifest.tools);
|
|
1287
|
+
const currentSourceHash = computeProjectLayerSourceHash(specs);
|
|
1288
|
+
let contextIndex = null;
|
|
1289
|
+
const issues = [];
|
|
1290
|
+
try {
|
|
1291
|
+
contextIndex = readProjectLayerContextIndex(basePath);
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1294
|
+
issues.push(
|
|
1295
|
+
issue("error", "invalid-context-index", `${PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH} \uD30C\uC2F1 \uC2E4\uD328: ${reason}`)
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
const contextMap = buildContextIndexMap(contextIndex);
|
|
1299
|
+
const expectedManagedPaths = new Set(specs.filter((spec) => spec.ownership === "managed").map((spec) => spec.path));
|
|
1300
|
+
const manifestManagedPaths = new Set(manifest.managed_files.map((file) => file.path));
|
|
1301
|
+
if (manifest.sourceHash !== currentSourceHash) {
|
|
1302
|
+
issues.push(
|
|
1303
|
+
issue("warning", "source-hash-drift", `template sourceHash \uBCC0\uACBD: ${manifest.sourceHash} -> ${currentSourceHash}`)
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
if (contextIndex === null) {
|
|
1307
|
+
issues.push(issue("error", "missing-context-index", `${PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH}\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`));
|
|
1308
|
+
}
|
|
1309
|
+
for (const expectedPath of expectedManagedPaths) {
|
|
1310
|
+
if (!manifestManagedPaths.has(expectedPath)) {
|
|
1311
|
+
issues.push(issue("error", "manifest-missing-managed-file", `manifest managed_files \uB204\uB77D: ${expectedPath}`));
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
for (const file of manifest.managed_files) {
|
|
1315
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1316
|
+
if (!existsSync(absolutePath)) {
|
|
1317
|
+
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1204
1318
|
continue;
|
|
1205
1319
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1320
|
+
const content = readFileSync6(absolutePath, "utf-8");
|
|
1321
|
+
const meta = parseAiOpsMeta(content);
|
|
1322
|
+
if (!meta) {
|
|
1323
|
+
issues.push(issue("error", "missing-managed-section", `managed section \uBA54\uD0C0 \uC5C6\uC74C: ${file.path}`));
|
|
1208
1324
|
continue;
|
|
1209
1325
|
}
|
|
1210
|
-
if (
|
|
1326
|
+
if (meta.sourceHash !== currentSourceHash) {
|
|
1327
|
+
issues.push(
|
|
1328
|
+
issue("warning", "managed-source-hash-drift", `${file.path} sourceHash \uBCC0\uACBD: ${meta.sourceHash} -> ${currentSourceHash}`)
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1211
1331
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
const
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
continue;
|
|
1332
|
+
for (const file of manifest.project_files) {
|
|
1333
|
+
if (!existsSync(resolveProjectLayerFilePath(basePath, file.path))) {
|
|
1334
|
+
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
for (const pack of manifest.packs) {
|
|
1338
|
+
for (const file of [...pack.documents, ...pack.files]) {
|
|
1339
|
+
if (!existsSync(resolveProjectLayerFilePath(basePath, file.path))) {
|
|
1340
|
+
issues.push(issue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1341
|
+
}
|
|
1223
1342
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1343
|
+
}
|
|
1344
|
+
for (const path of collectDocumentPathsFromManifest(manifest)) {
|
|
1345
|
+
const document = readDocumentSafely(basePath, path);
|
|
1346
|
+
if ("code" in document) {
|
|
1347
|
+
issues.push(document);
|
|
1226
1348
|
continue;
|
|
1227
1349
|
}
|
|
1228
|
-
|
|
1350
|
+
issues.push(...compareContextDocument({ expected: document, indexed: contextMap.get(path) }));
|
|
1229
1351
|
}
|
|
1230
|
-
return
|
|
1352
|
+
return { currentSourceHash, issues };
|
|
1231
1353
|
};
|
|
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;
|
|
1354
|
+
var auditProjectLayer = (basePath) => {
|
|
1355
|
+
const diffReport = diffProjectLayer(basePath);
|
|
1356
|
+
let manifest;
|
|
1357
|
+
try {
|
|
1358
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1359
|
+
} catch {
|
|
1360
|
+
return diffReport;
|
|
1246
1361
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
writeFileSync6(filePath, replaceSection(existing, PRETTIER_IGNORE_CONTENT), "utf-8");
|
|
1250
|
-
return;
|
|
1362
|
+
if (!manifest) {
|
|
1363
|
+
return diffReport;
|
|
1251
1364
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1365
|
+
let contextIndex = null;
|
|
1366
|
+
try {
|
|
1367
|
+
contextIndex = readProjectLayerContextIndex(basePath);
|
|
1368
|
+
} catch {
|
|
1369
|
+
contextIndex = null;
|
|
1370
|
+
}
|
|
1371
|
+
const documentPaths = collectDocumentPathsFromManifest(manifest);
|
|
1372
|
+
const documentPathSet = new Set(documentPaths);
|
|
1373
|
+
const contextPathSet = new Set(contextIndex?.documents.map((document) => document.path) ?? []);
|
|
1374
|
+
const issues = [...diffReport.issues];
|
|
1375
|
+
const docsStatusPath = resolveProjectLayerFilePath(basePath, "docs/docs-status.md");
|
|
1376
|
+
if (!existsSync(docsStatusPath)) {
|
|
1377
|
+
issues.push(issue("error", "missing-docs-status", "docs/docs-status.md\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
1378
|
+
return { currentSourceHash: diffReport.currentSourceHash, issues };
|
|
1379
|
+
}
|
|
1380
|
+
let docsStatusEntries = [];
|
|
1381
|
+
try {
|
|
1382
|
+
docsStatusEntries = parseDocsStatusEntries(readFileSync6(docsStatusPath, "utf-8"));
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
const reason = error instanceof Error ? error.message : "unknown error";
|
|
1385
|
+
issues.push(issue("error", "invalid-docs-status", `docs/docs-status.md \uD30C\uC2F1 \uC2E4\uD328: ${reason}`));
|
|
1386
|
+
}
|
|
1387
|
+
const docsStatusMap = new Map(docsStatusEntries.map((entry) => [entry.path, entry]));
|
|
1388
|
+
for (const path of documentPaths) {
|
|
1389
|
+
const document = readDocumentSafely(basePath, path);
|
|
1390
|
+
if ("code" in document) {
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
issues.push(...compareDocsStatusEntry({ expected: document, entry: docsStatusMap.get(path) }));
|
|
1264
1394
|
}
|
|
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}`);
|
|
1395
|
+
for (const entry of docsStatusEntries) {
|
|
1396
|
+
if (!documentPathSet.has(entry.path)) {
|
|
1397
|
+
issues.push(issue("warning", "docs-status-extra-document", `docs-status\uC5D0 manifest \uC678 \uBB38\uC11C\uAC00 \uC788\uC2B5\uB2C8\uB2E4: ${entry.path}`));
|
|
1398
|
+
}
|
|
1275
1399
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
};
|
|
1279
|
-
|
|
1280
|
-
if (params.requested === void 0 || params.requested.length === 0) {
|
|
1281
|
-
return [...params.supported];
|
|
1400
|
+
for (const contextPath of contextPathSet) {
|
|
1401
|
+
if (!documentPathSet.has(contextPath)) {
|
|
1402
|
+
issues.push(issue("warning", "context-extra-document", `context-layer\uC5D0 manifest \uC678 \uBB38\uC11C\uAC00 \uC788\uC2B5\uB2C8\uB2E4: ${contextPath}`));
|
|
1403
|
+
}
|
|
1282
1404
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1405
|
+
return { currentSourceHash: diffReport.currentSourceHash, issues };
|
|
1406
|
+
};
|
|
1407
|
+
function removeManagedProjectFile(basePath, relativePath) {
|
|
1408
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, relativePath);
|
|
1409
|
+
if (!existsSync(absolutePath)) {
|
|
1410
|
+
return { deleted: [], cleaned: [], preserved: [], notFound: [relativePath] };
|
|
1411
|
+
}
|
|
1412
|
+
const content = readFileSync6(absolutePath, "utf-8");
|
|
1413
|
+
if (!hasAiOpsSection(content)) {
|
|
1414
|
+
return { deleted: [], cleaned: [], preserved: [relativePath], notFound: [] };
|
|
1415
|
+
}
|
|
1416
|
+
const stripped = stripAiOpsSection(content);
|
|
1417
|
+
if (stripped.trim().length === 0) {
|
|
1418
|
+
rmSync(absolutePath);
|
|
1419
|
+
return { deleted: [relativePath], cleaned: [], preserved: [], notFound: [] };
|
|
1420
|
+
}
|
|
1421
|
+
writeFileSync4(absolutePath, stripped, "utf-8");
|
|
1422
|
+
return { deleted: [], cleaned: [relativePath], preserved: [], notFound: [] };
|
|
1423
|
+
}
|
|
1424
|
+
var removeCreateOnlyProjectFile = (basePath, file) => {
|
|
1425
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1426
|
+
if (!existsSync(absolutePath)) {
|
|
1427
|
+
return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
|
|
1428
|
+
}
|
|
1429
|
+
const content = readFileSync6(absolutePath, "utf-8").trimEnd();
|
|
1430
|
+
const currentHash = computeHash([content]);
|
|
1431
|
+
if (file.created && currentHash === file.templateHash) {
|
|
1432
|
+
rmSync(absolutePath);
|
|
1433
|
+
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1434
|
+
}
|
|
1435
|
+
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
1436
|
+
};
|
|
1437
|
+
var removePackOwnedFile = (basePath, file) => {
|
|
1438
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1439
|
+
if (!existsSync(absolutePath)) {
|
|
1440
|
+
return { deleted: [], cleaned: [], preserved: [], notFound: [file.path] };
|
|
1441
|
+
}
|
|
1442
|
+
const currentHash = computeHash([readFileSync6(absolutePath, "utf-8").trimEnd()]);
|
|
1443
|
+
if (currentHash === file.sourceHash) {
|
|
1444
|
+
rmSync(absolutePath);
|
|
1445
|
+
return { deleted: [file.path], cleaned: [], preserved: [], notFound: [] };
|
|
1446
|
+
}
|
|
1447
|
+
return { deleted: [], cleaned: [], preserved: [file.path], notFound: [] };
|
|
1448
|
+
};
|
|
1449
|
+
var mergeRemoveResults = (results) => ({
|
|
1450
|
+
deleted: results.flatMap((result) => result.deleted),
|
|
1451
|
+
cleaned: results.flatMap((result) => result.cleaned),
|
|
1452
|
+
preserved: results.flatMap((result) => result.preserved),
|
|
1453
|
+
notFound: results.flatMap((result) => result.notFound)
|
|
1454
|
+
});
|
|
1455
|
+
var removeEmptyDirs = (basePath, relativePaths) => {
|
|
1456
|
+
const dirs = [...new Set(relativePaths.map(toRelativeDir).filter((dir) => dir !== "."))].sort(
|
|
1457
|
+
(a, b) => b.length - a.length
|
|
1458
|
+
);
|
|
1459
|
+
for (const dir of [...dirs, ".ai-ops"]) {
|
|
1460
|
+
const absoluteDir = resolveProjectLayerFilePath(basePath, dir);
|
|
1461
|
+
if (!existsSync(absoluteDir)) continue;
|
|
1462
|
+
try {
|
|
1463
|
+
if (readdirSync3(absoluteDir).length === 0) {
|
|
1464
|
+
rmSync(absoluteDir, { recursive: true });
|
|
1465
|
+
}
|
|
1466
|
+
} catch {
|
|
1467
|
+
}
|
|
1287
1468
|
}
|
|
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
1469
|
};
|
|
1304
|
-
var
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1470
|
+
var uninstallProjectLayer = (basePath, manifest) => {
|
|
1471
|
+
const managedResults = manifest.managed_files.map((file) => removeManagedProjectFile(basePath, file.path));
|
|
1472
|
+
const projectResults = manifest.project_files.map((file) => removeCreateOnlyProjectFile(basePath, file));
|
|
1473
|
+
const packResults = manifest.packs.flatMap(
|
|
1474
|
+
(pack) => [...pack.documents, ...pack.files].map((file) => removePackOwnedFile(basePath, file))
|
|
1475
|
+
);
|
|
1476
|
+
const stateFiles = [PROJECT_LAYER_CONTEXT_INDEX_RELATIVE_PATH, PROJECT_LAYER_MANIFEST_RELATIVE_PATH];
|
|
1477
|
+
for (const stateFile of stateFiles) {
|
|
1478
|
+
rmSync(resolveProjectLayerFilePath(basePath, stateFile), { force: true });
|
|
1479
|
+
}
|
|
1480
|
+
const result = mergeRemoveResults([...managedResults, ...projectResults, ...packResults]);
|
|
1481
|
+
removeEmptyDirs(basePath, [...result.deleted, ...stateFiles]);
|
|
1482
|
+
return result;
|
|
1311
1483
|
};
|
|
1312
1484
|
|
|
1313
|
-
// src/
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1485
|
+
// src/core/pack.ts
|
|
1486
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync5, readFileSync as readFileSync7, readdirSync as readdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
1487
|
+
import { dirname as dirname7, isAbsolute as isAbsolute2, join as join11, relative as relative2, resolve as resolve6 } from "path";
|
|
1488
|
+
var PACK_REGISTRY_FILENAME = "pack-registry.json";
|
|
1489
|
+
var SPEC_LIFECYCLE_PACK_ID = "spec-lifecycle";
|
|
1490
|
+
var PACK_INSTALL_ROOT = "docs/specs/";
|
|
1491
|
+
var RESERVED_DOCUMENT_WARNINGS2 = [
|
|
1492
|
+
"\uD310\uB2E8 \uADFC\uAC70\uB85C \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694",
|
|
1493
|
+
"Do not use this document as current decision-making evidence"
|
|
1318
1494
|
];
|
|
1319
|
-
var
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
}
|
|
1495
|
+
var DEFAULT_PACKS_DIR = join11(COMPILER_DATA_DIR, "packs");
|
|
1496
|
+
var includesReservedDocumentWarning2 = (content) => RESERVED_DOCUMENT_WARNINGS2.some((warning) => content.includes(warning));
|
|
1497
|
+
var readPackCatalog = (packsDir) => PackCatalogSchema.parse(JSON.parse(readFileSync7(join11(packsDir, PACK_REGISTRY_FILENAME), "utf-8")));
|
|
1498
|
+
var assertPackInstallPath = (path) => {
|
|
1499
|
+
if (!isSafeProjectLayerPath(path) || !path.startsWith(PACK_INSTALL_ROOT)) {
|
|
1500
|
+
throw new Error(`Unsafe pack path: ${path}`);
|
|
1501
|
+
}
|
|
1326
1502
|
};
|
|
1327
|
-
var
|
|
1328
|
-
|
|
1329
|
-
const
|
|
1330
|
-
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1503
|
+
var readPackSourceFiles = (packDir) => {
|
|
1504
|
+
const files = [];
|
|
1505
|
+
const walk = (relativeDir = "") => {
|
|
1506
|
+
const absoluteDir = relativeDir.length > 0 ? join11(packDir, relativeDir) : packDir;
|
|
1507
|
+
const entries = readdirSync4(absoluteDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1508
|
+
for (const entry of entries) {
|
|
1509
|
+
const nextRelativePath = relativeDir.length > 0 ? join11(relativeDir, entry.name) : entry.name;
|
|
1510
|
+
if (entry.isDirectory()) {
|
|
1511
|
+
walk(nextRelativePath);
|
|
1512
|
+
continue;
|
|
1513
|
+
}
|
|
1514
|
+
assertPackInstallPath(nextRelativePath);
|
|
1515
|
+
const content = readFileSync7(join11(packDir, nextRelativePath), "utf-8");
|
|
1516
|
+
files.push({
|
|
1517
|
+
path: nextRelativePath,
|
|
1518
|
+
content,
|
|
1519
|
+
sourceHash: computeHash([content.trimEnd()])
|
|
1336
1520
|
});
|
|
1337
|
-
continue;
|
|
1338
1521
|
}
|
|
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));
|
|
1522
|
+
};
|
|
1523
|
+
walk();
|
|
1524
|
+
return files;
|
|
1348
1525
|
};
|
|
1349
|
-
var
|
|
1350
|
-
|
|
1351
|
-
const
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1526
|
+
var splitPackSourceFiles = (files) => {
|
|
1527
|
+
const documents = [];
|
|
1528
|
+
const regularFiles = [];
|
|
1529
|
+
for (const file of files) {
|
|
1530
|
+
if (!file.path.endsWith(".md")) {
|
|
1531
|
+
regularFiles.push(file);
|
|
1355
1532
|
continue;
|
|
1356
1533
|
}
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1534
|
+
const { frontmatter } = parseMarkdownFrontmatter(file.content);
|
|
1535
|
+
const parsed = ProjectLayerFrontmatterSchema.parse(frontmatter);
|
|
1536
|
+
if (parsed.status === "Reserved" && !includesReservedDocumentWarning2(file.content)) {
|
|
1537
|
+
throw new Error(`Reserved pack document must include warning text: ${file.path}`);
|
|
1360
1538
|
}
|
|
1361
|
-
|
|
1362
|
-
const availableTools = installedGlobalSkill ? supportedRequestedTools.filter((toolId) => installedGlobalSkill.tools.includes(toolId)) : [];
|
|
1363
|
-
const requestedTools = subtractSkillTools({
|
|
1364
|
-
requested: supportedRequestedTools,
|
|
1365
|
-
installed: availableTools
|
|
1366
|
-
});
|
|
1367
|
-
if (requestedTools.length === 0) {
|
|
1368
|
-
globalSkills.push({
|
|
1369
|
-
skill,
|
|
1370
|
-
availableTools
|
|
1371
|
-
});
|
|
1372
|
-
continue;
|
|
1373
|
-
}
|
|
1374
|
-
installableSkills.push({
|
|
1375
|
-
skill,
|
|
1376
|
-
requestedTools,
|
|
1377
|
-
globalTools: availableTools
|
|
1378
|
-
});
|
|
1539
|
+
documents.push(file);
|
|
1379
1540
|
}
|
|
1380
|
-
return {
|
|
1381
|
-
globalSkills,
|
|
1382
|
-
installableSkills
|
|
1383
|
-
};
|
|
1541
|
+
return { documents, files: regularFiles };
|
|
1384
1542
|
};
|
|
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`);
|
|
1543
|
+
var loadAllPacks = (packsDir) => {
|
|
1544
|
+
const catalog = readPackCatalog(packsDir);
|
|
1545
|
+
const entries = [...catalog.packs].sort((a, b) => a.id.localeCompare(b.id));
|
|
1546
|
+
return entries.map((entry) => {
|
|
1547
|
+
if (entry.id !== SPEC_LIFECYCLE_PACK_ID) {
|
|
1548
|
+
throw new Error(`Unsupported pack id: ${entry.id}`);
|
|
1549
|
+
}
|
|
1550
|
+
const packDir = resolve6(packsDir, entry.source_path);
|
|
1551
|
+
const relativeFromPacks = relative2(resolve6(packsDir), packDir);
|
|
1552
|
+
if (relativeFromPacks.length === 0 || relativeFromPacks.startsWith("..") || isAbsolute2(relativeFromPacks)) {
|
|
1553
|
+
throw new Error(`Pack source path escapes packs dir: ${entry.source_path}`);
|
|
1554
|
+
}
|
|
1555
|
+
const files = readPackSourceFiles(packDir);
|
|
1556
|
+
const split = splitPackSourceFiles(files);
|
|
1413
1557
|
return {
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1558
|
+
id: entry.id,
|
|
1559
|
+
sourceHash: computeHash(files.map((file) => `${file.path}:${file.content}`).sort()),
|
|
1560
|
+
documents: split.documents,
|
|
1561
|
+
files: split.files
|
|
1418
1562
|
};
|
|
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
1563
|
});
|
|
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
|
-
};
|
|
1442
|
-
var selectInitSkillScope = async () => {
|
|
1443
|
-
const scope = await p3.select({
|
|
1444
|
-
message: "\uC120\uD0DD\uB41C skills\uB97C \uC5B4\uB514\uC5D0 \uC124\uCE58\uD560\uAE4C\uC694?",
|
|
1445
|
-
options: [
|
|
1446
|
-
{ value: "user", label: "user (global)", hint: "\uAE30\uBCF8\uAC12. \uC5EC\uB7EC \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC7AC\uC0AC\uC6A9" },
|
|
1447
|
-
{ value: "project", label: "project", hint: "\uD604\uC7AC \uD504\uB85C\uC81D\uD2B8\uC5D0\uB9CC \uC124\uCE58" }
|
|
1448
|
-
]
|
|
1449
|
-
});
|
|
1450
|
-
return p3.isCancel(scope) ? null : scope;
|
|
1451
1564
|
};
|
|
1452
|
-
var
|
|
1453
|
-
const
|
|
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
|
-
|
|
1565
|
+
var resolvePackById = (packsDir, packId) => {
|
|
1566
|
+
const pack = loadAllPacks(packsDir).find((candidate) => candidate.id === packId);
|
|
1567
|
+
if (!pack) {
|
|
1568
|
+
throw new Error(`Unknown pack: ${packId}`);
|
|
1569
|
+
}
|
|
1570
|
+
return pack;
|
|
1571
|
+
};
|
|
1572
|
+
var serializePackFileContent = (content) => content.length === 0 ? "" : content.trimEnd() + "\n";
|
|
1573
|
+
var readProjectFileHash = (basePath, relativePath) => computeHash([readFileSync7(resolveProjectLayerFilePath(basePath, relativePath), "utf-8").trimEnd()]);
|
|
1574
|
+
var writePackFile = (basePath, file) => {
|
|
1575
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1576
|
+
mkdirSync5(dirname7(absolutePath), { recursive: true });
|
|
1577
|
+
writeFileSync5(absolutePath, serializePackFileContent(file.content), "utf-8");
|
|
1578
|
+
};
|
|
1579
|
+
var buildPackFileRecords = (files) => files.map((file) => ({
|
|
1580
|
+
path: file.path,
|
|
1581
|
+
sourceHash: file.sourceHash
|
|
1582
|
+
}));
|
|
1583
|
+
var buildPackRecord = (params) => ({
|
|
1584
|
+
id: params.pack.id,
|
|
1585
|
+
sourceHash: params.pack.sourceHash,
|
|
1586
|
+
documents: buildPackFileRecords(params.pack.documents),
|
|
1587
|
+
files: buildPackFileRecords(params.pack.files),
|
|
1588
|
+
installedAt: params.installedAt
|
|
1589
|
+
});
|
|
1590
|
+
var applyPackSourceFiles = (params) => {
|
|
1591
|
+
const written = [];
|
|
1592
|
+
const refreshed = [];
|
|
1593
|
+
const preserved = [];
|
|
1594
|
+
const deleted = [];
|
|
1595
|
+
const notFound = [];
|
|
1596
|
+
const sourceFiles = [...params.pack.documents, ...params.pack.files];
|
|
1597
|
+
const sourceByPath = new Map(sourceFiles.map((file) => [file.path, file]));
|
|
1598
|
+
const previousByPath = new Map(
|
|
1599
|
+
[...params.previousRecord?.documents ?? [], ...params.previousRecord?.files ?? []].map((file) => [
|
|
1600
|
+
file.path,
|
|
1601
|
+
file
|
|
1602
|
+
])
|
|
1603
|
+
);
|
|
1604
|
+
for (const file of sourceFiles) {
|
|
1605
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, file.path);
|
|
1606
|
+
const previous = previousByPath.get(file.path);
|
|
1607
|
+
if (!existsSync2(absolutePath)) {
|
|
1608
|
+
writePackFile(params.basePath, file);
|
|
1609
|
+
written.push(file.path);
|
|
1610
|
+
continue;
|
|
1481
1611
|
}
|
|
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
|
-
}
|
|
1612
|
+
if (previous === void 0) {
|
|
1613
|
+
preserved.push(file.path);
|
|
1614
|
+
continue;
|
|
1526
1615
|
}
|
|
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
|
-
}
|
|
1616
|
+
const currentHash = readProjectFileHash(params.basePath, file.path);
|
|
1617
|
+
if (currentHash !== previous.sourceHash) {
|
|
1618
|
+
preserved.push(file.path);
|
|
1619
|
+
continue;
|
|
1576
1620
|
}
|
|
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
|
-
}
|
|
1621
|
+
if (currentHash !== file.sourceHash) {
|
|
1622
|
+
writePackFile(params.basePath, file);
|
|
1623
|
+
refreshed.push(file.path);
|
|
1596
1624
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1625
|
+
}
|
|
1626
|
+
for (const previous of previousByPath.values()) {
|
|
1627
|
+
if (sourceByPath.has(previous.path)) {
|
|
1628
|
+
continue;
|
|
1599
1629
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1630
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, previous.path);
|
|
1631
|
+
if (!existsSync2(absolutePath)) {
|
|
1632
|
+
notFound.push(previous.path);
|
|
1633
|
+
continue;
|
|
1602
1634
|
}
|
|
1603
|
-
if (
|
|
1604
|
-
|
|
1635
|
+
if (readProjectFileHash(params.basePath, previous.path) === previous.sourceHash) {
|
|
1636
|
+
rmSync2(absolutePath);
|
|
1637
|
+
deleted.push(previous.path);
|
|
1638
|
+
} else {
|
|
1639
|
+
preserved.push(previous.path);
|
|
1605
1640
|
}
|
|
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")}`);
|
|
1641
|
+
}
|
|
1642
|
+
return { written, refreshed, preserved, deleted, notFound };
|
|
1643
|
+
};
|
|
1644
|
+
var removePackFiles = (basePath, record) => {
|
|
1645
|
+
const deleted = [];
|
|
1646
|
+
const preserved = [];
|
|
1647
|
+
const notFound = [];
|
|
1648
|
+
for (const file of [...record.documents, ...record.files]) {
|
|
1649
|
+
const absolutePath = resolveProjectLayerFilePath(basePath, file.path);
|
|
1650
|
+
if (!existsSync2(absolutePath)) {
|
|
1651
|
+
notFound.push(file.path);
|
|
1652
|
+
continue;
|
|
1638
1653
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1654
|
+
if (readProjectFileHash(basePath, file.path) === file.sourceHash) {
|
|
1655
|
+
rmSync2(absolutePath);
|
|
1656
|
+
deleted.push(file.path);
|
|
1657
|
+
} else {
|
|
1658
|
+
preserved.push(file.path);
|
|
1643
1659
|
}
|
|
1644
|
-
p3.outro("ai-ops init \uC644\uB8CC");
|
|
1645
|
-
} finally {
|
|
1646
|
-
process.off("SIGINT", handleSigint);
|
|
1647
1660
|
}
|
|
1661
|
+
return { written: [], refreshed: [], preserved, deleted, notFound };
|
|
1648
1662
|
};
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
const
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1663
|
+
var removeEmptyDirs2 = (basePath, relativePaths) => {
|
|
1664
|
+
const dirs = [...new Set(relativePaths.map((path) => dirname7(path)).filter((dir) => dir !== "."))].sort(
|
|
1665
|
+
(a, b) => b.length - a.length
|
|
1666
|
+
);
|
|
1667
|
+
for (const dir of dirs) {
|
|
1668
|
+
const absoluteDir = resolveProjectLayerFilePath(basePath, dir);
|
|
1669
|
+
if (!existsSync2(absoluteDir)) {
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
try {
|
|
1673
|
+
if (readdirSync4(absoluteDir).length === 0) {
|
|
1674
|
+
rmSync2(absoluteDir, { recursive: true });
|
|
1675
|
+
}
|
|
1676
|
+
} catch {
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
var requireProjectLayerManifest = (basePath) => {
|
|
1681
|
+
const manifest = readProjectLayerManifest(basePath);
|
|
1657
1682
|
if (!manifest) {
|
|
1658
|
-
|
|
1659
|
-
process.exit(1);
|
|
1683
|
+
throw new Error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1660
1684
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1685
|
+
return manifest;
|
|
1686
|
+
};
|
|
1687
|
+
var upsertPackRecord = (manifest, record, generatedAt) => ProjectLayerManifestSchema.parse({
|
|
1688
|
+
...manifest,
|
|
1689
|
+
packs: [...manifest.packs.filter((pack) => pack.id !== record.id), record],
|
|
1690
|
+
cliVersion: getCliVersion(),
|
|
1691
|
+
generatedAt
|
|
1692
|
+
});
|
|
1693
|
+
var removePackRecord = (manifest, packId, generatedAt) => ProjectLayerManifestSchema.parse({
|
|
1694
|
+
...manifest,
|
|
1695
|
+
packs: manifest.packs.filter((pack) => pack.id !== packId),
|
|
1696
|
+
cliVersion: getCliVersion(),
|
|
1697
|
+
generatedAt
|
|
1698
|
+
});
|
|
1699
|
+
var writeManifestWithDerivedState = (params) => {
|
|
1700
|
+
const derived = refreshProjectLayerDerivedState({
|
|
1701
|
+
basePath: params.basePath,
|
|
1702
|
+
manifest: params.manifest,
|
|
1703
|
+
generatedAt: params.generatedAt
|
|
1673
1704
|
});
|
|
1674
|
-
|
|
1705
|
+
writeProjectLayerManifest(params.basePath, derived.manifest);
|
|
1706
|
+
return derived;
|
|
1707
|
+
};
|
|
1708
|
+
var installProjectLayerPack = (params) => {
|
|
1709
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1710
|
+
const previousRecord = manifest.packs.find((pack2) => pack2.id === params.packId);
|
|
1711
|
+
if (previousRecord) {
|
|
1712
|
+
return updateProjectLayerPack(params);
|
|
1713
|
+
}
|
|
1714
|
+
const pack = resolvePackById(params.packsDir ?? DEFAULT_PACKS_DIR, params.packId);
|
|
1715
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1716
|
+
const applyResult = applyPackSourceFiles({ basePath: params.basePath, pack, previousRecord: null });
|
|
1717
|
+
const nextManifest = upsertPackRecord(manifest, buildPackRecord({ pack, installedAt }), installedAt);
|
|
1718
|
+
const derived = writeManifestWithDerivedState({
|
|
1719
|
+
basePath: params.basePath,
|
|
1720
|
+
manifest: nextManifest,
|
|
1721
|
+
generatedAt: installedAt
|
|
1722
|
+
});
|
|
1723
|
+
return { ...applyResult, manifest: derived.manifest, contextIndex: derived.contextIndex };
|
|
1724
|
+
};
|
|
1725
|
+
var updateProjectLayerPack = (params) => {
|
|
1726
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1727
|
+
const previousRecord = manifest.packs.find((pack2) => pack2.id === params.packId);
|
|
1728
|
+
if (!previousRecord) {
|
|
1729
|
+
throw new Error(`\uC124\uCE58\uB41C pack\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${params.packId}`);
|
|
1730
|
+
}
|
|
1731
|
+
const pack = resolvePackById(params.packsDir ?? DEFAULT_PACKS_DIR, params.packId);
|
|
1732
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1733
|
+
const applyResult = applyPackSourceFiles({ basePath: params.basePath, pack, previousRecord });
|
|
1734
|
+
const nextManifest = upsertPackRecord(
|
|
1675
1735
|
manifest,
|
|
1676
|
-
|
|
1736
|
+
buildPackRecord({ pack, installedAt: previousRecord.installedAt }),
|
|
1737
|
+
generatedAt
|
|
1738
|
+
);
|
|
1739
|
+
const derived = writeManifestWithDerivedState({
|
|
1740
|
+
basePath: params.basePath,
|
|
1741
|
+
manifest: nextManifest,
|
|
1742
|
+
generatedAt
|
|
1743
|
+
});
|
|
1744
|
+
removeEmptyDirs2(params.basePath, applyResult.deleted);
|
|
1745
|
+
return { ...applyResult, manifest: derived.manifest, contextIndex: derived.contextIndex };
|
|
1746
|
+
};
|
|
1747
|
+
var uninstallProjectLayerPack = (params) => {
|
|
1748
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1749
|
+
const previousRecord = manifest.packs.find((pack) => pack.id === params.packId);
|
|
1750
|
+
if (!previousRecord) {
|
|
1751
|
+
throw new Error(`\uC124\uCE58\uB41C pack\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${params.packId}`);
|
|
1752
|
+
}
|
|
1753
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1754
|
+
const applyResult = removePackFiles(params.basePath, previousRecord);
|
|
1755
|
+
const nextManifest = removePackRecord(manifest, params.packId, generatedAt);
|
|
1756
|
+
const derived = writeManifestWithDerivedState({
|
|
1757
|
+
basePath: params.basePath,
|
|
1758
|
+
manifest: nextManifest,
|
|
1759
|
+
generatedAt
|
|
1677
1760
|
});
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1761
|
+
removeEmptyDirs2(params.basePath, applyResult.deleted);
|
|
1762
|
+
return { ...applyResult, manifest: derived.manifest, contextIndex: derived.contextIndex };
|
|
1763
|
+
};
|
|
1764
|
+
var packIssue = (level, code, message) => ({
|
|
1765
|
+
level,
|
|
1766
|
+
code,
|
|
1767
|
+
message
|
|
1768
|
+
});
|
|
1769
|
+
var diffProjectLayerPack = (params) => {
|
|
1770
|
+
const manifest = requireProjectLayerManifest(params.basePath);
|
|
1771
|
+
const targets = params.packId ? manifest.packs.filter((pack) => pack.id === params.packId) : manifest.packs;
|
|
1772
|
+
const issues = [];
|
|
1773
|
+
if (targets.length === 0) {
|
|
1774
|
+
return { issues: [packIssue("warning", "missing-pack", "\uBE44\uAD50\uD560 \uC124\uCE58\uB41C pack\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.")] };
|
|
1775
|
+
}
|
|
1776
|
+
for (const record of targets) {
|
|
1777
|
+
const pack = resolvePackById(params.packsDir ?? DEFAULT_PACKS_DIR, record.id);
|
|
1778
|
+
if (record.sourceHash !== pack.sourceHash) {
|
|
1779
|
+
issues.push(
|
|
1780
|
+
packIssue("warning", "pack-source-hash-drift", `${record.id} sourceHash \uBCC0\uACBD: ${record.sourceHash} -> ${pack.sourceHash}`)
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
for (const file of [...record.documents, ...record.files]) {
|
|
1784
|
+
const absolutePath = resolveProjectLayerFilePath(params.basePath, file.path);
|
|
1785
|
+
if (!existsSync2(absolutePath)) {
|
|
1786
|
+
issues.push(packIssue("error", "missing-file", `\uD30C\uC77C \uC5C6\uC74C: ${file.path}`));
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
return { issues };
|
|
1791
|
+
};
|
|
1792
|
+
|
|
1793
|
+
// src/core/uninstall-plan.ts
|
|
1794
|
+
import { join as join12 } from "path";
|
|
1795
|
+
|
|
1796
|
+
// src/lib/paths.ts
|
|
1797
|
+
import { join as join13 } from "path";
|
|
1798
|
+
var resolveSkillsDir = () => join13(COMPILER_DATA_DIR, "skills");
|
|
1799
|
+
var resolveSubagentsDir = () => join13(COMPILER_DATA_DIR, "subagents");
|
|
1800
|
+
var resolvePacksDir = () => join13(COMPILER_DATA_DIR, "packs");
|
|
1801
|
+
var resolveBasePath = () => process.cwd();
|
|
1802
|
+
var resolveUserBasePath = () => {
|
|
1803
|
+
const userBasePath = process.env.AI_OPS_HOME ?? process.env.HOME;
|
|
1804
|
+
if (!userBasePath) {
|
|
1805
|
+
throw new Error("AI_OPS_HOME or HOME is required for global asset commands");
|
|
1806
|
+
}
|
|
1807
|
+
return userBasePath;
|
|
1808
|
+
};
|
|
1809
|
+
|
|
1810
|
+
// src/commands/project-layer-errors.ts
|
|
1811
|
+
import * as p from "@clack/prompts";
|
|
1812
|
+
import { ZodError } from "zod";
|
|
1813
|
+
var formatProjectLayerCommandError = (error) => {
|
|
1814
|
+
if (error instanceof ZodError) {
|
|
1815
|
+
return error.issues.map((issue2) => {
|
|
1816
|
+
const path = issue2.path.length > 0 ? issue2.path.join(".") : "manifest";
|
|
1817
|
+
return `${path}: ${issue2.message}`;
|
|
1818
|
+
}).join("; ");
|
|
1819
|
+
}
|
|
1820
|
+
return error instanceof Error ? error.message : "unknown error";
|
|
1821
|
+
};
|
|
1822
|
+
var reportInvalidProjectLayerManifest = (params) => {
|
|
1823
|
+
const reason = formatProjectLayerCommandError(params.error);
|
|
1824
|
+
p.log.error(`[invalid-manifest] .ai-ops/manifest.json \uD30C\uC2F1 \uC2E4\uD328: ${reason}`);
|
|
1825
|
+
process.exitCode = 1;
|
|
1826
|
+
p.outro(params.outro);
|
|
1827
|
+
};
|
|
1828
|
+
var reportProjectLayerApplyError = (params) => {
|
|
1829
|
+
const reason = formatProjectLayerCommandError(params.error);
|
|
1830
|
+
p.log.error(`[project-layer-apply] project operating layer \uC801\uC6A9 \uC2E4\uD328: ${reason}`);
|
|
1831
|
+
process.exitCode = 1;
|
|
1832
|
+
p.outro(params.outro);
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1835
|
+
// src/commands/init.ts
|
|
1836
|
+
var TOOL_OPTIONS = [
|
|
1837
|
+
{ value: "codex", label: "Codex" },
|
|
1838
|
+
{ value: "gemini", label: "Gemini CLI" },
|
|
1839
|
+
{ value: "claude-code", label: "Claude Code" }
|
|
1840
|
+
];
|
|
1841
|
+
var promptTools = async () => {
|
|
1842
|
+
const selectedTools = await p2.multiselect({
|
|
1843
|
+
message: "AI \uB3C4\uAD6C adapter\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1844
|
+
options: TOOL_OPTIONS,
|
|
1845
|
+
initialValues: TOOL_OPTIONS.map((option) => option.value),
|
|
1846
|
+
required: true
|
|
1683
1847
|
});
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1848
|
+
return p2.isCancel(selectedTools) ? null : resolveProjectLayerTools(selectedTools);
|
|
1849
|
+
};
|
|
1850
|
+
var initCommand = async (opts = {}) => {
|
|
1851
|
+
p2.intro("ai-ops init");
|
|
1852
|
+
const basePath = resolveBasePath();
|
|
1853
|
+
const tools = opts.tool && opts.tool.length > 0 ? resolveProjectLayerTools(opts.tool) : await promptTools();
|
|
1854
|
+
if (tools === null) {
|
|
1855
|
+
p2.cancel("\uCDE8\uC18C\uB428");
|
|
1856
|
+
process.exit(0);
|
|
1857
|
+
}
|
|
1858
|
+
let previousManifest;
|
|
1859
|
+
try {
|
|
1860
|
+
previousManifest = readProjectLayerManifest(basePath);
|
|
1861
|
+
} catch (error) {
|
|
1862
|
+
reportInvalidProjectLayerManifest({ error, outro: "ai-ops init \uC2E4\uD328" });
|
|
1687
1863
|
return;
|
|
1688
1864
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
1696
|
-
skill,
|
|
1697
|
-
requestedTools,
|
|
1698
|
-
scope: "project"
|
|
1865
|
+
let result;
|
|
1866
|
+
try {
|
|
1867
|
+
result = installProjectLayer({
|
|
1868
|
+
basePath,
|
|
1869
|
+
tools,
|
|
1870
|
+
previousManifest
|
|
1699
1871
|
});
|
|
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");
|
|
1872
|
+
} catch (error) {
|
|
1873
|
+
reportProjectLayerApplyError({ error, outro: "ai-ops init \uC2E4\uD328" });
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
p2.log.success(`project operating layer \uC124\uCE58 \uC644\uB8CC: ${result.manifest.managed_files.length + result.manifest.project_files.length}\uAC1C \uD30C\uC77C`);
|
|
1877
|
+
p2.log.info(`\uB3C4\uAD6C adapter: ${result.manifest.tools.join(", ")}`);
|
|
1878
|
+
if (result.appended.length > 0) {
|
|
1879
|
+
p2.log.info(`\uAE30\uC874 \uD30C\uC77C\uC5D0 managed section \uCD94\uAC00:
|
|
1880
|
+
${result.appended.map((file) => ` ${file}`).join("\n")}`);
|
|
1881
|
+
}
|
|
1882
|
+
if (result.refreshedProjectFiles.length > 0) {
|
|
1883
|
+
p2.log.info(`unmodified project-owned \uD30C\uC77C \uAC31\uC2E0:
|
|
1884
|
+
${result.refreshedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1885
|
+
}
|
|
1886
|
+
if (result.preservedProjectFiles.length > 0) {
|
|
1887
|
+
p2.log.info(`\uAE30\uC874 project-owned \uD30C\uC77C \uBCF4\uC874:
|
|
1888
|
+
${result.preservedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1889
|
+
}
|
|
1890
|
+
p2.outro("ai-ops init \uC644\uB8CC");
|
|
1758
1891
|
};
|
|
1759
1892
|
|
|
1760
|
-
// src/commands/
|
|
1761
|
-
import * as
|
|
1762
|
-
var
|
|
1893
|
+
// src/commands/update.ts
|
|
1894
|
+
import * as p3 from "@clack/prompts";
|
|
1895
|
+
var updateCommand = async (opts) => {
|
|
1763
1896
|
const basePath = resolveBasePath();
|
|
1764
|
-
|
|
1765
|
-
|
|
1897
|
+
p3.intro("ai-ops update");
|
|
1898
|
+
let manifest;
|
|
1899
|
+
try {
|
|
1900
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
reportInvalidProjectLayerManifest({ error, outro: "ai-ops update \uC2E4\uD328" });
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1766
1905
|
if (!manifest) {
|
|
1767
|
-
|
|
1906
|
+
p3.log.error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
1768
1907
|
process.exit(1);
|
|
1769
1908
|
}
|
|
1770
|
-
const
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
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
|
-
}
|
|
1909
|
+
const diffReport = diffProjectLayer(basePath);
|
|
1910
|
+
if (diffReport.issues.length === 0 && !opts.force) {
|
|
1911
|
+
p3.log.info("\uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
1912
|
+
p3.outro("ai-ops update \uC644\uB8CC");
|
|
1913
|
+
return;
|
|
1914
|
+
}
|
|
1915
|
+
let result;
|
|
1916
|
+
try {
|
|
1917
|
+
result = updateProjectLayer({ basePath, manifest });
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
reportProjectLayerApplyError({ error, outro: "ai-ops update \uC2E4\uD328" });
|
|
1920
|
+
return;
|
|
1811
1921
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1922
|
+
p3.log.success(`managed \uD30C\uC77C \uAC31\uC2E0: ${result.manifest.managed_files.length}\uAC1C`);
|
|
1923
|
+
if (result.createdProjectFiles.length > 0) {
|
|
1924
|
+
p3.log.info(`\uB204\uB77D\uB41C project-owned \uD30C\uC77C \uBCF5\uAD6C:
|
|
1925
|
+
${result.createdProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1815
1926
|
}
|
|
1816
|
-
|
|
1927
|
+
if (result.refreshedProjectFiles.length > 0) {
|
|
1928
|
+
p3.log.info(`unmodified project-owned \uD30C\uC77C \uAC31\uC2E0:
|
|
1929
|
+
${result.refreshedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1930
|
+
}
|
|
1931
|
+
if (result.preservedProjectFiles.length > 0) {
|
|
1932
|
+
p3.log.info(`project-owned \uD30C\uC77C \uBCF4\uC874:
|
|
1933
|
+
${result.preservedProjectFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
1934
|
+
}
|
|
1935
|
+
p3.outro("ai-ops update \uC644\uB8CC");
|
|
1817
1936
|
};
|
|
1818
1937
|
|
|
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);
|
|
1938
|
+
// src/commands/diff.ts
|
|
1939
|
+
import * as p4 from "@clack/prompts";
|
|
1940
|
+
var diffCommand = async () => {
|
|
1941
|
+
p4.intro("ai-ops diff");
|
|
1942
|
+
const report = diffProjectLayer(resolveBasePath());
|
|
1943
|
+
if (report.issues.length === 0) {
|
|
1944
|
+
p4.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
1945
|
+
p4.outro("ai-ops diff \uC644\uB8CC");
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
for (const item of report.issues) {
|
|
1949
|
+
const line = `[${item.code}] ${item.message}`;
|
|
1950
|
+
if (item.level === "error") {
|
|
1951
|
+
p4.log.error(line);
|
|
1850
1952
|
} else {
|
|
1851
|
-
|
|
1953
|
+
p4.log.warn(line);
|
|
1852
1954
|
}
|
|
1853
1955
|
}
|
|
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
|
-
}
|
|
1956
|
+
if (report.issues.some((item) => item.level === "error")) {
|
|
1957
|
+
process.exitCode = 1;
|
|
1869
1958
|
}
|
|
1870
|
-
|
|
1959
|
+
p4.outro("ai-ops diff \uC644\uB8CC");
|
|
1871
1960
|
};
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1961
|
+
|
|
1962
|
+
// src/commands/audit.ts
|
|
1963
|
+
import * as p5 from "@clack/prompts";
|
|
1964
|
+
var auditCommand = async () => {
|
|
1965
|
+
p5.intro("ai-ops audit");
|
|
1966
|
+
const report = auditProjectLayer(resolveBasePath());
|
|
1967
|
+
if (report.issues.length === 0) {
|
|
1968
|
+
p5.log.success("audit \uD1B5\uACFC. manifest, context-layer, frontmatter, docs-status\uAC00 \uC77C\uCE58\uD569\uB2C8\uB2E4.");
|
|
1969
|
+
p5.outro("ai-ops audit \uC644\uB8CC");
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
for (const item of report.issues) {
|
|
1973
|
+
const line = `[${item.code}] ${item.message}`;
|
|
1974
|
+
if (item.level === "error") {
|
|
1975
|
+
p5.log.error(line);
|
|
1976
|
+
} else {
|
|
1977
|
+
p5.log.warn(line);
|
|
1878
1978
|
}
|
|
1879
1979
|
}
|
|
1880
|
-
|
|
1980
|
+
if (report.issues.some((item) => item.level === "error")) {
|
|
1981
|
+
process.exitCode = 1;
|
|
1982
|
+
}
|
|
1983
|
+
p5.outro("ai-ops audit \uC644\uB8CC");
|
|
1881
1984
|
};
|
|
1882
1985
|
|
|
1883
1986
|
// src/commands/uninstall.ts
|
|
1884
|
-
|
|
1885
|
-
var uninstallCommand = async () => {
|
|
1987
|
+
import * as p6 from "@clack/prompts";
|
|
1988
|
+
var uninstallCommand = async (opts = {}) => {
|
|
1886
1989
|
const basePath = resolveBasePath();
|
|
1887
|
-
const manifestPath = resolveManifestPath(basePath);
|
|
1888
1990
|
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");
|
|
1991
|
+
let manifest;
|
|
1992
|
+
try {
|
|
1993
|
+
manifest = readProjectLayerManifest(basePath);
|
|
1994
|
+
} catch (error) {
|
|
1995
|
+
reportInvalidProjectLayerManifest({ error, outro: "ai-ops uninstall \uC2E4\uD328" });
|
|
1902
1996
|
return;
|
|
1903
1997
|
}
|
|
1904
|
-
if (
|
|
1905
|
-
p6.log.
|
|
1906
|
-
|
|
1998
|
+
if (!manifest) {
|
|
1999
|
+
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.");
|
|
2000
|
+
process.exitCode = 1;
|
|
2001
|
+
p6.outro("ai-ops uninstall \uC2E4\uD328");
|
|
2002
|
+
return;
|
|
1907
2003
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
2004
|
+
const targetFiles = [
|
|
2005
|
+
...manifest.managed_files.map((file) => file.path),
|
|
2006
|
+
...manifest.project_files.map((file) => file.path),
|
|
2007
|
+
".ai-ops/context-layer.json",
|
|
2008
|
+
".ai-ops/manifest.json"
|
|
2009
|
+
];
|
|
2010
|
+
p6.log.info(`\uCC98\uB9AC \uB300\uC0C1 (${targetFiles.length}\uAC1C):
|
|
2011
|
+
${targetFiles.map((file) => ` ${file}`).join("\n")}`);
|
|
2012
|
+
if (!opts.yes) {
|
|
2013
|
+
const confirmed = await p6.confirm({
|
|
2014
|
+
message: "project operating layer\uB97C \uC81C\uAC70\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
|
|
2015
|
+
initialValue: false
|
|
2016
|
+
});
|
|
2017
|
+
if (p6.isCancel(confirmed) || !confirmed) {
|
|
2018
|
+
p6.cancel("\uCDE8\uC18C\uB428");
|
|
2019
|
+
process.exit(0);
|
|
2020
|
+
}
|
|
1913
2021
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
})
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
process.exit(0);
|
|
2022
|
+
let result;
|
|
2023
|
+
try {
|
|
2024
|
+
result = uninstallProjectLayer(basePath, manifest);
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
reportProjectLayerApplyError({ error, outro: "ai-ops uninstall \uC2E4\uD328" });
|
|
2027
|
+
return;
|
|
1921
2028
|
}
|
|
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
2029
|
if (result.deleted.length > 0) {
|
|
1943
|
-
p6.log.success(`\uC0AD\uC81C \uC644\uB8CC
|
|
1944
|
-
${result.deleted.map((
|
|
2030
|
+
p6.log.success(`\uC0AD\uC81C \uC644\uB8CC:
|
|
2031
|
+
${result.deleted.map((file) => ` ${file}`).join("\n")}`);
|
|
1945
2032
|
}
|
|
1946
2033
|
if (result.cleaned.length > 0) {
|
|
1947
|
-
p6.log.success(
|
|
1948
|
-
|
|
1949
|
-
${result.cleaned.map((f) => ` ${f}`).join("\n")}`
|
|
1950
|
-
);
|
|
2034
|
+
p6.log.success(`managed section \uC81C\uAC70, \uC0AC\uC6A9\uC790 \uB0B4\uC6A9 \uBCF4\uC874:
|
|
2035
|
+
${result.cleaned.map((file) => ` ${file}`).join("\n")}`);
|
|
1951
2036
|
}
|
|
1952
|
-
if (result.
|
|
1953
|
-
p6.log.
|
|
1954
|
-
|
|
1955
|
-
${result.skipped.map((f) => ` ${f}`).join("\n")}`
|
|
1956
|
-
);
|
|
2037
|
+
if (result.preserved.length > 0) {
|
|
2038
|
+
p6.log.info(`\uC218\uC815\uB418\uC5C8\uAC70\uB098 \uAE30\uC874\uC5D0 \uC788\uB358 project-owned \uD30C\uC77C \uBCF4\uC874:
|
|
2039
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
1957
2040
|
}
|
|
1958
2041
|
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
|
-
);
|
|
2042
|
+
p6.log.info(`\uC774\uBBF8 \uC5C6\uC74C:
|
|
2043
|
+
${result.notFound.map((file) => ` ${file}`).join("\n")}`);
|
|
1971
2044
|
}
|
|
1972
|
-
if (settingsMessages.length > 0) {
|
|
1973
|
-
p6.log.success(`\uC124\uC815 \uD30C\uC77C \uCC98\uB9AC:
|
|
1974
|
-
${settingsMessages.map((m) => ` ${m}`).join("\n")}`);
|
|
1975
|
-
}
|
|
1976
|
-
p6.log.success(`manifest \uC0AD\uC81C: ${MANIFEST_FILENAME}`);
|
|
1977
2045
|
p6.outro("ai-ops uninstall \uC644\uB8CC");
|
|
1978
2046
|
};
|
|
1979
2047
|
|
|
1980
2048
|
// src/commands/skill.ts
|
|
1981
2049
|
import * as p7 from "@clack/prompts";
|
|
1982
|
-
import { rmSync as
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
}
|
|
2050
|
+
import { rmSync as rmSync4 } from "fs";
|
|
2051
|
+
|
|
2052
|
+
// src/lib/skill-state.ts
|
|
2053
|
+
var resolveRequestedTools = (params) => {
|
|
2054
|
+
if (params.requested === void 0 || params.requested.length === 0) {
|
|
2055
|
+
return [...params.supported];
|
|
2056
|
+
}
|
|
2057
|
+
const supportedSet = new Set(params.supported);
|
|
2058
|
+
const invalid = params.requested.filter((tool) => !supportedSet.has(tool));
|
|
2059
|
+
if (invalid.length > 0) {
|
|
2060
|
+
throw new Error(`Unsupported tools requested: ${invalid.join(", ")}`);
|
|
2061
|
+
}
|
|
2062
|
+
return [...params.requested];
|
|
2063
|
+
};
|
|
2064
|
+
var TOOL_ORDER2 = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
2065
|
+
var mergeSkillTools = (params) => {
|
|
2066
|
+
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
2067
|
+
return TOOL_ORDER2.filter((tool) => merged.has(tool));
|
|
2068
|
+
};
|
|
2069
|
+
var upsertInstalledSkill = (installedSkills, nextSkill) => {
|
|
2070
|
+
const nextSkillId = resolveCanonicalSkillId(nextSkill.id);
|
|
2071
|
+
const remaining = installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== nextSkillId);
|
|
2072
|
+
return [...remaining, nextSkill];
|
|
2073
|
+
};
|
|
2074
|
+
var removeInstalledSkill = (installedSkills, skillId) => {
|
|
2075
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
2076
|
+
return installedSkills.filter((skill) => resolveCanonicalSkillId(skill.id) !== targetSkillId);
|
|
2077
|
+
};
|
|
2078
|
+
var findInstalledSkill = (installedSkills, skillId) => {
|
|
2079
|
+
const targetSkillId = resolveCanonicalSkillId(skillId);
|
|
2080
|
+
return installedSkills.find((skill) => resolveCanonicalSkillId(skill.id) === targetSkillId);
|
|
2081
|
+
};
|
|
2082
|
+
|
|
2083
|
+
// src/lib/skill-install.ts
|
|
2084
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2085
|
+
import { dirname as dirname8, resolve as resolve7 } from "path";
|
|
2086
|
+
var installSkillPackages = (basePath, packages) => {
|
|
2087
|
+
const writtenRoots = [];
|
|
2088
|
+
for (const skillPackage of packages) {
|
|
2089
|
+
const absRoot = resolve7(basePath, skillPackage.rootDir);
|
|
2090
|
+
if (existsSync3(absRoot)) {
|
|
2091
|
+
rmSync3(absRoot, { recursive: true, force: true });
|
|
2092
|
+
}
|
|
2093
|
+
for (const file of skillPackage.files) {
|
|
2094
|
+
const absPath = resolve7(basePath, file.relativePath);
|
|
2095
|
+
mkdirSync6(dirname8(absPath), { recursive: true });
|
|
2096
|
+
writeFileSync6(absPath, file.content + "\n", "utf-8");
|
|
2097
|
+
}
|
|
2098
|
+
writtenRoots.push(skillPackage.rootDir);
|
|
2099
|
+
}
|
|
2100
|
+
return writtenRoots;
|
|
2101
|
+
};
|
|
2102
|
+
var removeDirectories = (basePath, relativeDirs) => {
|
|
2103
|
+
const removed = [];
|
|
2104
|
+
for (const relativeDir of relativeDirs) {
|
|
2105
|
+
const absPath = resolve7(basePath, relativeDir);
|
|
2106
|
+
if (!existsSync3(absPath)) continue;
|
|
2107
|
+
rmSync3(absPath, { recursive: true, force: true });
|
|
2108
|
+
removed.push(relativeDir);
|
|
2109
|
+
}
|
|
2110
|
+
return removed;
|
|
1989
2111
|
};
|
|
2112
|
+
|
|
2113
|
+
// src/commands/skill.ts
|
|
1990
2114
|
var loadCompilerInputs = () => {
|
|
1991
|
-
const compilerDataDir = resolveCompilerDataDir();
|
|
1992
2115
|
return {
|
|
1993
2116
|
allSkills: loadAllSkills(resolveSkillsDir()),
|
|
1994
|
-
sourceHash: computeSourceHash(compilerDataDir),
|
|
1995
2117
|
cliVersion: getCliVersion()
|
|
1996
2118
|
};
|
|
1997
2119
|
};
|
|
@@ -2003,42 +2125,12 @@ var resolveSkillById = (skills, skillId) => {
|
|
|
2003
2125
|
}
|
|
2004
2126
|
return skill;
|
|
2005
2127
|
};
|
|
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
2128
|
var writeUserSkillState = (params) => {
|
|
2037
2129
|
const registryPath = resolveSkillRegistryPath(params.basePath);
|
|
2038
2130
|
const previous = readSkillRegistry(registryPath);
|
|
2039
2131
|
const skills = params.removeSkillId ? removeInstalledSkill(previous?.skills ?? [], params.removeSkillId) : params.nextSkill ? upsertInstalledSkill(previous?.skills ?? [], params.nextSkill) : previous?.skills ?? [];
|
|
2040
2132
|
if (skills.length === 0) {
|
|
2041
|
-
|
|
2133
|
+
rmSync4(registryPath, { force: true });
|
|
2042
2134
|
return;
|
|
2043
2135
|
}
|
|
2044
2136
|
writeSkillRegistry(registryPath, {
|
|
@@ -2047,20 +2139,14 @@ var writeUserSkillState = (params) => {
|
|
|
2047
2139
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2048
2140
|
});
|
|
2049
2141
|
};
|
|
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
|
-
}
|
|
2142
|
+
var readInstalledSkills = (basePath) => {
|
|
2057
2143
|
return (readSkillRegistry(resolveSkillRegistryPath(basePath))?.skills ?? []).map((installedSkill) => ({
|
|
2058
2144
|
...installedSkill,
|
|
2059
2145
|
id: resolveCanonicalSkillId(installedSkill.id)
|
|
2060
2146
|
}));
|
|
2061
2147
|
};
|
|
2062
2148
|
var installSkill = (params) => {
|
|
2063
|
-
const installedSkills = readInstalledSkills(params.
|
|
2149
|
+
const installedSkills = readInstalledSkills(params.basePath);
|
|
2064
2150
|
const existingInstalledSkill = findInstalledSkill(installedSkills, params.skill.id);
|
|
2065
2151
|
const nextRequestedTools = mergeSkillTools({
|
|
2066
2152
|
existing: existingInstalledSkill?.tools,
|
|
@@ -2068,31 +2154,21 @@ var installSkill = (params) => {
|
|
|
2068
2154
|
});
|
|
2069
2155
|
const { packages, installedSkill } = buildSkillInstallPlan({
|
|
2070
2156
|
skill: params.skill,
|
|
2071
|
-
requestedTools: nextRequestedTools
|
|
2072
|
-
scope: params.scope
|
|
2157
|
+
requestedTools: nextRequestedTools
|
|
2073
2158
|
});
|
|
2074
2159
|
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
|
-
}
|
|
2160
|
+
writeUserSkillState({
|
|
2161
|
+
basePath: params.basePath,
|
|
2162
|
+
cliVersion: params.cliVersion,
|
|
2163
|
+
nextSkill: installedSkill
|
|
2164
|
+
});
|
|
2089
2165
|
return installedSkill;
|
|
2090
2166
|
};
|
|
2091
|
-
var skillListCommand = async (
|
|
2092
|
-
const
|
|
2167
|
+
var skillListCommand = async () => {
|
|
2168
|
+
const basePath = resolveUserBasePath();
|
|
2093
2169
|
const { allSkills } = loadCompilerInputs();
|
|
2094
|
-
const installedSkills = readInstalledSkills(
|
|
2095
|
-
p7.intro(
|
|
2170
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2171
|
+
p7.intro("ai-ops skill list");
|
|
2096
2172
|
const sections = [
|
|
2097
2173
|
{ kind: "reference", title: "reference skills" },
|
|
2098
2174
|
{ kind: "task", title: "task skills" }
|
|
@@ -2100,7 +2176,7 @@ var skillListCommand = async (opts) => {
|
|
|
2100
2176
|
const lines = allSkills.filter((skill) => skill.kind === kind).map((skill) => {
|
|
2101
2177
|
const installed = findInstalledSkill(installedSkills, skill.id);
|
|
2102
2178
|
const suffix = installed ? `installed for ${installed.tools.join(", ")}` : "not installed";
|
|
2103
|
-
return `- ${skill.id}
|
|
2179
|
+
return `- ${skill.id} - ${suffix}`;
|
|
2104
2180
|
});
|
|
2105
2181
|
if (lines.length === 0) {
|
|
2106
2182
|
return null;
|
|
@@ -2112,29 +2188,26 @@ ${lines.join("\n")}`;
|
|
|
2112
2188
|
p7.outro("ai-ops skill list \uC644\uB8CC");
|
|
2113
2189
|
};
|
|
2114
2190
|
var skillInstallCommand = async (skillId, opts) => {
|
|
2115
|
-
const
|
|
2116
|
-
const { allSkills,
|
|
2191
|
+
const basePath = resolveUserBasePath();
|
|
2192
|
+
const { allSkills, cliVersion } = loadCompilerInputs();
|
|
2117
2193
|
const skill = resolveSkillById(allSkills, skillId);
|
|
2118
|
-
assertScopeAllowed(skill, scope);
|
|
2119
2194
|
const requestedTools = resolveRequestedTools({ requested: opts.tool, supported: skill.supported_tools });
|
|
2120
2195
|
p7.intro(`ai-ops skill install ${skillId}`);
|
|
2121
2196
|
const installedSkill = installSkill({
|
|
2122
2197
|
skill,
|
|
2123
2198
|
requestedTools,
|
|
2124
|
-
scope,
|
|
2125
2199
|
basePath,
|
|
2126
|
-
cliVersion
|
|
2127
|
-
sourceHash
|
|
2200
|
+
cliVersion
|
|
2128
2201
|
});
|
|
2129
2202
|
p7.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSkill.id} (${installedSkill.installed_paths.join(", ")})`);
|
|
2130
2203
|
p7.outro("ai-ops skill install \uC644\uB8CC");
|
|
2131
2204
|
};
|
|
2132
|
-
var skillDiffCommand = async (skillId
|
|
2133
|
-
const
|
|
2205
|
+
var skillDiffCommand = async (skillId) => {
|
|
2206
|
+
const basePath = resolveUserBasePath();
|
|
2134
2207
|
const { allSkills } = loadCompilerInputs();
|
|
2135
|
-
const installedSkills = readInstalledSkills(
|
|
2208
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2136
2209
|
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2137
|
-
p7.intro(
|
|
2210
|
+
p7.intro("ai-ops skill diff");
|
|
2138
2211
|
if (targets.length === 0) {
|
|
2139
2212
|
p7.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2140
2213
|
p7.outro("ai-ops skill diff \uC644\uB8CC");
|
|
@@ -2144,8 +2217,7 @@ var skillDiffCommand = async (skillId, opts) => {
|
|
|
2144
2217
|
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2145
2218
|
const { installedSkill: next } = buildSkillInstallPlan({
|
|
2146
2219
|
skill,
|
|
2147
|
-
requestedTools: installedSkill.tools
|
|
2148
|
-
scope
|
|
2220
|
+
requestedTools: installedSkill.tools
|
|
2149
2221
|
});
|
|
2150
2222
|
const changed = next.sourceHash !== installedSkill.sourceHash;
|
|
2151
2223
|
return `- ${installedSkill.id}: ${changed ? "changed" : "up-to-date"} (${installedSkill.sourceHash} -> ${next.sourceHash})`;
|
|
@@ -2153,12 +2225,12 @@ var skillDiffCommand = async (skillId, opts) => {
|
|
|
2153
2225
|
p7.log.info(lines.join("\n"));
|
|
2154
2226
|
p7.outro("ai-ops skill diff \uC644\uB8CC");
|
|
2155
2227
|
};
|
|
2156
|
-
var skillUpdateCommand = async (skillId
|
|
2157
|
-
const
|
|
2158
|
-
const { allSkills,
|
|
2159
|
-
const installedSkills = readInstalledSkills(
|
|
2228
|
+
var skillUpdateCommand = async (skillId) => {
|
|
2229
|
+
const basePath = resolveUserBasePath();
|
|
2230
|
+
const { allSkills, cliVersion } = loadCompilerInputs();
|
|
2231
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2160
2232
|
const targets = skillId ? installedSkills.filter((skill) => skill.id === skillId) : installedSkills;
|
|
2161
|
-
p7.intro(
|
|
2233
|
+
p7.intro("ai-ops skill update");
|
|
2162
2234
|
if (targets.length === 0) {
|
|
2163
2235
|
p7.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C skill\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2164
2236
|
p7.outro("ai-ops skill update \uC644\uB8CC");
|
|
@@ -2168,57 +2240,29 @@ var skillUpdateCommand = async (skillId, opts) => {
|
|
|
2168
2240
|
const skill = resolveSkillById(allSkills, installedSkill.id);
|
|
2169
2241
|
const { packages, installedSkill: next } = buildSkillInstallPlan({
|
|
2170
2242
|
skill,
|
|
2171
|
-
requestedTools: installedSkill.tools
|
|
2172
|
-
scope
|
|
2243
|
+
requestedTools: installedSkill.tools
|
|
2173
2244
|
});
|
|
2174
2245
|
installSkillPackages(basePath, packages);
|
|
2175
2246
|
return next;
|
|
2176
2247
|
});
|
|
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
|
-
}
|
|
2248
|
+
const registryPath = resolveSkillRegistryPath(basePath);
|
|
2249
|
+
const previous = readSkillRegistry(registryPath);
|
|
2250
|
+
const nextSkillIds = new Set(nextInstalledSkills.map((skill) => skill.id));
|
|
2251
|
+
const untouched = (previous?.skills ?? []).filter(
|
|
2252
|
+
(installedSkill) => !nextSkillIds.has(resolveCanonicalSkillId(installedSkill.id))
|
|
2253
|
+
);
|
|
2254
|
+
writeSkillRegistry(registryPath, {
|
|
2255
|
+
skills: [...untouched, ...nextInstalledSkills],
|
|
2256
|
+
cliVersion,
|
|
2257
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2258
|
+
});
|
|
2215
2259
|
p7.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSkills.map((skill) => skill.id).join(", ")}`);
|
|
2216
2260
|
p7.outro("ai-ops skill update \uC644\uB8CC");
|
|
2217
2261
|
};
|
|
2218
|
-
var skillUninstallCommand = async (skillId
|
|
2219
|
-
const
|
|
2220
|
-
const {
|
|
2221
|
-
const installedSkills = readInstalledSkills(
|
|
2262
|
+
var skillUninstallCommand = async (skillId) => {
|
|
2263
|
+
const basePath = resolveUserBasePath();
|
|
2264
|
+
const { cliVersion } = loadCompilerInputs();
|
|
2265
|
+
const installedSkills = readInstalledSkills(basePath);
|
|
2222
2266
|
const installedSkill = findInstalledSkill(installedSkills, skillId);
|
|
2223
2267
|
p7.intro(`ai-ops skill uninstall ${skillId}`);
|
|
2224
2268
|
if (!installedSkill) {
|
|
@@ -2227,70 +2271,416 @@ var skillUninstallCommand = async (skillId, opts) => {
|
|
|
2227
2271
|
return;
|
|
2228
2272
|
}
|
|
2229
2273
|
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
|
-
}
|
|
2274
|
+
writeUserSkillState({
|
|
2275
|
+
basePath,
|
|
2276
|
+
cliVersion,
|
|
2277
|
+
removeSkillId: skillId
|
|
2278
|
+
});
|
|
2244
2279
|
p7.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
|
|
2245
2280
|
p7.outro("ai-ops skill uninstall \uC644\uB8CC");
|
|
2246
2281
|
};
|
|
2247
2282
|
|
|
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";
|
|
2283
|
+
// src/commands/subagent.ts
|
|
2251
2284
|
import * as p8 from "@clack/prompts";
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2285
|
+
import { existsSync as existsSync5, rmSync as rmSync6 } from "fs";
|
|
2286
|
+
|
|
2287
|
+
// src/lib/subagent-install.ts
|
|
2288
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync7, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
2289
|
+
import { dirname as dirname9, isAbsolute as isAbsolute3, relative as relative3, resolve as resolve8 } from "path";
|
|
2290
|
+
var resolveInsideBasePath = (basePath, relativePath) => {
|
|
2291
|
+
const absBasePath = resolve8(basePath);
|
|
2292
|
+
const absPath = resolve8(absBasePath, relativePath);
|
|
2293
|
+
const fromBase = relative3(absBasePath, absPath);
|
|
2294
|
+
if (fromBase.length === 0 || fromBase.startsWith("..") || isAbsolute3(fromBase)) {
|
|
2295
|
+
throw new Error(`Subagent path escapes AI_OPS_HOME: ${relativePath}`);
|
|
2296
|
+
}
|
|
2297
|
+
return absPath;
|
|
2298
|
+
};
|
|
2299
|
+
var installSubagentPackages = (basePath, packages) => {
|
|
2300
|
+
const written = [];
|
|
2301
|
+
for (const subagentPackage of packages) {
|
|
2302
|
+
for (const file of subagentPackage.files) {
|
|
2303
|
+
const absPath = resolveInsideBasePath(basePath, file.relativePath);
|
|
2304
|
+
if (existsSync4(absPath)) {
|
|
2305
|
+
rmSync5(absPath, { recursive: true, force: true });
|
|
2306
|
+
}
|
|
2307
|
+
mkdirSync7(dirname9(absPath), { recursive: true });
|
|
2308
|
+
writeFileSync7(absPath, file.content.trimEnd() + "\n", "utf-8");
|
|
2309
|
+
written.push(file.relativePath);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
return written;
|
|
2313
|
+
};
|
|
2314
|
+
var removeSubagentFiles = (basePath, relativePaths) => {
|
|
2315
|
+
const removed = [];
|
|
2316
|
+
for (const relativePath of relativePaths) {
|
|
2317
|
+
const absPath = resolveInsideBasePath(basePath, relativePath);
|
|
2318
|
+
if (!existsSync4(absPath)) continue;
|
|
2319
|
+
rmSync5(absPath, { recursive: true, force: true });
|
|
2320
|
+
removed.push(relativePath);
|
|
2321
|
+
}
|
|
2322
|
+
return removed;
|
|
2323
|
+
};
|
|
2324
|
+
|
|
2325
|
+
// src/lib/subagent-state.ts
|
|
2326
|
+
var resolveRequestedSubagentTools = (params) => {
|
|
2327
|
+
if (params.requested === void 0 || params.requested.length === 0) {
|
|
2328
|
+
return [...params.supported];
|
|
2329
|
+
}
|
|
2330
|
+
const supportedSet = new Set(params.supported);
|
|
2331
|
+
const invalid = params.requested.filter((tool) => !supportedSet.has(tool));
|
|
2332
|
+
if (invalid.length > 0) {
|
|
2333
|
+
throw new Error(`Unsupported tools requested: ${invalid.join(", ")}`);
|
|
2334
|
+
}
|
|
2335
|
+
return [...params.requested];
|
|
2336
|
+
};
|
|
2337
|
+
var TOOL_ORDER3 = [SKILL_TOOL.CLAUDE_CODE, SKILL_TOOL.CODEX, SKILL_TOOL.GEMINI];
|
|
2338
|
+
var mergeSubagentTools = (params) => {
|
|
2339
|
+
const merged = /* @__PURE__ */ new Set([...params.existing ?? [], ...params.requested]);
|
|
2340
|
+
return TOOL_ORDER3.filter((tool) => merged.has(tool));
|
|
2341
|
+
};
|
|
2342
|
+
var upsertInstalledSubagent = (installedSubagents, nextSubagent) => {
|
|
2343
|
+
const remaining = installedSubagents.filter((subagent) => subagent.id !== nextSubagent.id);
|
|
2344
|
+
return [...remaining, nextSubagent];
|
|
2345
|
+
};
|
|
2346
|
+
var removeInstalledSubagent = (installedSubagents, subagentId) => installedSubagents.filter((subagent) => subagent.id !== subagentId);
|
|
2347
|
+
var findInstalledSubagent = (installedSubagents, subagentId) => installedSubagents.find((subagent) => subagent.id === subagentId);
|
|
2348
|
+
var resolveInstalledSubagentPaths = (installedSubagent) => installedSubagent.tools.map((tool) => buildSubagentRelativePath(installedSubagent.id, tool));
|
|
2349
|
+
|
|
2350
|
+
// src/commands/subagent.ts
|
|
2351
|
+
var loadCompilerInputs2 = () => {
|
|
2352
|
+
return {
|
|
2353
|
+
allSubagents: loadAllSubagents(resolveSubagentsDir()),
|
|
2354
|
+
cliVersion: getCliVersion()
|
|
2355
|
+
};
|
|
2356
|
+
};
|
|
2357
|
+
var resolveSubagentById = (subagents, subagentId) => {
|
|
2358
|
+
const subagent = subagents.find((candidate) => candidate.id === subagentId);
|
|
2359
|
+
if (!subagent) {
|
|
2360
|
+
throw new Error(`Unknown subagent: ${subagentId}`);
|
|
2361
|
+
}
|
|
2362
|
+
return subagent;
|
|
2363
|
+
};
|
|
2364
|
+
var writeUserSubagentState = (params) => {
|
|
2365
|
+
const manifestPath = resolveSubagentManifestPath(params.basePath);
|
|
2366
|
+
const previous = readSubagentManifest(manifestPath);
|
|
2367
|
+
const subagents = params.removeSubagentId ? removeInstalledSubagent(previous?.subagents ?? [], params.removeSubagentId) : params.nextSubagent ? upsertInstalledSubagent(previous?.subagents ?? [], params.nextSubagent) : previous?.subagents ?? [];
|
|
2368
|
+
if (subagents.length === 0) {
|
|
2369
|
+
rmSync6(manifestPath, { force: true });
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
writeSubagentManifest(manifestPath, {
|
|
2373
|
+
subagents,
|
|
2374
|
+
cliVersion: params.cliVersion,
|
|
2375
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2376
|
+
});
|
|
2377
|
+
};
|
|
2378
|
+
var readInstalledSubagents = (basePath) => readSubagentManifest(resolveSubagentManifestPath(basePath))?.subagents ?? [];
|
|
2379
|
+
var warnMissingSkills = (requiredSkills) => {
|
|
2380
|
+
const missing = requiredSkills.filter((skill) => !existsSync5(skill.path));
|
|
2381
|
+
if (missing.length === 0) {
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
p8.log.warn(
|
|
2385
|
+
[
|
|
2386
|
+
"\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.",
|
|
2387
|
+
...missing.map((skill) => `- ${skill.tool}:${skill.skillName} (${skill.path})`)
|
|
2388
|
+
].join("\n")
|
|
2389
|
+
);
|
|
2390
|
+
};
|
|
2391
|
+
var installSubagent = (params) => {
|
|
2392
|
+
const installedSubagents = readInstalledSubagents(params.basePath);
|
|
2393
|
+
const existingInstalledSubagent = findInstalledSubagent(installedSubagents, params.subagent.id);
|
|
2394
|
+
const nextRequestedTools = mergeSubagentTools({
|
|
2395
|
+
existing: existingInstalledSubagent?.tools,
|
|
2396
|
+
requested: params.requestedTools
|
|
2397
|
+
});
|
|
2398
|
+
const { packages, installedSubagent, requiredSkills } = buildSubagentInstallPlan({
|
|
2399
|
+
subagent: params.subagent,
|
|
2400
|
+
requestedTools: nextRequestedTools,
|
|
2401
|
+
userBasePath: params.basePath
|
|
2402
|
+
});
|
|
2403
|
+
installSubagentPackages(params.basePath, packages);
|
|
2404
|
+
warnMissingSkills(requiredSkills);
|
|
2405
|
+
writeUserSubagentState({
|
|
2406
|
+
basePath: params.basePath,
|
|
2407
|
+
cliVersion: params.cliVersion,
|
|
2408
|
+
nextSubagent: installedSubagent
|
|
2409
|
+
});
|
|
2410
|
+
return installedSubagent;
|
|
2411
|
+
};
|
|
2412
|
+
var subagentListCommand = async () => {
|
|
2413
|
+
const basePath = resolveUserBasePath();
|
|
2414
|
+
const { allSubagents } = loadCompilerInputs2();
|
|
2415
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2416
|
+
p8.intro("ai-ops subagent list");
|
|
2417
|
+
const lines = allSubagents.map((subagent) => {
|
|
2418
|
+
const installed = findInstalledSubagent(installedSubagents, subagent.id);
|
|
2419
|
+
const suffix = installed ? `installed for ${installed.tools.join(", ")}` : "not installed";
|
|
2420
|
+
return `- ${subagent.id} - ${suffix}`;
|
|
2421
|
+
});
|
|
2422
|
+
p8.log.info(lines.join("\n"));
|
|
2423
|
+
p8.outro("ai-ops subagent list \uC644\uB8CC");
|
|
2424
|
+
};
|
|
2425
|
+
var subagentInstallCommand = async (subagentId, opts) => {
|
|
2426
|
+
const basePath = resolveUserBasePath();
|
|
2427
|
+
const { allSubagents, cliVersion } = loadCompilerInputs2();
|
|
2428
|
+
const subagent = resolveSubagentById(allSubagents, subagentId);
|
|
2429
|
+
const requestedTools = resolveRequestedSubagentTools({ requested: opts.tool, supported: subagent.supported_tools });
|
|
2430
|
+
p8.intro(`ai-ops subagent install ${subagentId}`);
|
|
2431
|
+
const installedSubagent = installSubagent({
|
|
2432
|
+
subagent,
|
|
2433
|
+
requestedTools,
|
|
2434
|
+
basePath,
|
|
2435
|
+
cliVersion
|
|
2436
|
+
});
|
|
2437
|
+
p8.log.success(`\uC124\uCE58 \uC644\uB8CC: ${installedSubagent.id} (${installedSubagent.installed_paths.join(", ")})`);
|
|
2438
|
+
p8.outro("ai-ops subagent install \uC644\uB8CC");
|
|
2439
|
+
};
|
|
2440
|
+
var subagentDiffCommand = async (subagentId) => {
|
|
2441
|
+
const basePath = resolveUserBasePath();
|
|
2442
|
+
const { allSubagents } = loadCompilerInputs2();
|
|
2443
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2444
|
+
const targets = subagentId ? installedSubagents.filter((subagent) => subagent.id === subagentId) : installedSubagents;
|
|
2445
|
+
p8.intro("ai-ops subagent diff");
|
|
2446
|
+
if (targets.length === 0) {
|
|
2447
|
+
p8.log.warn("\uBE44\uAD50\uD560 \uC124\uCE58\uB41C subagent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2448
|
+
p8.outro("ai-ops subagent diff \uC644\uB8CC");
|
|
2449
|
+
return;
|
|
2450
|
+
}
|
|
2451
|
+
const lines = targets.map((installedSubagent) => {
|
|
2452
|
+
const subagent = resolveSubagentById(allSubagents, installedSubagent.id);
|
|
2453
|
+
const { installedSubagent: next } = buildSubagentInstallPlan({
|
|
2454
|
+
subagent,
|
|
2455
|
+
requestedTools: installedSubagent.tools,
|
|
2456
|
+
userBasePath: basePath
|
|
2457
|
+
});
|
|
2458
|
+
const changed = next.sourceHash !== installedSubagent.sourceHash;
|
|
2459
|
+
return `- ${installedSubagent.id}: ${changed ? "changed" : "up-to-date"} (${installedSubagent.sourceHash} -> ${next.sourceHash})`;
|
|
2460
|
+
});
|
|
2461
|
+
p8.log.info(lines.join("\n"));
|
|
2462
|
+
p8.outro("ai-ops subagent diff \uC644\uB8CC");
|
|
2463
|
+
};
|
|
2464
|
+
var subagentUpdateCommand = async (subagentId) => {
|
|
2465
|
+
const basePath = resolveUserBasePath();
|
|
2466
|
+
const { allSubagents, cliVersion } = loadCompilerInputs2();
|
|
2467
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2468
|
+
const targets = subagentId ? installedSubagents.filter((subagent) => subagent.id === subagentId) : installedSubagents;
|
|
2469
|
+
p8.intro("ai-ops subagent update");
|
|
2470
|
+
if (targets.length === 0) {
|
|
2471
|
+
p8.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C subagent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2472
|
+
p8.outro("ai-ops subagent update \uC644\uB8CC");
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
const nextInstalledSubagents = targets.map((installedSubagent) => {
|
|
2476
|
+
const subagent = resolveSubagentById(allSubagents, installedSubagent.id);
|
|
2477
|
+
const { packages, installedSubagent: next, requiredSkills } = buildSubagentInstallPlan({
|
|
2478
|
+
subagent,
|
|
2479
|
+
requestedTools: installedSubagent.tools,
|
|
2480
|
+
userBasePath: basePath
|
|
2481
|
+
});
|
|
2482
|
+
installSubagentPackages(basePath, packages);
|
|
2483
|
+
warnMissingSkills(requiredSkills);
|
|
2484
|
+
return next;
|
|
2485
|
+
});
|
|
2486
|
+
const manifestPath = resolveSubagentManifestPath(basePath);
|
|
2487
|
+
const previous = readSubagentManifest(manifestPath);
|
|
2488
|
+
const nextSubagentIds = new Set(nextInstalledSubagents.map((subagent) => subagent.id));
|
|
2489
|
+
const untouched = (previous?.subagents ?? []).filter((subagent) => !nextSubagentIds.has(subagent.id));
|
|
2490
|
+
writeSubagentManifest(manifestPath, {
|
|
2491
|
+
subagents: [...untouched, ...nextInstalledSubagents],
|
|
2492
|
+
cliVersion,
|
|
2493
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2494
|
+
});
|
|
2495
|
+
p8.log.success(`\uAC31\uC2E0 \uC644\uB8CC: ${nextInstalledSubagents.map((subagent) => subagent.id).join(", ")}`);
|
|
2496
|
+
p8.outro("ai-ops subagent update \uC644\uB8CC");
|
|
2497
|
+
};
|
|
2498
|
+
var subagentUninstallCommand = async (subagentId) => {
|
|
2499
|
+
const basePath = resolveUserBasePath();
|
|
2500
|
+
const cliVersion = getCliVersion();
|
|
2501
|
+
const installedSubagents = readInstalledSubagents(basePath);
|
|
2502
|
+
const installedSubagent = findInstalledSubagent(installedSubagents, subagentId);
|
|
2503
|
+
p8.intro(`ai-ops subagent uninstall ${subagentId}`);
|
|
2504
|
+
if (!installedSubagent) {
|
|
2505
|
+
p8.log.warn("\uC124\uCE58\uB41C subagent\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.");
|
|
2506
|
+
p8.outro("ai-ops subagent uninstall \uC644\uB8CC");
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
const removed = removeSubagentFiles(basePath, resolveInstalledSubagentPaths(installedSubagent));
|
|
2510
|
+
writeUserSubagentState({
|
|
2511
|
+
basePath,
|
|
2512
|
+
cliVersion,
|
|
2513
|
+
removeSubagentId: subagentId
|
|
2514
|
+
});
|
|
2515
|
+
p8.log.success(`\uC81C\uAC70 \uC644\uB8CC: ${removed.join(", ")}`);
|
|
2516
|
+
p8.outro("ai-ops subagent uninstall \uC644\uB8CC");
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2519
|
+
// src/commands/pack.ts
|
|
2520
|
+
import * as p9 from "@clack/prompts";
|
|
2521
|
+
var readManifestForPackCommand = (basePath) => {
|
|
2522
|
+
try {
|
|
2523
|
+
return readProjectLayerManifest(basePath);
|
|
2524
|
+
} catch (error) {
|
|
2525
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
2526
|
+
p9.log.error(`.ai-ops/manifest.json \uD30C\uC2F1 \uC2E4\uD328: ${message}`);
|
|
2527
|
+
process.exitCode = 1;
|
|
2528
|
+
return null;
|
|
2529
|
+
}
|
|
2530
|
+
};
|
|
2531
|
+
var reportPackError = (error) => {
|
|
2532
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
2533
|
+
p9.log.error(message);
|
|
2534
|
+
process.exitCode = 1;
|
|
2535
|
+
};
|
|
2536
|
+
var packListCommand = async () => {
|
|
2537
|
+
const basePath = resolveBasePath();
|
|
2538
|
+
const packs = loadAllPacks(resolvePacksDir());
|
|
2539
|
+
const manifest = readManifestForPackCommand(basePath);
|
|
2540
|
+
const installedPackIds = new Set(manifest?.packs.map((pack) => pack.id) ?? []);
|
|
2541
|
+
p9.intro("ai-ops pack list");
|
|
2542
|
+
p9.log.info(
|
|
2543
|
+
packs.map((pack) => {
|
|
2544
|
+
const suffix = installedPackIds.has(pack.id) ? "installed" : "not installed";
|
|
2545
|
+
return `- ${pack.id} - ${suffix}`;
|
|
2546
|
+
}).join("\n")
|
|
2547
|
+
);
|
|
2548
|
+
p9.outro("ai-ops pack list \uC644\uB8CC");
|
|
2549
|
+
};
|
|
2550
|
+
var packInstallCommand = async (packId) => {
|
|
2551
|
+
const basePath = resolveBasePath();
|
|
2552
|
+
p9.intro(`ai-ops pack install ${packId}`);
|
|
2553
|
+
try {
|
|
2554
|
+
const result = installProjectLayerPack({ basePath, packId, packsDir: resolvePacksDir() });
|
|
2555
|
+
p9.log.success(`pack \uC124\uCE58 \uC644\uB8CC: ${packId}`);
|
|
2556
|
+
if (result.written.length > 0) {
|
|
2557
|
+
p9.log.info(`\uC0DD\uC131:
|
|
2558
|
+
${result.written.map((file) => ` ${file}`).join("\n")}`);
|
|
2559
|
+
}
|
|
2560
|
+
if (result.refreshed.length > 0) {
|
|
2561
|
+
p9.log.info(`\uAC31\uC2E0:
|
|
2562
|
+
${result.refreshed.map((file) => ` ${file}`).join("\n")}`);
|
|
2563
|
+
}
|
|
2564
|
+
if (result.preserved.length > 0) {
|
|
2565
|
+
p9.log.info(`\uBCF4\uC874:
|
|
2566
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
2567
|
+
}
|
|
2568
|
+
} catch (error) {
|
|
2569
|
+
reportPackError(error);
|
|
2570
|
+
}
|
|
2571
|
+
p9.outro("ai-ops pack install \uC644\uB8CC");
|
|
2572
|
+
};
|
|
2573
|
+
var packDiffCommand = async (packId) => {
|
|
2574
|
+
const basePath = resolveBasePath();
|
|
2575
|
+
p9.intro("ai-ops pack diff");
|
|
2576
|
+
try {
|
|
2577
|
+
const report = diffProjectLayerPack({ basePath, packId, packsDir: resolvePacksDir() });
|
|
2578
|
+
if (report.issues.length === 0) {
|
|
2579
|
+
p9.log.success("\uBCC0\uACBD \uC0AC\uD56D \uC5C6\uC74C. pack\uC774 \uCD5C\uC2E0 \uC0C1\uD0DC\uC785\uB2C8\uB2E4.");
|
|
2580
|
+
p9.outro("ai-ops pack diff \uC644\uB8CC");
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
for (const item of report.issues) {
|
|
2584
|
+
const line = `[${item.code}] ${item.message}`;
|
|
2585
|
+
if (item.level === "error") {
|
|
2586
|
+
p9.log.error(line);
|
|
2587
|
+
} else {
|
|
2588
|
+
p9.log.warn(line);
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
if (report.issues.some((item) => item.level === "error")) {
|
|
2592
|
+
process.exitCode = 1;
|
|
2593
|
+
}
|
|
2594
|
+
} catch (error) {
|
|
2595
|
+
reportPackError(error);
|
|
2596
|
+
}
|
|
2597
|
+
p9.outro("ai-ops pack diff \uC644\uB8CC");
|
|
2598
|
+
};
|
|
2599
|
+
var packUpdateCommand = async (packId) => {
|
|
2600
|
+
const basePath = resolveBasePath();
|
|
2601
|
+
p9.intro("ai-ops pack update");
|
|
2602
|
+
try {
|
|
2603
|
+
const manifest = readManifestForPackCommand(basePath);
|
|
2604
|
+
if (!manifest) {
|
|
2605
|
+
p9.log.error(".ai-ops/manifest.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 ai-ops init\uC744 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
2606
|
+
process.exitCode = 1;
|
|
2607
|
+
p9.outro("ai-ops pack update \uC644\uB8CC");
|
|
2608
|
+
return;
|
|
2609
|
+
}
|
|
2610
|
+
const targetPackIds = packId ? [packId] : manifest.packs.map((pack) => pack.id);
|
|
2611
|
+
if (targetPackIds.length === 0) {
|
|
2612
|
+
p9.log.warn("\uAC31\uC2E0\uD560 \uC124\uCE58\uB41C pack\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2613
|
+
p9.outro("ai-ops pack update \uC644\uB8CC");
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
for (const targetPackId of targetPackIds) {
|
|
2617
|
+
const result = updateProjectLayerPack({ basePath, packId: targetPackId, packsDir: resolvePacksDir() });
|
|
2618
|
+
p9.log.success(`pack \uAC31\uC2E0 \uC644\uB8CC: ${targetPackId}`);
|
|
2619
|
+
if (result.refreshed.length > 0) {
|
|
2620
|
+
p9.log.info(`\uAC31\uC2E0:
|
|
2621
|
+
${result.refreshed.map((file) => ` ${file}`).join("\n")}`);
|
|
2622
|
+
}
|
|
2623
|
+
if (result.preserved.length > 0) {
|
|
2624
|
+
p9.log.info(`\uBCF4\uC874:
|
|
2625
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
} catch (error) {
|
|
2629
|
+
reportPackError(error);
|
|
2258
2630
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2631
|
+
p9.outro("ai-ops pack update \uC644\uB8CC");
|
|
2632
|
+
};
|
|
2633
|
+
var packUninstallCommand = async (packId) => {
|
|
2634
|
+
const basePath = resolveBasePath();
|
|
2635
|
+
p9.intro(`ai-ops pack uninstall ${packId}`);
|
|
2636
|
+
try {
|
|
2637
|
+
const result = uninstallProjectLayerPack({ basePath, packId });
|
|
2638
|
+
p9.log.success(`pack \uC81C\uAC70 \uC644\uB8CC: ${packId}`);
|
|
2639
|
+
if (result.deleted.length > 0) {
|
|
2640
|
+
p9.log.info(`\uC0AD\uC81C:
|
|
2641
|
+
${result.deleted.map((file) => ` ${file}`).join("\n")}`);
|
|
2642
|
+
}
|
|
2643
|
+
if (result.preserved.length > 0) {
|
|
2644
|
+
p9.log.info(`\uBCF4\uC874:
|
|
2645
|
+
${result.preserved.map((file) => ` ${file}`).join("\n")}`);
|
|
2646
|
+
}
|
|
2647
|
+
} catch (error) {
|
|
2648
|
+
reportPackError(error);
|
|
2265
2649
|
}
|
|
2266
|
-
|
|
2650
|
+
p9.outro("ai-ops pack uninstall \uC644\uB8CC");
|
|
2267
2651
|
};
|
|
2268
2652
|
|
|
2269
2653
|
// src/bin/index.ts
|
|
2270
2654
|
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("
|
|
2655
|
+
program.name("ai-ops").description("AI agent operating layer manager").version("0.1.0");
|
|
2656
|
+
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));
|
|
2657
|
+
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));
|
|
2658
|
+
program.command("diff").description("project operating layer drift \uBE44\uAD50").action(() => diffCommand());
|
|
2659
|
+
program.command("audit").description("project operating layer \uC0C1\uD0DC \uAC80\uC0AC").action(() => auditCommand());
|
|
2660
|
+
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
2661
|
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(
|
|
2662
|
+
var applySkillInstallOptions = (command) => command.option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C \uC9C0\uC815");
|
|
2663
|
+
skillCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C skill \uBAA9\uB85D").action(() => skillListCommand());
|
|
2664
|
+
applySkillInstallOptions(skillCommand.command("install <skillId>").description("skill \uC124\uCE58")).action(
|
|
2282
2665
|
(skillId, opts) => skillInstallCommand(skillId, opts)
|
|
2283
2666
|
);
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
);
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
);
|
|
2290
|
-
|
|
2291
|
-
(
|
|
2667
|
+
skillCommand.command("diff [skillId]").description("skill \uBCC0\uACBD \uBE44\uAD50").action((skillId) => skillDiffCommand(skillId));
|
|
2668
|
+
skillCommand.command("update [skillId]").description("skill \uAC31\uC2E0").action((skillId) => skillUpdateCommand(skillId));
|
|
2669
|
+
skillCommand.command("uninstall <skillId>").description("skill \uC81C\uAC70").action((skillId) => skillUninstallCommand(skillId));
|
|
2670
|
+
var subagentCommand = program.command("subagent").description("\uC5D0\uC774\uC804\uD2B8 subagent \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2671
|
+
var applySubagentInstallOptions = (command) => command.option("--tool <tool...>", "\uB300\uC0C1 \uB3C4\uAD6C \uC9C0\uC815");
|
|
2672
|
+
subagentCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C subagent \uBAA9\uB85D").action(() => subagentListCommand());
|
|
2673
|
+
applySubagentInstallOptions(subagentCommand.command("install <subagentId>").description("subagent \uC124\uCE58")).action(
|
|
2674
|
+
(subagentId, opts) => subagentInstallCommand(subagentId, opts)
|
|
2292
2675
|
);
|
|
2293
|
-
|
|
2294
|
-
|
|
2676
|
+
subagentCommand.command("diff [subagentId]").description("subagent \uBCC0\uACBD \uBE44\uAD50").action((subagentId) => subagentDiffCommand(subagentId));
|
|
2677
|
+
subagentCommand.command("update [subagentId]").description("subagent \uAC31\uC2E0").action((subagentId) => subagentUpdateCommand(subagentId));
|
|
2678
|
+
subagentCommand.command("uninstall <subagentId>").description("subagent \uC81C\uAC70").action((subagentId) => subagentUninstallCommand(subagentId));
|
|
2679
|
+
var packCommand = program.command("pack").description("optional project operating layer pack \uC124\uCE58/\uC870\uD68C/\uAC31\uC2E0");
|
|
2680
|
+
packCommand.command("list").description("\uC0AC\uC6A9 \uAC00\uB2A5\uD55C pack \uBAA9\uB85D").action(() => packListCommand());
|
|
2681
|
+
packCommand.command("install <packId>").description("pack \uC124\uCE58").action((packId) => packInstallCommand(packId));
|
|
2682
|
+
packCommand.command("diff [packId]").description("pack \uBCC0\uACBD \uBE44\uAD50").action((packId) => packDiffCommand(packId));
|
|
2683
|
+
packCommand.command("update [packId]").description("pack \uAC31\uC2E0").action((packId) => packUpdateCommand(packId));
|
|
2684
|
+
packCommand.command("uninstall <packId>").description("pack \uC81C\uAC70").action((packId) => packUninstallCommand(packId));
|
|
2295
2685
|
program.parse();
|
|
2296
2686
|
//# sourceMappingURL=index.js.map
|