fdeops 3.9.6 → 3.9.8
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 +8 -49
- package/bin/check.js +15 -5
- package/bin/fde.js +180 -900
- package/bin/install.js +6 -1
- package/bin/lib/memory.js +164 -0
- package/bin/lib/render.js +666 -0
- package/bin/lib/trust.js +188 -0
- package/package.json +1 -1
package/bin/fde.js
CHANGED
|
@@ -29,6 +29,8 @@ const fs = require('fs')
|
|
|
29
29
|
const path = require('path')
|
|
30
30
|
const os = require('os')
|
|
31
31
|
const { execSync, execFileSync } = require('child_process')
|
|
32
|
+
const { createMemoryApi } = require('./lib/memory')
|
|
33
|
+
const { createTrustApi } = require('./lib/trust')
|
|
32
34
|
|
|
33
35
|
const HOME = os.homedir()
|
|
34
36
|
// FDEOPS_ENGAGEMENTS_ROOT isolates init/status/dashboard (and the registry) for
|
|
@@ -168,6 +170,14 @@ function readEng(eng, f) {
|
|
|
168
170
|
try { return fs.readFileSync(path.join(eng, f), 'utf8') } catch (_) { return '' }
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
// Redact private notes and template hints from every model-facing read.
|
|
174
|
+
function stripPrivate(md) {
|
|
175
|
+
return md
|
|
176
|
+
.replace(/<private>[\s\S]*?<\/private>/gi, '(private - redacted)')
|
|
177
|
+
.replace(/<private>[\s\S]*$/i, '(private - redacted)')
|
|
178
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
179
|
+
}
|
|
180
|
+
|
|
171
181
|
// Read + redact in one step - the default way dashboard code should ever touch
|
|
172
182
|
// a markdown file, so a forgotten stripPrivate() call can't leak a <private> block.
|
|
173
183
|
function readClean(eng, f) { return stripPrivate(readEng(eng, f)) }
|
|
@@ -201,7 +211,7 @@ function refuseSecret(kind, hit) {
|
|
|
201
211
|
console.error(
|
|
202
212
|
`refused: ${kind} looks like a ${hit}.\n` +
|
|
203
213
|
`Do not log credentials into engagement memory. Redact first, or pass --force if this is intentional.\n` +
|
|
204
|
-
`If you already wrote one: fde log --undo`
|
|
214
|
+
`If you already wrote one: fde log --undo (last write) or fde redact <term> --apply (buried)`
|
|
205
215
|
)
|
|
206
216
|
}
|
|
207
217
|
|
|
@@ -335,7 +345,6 @@ function rmTreeQuiet(dir) {
|
|
|
335
345
|
|
|
336
346
|
const OWNER_FILE = '.owner'
|
|
337
347
|
const DEBRIEF_PROPOSE = '.debrief-propose'
|
|
338
|
-
let _gitWarned = false
|
|
339
348
|
|
|
340
349
|
function gitBinOk() {
|
|
341
350
|
try {
|
|
@@ -386,72 +395,12 @@ function datedEntry(eng, date, text, signal) {
|
|
|
386
395
|
return bits.join(' ')
|
|
387
396
|
}
|
|
388
397
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (!gitBinOk()) {
|
|
396
|
-
if (!_gitWarned) {
|
|
397
|
-
process.stderr.write('⚠ git not found - engagement memory will not be versioned (receipts stay dated, but not tamper-evident)\n')
|
|
398
|
-
_gitWarned = true
|
|
399
|
-
}
|
|
400
|
-
writeOwnerIfMissing(eng)
|
|
401
|
-
return false
|
|
402
|
-
}
|
|
403
|
-
try {
|
|
404
|
-
execFileSync('git', ['init'], { cwd: eng, stdio: 'ignore', timeout: 10000 })
|
|
405
|
-
atomicWriteFile(
|
|
406
|
-
path.join(eng, '.gitignore'),
|
|
407
|
-
['*.lock', '*.tmp', '.last-write', '.debrief-propose', ''].join('\n')
|
|
408
|
-
)
|
|
409
|
-
writeOwnerIfMissing(eng)
|
|
410
|
-
commitMemory(eng, 'init engagement memory')
|
|
411
|
-
return true
|
|
412
|
-
} catch (_) {
|
|
413
|
-
return false
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function commitMemory(eng, message) {
|
|
418
|
-
if (!eng || !fs.existsSync(path.join(eng, '.git'))) {
|
|
419
|
-
if (!ensureMemoryGit(eng)) return null
|
|
420
|
-
}
|
|
421
|
-
if (!gitBinOk()) return null
|
|
422
|
-
const owner = writeOwnerIfMissing(eng)
|
|
423
|
-
try {
|
|
424
|
-
execFileSync('git', ['add', '-A'], { cwd: eng, stdio: 'ignore', timeout: 10000 })
|
|
425
|
-
const porcelain = execFileSync('git', ['status', '--porcelain'], {
|
|
426
|
-
cwd: eng, encoding: 'utf8', timeout: 10000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
427
|
-
})
|
|
428
|
-
if (!String(porcelain || '').trim()) return null
|
|
429
|
-
const env = {
|
|
430
|
-
...process.env,
|
|
431
|
-
GIT_AUTHOR_NAME: owner.name,
|
|
432
|
-
GIT_AUTHOR_EMAIL: owner.email,
|
|
433
|
-
GIT_COMMITTER_NAME: owner.name,
|
|
434
|
-
GIT_COMMITTER_EMAIL: owner.email,
|
|
435
|
-
}
|
|
436
|
-
execFileSync('git', ['-c', 'commit.gpgsign=false', 'commit', '-m', String(message || 'memory write').slice(0, 72)], {
|
|
437
|
-
cwd: eng, stdio: 'ignore', timeout: 15000, env,
|
|
438
|
-
})
|
|
439
|
-
return execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
|
|
440
|
-
cwd: eng, encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
441
|
-
}).toString().trim()
|
|
442
|
-
} catch (_) {
|
|
443
|
-
return null
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function memoryHead(eng) {
|
|
448
|
-
if (!eng || !fs.existsSync(path.join(eng, '.git'))) return ''
|
|
449
|
-
try {
|
|
450
|
-
return execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
|
|
451
|
-
cwd: eng, encoding: 'utf8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
452
|
-
}).toString().trim()
|
|
453
|
-
} catch (_) { return '' }
|
|
454
|
-
}
|
|
398
|
+
const {
|
|
399
|
+
ensureMemoryGit,
|
|
400
|
+
memoryDirtyManual,
|
|
401
|
+
commitMemory,
|
|
402
|
+
memoryHead,
|
|
403
|
+
} = createMemoryApi({ fs, path, gitBinOk, writeOwnerIfMissing, atomicWriteFile })
|
|
455
404
|
|
|
456
405
|
// Pull the body under a "## Heading" up to the next "##" (or EOF).
|
|
457
406
|
function sectionBody(md, heading) {
|
|
@@ -509,169 +458,13 @@ function appendLogEntry(eng, type, entry, opts = {}) {
|
|
|
509
458
|
lockedAppendFile(p, `\n${entry}\n`)
|
|
510
459
|
}
|
|
511
460
|
recordLastWrite(eng, LOG_FILES[type], entry)
|
|
512
|
-
if (!opts.skipCommit)
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
// Trust resolution: structured [signal:red|amber|green] tokens in stakeholders.md
|
|
517
|
-
// (written by `fde log contact --signal` and `fde debrief`) win - the latest dated
|
|
518
|
-
// one. Older than 21 days → stale: shown with a "?" marker + age so a forgotten
|
|
519
|
-
// signal never silently drives triage. The keyword grep survives only as the
|
|
520
|
-
// zero-effort floor when NO token exists anywhere - prose like "escalated to CTO,
|
|
521
|
-
// resolved amicably" must not flip a client amber forever.
|
|
522
|
-
function stakeholdersMemoryHealth(eng) {
|
|
523
|
-
// Hostile handoff: binary / unparseable stakeholders must not read as healthy green.
|
|
524
|
-
let buf
|
|
525
|
-
try { buf = fs.readFileSync(path.join(eng, 'stakeholders.md')) } catch (_) {
|
|
526
|
-
return { ok: true, warn: '' }
|
|
527
|
-
}
|
|
528
|
-
if (buf.includes(0)) {
|
|
529
|
-
return { ok: false, warn: 'memory unreadable - verify (binary data in stakeholders.md)' }
|
|
530
|
-
}
|
|
531
|
-
const md = buf.toString('utf8')
|
|
532
|
-
const ledger = readEng(eng, SIGNAL_LEDGER)
|
|
533
|
-
if (/\[signal:(red|amber|green)\]/i.test(md + '\n' + ledger)) {
|
|
534
|
-
return { ok: true, warn: '' }
|
|
535
|
-
}
|
|
536
|
-
const trustLine = md.match(/\*\*Trust:\*\*\s*([A-Za-z?]+)/i)
|
|
537
|
-
if (trustLine && !/^(red|amber|green)$/i.test(trustLine[1])) {
|
|
538
|
-
return { ok: false, warn: 'memory unreadable - verify (invalid trust value)' }
|
|
539
|
-
}
|
|
540
|
-
const table = parseMdTable(md)
|
|
541
|
-
const meaningful = md.split('\n').filter(l => {
|
|
542
|
-
const t = l.trim()
|
|
543
|
-
return t && !t.startsWith('#') && !t.startsWith('<!--') && !/^\|?\s*:?-{3,}/.test(t)
|
|
544
|
-
}).length
|
|
545
|
-
// Content present but no table and no structured signal → do not invent "green"
|
|
546
|
-
if (meaningful >= 3 && !table) {
|
|
547
|
-
return { ok: false, warn: 'memory unreadable - verify (stakeholders.md unparseable)' }
|
|
461
|
+
if (!opts.skipCommit) {
|
|
462
|
+
const files = [LOG_FILES[type]]
|
|
463
|
+
if (type === 'contact' && /\[signal:(red|amber|green)\]/i.test(entry)) files.push(SIGNAL_LEDGER)
|
|
464
|
+
commitMemory(eng, opts.commitMsg || `log ${type}`, { files })
|
|
548
465
|
}
|
|
549
|
-
return { ok: true, warn: '' }
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// Subject key for a signal-history line - first real name word (same spirit as
|
|
553
|
-
// extractStakeholders). A green about Randy must not clear an amber about Denise.
|
|
554
|
-
// Strip author tags [@email-local] so attribution never becomes the subject key.
|
|
555
|
-
function signalSubjectKey(text) {
|
|
556
|
-
const cleaned = String(text).replace(/\[@[^\]]+\]/g, '').replace(/\([^)]*\)/g, '')
|
|
557
|
-
const words = cleaned.split(/\s+/).filter(w => w && !/^(dr|mr|mrs|ms)\.?$/i.test(w))
|
|
558
|
-
const frag = (words[0] || '').replace(/[^a-z0-9]/gi, '').toLowerCase()
|
|
559
|
-
return frag.length >= 3 ? frag : ('anon:' + cleaned.slice(0, 48).toLowerCase())
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function parsePhase(ctx) {
|
|
563
|
-
// Template ships "**Phase:** land | discover | ..." - that is UNSET, not land.
|
|
564
|
-
const m = ctx.match(/\*\*Phase:\*\*\s*(.+)/i) || ctx.match(/^phase[:\s*]+(.+)$/im)
|
|
565
|
-
if (!m) return '?'
|
|
566
|
-
const raw = m[1].replace(/\*/g, '').trim()
|
|
567
|
-
if (!raw || /\|/.test(raw) || /^unset$/i.test(raw) || /^[\[(]/.test(raw)) return '?'
|
|
568
|
-
const one = raw.toLowerCase().match(/^(land|discover|plan|build|ship|close)\b/)
|
|
569
|
-
return one ? one[1] : '?'
|
|
570
466
|
}
|
|
571
467
|
|
|
572
|
-
function countOpenRisks(eng) {
|
|
573
|
-
const md = readClean(eng, 'risks.md')
|
|
574
|
-
const body = md.split(/^#{1,6}\s+Retired\b/im)[0] || md
|
|
575
|
-
let n = 0
|
|
576
|
-
for (const raw of body.split('\n')) {
|
|
577
|
-
const t = raw.trim()
|
|
578
|
-
if (!t || t.startsWith('<!--') || /^#{1,6}\s/.test(t)) continue
|
|
579
|
-
if (/risk\s*\|\s*status|mitigation/i.test(t) || /^\|?[\s|:-]+$/.test(t)) continue
|
|
580
|
-
if (/^[-*]/.test(t) || (/^\|/.test(t) && t.length > 12)) n++
|
|
581
|
-
}
|
|
582
|
-
return n
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
function nextActionLine(ctx) {
|
|
586
|
-
const body = sectionBody(ctx, 'Next action')
|
|
587
|
-
for (const raw of body.split('\n')) {
|
|
588
|
-
const t = raw.trim().replace(/^[-*]\s+/, '')
|
|
589
|
-
if (t) return t.slice(0, 120)
|
|
590
|
-
}
|
|
591
|
-
return ''
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function computeSignals(eng) {
|
|
595
|
-
// readClean, not readEng: status/dashboard echo topRisk and stakeholder lines
|
|
596
|
-
// to the terminal and the rendered HTML - a <private> risk must never surface.
|
|
597
|
-
const ctx = readClean(eng, 'context.md'); const stake = readClean(eng, 'stakeholders.md'); const risks = readClean(eng, 'risks.md')
|
|
598
|
-
// Prefer structured tokens from stakeholders + CLI ledger (ledger survives wipes)
|
|
599
|
-
const signalText = stake + '\n' + readClean(eng, SIGNAL_LEDGER)
|
|
600
|
-
const phase = parsePhase(ctx)
|
|
601
|
-
// Latest signal PER stakeholder, then worst-of those actives.
|
|
602
|
-
// Global "latest wins" let a green from person B hide a sponsor crisis on A.
|
|
603
|
-
const byPerson = new Map()
|
|
604
|
-
for (const l of signalText.split('\n')) {
|
|
605
|
-
const sm = l.match(/\[signal:(red|amber|green)\]/i)
|
|
606
|
-
if (!sm) continue
|
|
607
|
-
const date = (l.match(/\[(\d{4}-\d{2}-\d{2})\]/) || [])[1] || ''
|
|
608
|
-
const text = l.replace(/^\s*-\s*/, '')
|
|
609
|
-
.replace(/\[signal:(red|amber|green)\]/i, '')
|
|
610
|
-
.replace(/\[\d{4}-\d{2}-\d{2}\]/, '')
|
|
611
|
-
.replace(/\[@[^\]]+\]/g, '')
|
|
612
|
-
.trim()
|
|
613
|
-
const key = signalSubjectKey(text)
|
|
614
|
-
const prev = byPerson.get(key)
|
|
615
|
-
if (!prev || date >= prev.date) byPerson.set(key, { date, sig: sm[1].toLowerCase(), text })
|
|
616
|
-
}
|
|
617
|
-
const RANK = { red: 0, amber: 1, green: 2 }
|
|
618
|
-
let worst = null
|
|
619
|
-
for (const s of byPerson.values()) {
|
|
620
|
-
if (!worst || RANK[s.sig] < RANK[worst.sig] || (RANK[s.sig] === RANK[worst.sig] && s.date >= worst.date)) {
|
|
621
|
-
worst = s
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
const mem = stakeholdersMemoryHealth(eng)
|
|
625
|
-
let trust, signalAge = null, stale = false, trustReason = ''
|
|
626
|
-
if (!mem.ok && !worst) {
|
|
627
|
-
trust = 'amber'
|
|
628
|
-
trustReason = mem.warn
|
|
629
|
-
} else if (worst) {
|
|
630
|
-
trust = worst.sig === 'red' ? 'RED' : worst.sig
|
|
631
|
-
trustReason = (worst.text || '').slice(0, 80)
|
|
632
|
-
if (worst.date) {
|
|
633
|
-
signalAge = Math.max(0, Math.floor((Date.now() - Date.parse(worst.date)) / 86400000))
|
|
634
|
-
stale = signalAge > 21
|
|
635
|
-
}
|
|
636
|
-
} else {
|
|
637
|
-
const sLines = stake.split('\n').filter(l => !(/green/i.test(l) && /red|amber/i.test(l)))
|
|
638
|
-
trust = sLines.some(l => /\bred\b/i.test(l)) ? 'RED'
|
|
639
|
-
: sLines.some(l => /amber|gone quiet|routing around|escalat/i.test(l)) ? 'amber' : 'green'
|
|
640
|
-
}
|
|
641
|
-
const topRisk = (risks.split('\n').find(l => {
|
|
642
|
-
const t = l.trim()
|
|
643
|
-
return /^[-|]/.test(t) && t.length > 20 && !/^\|?[-\s|]+$/.test(t) &&
|
|
644
|
-
!/risk\s*\|\s*status|mitigation/i.test(t) && !t.startsWith('<!--')
|
|
645
|
-
}) || '').replace(/\|/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 80)
|
|
646
|
-
// Prefer the trust trigger (signal / memory warn) over a random risk line when triage is not green
|
|
647
|
-
const reason = (trust !== 'green' && (trustReason || mem.warn)) ? (trustReason || mem.warn) : topRisk
|
|
648
|
-
const openRisks = countOpenRisks(eng)
|
|
649
|
-
const nextAction = nextActionLine(ctx)
|
|
650
|
-
let updated = 'never', ageDays = Infinity
|
|
651
|
-
try {
|
|
652
|
-
ageDays = Math.floor((Date.now() - fs.statSync(path.join(eng, 'context.md')).mtimeMs) / 86400000)
|
|
653
|
-
updated = ageDays === 0 ? 'today' : `${ageDays}d ago`
|
|
654
|
-
} catch (_) {}
|
|
655
|
-
return { phase, trust, signalAge, stale, topRisk, reason, memoryWarn: mem.warn, openRisks, nextAction, updated, ageDays }
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
function resumeTriage(eng) {
|
|
659
|
-
const s = computeSignals(eng)
|
|
660
|
-
const label = s.trust + (s.stale ? '?' : '')
|
|
661
|
-
const phase = s.phase === '?' ? 'unset' : s.phase
|
|
662
|
-
const lines = [
|
|
663
|
-
`TRIAGE [${label.padEnd(6)}] phase:${phase} updated:${s.updated} open risks:${s.openRisks}`,
|
|
664
|
-
]
|
|
665
|
-
if (s.reason) {
|
|
666
|
-
const age = s.signalAge != null ? ` (${s.signalAge}d old${s.stale ? ', STALE - reconfirm' : ''})` : ''
|
|
667
|
-
lines.push(` trust: ${s.reason}${age}`)
|
|
668
|
-
}
|
|
669
|
-
if (s.nextAction) lines.push(` next: ${s.nextAction}`)
|
|
670
|
-
else lines.push(' next: (none set - add under ## Next action in context.md)')
|
|
671
|
-
return lines.join('\n')
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
|
|
675
468
|
// ---------- dashboard content extractors (best-effort, read-only) ----------
|
|
676
469
|
// The fieldbook's structured widgets (stakeholders, risks, log, stats) want
|
|
677
470
|
// data shapes that .fde/ markdown does not literally carry - it is written by
|
|
@@ -743,6 +536,15 @@ function parseMdTable(md) {
|
|
|
743
536
|
}
|
|
744
537
|
function colIndex(headers, rx) { return headers.findIndex(h => rx.test(h)) }
|
|
745
538
|
|
|
539
|
+
const {
|
|
540
|
+
signalSubjectKey,
|
|
541
|
+
nextActionLine,
|
|
542
|
+
computeSignals,
|
|
543
|
+
resumeTriage,
|
|
544
|
+
} = createTrustApi({
|
|
545
|
+
fs, path, readClean, readEng, parseMdTable, sectionBody, SIGNAL_LEDGER, memoryDirtyManual,
|
|
546
|
+
})
|
|
547
|
+
|
|
746
548
|
// Stakeholders: columns are matched by header wording, not position - real
|
|
747
549
|
// files use "Name" or "Name / role", "Stance" or "Signal", with or without a
|
|
748
550
|
// separate Role column (see examples/garvey-payments vs examples/kesterman-
|
|
@@ -1181,7 +983,9 @@ function cmdLogUndo() {
|
|
|
1181
983
|
if (led != null) withFileLock(ledgerPath, () => { atomicWriteFile(ledgerPath, led.endsWith('\n') ? led : led + '\n') })
|
|
1182
984
|
}
|
|
1183
985
|
try { fs.unlinkSync(metaPath) } catch (_) {}
|
|
1184
|
-
const
|
|
986
|
+
const undoFiles = [meta.file]
|
|
987
|
+
if (/\[signal:(red|amber|green)\]/i.test(meta.entry)) undoFiles.push(SIGNAL_LEDGER)
|
|
988
|
+
const hash = commitMemory(eng, `undo ${meta.file}`, { files: undoFiles })
|
|
1185
989
|
console.log(`undid last write → ${meta.file}${hash ? ` @${hash}` : ''}`)
|
|
1186
990
|
}
|
|
1187
991
|
|
|
@@ -1242,7 +1046,7 @@ function setContextPhase(eng, phase) {
|
|
|
1242
1046
|
md = md.replace(/\*\*Last updated:\*\*\s*.*/i, `**Last updated:** ${today}`)
|
|
1243
1047
|
}
|
|
1244
1048
|
withFileLock(p, () => { atomicWriteFile(p, md.endsWith('\n') ? md : md + '\n') })
|
|
1245
|
-
return commitMemory(eng, `phase ${phase}
|
|
1049
|
+
return commitMemory(eng, `phase ${phase}`, { files: ['context.md'] })
|
|
1246
1050
|
}
|
|
1247
1051
|
|
|
1248
1052
|
|
|
@@ -1444,7 +1248,9 @@ function cmdDebrief(args) {
|
|
|
1444
1248
|
|
|
1445
1249
|
const { counts, ctxLines } = routeDebriefInput(eng, input, { dry, force })
|
|
1446
1250
|
if (!dry) {
|
|
1447
|
-
const hash = commitMemory(eng, 'debrief'
|
|
1251
|
+
const hash = commitMemory(eng, 'debrief', {
|
|
1252
|
+
files: ['decisions.md', 'risks.md', 'delivery.md', 'stakeholders.md', 'context.md', SIGNAL_LEDGER],
|
|
1253
|
+
})
|
|
1448
1254
|
try { fs.unlinkSync(path.join(eng, DEBRIEF_PROPOSE)) } catch (_) {}
|
|
1449
1255
|
if (hash) console.log(`memory @${hash}`)
|
|
1450
1256
|
}
|
|
@@ -1487,7 +1293,7 @@ function cmdReceipts(args) {
|
|
|
1487
1293
|
const agreed = collect(AGREEMENTS)
|
|
1488
1294
|
const claimed = collect(CLAIMS)
|
|
1489
1295
|
if (agreed.length) {
|
|
1490
|
-
console.log('
|
|
1296
|
+
console.log('ON RECORD (dated - defensible):')
|
|
1491
1297
|
agreed.forEach(h => console.log(h))
|
|
1492
1298
|
}
|
|
1493
1299
|
if (claimed.length) {
|
|
@@ -1525,7 +1331,7 @@ function cmdCapture() {
|
|
|
1525
1331
|
try {
|
|
1526
1332
|
ensureMemoryGit(eng)
|
|
1527
1333
|
lockedAppendFile(path.join(eng, 'context.md'), block, { soft: true })
|
|
1528
|
-
commitMemory(eng, 'session capture')
|
|
1334
|
+
commitMemory(eng, 'session capture', { files: ['context.md'] })
|
|
1529
1335
|
} catch (_) {}
|
|
1530
1336
|
}
|
|
1531
1337
|
|
|
@@ -1557,7 +1363,7 @@ function cmdOwner(args) {
|
|
|
1557
1363
|
withFileLock(path.join(eng, OWNER_FILE), () => {
|
|
1558
1364
|
atomicWriteFile(path.join(eng, OWNER_FILE), `name: ${name}\nemail: ${email}\n`)
|
|
1559
1365
|
})
|
|
1560
|
-
const hash = commitMemory(eng, 'owner set')
|
|
1366
|
+
const hash = commitMemory(eng, 'owner set', { files: [OWNER_FILE] })
|
|
1561
1367
|
console.log(`owner → ${name} <${email}>${hash ? ` @${hash}` : ''}`)
|
|
1562
1368
|
return
|
|
1563
1369
|
}
|
|
@@ -1568,6 +1374,49 @@ function cmdOwner(args) {
|
|
|
1568
1374
|
else console.log('memory HEAD: (unversioned - git init on next write)')
|
|
1569
1375
|
}
|
|
1570
1376
|
|
|
1377
|
+
function riskFingerprint(text) {
|
|
1378
|
+
return String(text || '')
|
|
1379
|
+
.toLowerCase()
|
|
1380
|
+
.replace(/\[\d{4}-\d{2}-\d{2}\]/g, '')
|
|
1381
|
+
.replace(/\[@[^\]]+\]/g, '')
|
|
1382
|
+
.replace(/^\s*[-*|]\s*/, '')
|
|
1383
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
1384
|
+
.trim()
|
|
1385
|
+
.split(/\s+/)
|
|
1386
|
+
.slice(0, 5)
|
|
1387
|
+
.join(' ')
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
function findDuplicateOpenRisks(eng) {
|
|
1391
|
+
// Scan raw open risks (not extractRisks - that dedupes exact text).
|
|
1392
|
+
const md = readClean(eng, 'risks.md')
|
|
1393
|
+
const body = md.split(/^#{1,6}\s+Retired\b/im)[0] || md
|
|
1394
|
+
const items = []
|
|
1395
|
+
const table = parseMdTable(body)
|
|
1396
|
+
if (table) {
|
|
1397
|
+
const riskIdx = colIndex(table.headers, /^risk$/i)
|
|
1398
|
+
if (riskIdx !== -1) {
|
|
1399
|
+
for (const cs of table.rows) {
|
|
1400
|
+
const t = (cs[riskIdx] || '').trim()
|
|
1401
|
+
if (t) items.push(t)
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
for (const raw of body.split('\n')) {
|
|
1406
|
+
const t = raw.trim()
|
|
1407
|
+
const m = t.match(/^-\s*\[\d{4}-\d{2}-\d{2}\]\s*(?:\[@[^\]]+\]\s*)?(.*)$/)
|
|
1408
|
+
if (m && m[1].trim()) items.push(m[1].trim())
|
|
1409
|
+
}
|
|
1410
|
+
const byKey = new Map()
|
|
1411
|
+
for (const text of items) {
|
|
1412
|
+
const key = riskFingerprint(text)
|
|
1413
|
+
if (key.length < 12) continue
|
|
1414
|
+
if (!byKey.has(key)) byKey.set(key, [])
|
|
1415
|
+
byKey.get(key).push(text)
|
|
1416
|
+
}
|
|
1417
|
+
return [...byKey.values()].filter(g => g.length >= 2)
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1571
1420
|
function cmdDoctor() {
|
|
1572
1421
|
const eng = resolveEngagement()
|
|
1573
1422
|
if (!eng) { console.error('no engagement - run: fde resume --init <name>'); process.exit(2) }
|
|
@@ -1587,6 +1436,18 @@ function cmdDoctor() {
|
|
|
1587
1436
|
if (!sectionBody(readClean(eng, 'context.md'), 'Next action')) {
|
|
1588
1437
|
issues.push('no ## Next action in context.md - Monday morning has nothing to drive')
|
|
1589
1438
|
}
|
|
1439
|
+
if ((s.phase === 'close' || s.phase === 'ship') && s.openRisks > 0) {
|
|
1440
|
+
issues.push(
|
|
1441
|
+
`phase is ${s.phase} with ${s.openRisks} open risk(s) - retire, hand off, or move still-live ones before calling the embed done`
|
|
1442
|
+
)
|
|
1443
|
+
}
|
|
1444
|
+
const dupes = findDuplicateOpenRisks(eng)
|
|
1445
|
+
if (dupes.length) {
|
|
1446
|
+
const sample = (dupes[0][0] || '').replace(/\s+/g, ' ').trim().slice(0, 60)
|
|
1447
|
+
issues.push(
|
|
1448
|
+
`${dupes.length} duplicate open-risk cluster(s) (e.g. "${sample}${sample.length >= 60 ? '…' : ''}") - consolidate or retire echoes in risks.md`
|
|
1449
|
+
)
|
|
1450
|
+
}
|
|
1590
1451
|
console.log(`FDE DOCTOR - ${engagementSlugFromPath(eng)}`)
|
|
1591
1452
|
console.log(resumeTriage(eng))
|
|
1592
1453
|
if (!issues.length) {
|
|
@@ -1598,6 +1459,73 @@ function cmdDoctor() {
|
|
|
1598
1459
|
process.exit(1)
|
|
1599
1460
|
}
|
|
1600
1461
|
|
|
1462
|
+
// Remove buried lines that contain a search term (secrets noticed hours later).
|
|
1463
|
+
// Preview by default; --apply commits the scrub to the memory ledger.
|
|
1464
|
+
const REDACT_FILES = [
|
|
1465
|
+
'decisions.md', 'risks.md', 'delivery.md', 'stakeholders.md', 'context.md',
|
|
1466
|
+
'brief.md', 'reality.md', 'assumptions.md', SIGNAL_LEDGER,
|
|
1467
|
+
]
|
|
1468
|
+
|
|
1469
|
+
function cmdRedact(args) {
|
|
1470
|
+
const apply = args.includes('--apply')
|
|
1471
|
+
const term = args.filter(a => a !== '--apply').join(' ').trim()
|
|
1472
|
+
if (!term || term.length < 4) {
|
|
1473
|
+
console.error('usage: fde redact <term> [--apply]\n preview lines containing <term>; --apply removes them and commits the ledger')
|
|
1474
|
+
process.exit(1)
|
|
1475
|
+
}
|
|
1476
|
+
const eng = resolveEngagement({ forWrite: apply })
|
|
1477
|
+
if (!eng) { console.error('no engagement - run: fde resume --init <name>'); process.exit(2) }
|
|
1478
|
+
const needle = term.toLowerCase()
|
|
1479
|
+
const hits = []
|
|
1480
|
+
for (const file of REDACT_FILES) {
|
|
1481
|
+
const abs = path.join(eng, file)
|
|
1482
|
+
if (!fs.existsSync(abs)) continue
|
|
1483
|
+
const md = readEng(eng, file)
|
|
1484
|
+
const lines = md.split('\n')
|
|
1485
|
+
lines.forEach((line, i) => {
|
|
1486
|
+
if (line.toLowerCase().includes(needle)) hits.push({ file, lineNo: i + 1, line })
|
|
1487
|
+
})
|
|
1488
|
+
}
|
|
1489
|
+
if (!hits.length) {
|
|
1490
|
+
console.log(`redact: no lines contain ${JSON.stringify(term)}`)
|
|
1491
|
+
return
|
|
1492
|
+
}
|
|
1493
|
+
console.log(`REDACT - ${hits.length} matching line(s) for ${JSON.stringify(term)}`)
|
|
1494
|
+
hits.slice(0, 20).forEach(h => {
|
|
1495
|
+
const preview = h.line.length > 100 ? h.line.slice(0, 97) + '…' : h.line
|
|
1496
|
+
console.log(` ${h.file}:${h.lineNo} ${preview}`)
|
|
1497
|
+
})
|
|
1498
|
+
if (hits.length > 20) console.log(` … +${hits.length - 20} more`)
|
|
1499
|
+
if (!apply) {
|
|
1500
|
+
console.log('\nPreview only. Remove and commit: fde redact <term> --apply')
|
|
1501
|
+
console.log('Note: git history still holds prior commits - rotate the real secret.')
|
|
1502
|
+
return
|
|
1503
|
+
}
|
|
1504
|
+
ensureMemoryGit(eng)
|
|
1505
|
+
const touched = []
|
|
1506
|
+
const byFile = new Map()
|
|
1507
|
+
for (const h of hits) {
|
|
1508
|
+
if (!byFile.has(h.file)) byFile.set(h.file, new Set())
|
|
1509
|
+
byFile.get(h.file).add(h.lineNo)
|
|
1510
|
+
}
|
|
1511
|
+
for (const [file, lineNos] of byFile) {
|
|
1512
|
+
const abs = path.join(eng, file)
|
|
1513
|
+
const before = readEng(eng, file)
|
|
1514
|
+
const kept = before.split('\n').filter((_, i) => !lineNos.has(i + 1))
|
|
1515
|
+
const after = kept.join('\n')
|
|
1516
|
+
if (after === before) continue
|
|
1517
|
+
withFileLock(abs, () => { atomicWriteFile(abs, after.endsWith('\n') || after === '' ? after : after + '\n') })
|
|
1518
|
+
touched.push(file)
|
|
1519
|
+
}
|
|
1520
|
+
if (!touched.length) {
|
|
1521
|
+
console.log('nothing changed')
|
|
1522
|
+
return
|
|
1523
|
+
}
|
|
1524
|
+
const hash = commitMemory(eng, `redact ${term.slice(0, 40)}`, { files: touched })
|
|
1525
|
+
console.log(`redacted ${hits.length} line(s) in ${touched.join(', ')}${hash ? ` @${hash}` : ''}`)
|
|
1526
|
+
console.log('rotate the real credential if this was a secret - history may still contain it')
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1601
1529
|
function cmdPrep(args) {
|
|
1602
1530
|
const eng = resolveEngagement()
|
|
1603
1531
|
if (!eng) { console.error('no engagement - run: fde resume --init <name>'); process.exit(2) }
|
|
@@ -1719,7 +1647,7 @@ function cmdGarden(args) {
|
|
|
1719
1647
|
applied++
|
|
1720
1648
|
console.log(`applied: archived ${p.sessionBlocks.length} old session-end blocks → context-archive.md`)
|
|
1721
1649
|
}
|
|
1722
|
-
const hash = commitMemory(eng, 'garden')
|
|
1650
|
+
const hash = commitMemory(eng, 'garden', { files: ['context.md', 'context-archive.md'] })
|
|
1723
1651
|
if (!applied) console.log('no mechanical proposals applied (manual items remain)')
|
|
1724
1652
|
else console.log(`garden done${hash ? ` @${hash}` : ''}`)
|
|
1725
1653
|
}
|
|
@@ -1738,7 +1666,8 @@ function cmdStatus(args) {
|
|
|
1738
1666
|
const eng = path.join(ENGAGEMENTS_ROOT, d, '.fde')
|
|
1739
1667
|
if (!fs.existsSync(eng)) continue
|
|
1740
1668
|
const s = computeSignals(eng)
|
|
1741
|
-
|
|
1669
|
+
const note = [s.memoryWarn, (s.dirtyFiles && s.dirtyFiles.length) ? `dirty:${s.dirtyFiles.length}` : '', s.reason || s.topRisk].filter(Boolean).join(' · ').slice(0, 70)
|
|
1670
|
+
rows.push({ name: d, phase: s.phase, trust: s.trust, signalAge: s.signalAge, stale: s.stale, updated: s.updated, reason: note, memoryWarn: s.memoryWarn, dirtyFiles: s.dirtyFiles })
|
|
1742
1671
|
}
|
|
1743
1672
|
} else {
|
|
1744
1673
|
const eng = resolveEngagement()
|
|
@@ -1747,7 +1676,8 @@ function cmdStatus(args) {
|
|
|
1747
1676
|
process.exit(2)
|
|
1748
1677
|
}
|
|
1749
1678
|
const s = computeSignals(eng)
|
|
1750
|
-
|
|
1679
|
+
const note = [s.memoryWarn, (s.dirtyFiles && s.dirtyFiles.length) ? `dirty:${s.dirtyFiles.length}` : '', s.reason || s.topRisk].filter(Boolean).join(' · ').slice(0, 70)
|
|
1680
|
+
rows.push({ name: engagementSlugFromPath(eng), phase: s.phase, trust: s.trust, signalAge: s.signalAge, stale: s.stale, updated: s.updated, reason: note, memoryWarn: s.memoryWarn, dirtyFiles: s.dirtyFiles })
|
|
1751
1681
|
}
|
|
1752
1682
|
if (!rows.length) { console.log('no engagements yet'); return }
|
|
1753
1683
|
const order = { RED: 0, amber: 1, green: 2 }
|
|
@@ -1758,91 +1688,19 @@ function cmdStatus(args) {
|
|
|
1758
1688
|
const label = r.trust + (r.stale ? '?' : '')
|
|
1759
1689
|
const sig = r.signalAge != null ? `signal ${r.signalAge}d old${r.stale ? ' (STALE - reconfirm)' : ''} ` : ''
|
|
1760
1690
|
console.log(` [${label.padEnd(6)}] ${r.name.padEnd(24)} phase:${(r.phase === '?' ? 'unset' : r.phase).padEnd(10)} updated:${r.updated.padEnd(8)} ${sig}${r.reason}`)
|
|
1691
|
+
if (r.memoryWarn) console.log(` memory: ${r.memoryWarn}`)
|
|
1692
|
+
if (r.dirtyFiles && r.dirtyFiles.length) {
|
|
1693
|
+
console.log(` ⚠ dirty (uncommitted manual edits): ${r.dirtyFiles.slice(0, 5).join(', ')}`)
|
|
1694
|
+
}
|
|
1761
1695
|
}
|
|
1762
1696
|
if (!all) console.log('\n(current engagement only - pass --all for the full portfolio)')
|
|
1763
1697
|
console.log('\ntrust: worst active [signal:x] across stakeholders (latest per person) - a green from B cannot clear an amber/red on A; keyword heuristic only when none exists.')
|
|
1764
1698
|
}
|
|
1765
1699
|
|
|
1766
|
-
// ---------- dashboard (deterministic markdown → one local HTML) ----------
|
|
1767
|
-
|
|
1768
|
-
function escapeHtml(s) {
|
|
1769
|
-
return String(s)
|
|
1770
|
-
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
|
1771
|
-
.replace(/"/g, '"').replace(/'/g, ''')
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
// Never leak <private> notes or template hint comments into the rendered page.
|
|
1775
|
-
// Closed pairs are redacted; an unclosed <private> redacts to end-of-text so a
|
|
1776
|
-
// forgotten closing tag can never leak the rest of the file.
|
|
1777
|
-
function stripPrivate(md) {
|
|
1778
|
-
// Redact <private> before <!-- --> so a private block that itself contains a
|
|
1779
|
-
// comment can't survive. Closed pairs first; then any unclosed <private> to
|
|
1780
|
-
// end-of-text so a forgotten close tag never leaks the rest of the file.
|
|
1781
|
-
// Case-insensitive (<PRIVATE> too). This is the ONE redactor every read path
|
|
1782
|
-
// that can reach the model or a shared artifact must go through - see readClean.
|
|
1783
|
-
return md
|
|
1784
|
-
.replace(/<private>[\s\S]*?<\/private>/gi, '(private - redacted)')
|
|
1785
|
-
.replace(/<private>[\s\S]*$/i, '(private - redacted)')
|
|
1786
|
-
.replace(/<!--[\s\S]*?-->/g, '')
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
function inlineMd(s) {
|
|
1790
|
-
return escapeHtml(s)
|
|
1791
|
-
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
1792
|
-
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
1793
|
-
}
|
|
1794
1700
|
|
|
1795
|
-
//
|
|
1796
|
-
// documents, not logs (success.md, terrain.md, trust-profile.md): they don't
|
|
1797
|
-
// fit the structured stakeholder/risk/log widgets, but they're real content
|
|
1798
|
-
// (trust-profile.md especially - sacred data and AI policy) and must stay
|
|
1799
|
-
// visible in the fieldbook, not silently dropped. Headings, tables, bullet
|
|
1800
|
-
// lists, and paragraphs only - matches what these files actually contain.
|
|
1801
|
-
function mdBlockHtml(md) {
|
|
1802
|
-
const lines = md.split('\n')
|
|
1803
|
-
const out = []; let i = 0
|
|
1804
|
-
while (i < lines.length) {
|
|
1805
|
-
const t = lines[i].trim()
|
|
1806
|
-
if (!t) { i++; continue }
|
|
1807
|
-
if (/^\|.*\|/.test(t)) {
|
|
1808
|
-
const tbl = []
|
|
1809
|
-
while (i < lines.length && /^\s*\|.*\|/.test(lines[i])) { tbl.push(lines[i]); i++ }
|
|
1810
|
-
const parsed = parseMdTable(tbl.join('\n'))
|
|
1811
|
-
if (parsed) {
|
|
1812
|
-
out.push('<table class="fb-table"><thead><tr>' + parsed.headers.map(h => '<th>' + inlineMd(h) + '</th>').join('') + '</tr></thead><tbody>' +
|
|
1813
|
-
parsed.rows.map(r => '<tr>' + r.map(c => '<td>' + inlineMd(c) + '</td>').join('') + '</tr>').join('') + '</tbody></table>')
|
|
1814
|
-
}
|
|
1815
|
-
continue
|
|
1816
|
-
}
|
|
1817
|
-
const h = t.match(/^(#{1,6})\s+(.*)$/)
|
|
1818
|
-
if (h) { out.push('<div class="fb-sec">' + inlineMd(h[2]) + '</div>'); i++; continue }
|
|
1819
|
-
if (/^[-*]\s+/.test(t)) {
|
|
1820
|
-
const items = []
|
|
1821
|
-
while (i < lines.length && /^\s*[-*]\s+/.test(lines[i])) { items.push('<li>' + inlineMd(lines[i].trim().replace(/^[-*]\s+/, '')) + '</li>'); i++ }
|
|
1822
|
-
out.push('<ul class="fb-plain-list">' + items.join('') + '</ul>'); continue
|
|
1823
|
-
}
|
|
1824
|
-
const para = []
|
|
1825
|
-
while (i < lines.length && lines[i].trim() && !/^\s*\|.*\|/.test(lines[i]) && !/^#{1,6}\s/.test(lines[i].trim()) && !/^[-*]\s+/.test(lines[i].trim())) {
|
|
1826
|
-
para.push(lines[i].trim()); i++
|
|
1827
|
-
}
|
|
1828
|
-
out.push('<p class="fb-plain-p">' + inlineMd(para.join(' ')) + '</p>')
|
|
1829
|
-
}
|
|
1830
|
-
return out.join('\n')
|
|
1831
|
-
}
|
|
1701
|
+
// ---------- dashboard (deterministic markdown → one local HTML) ----------
|
|
1832
1702
|
|
|
1833
|
-
|
|
1834
|
-
// blank bullets - same heuristic the old dashboard used to skip empty files.
|
|
1835
|
-
function hasRealContent(md) {
|
|
1836
|
-
const txt = md
|
|
1837
|
-
.replace(/^#{1,6}\s.*$/gm, '')
|
|
1838
|
-
.replace(/^\s*\|[\s:|-]+\|\s*$/gm, '')
|
|
1839
|
-
.replace(/^\s*\|[\s|]*\|\s*$/gm, '')
|
|
1840
|
-
.replace(/\*\*[^*]+:\*\*\s*$/gm, '')
|
|
1841
|
-
.replace(/[-*]\s*$/gm, '')
|
|
1842
|
-
.replace(/\s+/g, ' ')
|
|
1843
|
-
.trim()
|
|
1844
|
-
return txt.length > 0
|
|
1845
|
-
}
|
|
1703
|
+
const render = require("./lib/render")
|
|
1846
1704
|
|
|
1847
1705
|
function gatherEngagements(opts = {}) {
|
|
1848
1706
|
const list = []
|
|
@@ -1860,518 +1718,6 @@ function gatherEngagements(opts = {}) {
|
|
|
1860
1718
|
return list
|
|
1861
1719
|
}
|
|
1862
1720
|
|
|
1863
|
-
// Geist (400-700 variable weight) + Geist Mono (400-600 variable weight),
|
|
1864
|
-
// latin subset only, base64-embedded woff2 - zero network calls, still opens
|
|
1865
|
-
// fully offline. Spliced in verbatim by the build step below; never fetched.
|
|
1866
|
-
const FONT_FACE_CSS = `@font-face {
|
|
1867
|
-
font-family: 'Geist';
|
|
1868
|
-
font-style: normal;
|
|
1869
|
-
font-weight: 400 700;
|
|
1870
|
-
font-display: swap;
|
|
1871
|
-
src: url(data:font/woff2;base64,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) format('woff2');
|
|
1872
|
-
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
1873
|
-
}
|
|
1874
|
-
@font-face {
|
|
1875
|
-
font-family: 'Geist Mono';
|
|
1876
|
-
font-style: normal;
|
|
1877
|
-
font-weight: 400 600;
|
|
1878
|
-
font-display: swap;
|
|
1879
|
-
src: url(data:font/woff2;base64,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) format('woff2');
|
|
1880
|
-
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
1881
|
-
}`
|
|
1882
|
-
|
|
1883
|
-
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
1884
|
-
const WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
|
|
1885
|
-
function formatToday(d) { return `${WEEKDAYS[d.getDay()]}, ${MONTHS[d.getMonth()]} ${d.getDate()}` }
|
|
1886
|
-
function formatLogDate(iso) {
|
|
1887
|
-
const m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})$/)
|
|
1888
|
-
return m ? `${MONTHS[parseInt(m[2], 10) - 1]} ${parseInt(m[3], 10)}` : iso
|
|
1889
|
-
}
|
|
1890
|
-
function trustWord(t) { return t === 'green' ? 'steady' : t === 'amber' ? 'watch' : 'at risk' }
|
|
1891
|
-
function dotClassFor(trust) { return trust === 'RED' ? 'red' : trust }
|
|
1892
|
-
|
|
1893
|
-
// Two-pane fieldbook: left rail (search + today + per-client nav), right main
|
|
1894
|
-
// panel with a Today queue view and a per-client detail view, plus a command
|
|
1895
|
-
// palette overlay. Light + dark are both full, real token sets (not one
|
|
1896
|
-
// derived from the other) - translated 1:1 from the design's THEMES object
|
|
1897
|
-
// into the file's existing :root / html[data-fde-theme="dark"] mechanism.
|
|
1898
|
-
function dashStyles() {
|
|
1899
|
-
return `
|
|
1900
|
-
:root{--bg:#f7f7f6;--panel:#eceae7;--rowhover:#e4e2de;--ink:#14140f;--ink-soft:#4a4942;--ink-faint:#7c7a71;--line:#dedcd6;--accent:#12885c;--accent-bg:#dcf1e6;--green:#12885c;--amber:#a56a19;--red:#b23e2b;--shadow:0 12px 48px rgba(0,0,0,.18)}
|
|
1901
|
-
html[data-fde-theme="dark"]{--bg:#0a0a09;--panel:#151513;--rowhover:#1c1c19;--ink:#f2f1ec;--ink-soft:#b0ada2;--ink-faint:#726f64;--line:#232320;--accent:#3ddc97;--accent-bg:#132420;--green:#3ddc97;--amber:#f0b860;--red:#ff8f83;--shadow:0 12px 48px rgba(0,0,0,.5)}
|
|
1902
|
-
${FONT_FACE_CSS}
|
|
1903
|
-
*,*::before,*::after{box-sizing:border-box}
|
|
1904
|
-
html,body{margin:0;padding:0;height:100%}
|
|
1905
|
-
body{font-family:'Geist',system-ui,-apple-system,sans-serif;color:var(--ink);background:var(--bg);-webkit-font-smoothing:antialiased;transition:background .15s,color .15s}
|
|
1906
|
-
::selection{background:var(--accent-bg)}
|
|
1907
|
-
button,input{font-family:inherit}
|
|
1908
|
-
code{background:var(--panel);padding:1px 5px;border-radius:4px;font-size:12px;color:var(--ink-soft);font-family:'Geist Mono',monospace}
|
|
1909
|
-
strong{font-weight:600}
|
|
1910
|
-
.empty{color:var(--ink-faint);font-style:italic;font-size:13px}
|
|
1911
|
-
.fb-app{height:100vh;display:flex;flex-direction:column;overflow:hidden}
|
|
1912
|
-
.fb-scroll::-webkit-scrollbar{width:7px;height:7px}
|
|
1913
|
-
.fb-scroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:7px}
|
|
1914
|
-
.fb-scroll::-webkit-scrollbar-track{background:transparent}
|
|
1915
|
-
.fb-header{flex:0 0 auto;display:flex;align-items:center;gap:18px;padding:14px 24px;background:var(--bg);border-bottom:1px solid var(--line)}
|
|
1916
|
-
.fb-brand{display:flex;align-items:baseline;gap:9px}
|
|
1917
|
-
.fb-wordmark{font-family:'Geist Mono',monospace;font-size:14px;font-weight:700;letter-spacing:.01em}
|
|
1918
|
-
.fb-tagline{font-family:'Geist Mono',monospace;font-size:12px;color:var(--ink-faint)}
|
|
1919
|
-
.fb-need{font-family:'Geist Mono',monospace;font-size:11.5px;color:var(--accent);margin-left:8px}
|
|
1920
|
-
.fb-spacer{flex:1 1 auto}
|
|
1921
|
-
.fb-today{font-family:'Geist Mono',monospace;font-size:11.5px;color:var(--ink-faint)}
|
|
1922
|
-
.fb-btn{border:1px solid var(--line);border-radius:6px;padding:5px 10px;background:transparent;color:var(--ink-faint);font-family:'Geist Mono',monospace;font-size:11px;cursor:pointer;line-height:1.4}
|
|
1923
|
-
.fb-btn:hover{color:var(--ink);border-color:var(--accent)}
|
|
1924
|
-
.fb-body{flex:1 1 auto;display:flex;min-height:0}
|
|
1925
|
-
.fb-rail{flex:0 0 288px;display:flex;flex-direction:column;background:var(--bg);border-right:1px solid var(--line)}
|
|
1926
|
-
.fb-rail-search{flex:0 0 auto;padding:12px 14px}
|
|
1927
|
-
.fb-search{width:100%;padding:6px 9px;border:1px solid var(--line);border-radius:6px;background:var(--panel);color:var(--ink);font-family:'Geist Mono',monospace;font-size:12px;outline:none}
|
|
1928
|
-
.fb-search:focus{border-color:var(--accent)}
|
|
1929
|
-
.fb-search::placeholder{color:var(--ink-faint)}
|
|
1930
|
-
.fb-rail-list{flex:1 1 auto;overflow-y:auto;padding:0 8px 16px}
|
|
1931
|
-
.fb-nav{width:100%;text-align:left;display:block;padding:9px 10px;border:none;border-left:2px solid transparent;border-radius:0 5px 5px 0;background:transparent;cursor:pointer;color:inherit;margin-bottom:2px}
|
|
1932
|
-
.fb-nav:hover{background:var(--panel)}
|
|
1933
|
-
.fb-nav.active{background:var(--panel);border-left-color:var(--accent)}
|
|
1934
|
-
.fb-nav.hide{display:none}
|
|
1935
|
-
.fb-nav-row{display:flex;align-items:center;gap:8px}
|
|
1936
|
-
.fb-nav-name,.fb-nav-today{font-family:'Geist Mono',monospace;font-size:12.5px;font-weight:500;color:var(--ink);flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1937
|
-
.fb-nav.active .fb-nav-name{font-weight:600}
|
|
1938
|
-
.fb-nav-today{color:var(--accent);font-weight:600}
|
|
1939
|
-
.fb-nav-count{margin-left:auto;font-family:'Geist Mono',monospace;font-size:10.5px;color:var(--ink-faint)}
|
|
1940
|
-
.fb-nav-meta{margin-top:3px;padding-left:14px;font-family:'Geist Mono',monospace;font-size:10.5px;color:var(--ink-faint)}
|
|
1941
|
-
.fb-nav-reason{margin-top:3px;padding-left:14px;font-size:12px;line-height:1.35;color:var(--ink-soft)}
|
|
1942
|
-
.fb-nav-reason.hide{display:none}
|
|
1943
|
-
.fb-nav-reason mark{background:var(--accent-bg);color:inherit;border-radius:2px;padding:0 1px}
|
|
1944
|
-
.fb-main{flex:1 1 auto;overflow-y:auto;background:var(--bg)}
|
|
1945
|
-
.fb-main-inner{max-width:1440px;margin:0 auto;padding:28px 48px 64px}
|
|
1946
|
-
.fb-grid-wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:22px 40px;margin-top:22px}
|
|
1947
|
-
.fb-grid-wrap .fb-block{margin-top:0}
|
|
1948
|
-
.fb-top-grid{display:grid;grid-template-columns:1fr 280px;gap:0 32px;align-items:stretch}
|
|
1949
|
-
.fb-vitals{display:flex;flex-direction:column;margin-top:22px;padding:16px 18px;background:var(--panel);border:1px solid var(--line);border-radius:8px}
|
|
1950
|
-
.fb-vital-row{display:flex;justify-content:space-between;gap:10px;padding:5px 0;font-size:12.5px}
|
|
1951
|
-
.fb-vital-row+.fb-vital-row{border-top:1px solid var(--line)}
|
|
1952
|
-
.fb-vital-label{font-family:'Geist Mono',monospace;font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-faint)}
|
|
1953
|
-
.fb-vital-val{color:var(--ink);font-weight:600;text-align:right}
|
|
1954
|
-
.fb-vital-div{margin:4px 0;border-top:1px solid var(--line)}
|
|
1955
|
-
@media(max-width:920px){.fb-top-grid{grid-template-columns:1fr}.fb-vitals{max-width:400px}}
|
|
1956
|
-
.fb-view[hidden]{display:none}
|
|
1957
|
-
.fb-eyebrow{font-family:'Geist Mono',monospace;font-size:11px;color:var(--ink-faint)}
|
|
1958
|
-
.fb-h1{font-family:'Geist Mono',monospace;font-size:26px;font-weight:600;letter-spacing:-.01em;margin:6px 0 0}
|
|
1959
|
-
.fb-title-row{display:flex;align-items:baseline;gap:10px;margin-top:6px}
|
|
1960
|
-
.fb-title-row .fb-h1{margin-top:0}
|
|
1961
|
-
.fb-trust{display:flex;align-items:center;gap:6px;font-family:'Geist Mono',monospace;font-size:12px}
|
|
1962
|
-
.fb-meta-line{margin-top:5px;font-family:'Geist Mono',monospace;font-size:12px;color:var(--ink-faint)}
|
|
1963
|
-
.fb-block{margin-top:22px;padding-top:18px;border-top:1px solid var(--line)}
|
|
1964
|
-
.fb-sec{font-family:'Geist Mono',monospace;font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--accent);margin:0 0 8px}
|
|
1965
|
-
.fb-sec-row{display:flex;align-items:baseline;gap:8px}
|
|
1966
|
-
.fb-sec-row .fb-sec{margin:0}
|
|
1967
|
-
.fb-count{font-family:'Geist Mono',monospace;font-size:10.5px;color:var(--ink-faint)}
|
|
1968
|
-
.fb-now-action{font-size:16.5px;font-weight:600;line-height:1.5;color:var(--ink)}
|
|
1969
|
-
.fb-now-session{margin-top:10px;font-size:14.5px;line-height:1.6;color:var(--ink-soft)}
|
|
1970
|
-
.fb-why{margin:0;font-size:14.5px;line-height:1.6;color:var(--ink)}
|
|
1971
|
-
.fb-why-reality{margin-top:8px}
|
|
1972
|
-
.fb-accent-label{color:var(--accent);font-weight:600}
|
|
1973
|
-
.fb-stats{display:flex;gap:22px;flex-wrap:wrap;font-family:'Geist Mono',monospace;font-size:13.5px}
|
|
1974
|
-
.fb-stat{display:inline-flex;align-items:baseline;gap:6px;padding:2px 0}
|
|
1975
|
-
.fb-arrow{color:var(--ink-faint)}
|
|
1976
|
-
.fb-stat-to{color:var(--ink);font-weight:600}
|
|
1977
|
-
.fb-list{margin-top:2px}
|
|
1978
|
-
.fb-row{display:flex;align-items:baseline;gap:10px;padding:7px 8px;margin:0 -8px;border-radius:5px}
|
|
1979
|
-
.fb-list .fb-row:nth-child(even){background:var(--panel)}
|
|
1980
|
-
.fb-row:hover{background:var(--rowhover)!important}
|
|
1981
|
-
.fb-person-name{font-size:14px;font-weight:600;color:var(--ink);flex:0 0 150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1982
|
-
.fb-person-role{font-family:'Geist Mono',monospace;font-size:11.5px;color:var(--ink-faint);flex:0 0 130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1983
|
-
.fb-person-note{font-size:13px;color:var(--ink-soft);flex:1 1 auto}
|
|
1984
|
-
.fb-risk-sev{font-family:'Geist Mono',monospace;font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;flex:0 0 38px}
|
|
1985
|
-
.fb-risk-text{font-size:14px;line-height:1.4;color:var(--ink);flex:1 1 auto}
|
|
1986
|
-
.fb-log-date{font-family:'Geist Mono',monospace;font-size:11.5px;color:var(--ink-faint);flex:0 0 48px}
|
|
1987
|
-
.fb-log-kind{font-family:'Geist Mono',monospace;font-size:10px;letter-spacing:.03em;text-transform:uppercase;flex:0 0 62px}
|
|
1988
|
-
.fb-log-text{flex:1 1 auto;font-size:14px;line-height:1.5;color:var(--ink)}
|
|
1989
|
-
.fb-more{border-top:1px solid var(--line);padding:12px 0}
|
|
1990
|
-
.fb-more:first-child{border-top:none;padding-top:0}
|
|
1991
|
-
.fb-more-sum{cursor:pointer;list-style:none;display:flex;align-items:center;gap:6px}
|
|
1992
|
-
.fb-more-sum::-webkit-details-marker{display:none}
|
|
1993
|
-
.fb-more-sum::before{content:'\\2023';color:var(--ink-faint);transition:transform .12s}
|
|
1994
|
-
.fb-more[open] .fb-more-sum::before{transform:rotate(90deg)}
|
|
1995
|
-
.fb-more-body{margin-top:10px}
|
|
1996
|
-
.fb-plain-p{font-size:14px;line-height:1.55;color:var(--ink-soft);margin:0 0 10px}
|
|
1997
|
-
.fb-plain-list{margin:0 0 10px;padding-left:18px}
|
|
1998
|
-
.fb-plain-list li{font-size:14px;line-height:1.55;color:var(--ink-soft);margin:2px 0}
|
|
1999
|
-
.fb-table{border-collapse:collapse;width:100%;margin:0 0 12px;font-size:13px}
|
|
2000
|
-
.fb-table th,.fb-table td{padding:6px 9px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line)}
|
|
2001
|
-
.fb-table th{font-family:'Geist Mono',monospace;font-size:10px;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-faint)}
|
|
2002
|
-
.fb-table tr:last-child td{border-bottom:none}
|
|
2003
|
-
.fb-queue-row,.fb-flag-row{cursor:pointer}
|
|
2004
|
-
.fb-queue-row:nth-child(even),.fb-flag-row:nth-child(even){background:var(--panel)}
|
|
2005
|
-
.fb-queue-slug{font-family:'Geist Mono',monospace;font-size:12px;color:var(--ink-faint);flex:0 0 130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
2006
|
-
.fb-queue-action{font-size:14px;line-height:1.45;color:var(--ink);flex:1 1 auto}
|
|
2007
|
-
.fb-queue-touched{font-family:'Geist Mono',monospace;font-size:10.5px;color:var(--ink-faint);flex:0 0 auto}
|
|
2008
|
-
.fb-flag-slug{font-family:'Geist Mono',monospace;font-size:12px;color:var(--ink-faint);flex:0 0 130px}
|
|
2009
|
-
.fb-flag-text{font-family:'Geist Mono',monospace;font-size:12.5px}
|
|
2010
|
-
.dot{display:inline-block;flex:0 0 auto;border-radius:50%;box-sizing:border-box}
|
|
2011
|
-
.dot.green{background:var(--green)}
|
|
2012
|
-
.dot.amber{background:var(--amber);border-radius:2px}
|
|
2013
|
-
.dot.red{background:transparent;border:1.5px solid var(--red)}
|
|
2014
|
-
.dot-sm{width:7px;height:7px}
|
|
2015
|
-
.dot-md{width:8px;height:8px}
|
|
2016
|
-
.dot-lg{width:9px;height:9px}
|
|
2017
|
-
.t-green{color:var(--green)}.t-amber{color:var(--amber)}.t-red{color:var(--red)}.t-accent{color:var(--accent)}.t-faint{color:var(--ink-faint)}.t-soft{color:var(--ink-soft)}
|
|
2018
|
-
.fb-hints{margin-top:26px;display:flex;gap:14px;flex-wrap:wrap;font-family:'Geist Mono',monospace;font-size:10.5px;color:var(--ink-faint);border-top:1px solid var(--line);padding-top:12px}
|
|
2019
|
-
.fb-hints-spacer{margin-left:auto}
|
|
2020
|
-
.fb-palette-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:60;display:none;align-items:flex-start;justify-content:center;padding-top:12vh}
|
|
2021
|
-
.fb-palette-backdrop.open{display:flex}
|
|
2022
|
-
.fb-palette{width:480px;max-width:90vw;background:var(--bg);border:1px solid var(--line);border-radius:10px;overflow:hidden;box-shadow:var(--shadow)}
|
|
2023
|
-
.fb-palette-input{width:100%;padding:12px 14px;border:none;border-bottom:1px solid var(--line);background:transparent;color:var(--ink);font-family:'Geist Mono',monospace;font-size:13.5px;outline:none;box-sizing:border-box}
|
|
2024
|
-
.fb-palette-list{max-height:300px;overflow-y:auto}
|
|
2025
|
-
.fb-palette-item{display:flex;gap:10px;align-items:baseline;padding:9px 14px;cursor:pointer}
|
|
2026
|
-
.fb-palette-item.active{background:var(--panel)}
|
|
2027
|
-
.fb-palette-item.hide{display:none}
|
|
2028
|
-
.fb-palette-kind{font-family:'Geist Mono',monospace;font-size:10px;letter-spacing:.04em;color:var(--ink-faint);flex:0 0 52px;text-transform:uppercase}
|
|
2029
|
-
.fb-palette-label{font-family:'Geist Mono',monospace;font-size:13px;color:var(--ink)}
|
|
2030
|
-
.fb-palette-hint{margin-left:auto;font-family:'Geist Mono',monospace;font-size:11px;color:var(--ink-faint)}
|
|
2031
|
-
@media(max-width:760px){.fb-rail{flex:0 0 220px}.fb-main-inner{padding:20px}}
|
|
2032
|
-
@media(max-width:640px){.fb-body{flex-direction:column}.fb-rail{flex:0 0 auto;max-height:40vh;border-right:none;border-bottom:1px solid var(--line)}.fb-main-inner{padding:16px}}
|
|
2033
|
-
`
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
// Vanilla DOM only - no framework. Read-only: view selection (today vs a
|
|
2037
|
-
// client) is a plain show/hide over pre-rendered panels, not a re-render.
|
|
2038
|
-
// Search filters the rail (hide + match-snippet, same mechanic the old
|
|
2039
|
-
// dashboard used); keyboard nav (j/k/arrows, /, Escape, cmd/ctrl+K) and the
|
|
2040
|
-
// command palette are wired the same way the theme toggle already was here -
|
|
2041
|
-
// addEventListener, guarded null checks, no inline handlers.
|
|
2042
|
-
function dashScript() {
|
|
2043
|
-
return [
|
|
2044
|
-
"(function(){",
|
|
2045
|
-
"'use strict';",
|
|
2046
|
-
"var SEL_KEY='fde-fieldbook-sel';",
|
|
2047
|
-
"function esc(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}",
|
|
2048
|
-
"function all(sel){return Array.prototype.slice.call(document.querySelectorAll(sel));}",
|
|
2049
|
-
"",
|
|
2050
|
-
"var views=all('.fb-view');",
|
|
2051
|
-
"var navItems=all('.fb-nav');",
|
|
2052
|
-
"var searchInput=document.getElementById('fb-search');",
|
|
2053
|
-
"",
|
|
2054
|
-
"function selectView(id){",
|
|
2055
|
-
" if(!document.getElementById('view-'+id))return;",
|
|
2056
|
-
" views.forEach(function(v){v.hidden=(v.id!=='view-'+id);});",
|
|
2057
|
-
" navItems.forEach(function(n){n.classList.toggle('active',n.getAttribute('data-target')===id);});",
|
|
2058
|
-
" try{localStorage.setItem(SEL_KEY,id);}catch(e){}",
|
|
2059
|
-
"}",
|
|
2060
|
-
"navItems.forEach(function(n){n.addEventListener('click',function(){selectView(n.getAttribute('data-target'));});});",
|
|
2061
|
-
"all('[data-nav]').forEach(function(n){n.addEventListener('click',function(){selectView(n.getAttribute('data-nav'));});});",
|
|
2062
|
-
"",
|
|
2063
|
-
"var startSel='today';",
|
|
2064
|
-
"try{var s=localStorage.getItem(SEL_KEY);if(s&&document.getElementById('view-'+s))startSel=s;}catch(e){}",
|
|
2065
|
-
"selectView(startSel);",
|
|
2066
|
-
"",
|
|
2067
|
-
"// search: filters the rail, shows a match snippet in place of the default reason line",
|
|
2068
|
-
"// NOTE: snippetFor() below returns a string built ONLY from esc()-escaped",
|
|
2069
|
-
"// fragments plus hardcoded '<mark>' literals - never raw untrusted markup -",
|
|
2070
|
-
"// so assigning it via innerHTML cannot inject a tag from engagement content.",
|
|
2071
|
-
"function snippetFor(raw,term){",
|
|
2072
|
-
" var i=raw.indexOf(term);",
|
|
2073
|
-
" if(i<0)return '';",
|
|
2074
|
-
" var start=Math.max(0,i-24),end=Math.min(raw.length,i+term.length+40);",
|
|
2075
|
-
" var pre=esc(raw.slice(start,i)),hit=esc(raw.slice(i,i+term.length)),post=esc(raw.slice(i+term.length,end));",
|
|
2076
|
-
" return (start>0?'\\u2026':'')+pre+'<mark>'+hit+'</mark>'+post+(end<raw.length?'\\u2026':'');",
|
|
2077
|
-
"}",
|
|
2078
|
-
"if(searchInput){searchInput.addEventListener('input',function(){",
|
|
2079
|
-
" var term=searchInput.value.trim().toLowerCase();",
|
|
2080
|
-
" navItems.forEach(function(n){",
|
|
2081
|
-
" if(n.getAttribute('data-target')==='today')return;",
|
|
2082
|
-
" var raw=(n.getAttribute('data-search')||'').toLowerCase();",
|
|
2083
|
-
" var hit=!term||raw.indexOf(term)>-1;",
|
|
2084
|
-
" n.classList.toggle('hide',!hit);",
|
|
2085
|
-
" var reason=n.querySelector('.fb-nav-reason');",
|
|
2086
|
-
" if(!reason)return;",
|
|
2087
|
-
" if(term&&hit){var snip=snippetFor(raw,term);reason.innerHTML=snip;reason.classList.toggle('hide',!snip);}",
|
|
2088
|
-
" else{reason.innerHTML=n.getAttribute('data-default-reason')||'';reason.classList.toggle('hide',!reason.innerHTML);}",
|
|
2089
|
-
" });",
|
|
2090
|
-
"});}",
|
|
2091
|
-
"",
|
|
2092
|
-
"// keyboard: j/k/arrows move rail selection, / focuses search, Esc blurs/closes",
|
|
2093
|
-
"function visibleIds(){return navItems.filter(function(n){return !n.classList.contains('hide');}).map(function(n){return n.getAttribute('data-target');});}",
|
|
2094
|
-
"function currentId(){var v=document.querySelector('.fb-view:not([hidden])');return v?v.id.replace('view-',''):'today';}",
|
|
2095
|
-
"document.addEventListener('keydown',function(ev){",
|
|
2096
|
-
" if((ev.metaKey||ev.ctrlKey)&&(ev.key==='k'||ev.key==='K')){ev.preventDefault();openPalette();return;}",
|
|
2097
|
-
" if(paletteIsOpen()){if(ev.key==='Escape'){ev.preventDefault();closePalette();}return;}",
|
|
2098
|
-
" var t=ev.target,typing=t&&(t.tagName==='INPUT'||t.tagName==='TEXTAREA'||t.isContentEditable);",
|
|
2099
|
-
" if(typing){if(ev.key==='Escape')t.blur();return;}",
|
|
2100
|
-
" if(ev.key==='/'){ev.preventDefault();if(searchInput)searchInput.focus();return;}",
|
|
2101
|
-
" var down=ev.key==='ArrowDown'||ev.key==='j',up=ev.key==='ArrowUp'||ev.key==='k';",
|
|
2102
|
-
" if(down||up){",
|
|
2103
|
-
" ev.preventDefault();",
|
|
2104
|
-
" var ids=visibleIds();",
|
|
2105
|
-
" var idx=ids.indexOf(currentId());",
|
|
2106
|
-
" var next=ids[Math.min(ids.length-1,Math.max(0,idx+(down?1:-1)))];",
|
|
2107
|
-
" if(next)selectView(next);",
|
|
2108
|
-
" }",
|
|
2109
|
-
"});",
|
|
2110
|
-
"",
|
|
2111
|
-
"// command palette: search-filterable nav (today + each client + theme toggle)",
|
|
2112
|
-
"var paletteBackdrop=document.getElementById('fb-palette-backdrop');",
|
|
2113
|
-
"var paletteInput=document.getElementById('fb-palette-input');",
|
|
2114
|
-
"var paletteInner=document.querySelector('.fb-palette');",
|
|
2115
|
-
"var paletteItems=all('.fb-palette-item');",
|
|
2116
|
-
"var pActive=0;",
|
|
2117
|
-
"function paletteIsOpen(){return !!(paletteBackdrop&&paletteBackdrop.classList.contains('open'));}",
|
|
2118
|
-
"function visiblePaletteItems(){return paletteItems.filter(function(it){return !it.classList.contains('hide');});}",
|
|
2119
|
-
"function highlightPalette(){",
|
|
2120
|
-
" var visible=visiblePaletteItems();",
|
|
2121
|
-
" paletteItems.forEach(function(it){it.classList.remove('active');});",
|
|
2122
|
-
" if(visible[pActive]){visible[pActive].classList.add('active');visible[pActive].scrollIntoView({block:'nearest'});}",
|
|
2123
|
-
"}",
|
|
2124
|
-
"function filterPalette(q){",
|
|
2125
|
-
" q=q.toLowerCase();",
|
|
2126
|
-
" paletteItems.forEach(function(it){",
|
|
2127
|
-
" var label=(it.getAttribute('data-label')||'').toLowerCase();",
|
|
2128
|
-
" var kind=(it.getAttribute('data-kind')||'').toLowerCase();",
|
|
2129
|
-
" it.classList.toggle('hide',!(!q||label.indexOf(q)>-1||kind.indexOf(q)>-1));",
|
|
2130
|
-
" });",
|
|
2131
|
-
" pActive=0;highlightPalette();",
|
|
2132
|
-
"}",
|
|
2133
|
-
"function openPalette(){",
|
|
2134
|
-
" if(!paletteBackdrop)return;",
|
|
2135
|
-
" paletteBackdrop.classList.add('open');",
|
|
2136
|
-
" if(paletteInput){paletteInput.value='';paletteInput.focus();}",
|
|
2137
|
-
" filterPalette('');",
|
|
2138
|
-
"}",
|
|
2139
|
-
"function closePalette(){if(paletteBackdrop)paletteBackdrop.classList.remove('open');}",
|
|
2140
|
-
"function runActive(){",
|
|
2141
|
-
" var it=visiblePaletteItems()[pActive];",
|
|
2142
|
-
" if(!it)return;",
|
|
2143
|
-
" var target=it.getAttribute('data-target');",
|
|
2144
|
-
" if(target==='__theme__')toggleTheme();",
|
|
2145
|
-
" else if(target)selectView(target);",
|
|
2146
|
-
" closePalette();",
|
|
2147
|
-
"}",
|
|
2148
|
-
"if(paletteBackdrop)paletteBackdrop.addEventListener('click',function(ev){if(ev.target===paletteBackdrop)closePalette();});",
|
|
2149
|
-
"if(paletteInner)paletteInner.addEventListener('click',function(ev){ev.stopPropagation();});",
|
|
2150
|
-
"var paletteBtn=document.getElementById('fb-palette-btn');",
|
|
2151
|
-
"if(paletteBtn)paletteBtn.addEventListener('click',openPalette);",
|
|
2152
|
-
"paletteItems.forEach(function(it){",
|
|
2153
|
-
" it.addEventListener('click',function(){pActive=visiblePaletteItems().indexOf(it);runActive();});",
|
|
2154
|
-
" it.addEventListener('mouseenter',function(){var v=visiblePaletteItems();var i=v.indexOf(it);if(i>-1){pActive=i;highlightPalette();}});",
|
|
2155
|
-
"});",
|
|
2156
|
-
"if(paletteInput){",
|
|
2157
|
-
" paletteInput.addEventListener('input',function(){filterPalette(paletteInput.value);});",
|
|
2158
|
-
" paletteInput.addEventListener('keydown',function(ev){",
|
|
2159
|
-
" if(ev.key==='Escape'){closePalette();return;}",
|
|
2160
|
-
" if(ev.key==='ArrowDown'){ev.preventDefault();pActive=Math.min(visiblePaletteItems().length-1,pActive+1);highlightPalette();return;}",
|
|
2161
|
-
" if(ev.key==='ArrowUp'){ev.preventDefault();pActive=Math.max(0,pActive-1);highlightPalette();return;}",
|
|
2162
|
-
" if(ev.key==='Enter'){ev.preventDefault();runActive();}",
|
|
2163
|
-
" });",
|
|
2164
|
-
"}",
|
|
2165
|
-
"",
|
|
2166
|
-
"// theme toggle - local only, no network; pre-paint script in <head> avoids a flash",
|
|
2167
|
-
"var themeBtn=document.getElementById('fde-theme-btn');",
|
|
2168
|
-
"function currentTheme(){return document.documentElement.getAttribute('data-fde-theme')==='dark'?'dark':'light';}",
|
|
2169
|
-
"function setThemeLabel(){if(themeBtn)themeBtn.textContent=(currentTheme()==='dark'?'light':'dark');}",
|
|
2170
|
-
"function toggleTheme(){",
|
|
2171
|
-
" var next=currentTheme()==='dark'?'light':'dark';",
|
|
2172
|
-
" try{localStorage.setItem('fde-fieldbook-theme',next);}catch(e){}",
|
|
2173
|
-
" document.documentElement.setAttribute('data-fde-theme',next);",
|
|
2174
|
-
" setThemeLabel();",
|
|
2175
|
-
"}",
|
|
2176
|
-
"setThemeLabel();",
|
|
2177
|
-
"if(themeBtn)themeBtn.addEventListener('click',toggleTheme);",
|
|
2178
|
-
"})();",
|
|
2179
|
-
].join('\n')
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
// ---- HTML builders (one per fieldbook widget) - kept small and named so the
|
|
2183
|
-
// structured extractors above and the markup below stay easy to trace. ----
|
|
2184
|
-
|
|
2185
|
-
function railItemHtml(e) {
|
|
2186
|
-
const dotClass = dotClassFor(e.signals.trust)
|
|
2187
|
-
const touchedClass = e.quiet ? 't-amber' : 't-faint'
|
|
2188
|
-
const preview = e.next.length > 58 ? e.next.slice(0, 56) + '…' : e.next
|
|
2189
|
-
const defaultReason = e.signals.trust !== 'green' && preview ? escapeHtml(preview) : ''
|
|
2190
|
-
return `<button class="fb-nav" data-target="eng-${e.slug}" data-search="${e.searchBlob}" data-default-reason="${defaultReason}">
|
|
2191
|
-
<div class="fb-nav-row"><span class="dot dot-sm ${dotClass}"></span><span class="fb-nav-name">${inlineMd(e.name)}</span></div>
|
|
2192
|
-
<div class="fb-nav-meta">${escapeHtml(e.phaseLabel)} · <span class="${touchedClass}">${escapeHtml(e.signals.updated)}</span></div>
|
|
2193
|
-
<div class="fb-nav-reason${defaultReason ? '' : ' hide'}">${defaultReason}</div>
|
|
2194
|
-
</button>`
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
|
-
function queueRowHtml(e) {
|
|
2198
|
-
const dotClass = dotClassFor(e.signals.trust)
|
|
2199
|
-
const noAction = !e.hasNext
|
|
2200
|
-
const actionText = noAction ? 'no next action set - open and add one' : e.next
|
|
2201
|
-
const touchedClass = e.quiet ? 't-amber' : 't-faint'
|
|
2202
|
-
return `<div class="fb-row fb-queue-row" data-nav="eng-${e.slug}">
|
|
2203
|
-
<span class="dot dot-md ${dotClass}"></span>
|
|
2204
|
-
<span class="fb-queue-slug">${inlineMd(e.name)}</span>
|
|
2205
|
-
<span class="fb-queue-action${noAction ? ' t-amber' : ''}">${inlineMd(actionText)}</span>
|
|
2206
|
-
<span class="fb-queue-touched ${touchedClass}">${escapeHtml(e.signals.updated)}</span>
|
|
2207
|
-
</div>`
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
function flagRowHtml(e, text, colorClass) {
|
|
2211
|
-
return `<div class="fb-row fb-flag-row" data-nav="eng-${e.slug}">
|
|
2212
|
-
<span class="fb-flag-slug">${inlineMd(e.name)}</span>
|
|
2213
|
-
<span class="fb-flag-text t-${colorClass}">${escapeHtml(text)}</span>
|
|
2214
|
-
</div>`
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
function todayViewHtml({ today, total, attentionCount, highRiskTotal, todayQueue, flagsHtml, hasEngagements }) {
|
|
2218
|
-
if (!hasEngagements) {
|
|
2219
|
-
return `<div id="view-today" class="fb-view">
|
|
2220
|
-
<div class="fb-eyebrow">portfolio</div>
|
|
2221
|
-
<h1 class="fb-h1">Today</h1>
|
|
2222
|
-
<p class="empty">No engagements yet. Start one with <code>fde resume --init <client-name></code>, then re-run <code>fde dashboard</code>.</p>
|
|
2223
|
-
</div>`
|
|
2224
|
-
}
|
|
2225
|
-
return `<div id="view-today" class="fb-view">
|
|
2226
|
-
<div class="fb-eyebrow">portfolio</div>
|
|
2227
|
-
<h1 class="fb-h1">Today</h1>
|
|
2228
|
-
<div class="fb-meta-line">${escapeHtml(today)} · ${total} engagement${total === 1 ? '' : 's'} · ${attentionCount} need you · ${highRiskTotal} high risk${highRiskTotal === 1 ? '' : 's'} open</div>
|
|
2229
|
-
<div class="fb-block">
|
|
2230
|
-
<div class="fb-sec">Queue — next action per client, worst first</div>
|
|
2231
|
-
${todayQueue}
|
|
2232
|
-
</div>
|
|
2233
|
-
${flagsHtml ? `<div class="fb-block">
|
|
2234
|
-
<div class="fb-sec">Flags</div>
|
|
2235
|
-
${flagsHtml}
|
|
2236
|
-
</div>` : ''}
|
|
2237
|
-
</div>`
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
function clientViewHtml(e) {
|
|
2241
|
-
const dotClass = dotClassFor(e.signals.trust)
|
|
2242
|
-
const sectorLine = [
|
|
2243
|
-
e.overlay ? escapeHtml(e.overlay) : '',
|
|
2244
|
-
escapeHtml(e.phaseLabel),
|
|
2245
|
-
e.days != null ? `day ${e.days}` : '',
|
|
2246
|
-
`touched ${escapeHtml(e.signals.updated)}`,
|
|
2247
|
-
].filter(Boolean).join(' · ')
|
|
2248
|
-
|
|
2249
|
-
const nowBlock = `<div class="fb-block">
|
|
2250
|
-
<div class="fb-sec">Now</div>
|
|
2251
|
-
<div class="fb-now-action${e.hasNext ? '' : ' t-amber'}">${e.hasNext ? inlineMd(e.next) : 'next action not set'}</div>
|
|
2252
|
-
${e.lastSession ? `<div class="fb-now-session">${inlineMd(e.lastSession)}</div>` : ''}
|
|
2253
|
-
</div>`
|
|
2254
|
-
|
|
2255
|
-
// Brief vs reality is the whole point of "discover" - what they said they
|
|
2256
|
-
// needed vs what's actually true. Squeezed onto one truncated line, that
|
|
2257
|
-
// contrast disappears. Each gets its own line, its own room to finish a
|
|
2258
|
-
// thought, not a race to fit before an ellipsis.
|
|
2259
|
-
const whyBlock = (e.brief || e.reality) ? `<div class="fb-block">
|
|
2260
|
-
<div class="fb-sec">Why</div>
|
|
2261
|
-
${e.brief ? `<p class="fb-why"><span class="t-faint">What they asked for:</span> ${inlineMd(e.brief)}</p>` : ''}
|
|
2262
|
-
${e.reality ? `<p class="fb-why fb-why-reality"><span class="fb-accent-label">What's actually true:</span> ${inlineMd(e.reality)}</p>` : ''}
|
|
2263
|
-
</div>` : ''
|
|
2264
|
-
|
|
2265
|
-
// Vitals: a fixed field-facing gut-check panel, not a Movement block that
|
|
2266
|
-
// only exists when text mining finds numbers. Every row here is already
|
|
2267
|
-
// computed elsewhere in this file for other purposes (status/dashboard
|
|
2268
|
-
// triage, receipts, signals) - never a new fabricated field, just the
|
|
2269
|
-
// existing facts surfaced where an FDE's eye lands first. Sits beside
|
|
2270
|
-
// Now/Why so that column's natural reading width doesn't leave the top of
|
|
2271
|
-
// the page empty on a wide screen, and carries enough real rows to earn
|
|
2272
|
-
// roughly the same height instead of floating short beside a tall column.
|
|
2273
|
-
// Plain English, not fdeops shorthand - a junior FDE reading this for the
|
|
2274
|
-
// first time gets the same clear meaning as someone who wrote the CLI.
|
|
2275
|
-
// "signal" (the --signal flag's own word) becomes "trust"; a bare day
|
|
2276
|
-
// count becomes "started: 3 days ago" in the same relative-time phrasing
|
|
2277
|
-
// as everything else here, so there's one pattern to learn, not several.
|
|
2278
|
-
const dotGlyph = sig => sig === 'green' ? '●' : sig === 'red' ? '○' : '■' // filled circle / hollow ring / filled square - same shapes as the People list below
|
|
2279
|
-
const signalColor = sig => sig === 'green' ? '#12885c' : sig === 'red' ? '#b23e2b' : '#a56a19'
|
|
2280
|
-
const teamStatus = e.stakeholders.length
|
|
2281
|
-
? ['green', 'amber', 'red'].map(sig => e.stakeholders.filter(p => p.signal === sig).length)
|
|
2282
|
-
.map((n, i) => n ? `<span style="color:${signalColor(['green', 'amber', 'red'][i])}">${dotGlyph(['green', 'amber', 'red'][i])} ${n}</span>` : null)
|
|
2283
|
-
.filter(Boolean).join(' ')
|
|
2284
|
-
: ''
|
|
2285
|
-
const vitalsRows = [
|
|
2286
|
-
['trust', trustWord(e.signals.trust), dotClass],
|
|
2287
|
-
e.signals.signalAge != null ? ['trust confirmed', e.signals.signalAge === 0 ? 'today' : `${e.signals.signalAge} days ago${e.signals.stale ? ' - recheck' : ''}`, e.signals.stale ? 'red' : ''] : null,
|
|
2288
|
-
['phase', e.phaseLabel, ''],
|
|
2289
|
-
e.days != null ? ['started', e.days === 0 ? 'today' : `${e.days} days ago`, ''] : null,
|
|
2290
|
-
['last updated', e.signals.updated, e.quiet ? 'amber' : ''],
|
|
2291
|
-
e.log.length ? ['last note', formatLogDate(e.log[0].date), ''] : null,
|
|
2292
|
-
e.risks.length ? ['open risks', `${e.risks.length}${e.highRisks ? ` (${e.highRisks} urgent)` : ''}`, e.highRisks ? 'red' : ''] : null,
|
|
2293
|
-
e.stakeholders.length ? ['people', `${e.stakeholders.length}`, ''] : null,
|
|
2294
|
-
].filter(Boolean)
|
|
2295
|
-
const vitalsBlock = `<div class="fb-vitals">
|
|
2296
|
-
${vitalsRows.map(([label, val, tone]) => `<div class="fb-vital-row"><span class="fb-vital-label">${escapeHtml(label)}</span><span class="fb-vital-val${tone ? ' t-' + tone : ''}">${escapeHtml(val)}</span></div>`).join('\n')}
|
|
2297
|
-
${teamStatus ? `<div class="fb-vital-row"><span class="fb-vital-label">team status</span><span class="fb-vital-val">${teamStatus}</span></div>` : ''}
|
|
2298
|
-
${e.stats.length ? `<div class="fb-vital-div"></div>${e.stats.map(s => `<div class="fb-vital-row"><span class="fb-vital-label">${inlineMd(s.label)}</span><span class="fb-vital-val">${inlineMd(s.from)} <span class="fb-arrow">→</span> <span class="fb-stat-to">${inlineMd(s.to)}</span></span></div>`).join('\n')}` : ''}
|
|
2299
|
-
</div>`
|
|
2300
|
-
|
|
2301
|
-
const peopleBlock = e.stakeholders.length ? `<div class="fb-block">
|
|
2302
|
-
<div class="fb-sec">People</div>
|
|
2303
|
-
<div class="fb-list">
|
|
2304
|
-
${e.stakeholders.map(p => `<div class="fb-row fb-person">
|
|
2305
|
-
<span class="dot dot-sm ${p.signal}" title="${trustWord(p.signal)}"></span>
|
|
2306
|
-
<span class="fb-person-name">${inlineMd(p.name)}</span>
|
|
2307
|
-
<span class="fb-person-role">${inlineMd(p.role)}</span>
|
|
2308
|
-
<span class="fb-person-note">${inlineMd(p.note)}</span>
|
|
2309
|
-
</div>`).join('\n')}
|
|
2310
|
-
</div>
|
|
2311
|
-
</div>` : ''
|
|
2312
|
-
|
|
2313
|
-
const riskBlock = e.risks.length ? `<div class="fb-block">
|
|
2314
|
-
<div class="fb-sec-row"><div class="fb-sec">Risk</div><span class="fb-count">${e.risks.length} open</span></div>
|
|
2315
|
-
<div class="fb-list">
|
|
2316
|
-
${e.risks.map(r => `<div class="fb-row fb-risk">
|
|
2317
|
-
<span class="fb-risk-sev t-${r.severity === 'high' ? 'red' : 'amber'}">${escapeHtml(r.severity)}</span>
|
|
2318
|
-
<span class="fb-risk-text">${inlineMd(r.text)}</span>
|
|
2319
|
-
</div>`).join('\n')}
|
|
2320
|
-
</div>
|
|
2321
|
-
</div>` : ''
|
|
2322
|
-
|
|
2323
|
-
const logBlock = e.log.length ? `<div class="fb-block">
|
|
2324
|
-
<div class="fb-sec">Log</div>
|
|
2325
|
-
<div class="fb-list">
|
|
2326
|
-
${e.log.map(g => `<div class="fb-row fb-log">
|
|
2327
|
-
<span class="fb-log-date">${escapeHtml(formatLogDate(g.date))}</span>
|
|
2328
|
-
<span class="fb-log-kind t-${g.kind === 'receipt' ? 'green' : g.kind === 'decision' ? 'accent' : 'faint'}">${escapeHtml(g.kind)}</span>
|
|
2329
|
-
<span class="fb-log-text">${inlineMd(g.text)}</span>
|
|
2330
|
-
</div>`).join('\n')}
|
|
2331
|
-
</div>
|
|
2332
|
-
</div>` : ''
|
|
2333
|
-
|
|
2334
|
-
// reference docs (success/terrain/trust-profile) - collapsed by default,
|
|
2335
|
-
// never hidden entirely: trust-profile.md carries the data boundary
|
|
2336
|
-
const moreBlock = e.moreSections.length ? `<div class="fb-block">
|
|
2337
|
-
${e.moreSections.map(s => `<details class="fb-more"><summary class="fb-sec fb-more-sum">${escapeHtml(s.title)}</summary><div class="fb-more-body">${s.html}</div></details>`).join('\n')}
|
|
2338
|
-
</div>` : ''
|
|
2339
|
-
|
|
2340
|
-
// People + Risk pair naturally (who's involved / what's at stake) and read
|
|
2341
|
-
// well side by side on a wide screen - a single 760px column left half the
|
|
2342
|
-
// window empty. Stats stays full width (it's already a wrapping strip of
|
|
2343
|
-
// small tiles that benefits from horizontal room); Log stays full width
|
|
2344
|
-
// (a chronological list reads better long than squeezed into a column).
|
|
2345
|
-
const pairedBlock = (peopleBlock || riskBlock) ? `<div class="fb-grid-wrap">${peopleBlock}${riskBlock}</div>` : ''
|
|
2346
|
-
|
|
2347
|
-
return `<div id="view-eng-${e.slug}" class="fb-view" hidden>
|
|
2348
|
-
<div class="fb-eyebrow">.fde/${escapeHtml(e.slug)}</div>
|
|
2349
|
-
<div class="fb-title-row">
|
|
2350
|
-
<h1 class="fb-h1">${inlineMd(e.name)}</h1>
|
|
2351
|
-
<span class="fb-trust"><span class="dot dot-lg ${dotClass}"></span><span class="t-${dotClass}">${trustWord(e.signals.trust)}</span></span>
|
|
2352
|
-
</div>
|
|
2353
|
-
<div class="fb-meta-line">${sectorLine}</div>
|
|
2354
|
-
<div class="fb-top-grid">
|
|
2355
|
-
<div>${nowBlock}${whyBlock}</div>
|
|
2356
|
-
${vitalsBlock}
|
|
2357
|
-
</div>
|
|
2358
|
-
${pairedBlock}${logBlock}${moreBlock}
|
|
2359
|
-
</div>`
|
|
2360
|
-
}
|
|
2361
|
-
|
|
2362
|
-
function paletteItemsHtml(ordered) {
|
|
2363
|
-
const items = [
|
|
2364
|
-
{ kind: 'view', label: 'today', hint: 'portfolio queue', target: 'today' },
|
|
2365
|
-
...ordered.map(e => ({ kind: 'open', label: e.slug, hint: trustWord(e.signals.trust), target: 'eng-' + e.slug })),
|
|
2366
|
-
{ kind: 'action', label: 'toggle theme', hint: 'light / dark', target: '__theme__' },
|
|
2367
|
-
]
|
|
2368
|
-
return items.map(it => `<div class="fb-palette-item" data-target="${escapeHtml(it.target)}" data-label="${escapeHtml(it.label)}" data-kind="${escapeHtml(it.kind)}">
|
|
2369
|
-
<span class="fb-palette-kind">${escapeHtml(it.kind)}</span>
|
|
2370
|
-
<span class="fb-palette-label">${escapeHtml(it.label)}</span>
|
|
2371
|
-
<span class="fb-palette-hint">${escapeHtml(it.hint)}</span>
|
|
2372
|
-
</div>`).join('\n')
|
|
2373
|
-
}
|
|
2374
|
-
|
|
2375
1721
|
function cmdDashboard(args) {
|
|
2376
1722
|
const all = args.includes('--all')
|
|
2377
1723
|
const outIdx = args.indexOf('--out')
|
|
@@ -2391,7 +1737,7 @@ function cmdDashboard(args) {
|
|
|
2391
1737
|
}
|
|
2392
1738
|
const counts = { green: 0, amber: 0, RED: 0 }
|
|
2393
1739
|
engagements.forEach(e => { counts[e.signals.trust]++ })
|
|
2394
|
-
const today = formatToday(new Date())
|
|
1740
|
+
const today = render.formatToday(new Date())
|
|
2395
1741
|
|
|
2396
1742
|
// enrich each engagement with everything the read-only fieldbook renders -
|
|
2397
1743
|
// next action + last-session excerpt (reused from context.md), brief/reality
|
|
@@ -2423,9 +1769,9 @@ function cmdDashboard(args) {
|
|
|
2423
1769
|
['terrain.md', 'Terrain'],
|
|
2424
1770
|
['trust-profile.md', 'Trust profile'],
|
|
2425
1771
|
].map(([f, title]) => [title, readClean(e.dir, f)])
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
e.searchBlob = escapeHtml([
|
|
1772
|
+
.filter(([, md]) => render.hasRealContent(md))
|
|
1773
|
+
.map(([title, md]) => ({ title, html: render.mdBlockHtml(md, parseMdTable) }))
|
|
1774
|
+
e.searchBlob = render.escapeHtml([
|
|
2429
1775
|
e.name, e.next, e.lastSession, e.reality, e.brief,
|
|
2430
1776
|
...e.log.map(g => g.text), ...e.risks.map(r => r.text),
|
|
2431
1777
|
...e.stakeholders.map(p => `${p.name} ${p.role} ${p.note}`),
|
|
@@ -2433,75 +1779,7 @@ function cmdDashboard(args) {
|
|
|
2433
1779
|
].join(' ').toLowerCase())
|
|
2434
1780
|
})
|
|
2435
1781
|
|
|
2436
|
-
|
|
2437
|
-
const tierRank = { RED: 0, amber: 1, green: 2 }
|
|
2438
|
-
const ordered = engagements.slice().sort((a, b) => tierRank[a.signals.trust] - tierRank[b.signals.trust])
|
|
2439
|
-
const attentionCount = engagements.filter(e => e.signals.trust !== 'green').length
|
|
2440
|
-
const highRiskTotal = engagements.reduce((n, e) => n + e.highRisks, 0)
|
|
2441
|
-
|
|
2442
|
-
const railItems = ordered.map(railItemHtml).join('\n')
|
|
2443
|
-
const todayQueue = ordered.map(queueRowHtml).join('\n') || '<p class="empty">No engagements yet.</p>'
|
|
2444
|
-
const flagRows = []
|
|
2445
|
-
ordered.forEach(e => {
|
|
2446
|
-
if (e.quiet) flagRows.push(flagRowHtml(e, `quiet - last touched ${e.signals.updated}`, 'amber'))
|
|
2447
|
-
if (e.highRisks) flagRows.push(flagRowHtml(e, `${e.highRisks} high risk${e.highRisks > 1 ? 's' : ''} open`, 'red'))
|
|
2448
|
-
})
|
|
2449
|
-
|
|
2450
|
-
const todayView = todayViewHtml({
|
|
2451
|
-
today, total: engagements.length, attentionCount, highRiskTotal,
|
|
2452
|
-
todayQueue, flagsHtml: flagRows.join('\n'), hasEngagements: engagements.length > 0,
|
|
2453
|
-
})
|
|
2454
|
-
const clientViews = ordered.map(clientViewHtml).join('\n')
|
|
2455
|
-
const paletteItems = paletteItemsHtml(ordered)
|
|
2456
|
-
|
|
2457
|
-
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8">
|
|
2458
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
2459
|
-
<script>try{var t=localStorage.getItem("fde-fieldbook-theme");if(t==="dark"||(!t&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches))document.documentElement.setAttribute("data-fde-theme","dark");}catch(e){}</script>
|
|
2460
|
-
<title>FDE Fieldbook</title><style>${dashStyles()}</style></head><body>
|
|
2461
|
-
<div class="fb-app">
|
|
2462
|
-
<header class="fb-header">
|
|
2463
|
-
<div class="fb-brand">
|
|
2464
|
-
<span class="fb-wordmark">FDEOPS</span>
|
|
2465
|
-
<span class="fb-tagline">fieldbook</span>
|
|
2466
|
-
${engagements.length ? `<span class="fb-need">${attentionCount}/${engagements.length} need you</span>` : ''}
|
|
2467
|
-
</div>
|
|
2468
|
-
<div class="fb-spacer"></div>
|
|
2469
|
-
<span class="fb-today">${escapeHtml(today)}</span>
|
|
2470
|
-
<button id="fb-palette-btn" class="fb-btn" type="button" title="Command palette (Ctrl/Cmd+K)">⌘K</button>
|
|
2471
|
-
<button id="fde-theme-btn" class="fb-btn" type="button">dark</button>
|
|
2472
|
-
</header>
|
|
2473
|
-
<div class="fb-body">
|
|
2474
|
-
<aside class="fb-rail">
|
|
2475
|
-
<div class="fb-rail-search"><input id="fb-search" class="fb-search" placeholder="search clients, notes, risks…" spellcheck="false"></div>
|
|
2476
|
-
<div class="fb-rail-list fb-scroll">
|
|
2477
|
-
<button class="fb-nav" data-target="today">
|
|
2478
|
-
<div class="fb-nav-row"><span class="fb-nav-today">today</span>${engagements.length ? `<span class="fb-nav-count">${attentionCount} need you</span>` : ''}</div>
|
|
2479
|
-
</button>
|
|
2480
|
-
${railItems}
|
|
2481
|
-
</div>
|
|
2482
|
-
</aside>
|
|
2483
|
-
<main class="fb-main fb-scroll">
|
|
2484
|
-
<div class="fb-main-inner">
|
|
2485
|
-
${todayView}
|
|
2486
|
-
${clientViews}
|
|
2487
|
-
<div class="fb-hints">
|
|
2488
|
-
<span><span class="t-soft">↑↓ / j k</span> client</span>
|
|
2489
|
-
<span><span class="t-soft">/</span> search</span>
|
|
2490
|
-
<span><span class="t-soft">⌘K</span> palette</span>
|
|
2491
|
-
<span class="fb-hints-spacer">deterministic render of .fde/ - edit the markdown, re-run fde dashboard</span>
|
|
2492
|
-
</div>
|
|
2493
|
-
</div>
|
|
2494
|
-
</main>
|
|
2495
|
-
</div>
|
|
2496
|
-
<div id="fb-palette-backdrop" class="fb-palette-backdrop">
|
|
2497
|
-
<div class="fb-palette">
|
|
2498
|
-
<input id="fb-palette-input" class="fb-palette-input" placeholder="jump to client or action…" spellcheck="false">
|
|
2499
|
-
<div class="fb-palette-list fb-scroll">${paletteItems}</div>
|
|
2500
|
-
</div>
|
|
2501
|
-
</div>
|
|
2502
|
-
</div>
|
|
2503
|
-
<script>${dashScript()}</script>
|
|
2504
|
-
</body></html>`
|
|
1782
|
+
const html = render.buildFieldbookHtml({ engagements, today })
|
|
2505
1783
|
|
|
2506
1784
|
try {
|
|
2507
1785
|
fs.mkdirSync(path.dirname(outPath), { recursive: true })
|
|
@@ -2538,6 +1816,7 @@ function printUsage() {
|
|
|
2538
1816
|
fde debrief --smart propose routing from messy notes → review → fde debrief --apply
|
|
2539
1817
|
fde prep [label] grounded walk-in brief from existing .fde/ only
|
|
2540
1818
|
fde doctor lint engagement memory (stale signals, gaps)
|
|
1819
|
+
fde redact <term> preview/remove lines containing a buried term (pass --apply to commit)
|
|
2541
1820
|
fde garden [--apply] propose safe consolidations (contract: no new facts; git-reversible)
|
|
2542
1821
|
fde owner [set email] who keeps this engagement record
|
|
2543
1822
|
fde receipts <term> "what did we agree?" with dates
|
|
@@ -2558,6 +1837,7 @@ switch (cmd) {
|
|
|
2558
1837
|
case 'debrief': cmdDebrief(args); break
|
|
2559
1838
|
case 'prep': cmdPrep(args); break
|
|
2560
1839
|
case 'doctor': cmdDoctor(); break
|
|
1840
|
+
case 'redact': cmdRedact(args); break
|
|
2561
1841
|
case 'garden': cmdGarden(args); break
|
|
2562
1842
|
case 'owner': cmdOwner(args); break
|
|
2563
1843
|
case 'receipts': cmdReceipts(args); break
|