@evo-dev/core 0.0.1-alpha

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.
Files changed (51) hide show
  1. package/assets/agents/review/code-reviewer/examples.md +19 -0
  2. package/assets/agents/review/code-reviewer/manifest.json +10 -0
  3. package/assets/agents/review/code-reviewer/prompt.md +59 -0
  4. package/assets/agents/review/code-reviewer/verification.md +11 -0
  5. package/assets/skills/coding/engineering-discipline/SKILL.md +63 -0
  6. package/assets/skills/coding/engineering-discipline/anti-patterns.md +21 -0
  7. package/assets/skills/coding/engineering-discipline/examples.md +19 -0
  8. package/assets/skills/coding/engineering-discipline/manifest.json +10 -0
  9. package/assets/skills/coding/engineering-discipline/verification.md +11 -0
  10. package/assets/workflows/rd-bug-fix/WORKFLOW.json +45 -0
  11. package/assets/workflows/rd-code-review/WORKFLOW.json +45 -0
  12. package/assets/workflows/rd-docs-update/WORKFLOW.json +45 -0
  13. package/assets/workflows/rd-feature-implementation/WORKFLOW.json +45 -0
  14. package/assets/workflows/rd-refactor/WORKFLOW.json +45 -0
  15. package/assets/workflows/rd-release-readiness/WORKFLOW.json +49 -0
  16. package/assets/workflows/rd-security-boundary-review/WORKFLOW.json +45 -0
  17. package/assets/workflows/rd-test-generation/WORKFLOW.json +45 -0
  18. package/dist/assets/index.js +209 -0
  19. package/dist/config/index.js +601 -0
  20. package/dist/index.js +4879 -0
  21. package/dist/plugins/index.js +265 -0
  22. package/package.json +30 -0
  23. package/src/.gitkeep +0 -0
  24. package/src/agents/index.ts +561 -0
  25. package/src/assets/errors.ts +21 -0
  26. package/src/assets/index.ts +18 -0
  27. package/src/assets/manifest.ts +109 -0
  28. package/src/assets/scanner.ts +189 -0
  29. package/src/config/errors.ts +21 -0
  30. package/src/config/index.ts +26 -0
  31. package/src/config/paths.ts +43 -0
  32. package/src/config/registry.ts +84 -0
  33. package/src/config/settings.ts +212 -0
  34. package/src/config/state.ts +130 -0
  35. package/src/config/store.ts +166 -0
  36. package/src/daemon/index.ts +414 -0
  37. package/src/hooks/index.ts +1023 -0
  38. package/src/index.ts +14 -0
  39. package/src/learning/index.ts +714 -0
  40. package/src/observability/index.ts +272 -0
  41. package/src/pack/index.ts +779 -0
  42. package/src/plugins/capabilities.ts +347 -0
  43. package/src/plugins/index.ts +41 -0
  44. package/src/plugins/registry.ts +60 -0
  45. package/src/plugins/types.ts +123 -0
  46. package/src/project/index.ts +507 -0
  47. package/src/protected-zones/index.ts +137 -0
  48. package/src/sync/index.ts +7 -0
  49. package/src/sync/orchestrator.ts +298 -0
  50. package/src/task/index.ts +840 -0
  51. package/src/workflow/index.ts +137 -0
