@orchestrator-claude/definitions 3.32.2 → 3.34.0

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 (117) hide show
  1. package/README.md +145 -0
  2. package/agents/adversarial-reviewer.md +2 -2
  3. package/agents/azure-devops-engineer.md +9 -1
  4. package/agents/cicd-platform-engineer.md +477 -0
  5. package/agents/debug-sidecar.md +6 -1
  6. package/agents/doc-sidecar.md +6 -1
  7. package/agents/frontend-engineer.md +173 -0
  8. package/agents/implementer.md +39 -23
  9. package/agents/methodology-researcher.md +2 -1
  10. package/agents/operational-artifact-builder.md +2 -1
  11. package/agents/orchestrator.md +47 -76
  12. package/agents/planner.md +2 -2
  13. package/agents/presentation-engineer.md +1 -1
  14. package/agents/python-data-engineer.md +214 -0
  15. package/agents/reviewer.md +16 -8
  16. package/agents/specifier.md +1 -1
  17. package/agents/task-generator.md +1 -1
  18. package/agents/test-sidecar.md +15 -4
  19. package/dist/cli.js +78 -0
  20. package/dist/cli.js.map +1 -1
  21. package/dist/commands/DiffCommand.js +1 -1
  22. package/dist/commands/DiffCommand.js.map +1 -1
  23. package/dist/commands/InstallCommand.d.ts +41 -0
  24. package/dist/commands/InstallCommand.d.ts.map +1 -0
  25. package/dist/commands/InstallCommand.js +253 -0
  26. package/dist/commands/InstallCommand.js.map +1 -0
  27. package/dist/http/ApiClient.d.ts +5 -0
  28. package/dist/http/ApiClient.d.ts.map +1 -1
  29. package/dist/http/ApiClient.js.map +1 -1
  30. package/dist/install/DevCheckoutDetector.d.ts +66 -0
  31. package/dist/install/DevCheckoutDetector.d.ts.map +1 -0
  32. package/dist/install/DevCheckoutDetector.js +150 -0
  33. package/dist/install/DevCheckoutDetector.js.map +1 -0
  34. package/dist/install/HooksWirer.d.ts +29 -0
  35. package/dist/install/HooksWirer.d.ts.map +1 -0
  36. package/dist/install/HooksWirer.js +80 -0
  37. package/dist/install/HooksWirer.js.map +1 -0
  38. package/dist/install/LocalDiffReporter.d.ts +25 -0
  39. package/dist/install/LocalDiffReporter.d.ts.map +1 -0
  40. package/dist/install/LocalDiffReporter.js +50 -0
  41. package/dist/install/LocalDiffReporter.js.map +1 -0
  42. package/dist/install/Materializer.d.ts +67 -0
  43. package/dist/install/Materializer.d.ts.map +1 -0
  44. package/dist/install/Materializer.js +196 -0
  45. package/dist/install/Materializer.js.map +1 -0
  46. package/dist/install/McpRegistrar.d.ts +63 -0
  47. package/dist/install/McpRegistrar.d.ts.map +1 -0
  48. package/dist/install/McpRegistrar.js +114 -0
  49. package/dist/install/McpRegistrar.js.map +1 -0
  50. package/dist/install/ProcessRunner.d.ts +25 -0
  51. package/dist/install/ProcessRunner.d.ts.map +1 -0
  52. package/dist/install/ProcessRunner.js +41 -0
  53. package/dist/install/ProcessRunner.js.map +1 -0
  54. package/dist/install/ProjectSettingsNeutralizer.d.ts +39 -0
  55. package/dist/install/ProjectSettingsNeutralizer.d.ts.map +1 -0
  56. package/dist/install/ProjectSettingsNeutralizer.js +100 -0
  57. package/dist/install/ProjectSettingsNeutralizer.js.map +1 -0
  58. package/dist/install/types.d.ts +52 -0
  59. package/dist/install/types.d.ts.map +1 -0
  60. package/dist/install/types.js +8 -0
  61. package/dist/install/types.js.map +1 -0
  62. package/dist/manifest/ManifestLoader.d.ts.map +1 -1
  63. package/dist/manifest/ManifestLoader.js +3 -2
  64. package/dist/manifest/ManifestLoader.js.map +1 -1
  65. package/dist/manifest/types.d.ts +3 -0
  66. package/dist/manifest/types.d.ts.map +1 -1
  67. package/dist/scripts/bundle-hooks.d.ts +39 -0
  68. package/dist/scripts/bundle-hooks.d.ts.map +1 -0
  69. package/dist/scripts/bundle-hooks.js +165 -0
  70. package/dist/scripts/bundle-hooks.js.map +1 -0
  71. package/dist/scripts/generate-manifest.d.ts +22 -3
  72. package/dist/scripts/generate-manifest.d.ts.map +1 -1
  73. package/dist/scripts/generate-manifest.js +78 -35
  74. package/dist/scripts/generate-manifest.js.map +1 -1
  75. package/hooks/dangling-guard.mjs +7426 -0
  76. package/hooks/dangling-guard.ts +223 -6
  77. package/hooks/gate-guardian.mjs +463 -0
  78. package/hooks/hooks.json +63 -0
  79. package/hooks/lib/additional-directories-sync.ts +295 -0
  80. package/hooks/lib/api-client.ts +388 -7
  81. package/hooks/lib/session-selection.ts +81 -0
  82. package/hooks/mailbox-listener.mjs +259 -0
  83. package/hooks/post-compact.mjs +7931 -0
  84. package/hooks/post-implement.mjs +238 -0
  85. package/hooks/session-start.mjs +8234 -0
  86. package/hooks/session-start.ts +264 -23
  87. package/hooks/sprint-registry.mjs +59 -0
  88. package/hooks/subagent-start.mjs +289 -0
  89. package/hooks/subagent-stop.mjs +7793 -0
  90. package/hooks/subagent-stop.ts +4 -2
  91. package/hooks/user-prompt.mjs +329 -0
  92. package/hooks/workflow-guard.mjs +7805 -0
  93. package/hooks/workflow-guard.ts +360 -119
  94. package/hooks-settings-template.json +63 -0
  95. package/manifest.json +124 -90
  96. package/mcp-servers.json +48 -0
  97. package/package.json +10 -4
  98. package/personas/auditor-compliance.md +104 -0
  99. package/personas/cfo-conservador.md +88 -0
  100. package/personas/designer-perfeccionista.md +104 -0
  101. package/personas/diretor-cetico-fmcg.md +80 -0
  102. package/personas/engenheiro-cetico-construcao.md +107 -0
  103. package/skills/artifact-generator/SKILL.md +1 -1
  104. package/skills/artifact-production/SKILL.md +28 -0
  105. package/skills/backlog-synthesis/SKILL.md +8 -3
  106. package/skills/checkpoint-protocol/SKILL.md +7 -0
  107. package/skills/infographic-engine/SKILL.md +116 -0
  108. package/skills/orchestrator/SKILL.md +93 -7
  109. package/skills/persona-library/SKILL.md +1 -1
  110. package/skills/project-conventions/SKILL.md +53 -30
  111. package/skills/project-management-enterprise/SKILL.md +1 -1
  112. package/skills/python-data-engineering/SKILL.md +792 -0
  113. package/skills/release/SKILL.md +29 -18
  114. package/skills/release/release.sh +65 -4
  115. package/skills/storybook-validated-loop/SKILL.md +110 -0
  116. package/skills/tdd-discipline/SKILL.md +16 -0
  117. package/skills/workflow-coordination/SKILL.md +117 -16
