@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.4

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 (167) hide show
  1. package/bin/doflow.js +2 -2
  2. package/core/.claude-plugin/plugin.json +1 -1
  3. package/core/.codex-plugin/plugin.json +1 -1
  4. package/core/.plugin/plugin.json +1 -1
  5. package/core/registry/storage.json +87 -83
  6. package/core/registry/verification.json +1 -1
  7. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
  8. package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
  9. package/package.json +4 -2
  10. package/src/adapters/antigravity/{index.ts → index.js} +60 -60
  11. package/src/adapters/claude/{index.ts → index.js} +48 -48
  12. package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
  13. package/src/adapters/codex/{agents.ts → agents.js} +24 -24
  14. package/src/adapters/codex/{config.ts → config.js} +49 -49
  15. package/src/adapters/codex/{hooks.ts → hooks.js} +23 -23
  16. package/src/adapters/codex/{index.ts → index.js} +98 -98
  17. package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
  18. package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
  19. package/src/adapters/copilot/{index.ts → index.js} +42 -42
  20. package/src/adapters/{copy-tree.ts → copy-tree.js} +70 -70
  21. package/src/adapters/gemini/{hooks.ts → hooks.js} +24 -24
  22. package/src/adapters/gemini/{index.ts → index.js} +41 -41
  23. package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
  24. package/src/adapters/{index.ts → index.js} +13 -13
  25. package/src/adapters/kiro/{index.ts → index.js} +34 -34
  26. package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
  27. package/src/adapters/opencode/{index.ts → index.js} +52 -52
  28. package/src/adapters/pi/{index.ts → index.js} +30 -30
  29. package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
  30. package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
  31. package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
  32. package/src/cli/commands/{install.ts → install.js} +24 -24
  33. package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
  34. package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
  35. package/src/cli/commands/{remove.ts → remove.js} +10 -10
  36. package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
  37. package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
  38. package/src/cli/commands/{status.ts → status.js} +24 -24
  39. package/src/cli/commands/{tools.ts → tools.js} +16 -16
  40. package/src/cli/commands/{update.ts → update.js} +19 -19
  41. package/src/cli/{index.ts → index.js} +26 -26
  42. package/src/cli/{runtime-commands.ts → runtime-commands.js} +48 -48
  43. package/src/cli/{shared.ts → shared.js} +40 -40
  44. package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
  45. package/src/helper/{git.ts → git.js} +2 -2
  46. package/src/helper/{guardrails.ts → guardrails.js} +10 -10
  47. package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
  48. package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
  49. package/src/helper/{prompt.ts → prompt.js} +5 -5
  50. package/src/helper/{repo-root.ts → repo-root.js} +2 -2
  51. package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
  52. package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
  53. package/src/helper/{toml.ts → toml.js} +48 -48
  54. package/src/install/{backup-set.ts → backup-set.js} +16 -16
  55. package/src/install/{backup.ts → backup.js} +65 -65
  56. package/src/install/{context.ts → context.js} +15 -15
  57. package/src/install/{manifest.ts → manifest.js} +27 -27
  58. package/src/install/{mcp.ts → mcp.js} +30 -30
  59. package/src/install/{paths.ts → paths.js} +15 -15
  60. package/src/install/{targets.ts → targets.js} +10 -10
  61. package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
  62. package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
  63. package/src/lifecycle/{index.ts → index.js} +140 -140
  64. package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
  65. package/src/lifecycle/{policies.ts → policies.js} +11 -11
  66. package/src/lifecycle/{view.ts → view.js} +28 -28
  67. package/src/registry/{index.ts → index.js} +60 -60
  68. package/src/release/build-package.js +91 -0
  69. package/src/release/changelog-section.js +70 -0
  70. package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
  71. package/src/runtime/advice/{cli.ts → cli.js} +31 -31
  72. package/src/runtime/advice/{client.ts → client.js} +44 -44
  73. package/src/runtime/advice/{config.ts → config.js} +16 -16
  74. package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
  75. package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
  76. package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
  77. package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
  78. package/src/runtime/advice/{record.ts → record.js} +19 -19
  79. package/src/runtime/advice/{registry.ts → registry.js} +16 -16
  80. package/src/runtime/advice/{strength.ts → strength.js} +7 -7
  81. package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
  82. package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
  83. package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
  84. package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
  85. package/src/runtime/{claims.ts → claims.js} +30 -30
  86. package/src/runtime/{cli-result.ts → cli-result.js} +4 -4
  87. package/src/runtime/{cli.ts → cli.js} +37 -37
  88. package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
  89. package/src/runtime/{context-pack.ts → context-pack.js} +20 -20
  90. package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
  91. package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +25 -25
  92. package/src/runtime/failure/{capture.ts → capture.js} +29 -29
  93. package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
  94. package/src/runtime/failure/{cli.ts → cli.js} +30 -30
  95. package/src/runtime/failure/{home.ts → home.js} +6 -6
  96. package/src/runtime/failure/{store.ts → store.js} +32 -32
  97. package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
  98. package/src/runtime/{freshness.ts → freshness.js} +15 -15
  99. package/src/runtime/{health.ts → health.js} +72 -72
  100. package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
  101. package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
  102. package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
  103. package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
  104. package/src/runtime/inventory/{index.ts → index.js} +36 -36
  105. package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
  106. package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
  107. package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
  108. package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
  109. package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
  110. package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
  111. package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
  112. package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
  113. package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
  114. package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
  115. package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
  116. package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
  117. package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
  118. package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
  119. package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
  120. package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
  121. package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
  122. package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
  123. package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
  124. package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
  125. package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
  126. package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
  127. package/src/runtime/{mask.ts → mask.js} +22 -22
  128. package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
  129. package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
  130. package/src/runtime/{model-router.ts → model-router.js} +20 -20
  131. package/src/runtime/{outcome.ts → outcome.js} +27 -27
  132. package/src/runtime/{reach.ts → reach.js} +21 -21
  133. package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
  134. package/src/runtime/{readiness.ts → readiness.js} +35 -35
  135. package/src/runtime/{recovery.ts → recovery.js} +15 -15
  136. package/src/runtime/{research-request.ts → research-request.js} +34 -34
  137. package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +40 -40
  138. package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +19 -19
  139. package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
  140. package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
  141. package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
  142. package/src/runtime/storage/{cli.ts → cli.js} +9 -9
  143. package/src/runtime/storage/{engine.ts → engine.js} +37 -37
  144. package/src/runtime/storage/{registry.ts → registry.js} +19 -19
  145. package/src/runtime/storage/{roots.ts → roots.js} +8 -8
  146. package/src/runtime/storage/{rules.ts → rules.js} +26 -26
  147. package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
  148. package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
  149. package/src/runtime/{task-attempts.ts → task-attempts.js} +61 -61
  150. package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
  151. package/src/runtime/{task-scope.ts → task-scope.js} +13 -13
  152. package/src/runtime/{task-state.ts → task-state.js} +15 -15
  153. package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
  154. package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
  155. package/src/runtime/trace/{render.ts → render.js} +7 -7
  156. package/src/runtime/trace/{views.ts → views.js} +40 -40
  157. package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
  158. package/src/runtime/verification/{engine.ts → engine.js} +143 -143
  159. package/src/runtime/verification/{registry.ts → registry.js} +6 -6
  160. package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
  161. package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
  162. package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
  163. package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
  164. package/src/runtime/{worktree.ts → worktree.js} +31 -31
  165. package/src/state/{index.ts → index.js} +26 -26
  166. package/src/state/{lockfile.ts → lockfile.js} +20 -20
  167. package/src/state/{run-lock.ts → run-lock.js} +44 -44
