ticketlens 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -587,7 +587,7 @@ ticketlens CNV1-2 --summarize # AI summary via your own API key (BYOK
|
|
|
587
587
|
ticketlens CNV1-2 --summarize --cloud # AI summary via TicketLens API (no local key needed)
|
|
588
588
|
ticketlens CNV1-2 --handoff # AI handoff brief from the ticket's comment thread (BYOK)
|
|
589
589
|
ticketlens CNV1-2 --handoff --cloud # AI handoff brief via TicketLens API
|
|
590
|
-
ticketlens CNV1-2 --compliance # Check ticket requirements against local diff [Free 3/mo]
|
|
590
|
+
ticketlens CNV1-2 --compliance # Check ticket requirements against local diff [Pro/Free 3/mo]
|
|
591
591
|
ticketlens triage --stale=3 # Custom stale threshold (default is 5)
|
|
592
592
|
ticketlens triage --digest # POST scored triage results to digest endpoint
|
|
593
593
|
ticketlens schedule # Set up a scheduled daily digest
|
|
@@ -735,11 +735,13 @@ npm test
|
|
|
735
735
|
See [ROADMAP.md](ROADMAP.md) for the full plan.
|
|
736
736
|
|
|
737
737
|
Recently shipped:
|
|
738
|
-
- **
|
|
738
|
+
- **Collision detection** (`ticketlens collisions`) — shows which files your branch shares with teammates' in-flight branches; `--push` auto-sends git branch data to the team snapshot. Team tier
|
|
739
|
+
- **Shareable triage snapshot** (`ticketlens triage --share`) — generates a 24h signed URL; recipients open it in any browser, no account needed. Team tier
|
|
740
|
+
- **Compliance push** (`ticketlens triage --push`) — enriches the team snapshot with per-ticket compliance status and coverage from the local ledger. Pro tier
|
|
741
|
+
- **"TicketLens for PRs"** (`ticketlens review`) — assembles a code-review context brief from your current branch: linked tickets, changed files, and ticket context in one brief
|
|
739
742
|
- **Confluence pages** — linked Confluence pages fetched automatically and included in the brief; origin-validated, non-fatal, capped at 10 pages
|
|
740
743
|
- **Linear support** — `ticketlens init` → Linear; connects via GraphQL API key, fetches tickets, triage, and statuses
|
|
741
744
|
- **GitHub Issues support** — `ticketlens init` → GitHub Issues; PAT-based, same normalized ticket shape
|
|
742
|
-
- **Tracker-aware config** — `ticketlens config` shows the right labels and skips irrelevant prompts per tracker type; always re-validates the connection
|
|
743
745
|
|
|
744
746
|
---
|
|
745
747
|
|
package/bin/ticketlens.mjs
CHANGED
|
@@ -203,13 +203,17 @@ switch (command) {
|
|
|
203
203
|
process.stdin.setRawMode(true);
|
|
204
204
|
process.stdin.resume();
|
|
205
205
|
process.stdin.setEncoding('utf8');
|
|
206
|
-
process.stdin.
|
|
207
|
-
|
|
208
|
-
process.stdin.
|
|
206
|
+
const cleanup = () => { process.stdin.setRawMode(false); process.stdin.pause(); };
|
|
207
|
+
const onData = char => {
|
|
208
|
+
process.stdin.removeListener('end', onEnd);
|
|
209
|
+
cleanup();
|
|
209
210
|
process.stderr.write('\n');
|
|
210
211
|
if (char === '\x03') process.exit(0);
|
|
211
212
|
res(char === 'y' || char === 'Y');
|
|
212
|
-
}
|
|
213
|
+
};
|
|
214
|
+
const onEnd = () => { process.stdin.removeListener('data', onData); cleanup(); res(false); };
|
|
215
|
+
process.stdin.once('data', onData);
|
|
216
|
+
process.stdin.once('end', onEnd);
|
|
213
217
|
});
|
|
214
218
|
if (!answer) {
|
|
215
219
|
process.stderr.write(` ${s.dim('Cancelled.')}\n`);
|
package/package.json
CHANGED
|
@@ -82,11 +82,19 @@ export function clearBriefCache(ticketKey, profileName, configDir = DEFAULT_CONF
|
|
|
82
82
|
} catch { /* non-fatal */ }
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// Memoize getBriefCacheEntries per configDir within a single process invocation.
|
|
86
|
+
// 5s TTL is well beyond the lifetime of any CLI run; prevents repeated stat+read+parse
|
|
87
|
+
// when multiple callers (cache list, cache prune, cache status) hit the same directory.
|
|
88
|
+
const _entriesCache = new Map(); // configDir → { entries, time }
|
|
89
|
+
const _ENTRIES_TTL_MS = 5_000;
|
|
90
|
+
|
|
85
91
|
/**
|
|
86
92
|
* Returns all brief cache entries across all profiles.
|
|
87
93
|
* Each entry: { profileName, ticketKey, filePath, size, mtimeMs, fetchedAt, depth }
|
|
88
94
|
*/
|
|
89
95
|
export function getBriefCacheEntries(configDir = DEFAULT_CONFIG_DIR) {
|
|
96
|
+
const cached = _entriesCache.get(configDir);
|
|
97
|
+
if (cached && Date.now() - cached.time < _ENTRIES_TTL_MS) return cached.entries;
|
|
90
98
|
const cacheDir = path.join(configDir, 'cache');
|
|
91
99
|
if (!fs.existsSync(cacheDir)) return [];
|
|
92
100
|
|
|
@@ -120,6 +128,7 @@ export function getBriefCacheEntries(configDir = DEFAULT_CONFIG_DIR) {
|
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
|
|
131
|
+
_entriesCache.set(configDir, { entries, time: Date.now() });
|
|
123
132
|
return entries;
|
|
124
133
|
}
|
|
125
134
|
|
|
@@ -8,8 +8,9 @@ import { join } from 'node:path';
|
|
|
8
8
|
import { createHmac, randomBytes } from 'node:crypto';
|
|
9
9
|
import { DEFAULT_CONFIG_DIR } from './config.mjs';
|
|
10
10
|
|
|
11
|
-
const LEDGER_FILE
|
|
12
|
-
const KEY_FILE
|
|
11
|
+
const LEDGER_FILE = 'ledger.jsonl';
|
|
12
|
+
const KEY_FILE = 'ledger-key';
|
|
13
|
+
const MAX_LEDGER_RECORDS = 5_000;
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Append one compliance record to ledger.jsonl.
|
|
@@ -25,6 +26,7 @@ export function appendLedger(record, { configDir = DEFAULT_CONFIG_DIR, fsModule
|
|
|
25
26
|
|
|
26
27
|
const line = JSON.stringify({ ts: new Date().toISOString(), ...record }) + '\n';
|
|
27
28
|
fsModule.appendFileSync(join(configDir, LEDGER_FILE), line, 'utf8');
|
|
29
|
+
_rotateIfNeeded(configDir, fsModule);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
|
@@ -84,6 +86,23 @@ export function exportLedger(format, { configDir = DEFAULT_CONFIG_DIR, fsModule
|
|
|
84
86
|
|
|
85
87
|
// ── Internal helpers ─────────────────────────────────────────────────────────
|
|
86
88
|
|
|
89
|
+
function _rotateIfNeeded(configDir, fsModule) {
|
|
90
|
+
const ledgerPath = join(configDir, LEDGER_FILE);
|
|
91
|
+
// Cheap size check first — each JSONL record averages ~180 bytes, so 5000 records ≈ 900 KB.
|
|
92
|
+
// Skip the full read when the file is well under the threshold.
|
|
93
|
+
try {
|
|
94
|
+
if (fsModule.statSync(ledgerPath).size < 900_000) return;
|
|
95
|
+
} catch { return; }
|
|
96
|
+
|
|
97
|
+
let raw;
|
|
98
|
+
try { raw = fsModule.readFileSync(ledgerPath, 'utf8'); } catch { return; }
|
|
99
|
+
|
|
100
|
+
const lines = raw.split('\n').filter(l => l.trim().length > 0);
|
|
101
|
+
if (lines.length > MAX_LEDGER_RECORDS) {
|
|
102
|
+
fsModule.writeFileSync(ledgerPath, lines.slice(-MAX_LEDGER_RECORDS).join('\n') + '\n', 'utf8');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
87
106
|
function _getOrCreateKey(configDir, fsModule) {
|
|
88
107
|
const keyPath = join(configDir, KEY_FILE);
|
|
89
108
|
try {
|