@evo-dev/evodev 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 (37) hide show
  1. package/dist/.agents/plugins/marketplace.json +20 -0
  2. package/dist/.claude-plugin/marketplace.json +22 -0
  3. package/dist/assets/agents/review/code-reviewer/examples.md +19 -0
  4. package/dist/assets/agents/review/code-reviewer/manifest.json +10 -0
  5. package/dist/assets/agents/review/code-reviewer/prompt.md +59 -0
  6. package/dist/assets/agents/review/code-reviewer/verification.md +11 -0
  7. package/dist/assets/skills/coding/engineering-discipline/SKILL.md +63 -0
  8. package/dist/assets/skills/coding/engineering-discipline/anti-patterns.md +21 -0
  9. package/dist/assets/skills/coding/engineering-discipline/examples.md +19 -0
  10. package/dist/assets/skills/coding/engineering-discipline/manifest.json +10 -0
  11. package/dist/assets/skills/coding/engineering-discipline/verification.md +11 -0
  12. package/dist/assets/workflows/rd-bug-fix/WORKFLOW.json +45 -0
  13. package/dist/assets/workflows/rd-code-review/WORKFLOW.json +45 -0
  14. package/dist/assets/workflows/rd-docs-update/WORKFLOW.json +45 -0
  15. package/dist/assets/workflows/rd-feature-implementation/WORKFLOW.json +45 -0
  16. package/dist/assets/workflows/rd-refactor/WORKFLOW.json +45 -0
  17. package/dist/assets/workflows/rd-release-readiness/WORKFLOW.json +49 -0
  18. package/dist/assets/workflows/rd-security-boundary-review/WORKFLOW.json +45 -0
  19. package/dist/assets/workflows/rd-test-generation/WORKFLOW.json +45 -0
  20. package/dist/evodev +11 -0
  21. package/dist/index.js +11283 -0
  22. package/dist/plugins/evodev/.claude-plugin/plugin.json +9 -0
  23. package/dist/plugins/evodev/.codex-plugin/plugin.json +19 -0
  24. package/dist/plugins/evodev/hooks/codex-hooks.json +121 -0
  25. package/dist/plugins/evodev/hooks/codex.ts +257 -0
  26. package/dist/plugins/evodev/hooks/command-runner.ts +30 -0
  27. package/dist/plugins/evodev/hooks/detect.ts +101 -0
  28. package/dist/plugins/evodev/hooks/hooks.json +197 -0
  29. package/dist/plugins/evodev/hooks/hooks.ts +217 -0
  30. package/dist/plugins/evodev/hooks/index.ts +52 -0
  31. package/dist/plugins/evodev/hooks/paths.ts +53 -0
  32. package/dist/plugins/evodev/hooks/plugin.ts +513 -0
  33. package/dist/plugins/evodev/hooks/runtime.ts +113 -0
  34. package/dist/plugins/evodev/hooks/transform-agent.ts +30 -0
  35. package/dist/plugins/evodev/hooks/transform-skill.ts +18 -0
  36. package/dist/plugins/evodev/package.json +16 -0
  37. package/package.json +29 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "evodev",
