wogiflow 2.29.5 → 2.29.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "2.29.5",
3
+ "version": "2.29.7",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "scripts": {
12
12
  "flow": "./scripts/flow",
13
- "test": "NODE_ENV=test node --test tests/auto-compact-prompt.test.js tests/flow-paths.test.js tests/flow-io.test.js tests/flow-config-loader.test.js tests/flow-damage-control.test.js tests/flow-output.test.js tests/flow-constants.test.js tests/flow-session-state.test.js tests/flow-hooks-integration.test.js tests/flow-utils.test.js tests/flow-security.test.js tests/flow-memory-db.test.js tests/flow-durable-session.test.js tests/flow-skill-matcher.test.js tests/flow-bridge.test.js tests/flow-proactive-compact.test.js tests/flow-cascade-completion.test.js tests/flow-capture-gate.test.js tests/flow-correction-detector-hybrid.test.js tests/flow-promote.test.js tests/flow-archive-runs.test.js tests/flow-memory.test.js tests/flow-hooks-pre-tool-helpers.test.js tests/flow-hooks-bugfix-scope-gate.test.js tests/flow-hooks-routing-gate.test.js tests/flow-hooks-phase-read-gate.test.js tests/flow-hooks-commit-log-gate.test.js tests/flow-hooks-deploy-gate.test.js tests/flow-hooks-todowrite-gate.test.js tests/flow-hooks-git-safety-gate.test.js tests/flow-hooks-scope-mutation-gate.test.js tests/flow-hooks-strike-gate.test.js tests/flow-hooks-component-check.test.js tests/flow-hooks-scope-gate.test.js tests/flow-hooks-implementation-gate.test.js tests/flow-hooks-research-gate.test.js tests/flow-hooks-loop-check.test.js tests/flow-hooks-manager-boundary-gate.test.js tests/flow-hooks-phase-gate.test.js tests/flow-hooks-pre-tool-orchestrator.test.js tests/flow-hooks-observation-capture.test.js tests/flow-hooks-task-gate.test.js tests/flow-durable-session-suspension.test.js tests/flow-health-mcp-scopes.test.js tests/flow-lean-config.test.js tests/flow-workspace-autopickup.test.js tests/flow-worker-boundary-gate.test.js tests/flow-worker-question-classifier.test.js tests/flow-completion-truth-gate-contradictions.test.js tests/flow-structure-sensor.test.js tests/flow-workspace-dispatch-tracking.test.js tests/workspace-ipc-sqlite.test.js tests/workspace-ipc-multi-worker.test.js tests/flow-story-gates.test.js tests/flow-workspace-restart-handoff.test.js tests/flow-wogi-claude-wrapper.test.js tests/flow-wave1-integrations.test.js tests/flow-wave2-integrations.test.js tests/flow-wave3-integrations.test.js tests/flow-commit-claims-gate.test.js tests/auto-review.test.js tests/gate-telemetry-surface.test.js tests/agents-md-alias.test.js tests/flow-skill-manage.test.js tests/fuzzy-patch.test.js tests/mode-schema.test.js tests/flow-feature-dossier.test.js tests/flow-autonomous-mode.test.js tests/flow-epic-cascade.test.js tests/flow-workspace-summary.test.js tests/flow-hooks-research-evidence-gate.test.js tests/flow-worker-mcp-strip.test.js tests/flow-orchestrate-corrections.test.js tests/flow-source-fidelity.test.js tests/flow-hooks-long-input-enforcement.test.js tests/workspace-channel-tracking.test.js tests/flow-hooks-deletion-log.test.js && NODE_ENV=test node tests/run-quality-gates.test.js",
13
+ "test": "NODE_ENV=test node --test tests/auto-compact-prompt.test.js tests/flow-paths.test.js tests/flow-io.test.js tests/flow-config-loader.test.js tests/flow-damage-control.test.js tests/flow-output.test.js tests/flow-constants.test.js tests/flow-session-state.test.js tests/flow-hooks-integration.test.js tests/flow-utils.test.js tests/flow-security.test.js tests/flow-memory-db.test.js tests/flow-durable-session.test.js tests/flow-skill-matcher.test.js tests/flow-bridge.test.js tests/flow-proactive-compact.test.js tests/flow-cascade-completion.test.js tests/flow-capture-gate.test.js tests/flow-correction-detector-hybrid.test.js tests/flow-promote.test.js tests/flow-archive-runs.test.js tests/flow-memory.test.js tests/flow-hooks-pre-tool-helpers.test.js tests/flow-hooks-bugfix-scope-gate.test.js tests/flow-hooks-routing-gate.test.js tests/flow-hooks-phase-read-gate.test.js tests/flow-hooks-commit-log-gate.test.js tests/flow-hooks-deploy-gate.test.js tests/flow-hooks-todowrite-gate.test.js tests/flow-hooks-git-safety-gate.test.js tests/flow-hooks-scope-mutation-gate.test.js tests/flow-hooks-strike-gate.test.js tests/flow-hooks-component-check.test.js tests/flow-hooks-scope-gate.test.js tests/flow-hooks-implementation-gate.test.js tests/flow-hooks-research-gate.test.js tests/flow-hooks-loop-check.test.js tests/flow-hooks-manager-boundary-gate.test.js tests/flow-hooks-phase-gate.test.js tests/flow-hooks-pre-tool-orchestrator.test.js tests/flow-hooks-observation-capture.test.js tests/flow-hooks-task-gate.test.js tests/flow-durable-session-suspension.test.js tests/flow-health-mcp-scopes.test.js tests/flow-lean-config.test.js tests/flow-workspace-autopickup.test.js tests/flow-worker-boundary-gate.test.js tests/flow-worker-question-classifier.test.js tests/flow-completion-truth-gate-contradictions.test.js tests/flow-structure-sensor.test.js tests/flow-workspace-dispatch-tracking.test.js tests/workspace-ipc-sqlite.test.js tests/workspace-ipc-multi-worker.test.js tests/flow-story-gates.test.js tests/flow-workspace-restart-handoff.test.js tests/flow-wogi-claude-wrapper.test.js tests/flow-wave1-integrations.test.js tests/flow-wave2-integrations.test.js tests/flow-wave3-integrations.test.js tests/flow-commit-claims-gate.test.js tests/auto-review.test.js tests/gate-telemetry-surface.test.js tests/agents-md-alias.test.js tests/flow-skill-manage.test.js tests/fuzzy-patch.test.js tests/mode-schema.test.js tests/flow-feature-dossier.test.js tests/flow-autonomous-mode.test.js tests/flow-epic-cascade.test.js tests/flow-workspace-summary.test.js tests/flow-hooks-research-evidence-gate.test.js tests/flow-worker-mcp-strip.test.js tests/flow-orchestrate-corrections.test.js tests/flow-source-fidelity.test.js tests/flow-hooks-long-input-enforcement.test.js tests/workspace-channel-tracking.test.js tests/flow-hooks-deletion-log.test.js tests/flow-task-boundary-reset.test.js tests/flow-deferral-gate.test.js tests/flow-research-required-gate.test.js && NODE_ENV=test node tests/run-quality-gates.test.js",
14
14
  "test:syntax": "find scripts/ lib/ -name '*.js' -not -path '*/node_modules/*' -exec node --check {} +",
15
15
  "lint": "eslint scripts/ lib/ tests/",
16
16
  "lint:ci": "eslint scripts/ lib/ tests/ --max-warnings 0",
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow — Deferral Authorization CLI (wf-f9912af6)
5
+ *
6
+ * Explicit user-authorization helper for the deferral gate. Used when the AI
7
+ * needs to record that the user picked a defer-style menu option in
8
+ * /wogi-review (e.g., "Create tasks for all - fix later in batches").
9
+ *
10
+ * Usage:
11
+ * flow defer-auth grant --scope=all --reason="<verbatim user phrase>"
12
+ * flow defer-auth grant --findings=F5,F6,F7 --reason="..."
13
+ * flow defer-auth clear
14
+ * flow defer-auth status
15
+ */
16
+
17
+ const path = require('node:path');
18
+ const gate = require('./hooks/core/deferral-gate');
19
+
20
+ function parseArgs(argv) {
21
+ const args = {};
22
+ for (const a of argv) {
23
+ if (a.startsWith('--')) {
24
+ const eq = a.indexOf('=');
25
+ if (eq === -1) {
26
+ args[a.slice(2)] = true;
27
+ } else {
28
+ args[a.slice(2, eq)] = a.slice(eq + 1);
29
+ }
30
+ }
31
+ }
32
+ return args;
33
+ }
34
+
35
+ function cmdGrant(args) {
36
+ let scope = 'all';
37
+ if (args.findings) {
38
+ scope = String(args.findings)
39
+ .split(',')
40
+ .map(s => s.trim())
41
+ .filter(Boolean);
42
+ if (scope.length === 0) {
43
+ console.error('grant: --findings must be a non-empty comma-separated list');
44
+ process.exit(2);
45
+ }
46
+ } else if (args.scope === 'all' || args.scope === undefined) {
47
+ scope = 'all';
48
+ } else {
49
+ scope = String(args.scope);
50
+ }
51
+ const reason = args.reason ? String(args.reason) : 'cli-grant';
52
+ const ttlSec = args['ttl-sec'] ? parseInt(args['ttl-sec'], 10) : undefined;
53
+
54
+ const payload = gate.writeAuth({
55
+ scope,
56
+ source: reason,
57
+ grantedBy: 'explicit-cli',
58
+ ttlSec
59
+ });
60
+
61
+ if (!payload) {
62
+ console.error('grant: failed to write authorization marker');
63
+ process.exit(1);
64
+ }
65
+ console.log(JSON.stringify({ status: 'granted', ...payload }, null, 2));
66
+ }
67
+
68
+ function cmdClear() {
69
+ gate.clearAuth();
70
+ gate.clearNoDeferPin();
71
+ console.log(JSON.stringify({ status: 'cleared' }, null, 2));
72
+ }
73
+
74
+ function cmdStatus() {
75
+ const auth = gate.loadAuth();
76
+ const pin = gate.loadNoDeferPin();
77
+ console.log(JSON.stringify({
78
+ authorization: auth || null,
79
+ noDeferPin: pin || null,
80
+ authPath: gate.getAuthPath(),
81
+ pinPath: gate.getNoDeferPinPath()
82
+ }, null, 2));
83
+ }
84
+
85
+ function usage() {
86
+ console.log('Usage: flow defer-auth <grant|clear|status> [--scope=all|<id>] [--findings=F1,F2] [--reason="..."] [--ttl-sec=600]');
87
+ process.exit(2);
88
+ }
89
+
90
+ function main() {
91
+ const [, , subcommand, ...rest] = process.argv;
92
+ const args = parseArgs(rest);
93
+ switch (subcommand) {
94
+ case 'grant': return cmdGrant(args);
95
+ case 'clear': return cmdClear();
96
+ case 'status': return cmdStatus();
97
+ default: return usage();
98
+ }
99
+ }
100
+
101
+ if (require.main === module) main();
102
+
103
+ module.exports = { parseArgs, cmdGrant, cmdClear, cmdStatus };
@@ -336,6 +336,7 @@ function saveReadyData(data) {
336
336
  const toSave = { ...data, lastUpdated: new Date().toISOString() };
337
337
  const result = writeJson(PATHS.ready, toSave);
338
338
  invalidateReadyDataCache(); // Invalidate AFTER write completes to avoid stale cache race
339
+ maybeArmTaskBoundaryRestart(previousData, toSave);
339
340
  return result;
340
341
  }
341
342
 
@@ -359,10 +360,61 @@ async function saveReadyDataAsync(data) {
359
360
  const toSave = { ...data, lastUpdated: new Date().toISOString() };
360
361
  const result = writeJson(PATHS.ready, toSave);
361
362
  invalidateReadyDataCache(); // Invalidate AFTER write completes
363
+ maybeArmTaskBoundaryRestart(previousData, toSave);
362
364
  return result;
363
365
  });
