@syntesseraai/opencode-feature-factory 0.1.15 → 0.1.17

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@syntesseraai/opencode-feature-factory",
4
- "version": "0.1.15",
4
+ "version": "0.1.17",
5
5
  "description": "OpenCode plugin for Feature Factory agents - provides planning, implementation, review, testing, and validation agents",
6
6
  "type": "module",
7
7
  "license": "MIT",
package/src/discovery.ts CHANGED
@@ -6,8 +6,6 @@ import {
6
6
  readJsonFile,
7
7
  } from './quality-gate-config';
8
8
 
9
- // Shell type - uses `any` to be compatible with OpenCode's BunShell
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
9
  type BunShell = any;
12
10
 
13
11
  // ============================================================================
package/src/index.ts CHANGED
@@ -100,17 +100,11 @@ function mergeHooks(...hookSets: Partial<Hooks>[]): Hooks {
100
100
 
101
101
  const existingHandler = merged[key as keyof Hooks];
102
102
  if (existingHandler) {
103
- // Chain handlers - run existing first, then new
104
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
103
  merged[key as keyof Hooks] = (async (...args: any[]) => {
106
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
104
  await (existingHandler as (...args: any[]) => Promise<void>)(...args);
108
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
105
  await (handler as (...args: any[]) => Promise<void>)(...args);
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
106
  }) as any;
112
107
  } else {
113
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
108
  merged[key as keyof Hooks] = handler as any;
115
109
  }
116
110
  }
@@ -17,8 +17,6 @@ export const DEFAULT_QUALITY_GATE: Required<
17
17
  },
18
18
  };
19
19
 
20
- // Shell type - uses `any` to be compatible with OpenCode's BunShell
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
20
  type BunShell = any;
23
21
 
24
22
  /**
@@ -8,6 +8,7 @@ interface SessionState {
8
8
  dirty: boolean;
9
9
  qualityGatePassed: boolean;
10
10
  idleDebounce: ReturnType<typeof setTimeout> | null;
11
+ running: boolean;
11
12
  parentID?: string;
12
13
  isReadOnly?: boolean;
13
14
  }
@@ -23,6 +24,7 @@ function getSessionState(sessionId: string): SessionState {
23
24
  dirty: true,
24
25
  qualityGatePassed: false,
25
26
  idleDebounce: null,
27
+ running: false,
26
28
  };
27
29
  sessions.set(sessionId, state);
28
30
  return state;
@@ -75,7 +77,7 @@ export async function createQualityGateHooks(input: PluginInput): Promise<Partia
75
77
  }
76
78
  }
77
79
 
78
- async function ensureSessionMetadata(sessionId: string, state: SessionState): Promise<void> {
80
+ async function _ensureSessionMetadata(sessionId: string, state: SessionState): Promise<void> {
79
81
  if (state.isReadOnly !== undefined) return;
80
82
 
81
83
  try {
@@ -104,6 +106,11 @@ export async function createQualityGateHooks(input: PluginInput): Promise<Partia
104
106
  return;
105
107
  }
106
108
 
109
+ if (state.running) {
110
+ await log(client, 'debug', 'quality-gate.skipped (already running)', { sessionId });
111
+ return;
112
+ }
113
+
107
114
  const now = Date.now();
108
115
  const cacheExpired = now - state.lastRunAt > 30 * 1000;
109
116
 
@@ -119,19 +126,9 @@ export async function createQualityGateHooks(input: PluginInput): Promise<Partia
119
126
  return;
120
127
  }
121
128
 
122
- await log(client, 'info', 'quality-gate.started', { sessionId, directory });
129
+ state.running = true;
123
130
 
124
- await client.session.prompt({
125
- path: { id: sessionId },
126
- body: {
127
- parts: [
128
- {
129
- type: 'text',
130
- text: `🔄 Quality gate is running, please stand-by for results ...`,
131
- },
132
- ],
133
- },
134
- });
131
+ await log(client, 'info', 'quality-gate.started', { sessionId, directory });
135
132
 
136
133
  let ciOutput = '';
137
134
  let ciPassed = false;
@@ -150,21 +147,9 @@ export async function createQualityGateHooks(input: PluginInput): Promise<Partia
150
147
  state.lastRunAt = Date.now();
151
148
  state.dirty = false;
152
149
  state.qualityGatePassed = ciPassed;
150
+ state.running = false;
153
151
 
154
- if (ciPassed) {
155
- await client.session.prompt({
156
- path: { id: sessionId },
157
- body: {
158
- parts: [
159
- {
160
- type: 'text',
161
- text: `✅ Quality gate passed`,
162
- },
163
- ],
164
- },
165
- });
166
- await log(client, 'debug', 'quality-gate.passed', { sessionId });
167
- } else {
152
+ if (!ciPassed) {
168
153
  await client.session.prompt({
169
154
  path: { id: sessionId },
170
155
  body: {
@@ -201,6 +186,7 @@ export async function createQualityGateHooks(input: PluginInput): Promise<Partia
201
186
  dirty: true,
202
187
  qualityGatePassed: false,
203
188
  idleDebounce: null,
189
+ running: false,
204
190
  parentID: sessionInfo?.parentID,
205
191
  isReadOnly,
206
192
  });