3
+ "description": "EvoDev hook integration for disciplined AI coding workflows.",
4
+ "version": "0.1.0",
5
+ "author": {
6
+ "name": "EvoDev"
7
+ },
8
+ "license": "MIT"
9
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "evodev",
3
+ "version": "0.1.0",
4
+ "description": "EvoDev hook integration for disciplined AI coding workflows.",
5
+ "author": {
6
+ "name": "EvoDev"
7
+ },
8
+ "license": "MIT",
9
+ "hooks": "./hooks/codex-hooks.json",
10
+ "interface": {
11
+ "displayName": "EvoDev",
12
+ "shortDescription": "Disciplined AI coding workflow integration.",
13
+ "longDescription": "EvoDev integrates with Codex plugin installation while keeping Codex skill and agent sync reserved until capability verification is complete.",
14
+ "developerName": "EvoDev",
15
+ "category": "Productivity",
16
+ "capabilities": ["Plugin Install"],
17
+ "defaultPrompt": ["Check EvoDev doctor status."]
18
+ }
19
+ }
@@ -0,0 +1,121 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
10
+ "timeout": 30
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "UserPromptSubmit": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
21
+ "timeout": 30
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "PreToolUse": [
27
+ {
28
+ "matcher": "*",
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
33
+ "timeout": 30
34
+ }
35
+ ]
36
+ }
37
+ ],
38
+ "PermissionRequest": [
39
+ {
40
+ "matcher": "*",
41
+ "hooks": [
42
+ {
43
+ "type": "command",
44
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
45
+ "timeout": 30
46
+ }
47
+ ]
48
+ }
49
+ ],
50
+ "PostToolUse": [
51
+ {
52
+ "matcher": "*",
53
+ "hooks": [
54
+ {
55
+ "type": "command",
56
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
57
+ "timeout": 30
58
+ }
59
+ ]
60
+ }
61
+ ],
62
+ "PreCompact": [
63
+ {
64
+ "hooks": [
65
+ {
66
+ "type": "command",
67
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
68
+ "timeout": 30
69
+ }
70
+ ]
71
+ }
72
+ ],
73
+ "PostCompact": [
74
+ {
75
+ "hooks": [
76
+ {
77
+ "type": "command",
78
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
79
+ "timeout": 30
80
+ }
81
+ ]
82
+ }
83
+ ],
84
+ "SubagentStart": [
85
+ {
86
+ "matcher": "*",
87
+ "hooks": [
88
+ {
89
+ "type": "command",
90
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
91
+ "timeout": 30
92
+ }
93
+ ]
94
+ }
95
+ ],
96
+ "SubagentStop": [
97
+ {
98
+ "matcher": "*",
99
+ "hooks": [
100
+ {
101
+ "type": "command",
102
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
103
+ "timeout": 30
104
+ }
105
+ ]
106
+ }
107
+ ],
108
+ "Stop": [
109
+ {
110
+ "matcher": "*",
111
+ "hooks": [
112
+ {
113
+ "type": "command",
114
+ "command": "NODE_PATH=\"$(dirname \"$(bun pm bin -g)\")/install/global/node_modules${NODE_PATH:+:$NODE_PATH}\" bun run \"${PLUGIN_ROOT}/hooks/runtime.ts\" hook runtime --target codex",
115
+ "timeout": 30
116
+ }
117
+ ]
118
+ }
119
+ ]
120
+ }
121
+ }
@@ -0,0 +1,257 @@
1
+ import type {
2
+ CodeAgentCapabilities,
3
+ CodeAgentPlugin,
4
+ DoctorCheck,
5
+ DoctorContext,
6
+ PluginDetectionResult,
7
+ PluginInstallInput,
8
+ PluginInstallResult,
9
+ SyncAgentsInput,
10
+ SyncResult,
11
+ SyncSkillsInput,
12
+ } from "@evo-dev/core/plugins";
13
+ import { runNodeCommand } from "./command-runner.ts";
14
+
15
+ const CODEX_RESERVED_MESSAGE =
16
+ "Codex asset sync is reserved until Codex capabilities and formats are verified.";
17
+
18
+ export interface CodexCommandResult {
19
+ exitCode: number;
20
+ stdout?: string;
21
+ stderr?: string;
22
+ }
23
+
24
+ export interface CodexCommandRunner {
25
+ run(command: string, args: string[]): Promise<CodexCommandResult>;
26
+ }
27
+
28
+ export interface CodexPluginOptions {
29
+ commandRunner?: CodexCommandRunner;
30
+ pluginSelector?: string;
31
+ pluginMarketplaceSource?: string;
32
+ }
33
+
34
+ export function createCodexPlugin(options: CodexPluginOptions = {}): CodeAgentPlugin {
35
+ return {
36
+ id: "codex",
37
+ name: "Codex",
38
+ async detect(): Promise<PluginDetectionResult> {
39
+ return detectCodex({ commandRunner: options.commandRunner });
40
+ },
41
+ async getCapabilities(): Promise<CodeAgentCapabilities> {
42
+ return {
43
+ skills: { supported: false, format: "codex-skill" },
44
+ agents: { supported: false, format: "codex-agent" },
45
+ hooks: { supported: false, events: [] },
46
+ };
47
+ },
48
+ async doctor(context: DoctorContext): Promise<DoctorCheck[]> {
49
+ const enabled = context.settings.plugins.codex.enabled;
50
+ const detection = await detectCodex({ commandRunner: options.commandRunner });
51
+
52
+ return [
53
+ detectionToDoctorCheck(detection),
54
+ {
55
+ id: "codex.assets.reserved",
56
+ status: enabled ? "warn" : "skip",
57
+ message: enabled
58
+ ? `${CODEX_RESERVED_MESSAGE} Code Agent plugin install is supported, but Codex skill/agent asset sync remains disabled.`
59
+ : `${CODEX_RESERVED_MESSAGE} Codex is disabled in settings.`,
60
+ },
61
+ ];
62
+ },
63
+ async syncSkills(input: SyncSkillsInput): Promise<SyncResult> {
64
+ const result = createSkippedSyncResult(input.targetPlugin);
65
+ for (const skill of input.skills) {
66
+ result.skipped.push(skill.registryKey);
67
+ }
68
+ addCodexSyncDiagnostic(result, input.dryRun, "skill");
69
+ return result;
70
+ },
71
+ async syncAgents(input: SyncAgentsInput): Promise<SyncResult> {
72
+ const result = createSkippedSyncResult(input.targetPlugin);
73
+ for (const agent of input.agents) {
74
+ result.skipped.push(agent.registryKey);
75
+ }
76
+ addCodexSyncDiagnostic(result, input.dryRun, "agent");
77
+ return result;
78
+ },
79
+ async installPlugin(input: PluginInstallInput): Promise<PluginInstallResult> {
80
+ return installCodexPlugin(input, options);
81
+ },
82
+ };
83
+ }
84
+
85
+ export const codexPlugin = createCodexPlugin();
86
+
87
+ async function detectCodex(
88
+ options: Pick<CodexPluginOptions, "commandRunner">,
89
+ ): Promise<PluginDetectionResult> {
90
+ const command = "codex";
91
+ const runner = options.commandRunner ?? new NodeCodexCommandRunner();
92
+
93
+ try {
94
+ const result = await runner.run(command, ["--version"]);
95
+ if (result.exitCode === 0) {
96
+ const version = result.stdout?.trim();
97
+ return {
98
+ status: "installed",
99
+ message: version ? `Codex available: ${version}` : "Codex available: codex",
100
+ };
101
+ }
102
+
103
+ return {
104
+ status: isMissingExit(result.exitCode, result.stderr) ? "missing" : "unknown",
105
+ message: isMissingExit(result.exitCode, result.stderr)
106
+ ? "Codex command not found: codex"
107
+ : `Codex detection returned exit code ${result.exitCode}`,
108
+ };
109
+ } catch (error) {
110
+ return {
111
+ status: isCommandMissingError(error) ? "missing" : "unknown",
112
+ message: isCommandMissingError(error)
113
+ ? "Codex command not found: codex"
114
+ : `Codex detection failed: ${describeError(error)}`,
115
+ };
116
+ }
117
+ }
118
+
119
+ async function installCodexPlugin(
120
+ input: PluginInstallInput,
121
+ options: Pick<CodexPluginOptions, "commandRunner" | "pluginSelector" | "pluginMarketplaceSource">,
122
+ ): Promise<PluginInstallResult> {
123
+ const command = "codex";
124
+ const selector = options.pluginSelector ?? "evodev@evodev";
125
+ const args = ["plugin", "add", selector];
126
+ const runner = options.commandRunner ?? new NodeCodexCommandRunner();
127
+
128
+ try {
129
+ if (options.pluginMarketplaceSource !== undefined) {
130
+ const marketplaceResult = await runner.run(command, [
131
+ "plugin",
132
+ "marketplace",
133
+ "add",
134
+ options.pluginMarketplaceSource,
135
+ ]);
136
+ if (marketplaceResult.exitCode !== 0) {
137
+ return createPluginInstallResult(
138
+ input.targetPlugin,
139
+ "failed",
140
+ command,
141
+ args,
142
+ marketplaceResult.stderr ??
143
+ marketplaceResult.stdout ??
144
+ `Codex marketplace add exited with code ${marketplaceResult.exitCode}`,
145
+ );
146
+ }
147
+ }
148
+
149
+ const result = await runner.run(command, args);
150
+ if (result.exitCode === 0) {
151
+ return createPluginInstallResult(
152
+ input.targetPlugin,
153
+ "installed",
154
+ command,
155
+ args,
156
+ result.stdout,
157
+ );
158
+ }
159
+
160
+ return createPluginInstallResult(
161
+ input.targetPlugin,
162
+ "failed",
163
+ command,
164
+ args,
165
+ result.stderr ?? result.stdout ?? `Codex plugin add exited with code ${result.exitCode}`,
166
+ );
167
+ } catch (error) {
168
+ return createPluginInstallResult(
169
+ input.targetPlugin,
170
+ "failed",
171
+ command,
172
+ args,
173
+ describeError(error),
174
+ );
175
+ }
176
+ }
177
+
178
+ class NodeCodexCommandRunner implements CodexCommandRunner {
179
+ async run(command: string, args: string[]): Promise<CodexCommandResult> {
180
+ return runNodeCommand(command, args);
181
+ }
182
+ }
183
+
184
+ function detectionToDoctorCheck(detection: PluginDetectionResult): DoctorCheck {
185
+ if (detection.status === "installed") {
186
+ return { id: "codex.available", status: "pass", message: detection.message };
187
+ }
188
+
189
+ if (detection.status === "missing") {
190
+ return { id: "codex.available", status: "warn", message: detection.message };
191
+ }
192
+
193
+ return { id: "codex.available", status: "warn", message: detection.message };
194
+ }
195
+
196
+ function createSkippedSyncResult(targetPlugin: string): SyncResult {
197
+ return {
198
+ targetPlugin,
199
+ syncedSkills: [],
200
+ syncedAgents: [],
201
+ skipped: [],
202
+ warnings: [],
203
+ errors: [],
204
+ };
205
+ }
206
+
207
+ function addCodexSyncDiagnostic(
208
+ result: SyncResult,
209
+ dryRun: boolean,
210
+ assetKind: "skill" | "agent",
211
+ ): void {
212
+ const message = `Codex ${assetKind} sync unsupported: capabilities are unverified; no writes planned in I10.`;
213
+ if (dryRun) result.warnings.push(message);
214
+ else result.errors.push(message);
215
+ }
216
+
217
+ function createPluginInstallResult(
218
+ targetPlugin: string,
219
+ status: PluginInstallResult["status"],
220
+ command: string,
221
+ args: string[],
222
+ message?: string,
223
+ ): PluginInstallResult {
224
+ const normalizedMessage = message?.trim() || `${command} ${args.join(" ")}`;
225
+ return {
226
+ targetPlugin,
227
+ status,
228
+ command,
229
+ args,
230
+ message: normalizedMessage,
231
+ warnings: [],
232
+ errors: status === "failed" ? [normalizedMessage] : [],
233
+ };
234
+ }
235
+
236
+ function isMissingExit(exitCode: number, stderr?: string): boolean {
237
+ if (exitCode === 127) return true;
238
+ const normalized = stderr?.toLowerCase() ?? "";
239
+ return normalized.includes("command not found") || normalized.includes("not recognized");
240
+ }
241
+
242
+ function isCommandMissingError(error: unknown): boolean {
243
+ return (
244
+ error instanceof Error &&
245
+ ("code" in error
246
+ ? (error as NodeJS.ErrnoException).code === "ENOENT"
247
+ : error.message.includes("ENOENT"))
248
+ );
249
+ }
250
+
251
+ function describeError(error: unknown): string {
252
+ if (error instanceof Error) {
253
+ return error.message;
254
+ }
255
+
256
+ return String(error);
257
+ }
@@ -0,0 +1,30 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ export interface NodeCommandResult {
4
+ exitCode: number;
5
+ stdout?: string;
6
+ stderr?: string;
7
+ }
8
+
9
+ export async function runNodeCommand(command: string, args: string[]): Promise<NodeCommandResult> {
10
+ return new Promise((resolve, reject) => {
11
+ const child = spawn(command, args, {
12
+ stdio: ["ignore", "pipe", "pipe"],
13
+ });
14
+ let stdout = "";
15
+ let stderr = "";
16
+
17
+ child.stdout.setEncoding("utf8");
18
+ child.stderr.setEncoding("utf8");
19
+ child.stdout.on("data", (chunk: string) => {
20
+ stdout += chunk;
21
+ });
22
+ child.stderr.on("data", (chunk: string) => {
23
+ stderr += chunk;
24
+ });
25
+ child.once("error", reject);
26
+ child.once("close", (code) => {
27
+ resolve({ exitCode: code ?? 1, stdout, stderr });
28
+ });
29
+ });
30
+ }
@@ -0,0 +1,101 @@
1
+ import type { PluginDetectionResult } from "@evo-dev/core/plugins";
2
+ import { runNodeCommand } from "./command-runner.ts";
3
+
4
+ export interface ClaudeCommandResult {
5
+ exitCode: number;
6
+ stdout?: string;
7
+ stderr?: string;
8
+ }
9
+
10
+ export interface ClaudeCommandRunner {
11
+ run(command: string, args: string[]): Promise<ClaudeCommandResult>;
12
+ }
13
+
14
+ export interface DetectClaudeOptions {
15
+ commandRunner?: ClaudeCommandRunner;
16
+ command?: string;
17
+ }
18
+
19
+ export async function detectClaudeCode(
20
+ options: DetectClaudeOptions = {},
21
+ ): Promise<PluginDetectionResult> {
22
+ const command = options.command ?? "claude";
23
+ const runner = options.commandRunner ?? new NodeClaudeCommandRunner();
24
+
25
+ try {
26
+ const result = await runner.run(command, ["--version"]);
27
+
28
+ if (result.exitCode === 0) {
29
+ return {
30
+ status: "installed",
31
+ message: formatInstalledMessage(command, result.stdout),
32
+ };
33
+ }
34
+
35
+ if (isMissingExit(result.exitCode, result.stderr)) {
36
+ return {
37
+ status: "missing",
38
+ message: `Claude Code command not found: ${command}`,
39
+ };
40
+ }
41
+
42
+ return {
43
+ status: "unknown",
44
+ message: `Claude Code detection returned exit code ${result.exitCode}`,
45
+ };
46
+ } catch (error) {
47
+ if (isCommandMissingError(error)) {
48
+ return {
49
+ status: "missing",
50
+ message: `Claude Code command not found: ${command}`,
51
+ };
52
+ }
53
+
54
+ return {
55
+ status: "unknown",
56
+ message: `Claude Code detection failed: ${describeError(error)}`,
57
+ };
58
+ }
59
+ }
60
+
61
+ class NodeClaudeCommandRunner implements ClaudeCommandRunner {
62
+ async run(command: string, args: string[]): Promise<ClaudeCommandResult> {
63
+ return runNodeCommand(command, args);
64
+ }
65
+ }
66
+
67
+ function formatInstalledMessage(command: string, stdout?: string): string {
68
+ const version = stdout?.trim();
69
+
70
+ if (version) {
71
+ return `Claude Code available: ${version}`;
72
+ }
73
+
74
+ return `Claude Code available: ${command}`;
75
+ }
76
+
77
+ function isMissingExit(exitCode: number, stderr?: string): boolean {
78
+ if (exitCode === 127) {
79
+ return true;
80
+ }
81
+
82
+ const normalized = stderr?.toLowerCase() ?? "";
83
+ return normalized.includes("command not found") || normalized.includes("not recognized");
84
+ }
85
+
86
+ function isCommandMissingError(error: unknown): boolean {
87
+ return (
88
+ error instanceof Error &&
89
+ ("code" in error
90
+ ? (error as NodeJS.ErrnoException).code === "ENOENT"
91
+ : error.message.includes("ENOENT"))
92
+ );
93
+ }
94
+
95
+ function describeError(error: unknown): string {
96
+ if (error instanceof Error) {
97
+ return error.message;
98
+ }
99
+
100
+ return String(error);
101
+ }