@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
@@ -4,7 +4,7 @@
4
4
 
5
5
  const fs = require('node:fs');
6
6
  const path = require('node:path');
7
- const { resolveActiveFeature } = require('./feature-resolve.ts');
7
+ const { resolveActiveFeature } = require('./feature-resolve.js');
8
8
 
9
9
  // Which feature a task record belongs to (feature 045, IC-001/IC-002).
10
10
  //
@@ -19,7 +19,7 @@ const SAFE_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
19
19
  const SLUG_RULE = 'use letters, digits, dot, underscore or dash, start with a letter or digit, and no ".."';
20
20
 
21
21
  /** The one shape check for a feature slug (IC-009); do-paths.sh applies the same rule in bash. */
22
- function isSafeSlug(slug: any): boolean {
22
+ function isSafeSlug(slug ) {
23
23
  return typeof slug === 'string' && SAFE_SLUG.test(slug) && !slug.includes('..');
24
24
  }
25
25
 
@@ -29,31 +29,31 @@ function isSafeSlug(slug: any): boolean {
29
29
  * @param {*} slug
30
30
  * @returns {{error: 'invalid-slug', message: string, hint: string}|null}
31
31
  */
32
- function invalidSlugRefusal(slug: any): { error: 'invalid-slug'; message: string; hint: string } | null {
32
+ function invalidSlugRefusal(slug ) {
33
33
  if (typeof slug !== 'string' || slug === '' || isSafeSlug(slug)) return null;
34
34
  return { error: 'invalid-slug', message: `slug "${slug}" is not a valid feature slug: ${SLUG_RULE}`, hint: SLUG_RULE };
35
35
  }
36
36
 
37
37
  /** (projectRoot, slug) -> { slug, hasRegister } | { reason }. One resolver spawn per key per process. */
38
- const featureCache = new Map<string, any>();
38
+ const featureCache = new Map ();
39
39
 
40
40
  /** A slug the CLI named explicitly (`--slug`), applied to every store call in this process that was
41
41
  * not given its own. The dispatcher sets it once so the verbs whose handlers build their ledger
42
42
  * internally (readiness, evidence) route the same way as the ones that take `slug` directly. */
43
- let defaultSlug: string | null = null;
43
+ let defaultSlug = null;
44
44
 
45
- function setDefaultSlug(slug: any): void {
45
+ function setDefaultSlug(slug ) {
46
46
  defaultSlug = typeof slug === 'string' && slug !== '' ? slug : null;
47
47
  }
48
48
 
49
- function clearTaskScopeCache(): void {
49
+ function clearTaskScopeCache() {
50
50
  featureCache.clear();
51
51
  }
52
52
 
53
53
  /** The resolver reports the git top level, which is the project root or one of its ancestors, and
54
54
  * the feature folders live directly under it — so no `agent-docs/doflow` on the way up means there
55
55
  * is nothing to resolve and the spawn is skipped. */
56
- function hasFeatureFolderAbove(projectRoot: string): boolean {
56
+ function hasFeatureFolderAbove(projectRoot ) {
57
57
  let dir = path.resolve(projectRoot);
58
58
  for (;;) {
59
59
  try {
@@ -65,10 +65,10 @@ function hasFeatureFolderAbove(projectRoot: string): boolean {
65
65
  }
66
66
  }
67
67
 
68
- function featureFor(projectRoot: string, slug: string | null | undefined): any {
68
+ function featureFor(projectRoot , slug ) {
69
69
  const key = `${path.resolve(projectRoot)}\0${slug || ''}`;
70
70
  if (featureCache.has(key)) return featureCache.get(key);
71
- let value: any = { reason: 'no-feature' };
71
+ let value = { reason: 'no-feature' };
72
72
  try {
73
73
  if (hasFeatureFolderAbove(projectRoot)) {
74
74
  const found = resolveActiveFeature({ projectRoot, slug: slug || null });
@@ -90,7 +90,7 @@ function featureFor(projectRoot: string, slug: string | null | undefined): any {
90
90
  * `reason` is `namespaced` when a namespace applies, else `no-feature`, `no-register` or
91
91
  * `feature-level-id`.
92
92
  */
93
- function resolveTaskScope({ projectRoot, taskId, slug = null }: { projectRoot: string; taskId: string; slug?: string | null }): { namespace: string | null; slug: string | null; reason: string } {
93
+ function resolveTaskScope({ projectRoot, taskId, slug = null } ) {
94
94
  const feature = featureFor(projectRoot, slug || defaultSlug);
95
95
  if (feature.reason) return { namespace: null, slug: null, reason: feature.reason };
96
96
  if (!feature.hasRegister) return { namespace: null, slug: feature.slug, reason: 'no-register' };
@@ -107,7 +107,7 @@ function resolveTaskScope({ projectRoot, taskId, slug = null }: { projectRoot: s
107
107
  * @param {string} options.slug
108
108
  * @returns {boolean}
109
109
  */
110
- function slugNamesNoFeature({ projectRoot, slug }: { projectRoot: string; slug: string }): boolean {
110
+ function slugNamesNoFeature({ projectRoot, slug } ) {
111
111
  const feature = featureFor(projectRoot, slug);
112
112
  return Boolean(feature.reason) || feature.exists === false;
113
113
  }
@@ -121,7 +121,7 @@ function slugNamesNoFeature({ projectRoot, slug }: { projectRoot: string; slug:
121
121
  * @param {string|null} [options.slug]
122
122
  * @returns {string}
123
123
  */
124
- function taskStoreDir({ projectRoot, store, taskId, slug = null }: { projectRoot: string; store: string; taskId: string; slug?: string | null }): string {
124
+ function taskStoreDir({ projectRoot, store, taskId, slug = null } ) {
125
125
  const base = path.join(projectRoot, '.doflow', 'state', store);
126
126
  const { namespace } = resolveTaskScope({ projectRoot, taskId, slug });
127
127
  return namespace ? path.join(base, namespace) : base;
@@ -28,8 +28,8 @@ const { isDeepStrictEqual } = require('node:util');
28
28
 
29
29
  /** Three-way merge against the reader's snapshot. Unchanged local records never overwrite a
30
30
  * newer disk version; concurrent edits to the same record must be retried after an explicit read. */
31
- function mergeRecords(local: any[], disk: any[], baseline: Map<any, any>): any[] {
32
- const merged = new Map(disk.map((item: any) => [item.id, item]));
31
+ function mergeRecords(local , disk , baseline ) {
32
+ const merged = new Map(disk.map((item ) => [item.id, item]));
33
33
  for (const item of local) {
34
34
  const previous = baseline.get(item.id);
35
35
  const current = merged.get(item.id);
@@ -48,7 +48,7 @@ const LOCK_RETRIES = 250;
48
48
  const LOCK_WAIT_MS = 20;
49
49
 
50
50
  /** Synchronous sleep without spinning: Atomics.wait on a throwaway buffer. */
51
- function sleep(ms: number): void {
51
+ function sleep(ms ) {
52
52
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
53
53
  }
54
54
 
@@ -59,28 +59,28 @@ function sleep(ms: number): void {
59
59
  * @param {string} file
60
60
  * @returns {(() => void)|null}
61
61
  */
62
- function tryAcquireLock(fsImpl: any, file: string): (() => void) | null {
62
+ function tryAcquireLock(fsImpl , file ) {
63
63
  const lockDir = `${file}.lock`;
64
64
  const release = () => { try { fsImpl.rmdirSync(lockDir); } catch { /* released is released */ } };
65
65
  try {
66
66
  fsImpl.mkdirSync(lockDir);
67
67
  return release;
68
- } catch (error: any) {
68
+ } catch (error ) {
69
69
  if (error.code !== 'EEXIST') throw error;
70
70
  }
71
- let lock: any;
72
- try { lock = fsImpl.statSync(lockDir); } catch (error: any) {
71
+ let lock ;
72
+ try { lock = fsImpl.statSync(lockDir); } catch (error ) {
73
73
  if (error.code !== 'ENOENT') return null;
74
- try { fsImpl.mkdirSync(lockDir); return release; } catch (retryError: any) {
74
+ try { fsImpl.mkdirSync(lockDir); return release; } catch (retryError ) {
75
75
  if (retryError.code === 'EEXIST') return null;
76
76
  throw retryError;
77
77
  }
78
78
  }
79
79
  if (Date.now() - lock.mtimeMs <= LOCK_STALE_MS) return null;
80
- try { fsImpl.rmdirSync(lockDir); } catch (error: any) {
80
+ try { fsImpl.rmdirSync(lockDir); } catch (error ) {
81
81
  if (error.code !== 'ENOENT') return null;
82
82
  }
83
- try { fsImpl.mkdirSync(lockDir); return release; } catch (error: any) {
83
+ try { fsImpl.mkdirSync(lockDir); return release; } catch (error ) {
84
84
  if (error.code === 'EEXIST') return null;
85
85
  throw error;
86
86
  }
@@ -93,7 +93,7 @@ function tryAcquireLock(fsImpl: any, file: string): (() => void) | null {
93
93
  * @param {string} file
94
94
  * @returns {() => void}
95
95
  */
96
- function acquireLock(fsImpl: any, file: string): () => void {
96
+ function acquireLock(fsImpl , file ) {
97
97
  const lockDir = `${file}.lock`;
98
98
  // The stale rule below is wall-clock, so the wait must be too: on a host where each attempt costs
99
99
  // real time, counting attempts alone would outlive a live holder's staleness window and then
@@ -123,12 +123,12 @@ function acquireLock(fsImpl: any, file: string): () => void {
123
123
  * @param {string} file
124
124
  * @returns {Object|null} the parsed payload
125
125
  */
126
- function readTaskState(fsImpl: any, file: string): any {
126
+ function readTaskState(fsImpl , file ) {
127
127
  if (!fsImpl.existsSync(file)) return null;
128
- let data: any;
128
+ let data ;
129
129
  try {
130
130
  data = JSON.parse(fsImpl.readFileSync(file, 'utf8'));
131
- } catch (error: any) {
131
+ } catch (error ) {
132
132
  throw new Error(`Failed to load state file '${file}': ${error.message}. The write path is atomic `
133
133
  + '(tmp + rename), so a torn file means outside interference; recover it from version control '
134
134
  + 'or move it aside and re-record the task\'s items.');
@@ -153,7 +153,7 @@ function readTaskState(fsImpl: any, file: string): any {
153
153
  * that must inspect or set aside what is there under the same exclusion as the write
154
154
  * @returns {string} the file path written
155
155
  */
156
- function updateTaskState({ fsImpl, file, build, prepare }: { fsImpl: any; file: string; build: (disk: any) => any; prepare?: () => void }): string {
156
+ function updateTaskState({ fsImpl, file, build, prepare } ) {
157
157
  fsImpl.mkdirSync(path.dirname(file), { recursive: true });
158
158
  const release = acquireLock(fsImpl, file);
159
159
  try {
@@ -26,16 +26,16 @@
26
26
 
27
27
  const fs = require('node:fs');
28
28
  const path = require('node:path');
29
- const { formatMs, finish } = require('./render.ts');
29
+ const { formatMs, finish } = require('./render.js');
30
30
 
31
31
  const ORCHESTRATION_DIRNAME = 'orchestration';
32
32
 
33
33
  /** Mirrors resolveRunsLocation's shape, for the sibling state directory this reads. */
34
- function orchestrationDir(root: string): string {
34
+ function orchestrationDir(root ) {
35
35
  return path.join(root, '.doflow', 'state', ORCHESTRATION_DIRNAME);
36
36
  }
37
37
 
38
- function parseAt(value: any): number | null {
38
+ function parseAt(value ) {
39
39
  const ms = Date.parse(value);
40
40
  return Number.isNaN(ms) ? null : ms;
41
41
  }
@@ -43,10 +43,10 @@ function parseAt(value: any): number | null {
43
43
  /** Every record in a directory, plus the names of the ones that did not parse. A file that cannot be
44
44
  * read is counted and named rather than skipped: a report that quietly drops what it could not read
45
45
  * overstates its own coverage, which is the defect `malformedLines` already guards against for runs. */
46
- function readRecords(dir: string, fsImpl: any = fs): any {
46
+ function readRecords(dir , fsImpl = fs) {
47
47
  if (!fsImpl.existsSync(dir)) return { records: [], unreadable: [], exists: false };
48
- const records: any[] = [];
49
- const unreadable: any[] = [];
48
+ const records = [];
49
+ const unreadable = [];
50
50
  for (const name of fsImpl.readdirSync(dir).sort()) {
51
51
  if (!name.endsWith('.json')) continue;
52
52
  try {
@@ -56,7 +56,7 @@ function readRecords(dir: string, fsImpl: any = fs): any {
56
56
  continue;
57
57
  }
58
58
  records.push(parsed);
59
- } catch (err: any) {
59
+ } catch (err ) {
60
60
  unreadable.push({ file: name, reason: err.message });
61
61
  }
62
62
  }
@@ -65,11 +65,11 @@ function readRecords(dir: string, fsImpl: any = fs): any {
65
65
 
66
66
  /** A stage row is "not work done" when the orchestrator backfilled it or imported it rather than
67
67
  * executing it. Its duration measures when the record was written, so it is listed but never averaged. */
68
- function isSynthetic(entry: any): boolean {
68
+ function isSynthetic(entry ) {
69
69
  return Boolean(entry.backfilled) || (entry.executionStatus && entry.executionStatus !== 'completed');
70
70
  }
71
71
 
72
- function median(values: number[]): number | null {
72
+ function median(values ) {
73
73
  if (!values.length) return null;
74
74
  const sorted = [...values].sort((a, b) => a - b);
75
75
  const mid = Math.floor(sorted.length / 2);
@@ -77,11 +77,11 @@ function median(values: number[]): number | null {
77
77
  }
78
78
 
79
79
  /** One run's rows, walking history in recorded order with a cursor so no interval is counted twice. */
80
- function runRows(record: any): any {
80
+ function runRows(record ) {
81
81
  const history = Array.isArray(record.history) ? record.history : [];
82
82
  let cursor = parseAt(record.startedAt);
83
- const stages: any[] = [];
84
- const gates: any[] = [];
83
+ const stages = [];
84
+ const gates = [];
85
85
  let reruns = 0;
86
86
 
87
87
  for (const entry of history) {
@@ -131,13 +131,13 @@ function runRows(record: any): any {
131
131
  /** Median elapsed per stage id across runs of one class. Synthetic rows are excluded here and still
132
132
  * listed in their run: averaging the moment a file was written into the figure most likely to be
133
133
  * quoted would corrupt it. */
134
- function aggregateStages(runs: any[]): any[] {
135
- const perStage = new Map<string, number[]>();
134
+ function aggregateStages(runs ) {
135
+ const perStage = new Map ();
136
136
  for (const run of runs) {
137
137
  for (const row of run.stages) {
138
138
  if (row.synthetic || row.elapsedMs === null || !row.stage) continue;
139
139
  if (!perStage.has(row.stage)) perStage.set(row.stage, []);
140
- perStage.get(row.stage)!.push(row.elapsedMs);
140
+ perStage.get(row.stage) .push(row.elapsedMs);
141
141
  }
142
142
  }
143
143
  return [...perStage.entries()].map(([stage, values]) => ({
@@ -146,14 +146,14 @@ function aggregateStages(runs: any[]): any[] {
146
146
  }
147
147
 
148
148
  /** Pure. Reads nothing and writes nothing; takes already-parsed records. */
149
- function buildIndicators({ records = [], unreadable = [], exists = true } = {}): any {
150
- const runs = (records as any[]).map(runRows);
151
- const byClass = new Map<string, any>();
149
+ function buildIndicators({ records = [], unreadable = [], exists = true } = {}) {
150
+ const runs = (records ).map(runRows);
151
+ const byClass = new Map ();
152
152
 
153
153
  for (const run of runs) {
154
154
  const key = run.taskClass || '(unclassified)';
155
155
  if (!byClass.has(key)) byClass.set(key, { taskClass: key, runs: [], stageAggregate: [] });
156
- byClass.get(key)!.runs.push(run);
156
+ byClass.get(key) .runs.push(run);
157
157
  }
158
158
 
159
159
  for (const group of byClass.values()) group.stageAggregate = aggregateStages(group.runs);
@@ -176,7 +176,7 @@ function buildIndicators({ records = [], unreadable = [], exists = true } = {}):
176
176
  }
177
177
 
178
178
  /** One class block: its runs, each run's stage and gate rows, then the class median. */
179
- function printClass(group: any): void {
179
+ function printClass(group ) {
180
180
  console.log('─'.repeat(78));
181
181
  console.log(`CLASS: ${group.taskClass} (${group.runs.length} run(s))`);
182
182
  for (const run of group.runs) {
@@ -195,7 +195,7 @@ function printClass(group: any): void {
195
195
  }
196
196
  }
197
197
 
198
- function handleIndicatorsCommand({ json = false, projectRoot = process.cwd() } = {}): number {
198
+ function handleIndicatorsCommand({ json = false, projectRoot = process.cwd() } = {}) {
199
199
  const dir = orchestrationDir(projectRoot);
200
200
  const read = readRecords(dir);
201
201
  const view = buildIndicators(read);
@@ -220,7 +220,7 @@ function handleIndicatorsCommand({ json = false, projectRoot = process.cwd() } =
220
220
 
221
221
  console.log(`Runs: ${view.runCount} across ${view.byClass.length} task class(es)`);
222
222
  if (view.unreadable.length) {
223
- console.log(`Unreadable: ${view.unreadable.length} record(s) — ${view.unreadable.map((u: any) => u.file).join(', ')}`);
223
+ console.log(`Unreadable: ${view.unreadable.length} record(s) — ${view.unreadable.map((u ) => u.file).join(', ')}`);
224
224
  }
225
225
 
226
226
  for (const group of view.byClass) printClass(group);
@@ -12,7 +12,7 @@ const {
12
12
  buildStats,
13
13
  buildDiscover,
14
14
  PROVIDER,
15
- } = require('./views.ts');
15
+ } = require('./views.js');
16
16
  const {
17
17
  shortTime,
18
18
  formatMs,
@@ -20,7 +20,7 @@ const {
20
20
  integrityNote,
21
21
  printEmptyLedgerNotice,
22
22
  DISCOVER_MARK,
23
- } = require('./render.ts');
23
+ } = require('./render.js');
24
24
 
25
25
  /**
26
26
  * Task-run telemetry — the JavaScript port of
@@ -49,7 +49,7 @@ const {
49
49
  * They price one specific model; a trace naming a different one gets a cost only if the caller
50
50
  * supplies its rates, because inventing a price for an unpriced model is inventing a metric.
51
51
  */
52
- const DEFAULT_TOKEN_RATES: Readonly<{ inputPerToken: number; outputPerToken: number }> = Object.freeze({
52
+ const DEFAULT_TOKEN_RATES = Object.freeze({
53
53
  inputPerToken: 0.000003,
54
54
  outputPerToken: 0.000015,
55
55
  });
@@ -60,20 +60,20 @@ const COST_DECIMALS = 6;
60
60
  * @param {number} value
61
61
  * @returns {number} rounded to six decimals, matching the Python's `round(x, 6)`
62
62
  */
63
- function roundCost(value: number): number {
63
+ function roundCost(value ) {
64
64
  const factor = 10 ** COST_DECIMALS;
65
65
  return Math.round(value * factor) / factor;
66
66
  }
67
67
 
68
68
  class TaskRunTelemetry {
69
- taskId: string;
70
- agent: string;
71
- model: string;
72
- rates: { inputPerToken: number; outputPerToken: number } | null;
73
- events: any[];
74
- clock: () => number;
75
- startedAtMs: number;
76
- startedAt: string;
69
+ taskId ;
70
+ agent ;
71
+ model ;
72
+ rates ;
73
+ events ;
74
+ clock ;
75
+ startedAtMs ;
76
+ startedAt ;
77
77
 
78
78
  /**
79
79
  * @param {string} taskId
@@ -84,7 +84,7 @@ class TaskRunTelemetry {
84
84
  * @param {{inputPerToken: number, outputPerToken: number}|null} [options.rates]
85
85
  * @param {Function} [options.clock] injection seam for tests; returns elapsed milliseconds
86
86
  */
87
- constructor(taskId: string, options: any = {}) {
87
+ constructor(taskId , options = {}) {
88
88
  const opts = typeof options === 'string' ? { agent: options } : options;
89
89
  this.taskId = taskId;
90
90
  this.agent = opts.agent || 'codex';
@@ -105,7 +105,7 @@ class TaskRunTelemetry {
105
105
  * @param {Object} [details={}]
106
106
  * @returns {Object} the recorded event
107
107
  */
108
- recordEvent(eventType: string, details: any = {}): any {
108
+ recordEvent(eventType , details = {}) {
109
109
  const event = {
110
110
  timestamp: new Date().toISOString(),
111
111
  type: eventType,
@@ -119,7 +119,7 @@ class TaskRunTelemetry {
119
119
  * Milliseconds elapsed since the recorder was constructed.
120
120
  * @returns {number}
121
121
  */
122
- elapsedMs(): number {
122
+ elapsedMs() {
123
123
  return Math.max(0, this.clock() - this.startedAtMs);
124
124
  }
125
125
 
@@ -130,7 +130,7 @@ class TaskRunTelemetry {
130
130
  * @param {number} [outputTokens=0]
131
131
  * @returns {Object} trace record
132
132
  */
133
- exportTrace(success: boolean, inputTokens: number = 0, outputTokens: number = 0): any {
133
+ exportTrace(success , inputTokens = 0, outputTokens = 0) {
134
134
  // Defect fix (2): cost is only reported when rates are known for this run. The default rates
135
135
  // reproduce the Python's number exactly; a caller that clears them (`rates: null`) gets `null`
136
136
  // rather than a confident figure derived from another model's price list.
@@ -180,7 +180,7 @@ const LEDGER_FILE_RE = /^\d{4}-\d{2}-\d{2}\.jsonl$/;
180
180
  * `wire` is the on-disk key: snake_case, matching the five fields `doflow-run` already writes, so
181
181
  * one file never mixes two naming conventions.
182
182
  */
183
- const RUN_FIELDS: Readonly<Array<{ key: string; wire: string; type: string }>> = Object.freeze([
183
+ const RUN_FIELDS = Object.freeze([
184
184
  { key: 'verb', wire: 'verb', type: 'token' },
185
185
  { key: 'kind', wire: 'kind', type: 'token' },
186
186
  { key: 'capability', wire: 'capability', type: 'token' },
@@ -215,7 +215,7 @@ const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?$/;
215
215
  * @param {string} configDir an install root, e.g. `<project>/.doflow` or `~/.doflow`
216
216
  * @returns {string}
217
217
  */
218
- function runsDir(configDir: string): string {
218
+ function runsDir(configDir ) {
219
219
  return path.join(configDir, 'state', RUNS_DIRNAME);
220
220
  }
221
221
 
@@ -234,8 +234,8 @@ function runsDir(configDir: string): string {
234
234
  * subdirectory, so a project with state but no guidance tree falls through to the home install
235
235
  * @returns {{dir: string, configDir: string, origin: string, searched: string[]}}
236
236
  */
237
- function resolveRunsLocation({ start, global: forceGlobal = false, env = process.env, homeDir = os.homedir(), fsImpl = fs, containing = '' }: any = {}): any {
238
- const searched: string[] = [];
237
+ function resolveRunsLocation({ start, global: forceGlobal = false, env = process.env, homeDir = os.homedir(), fsImpl = fs, containing = '' } = {}) {
238
+ const searched = [];
239
239
  const home = path.resolve(homeDir || '.');
240
240
  if (forceGlobal) {
241
241
  const configDir = path.join(home, '.doflow');
@@ -274,14 +274,14 @@ function resolveRunsLocation({ start, global: forceGlobal = false, env = process
274
274
  * fields and never carries their values — a rejection report that echoed the value would leak
275
275
  * exactly the content the rejection exists to keep out.
276
276
  */
277
- function sanitizeRunEvent(input: any, { now = new Date() } = {}): any {
277
+ function sanitizeRunEvent(input , { now = new Date() } = {}) {
278
278
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
279
279
  return { record: null, dropped: [], reason: 'event must be an object' };
280
280
  }
281
281
  const byKey = new Map(RUN_FIELDS.map((field) => [field.key, field]));
282
282
  const byWire = new Map(RUN_FIELDS.map((field) => [field.wire, field]));
283
- const record: any = {};
284
- const dropped: string[] = [];
283
+ const record = {};
284
+ const dropped = [];
285
285
 
286
286
  const timestamp = typeof input.timestamp === 'string' && ISO_TIMESTAMP.test(input.timestamp)
287
287
  ? input.timestamp
@@ -317,9 +317,9 @@ function sanitizeRunEvent(input: any, { now = new Date() } = {}): any {
317
317
  * @param {*} raw parsed JSON value
318
318
  * @returns {Object|null} null when the line is not a usable record
319
319
  */
320
- function normalizeRunRecord(raw: any): any | null {
320
+ function normalizeRunRecord(raw ) {
321
321
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
322
- const out: any = { timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : null };
322
+ const out = { timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : null };
323
323
  for (const field of RUN_FIELDS) {
324
324
  const value = raw[field.wire] !== undefined ? raw[field.wire] : raw[field.key];
325
325
  if (value === undefined || value === null) continue;
@@ -337,9 +337,9 @@ function normalizeRunRecord(raw: any): any | null {
337
337
  }
338
338
 
339
339
  class RunLedger {
340
- fsImpl: any;
341
- clock: () => Date;
342
- dir: string;
340
+ fsImpl ;
341
+ clock ;
342
+ dir ;
343
343
 
344
344
  /**
345
345
  * @param {Object} [options]
@@ -348,7 +348,7 @@ class RunLedger {
348
348
  * @param {Object} [options.fsImpl=fs]
349
349
  * @param {Function} [options.clock] returns the current Date; injection seam for tests
350
350
  */
351
- constructor({ dir, configDir, fsImpl = fs, clock = () => new Date() }: any = {}) {
351
+ constructor({ dir, configDir, fsImpl = fs, clock = () => new Date() } = {}) {
352
352
  this.fsImpl = fsImpl;
353
353
  this.clock = clock;
354
354
  if (dir) this.dir = path.resolve(dir);
@@ -362,7 +362,7 @@ class RunLedger {
362
362
  * @param {Object} event
363
363
  * @returns {{written: boolean, file?: string, record?: Object, dropped: string[], reason?: string}}
364
364
  */
365
- append(event: any): any {
365
+ append(event ) {
366
366
  const { record, dropped, reason } = sanitizeRunEvent(event, { now: this.clock() });
367
367
  if (!record) return { written: false, dropped, reason };
368
368
  try {
@@ -370,7 +370,7 @@ class RunLedger {
370
370
  const file = path.join(this.dir, `${record.timestamp.slice(0, 10)}.jsonl`);
371
371
  this.fsImpl.appendFileSync(file, `${JSON.stringify(record)}\n`, 'utf8');
372
372
  return { written: true, file, record, dropped };
373
- } catch (error: any) {
373
+ } catch (error ) {
374
374
  return { written: false, record, dropped, reason: error.message };
375
375
  }
376
376
  }
@@ -383,8 +383,8 @@ class RunLedger {
383
383
  * @param {number} [options.days] keep only partitions dated within the last N calendar days
384
384
  * @returns {{dir: string, exists: boolean, files: string[], records: Object[], malformedLines: number, unreadableFiles: string[], days: number|null}}
385
385
  */
386
- read({ days = null }: any = {}): any {
387
- const result: any = {
386
+ read({ days = null } = {}) {
387
+ const result = {
388
388
  dir: this.dir,
389
389
  exists: false,
390
390
  files: [],
@@ -394,7 +394,7 @@ class RunLedger {
394
394
  days: days && days > 0 ? days : null,
395
395
  };
396
396
 
397
- let entries: string[];
397
+ let entries ;
398
398
  try {
399
399
  entries = this.fsImpl.readdirSync(this.dir);
400
400
  } catch {
@@ -402,7 +402,7 @@ class RunLedger {
402
402
  }
403
403
  result.exists = true;
404
404
 
405
- let names: string[] = entries.filter((name) => LEDGER_FILE_RE.test(name)).sort();
405
+ let names = entries.filter((name) => LEDGER_FILE_RE.test(name)).sort();
406
406
  if (result.days) {
407
407
  // Calendar days, not "the last N files": a quiet week must not silently widen the window.
408
408
  const cutoff = new Date(this.clock().getTime() - (result.days - 1) * 24 * 60 * 60 * 1000)
@@ -412,7 +412,7 @@ class RunLedger {
412
412
  result.files = names;
413
413
 
414
414
  for (const name of names) {
415
- let text: string;
415
+ let text ;
416
416
  try {
417
417
  text = this.fsImpl.readFileSync(path.join(this.dir, name), 'utf8');
418
418
  } catch {
@@ -421,7 +421,7 @@ class RunLedger {
421
421
  }
422
422
  for (const line of text.split('\n')) {
423
423
  if (line.trim() === '') continue;
424
- let parsed: any;
424
+ let parsed ;
425
425
  try {
426
426
  parsed = JSON.parse(line);
427
427
  } catch {
@@ -436,7 +436,7 @@ class RunLedger {
436
436
  }
437
437
 
438
438
  // Undated records sort last rather than being dropped: they still count toward usage.
439
- result.records.sort((a: any, b: any) => (a.at ?? Number.MAX_SAFE_INTEGER) - (b.at ?? Number.MAX_SAFE_INTEGER));
439
+ result.records.sort((a , b ) => (a.at ?? Number.MAX_SAFE_INTEGER) - (b.at ?? Number.MAX_SAFE_INTEGER));
440
440
  return result;
441
441
  }
442
442
  }
@@ -455,7 +455,7 @@ class RunLedger {
455
455
  * @param {string} [options.projectRoot]
456
456
  * @returns {number} process exit code
457
457
  */
458
- function handleTraceCommand({ json = false, days = null, global: useGlobal = false, projectRoot }: any = {}): number {
458
+ function handleTraceCommand({ json = false, days = null, global: useGlobal = false, projectRoot } = {}) {
459
459
  const location = resolveRunsLocation({ start: projectRoot, global: useGlobal });
460
460
  const view = buildTrace(new RunLedger({ dir: location.dir }).read({ days }));
461
461
  view.origin = location.origin;
@@ -503,7 +503,7 @@ function handleTraceCommand({ json = false, days = null, global: useGlobal = fal
503
503
  * @param {Object} options
504
504
  * @returns {number} process exit code
505
505
  */
506
- function handleStatsCommand({ json = false, days = null, global: useGlobal = false, projectRoot }: any = {}): number {
506
+ function handleStatsCommand({ json = false, days = null, global: useGlobal = false, projectRoot } = {}) {
507
507
  const location = resolveRunsLocation({ start: projectRoot, global: useGlobal });
508
508
  const view = buildStats(new RunLedger({ dir: location.dir }).read({ days }));
509
509
  view.origin = location.origin;
@@ -556,7 +556,7 @@ function handleStatsCommand({ json = false, days = null, global: useGlobal = fal
556
556
  * @param {Object} [options.providerHealth] injected for tests; probed from `health.ts` otherwise
557
557
  * @returns {number} process exit code — 1 when there is a finding to act on
558
558
  */
559
- function handleDiscoverCommand({ json = false, days = null, global: useGlobal = false, projectRoot, providerHealth }: any = {}): number {
559
+ function handleDiscoverCommand({ json = false, days = null, global: useGlobal = false, projectRoot, providerHealth } = {}) {
560
560
  const location = resolveRunsLocation({ start: projectRoot, global: useGlobal });
561
561
  const read = new RunLedger({ dir: location.dir }).read({ days });
562
562
 
@@ -565,7 +565,7 @@ function handleDiscoverCommand({ json = false, days = null, global: useGlobal =
565
565
  let health = providerHealth;
566
566
  if (health === undefined) {
567
567
  try {
568
- health = require('../health.ts').probeProviders({ ids: Object.values(PROVIDER) });
568
+ health = require('../health.js').probeProviders({ ids: Object.values(PROVIDER) });
569
569
  } catch {
570
570
  health = null;
571
571
  }
@@ -12,12 +12,12 @@
12
12
 
13
13
  /** ISO timestamp to a compact local-independent `MM-DD HH:MM:SS`. Keeps columns narrow without
14
14
  * inventing a timezone the record does not carry. */
15
- function shortTime(timestamp: any): string {
15
+ function shortTime(timestamp ) {
16
16
  if (typeof timestamp !== 'string' || timestamp.length < 19) return '(undated)';
17
17
  return `${timestamp.slice(5, 10)} ${timestamp.slice(11, 19)}`;
18
18
  }
19
19
 
20
- function formatMs(value: any): string {
20
+ function formatMs(value ) {
21
21
  if (typeof value !== 'number') return '-';
22
22
  if (value < 1000) return `${value}ms`;
23
23
  return `${(value / 1000).toFixed(1)}s`;
@@ -33,7 +33,7 @@ function formatMs(value: any): string {
33
33
  * @param {number} code
34
34
  * @returns {number}
35
35
  */
36
- function finish(code: number): number {
36
+ function finish(code ) {
37
37
  process.exitCode = code;
38
38
  return code;
39
39
  }
@@ -44,22 +44,22 @@ function finish(code: number): number {
44
44
  * @param {Object} ledger
45
45
  * @returns {string|null}
46
46
  */
47
- function integrityNote(ledger: any): string | null {
48
- const parts: string[] = [];
47
+ function integrityNote(ledger ) {
48
+ const parts = [];
49
49
  if (ledger.malformedLines) parts.push(`${ledger.malformedLines} unreadable ledger line(s) skipped`);
50
50
  if (ledger.unreadableFiles.length) parts.push(`${ledger.unreadableFiles.length} unreadable partition(s): ${ledger.unreadableFiles.join(', ')}`);
51
51
  return parts.length ? `Note: ${parts.join('; ')}.` : null;
52
52
  }
53
53
 
54
54
  /** One place that turns a resolved location into the sentence an empty ledger deserves. */
55
- function printEmptyLedgerNotice(ledger: any, reason: any): void {
55
+ function printEmptyLedgerNotice(ledger , reason ) {
56
56
  console.log(` ${reason}`);
57
57
  console.log(` Location: ${ledger.dir}`);
58
58
  console.log(' An empty ledger is a normal state for a fresh install. It is not evidence that');
59
59
  console.log(' nothing ran, and no conclusion about usage can be drawn from it.');
60
60
  }
61
61
 
62
- const DISCOVER_MARK: Record<string, string> = Object.freeze({
62
+ const DISCOVER_MARK = Object.freeze({
63
63
  FINDING: '! FINDING',
64
64
  CLEAR: '✓ CLEAR',
65
65
  NOT_DETERMINED: '? UNKNOWN',