@@ -22,11 +22,11 @@
22
22
  const fs = require('node:fs');
23
23
  const path = require('node:path');
24
24
  const crypto = require('node:crypto');
25
- const { updateTaskState, readTaskState } = require('./task-state.ts');
26
- const { parsePlan } = require('./scaffold/artifacts.ts');
27
- const { EvidenceLedger } = require('./evidence-ledger.ts');
28
- const { projectRoot: lifecycleProjectRoot } = require('./lifecycle/root.ts');
29
- const { isSafeSlug } = require('./task-scope.ts');
25
+ const { updateTaskState, readTaskState } = require('./task-state.js');
26
+ const { parsePlan } = require('./scaffold/artifacts.js');
27
+ const { EvidenceLedger } = require('./evidence-ledger.js');
28
+ const { projectRoot: lifecycleProjectRoot } = require('./lifecycle/root.js');
29
+ const { isSafeSlug } = require('./task-scope.js');
30
30
 
31
31
  const SCHEMA_VERSION = 1;
32
32
  const MODES = Object.freeze(['manual', 'sequential', 'subagent']);
@@ -37,8 +37,8 @@ const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._ -]*$/;
37
37
 
38
38
  /** A failure that is the caller's or the environment's, not a finding: exit 2. */
39
39
  class TaskAttemptError extends Error {
40
- declare code: any;
41
- constructor(code: any, message: any) {
40
+
41
+ constructor(code , message ) {
42
42
  super(message);
43
43
  this.name = 'TaskAttemptError';
44
44
  this.code = code;
@@ -48,13 +48,13 @@ class TaskAttemptError extends Error {
48
48
  /** Thrown inside the locked build to abandon a write without touching the store. */
49
49
  class NoWrite extends Error {}
50
50
 
51
- function storeFile(root: any, slug: any) {
51
+ function storeFile(root , slug ) {
52
52
  return path.join(root, '.doflow', 'state', 'task-attempts', `${slug}.json`);
53
53
  }
54
54
 
55
- function uniq(values: any) { return [...new Set(values)]; }
55
+ function uniq(values ) { return [...new Set(values)]; }
56
56
 
57
- function assertSlug(slug: any) {
57
+ function assertSlug(slug ) {
58
58
  if (!isSafeSlug(slug)) throw new TaskAttemptError('usage', `'${slug}' is not a valid feature slug`);
59
59
  return slug;
60
60
  }
@@ -63,7 +63,7 @@ function assertSlug(slug: any) {
63
63
 
64
64
  /** The hash a claim records and `finish` compares: everything about the task's plan line except
65
65
  * its checkbox, so ticking the box after a finish is not an edit of the task. */
66
- function taskLineHash(task: any) {
66
+ function taskLineHash(task ) {
67
67
  const material = JSON.stringify([
68
68
  task.id, task.description, task.stories, task.owner, task.files, task.dependsOn, task.externalContract,
69
69
  ]);
@@ -74,27 +74,27 @@ function taskLineHash(task: any) {
74
74
  * The plan's tasks, in plan order, with phase order taken from first appearance.
75
75
  * @returns {{planPath: string, tasks: Array<{id: string, phase: string, phaseIndex: number, checked: boolean, hash: string}>}}
76
76
  */
77
- function loadPlan({ roots, slug, fsImpl = fs }: any) {
77
+ function loadPlan({ roots, slug, fsImpl = fs } ) {
78
78
  const relative = path.join('agent-docs', 'doflow', slug, 'plan.md');
79
- const found = roots.map((root: any) => path.join(root, relative)).find((file: any) => fsImpl.existsSync(file));
79
+ const found = roots.map((root ) => path.join(root, relative)).find((file ) => fsImpl.existsSync(file));
80
80
  if (!found) throw new TaskAttemptError('plan-unreadable', `no plan.md for feature '${slug}' (looked for ${relative} under ${roots.join(', ')})`);
81
81
  let text;
82
- try { text = fsImpl.readFileSync(found, 'utf8'); } catch (error: any) {
82
+ try { text = fsImpl.readFileSync(found, 'utf8'); } catch (error ) {
83
83
  throw new TaskAttemptError('plan-unreadable', `cannot read ${found}: ${error.message}`);
84
84
  }
85
85
  const parsed = parsePlan(text);
86
86
  if (parsed.tasks.length === 0) {
87
- throw new TaskAttemptError('plan-malformed', `${found} holds no \`- [ ] <phase>.<n>\` tasks: ${parsed.gaps.map((g: any) => g.why).join('; ') || 'nothing to track'}`);
87
+ throw new TaskAttemptError('plan-malformed', `${found} holds no \`- [ ] <phase>.<n>\` tasks: ${parsed.gaps.map((g ) => g.why).join('; ') || 'nothing to track'}`);
88
88
  }
89
89
  const seen = new Set();
90
90
  for (const task of parsed.tasks) {
91
91
  if (seen.has(task.id)) throw new TaskAttemptError('plan-malformed', `plan task id '${task.id}' appears more than once in ${found}`);
92
92
  seen.add(task.id);
93
93
  }
94
- const phases = uniq(parsed.tasks.map((t: any) => t.phase));
94
+ const phases = uniq(parsed.tasks.map((t ) => t.phase));
95
95
  return {
96
96
  planPath: relative.split(path.sep).join('/'),
97
- tasks: parsed.tasks.map((t: any) => ({
97
+ tasks: parsed.tasks.map((t ) => ({
98
98
  id: t.id, phase: t.phase, phaseIndex: phases.indexOf(t.phase), checked: t.done, hash: taskLineHash(t),
99
99
  })),
100
100
  };
@@ -102,19 +102,19 @@ function loadPlan({ roots, slug, fsImpl = fs }: any) {
102
102
 
103
103
  // ── store ──────────────────────────────────────────────────────────────────────────────────────
104
104
 
105
- function unreadable(file: any, why: any) {
105
+ function unreadable(file , why ) {
106
106
  return new TaskAttemptError('store-unreadable', `${why} Refusing to read or write '${file}'; it was left unchanged.`);
107
107
  }
108
108
 
109
109
  /** The store as an object, or a fresh empty one when the file does not exist. Shape-checked: a file
110
110
  * that parses but is not a ledger is as unusable as one that does not parse. */
111
- function normalizeStore(data: any, file: any) {
111
+ function normalizeStore(data , file ) {
112
112
  if (data === null || data === undefined) return { version: SCHEMA_VERSION, revision: 0, planPath: null, tasks: {} };
113
113
  if (typeof data !== 'object' || Array.isArray(data) || data.version !== SCHEMA_VERSION
114
114
  || typeof data.tasks !== 'object' || data.tasks === null || Array.isArray(data.tasks)) {
115
115
  throw unreadable(file, 'It is not a version-1 task-attempt ledger.');
116
116
  }
117
- for (const [id, entry] of (Object.entries(data.tasks) as [string, any][])) {
117
+ for (const [id, entry] of (Object.entries(data.tasks) )) {
118
118
  if (!entry || !Number.isInteger(entry.nextToken) || !Array.isArray(entry.attempts)) {
119
119
  throw unreadable(file, `Task '${id}' has no readable attempt list.`);
120
120
  }
@@ -122,10 +122,10 @@ function normalizeStore(data: any, file: any) {
122
122
  return data;
123
123
  }
124
124
 
125
- function readStore(file: any, fsImpl: any) {
125
+ function readStore(file , fsImpl ) {
126
126
  try {
127
127
  return normalizeStore(readTaskState(fsImpl, file), file);
128
- } catch (error: any) {
128
+ } catch (error ) {
129
129
  if (error instanceof TaskAttemptError) throw error;
130
130
  throw unreadable(file, error.message);
131
131
  }
@@ -133,12 +133,12 @@ function readStore(file: any, fsImpl: any) {
133
133
 
134
134
  /** One locked read-decide-write. `apply(store)` returns `{result, write}`; with `write` false the
135
135
  * store is not touched, which is how a refusal or a stale token changes nothing. */
136
- function mutate({ file, fsImpl, apply }: any) {
137
- let outcome: any = null;
136
+ function mutate({ file, fsImpl, apply } ) {
137
+ let outcome = null;
138
138
  try {
139
139
  updateTaskState({
140
140
  fsImpl, file,
141
- build: (disk: any) => {
141
+ build: (disk ) => {
142
142
  const store = normalizeStore(disk, file);
143
143
  store.revision = (Number.isInteger(store.revision) ? store.revision : 0) + 1;
144
144
  outcome = apply(store);
@@ -146,7 +146,7 @@ function mutate({ file, fsImpl, apply }: any) {
146
146
  return store;
147
147
  },
148
148
  });
149
- } catch (error: any) {
149
+ } catch (error ) {
150
150
  if (error instanceof NoWrite) return outcome.result;
151
151
  if (error instanceof TaskAttemptError) throw error;
152
152
  throw unreadable(file, error.message);
@@ -154,24 +154,24 @@ function mutate({ file, fsImpl, apply }: any) {
154
154
  return outcome.result;
155
155
  }
156
156
 
157
- function entryFor(store: any, taskId: any) {
157
+ function entryFor(store , taskId ) {
158
158
  if (!store.tasks[taskId]) store.tasks[taskId] = { nextToken: 1, attempts: [] };
159
159
  return store.tasks[taskId];
160
160
  }
161
161
 
162
- const activeAttempt = (entry: any) => (entry ? entry.attempts.find((a: any) => a.state === 'active') || null : null);
163
- const doneAttempt = (entry: any) => (entry ? entry.attempts.find((a: any) => a.state === 'finished' && a.result === 'COMPLETED') || null : null);
162
+ const activeAttempt = (entry ) => (entry ? entry.attempts.find((a ) => a.state === 'active') || null : null);
163
+ const doneAttempt = (entry ) => (entry ? entry.attempts.find((a ) => a.state === 'finished' && a.result === 'COMPLETED') || null : null);
164
164
 
165
- function latestFinished(entry: any) {
166
- const finished = entry ? entry.attempts.filter((a: any) => a.state === 'finished') : [];
167
- return finished.length ? finished.reduce((a: any, b: any) => (b.token > a.token ? b : a)) : null;
165
+ function latestFinished(entry ) {
166
+ const finished = entry ? entry.attempts.filter((a ) => a.state === 'finished') : [];
167
+ return finished.length ? finished.reduce((a , b ) => (b.token > a.token ? b : a)) : null;
168
168
  }
169
169
 
170
170
  // ── evidence ───────────────────────────────────────────────────────────────────────────────────
171
171
 
172
172
  /** Evidence ids recorded in any task file under the evidence store, to tell "belongs to another task"
173
173
  * from "does not exist". Reads only; a file that cannot be read simply does not match. */
174
- function evidenceBelongsElsewhere({ roots, slug, evidenceId, fsImpl }: any) {
174
+ function evidenceBelongsElsewhere({ roots, slug, evidenceId, fsImpl } ) {
175
175
  for (const root of roots) {
176
176
  const base = path.join(root, '.doflow', 'state', 'evidence');
177
177
  for (const dir of [base, path.join(base, slug)]) {
@@ -181,7 +181,7 @@ function evidenceBelongsElsewhere({ roots, slug, evidenceId, fsImpl }: any) {
181
181
  if (!name.endsWith('.json')) continue;
182
182
  try {
183
183
  const data = JSON.parse(fsImpl.readFileSync(path.join(dir, name), 'utf8'));
184
- if (Array.isArray(data.evidence) && data.evidence.some((item: any) => item && item.id === evidenceId)) return true;
184
+ if (Array.isArray(data.evidence) && data.evidence.some((item ) => item && item.id === evidenceId)) return true;
185
185
  } catch { /* unreadable files do not match */ }
186
186
  }
187
187
  }
@@ -190,7 +190,7 @@ function evidenceBelongsElsewhere({ roots, slug, evidenceId, fsImpl }: any) {
190
190
  }
191
191
 
192
192
  /** The refusal reason for `evidenceId` as proof of a COMPLETED attempt, or null when it qualifies (FR-004). */
193
- function evidenceRefusal({ evidenceId, slug, taskId, startedAt, roots, fsImpl }: any) {
193
+ function evidenceRefusal({ evidenceId, slug, taskId, startedAt, roots, fsImpl } ) {
194
194
  if (!evidenceId) return 'evidence-required';
195
195
  const allowed = [slug, taskId];
196
196
  let item = null;
@@ -216,25 +216,25 @@ function evidenceRefusal({ evidenceId, slug, taskId, startedAt, roots, fsImpl }:
216
216
 
217
217
  // ── actions ────────────────────────────────────────────────────────────────────────────────────
218
218
 
219
- function rootsFor({ cwd, lifecycleRoot }: any) {
219
+ function rootsFor({ cwd, lifecycleRoot } ) {
220
220
  return uniq([path.resolve(cwd), lifecycleRoot]);
221
221
  }
222
222
 
223
- function claim({ file, fsImpl, roots, slug, task, owner, mode, now = new Date() }: any) {
223
+ function claim({ file, fsImpl, roots, slug, task, owner, mode, now = new Date() } ) {
224
224
  if (!TASK_ID.test(task || '')) throw new TaskAttemptError('usage', '--task is required: a plan task id such as A.1');
225
225
  if (!owner || !SAFE_NAME.test(owner)) throw new TaskAttemptError('usage', '--owner is required: the name of the agent or person taking the task');
226
226
  if (!MODES.includes(mode)) throw new TaskAttemptError('usage', `--mode is required: one of ${MODES.join(', ')}`);
227
227
  const plan = loadPlan({ roots, slug, fsImpl });
228
228
  return mutate({
229
229
  file, fsImpl,
230
- apply: (store: any) => {
231
- const refuse = (reason: any, extra: any = {}) => ({ write: false, result: { disposition: 'refused', taskId: task, reason, ...extra } });
232
- const target = plan.tasks.find((t: any) => t.id === task);
230
+ apply: (store ) => {
231
+ const refuse = (reason , extra = {}) => ({ write: false, result: { disposition: 'refused', taskId: task, reason, ...extra } });
232
+ const target = plan.tasks.find((t ) => t.id === task);
233
233
  if (!target) return refuse('unknown-task');
234
234
  if (doneAttempt(store.tasks[task])) return refuse('already-done');
235
235
  const active = activeAttempt(store.tasks[task]);
236
236
  if (active) return refuse('active-attempt', { activeToken: active.token, owner: active.owner, startedAt: active.startedAt });
237
- const unmet = plan.tasks.filter((t: any) => t.phaseIndex < target.phaseIndex && !doneAttempt(store.tasks[t.id])).map((t: any) => t.id);
237
+ const unmet = plan.tasks.filter((t ) => t.phaseIndex < target.phaseIndex && !doneAttempt(store.tasks[t.id])).map((t ) => t.id);
238
238
  if (unmet.length) return refuse('unmet-phase', { unmet });
239
239
  const entry = entryFor(store, task);
240
240
  const token = entry.nextToken;
@@ -249,23 +249,23 @@ function claim({ file, fsImpl, roots, slug, task, owner, mode, now = new Date()
249
249
  });
250
250
  }
251
251
 
252
- function stale(taskId: any, token: any, reason: any) {
252
+ function stale(taskId , token , reason ) {
253
253
  return { write: false, result: { disposition: 'stale', taskId, token, reason } };
254
254
  }
255
255
 
256
- function finish({ file, fsImpl, roots, slug, task, token, result, evidenceId }: any) {
256
+ function finish({ file, fsImpl, roots, slug, task, token, result, evidenceId } ) {
257
257
  if (!TASK_ID.test(task || '')) throw new TaskAttemptError('usage', '--task is required: a plan task id such as A.1');
258
258
  if (!Number.isInteger(token) || token < 1) throw new TaskAttemptError('usage', '--token is required: the token claim returned');
259
259
  if (!RESULTS.includes(result)) throw new TaskAttemptError('usage', `--result is required: one of ${RESULTS.join(', ')}`);
260
260
  return mutate({
261
261
  file, fsImpl,
262
- apply: (store: any) => {
262
+ apply: (store ) => {
263
263
  const active = activeAttempt(store.tasks[task]);
264
264
  if (!active) return stale(task, token, 'no-active-attempt');
265
265
  if (active.token !== token) return stale(task, token, 'token-mismatch');
266
- const refuse = (reason: any) => ({ write: false, result: { disposition: 'refused', taskId: task, token, reason } });
266
+ const refuse = (reason ) => ({ write: false, result: { disposition: 'refused', taskId: task, token, reason } });
267
267
  const plan = loadPlan({ roots, slug, fsImpl });
268
- const line = plan.tasks.find((t: any) => t.id === task);
268
+ const line = plan.tasks.find((t ) => t.id === task);
269
269
  if (!line) return refuse('unknown-task');
270
270
  if (line.hash !== active.taskLineHash) return refuse('plan-changed');
271
271
  if (result === 'COMPLETED') {
@@ -280,13 +280,13 @@ function finish({ file, fsImpl, roots, slug, task, token, result, evidenceId }:
280
280
  });
281
281
  }
282
282
 
283
- function release({ file, fsImpl, task, token, reason }: any) {
283
+ function release({ file, fsImpl, task, token, reason } ) {
284
284
  if (!TASK_ID.test(task || '')) throw new TaskAttemptError('usage', '--task is required: a plan task id such as A.1');
285
285
  if (!Number.isInteger(token) || token < 1) throw new TaskAttemptError('usage', '--token is required: the token claim returned');
286
286
  if (!reason || !String(reason).trim()) throw new TaskAttemptError('usage', '--reason is required: why the attempt is being released');
287
287
  return mutate({
288
288
  file, fsImpl,
289
- apply: (store: any) => {
289
+ apply: (store ) => {
290
290
  const active = activeAttempt(store.tasks[task]);
291
291
  if (!active) return stale(task, token, 'no-active-attempt');
292
292
  if (active.token !== token) return stale(task, token, 'token-mismatch');
@@ -298,10 +298,10 @@ function release({ file, fsImpl, task, token, reason }: any) {
298
298
  }
299
299
 
300
300
  /** Completion read from the ledger and the plan, never from checkboxes (FR-005). */
301
- function status({ file, fsImpl, roots, slug }: any) {
301
+ function status({ file, fsImpl, roots, slug } ) {
302
302
  const plan = loadPlan({ roots, slug, fsImpl });
303
303
  const store = readStore(file, fsImpl);
304
- const tasks = plan.tasks.map((t: any) => {
304
+ const tasks = plan.tasks.map((t ) => {
305
305
  const entry = store.tasks[t.id];
306
306
  const active = activeAttempt(entry);
307
307
  const done = doneAttempt(entry);
@@ -310,7 +310,7 @@ function status({ file, fsImpl, roots, slug }: any) {
310
310
  if (done) derived = 'done';
311
311
  else if (active) derived = 'running';
312
312
  else if (last && last.result === 'BLOCKED') derived = 'blocked';
313
- else if (plan.tasks.every((o: any) => o.phaseIndex >= t.phaseIndex || doneAttempt(store.tasks[o.id]))) derived = 'ready';
313
+ else if (plan.tasks.every((o ) => o.phaseIndex >= t.phaseIndex || doneAttempt(store.tasks[o.id]))) derived = 'ready';
314
314
  else derived = 'pending';
315
315
  return {
316
316
  id: t.id, phase: t.phase, derived,
@@ -320,10 +320,10 @@ function status({ file, fsImpl, roots, slug }: any) {
320
320
  };
321
321
  });
322
322
  return {
323
- complete: tasks.length > 0 && tasks.every((t: any) => t.derived === 'done'),
323
+ complete: tasks.length > 0 && tasks.every((t ) => t.derived === 'done'),
324
324
  revision: store.revision,
325
325
  tasks,
326
- unclaimedChecked: plan.tasks.filter((t: any) => t.checked && !doneAttempt(store.tasks[t.id])).map((t: any) => t.id),
326
+ unclaimedChecked: plan.tasks.filter((t ) => t.checked && !doneAttempt(store.tasks[t.id])).map((t ) => t.id),
327
327
  };
328
328
  }
329
329
 
@@ -333,26 +333,26 @@ function status({ file, fsImpl, roots, slug }: any) {
333
333
  * otherwise. A store or plan that cannot be read throws, so the gate refuses rather than guessing.
334
334
  * @returns {{state: 'none'|'complete'|'incomplete', slug: string|null, incomplete: Array<string>}}
335
335
  */
336
- function checkCompletion({ slug, cwd, fsImpl = fs }: any) {
336
+ function checkCompletion({ slug, cwd, fsImpl = fs } ) {
337
337
  if (!isSafeSlug(slug)) return { state: 'none', slug: null, incomplete: [] };
338
338
  const lifecycleRoot = lifecycleProjectRoot(cwd);
339
339
  const file = storeFile(lifecycleRoot, slug);
340
340
  if (!fsImpl.existsSync(file)) return { state: 'none', slug, incomplete: [] };
341
341
  const answer = status({ file, fsImpl, roots: rootsFor({ cwd, lifecycleRoot }), slug });
342
- const incomplete = answer.tasks.filter((t: any) => t.derived !== 'done').map((t: any) => t.id);
342
+ const incomplete = answer.tasks.filter((t ) => t.derived !== 'done').map((t ) => t.id);
343
343
  return { state: answer.complete ? 'complete' : 'incomplete', slug, incomplete };
344
344
  }
345
345
 
346
346
  // ── CLI ────────────────────────────────────────────────────────────────────────────────────────
347
347
 
348
- function failure(code: any, message: any, json: any) {
349
- const { finishRuntime } = require('./cli-result.ts');
348
+ function failure(code , message , json ) {
349
+ const { finishRuntime } = require('./cli-result.js');
350
350
  if (json) console.log(JSON.stringify({ ok: false, error: code, message }, null, 2));
351
351
  else console.error(`doflow task-attempt: ${message}`);
352
352
  return finishRuntime(2);
353
353
  }
354
354
 
355
- function describe(result: any) {
355
+ function describe(result ) {
356
356
  const parts = [`${result.disposition}`];
357
357
  if (result.taskId) parts.push(`task ${result.taskId}`);
358
358
  if (result.token !== undefined) parts.push(`token ${result.token}`);
@@ -370,8 +370,8 @@ function describe(result: any) {
370
370
  function handleTaskAttemptCommand({
371
371
  action = 'status', slug = null, task, owner, mode, token, result, evidenceId, reason, json = false,
372
372
  cwd = process.cwd(), resolveSlug = null, fsImpl = fs, now = new Date(),
373
- }: any = {}) {
374
- const { finishRuntime } = require('./cli-result.ts');
373
+ } = {}) {
374
+ const { finishRuntime } = require('./cli-result.js');
375
375
  try {
376
376
  if (!ACTIONS.includes(action)) throw new TaskAttemptError('usage', `unknown action '${action}'; valid: ${ACTIONS.join(' | ')}`);
377
377
  const feature = slug || (resolveSlug ? resolveSlug(cwd) : null);
@@ -403,7 +403,7 @@ function handleTaskAttemptCommand({
403
403
  if (answer.unclaimedChecked.length) console.log(`Checked in plan.md with no COMPLETED attempt: ${answer.unclaimedChecked.join(', ')}`);
404
404
  } else (ok ? console.log : console.error)(describe(answer));
405
405
  return finishRuntime(ok ? 0 : 1);
406
- } catch (error: any) {
406
+ } catch (error ) {
407
407
  if (error instanceof TaskAttemptError) return failure(error.code, error.message, json);
408
408
  throw error;
409
409
  }
@@ -5,16 +5,16 @@
5
5
  const path = require('node:path');
6
6
  const fs = require('node:fs');
7
7
  const { TextDecoder } = require('node:util');
8
- const { WorkflowEngine, CALLER_ROLES } = require('./workflow-engine.ts');
9
- const { finishRuntime } = require('./cli-result.ts');
10
- const { REPO_ROOT } = require('../helper/repo-root.ts');
11
- const { loadAdviceRegistry } = require('./advice/registry.ts');
12
- const { createAdviceClient } = require('./advice/client.ts');
13
- const { materializeKind } = require('./advice/kinds.ts');
14
- const { appendOutcome, findPick } = require('./advice/record.ts');
15
- const { strengthWords } = require('./advice/strength.ts');
16
- const { loadStorageRegistry } = require('./storage/registry.ts');
17
- const { resolveRoots } = require('./storage/roots.ts');
8
+ const { WorkflowEngine, CALLER_ROLES } = require('./workflow-engine.js');
9
+ const { finishRuntime } = require('./cli-result.js');
10
+ const { REPO_ROOT } = require('../helper/repo-root.js');
11
+ const { loadAdviceRegistry } = require('./advice/registry.js');
12
+ const { createAdviceClient } = require('./advice/client.js');
13
+ const { materializeKind } = require('./advice/kinds.js');
14
+ const { appendOutcome, findPick } = require('./advice/record.js');
15
+ const { strengthWords } = require('./advice/strength.js');
16
+ const { loadStorageRegistry } = require('./storage/registry.js');
17
+ const { resolveRoots } = require('./storage/roots.js');
18
18
 
19
19
 
20
20
  /**
@@ -79,7 +79,7 @@ const NEAR_MISS_KEYS = ['class', 'taskType', 'type', 'proposedClass', 'workflow'
79
79
  * silent skip of the fit check. */
80
80
  const CALLER_NEAR_MISS_KEYS = ['caller', 'callerSkill', 'skill', 'skillId', 'invokedBy', 'calledBy'];
81
81
 
82
- function isPlainObject(value: any) {
82
+ function isPlainObject(value ) {
83
83
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
84
84
  }
85
85
 
@@ -90,7 +90,7 @@ function isPlainObject(value: any) {
90
90
  * a silent match. Trimming here would be a small coercion that hides a real formatting bug in the
91
91
  * caller's output.
92
92
  */
93
- function readProposal(proposal: any) {
93
+ function readProposal(proposal ) {
94
94
  if (proposal === null || proposal === undefined) {
95
95
  return {
96
96
  taskClass: null, rationale: null, proposedBy: null, callingSkill: null,
@@ -138,10 +138,10 @@ function readProposal(proposal: any) {
138
138
  * every `do-*` skill. That is acceptable for a hint nobody acts on; it is why the messages present
139
139
  * the result as "Did you mean" rather than as a shortlist.
140
140
  */
141
- function suggestFrom(proposed: any, candidates: any) {
141
+ function suggestFrom(proposed , candidates ) {
142
142
  if (typeof proposed !== 'string' || proposed.trim() === '') return [];
143
143
  const needle = proposed.trim().toLowerCase();
144
- return candidates.filter((candidate: any) => {
144
+ return candidates.filter((candidate ) => {
145
145
  const other = candidate.toLowerCase();
146
146
  return other === needle || other.includes(needle) || needle.includes(other);
147
147
  });
@@ -163,8 +163,8 @@ function suggestFrom(proposed: any, candidates: any) {
163
163
  * hostedStageIds: Array<string>, hostingClasses: Array<{taskClass: string, stageIds: Array<string>}>,
164
164
  * callerSuggestions: Array<string>}} frozen
165
165
  */
166
- function judgeFit(engine: any, taskClass: string, callingSkill: any, nearMissCallerKeys: any) {
167
- const unevaluated = (reason: any, extra: any = {}) => Object.freeze({
166
+ function judgeFit(engine , taskClass , callingSkill , nearMissCallerKeys ) {
167
+ const unevaluated = (reason , extra = {}) => Object.freeze({
168
168
  state: FIT_STATES.NOT_EVALUATED,
169
169
  reason,
170
170
  callingSkill,
@@ -194,7 +194,7 @@ function judgeFit(engine: any, taskClass: string, callingSkill: any, nearMissCal
194
194
  // Derived from the stage lists rather than declared, so it cannot drift from them. On HOSTED it
195
195
  // includes the accepted class: it is the caller's complete map, not a list of alternatives.
196
196
  const hostingClasses = engine.classesHosting(callingSkill);
197
- const decided = (state: any, reason: any, hostedStageIds: any = []) => Object.freeze({
197
+ const decided = (state , reason , hostedStageIds = []) => Object.freeze({
198
198
  state, reason, callingSkill, callerRole, hostedStageIds, hostingClasses, callerSuggestions: [],
199
199
  });
200
200
 
@@ -212,9 +212,9 @@ function judgeFit(engine: any, taskClass: string, callingSkill: any, nearMissCal
212
212
  }
213
213
 
214
214
  /** `bug (root-cause), refactor (architecture-mapping)` — the caller's hosting map, as prose. */
215
- function renderHostingClasses(hostingClasses: any) {
215
+ function renderHostingClasses(hostingClasses ) {
216
216
  return hostingClasses
217
- .map(({ taskClass, stageIds }: any) => `${taskClass} (${stageIds.join(', ')})`)
217
+ .map(({ taskClass, stageIds } ) => `${taskClass} (${stageIds.join(', ')})`)
218
218
  .join(', ');
219
219
  }
220
220
 
@@ -223,7 +223,7 @@ function renderHostingClasses(hostingClasses: any) {
223
223
  * when fit was not evaluated: an operator must be able to read the blind spot off the message
224
224
  * without inspecting the JSON.
225
225
  */
226
- function describeAcceptedFit(fit: any, nearMissCallerKeys: any) {
226
+ function describeAcceptedFit(fit , nearMissCallerKeys ) {
227
227
  if (fit.state === FIT_STATES.HOSTED) {
228
228
  return ` It hosts '${fit.callingSkill}' at stage(s): ${fit.hostedStageIds.join(', ')}.`;
229
229
  }
@@ -233,7 +233,7 @@ function describeAcceptedFit(fit: any, nearMissCallerKeys: any) {
233
233
  }
234
234
  if (fit.reason === FIT_REASONS.CALLER_KEY_NEAR_MISS) {
235
235
  return ' Fit was NOT evaluated: the proposal carries '
236
- + `${nearMissCallerKeys.map((key: any) => `'${key}'`).join(', ')}; the calling skill must be given `
236
+ + `${nearMissCallerKeys.map((key ) => `'${key}'`).join(', ')}; the calling skill must be given `
237
237
  + 'as `callingSkill`. Membership is all this decision establishes.';
238
238
  }
239
239
  return ' Fit was NOT evaluated: no calling skill was named, so this decision establishes that the '
@@ -245,7 +245,7 @@ function describeAcceptedFit(fit: any, nearMissCallerKeys: any) {
245
245
  * The message for a rejection the fit check produced. Each one names the remedy: a rejection nobody
246
246
  * can act on is a stop rather than a fix.
247
247
  */
248
- function describeFitRejection(fit: any, taskClass: any, workflow: any, engine: any) {
248
+ function describeFitRejection(fit , taskClass , workflow , engine ) {
249
249
  if (fit.reason === REJECTION_REASONS.UNKNOWN_CALLER) {
250
250
  const hint = fit.callerSuggestions.length > 0
251
251
  ? ` Did you mean: ${fit.callerSuggestions.join(', ')}?`
@@ -272,14 +272,14 @@ function describeFitRejection(fit: any, taskClass: any, workflow: any, engine: a
272
272
  }
273
273
 
274
274
  class TaskClassifier {
275
- declare engine: any;
276
- declare now: any;
275
+
276
+
277
277
  /**
278
278
  * @param {Object} [options]
279
279
  * @param {WorkflowEngine} [options.engine] Pre-built engine; otherwise one is built from `options`.
280
280
  * @param {Function} [options.now] Clock returning an ISO timestamp, for deterministic tests.
281
281
  */
282
- constructor(options: any = {}) {
282
+ constructor(options = {}) {
283
283
  this.engine = options.engine || new WorkflowEngine(options);
284
284
  this.now = options.now || (() => new Date().toISOString());
285
285
  }
@@ -306,7 +306,7 @@ class TaskClassifier {
306
306
  * skill can occupy — including `NOT_EVALUATED` when no caller was named, since an unchecked fit
307
307
  * must never read as a checked one.
308
308
  */
309
- classify(proposal: any, context: any = {}) {
309
+ classify(proposal , context = {}) {
310
310
  const validClasses = this.listClasses();
311
311
  const parsed = readProposal(proposal);
312
312
  const base = {
@@ -411,7 +411,7 @@ class TaskClassifier {
411
411
  * @param {Object} [context]
412
412
  * @returns {Object} the resolved workflow
413
413
  */
414
- resolve(proposal: any, context: any = {}) {
414
+ resolve(proposal , context = {}) {
415
415
  const decision = this.classify(proposal, context);
416
416
  if (decision.outcome !== CLASSIFICATION_OUTCOMES.ACCEPTED) {
417
417
  throw new Error(decision.message);
@@ -438,7 +438,7 @@ class TaskClassifier {
438
438
  * @param {boolean} [options.json=false]
439
439
  * @returns {number|Promise<number>} exit code; advisory calls are asynchronous
440
440
  */
441
- function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill, request, requestFile, taskId, projectRoot, json = false }: any = {}) {
441
+ function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill, request, requestFile, taskId, projectRoot, json = false } = {}) {
442
442
  const classifier = new TaskClassifier({ repoRoot: REPO_ROOT });
443
443
  const decision = classifier.classify({ taskClass, rationale, proposedBy, callingSkill });
444
444
  const exitCode = decision.outcome === CLASSIFICATION_OUTCOMES.ACCEPTED
@@ -459,7 +459,7 @@ function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill,
459
459
  try {
460
460
  state = new TextDecoder('utf-8', { fatal: true }).decode(fs.readFileSync(requestFile));
461
461
  if (!state.length) throw new Error('file is empty');
462
- } catch (error: any) {
462
+ } catch (error ) {
463
463
  fileError = error;
464
464
  }
465
465
  }
@@ -471,7 +471,7 @@ function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill,
471
471
  const advice = loadAdviceRegistry({ repoRoot: REPO_ROOT });
472
472
  const storage = loadStorageRegistry({ repoRoot: REPO_ROOT });
473
473
  const roots = resolveRoots({ registry: storage, projectRoot: path.resolve(projectRoot || process.cwd()) });
474
- const source = (name: any) => JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'core/registry', name), 'utf8'));
474
+ const source = (name ) => JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'core/registry', name), 'utf8'));
475
475
  const sources = { workflows: source('workflows.json') };
476
476
  const built = materializeKind('task-class', { adviceRegistry: advice.ok ? advice.registry : null, sources });
477
477
  const client = createAdviceClient({ adviceRegistry: advice.ok ? advice.registry : null, roots, sources });
@@ -498,12 +498,12 @@ function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill,
498
498
  }
499
499
  }
500
500
 
501
- function emptySecondOpinion(status: any, reason: any) {
501
+ function emptySecondOpinion(status , reason ) {
502
502
  return { status, reason, pick: null, strength: null, between: null, agrees: null, proposed: null,
503
503
  pickId: null, probabilities: null, confidence: null, model: null };
504
504
  }
505
505
 
506
- function secondOpinion(result: any, proposed: any) {
506
+ function secondOpinion(result , proposed ) {
507
507
  const answer = result.answers?.[0];
508
508
  if (result.status !== 'answered' || !answer) return emptySecondOpinion(result.status, result.reason);
509
509
  return {
@@ -513,7 +513,7 @@ function secondOpinion(result: any, proposed: any) {
513
513
  };
514
514
  }
515
515
 
516
- function printClassifyDecision(decision: any, opinion: any) {
516
+ function printClassifyDecision(decision , opinion ) {
517
517
  console.log(`\nDoFlow Task Classification [${decision.outcome}]:`);
518
518
  console.log('═'.repeat(78));
519
519
  console.log(decision.message);
@@ -527,7 +527,7 @@ function printClassifyDecision(decision: any, opinion: any) {
527
527
  }
528
528
  if (decision.fit.state === 'NOT_HOSTED' && decision.fit.hostingClasses.length > 0) {
529
529
  console.log(`Classes that host ${decision.fit.callingSkill}: `
530
- + decision.fit.hostingClasses.map((h: any) => `${h.taskClass} (${h.stageIds.join(', ')})`).join(', '));
530
+ + decision.fit.hostingClasses.map((h ) => `${h.taskClass} (${h.stageIds.join(', ')})`).join(', '));
531
531
  }
532
532
  if (decision.workflow) {
533
533
  console.log('─'.repeat(78));