@goodandready/dsh-moa 0.2.14 → 0.2.16

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/README.md CHANGED
@@ -282,6 +282,13 @@ npm test
282
282
 
283
283
  ---
284
284
 
285
+
286
+ ## 🛠️ Internal Tooling & Development
287
+
288
+ For local verification and package integrity validation:
289
+ - `npm test`: runs the full test suite (95 tests)
290
+ - `./deploy.sh`: local infrastructure validation script (verifies package size, identity parity in package.json/cordis.patch.yml/client.js, and tests). Excluded from the published npm package.
291
+
285
292
  ## 📄 License
286
293
 
287
294
  MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Run a non-critical side effect; never throw.
3
+ * Logs at debug/warn when a logger is provided or via console.debug.
4
+ *
5
+ * @param {string} label - Context identifier for telemetry / diagnostics
6
+ * @param {Function} fn - Operation to execute
7
+ * @param {object} [logger] - Optional logger instance
8
+ */
9
+ export function bestEffort(label, fn, logger) {
10
+ const log = (err) => {
11
+ try {
12
+ if (logger && typeof logger.debug === 'function') {
13
+ logger.debug(`[dsh-moa:bestEffort] ${label}:`, err?.message || err)
14
+ } else if (typeof console !== 'undefined' && typeof console.debug === 'function') {
15
+ console.debug(`[dsh-moa:bestEffort] ${label}:`, err?.message || err)
16
+ }
17
+ } catch {
18
+ // Logger itself must never crash the process
19
+ }
20
+ }
21
+
22
+ try {
23
+ const result = fn()
24
+ if (result && typeof result.then === 'function') {
25
+ return result.then(
26
+ (value) => value,
27
+ (err) => {
28
+ log(err)
29
+ return undefined
30
+ }
31
+ )
32
+ }
33
+ return result
34
+ } catch (err) {
35
+ log(err)
36
+ return undefined
37
+ }
38
+ }
package/lib/client.js CHANGED
@@ -1444,7 +1444,7 @@ window.__ModuleLoader__.load({
1444
1444
  const snapshot = React.useSyncExternalStore
1445
1445
  ? React.useSyncExternalStore(
1446
1446
  React.useMemo(() => (cb) => (scope ? scope.subscribe(cb) : () => {}), [scope]),
1447
- React.useCallback(() => (scope ? scope.getSnapshot() : { status: 'ready' }), [scope]),
1447
+ React.useCallback(() => (scope ? scope.getSnapshot() : { status: 'unavailable' }), [scope]),
1448
1448
  React.useCallback(() => ({ status: 'loading' }), [])
1449
1449
  )
1450
1450
  : null
@@ -1461,8 +1461,11 @@ window.__ModuleLoader__.load({
1461
1461
  const [leaderboardPresetFilter, setLeaderboardPresetFilter] = React.useState('all')
1462
1462
 
1463
1463
  React.useEffect(() => {
1464
- if (!scope || !snapshot) return
1465
- const snapStatus = snapshot.status || 'ready'
1464
+ if (!scope || !snapshot) {
1465
+ setStatus('unavailable')
1466
+ return
1467
+ }
1468
+ const snapStatus = snapshot.status || 'unavailable'
1466
1469
  if (snapStatus === 'ready') {
1467
1470
  const val = snapshot.value || {}
1468
1471
  if (Array.isArray(val.presets)) setPresets(val.presets)
@@ -1730,7 +1733,7 @@ window.__ModuleLoader__.load({
1730
1733
  return () => {
1731
1734
  for (const off of undo) {
1732
1735
  if (typeof off === 'function') {
1733
- try { off() } catch {}
1736
+ try { off() } catch (offErr) { /* unsubscribe best-effort */ }
1734
1737
  }
1735
1738
  }
1736
1739
  }
@@ -1,3 +1,4 @@
1
+ import { bestEffort } from './best-effort.js'
1
2
  import vm from 'node:vm'
2
3
  import fs from 'node:fs/promises'
3
4
  import path from 'node:path'
@@ -90,6 +91,18 @@ function sanitizePath(filePath) {
90
91
  * Collects readable text files from current workspace to feed as context for candidate models (#6).
91
92
  * Ignores node_modules, .git, .moa, binary files, large bundles.
92
93
  */
94
+ /**
95
+ * Formats collected project files into structured Markdown code blocks
96
+ * suitable for inclusion in candidate system prompts.
97
+ */
98
+ export function formatProjectContext(projectFiles = []) {
99
+ if (!Array.isArray(projectFiles) || projectFiles.length === 0) return ''
100
+ const fence = '```'
101
+ return projectFiles
102
+ .map((f) => `### File: ${f.relativePath}\n${fence}\n${f.content}\n${fence}`)
103
+ .join('\n\n')
104
+ }
105
+
93
106
  export async function collectProjectContext(baseDir, maxCharBudget = 16000) {
94
107
  if (!baseDir) return { files: [], totalChars: 0 }
95
108
 
@@ -102,51 +115,63 @@ export async function collectProjectContext(baseDir, maxCharBudget = 16000) {
102
115
  const collected = []
103
116
  let totalChars = 0
104
117
 
118
+ let skippedFiles = 0
119
+ const skippedList = []
120
+
105
121
  async function scan(currentDir, relPrefix = '') {
106
122
  if (totalChars >= maxCharBudget) return
123
+ let list
107
124
  try {
108
- const list = await fs.readdir(currentDir, { withFileTypes: true })
109
- for (const item of list) {
110
- if (totalChars >= maxCharBudget) break
111
- // Skip all dotfiles, including .env and .env.* variants: their
112
- // contents must never be shipped to third-party model providers.
113
- if (item.name.startsWith('.')) continue
125
+ list = await fs.readdir(currentDir, { withFileTypes: true })
126
+ } catch (readDirErr) {
127
+ skippedFiles++
128
+ skippedList.push(relPrefix || '.')
129
+ return
130
+ }
114
131
 
115
- const rel = relPrefix ? `${relPrefix}/${item.name}` : item.name
116
- const full = path.join(currentDir, item.name)
132
+ for (const item of list) {
133
+ if (totalChars >= maxCharBudget) break
134
+ // Skip all dotfiles, including .env and .env.* variants: their
135
+ // contents must never be shipped to third-party model providers.
136
+ if (item.name.startsWith('.')) continue
117
137
 
118
- if (item.isDirectory()) {
119
- if (!ignoreDirs.has(item.name)) {
120
- await scan(full, rel)
121
- }
122
- } else if (item.isFile()) {
123
- const ext = path.extname(item.name).toLowerCase()
124
- if (codeExts.has(ext)) {
125
- try {
126
- const stat = await fs.stat(full)
127
- if (stat.size > 100000) continue // skip files > 100kb
128
- const content = await fs.readFile(full, 'utf8')
129
- const budgetRemaining = maxCharBudget - totalChars
130
- const truncated = content.length > budgetRemaining ? content.slice(0, budgetRemaining) + '\n... [truncated]' : content
131
- totalChars += truncated.length
132
- collected.push({ relativePath: rel, content: truncated, size: stat.size })
133
- } catch {}
138
+ const rel = relPrefix ? `${relPrefix}/${item.name}` : item.name
139
+ const full = path.join(currentDir, item.name)
140
+
141
+ if (item.isDirectory()) {
142
+ if (!ignoreDirs.has(item.name)) {
143
+ await scan(full, rel)
144
+ }
145
+ } else if (item.isFile()) {
146
+ const ext = path.extname(item.name).toLowerCase()
147
+ if (codeExts.has(ext)) {
148
+ try {
149
+ const stat = await fs.stat(full)
150
+ if (stat.size > 100000) continue // skip files > 100kb
151
+ const content = await fs.readFile(full, 'utf8')
152
+ const budgetRemaining = maxCharBudget - totalChars
153
+ const truncated = content.length > budgetRemaining ? content.slice(0, budgetRemaining) + '\n... [truncated]' : content
154
+ totalChars += truncated.length
155
+ collected.push({ relativePath: rel, content: truncated, size: stat.size })
156
+ } catch (fileErr) {
157
+ skippedFiles++
158
+ skippedList.push(rel)
134
159
  }
135
160
  }
136
161
  }
137
- } catch {}
162
+ }
138
163
  }
139
164
 
140
165
  await scan(baseDir)
141
- return { files: collected, totalChars }
166
+ return { files: collected, totalChars, skippedFiles, skippedList }
142
167
  }
143
168
 
144
169
  /**
145
170
  * Checks if a task prompt is an iterative refinement / modification
146
171
  * of an existing project rather than a greenfield project creation (#4).
147
172
  */
148
- export function isRefinementTask(userPrompt = '', projectFiles = []) {
149
- if (!Array.isArray(projectFiles) || projectFiles.length === 0) return false
173
+ export function isRefinementTask(userPrompt = '', projectFiles = null) {
174
+ if (Array.isArray(projectFiles) && projectFiles.length === 0) return false
150
175
  const p = userPrompt.toLowerCase().trim()
151
176
  if (!p) return false
152
177
 
@@ -212,7 +237,7 @@ export async function promoteCandidateWorkspace(baseDir, winningIndex, options =
212
237
  const entries = await getFilesRecursively(winningDir)
213
238
  if (entries.length === 0) {
214
239
  if (!options.keepMoa) {
215
- try { await fs.rm(moaRoot, { recursive: true, force: true }) } catch {}
240
+ await bestEffort('clean moaRoot on empty candidate', () => fs.rm(moaRoot, { recursive: true, force: true }))
216
241
  }
217
242
  return []
218
243
  }
@@ -235,7 +260,7 @@ export async function promoteCandidateWorkspace(baseDir, winningIndex, options =
235
260
  return promoted
236
261
  } catch (err) {
237
262
  if (!options.keepMoa) {
238
- try { await fs.rm(moaRoot, { recursive: true, force: true }) } catch {}
263
+ await bestEffort('clean moaRoot on promote error', () => fs.rm(moaRoot, { recursive: true, force: true }))
239
264
  }
240
265
  return []
241
266
  }
@@ -247,9 +272,7 @@ export async function promoteCandidateWorkspace(baseDir, winningIndex, options =
247
272
  export async function cleanMoaWorkspaces(baseDir) {
248
273
  if (!baseDir) return
249
274
  const moaRoot = path.join(baseDir, '.moa')
250
- try {
251
- await fs.rm(moaRoot, { recursive: true, force: true })
252
- } catch {}
275
+ await bestEffort('cleanMoaWorkspaces', () => fs.rm(moaRoot, { recursive: true, force: true }))
253
276
  }
254
277
 
255
278
  async function getFilesRecursively(dir) {
@@ -257,13 +280,14 @@ async function getFilesRecursively(dir) {
257
280
  try {
258
281
  const stat = await fs.stat(dir)
259
282
  if (!stat.isDirectory()) return []
260
- } catch {
283
+ } catch (statErr) {
261
284
  return []
262
285
  }
263
286
 
264
287
  async function scan(current, relPrefix = '') {
288
+ let list
265
289
  try {
266
- const list = await fs.readdir(current, { withFileTypes: true })
290
+ list = await fs.readdir(current, { withFileTypes: true })
267
291
  for (const item of list) {
268
292
  const rel = relPrefix ? `${relPrefix}/${item.name}` : item.name
269
293
  const full = path.join(current, item.name)
@@ -273,7 +297,9 @@ async function getFilesRecursively(dir) {
273
297
  results.push(rel)
274
298
  }
275
299
  }
276
- } catch {}
300
+ } catch (readErr) {
301
+ return
302
+ }
277
303
  }
278
304
  await scan(dir)
279
305
  return results
package/lib/history.js CHANGED
@@ -15,7 +15,7 @@ const DEFAULT_HISTORY_FILE = path.join(DEFAULT_HISTORY_DIR, 'moa-history.jsonl')
15
15
  /** Max history file size before rotation (10 MB) */
16
16
  export const MAX_HISTORY_BYTES = 10 * 1024 * 1024
17
17
 
18
- export function getHistoryFilePath(customDir) {
18
+ function getHistoryFilePath(customDir) {
19
19
  if (customDir) {
20
20
  return path.join(customDir, '.moa-history.jsonl')
21
21
  }
@@ -33,7 +33,7 @@ export function rotateHistoryFileIfNeeded(filePath = DEFAULT_HISTORY_FILE, maxBy
33
33
  if (stat.size >= maxBytes) {
34
34
  const backupPath = `${filePath}.1`
35
35
  if (fs.existsSync(backupPath)) {
36
- try { fs.unlinkSync(backupPath) } catch {}
36
+ try { fs.unlinkSync(backupPath) } catch (unlinkErr) { /* backup unlink best-effort */ }
37
37
  }
38
38
  fs.renameSync(filePath, backupPath)
39
39
  return true
@@ -241,7 +241,7 @@ export function getMoaRunById(runId, filePath = DEFAULT_HISTORY_FILE) {
241
241
  try {
242
242
  const item = JSON.parse(lines[i])
243
243
  if (item && item.id === runId) return item
244
- } catch {}
244
+ } catch (parseErr) { /* corrupt line skipped */ }
245
245
  }
246
246
  return null
247
247
  } catch {
@@ -270,7 +270,7 @@ export function exportMoaHistory(filePath = DEFAULT_HISTORY_FILE, options = {})
270
270
  if (!item) continue
271
271
  if (presetFilter && presetFilter !== 'all' && item.preset !== presetFilter) continue
272
272
  records.push(item)
273
- } catch {}
273
+ } catch (parseErr) { /* corrupt line skipped */ }
274
274
  }
275
275
 
276
276
  if (format === 'csv') {
@@ -298,3 +298,13 @@ export function exportMoaHistory(filePath = DEFAULT_HISTORY_FILE, options = {})
298
298
  return options?.format === 'csv' ? '' : '[]'
299
299
  }
300
300
  }
301
+
302
+ export function candidatesForHistory(referenceOutputs) {
303
+ return (referenceOutputs || []).map((r) => ({
304
+ provider: r.slot?.provider || "",
305
+ model: r.slot?.model || "",
306
+ files: (r.files || []).map((f) => f.relativePath),
307
+ usage: r.usage || { inputTokens: 0, outputTokens: 0 },
308
+ costUsd: r.costUsd || 0,
309
+ }))
310
+ }