tldr-experts 0.3.0
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/CHANGELOG.md +913 -0
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/hooks/answer-capture.js +174 -0
- package/dist/hooks/budget-gate.js +173 -0
- package/dist/hooks/chunk-0bt6yb2q.js +88 -0
- package/dist/hooks/chunk-1zwcxd3f.js +66 -0
- package/dist/hooks/chunk-9gb21660.js +66 -0
- package/dist/hooks/chunk-a8p2rc94.js +20 -0
- package/dist/hooks/chunk-ae6bkfs5.js +0 -0
- package/dist/hooks/chunk-azctppjh.js +198 -0
- package/dist/hooks/chunk-g395gk7e.js +503 -0
- package/dist/hooks/chunk-j234zf0t.js +339 -0
- package/dist/hooks/chunk-kw4tffzf.js +139 -0
- package/dist/hooks/chunk-p274ckxv.js +7435 -0
- package/dist/hooks/chunk-sdjnnmzz.js +497 -0
- package/dist/hooks/chunk-t56k6146.js +14 -0
- package/dist/hooks/chunk-t8tdv11p.js +35 -0
- package/dist/hooks/chunk-x98qs959.js +302 -0
- package/dist/hooks/chunk-y0jdr3et.js +627 -0
- package/dist/hooks/claim-sources.js +72 -0
- package/dist/hooks/dod-gate.js +198 -0
- package/dist/hooks/no-reask.js +67 -0
- package/dist/hooks/session-start.js +1348 -0
- package/dist/hooks/statusline.js +116 -0
- package/dist/tldrx.js +34219 -0
- package/env.yml +79 -0
- package/package.json +60 -0
- package/plugin/.claude-plugin/plugin.json +9 -0
- package/plugin/README.md +113 -0
- package/plugin/agents/README.md +22 -0
- package/plugin/hooks/hooks.json +107 -0
- package/plugin/skills/tldrx/SKILL.md +170 -0
- package/stages/build/stage.md +70 -0
- package/stages/build/stage.yml +54 -0
- package/stages/how/stage.md +89 -0
- package/stages/how/stage.yml +53 -0
- package/stages/plan/stage.md +89 -0
- package/stages/plan/stage.yml +48 -0
- package/stages/watch/stage.md +85 -0
- package/stages/watch/stage.yml +61 -0
- package/stages/what/stage.md +89 -0
- package/stages/what/stage.yml +61 -0
- package/templates/budget.yml +17 -0
- package/templates/competencies.yml +14 -0
- package/templates/env.yml +19 -0
- package/templates/epic.md +38 -0
- package/templates/expert.md +51 -0
- package/templates/experts/architect.md +77 -0
- package/templates/experts/delivery.md +76 -0
- package/templates/experts/developer.md +78 -0
- package/templates/experts/operations.md +74 -0
- package/templates/experts/product.md +78 -0
- package/templates/facts.yml +18 -0
- package/templates/handoff.md +63 -0
- package/templates/process.yml +33 -0
- package/templates/questions.md +63 -0
- package/templates/run.yml +24 -0
- package/templates/story.md +55 -0
- package/templates/watcher.md +68 -0
- package/templates/waves.yml +17 -0
- package/templates/workspace.yml +30 -0
- package/workflows/bugfix.yml +26 -0
- package/workflows/docs.yml +23 -0
- package/workflows/feature.yml +22 -0
- package/workflows/hotfix.yml +24 -0
- package/workflows/integration.yml +24 -0
- package/workflows/migration.yml +24 -0
- package/workflows/performance.yml +23 -0
- package/workflows/prototype.yml +24 -0
- package/workflows/refactor.yml +24 -0
- package/workflows/retro.yml +20 -0
- package/workflows/security-patch.yml +23 -0
- package/workflows/spike.yml +23 -0
- package/workflows/upgrade.yml +23 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseHookInput,
|
|
3
|
+
readStdin
|
|
4
|
+
} from "./chunk-a8p2rc94.js";
|
|
5
|
+
|
|
6
|
+
// src/hooks/lib/decide.ts
|
|
7
|
+
function deny(reason) {
|
|
8
|
+
process.stdout.write(`${JSON.stringify({
|
|
9
|
+
hookSpecificOutput: {
|
|
10
|
+
hookEventName: "PreToolUse",
|
|
11
|
+
permissionDecision: "deny",
|
|
12
|
+
permissionDecisionReason: reason
|
|
13
|
+
}
|
|
14
|
+
})}
|
|
15
|
+
`);
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
function allow() {
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
function postContext(text) {
|
|
22
|
+
process.stdout.write(`${JSON.stringify({
|
|
23
|
+
hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: text }
|
|
24
|
+
})}
|
|
25
|
+
`);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
function sessionContext(text) {
|
|
29
|
+
process.stdout.write(`${JSON.stringify({
|
|
30
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text }
|
|
31
|
+
})}
|
|
32
|
+
`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
function failOpen(name, error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
37
|
+
process.stderr.write(`tldrx hook ${name}: internal error, allowing — ${message.split(`
|
|
38
|
+
`)[0] ?? ""}
|
|
39
|
+
`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
async function runHook(name, body) {
|
|
43
|
+
try {
|
|
44
|
+
await body();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
failOpen(name, error);
|
|
47
|
+
}
|
|
48
|
+
return allow();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/hooks/lib/payload.ts
|
|
52
|
+
async function readPayload() {
|
|
53
|
+
return parseHookInput(await readStdin()) ?? {};
|
|
54
|
+
}
|
|
55
|
+
function toolInput(payload) {
|
|
56
|
+
const input = payload.tool_input;
|
|
57
|
+
return input !== undefined && typeof input === "object" ? input : {};
|
|
58
|
+
}
|
|
59
|
+
function filePathOf(payload) {
|
|
60
|
+
return toolInput(payload).file_path ?? payload.file_path ?? payload.path ?? "";
|
|
61
|
+
}
|
|
62
|
+
function isWriteOrEdit(payload) {
|
|
63
|
+
return payload.tool_name === "Write" || payload.tool_name === "Edit";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { deny, allow, postContext, sessionContext, failOpen, runHook, readPayload, toolInput, filePathOf, isWriteOrEdit };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/hooks/lib/messages.ts
|
|
2
|
+
var GRAMMAR = "[src: <repo:path:line> | https://… | Q<n> | F<n> | $ <cmd> → exit <n> | graph:<node> | absent:<path>]";
|
|
3
|
+
function usd(amount) {
|
|
4
|
+
return `$${amount.toFixed(2)}`;
|
|
5
|
+
}
|
|
6
|
+
function claimSourcesDeny(relPath, lines) {
|
|
7
|
+
const list = lines.map((n) => `L${n}`).join(", ");
|
|
8
|
+
return `[tldrx] claim-sources: ${lines.length} unsourced bullet(s) in ${relPath} — ${list}.
|
|
9
|
+
` + `Every bullet under Findings/Decisions/Unknowns/Evidence ledger must end with ${GRAMMAR}. ` + "Add the source or delete the claim.";
|
|
10
|
+
}
|
|
11
|
+
function claimSourcesEmptySectionDeny(relPath, sections) {
|
|
12
|
+
const list = sections.map((s) => `"${s.name}" (L${s.line})`).join(", ");
|
|
13
|
+
return `[tldrx] claim-sources: ${sections.length} checked section(s) in ${relPath} contain no list ` + `items — ${list}.
|
|
14
|
+
` + "Findings/Decisions/Unknowns/Evidence ledger must each hold at least one sourced item; prose alone " + "is not a claim anything can check. If there is genuinely nothing, say so as an item: " + "`- none [src: absent:<what you looked at>]`.";
|
|
15
|
+
}
|
|
16
|
+
function claimSourcesMalformedDeny(relPath, issues) {
|
|
17
|
+
const list = issues.map((i) => `L${i.line}`).join(", ");
|
|
18
|
+
return `[tldrx] claim-sources: ${issues.length} malformed citation(s) in ${relPath} — ${list}.
|
|
19
|
+
` + "The bullet cites a source but the token could not be read. The `[src: …]` token must be the LAST " + "thing on the line: remove the backticks around it and any words after it. A closing quote, bracket " + "or a final `.` after the `]` is fine.";
|
|
20
|
+
}
|
|
21
|
+
function claimSourcesUnresolvedDeny(relPath, issues) {
|
|
22
|
+
const detail = issues.map((i) => `L${i.line}: ${i.message}`).join(`
|
|
23
|
+
`);
|
|
24
|
+
return `[tldrx] claim-sources: ${issues.length} unresolvable source(s) in ${relPath}.
|
|
25
|
+
` + `${detail}
|
|
26
|
+
` + "A cited file must exist with the line in range, a command must be one of workspace.yml's, and " + "`$ … → exit n` belongs only in the Evidence ledger. Fix the citation or delete the claim.";
|
|
27
|
+
}
|
|
28
|
+
function noReAskDeny(questionId, questionTitle, fact) {
|
|
29
|
+
const who = fact.source.who;
|
|
30
|
+
const when = fact.source.when.slice(0, 10);
|
|
31
|
+
const run = fact.source.run ?? "init";
|
|
32
|
+
return `[tldrx] no-re-ask: ${questionId} "${questionTitle}" is already answered by ${fact.id} ` + `(${who}, ${when}, run ${run}): "${ellipsise(fact.fact)}". Cite it as [src: ${fact.id}] instead of asking.
|
|
33
|
+
` + "If the fact is stale, set its `retired: {at, by, reason}` in .tldrx/memory/facts.yml first, " + "then re-write the question.";
|
|
34
|
+
}
|
|
35
|
+
function dodGateDeny(storyId, command, repo, exitCode, outputTail) {
|
|
36
|
+
const tail = outputTail.trim() === "" ? "(no output)" : outputTail.trim();
|
|
37
|
+
return `[tldrx] DoD-gate: story ${storyId} cannot be marked done — \`${command}\` in repo ${repo} ` + `exited ${exitCode} (expected 0).
|
|
38
|
+
` + `Command output tail: ${tail}
|
|
39
|
+
` + "Fix the code or the story's dod block; done means proven, not asserted.";
|
|
40
|
+
}
|
|
41
|
+
function dodGateMissingBlockDeny(storyId, relPath) {
|
|
42
|
+
return `[tldrx] DoD-gate: story ${storyId} cannot be marked done — no fenced \`\`\`dod block in ${relPath}.
|
|
43
|
+
` + "Add the commands that prove it (test, lint, typecheck from map/commands.md) and let the gate re-run them; " + "done means proven, not asserted.";
|
|
44
|
+
}
|
|
45
|
+
function dodGateRefusedCommandDeny(storyId, command, why) {
|
|
46
|
+
return `[tldrx] DoD-gate: story ${storyId} cannot be marked done — refusing to RUN \`${command}\`.
|
|
47
|
+
` + `${why}
|
|
48
|
+
` + "This gate executes its commands for real, as you. It runs only what the workspace declared, " + "argv-split with no shell — a story is data, and data does not get to invent a command.";
|
|
49
|
+
}
|
|
50
|
+
function dodGateInternalErrorDeny(storyId, message) {
|
|
51
|
+
return `[tldrx] DoD-gate: story ${storyId} cannot be marked done — the gate itself failed: ${message}
|
|
52
|
+
` + "This gate fails closed: an unproven story stays not-done. Fix the gate's inputs and try again.";
|
|
53
|
+
}
|
|
54
|
+
function budgetGateDeny(stage, phase, left, ceiling, estimate, fixCommand) {
|
|
55
|
+
return `[tldrx] budget-gate: refusing to start stage "${stage}" — phase ${phase} has ${usd(left)} left of ` + `${usd(ceiling)} and the stage estimate is ${usd(estimate)}. Run \`${fixCommand}\` (add \`--take-from ` + `<phase>\` to move the money instead of adding it), lower budget_usd in .tldrx/stages/${stage}/stage.yml, ` + "or set on_exceed: warn.";
|
|
56
|
+
}
|
|
57
|
+
function ellipsise(text, max = 40) {
|
|
58
|
+
if (text.length <= max)
|
|
59
|
+
return text;
|
|
60
|
+
const head = text.slice(0, max);
|
|
61
|
+
const lastSpace = head.lastIndexOf(" ");
|
|
62
|
+
const cut = lastSpace > 0 ? head.slice(0, lastSpace) : head;
|
|
63
|
+
return `${cut.replace(/[\s.,;:—-]+$/, "")}…`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { claimSourcesDeny, claimSourcesEmptySectionDeny, claimSourcesMalformedDeny, claimSourcesUnresolvedDeny, noReAskDeny, dodGateDeny, dodGateMissingBlockDeny, dodGateRefusedCommandDeny, dodGateInternalErrorDeny, budgetGateDeny };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runtime
|
|
3
|
+
} from "./chunk-p274ckxv.js";
|
|
4
|
+
|
|
5
|
+
// src/core/hooks/passthrough.ts
|
|
6
|
+
async function readStdin() {
|
|
7
|
+
return await runtime.readStdin();
|
|
8
|
+
}
|
|
9
|
+
function parseHookInput(text) {
|
|
10
|
+
if (text.trim() === "")
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
const value = JSON.parse(text);
|
|
14
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { readStdin, parseHookInput };
|
|
File without changes
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asDocument,
|
|
3
|
+
requireEnum,
|
|
4
|
+
requireKeys,
|
|
5
|
+
requireNumber,
|
|
6
|
+
requireString,
|
|
7
|
+
result
|
|
8
|
+
} from "./chunk-0bt6yb2q.js";
|
|
9
|
+
|
|
10
|
+
// src/core/events/EventLog.ts
|
|
11
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
|
|
14
|
+
// src/core/events/Event.ts
|
|
15
|
+
var EVENT_TYPES = [
|
|
16
|
+
"run.created",
|
|
17
|
+
"run.closed",
|
|
18
|
+
"run.unlocked",
|
|
19
|
+
"run.cancelled",
|
|
20
|
+
"phase.started",
|
|
21
|
+
"phase.done",
|
|
22
|
+
"stage.started",
|
|
23
|
+
"stage.done",
|
|
24
|
+
"stage.failed",
|
|
25
|
+
"stage.skipped",
|
|
26
|
+
"task.started",
|
|
27
|
+
"task.done",
|
|
28
|
+
"agent.spawned",
|
|
29
|
+
"agent.result",
|
|
30
|
+
"question.asked",
|
|
31
|
+
"question.answered",
|
|
32
|
+
"gate.requested",
|
|
33
|
+
"gate.approved",
|
|
34
|
+
"gate.rejected",
|
|
35
|
+
"gate.revoked",
|
|
36
|
+
"check.passed",
|
|
37
|
+
"check.failed",
|
|
38
|
+
"budget.warned",
|
|
39
|
+
"budget.blocked",
|
|
40
|
+
"budget.raised",
|
|
41
|
+
"fact.added",
|
|
42
|
+
"fact.retired",
|
|
43
|
+
"map.refreshed",
|
|
44
|
+
"ticket.synced",
|
|
45
|
+
"error"
|
|
46
|
+
];
|
|
47
|
+
var EVENT_KEYS = ["ts", "run", "stage", "type", "actor", "cost_usd", "payload"];
|
|
48
|
+
var MAX_LINE_BYTES = 8 * 1024;
|
|
49
|
+
var MAX_PAYLOAD_BYTES = 4 * 1024;
|
|
50
|
+
var MAX_PAYLOAD_DEPTH = 3;
|
|
51
|
+
function validateEvent(input) {
|
|
52
|
+
const issues = [];
|
|
53
|
+
const doc = asDocument(input, issues);
|
|
54
|
+
if (!doc)
|
|
55
|
+
return result(issues);
|
|
56
|
+
requireKeys(doc, EVENT_KEYS, "", issues);
|
|
57
|
+
for (const key of Object.keys(doc)) {
|
|
58
|
+
if (!EVENT_KEYS.includes(key)) {
|
|
59
|
+
issues.push({ path: key, message: `unexpected key \`${key}\` — the envelope is exactly ${EVENT_KEYS.join(", ")}` });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
requireString(doc.ts, "ts", issues);
|
|
63
|
+
requireString(doc.run, "run", issues);
|
|
64
|
+
if (doc.stage !== null)
|
|
65
|
+
requireString(doc.stage, "stage", issues);
|
|
66
|
+
requireEnum(doc.type, EVENT_TYPES, "type", issues);
|
|
67
|
+
requireString(doc.actor, "actor", issues);
|
|
68
|
+
requireNumber(doc.cost_usd, "cost_usd", issues);
|
|
69
|
+
if (typeof doc.cost_usd === "number" && doc.cost_usd < 0) {
|
|
70
|
+
issues.push({ path: "cost_usd", message: "must be >= 0" });
|
|
71
|
+
}
|
|
72
|
+
if (typeof doc.payload !== "object" || doc.payload === null || Array.isArray(doc.payload)) {
|
|
73
|
+
issues.push({ path: "payload", message: "expected an object" });
|
|
74
|
+
} else {
|
|
75
|
+
const bytes = Buffer.byteLength(JSON.stringify(doc.payload), "utf8");
|
|
76
|
+
if (bytes > MAX_PAYLOAD_BYTES) {
|
|
77
|
+
issues.push({ path: "payload", message: `${bytes} bytes exceeds the ${MAX_PAYLOAD_BYTES} byte cap` });
|
|
78
|
+
}
|
|
79
|
+
const depth = objectDepth(doc.payload);
|
|
80
|
+
if (depth > MAX_PAYLOAD_DEPTH) {
|
|
81
|
+
issues.push({ path: "payload", message: `nesting depth ${depth} exceeds ${MAX_PAYLOAD_DEPTH}` });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result(issues);
|
|
85
|
+
}
|
|
86
|
+
function serializeEvent(event) {
|
|
87
|
+
return JSON.stringify({
|
|
88
|
+
ts: event.ts,
|
|
89
|
+
run: event.run,
|
|
90
|
+
stage: event.stage,
|
|
91
|
+
type: event.type,
|
|
92
|
+
actor: event.actor,
|
|
93
|
+
cost_usd: event.cost_usd,
|
|
94
|
+
payload: event.payload
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function objectDepth(value, depth = 1) {
|
|
98
|
+
if (typeof value !== "object" || value === null)
|
|
99
|
+
return depth - 1;
|
|
100
|
+
let deepest = depth;
|
|
101
|
+
for (const child of Object.values(value)) {
|
|
102
|
+
if (typeof child === "object" && child !== null) {
|
|
103
|
+
deepest = Math.max(deepest, objectDepth(child, depth + 1));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return deepest;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/core/events/EventLog.ts
|
|
110
|
+
class EventLog {
|
|
111
|
+
path;
|
|
112
|
+
constructor(path) {
|
|
113
|
+
this.path = path;
|
|
114
|
+
}
|
|
115
|
+
static forRun(runDir) {
|
|
116
|
+
return new EventLog(join(runDir, "events.jsonl"));
|
|
117
|
+
}
|
|
118
|
+
get sizeBytes() {
|
|
119
|
+
return existsSync(this.path) ? statSync(this.path).size : 0;
|
|
120
|
+
}
|
|
121
|
+
append(event) {
|
|
122
|
+
const validation = validateEvent(event);
|
|
123
|
+
if (!validation.ok) {
|
|
124
|
+
const first = validation.issues[0];
|
|
125
|
+
throw new Error(`refusing to append an invalid event: ${first?.path ?? ""} ${first?.message ?? ""}`);
|
|
126
|
+
}
|
|
127
|
+
const line = serializeEvent(event);
|
|
128
|
+
const bytes = Buffer.byteLength(line, "utf8") + 1;
|
|
129
|
+
if (bytes > MAX_LINE_BYTES) {
|
|
130
|
+
throw new Error(`event line is ${bytes} bytes (max ${MAX_LINE_BYTES})`);
|
|
131
|
+
}
|
|
132
|
+
const before = this.sizeBytes;
|
|
133
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
134
|
+
appendFileSync(this.path, `${line}
|
|
135
|
+
`, "utf8");
|
|
136
|
+
const after = this.sizeBytes;
|
|
137
|
+
if (after < before + bytes) {
|
|
138
|
+
throw new Error(`events.jsonl shrank under an append (${before} -> ${after} bytes): the log is append-only`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
tryAppend(event) {
|
|
142
|
+
try {
|
|
143
|
+
this.append(event);
|
|
144
|
+
return null;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return error instanceof Error ? error.message : String(error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
replaceAll(content) {
|
|
150
|
+
const next = Buffer.byteLength(content, "utf8");
|
|
151
|
+
const current = this.sizeBytes;
|
|
152
|
+
if (next < current) {
|
|
153
|
+
throw new Error(`refusing to shorten events.jsonl (${current} -> ${next} bytes): the log is append-only`);
|
|
154
|
+
}
|
|
155
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
156
|
+
writeFileSync(this.path, content, "utf8");
|
|
157
|
+
}
|
|
158
|
+
readAll() {
|
|
159
|
+
if (!existsSync(this.path))
|
|
160
|
+
return { events: [], lines: [], skipped: 0 };
|
|
161
|
+
let text;
|
|
162
|
+
try {
|
|
163
|
+
text = readFileSync(this.path, "utf8");
|
|
164
|
+
} catch {
|
|
165
|
+
return { events: [], lines: [], skipped: 0 };
|
|
166
|
+
}
|
|
167
|
+
const events = [];
|
|
168
|
+
const lines = [];
|
|
169
|
+
let skipped = 0;
|
|
170
|
+
text.split(`
|
|
171
|
+
`).forEach((line, index) => {
|
|
172
|
+
if (line.trim() === "")
|
|
173
|
+
return;
|
|
174
|
+
try {
|
|
175
|
+
events.push(JSON.parse(line));
|
|
176
|
+
lines.push(index + 1);
|
|
177
|
+
} catch {
|
|
178
|
+
skipped += 1;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
return { events, lines, skipped };
|
|
182
|
+
}
|
|
183
|
+
read() {
|
|
184
|
+
const result2 = this.readAll();
|
|
185
|
+
if (result2.skipped > 0 && !WARNED.has(this.path)) {
|
|
186
|
+
WARNED.add(this.path);
|
|
187
|
+
process.stderr.write(`tldrx: ${this.path}: ${skippedNote(result2.skipped)} (unparseable — a torn write)
|
|
188
|
+
`);
|
|
189
|
+
}
|
|
190
|
+
return result2.events;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function skippedNote(skipped) {
|
|
194
|
+
return `${String(skipped)} line${skipped === 1 ? "" : "s"} skipped`;
|
|
195
|
+
}
|
|
196
|
+
var WARNED = new Set;
|
|
197
|
+
|
|
198
|
+
export { EventLog };
|