@@ -12,9 +12,15 @@ import {
12
12
  getWorkflowStatus,
13
13
  getLastCompletedWorkflow,
14
14
  consumeLastCompletedWorkflow,
15
+ getNextAction,
16
+ getEffectiveProjectId,
17
+ listInvocationStatesForWorkflow,
18
+ isInvocationStateStale,
19
+ hasFreshDispatchIntent,
15
20
  outputBlock,
16
21
  postTelemetry,
17
22
  type TelemetryPayload,
23
+ type InvocationStatePayload,
18
24
  } from './lib/api-client.js';
19
25
  import {
20
26
  parseJsonlSession,
@@ -116,6 +122,191 @@ async function emitFinopsTelemetry(
116
122
  }
117
123
  }
118
124
 
125
+ // ---------------------------------------------------------------------------
126
+ // TD-203: async-aware + cwd-scoped block-decision (pure, unit-testable)
127
+ // ---------------------------------------------------------------------------
128
+
129
+ export type DanglingAction = 'allow' | 'block' | 'info-allow';
130
+
131
+ export interface DanglingDecision {
132
+ action: DanglingAction;
133
+ reason: string;
134
+ }
135
+
136
+ /** Aggregated signal from listInvocationStatesForWorkflow() + isInvocationStateStale(). */
137
+ export type AgentInvocationSignal = 'fresh' | 'stale' | 'none';
138
+
139
+ export interface DanglingWorkflowInput {
140
+ id: string;
141
+ status: string;
142
+ phase: string;
143
+ /**
144
+ * TD-203 Class-B cwd-scope (TD-204): the workflow's OWN project, when the
145
+ * API surfaces it.
146
+ *
147
+ * VERIFIED (S365, Verify-Before-Assert): WorkflowStatus (api-client.ts)
148
+ * currently has NO projectId field. This is NOT an unaddressed gap — GET
149
+ * /api/v1/workflows (list) and GET /api/v1/workflows/:id/status are
150
+ * ALREADY server-side scoped by the X-Project-ID header sent on every
151
+ * apiGet call: WorkflowsController.getAll -> listWorkflows ->
152
+ * findByProjectId ("RF-002 (TD-183): always scoped via findByProjectId —
153
+ * never falls back to findAll()"), and WorkflowsController.getStatus ->
154
+ * row-scoped getStatus(id, projectId) -> 404 on cross-tenant (TD-184
155
+ * RF-001, Camada 1). Since BOTH calls this hook makes are already
156
+ * project-scoped server-side, it is structurally impossible, under the
157
+ * current architecture, for getActiveWorkflow() to return a workflow
158
+ * belonging to a project other than this hook's own resolved PROJECT_ID —
159
+ * this field is always undefined in production wiring today.
160
+ *
161
+ * This branch is kept (dormant, forward-compatible) and fully
162
+ * unit-tested via synthetic inputs, so it activates automatically the
163
+ * moment a projectId passthrough is added to the API surface, without
164
+ * requiring any further change to this decider.
165
+ */
166
+ projectId?: string | null;
167
+ }
168
+
169
+ export interface DanglingDecisionInput {
170
+ workflow: DanglingWorkflowInput | null;
171
+ /** This hook's own resolved project (getEffectiveProjectId()), or null when unresolved. */
172
+ currentProjectId: string | null;
173
+ agentInvocationState: AgentInvocationSignal;
174
+ hasPendingAction: boolean;
175
+ /**
176
+ * TD-205 bg-dispatch race: true when a dispatch-intent marker exists for
177
+ * this workflow AND is still within the short grace window (computed by
178
+ * `hasFreshDispatchIntent()` in api-client.ts — seconds, NOT the 4h
179
+ * INVOCATION_STATE_TTL_MS). The marker is stamped SYNCHRONOUSLY by
180
+ * pre-agent-dispatch.ts at PreToolUse[Agent] — which fires before the
181
+ * sub-agent spawns / before the turn yields — so it is guaranteed present
182
+ * even when a background dispatch + immediate yield lets this Stop hook
183
+ * race ahead of subagent-start.ts's async startInvocation()+
184
+ * writeInvocationState(). Optional (defaults to false) so pre-existing
185
+ * callers/tests that predate TD-205 are unaffected.
186
+ */
187
+ recentlyDispatchedWithinGraceWindow?: boolean;
188
+ }
189
+
190
+ const ACTIVE_WORKFLOW_STATUSES = ['in_progress', 'awaiting_agent', 'awaiting_approval'];
191
+
192
+ /**
193
+ * Pure decision function for the dangling-guard Stop hook (TD-203).
194
+ *
195
+ * Evaluates precedence rules 2-8 top-down, first match wins (rule 1 — the
196
+ * stop_hook_active loop guard — is handled in main() before this is called,
197
+ * unchanged from the pre-TD-203 behavior). Takes only injected inputs — no
198
+ * fs/API calls — so every branch is independently testable and
199
+ * deterministic.
200
+ */
201
+ export function decideDanglingAction(input: DanglingDecisionInput): DanglingDecision {
202
+ const {
203
+ workflow,
204
+ currentProjectId,
205
+ agentInvocationState,
206
+ hasPendingAction,
207
+ recentlyDispatchedWithinGraceWindow = false,
208
+ } = input;
209
+
210
+ // Rule 2: no active workflow -> allow
211
+ if (!workflow) {
212
+ return { action: 'allow', reason: 'No active workflow' };
213
+ }
214
+
215
+ // Rule 3: Class-B cwd-scope (TD-204) — the workflow belongs to a
216
+ // DIFFERENT project than this session's resolved project -> info-allow
217
+ // (empty domain, never a bypass; see DanglingWorkflowInput.projectId doc).
218
+ if (
219
+ workflow.projectId != null &&
220
+ currentProjectId != null &&
221
+ workflow.projectId !== currentProjectId
222
+ ) {
223
+ return {
224
+ action: 'info-allow',
225
+ reason: `Workflow ${workflow.id} belongs to project ${workflow.projectId}, not this session's project (${currentProjectId}) — empty domain, no-op (Class-B cwd-scope, TD-204)`,
226
+ };
227
+ }
228
+
229
+ // Rule 4: workflow status not in the active set -> allow
230
+ if (!ACTIVE_WORKFLOW_STATUSES.includes(workflow.status)) {
231
+ return {
232
+ action: 'allow',
233
+ reason: `Workflow ${workflow.id} status ${workflow.status} is not active`,
234
+ };
235
+ }
236
+
237
+ // Rule 5: a fresh in-session sub-agent invocation is in-flight -> info-allow.
238
+ // Re-invocation on agent completion advances the workflow — no abandonment.
239
+ if (agentInvocationState === 'fresh') {
240
+ return {
241
+ action: 'info-allow',
242
+ reason: `Workflow ${workflow.id} active, awaiting agent completion (fresh invocation signal); allowing stop, will resume on re-invocation`,
243
+ };
244
+ }
245
+
246
+ // Rule 6 (TD-205): the invocation-state signal is 'none' (subagent-start.ts
247
+ // has not yet written its file) BUT a dispatch-intent marker is still fresh
248
+ // -> info-allow. This is the bg-dispatch-race fix: pre-agent-dispatch.ts
249
+ // stamps the marker SYNCHRONOUSLY at PreToolUse[Agent] (fires before the
250
+ // sub-agent spawns / before the turn yields), so it is guaranteed present
251
+ // even when a background dispatch + immediate yield lets this Stop hook
252
+ // race ahead of subagent-start.ts's async write. Scoped strictly to 'none'
253
+ // — a 'stale' signal (rule 7 below) always takes precedence over any
254
+ // dispatch-intent, and the grace window is short (seconds, computed by
255
+ // hasFreshDispatchIntent()) so a genuinely-dead agent whose marker outlives
256
+ // the window still falls through to rule 9 (safety net intact — the window
257
+ // never becomes a permanent hole).
258
+ if (agentInvocationState === 'none' && recentlyDispatchedWithinGraceWindow) {
259
+ return {
260
+ action: 'info-allow',
261
+ reason: `Workflow ${workflow.id} active, no invocation-state file yet but a fresh dispatch-intent marker exists (bg-dispatch race, TD-205); allowing stop, will resume once subagent-start.ts lands or on the next Stop`,
262
+ };
263
+ }
264
+
265
+ // Rule 7: the ONLY "alive" signal is a STALE invocation (agent crashed) ->
266
+ // block. Safety net: without this fallback, async-awareness (rule 5)
267
+ // becomes a hole that lets a dead agent hold the workflow dangling forever.
268
+ if (agentInvocationState === 'stale') {
269
+ return {
270
+ action: 'block',
271
+ reason: `[DANGLING-GUARD] Workflow ${workflow.id} has a STALE agent invocation (agent likely crashed/died) and is still ${workflow.status}. You must call mcp__orchestrator-extended__completeWorkflow({ workflowId: '${workflow.id}' }) before ending the session.`,
272
+ };
273
+ }
274
+
275
+ // Rule 8: the workflow is explicitly waiting on a human (a pending action
276
+ // is parked, or the phase gate itself is awaiting_approval) -> info-allow.
277
+ // Ending the session here is expected; the human will resume.
278
+ if (hasPendingAction || workflow.status === 'awaiting_approval') {
279
+ return {
280
+ action: 'info-allow',
281
+ reason: `Workflow ${workflow.id} active, awaiting human (${hasPendingAction ? 'pending action parked' : 'awaiting_approval'}); allowing stop, will resume on re-invocation`,
282
+ };
283
+ }
284
+
285
+ // Rule 9: genuine dangling — active in THIS project, no fresh agent, not a
286
+ // human-wait. This is the case the guard exists for.
287
+ return {
288
+ action: 'block',
289
+ reason: `[DANGLING-GUARD] Workflow ${workflow.id} is still ${workflow.status} (phase: ${workflow.phase}). You must call mcp__orchestrator-extended__completeWorkflow({ workflowId: '${workflow.id}' }) before ending the session.`,
290
+ };
291
+ }
292
+
293
+ /**
294
+ * Aggregates raw invocation-state payloads (from listInvocationStatesForWorkflow)
295
+ * into the AgentInvocationSignal consumed by decideDanglingAction. ANY fresh
296
+ * invocation wins over a stale one (a live agent legitimizes the wait even if
297
+ * a stale file left behind by a different, earlier crashed agent also exists
298
+ * for the same workflow). Reuses isInvocationStateStale's default TTL
299
+ * (INVOCATION_STATE_TTL_MS, 4h) — the SAME staleness definition subagent-stop.ts
300
+ * already uses for its own zombie-completion safety net on the identical
301
+ * state files, so both hooks agree on when an invocation record is dead.
302
+ */
303
+ export function summarizeAgentInvocations(
304
+ invocations: InvocationStatePayload[]
305
+ ): AgentInvocationSignal {
306
+ if (invocations.length === 0) return 'none';
307
+ return invocations.some((inv) => !isInvocationStateStale(inv)) ? 'fresh' : 'stale';
308
+ }
309
+
119
310
  async function main(): Promise<void> {
120
311
  const stdin = await readStdin();
121
312
  log(HOOK, `Stop hook triggered — stdin keys: [${Object.keys(stdin).join(', ')}]`);
@@ -176,12 +367,38 @@ async function main(): Promise<void> {
176
367
 
177
368
  log(HOOK, `workflow=${workflow.id} status=${wfStatus} phase=${phase}`);
178
369
 
179
- const activeStatuses = ['in_progress', 'awaiting_agent', 'awaiting_approval'];
180
- if (activeStatuses.includes(wfStatus)) {
181
- log(HOOK, 'BLOCK stop (workflow still active)');
182
- outputBlock(
183
- `[DANGLING-GUARD] Workflow ${workflow.id} is still ${wfStatus} (phase: ${phase}). You must call mcp__orchestrator-extended__completeWorkflow({ workflowId: '${workflow.id}' }) before ending the session.`
184
- );
370
+ // TD-203: gather async-awareness + cwd-scope signals, then delegate the
371
+ // block/allow/info-allow decision to the pure decider.
372
+ const invocations = listInvocationStatesForWorkflow(workflow.id);
373
+ const agentInvocationState = summarizeAgentInvocations(invocations);
374
+ // TD-205: bg-dispatch race signal — a dispatch-intent marker stamped
375
+ // synchronously by pre-agent-dispatch.ts, still within its short grace
376
+ // window (api-client.ts's DISPATCH_INTENT_GRACE_WINDOW_MS).
377
+ const recentlyDispatchedWithinGraceWindow = hasFreshDispatchIntent(workflow.id);
378
+ const nextAction = await getNextAction(workflow.id);
379
+ const hasPendingAction = nextAction?.hasAction === true;
380
+ const currentProjectId = getEffectiveProjectId() || null;
381
+
382
+ const decision = decideDanglingAction({
383
+ // workflow.projectId intentionally omitted (undefined) — see
384
+ // DanglingWorkflowInput.projectId doc: not yet surfaced by the API,
385
+ // already covered transitively by server-side X-Project-ID scoping.
386
+ workflow: { id: workflow.id, status: wfStatus, phase },
387
+ currentProjectId,
388
+ agentInvocationState,
389
+ hasPendingAction,
390
+ recentlyDispatchedWithinGraceWindow,
391
+ });
392
+
393
+ log(HOOK, `decision=${decision.action} — ${decision.reason}`);
394
+
395
+ if (decision.action === 'block') {
396
+ outputBlock(decision.reason);
397
+ return;
398
+ }
399
+
400
+ if (decision.action === 'info-allow') {
401
+ console.error(`[DANGLING-GUARD] ${decision.reason}`);
185
402
  return;
186
403
  }
187
404