@evo-dev/evodev 0.0.1-alpha.5 → 0.0.1-alpha.7

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "evodev",
3
3
  "description": "EvoDev hook, skill, and Teams MCP integration for disciplined AI coding workflows.",
4
- "version": "0.0.1-alpha.5",
4
+ "version": "0.0.1-alpha.7",
5
5
  "author": {
6
6
  "name": "EvoDev"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evodev",
3
- "version": "0.0.1-alpha.5",
3
+ "version": "0.0.1-alpha.7",
4
4
  "description": "EvoDev hook, skill, and Teams MCP integration for disciplined AI coding workflows.",
5
5
  "author": {
6
6
  "name": "EvoDev"
@@ -158,7 +158,7 @@ export function planClaudeHookInstallDryRun(
158
158
  action: "merge-with-backup",
159
159
  targetPath: paths.settingsPath,
160
160
  reason:
161
- "would add or replace EvoDev-managed user-level hook entries without touching user-owned hooks",
161
+ "would reconcile EvoDev-managed user-level hook entries with Claude plugin ownership without touching user-owned hooks",
162
162
  },
163
163
  ],
164
164
  warnings: ["Project .claude, .codex, CLAUDE.md, and AGENTS.md are not targeted."],
@@ -191,12 +191,34 @@ export async function installClaudeHooks(input: {
191
191
  paths?: ClaudePaths;
192
192
  now?: string;
193
193
  pluginSelector?: string;
194
+ pluginManaged?: boolean;
194
195
  }): Promise<HookInstallResult> {
195
196
  const paths = input.paths ?? resolveClaudePaths({ homeDir: input.homeDir });
196
197
  const existing = await readTextIfExists(paths.settingsPath);
198
+ const pluginSelector = input.pluginSelector ?? DEFAULT_CLAUDE_PLUGIN_SELECTOR;
199
+ if (input.pluginManaged === true || isClaudePluginEnabled(existing, pluginSelector)) {
200
+ const next = removeClaudeManagedUserHooks(existing);
201
+ if (next === null) {
202
+ return {
203
+ target: "claude",
204
+ targetPath: paths.settingsPath,
205
+ backupPath: null,
206
+ changed: false,
207
+ warnings: [],
208
+ };
209
+ }
210
+ return writeMergedConfig({
211
+ target: "claude",
212
+ path: paths.settingsPath,
213
+ existing,
214
+ next,
215
+ now: input.now,
216
+ warnings: [],
217
+ });
218
+ }
197
219
  const runtimeCommand = await resolveClaudeUserHookRuntimeCommand({
198
220
  paths,
199
- pluginSelector: input.pluginSelector ?? DEFAULT_CLAUDE_PLUGIN_SELECTOR,
221
+ pluginSelector,
200
222
  });
201
223
  const next = mergeClaudeSettingsHooks(existing, createClaudeHookConfig(runtimeCommand).hooks);
202
224
  return writeMergedConfig({
@@ -497,6 +519,37 @@ function mergeClaudeSettingsHooks(
497
519
  return `${JSON.stringify({ ...settings, hooks: nextHooks }, null, 2)}\n`;
498
520
  }
499
521
 
522
+ function isClaudePluginEnabled(existing: string | null, pluginSelector: string): boolean {
523
+ if (existing === null) return false;
524
+ const settings = parseJsonConfig(existing, "Claude settings");
525
+ const enabledPlugins = isRecord(settings.enabledPlugins) ? settings.enabledPlugins : {};
526
+ return enabledPlugins[pluginSelector] === true;
527
+ }
528
+
529
+ function removeClaudeManagedUserHooks(existing: string | null): string | null {
530
+ if (existing === null) return null;
531
+ const settings = parseJsonConfig(existing, "Claude settings");
532
+ if (!isRecord(settings.hooks)) return existing;
533
+
534
+ const nextHooks: Record<string, unknown> = {};
535
+ let changed = false;
536
+ for (const [eventName, groups] of Object.entries(settings.hooks)) {
537
+ if (!Array.isArray(groups)) {
538
+ nextHooks[eventName] = groups;
539
+ continue;
540
+ }
541
+ const retained = groups.filter((group) => !isEvoDevHookGroup(group, "claude"));
542
+ changed ||= retained.length !== groups.length;
543
+ if (retained.length > 0 || groups.length === 0) nextHooks[eventName] = retained;
544
+ }
545
+ if (!changed) return existing;
546
+
547
+ const nextSettings = { ...settings };
548
+ if (Object.keys(nextHooks).length === 0) nextSettings.hooks = undefined;
549
+ else nextSettings.hooks = nextHooks;
550
+ return `${JSON.stringify(nextSettings, null, 2)}\n`;
551
+ }
552
+
500
553
  function parseJsonConfig(existing: string | null, label: string): Record<string, unknown> {
501
554
  if (existing === null || existing.trim() === "") return {};
502
555
  const parsed = JSON.parse(existing) as unknown;
@@ -184,6 +184,12 @@ async function appendExecutionEventSafely(input: {
184
184
  environment: input.environment,
185
185
  payload: input.payload,
186
186
  });
187
+ const cwd =
188
+ input.payload !== null && typeof input.payload.cwd === "string" ? input.payload.cwd : null;
189
+ const workspace =
190
+ team === null && cwd !== null
191
+ ? await input.core.resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd })
192
+ : null;
187
193
  const traceRefId = await recordTraceRefSafely(input);
188
194
  await input.core.appendEvoDevExecutionEvent(
189
195
  input.homeDir,
@@ -192,7 +198,7 @@ async function appendExecutionEventSafely(input: {
192
198
  target: input.target,
193
199
  eventType: input.event?.type ?? runtimePhaseEventType(input.phase),
194
200
  sessionKey,
195
- projectKey: team?.projectKey ?? null,
201
+ projectKey: team?.projectKey ?? workspace?.projectKey ?? null,
196
202
  runId: team?.runId ?? null,
197
203
  roleId: team?.roleId ?? null,
198
204
  taskId: input.event?.scope.taskId ?? null,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evo-dev/plugin",
3
- "version": "0.0.1-alpha.5",
3
+ "version": "0.0.1-alpha.7",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./hooks/index.ts"