[object Object]

← back to Answer Cockpit

chore: lint, refactor, v0.2.0 (session close)

813fd6af7e4d52e5f66d81f0daa3cadd440f8430 · 2026-09-16 08:38:11 -0700 · Steve Abrams

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Files touched

Diff

commit 813fd6af7e4d52e5f66d81f0daa3cadd440f8430
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 16 08:38:11 2026 -0700

    chore: lint, refactor, v0.2.0 (session close)
    
    Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---
 lib/queue.js      | 8 +++++---
 lib/transcript.js | 8 +++++---
 lib/writeback.js  | 4 +++-
 package.json      | 6 ++++--
 server.js         | 9 +++++----
 5 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/lib/queue.js b/lib/queue.js
index ada641e..c370f20 100644
--- a/lib/queue.js
+++ b/lib/queue.js
@@ -9,11 +9,13 @@
 //                 sort variant=="stopped" first then PRIORITY, attach rgb/emoji from
 //                 COLORS (terminal_status.py:48-61), merge memos by TK, resolve detail.
 const path = require('path');
+const crypto = require('crypto');
 const { execFile } = require('child_process');
 const transcript = require('./transcript');
 const memo = require('./memo');
 const audit = require('./audit');
 const writeback = require('./writeback');
+const ticketlane = require('./ticketlane');
 
 const HOME = process.env.HOME || require('os').homedir();
 const TS_PY = path.join(HOME, 'Projects/terminal-status/terminal_status.py');
@@ -126,7 +128,7 @@ function digestOf(detail) {
   // correct click in the same second. Labels are identical across both sources.
   const basis = q.options.map((o) => String(o.label || '').replace(/\s+/g, ' ').trim().toLowerCase()).join('|');
   if (!basis) return '-';
-  return require('crypto').createHash('sha1').update(basis).digest('hex').slice(0, 8);
+  return crypto.createHash('sha1').update(basis).digest('hex').slice(0, 8);
 }
 function keyOf(row, detail) { return `${row.tty}|${row.ticket || '-'}|${row.color}|${digestOf(detail)}`; }
 function keyFor(row) { const s = transcript.resolve(row); return keyOf(row, s.detail); }