@@ -0,0 +1,347 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import type { CodeAgentCapabilities, CodeAgentPlugin, PluginDetectionResult } from "./types.ts";
4
+
5
+ export type CapabilityState = "declared" | "unverified" | "verified" | "enabled" | "unsupported";
6
+
7
+ export interface PluginCapabilityNegotiation {
8
+ pluginId: string;
9
+ skills: CapabilityState;
10
+ agents: CapabilityState;
11
+ hooks: CapabilityState;
12
+ canPlanWrites: boolean;
13
+ warnings: string[];
14
+ blockers: string[];
15
+ }
16
+
17
+ export interface CodexCapabilityVerificationArtifact {
18
+ version: 1;
19
+ pluginId: "codex";
20
+ status: "verified-readonly";
21
+ verifiedAt: string;
22
+ createdAt: string;
23
+ metadataOnly: true;
24
+ rawOutputStored: false;
25
+ sourceContentStored: false;
26
+ promptHistoryStored: false;
27
+ promptStored: false;
28
+ secretsStored: false;
29
+ externalUpload: false;
30
+ networkUsed: false;
31
+ toolSummary: {
32
+ tool: "codex";
33
+ detectionStatus: string;
34
+ version: string | null;
35
+ };
36
+ capabilities: {
37
+ skills: "unverified";
38
+ agents: "unverified";
39
+ hooks: "unverified";
40
+ };
41
+ writeBoundary: {
42
+ writesAllowed: false;
43
+ allowedPaths: [];
44
+ userLevelOnly: true;
45
+ noOverwrite: true;
46
+ projectWritesAllowed: false;
47
+ codeAgentWritesAllowed: false;
48
+ };
49
+ privacy: {
50
+ classification: "local-private";
51
+ metadataOnly: true;
52
+ rawOutputStored: false;
53
+ sourceContentStored: false;
54
+ promptHistoryStored: false;
55
+ secretsStored: false;
56
+ externalUpload: false;
57
+ };
58
+ blockers: string[];
59
+ diagnostics: string[];
60
+ evidenceRefs: string[];
61
+ evidence: {
62
+ detectionStatus: string;
63
+ detectionMessage: string;
64
+ writesAllowed: false;
65
+ notes: string[];
66
+ };
67
+ }
68
+
69
+ export function createUnknownNegotiatedCapabilities(pluginId: string): PluginCapabilityNegotiation {
70
+ return {
71
+ pluginId,
72
+ skills: "unsupported",
73
+ agents: "unsupported",
74
+ hooks: "unsupported",
75
+ canPlanWrites: false,
76
+ warnings: [],
77
+ blockers: [`Plugin ${pluginId} capabilities are unknown and unsupported.`],
78
+ };
79
+ }
80
+
81
+ export function isCapabilityUsable(state: CapabilityState): boolean {
82
+ return state === "enabled";
83
+ }
84
+
85
+ export function assertNoUnverifiedWrites(negotiation: PluginCapabilityNegotiation): void {
86
+ if (!negotiation.canPlanWrites) {
87
+ throw new Error(`Plugin ${negotiation.pluginId} has no verified enabled write capabilities.`);
88
+ }
89
+ }
90
+
91
+ export function negotiatePluginCapabilities(input: {
92
+ pluginId: string;
93
+ declared: CodeAgentCapabilities;
94
+ verified?: CodexCapabilityVerificationArtifact | null;
95
+ enabled: boolean;
96
+ }): PluginCapabilityNegotiation {
97
+ if (input.pluginId === "codex") {
98
+ const declaredSupport = [
99
+ input.declared.skills.supported ? "skills" : null,
100
+ input.declared.agents.supported ? "agents" : null,
101
+ input.declared.hooks.supported ? "hooks" : null,
102
+ ].filter(Boolean);
103
+ const blockers = ["Codex capabilities are unverified; writes are unsupported in I10."];
104
+ const warnings = [
105
+ input.verified
106
+ ? "Codex readonly verification artifact exists; sync remains no-write until formats are verified."
107
+ : null,
108
+ declaredSupport.length > 0
109
+ ? `Codex declares ${declaredSupport.join(", ")} support, but declared support alone cannot authorize writes.`
110
+ : null,
111
+ ].filter((warning): warning is string => warning !== null);
112
+
113
+ return {
114
+ pluginId: input.pluginId,
115
+ skills: input.declared.skills.supported ? "unverified" : "unsupported",
116
+ agents: input.declared.agents.supported ? "unverified" : "unsupported",
117
+ hooks: input.declared.hooks.supported ? "unverified" : "unsupported",
118
+ canPlanWrites: false,
119
+ warnings,
120
+ blockers,
121
+ };
122
+ }
123
+ return {
124
+ pluginId: input.pluginId,
125
+ skills: resolveNonCodexCapabilityState(input.declared.skills.supported, input.enabled),
126
+ agents: resolveNonCodexCapabilityState(input.declared.agents.supported, input.enabled),
127
+ hooks: resolveNonCodexCapabilityState(input.declared.hooks.supported, input.enabled),
128
+ canPlanWrites:
129
+ input.enabled && input.declared.skills.supported && input.declared.agents.supported,
130
+ warnings: input.enabled
131
+ ? []
132
+ : [
133
+ `Plugin ${input.pluginId} has declared capabilities but is not enabled; writes are not authorized.`,
134
+ ],
135
+ blockers: [],
136
+ };
137
+ }
138
+
139
+ function resolveNonCodexCapabilityState(supported: boolean, enabled: boolean): CapabilityState {
140
+ if (!supported) return "unsupported";
141
+ return enabled ? "enabled" : "declared";
142
+ }
143
+
144
+ export async function createCodexCapabilityVerificationArtifact(input: {
145
+ detection: PluginDetectionResult;
146
+ createdAt?: string;
147
+ }): Promise<CodexCapabilityVerificationArtifact> {
148
+ const timestamp = input.createdAt ?? new Date().toISOString();
149
+ const detectionMessage = sanitizeText(input.detection.message);
150
+ const diagnostics = [
151
+ "Codex remains no-write until concrete user-level paths and formats are verified.",
152
+ ];
153
+ return {
154
+ version: 1,
155
+ pluginId: "codex",
156
+ status: "verified-readonly",
157
+ verifiedAt: timestamp,
158
+ createdAt: timestamp,
159
+ metadataOnly: true,
160
+ rawOutputStored: false,
161
+ sourceContentStored: false,
162
+ promptHistoryStored: false,
163
+ promptStored: false,
164
+ secretsStored: false,
165
+ externalUpload: false,
166
+ networkUsed: false,
167
+ toolSummary: { tool: "codex", detectionStatus: input.detection.status, version: null },
168
+ capabilities: { skills: "unverified", agents: "unverified", hooks: "unverified" },
169
+ writeBoundary: {
170
+ writesAllowed: false,
171
+ allowedPaths: [],
172
+ userLevelOnly: true,
173
+ noOverwrite: true,
174
+ projectWritesAllowed: false,
175
+ codeAgentWritesAllowed: false,
176
+ },
177
+ privacy: {
178
+ classification: "local-private",
179
+ metadataOnly: true,
180
+ rawOutputStored: false,
181
+ sourceContentStored: false,
182
+ promptHistoryStored: false,
183
+ secretsStored: false,
184
+ externalUpload: false,
185
+ },
186
+ blockers: ["Codex asset capabilities are unverified and unsupported for writes in I10."],
187
+ diagnostics,
188
+ evidenceRefs: [],
189
+ evidence: {
190
+ detectionStatus: input.detection.status,
191
+ detectionMessage,
192
+ writesAllowed: false,
193
+ notes: [
194
+ "Metadata-only readonly artifact; no Codex paths, formats, hooks, or writes verified.",
195
+ ],
196
+ },
197
+ };
198
+ }
199
+
200
+ export function resolveCodexCapabilityArtifactPath(homeDir: string): string {
201
+ return join(homeDir, ".evodev", "STATE", "plugins", "codex", "capability-verification.json");
202
+ }
203
+
204
+ export async function writeCodexCapabilityVerificationArtifact(
205
+ homeDir: string,
206
+ artifact: CodexCapabilityVerificationArtifact,
207
+ ): Promise<string> {
208
+ validateCodexCapabilityVerificationArtifact(artifact);
209
+ const path = resolveCodexCapabilityArtifactPath(homeDir);
210
+ await mkdir(dirname(path), { recursive: true });
211
+ await writeFile(path, `${JSON.stringify(artifact, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
212
+ return path;
213
+ }
214
+
215
+ export async function readCodexCapabilityVerificationArtifact(
216
+ homeDir: string,
217
+ ): Promise<CodexCapabilityVerificationArtifact | null> {
218
+ try {
219
+ const artifact = JSON.parse(
220
+ await readFile(resolveCodexCapabilityArtifactPath(homeDir), "utf8"),
221
+ );
222
+ validateCodexCapabilityVerificationArtifact(artifact);
223
+ return artifact;
224
+ } catch (error) {
225
+ if (
226
+ error instanceof Error &&
227
+ "code" in error &&
228
+ (error as NodeJS.ErrnoException).code === "ENOENT"
229
+ ) {
230
+ return null;
231
+ }
232
+ throw error;
233
+ }
234
+ }
235
+
236
+ export function validateCodexCapabilityVerificationArtifact(
237
+ artifact: CodexCapabilityVerificationArtifact,
238
+ ): void {
239
+ if (artifact.version !== 1 || artifact.pluginId !== "codex")
240
+ throw new Error("Invalid Codex artifact identity.");
241
+ if (
242
+ artifact.metadataOnly !== true ||
243
+ artifact.rawOutputStored !== false ||
244
+ artifact.sourceContentStored !== false ||
245
+ artifact.promptStored !== false ||
246
+ artifact.promptHistoryStored !== false ||
247
+ artifact.secretsStored !== false ||
248
+ artifact.externalUpload !== false ||
249
+ artifact.networkUsed !== false ||
250
+ artifact.privacy?.metadataOnly !== true ||
251
+ artifact.privacy.rawOutputStored !== false ||
252
+ artifact.privacy.sourceContentStored !== false ||
253
+ artifact.privacy.promptHistoryStored !== false ||
254
+ artifact.privacy.secretsStored !== false ||
255
+ artifact.privacy.externalUpload !== false ||
256
+ artifact.writeBoundary?.writesAllowed !== false ||
257
+ artifact.writeBoundary.allowedPaths.length !== 0 ||
258
+ artifact.writeBoundary.userLevelOnly !== true ||
259
+ artifact.writeBoundary.noOverwrite !== true ||
260
+ artifact.writeBoundary.projectWritesAllowed !== false ||
261
+ artifact.writeBoundary.codeAgentWritesAllowed !== false
262
+ ) {
263
+ throw new Error("Codex artifact must be metadata-only and local-only.");
264
+ }
265
+ assertNoSensitiveContent(artifact);
266
+ }
267
+
268
+ export function formatCodexCapabilityVerificationArtifact(
269
+ artifact: CodexCapabilityVerificationArtifact,
270
+ path?: string,
271
+ ): string {
272
+ return [
273
+ "EvoDev Codex capability verification",
274
+ "",
275
+ `Status: ${artifact.status}`,
276
+ `Artifact: ${path ?? "dry-run only"}`,
277
+ `Verified at: ${artifact.verifiedAt}`,
278
+ "Capabilities: skills=unverified agents=unverified hooks=unverified",
279
+ "Writes allowed: false",
280
+ "Allowed paths: none",
281
+ "User-level write paths verified: false",
282
+ "Project writes allowed: false",
283
+ "No-overwrite verified: false",
284
+ `External upload: ${artifact.externalUpload}`,
285
+ `Detection: ${artifact.evidence.detectionStatus}`,
286
+ ...artifact.evidence.notes.map((note) => `Note: ${note}`),
287
+ ].join("\n");
288
+ }
289
+
290
+ export async function runPluginConformance(
291
+ plugin: CodeAgentPlugin,
292
+ ): Promise<{ ok: boolean; findings: string[] }> {
293
+ const findings: string[] = [];
294
+ try {
295
+ await plugin.detect();
296
+ await plugin.getCapabilities();
297
+ } catch (error) {
298
+ findings.push(
299
+ `Plugin ${plugin.id} threw during detect/capabilities: ${error instanceof Error ? error.message : String(error)}`,
300
+ );
301
+ }
302
+ if (plugin.id === "codex") {
303
+ const capabilities = await plugin.getCapabilities();
304
+ if (
305
+ capabilities.skills.supported ||
306
+ capabilities.agents.supported ||
307
+ capabilities.hooks.supported
308
+ ) {
309
+ findings.push("Codex must not declare verified runtime support in I10.");
310
+ }
311
+ }
312
+ return { ok: findings.length === 0, findings };
313
+ }
314
+
315
+ function sanitizeText(value: string): string {
316
+ return value
317
+ .replace(
318
+ /https?:\/\/\S+|\b(secret|token|password|private|internal|api[_-]?key)\b/gi,
319
+ "[redacted]",
320
+ )
321
+ .slice(0, 300);
322
+ }
323
+
324
+ function assertNoSensitiveContent(value: unknown): void {
325
+ if (typeof value === "string") {
326
+ if (value === "local-private") return;
327
+ if (
328
+ /https?:\/\/\S+|\b(secret|token|password|private|internal|api[_-]?key|raw output|raw source|raw prompt)\b/i.test(
329
+ value,
330
+ )
331
+ ) {
332
+ throw new Error("Codex artifact contains sensitive content.");
333
+ }
334
+ return;
335
+ }
336
+ if (Array.isArray(value)) {
337
+ for (const item of value) assertNoSensitiveContent(item);
338
+ return;
339
+ }
340
+ if (typeof value !== "object" || value === null) return;
341
+ for (const [key, child] of Object.entries(value)) {
342
+ if (/raw|prompt|source|secret|token|password|memorybody/i.test(key) && child !== false) {
343
+ throw new Error(`Codex artifact contains forbidden field: ${key}`);
344
+ }
345
+ assertNoSensitiveContent(child);
346
+ }
347
+ }
@@ -0,0 +1,41 @@
1
+ export {
2
+ type CapabilityState,
3
+ type CodexCapabilityVerificationArtifact,
4
+ type PluginCapabilityNegotiation,
5
+ assertNoUnverifiedWrites,
6
+ createCodexCapabilityVerificationArtifact,
7
+ createUnknownNegotiatedCapabilities,
8
+ formatCodexCapabilityVerificationArtifact,
9
+ isCapabilityUsable,
10
+ negotiatePluginCapabilities,
11
+ readCodexCapabilityVerificationArtifact,
12
+ resolveCodexCapabilityArtifactPath,
13
+ runPluginConformance,
14
+ validateCodexCapabilityVerificationArtifact,
15
+ writeCodexCapabilityVerificationArtifact,
16
+ } from "./capabilities.ts";
17
+ export {
18
+ PluginRegistry,
19
+ PluginRegistryError,
20
+ createPluginRegistry,
21
+ getEnabledPluginIds,
22
+ } from "./registry.ts";
23
+ export type {
24
+ CodeAgentCapabilities,
25
+ CodeAgentPlugin,
26
+ DoctorCheck,
27
+ DoctorCheckStatus,
28
+ DoctorContext,
29
+ HookEvent,
30
+ HookEventType,
31
+ PluginDetectionResult,
32
+ PluginDetectionStatus,
33
+ PluginId,
34
+ PluginInstallInput,
35
+ PluginInstallResult,
36
+ PluginInstallStatus,
37
+ SyncAgentsInput,
38
+ SyncPlan,
39
+ SyncResult,
40
+ SyncSkillsInput,
41
+ } from "./types.ts";
@@ -0,0 +1,60 @@
1
+ import type { EvoDevSettings } from "../config/index.ts";
2
+ import type { CodeAgentPlugin, PluginId } from "./types.ts";
3
+
4
+ export class PluginRegistryError extends Error {
5
+ constructor(message: string) {
6
+ super(message);
7
+ this.name = "PluginRegistryError";
8
+ }
9
+ }
10
+
11
+ export class PluginRegistry {
12
+ readonly #plugins = new Map<PluginId, CodeAgentPlugin>();
13
+
14
+ register(plugin: CodeAgentPlugin): void {
15
+ if (this.#plugins.has(plugin.id)) {
16
+ throw new PluginRegistryError(`Plugin already registered: ${plugin.id}`);
17
+ }
18
+
19
+ this.#plugins.set(plugin.id, plugin);
20
+ }
21
+
22
+ get(pluginId: PluginId): CodeAgentPlugin | undefined {
23
+ return this.#plugins.get(pluginId);
24
+ }
25
+
26
+ require(pluginId: PluginId): CodeAgentPlugin {
27
+ const plugin = this.get(pluginId);
28
+
29
+ if (plugin === undefined) {
30
+ throw new PluginRegistryError(`Plugin not registered: ${pluginId}`);
31
+ }
32
+
33
+ return plugin;
34
+ }
35
+
36
+ list(): CodeAgentPlugin[] {
37
+ return [...this.#plugins.values()].sort((left, right) => left.id.localeCompare(right.id));
38
+ }
39
+
40
+ getEnabled(settings: EvoDevSettings): CodeAgentPlugin[] {
41
+ return getEnabledPluginIds(settings).map((pluginId) => this.require(pluginId));
42
+ }
43
+ }
44
+
45
+ export function createPluginRegistry(plugins: CodeAgentPlugin[] = []): PluginRegistry {
46
+ const registry = new PluginRegistry();
47
+
48
+ for (const plugin of plugins) {
49
+ registry.register(plugin);
50
+ }
51
+
52
+ return registry;
53
+ }
54
+
55
+ export function getEnabledPluginIds(settings: EvoDevSettings): PluginId[] {
56
+ return Object.entries(settings.plugins)
57
+ .filter(([, pluginSettings]) => pluginSettings.enabled)
58
+ .map(([pluginId]) => pluginId)
59
+ .sort((left, right) => left.localeCompare(right));
60
+ }
@@ -0,0 +1,123 @@
1
+ import type { ScannedAsset } from "../assets/index.ts";
2
+ import type { AgentManifest, SkillManifest } from "../assets/manifest.ts";
3
+ import type { EvoDevSettings } from "../config/index.ts";
4
+
5
+ export type PluginId = string;
6
+
7
+ export type PluginDetectionStatus = "installed" | "missing" | "unknown" | "reserved";
8
+
9
+ export interface PluginDetectionResult {
10
+ status: PluginDetectionStatus;
11
+ message: string;
12
+ }
13
+
14
+ export type HookEventType =
15
+ | "SessionStart"
16
+ | "UserPromptSubmit"
17
+ | "UserPromptExpansion"
18
+ | "PreToolUse"
19
+ | "PermissionRequest"
20
+ | "PostToolUse"
21
+ | "PostToolUseFailure"
22
+ | "PostToolBatch"
23
+ | "PermissionDenied"
24
+ | "SubagentStart"
25
+ | "Stop"
26
+ | "StopFailure"
27
+ | "TeammateIdle"
28
+ | "SubagentStop"
29
+ | "AgentStop"
30
+ | "TaskCreated"
31
+ | "TaskCompleted"
32
+ | "PreCompact"
33
+ | "PostCompact"
34
+ | "SessionEnd";
35
+
36
+ export interface HookEvent {
37
+ type: HookEventType;
38
+ payload: Record<string, unknown>;
39
+ }
40
+
41
+ export interface CodeAgentCapabilities {
42
+ skills: {
43
+ supported: boolean;
44
+ format: "claude-skill" | "codex-skill" | "markdown";
45
+ };
46
+ agents: {
47
+ supported: boolean;
48
+ format: "claude-agent" | "codex-agent" | "prompt-md";
49
+ };
50
+ hooks: {
51
+ supported: boolean;
52
+ events: HookEventType[];
53
+ };
54
+ }
55
+
56
+ export type DoctorCheckStatus = "pass" | "warn" | "fail" | "skip";
57
+
58
+ export interface DoctorContext {
59
+ settings: EvoDevSettings;
60
+ homeDir: string;
61
+ }
62
+
63
+ export interface DoctorCheck {
64
+ id: string;
65
+ status: DoctorCheckStatus;
66
+ message: string;
67
+ }
68
+
69
+ export interface SyncSkillsInput {
70
+ targetPlugin: PluginId;
71
+ skills: ScannedAsset<SkillManifest>[];
72
+ dryRun: boolean;
73
+ }
74
+
75
+ export interface SyncAgentsInput {
76
+ targetPlugin: PluginId;
77
+ agents: ScannedAsset<AgentManifest>[];
78
+ dryRun: boolean;
79
+ }
80
+
81
+ export interface PluginInstallInput {
82
+ targetPlugin: PluginId;
83
+ }
84
+
85
+ export type PluginInstallStatus = "installed" | "already-installed" | "skipped" | "failed";
86
+
87
+ export interface PluginInstallResult {
88
+ targetPlugin: PluginId;
89
+ status: PluginInstallStatus;
90
+ command: string;
91
+ args: string[];
92
+ message: string;
93
+ warnings: string[];
94
+ errors: string[];
95
+ }
96
+
97
+ export interface SyncResult {
98
+ targetPlugin: PluginId;
99
+ syncedSkills: string[];
100
+ syncedAgents: string[];
101
+ skipped: string[];
102
+ warnings: string[];
103
+ errors: string[];
104
+ }
105
+
106
+ export interface SyncPlan {
107
+ targetPlugin: PluginId;
108
+ skills: ScannedAsset<SkillManifest>[];
109
+ agents: ScannedAsset<AgentManifest>[];
110
+ dryRun: boolean;
111
+ }
112
+
113
+ export interface CodeAgentPlugin {
114
+ id: PluginId;
115
+ name: string;
116
+ detect(): Promise<PluginDetectionResult>;
117
+ getCapabilities(): Promise<CodeAgentCapabilities>;
118
+ doctor(context: DoctorContext): Promise<DoctorCheck[]>;
119
+ syncSkills(input: SyncSkillsInput): Promise<SyncResult>;
120
+ syncAgents(input: SyncAgentsInput): Promise<SyncResult>;
121
+ installPlugin?(input: PluginInstallInput): Promise<PluginInstallResult>;
122
+ handleHookEvent?(event: HookEvent): Promise<void>;
123
+ }