364
366
  }
365
367
 
368
+ /**
369
+ * wf-ee4e343b — Phase 1 chokepoint for task-boundary auto-restart.
370
+ *
371
+ * Why this exists: Phase 1 marker writes were previously split across three
372
+ * disjoint paths (`flow done`, `task-completed.js` hook, Stop-hook fallback
373
+ * with a 5-min freshness window). The Stop-hook fallback misses real-world
374
+ * timing (user takes >5min to type next message → fallback rejects) and the
375
+ * other two paths are not always called. By detecting "new entry in
376
+ * recentlyCompleted" right here in saveReadyData — the actual chokepoint
377
+ * every completion goes through — we arm the marker at the moment of
378
+ * completion regardless of who completed the task.
379
+ *
380
+ * Gated on WOGI_WRAPPER_PID so test/CLI/non-wrapper invocations don't
381
+ * write spurious markers. Lazy-required to avoid circular dependency
382
+ * (task-boundary-reset.js → flow-utils.js).
383
+ */
384
+ function maybeArmTaskBoundaryRestart(previousData, savedData) {
385
+ try {
386
+ if (!process.env.WOGI_WRAPPER_PID) return;
387
+ // First-save guard (F2): when ready.json doesn't yet exist, previousData
388
+ // is null. If savedData arrives pre-populated (fresh install seeded from
389
+ // backup, init script bootstrapping recentlyCompleted, etc.) we MUST NOT
390
+ // arm a restart marker — there's no completion event, just an initial
391
+ // state snapshot. Real completions always have a previousData to diff
392
+ // against because saveReadyData is the only writer.
393
+ if (!previousData) return;
394
+ // F7 fix (wf-ee4e343b cleanup): F2 was asymmetric — readJson returns {}
395
+ // (truthy) on corrupt JSON or missing top-level keys, so the !previousData
396
+ // guard would not catch that case. A corrupt ready.json that recovers as
397
+ // {} followed by a save with populated recentlyCompleted would still
398
+ // false-positive. Require previousData.recentlyCompleted to be an actual
399
+ // array for the diff to be meaningful — anything else is "we don't know
400
+ // the prior state," which is structurally identical to first-save and
401
+ // must NOT arm.
402
+ if (!Array.isArray(previousData.recentlyCompleted)) return;
403
+ const prevTop = previousData.recentlyCompleted[0];
404
+ const curTop = savedData?.recentlyCompleted?.[0];
405
+ if (!curTop || !curTop.id) return;
406
+ if (prevTop && prevTop.id === curTop.id) return; // no new completion
407
+ const { markRestartPending } = require('./hooks/core/task-boundary-reset');
408
+ markRestartPending({
409
+ taskId: curTop.id,
410
+ taskTitle: curTop.title,
411
+ source: 'saveReadyData'
412
+ });
413
+ } catch (_err) {
414
+ // Fail-open — never let an observability/marker write break ready.json save
415
+ }
416
+ }
417
+
366
418
  /**
367
419
  * Archive overflow completed tasks to a log file (v3.2)
368
420
  * When recentlyCompleted exceeds 10 items, archive the overflow
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Flow — Deferral Intent Classifier (wf-f9912af6)
5
+ *
6
+ * Regex-based detector for explicit user deferral intent in UserPromptSubmit
7
+ * messages. Cheap (no Haiku call), deterministic, runs every prompt.
8
+ *
9
+ * NEGATIVE intent takes precedence over POSITIVE — if the user says both
10
+ * "fix everything" and "skip Y" in the same message, we assume they want
11
+ * everything fixed (the defer-everything pattern is the dangerous one this
12
+ * gate exists to stop).
13
+ *
14
+ * Negative match → write `no-defer-pin.json` (HARD block, overrides any auth)
15
+ * Positive match → write `deferral-authorization.json` (allows specific scope)
16
+ * Neither → no-op
17
+ *
18
+ * Fail-open: any error in classification falls through silently.
19
+ */
20
+
21
+ // Negative phrases (HIGH PRIORITY — clear auth, write no-defer pin)
22
+ const NEGATIVE_PATTERNS = [
23
+ /\bfix\s+(everything|all\s+of\s+(them|it)|all\s+findings?)\b/i,
24
+ /\bno\s+deferr?als?\b/i,
25
+ /\b(don'?t|do\s+not)\s+defer\b/i,
26
+ /\bi\s+don'?t\s+(want|like)\s+(tech\s*-?\s*debt|technical\s*-?\s*debt|deferr?al)/i,
27
+ /\bnever\s+defer\b/i,
28
+ /\balways\s+fix\s+(what'?s\s+broken|what\s+needs?\s+fixing)/i,
29
+ /\bnothing\s+(should\s+be|gets)\s+deferr?ed\b/i,
30
+ ];
31
+
32
+ // Positive phrases (MEDIUM PRIORITY — write auth marker)
33
+ // We're conservative: require defer/skip phrasing to be coupled with finding
34
+ // context (this/that/those/it/option N/F\d+/severity word) to avoid catching
35
+ // unrelated mentions like "let's defer the meeting".
36
+ const POSITIVE_PATTERNS = [
37
+ // "defer X" / "skip X" with a referent
38
+ /\b(defer|skip|ignore|drop)\s+(this|that|those|it|them|f\d+|finding\s+\w+)\b/i,
39
+ /\bleave\s+(this|that|those|f\d+|.*?)\s+(for\s+)?later\b/i,
40
+
41
+ // /wogi-review menu options that mean defer
42
+ /\boption\s*[24]\b/i, // option 2 = "fix critical only"; option 4 = "create tasks for all (defer)"
43
+ /\bcreate\s+tasks?\s+for\s+(all|the\s+rest|remaining)\b/i,
44
+
45
+ // Severity-scoped deferrals
46
+ /\bfix\s+(only\s+)?(critical|high)\s*(\s*\/\s*high)?\s+only\b/i,
47
+ /\bfix\s+(critical|high)\s+(only|first)\b/i,
48
+ /\bskip\s+(low|medium|low\s*\/\s*medium)\b/i,
49
+
50
+ // Ship-as-is style
51
+ /\bship\s+(it\s+)?as\s*-?\s*is\b/i,
52
+ /\bgood\s+enough\s+(as\s*-?\s*is|for\s+now)\b/i,
53
+ /\bcall\s+it\s+(done|good)\b/i,
54
+ ];
55
+
56
+ /**
57
+ * Classify a user prompt for deferral intent.
58
+ *
59
+ * @param {string} prompt - the user's UserPromptSubmit text
60
+ * @returns {{ intent: 'negative'|'positive'|'none', match?: string, scope?: string|string[] }}
61
+ */
62
+ function classifyDeferralIntent(prompt) {
63
+ if (!prompt || typeof prompt !== 'string') return { intent: 'none' };
64
+
65
+ // Negative first — overrides positive
66
+ for (const rx of NEGATIVE_PATTERNS) {
67
+ const m = prompt.match(rx);
68
+ if (m) return { intent: 'negative', match: m[0] };
69
+ }
70
+
71
+ // Positive
72
+ for (const rx of POSITIVE_PATTERNS) {
73
+ const m = prompt.match(rx);
74
+ if (m) {
75
+ // Try to extract scope — look for F\d+ ids in the prompt
76
+ const findingIds = Array.from(prompt.matchAll(/\bF\d+\b/g)).map(x => x[0]);
77
+ return {
78
+ intent: 'positive',
79
+ match: m[0],
80
+ scope: findingIds.length > 0 ? findingIds : 'all'
81
+ };
82
+ }
83
+ }
84
+
85
+ return { intent: 'none' };
86
+ }
87
+
88
+ /**
89
+ * Apply classification result to the gate's state files. Wired into
90
+ * UserPromptSubmit. Fail-open throughout.
91
+ */
92
+ function applyClassification(prompt, config) {
93
+ try {
94
+ if (config?.deferralGate?.classifyUserPrompts === false) return { applied: false, reason: 'classifier-disabled' };
95
+
96
+ const result = classifyDeferralIntent(prompt);
97
+ if (result.intent === 'none') return { applied: false, reason: 'no-match' };
98
+
99
+ // Lazy-require to avoid load-order coupling
100
+ const gate = require('./deferral-gate');
101
+
102
+ if (result.intent === 'negative') {
103
+ gate.writeNoDeferPin({ source: result.match });
104
+ return { applied: true, intent: 'negative', match: result.match };
105
+ }
106
+
107
+ if (result.intent === 'positive') {
108
+ gate.writeAuth({
109
+ scope: result.scope,
110
+ source: result.match,
111
+ grantedBy: 'user-prompt',
112
+ config
113
+ });
114
+ return { applied: true, intent: 'positive', match: result.match, scope: result.scope };
115
+ }
116
+
117
+ return { applied: false, reason: 'unhandled-intent' };
118
+ } catch (err) {
119
+ if (process.env.DEBUG) console.error(`[deferral-classifier] applyClassification error (fail-open): ${err.message}`);
120
+ return { applied: false, reason: `error: ${err.message}` };
121
+ }
122
+ }
123
+
124
+ module.exports = {
125
+ classifyDeferralIntent,
126
+ applyClassification,
127
+ NEGATIVE_PATTERNS,
128
+ POSITIVE_PATTERNS
129
+ };