@@ -216,8 +218,8 @@ async function build(opts = {}) {
   // open — invisible to a tty scan by construction. Display/route only: nothing to type into.
   let ticketBacklog = [], ticketBacklogCount = 0;
   try {
-    const all = await require('./ticketlane').loadTickets();
-    const lane = all.filter((c) => !linked.has(c.ticket)).map((c) => ({ ...c, urgency: require('./ticketlane').urgency(c), body: String(c.body || '').slice(0, 800) }))
+    const all = await ticketlane.loadTickets();
+    const lane = all.filter((c) => !linked.has(c.ticket)).map((c) => ({ ...c, urgency: ticketlane.urgency(c), body: String(c.body || '').slice(0, 800) }))
       .sort((a, b) => b.urgency - a.urgency || new Date(a.created) - new Date(b.created));
     ticketBacklogCount = lane.length;
     ticketBacklog = opts.orphans ? lane.slice(0, 80) : [];
diff --git a/lib/transcript.js b/lib/transcript.js
index ed5c072..c16a87e 100644
--- a/lib/transcript.js
+++ b/lib/transcript.js
@@ -13,6 +13,7 @@
 //   Cache 30s per pid; re-resolve when updated_at changes.
 const fs = require('fs');
 const path = require('path');
+const crypto = require('crypto');
 const { execFileSync } = require('child_process');
 
 const HOME = process.env.HOME || require('os').homedir();
@@ -21,6 +22,7 @@ const SESSIONS = path.join(HOME, '.claude/sessions');
 const MAP_DIR = path.join(HOME, '.claude/answer-cockpit/map');
 const TAIL_BYTES = 256 * 1024;
 const CACHE_MS = 30 * 1000;
+const LAST_TEXT_MAX = 2000; // lastText char cap (parseTail + parsePane)
 const cache = new Map(); // pid → {key, ts, result}
 
 // ---- tail parser (copied from claude-control-center/server.js:337-356 tailJsonl) ----
@@ -101,7 +103,7 @@ function parseTail(filepath) {
   }
   return {
     question, answeredQuestion,
-    lastText: lastText ? lastText.slice(-2000) : null,
+    lastText: lastText ? lastText.slice(-LAST_TEXT_MAX) : null,
     pasteCmd, lastTs, sessionId, cwd, turns: turns.length,
   };
 }
@@ -243,7 +245,7 @@ function allPaneContents() {
   if (Date.now() - paneBatch.ts < PANE_BATCH_MS) return paneBatch.map;
   // Per-call NONCE delimiters: a pane that happens to print the literal delimiter (e.g. a session
   // reviewing this file) can no longer truncate its own capture (Cody FIX-FIRST #4).
-  const nonce = require('crypto').randomBytes(6).toString('hex');
+  const nonce = crypto.randomBytes(6).toString('hex');
   const BEGIN = '@@TTY-' + nonce + ' ', END = '@@END-' + nonce;
   const script = `tell application "iTerm2"
 set out to ""
@@ -369,7 +371,7 @@ function parsePane(text) {
   const tail = content.slice(-45);
   let pasteCmd = null;
   for (let i = tail.length - 1; i >= 0 && !pasteCmd; i--) { const m = tail[i].match(/^\s*!\s+(.+)$/); if (m) pasteCmd = '! ' + m[1].trim(); }
-  const lastText = (degenerate ? nonEmpty.map((l) => l.trim()).join('') : tail.join('\n')).slice(-2000);
+  const lastText = (degenerate ? nonEmpty.map((l) => l.trim()).join('') : tail.join('\n')).slice(-LAST_TEXT_MAX);
   // answeredQuestion is UNKNOWN in pane mode (no tool_use ids) — null, never a false "not stale".
   return { question, answeredQuestion: null, lastText: lastText || null, pasteCmd, lastTs: null, sessionId: null, cwd: null, turns: 0, source: 'pane', queued, degenerate, menuOnScreen: footer >= 0 };
 }
diff --git a/lib/writeback.js b/lib/writeback.js
index 35b4a13..2f2303a 100644
--- a/lib/writeback.js
+++ b/lib/writeback.js
@@ -17,6 +17,8 @@
 //   - every accessor inside `try`; returns {typed:true} only when a tty matched.
 //   - refuse the cockpit's own tty (climb ppid like colordots.sh:61-72).
 const fs = require('fs');
+const path = require('path');
+const os = require('os');
 const { execFile, execFileSync } = require('child_process');
 
 const LOCKDIR = '/tmp/answer-cockpit.lock';
@@ -183,7 +185,7 @@ async function focus(tty, opts = {}) {
 function repaint(tty, label) {
   return new Promise((resolve) => {
     if (!TTY_RE.test(String(tty))) return resolve({ ok: false, err: 'bad tty' });
-    const py = require('path').join(process.env.HOME || require('os').homedir(), 'Projects/terminal-status/terminal_status.py');
+    const py = path.join(process.env.HOME || os.homedir(), 'Projects/terminal-status/terminal_status.py');
     execFile('python3', [py, 'set', 'green', String(label || '').slice(0, 120), '--tty', tty, '--quiet'], { timeout: 30000 }, (err, stdout, stderr) => {
       if (err) return resolve({ ok: false, err: (stderr || err.message).trim().slice(0, 300) });
       resolve({ ok: true });
diff --git a/package.json b/package.json
index c331282..98bd593 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "answer-cockpit",
-  "version": "0.1.0",
+  "version": "0.2.0",
   "private": true,
   "description": "Answer Cockpit — one-card needs-Steve stream (:9805). Zero-dependency Node http.",
   "main": "server.js",
@@ -8,5 +8,7 @@
     "start": "node server.js",
     "smoke": "node scripts/smoke.js"
   },
-  "engines": { "node": ">=18" }
+  "engines": {
+    "node": ">=18"
+  }
 }
diff --git a/server.js b/server.js
index 19054b2..8e8ed96 100644
--- a/server.js
+++ b/server.js
@@ -28,6 +28,8 @@ const queue = require('./lib/queue');
 const memo = require('./lib/memo');
 const writeback = require('./lib/writeback');
 const audit = require('./lib/audit');
+const transcript = require('./lib/transcript');
+const menu = require('./lib/menu');
 
 const HOST = '127.0.0.1'; // hard-coded on purpose — never expose (it types into live agents)
 const TEST = process.argv.includes('--test');
@@ -90,9 +92,8 @@ async function guardTarget(body, { needsSteveOnly = true, requireKey = true } =
   // menu is live on screen (a pane-detected question is answerable even if the dot is none/green).
   // GENUINELY fresh: drop the 4s pane batch + this pid's 30s resolve cache before resolving, so a
   // menu that closed/advanced since the last poll cannot digest-match a stale card (Cody #3).
-  const tr = require('./lib/transcript');
-  tr.invalidatePanes(); tr._cache.delete(String(row.pid));
-  const sess = tr.resolve(row);
+  transcript.invalidatePanes(); transcript._cache.delete(String(row.pid));
+  const sess = transcript.resolve(row);
   const liveMenu = !!(sess.detail && sess.detail.question && !sess.detail.queued);
   if (requireKey) {
     const key = queue.keyOf(row, sess.detail); // includes the content digest — a changed question 409s
@@ -171,7 +172,7 @@ const server = http.createServer(async (req, res) => {
       // against a live menu (a stale client or a curl script must not be able to mis-answer).
       let typed = text, optionLabel = typeof body.optionLabel === 'string' ? body.optionLabel.slice(0, 200) : null, translated = false;
       if (g.liveMenu && !(body.force === true && body.rawText === true)) {
-        const m = require('./lib/menu').resolveMenuAnswer(text, g.sess.detail.question);
+        const m = menu.resolveMenuAnswer(text, g.sess.detail.question);
         if (m.error) { audit.audit({ action: 'answer', tty: g.row.tty, ticket: g.row.ticket, color: g.row.color, text, ok: false, err: m.error, refused: true }); return json(res, 400, { error: m.error, liveMenu: true }); }
         typed = m.text; translated = m.translated; if (m.optionLabel) optionLabel = m.optionLabel;
       }

← c938da9 TK-11793: pane batch TTL 12s + warmed once per build (/api/q  ·  back to Answer Cockpit  ·  TK-11793: scale fix at 51 sessions — background async pane w 0ace03e →