flanders 0.0.1
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/lib/Claude.d.ts +129 -0
- package/lib/Claude.js +390 -0
- package/lib/ClaudeSession.d.ts +34 -0
- package/lib/ClaudeSession.js +293 -0
- package/lib/Flanders.d.ts +21 -0
- package/lib/Flanders.js +64 -0
- package/lib/Git.d.ts +12 -0
- package/lib/Git.js +128 -0
- package/lib/PlanFile.d.ts +38 -0
- package/lib/PlanFile.js +167 -0
- package/lib/ScriptRunner.d.ts +26 -0
- package/lib/ScriptRunner.js +109 -0
- package/lib/Workspace.d.ts +32 -0
- package/lib/Workspace.js +78 -0
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +328 -0
- package/lib/commands/Implement.d.ts +64 -0
- package/lib/commands/Implement.js +696 -0
- package/lib/commands/Install.d.ts +20 -0
- package/lib/commands/Install.js +126 -0
- package/lib/contexts.d.ts +77 -0
- package/lib/contexts.js +3 -0
- package/lib/fsUtils.d.ts +5 -0
- package/lib/fsUtils.js +51 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +6 -0
- package/lib/prompts.d.ts +18 -0
- package/lib/prompts.js +222 -0
- package/lib/skills.d.ts +3 -0
- package/lib/skills.js +207 -0
- package/lib/ui/BottomBlock.d.ts +70 -0
- package/lib/ui/BottomBlock.js +193 -0
- package/lib/ui/formatters.d.ts +29 -0
- package/lib/ui/formatters.js +222 -0
- package/lib/wait.d.ts +2 -0
- package/lib/wait.js +36 -0
- package/package.json +41 -0
package/lib/skills.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.planSkillBody = exports.ruleSkillBody = exports.contractSkillBody = void 0;
|
|
4
|
+
exports.contractSkillBody = `---
|
|
5
|
+
description: Translate a free-form request into one or more contract markdown files inside the project's contracts/ folder.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are the /flanders-contract skill. Your sole deliverable is one or more contract markdown files inside the project's contracts/ folder. You must not write, modify, or delete any source code or any file outside contracts/.
|
|
9
|
+
|
|
10
|
+
## Input resolution
|
|
11
|
+
|
|
12
|
+
The user invokes you as: /flanders-contract [<data>]
|
|
13
|
+
|
|
14
|
+
- If <data> is omitted, take the user's natural-language request from the same turn or from subsequent turns of the conversation.
|
|
15
|
+
- If <data> is supplied and resolves to an existing file path, read the file's content and use it as input.
|
|
16
|
+
- If <data> is supplied and does not resolve to an existing file, use the value verbatim as inline input.
|
|
17
|
+
|
|
18
|
+
## What a contract is
|
|
19
|
+
|
|
20
|
+
A contract is a markdown document that describes the public-facing obligations of a piece of software. It captures what a user of that software will see, do, and rely on. Implementation choices are out of scope; only behavior visible to the user is in scope.
|
|
21
|
+
|
|
22
|
+
Contracts are the most public surface of the project. Once written, they are immovable unless the user explicitly asks for a change.
|
|
23
|
+
|
|
24
|
+
## Procedure
|
|
25
|
+
|
|
26
|
+
1. Resolve the input from the invocation rule above.
|
|
27
|
+
2. Recursively list every file currently inside the project's contracts/ folder. Capture relative paths from the project root. When the folder does not exist or is empty, the listing is empty. This listing is exhaustive — do not enumerate files in any other way.
|
|
28
|
+
3. Run the clarification phase before writing anything to disk:
|
|
29
|
+
- Pick the files relevant to the request from the listing and read their content to understand the project context.
|
|
30
|
+
- Ask clarifying questions sequentially — one question per turn — whenever the request leaves an obligation ambiguous, leaves a UI or logic decision unspecified, or admits multiple valid interpretations. Do not bundle several questions in one turn.
|
|
31
|
+
- Prefer multiple-choice questions when the answer space is bounded. Use open-ended questions only when multiple-choice would force a false dichotomy.
|
|
32
|
+
- When two or three substantially different approaches would all satisfy the request, present those approaches with a short trade-off summary for each and ask the user to pick or redirect, instead of silently choosing one.
|
|
33
|
+
- The clarification phase ends only when you have enough information to draft contract files that contain no placeholders, no contradictions, and no scope ambiguity.
|
|
34
|
+
4. Run the drafting phase. Before persisting any file:
|
|
35
|
+
- Present the planned file layout (which files will exist, what each file will cover) and the key obligations of each file as a structured summary, and wait for user approval or redirection.
|
|
36
|
+
- For non-trivial requests, present the draft of each file (or each section, when a file is large) and wait for approval before moving on. Trivial requests may be presented as a single combined draft.
|
|
37
|
+
- Update related existing contract files in place when the request affects obligations they already cover, and create new files only for obligations not already covered. Do not duplicate an existing obligation across files.
|
|
38
|
+
5. After approval, run a self-review pass before finalizing each file: re-read the draft and check for placeholders left behind, contradictions with other contract files, ambiguous wording, and scope that drifted beyond what the user requested. Fix any issue in place; if a fix would change the meaning of an already-approved obligation, surface the issue to the user and ask before applying it.
|
|
39
|
+
6. Organize the resulting files in whichever shape best fits the requested product:
|
|
40
|
+
- A single descriptive file when the product is small.
|
|
41
|
+
- Multiple files inside contracts/ when the product has clearly separable concerns (for example, a logic file and a UI file).
|
|
42
|
+
- Subfolders grouping related files when the product has multiple sections (for example, one folder per major feature).
|
|
43
|
+
7. Filenames must be descriptive of their content — the user must be able to tell what each file covers from its name alone.
|
|
44
|
+
|
|
45
|
+
## Output language
|
|
46
|
+
|
|
47
|
+
Write contract files in the same natural language as the input request. If the input is in Spanish, the output is in Spanish; if English, English; and so on. Do not translate unless the user says otherwise.
|
|
48
|
+
|
|
49
|
+
## Idempotency and overwrites
|
|
50
|
+
|
|
51
|
+
Existing files in contracts/ are not protected. Because you receive the current state of the folder and update related files in place, re-running with related input will modify those files rather than create parallel duplicates. Preserving prior versions is the user's responsibility (typically through version control).`;
|
|
52
|
+
exports.ruleSkillBody = `---
|
|
53
|
+
description: Translate a free-form request into one or more rule markdown files inside the project's rules/ folder.
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
You are the /flanders-rule skill. Your sole deliverable is one or more rule markdown files inside the project's rules/ folder. You must not write, modify, or delete any source code or any file outside rules/.
|
|
57
|
+
|
|
58
|
+
## Input resolution
|
|
59
|
+
|
|
60
|
+
The user invokes you as: /flanders-rule [<data>]
|
|
61
|
+
|
|
62
|
+
- If <data> is omitted, take the user's natural-language request from the same turn or from subsequent turns of the conversation.
|
|
63
|
+
- If <data> is supplied and resolves to an existing file path, read the file's content and use it as input.
|
|
64
|
+
- If <data> is supplied and does not resolve to an existing file, use the value verbatim as inline input.
|
|
65
|
+
|
|
66
|
+
## What a rule is
|
|
67
|
+
|
|
68
|
+
A rule is a markdown document that captures a single, atomic piece of implementation guidance — a constraint, convention, or pattern that the project's code must follow. Each rule file describes exactly one rule.
|
|
69
|
+
|
|
70
|
+
Bundles of related rules (for example, the multiple obligations that make up SOLID, or the dispose pattern) are modeled as a subfolder under rules/ containing one file per atomic rule inside, never as a single multi-rule file.
|
|
71
|
+
|
|
72
|
+
The namespace of a rule is its relative path inside rules/ — the combination of its enclosing subfolders and its filename. The namespace is what downstream tooling uses to organize, filter, and reference rules.
|
|
73
|
+
|
|
74
|
+
Rules are immovable once written unless the user explicitly asks for a change.
|
|
75
|
+
|
|
76
|
+
## Procedure
|
|
77
|
+
|
|
78
|
+
1. Resolve the input from the invocation rule above.
|
|
79
|
+
2. Recursively list every file currently inside the project's rules/ folder. Capture relative paths from the project root. When the folder does not exist or is empty, the listing is empty. This listing is exhaustive — do not enumerate files in any other way.
|
|
80
|
+
3. Run the clarification phase before writing anything to disk:
|
|
81
|
+
- Pick the files relevant to the request from the listing and read their content to understand the project's existing rule set.
|
|
82
|
+
- Ask the user clarifying questions sequentially — one question per turn — whenever the request leaves a rule ambiguous, leaves the scope of enforcement unspecified, or admits multiple valid interpretations. Do not bundle several questions in one turn.
|
|
83
|
+
- Prefer multiple-choice questions when the answer space is bounded. Use open-ended questions only when multiple-choice would force a false dichotomy.
|
|
84
|
+
- When two or three substantially different formulations of a rule would all satisfy the request, present those formulations with a short trade-off summary for each and ask the user to pick or redirect, instead of silently choosing one.
|
|
85
|
+
- The clarification phase ends only when you have enough information to draft rule files that contain no placeholders, no contradictions, and no scope ambiguity.
|
|
86
|
+
4. Run the drafting phase. Before persisting any file:
|
|
87
|
+
- Present the planned file layout (which rule files will exist, in which subfolders, and the atomic rule each file captures) as a structured summary, and wait for user approval or redirection.
|
|
88
|
+
- For non-trivial requests, present the draft of each file (or each section, when a file is large) and wait for approval before moving on. Trivial requests may be presented as a single combined draft.
|
|
89
|
+
- Update related existing rule files in place when the request affects rules they already cover, and create new files only for rules not already covered. Do not duplicate the same rule across files.
|
|
90
|
+
5. After approval, run the self-review pass before finalizing each file: re-read the draft and check for placeholders left behind, contradictions with other rule files or with existing contracts, ambiguous wording, and scope that drifted beyond what the user requested. Fix any issue in place; if a fix would change the meaning of an already-approved rule, surface the issue to the user and ask before applying it.
|
|
91
|
+
6. Organize the resulting files so that each rule lives in its own file. Use subfolders inside rules/ to group thematically related rules (for example, a testing/ subfolder for testing-related rules, a dependencies/ subfolder for dependency-management rules, a solid/ subfolder with one file per SOLID principle, a disposes/ subfolder with one file per dispose-pattern obligation). A bundle of related rules MUST be modeled as a subfolder of single-rule files, never as one multi-rule file.
|
|
92
|
+
7. Filenames must be descriptive of the single rule the file captures — the user must be able to tell which rule a file pins from its name alone.
|
|
93
|
+
|
|
94
|
+
## Output language
|
|
95
|
+
|
|
96
|
+
Write rule files in the same natural language as the input request. If the input is in Spanish, the output is in Spanish; if English, English; and so on. Do not translate unless the user says otherwise.
|
|
97
|
+
|
|
98
|
+
## Idempotency and overwrites
|
|
99
|
+
|
|
100
|
+
Existing files in rules/ are not protected. Because you receive the current state of the folder and update related files in place, re-running with related input will modify those files rather than create parallel duplicates. Preserving prior versions is the user's responsibility (typically through version control).`;
|
|
101
|
+
exports.planSkillBody = `---
|
|
102
|
+
description: Produce a contract-aware work plan inside the project's plans/ folder.
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
You are the /flanders-plan skill. Your sole deliverable is exactly one markdown plan file inside the project's plans/ folder. You must not write, modify, or delete any source code or any file outside plans/.
|
|
106
|
+
|
|
107
|
+
## Input resolution
|
|
108
|
+
|
|
109
|
+
The user invokes you as: /flanders-plan [<data>]
|
|
110
|
+
|
|
111
|
+
- If <data> is omitted, take the user's natural-language request from the conversation.
|
|
112
|
+
- If <data> is supplied and resolves to an existing file path, read the file's content and use it as input.
|
|
113
|
+
- If <data> is supplied and does not resolve to an existing file, use the value verbatim as inline input.
|
|
114
|
+
|
|
115
|
+
## Procedure
|
|
116
|
+
|
|
117
|
+
1. Recursively list every file inside the project's contracts/ folder and every file inside the project's rules/ folder. Capture relative paths from the project root. The contracts listing is the canonical reference of contracts for this run; the rules listing is the canonical reference of rules for this run.
|
|
118
|
+
2. Resolve the input from the invocation rule above.
|
|
119
|
+
3. Produce exactly one markdown file inside the project's plans/ folder. The filename must be descriptive of the plan's subject.
|
|
120
|
+
|
|
121
|
+
## Plan file format
|
|
122
|
+
|
|
123
|
+
The plan file must follow these rules exactly:
|
|
124
|
+
|
|
125
|
+
### Task lines
|
|
126
|
+
|
|
127
|
+
A task is a markdown list item that carries a checkbox and a metrics object at the start of its content. The full shape of a task line is:
|
|
128
|
+
|
|
129
|
+
[ ]{"it":0,"ot":0,"t":0} 1.1 TITLE
|
|
130
|
+
|
|
131
|
+
with the following pieces, in this exact order and spacing:
|
|
132
|
+
|
|
133
|
+
- A checkbox, in one of two states:
|
|
134
|
+
- \`[ ]\` — open (not yet implemented).
|
|
135
|
+
- \`[x]\` — done (already implemented).
|
|
136
|
+
- Immediately after the closing \`]\`, with no whitespace between them, the metrics object (a strict JSON literal — see Task metrics below).
|
|
137
|
+
- A single space after the closing \`}\`.
|
|
138
|
+
- The task number (see Numbering).
|
|
139
|
+
- A single space.
|
|
140
|
+
- The task title.
|
|
141
|
+
|
|
142
|
+
No malformed variants such as \`[]\`, \`[ x]\`, or \`[X ]\` are permitted. All new tasks are written as open (\`[ ]\`).
|
|
143
|
+
|
|
144
|
+
### Task metrics
|
|
145
|
+
|
|
146
|
+
Every leaf task line carries a metrics object \`{"it":0,"ot":0,"t":0}\` at generation time. This is a strict JSON literal with three integer fields: \`it\` (input tokens), \`ot\` (output tokens), and \`t\` (time in seconds), all set to zero for new tasks. The object is placed immediately after the checkbox with no whitespace between \`]\` and \`{\`, and one space between the closing \`}\` and the task number.
|
|
147
|
+
|
|
148
|
+
### Hierarchy and sub-tasks
|
|
149
|
+
|
|
150
|
+
- A leaf task (no sub-tasks) carries a checkbox.
|
|
151
|
+
- A parent task (has sub-tasks with their own checkboxes) does NOT carry its own checkbox. It appears as a heading or list item with a title and description, but no checkbox.
|
|
152
|
+
|
|
153
|
+
Checkboxes appear only on the smallest atomic units of work, never on a unit that aggregates other checkboxed units.
|
|
154
|
+
|
|
155
|
+
### Numbering
|
|
156
|
+
|
|
157
|
+
Tasks are numbered hierarchically:
|
|
158
|
+
- Top-level tasks: 1, 2, 3, ...
|
|
159
|
+
- Sub-tasks of task 2: 2.1, 2.2, 2.3, ...
|
|
160
|
+
- Deeper levels follow the same dotted convention.
|
|
161
|
+
|
|
162
|
+
The numbering is part of the visible task identifier.
|
|
163
|
+
|
|
164
|
+
### Ordering
|
|
165
|
+
|
|
166
|
+
Tasks are written in the order they must be implemented, accounting for dependencies. A task that depends on another must appear after the task it depends on.
|
|
167
|
+
|
|
168
|
+
### Task content
|
|
169
|
+
|
|
170
|
+
- Write each leaf task with a detailed description and explicit acceptance criteria — the conditions that must be true once the task is implemented for it to be considered complete.
|
|
171
|
+
- Every leaf task carries the initial metrics object \`{"it":0,"ot":0,"t":0}\` literally. Done tasks generated by \`/flanders-plan\` follow the same shape with the same zero values.
|
|
172
|
+
- Choose a granularity that is neither too broad nor too narrow. Tasks must be small enough for a single AI invocation without excessive tokens, but large enough that splitting further would create artificial fragmentation. When in doubt, subdivide.
|
|
173
|
+
- For every leaf task, link the relevant contract file or files by their listed relative path. When the relevant obligation lives in a specific section or line range, reference that section or line range as well.
|
|
174
|
+
- For every leaf task, link the relevant rule file or files by their listed relative path. The planner MUST read every rule file it determines is relevant to the request before drafting the plan; reading the relevant rules is not optional. When a rule's enforcement is bound to a specific scope, reference that scope alongside the file path.
|
|
175
|
+
- Rule selection per task is scope-driven, not topic-driven. Before listing the rule links for a leaf task, walk the rules/ listing and ask: which rule namespaces are in scope for the work this task actually performs? Use the namespace as the scope hint. Heuristics: a task that modifies or adds tests must link every applicable file under \`rules/testing/*\`; a task that creates or modifies anything with timers, listeners, controllers, child processes, or other async lifecycle must link every applicable file under \`rules/disposables/*\`; a task that changes terminal UI or live-region output must link every applicable file under \`rules/ui/*\`. Walk every namespace whose scope could plausibly apply, and pick every file whose obligation could be triggered by the task. Under-linking is costly: the downstream implementor is FAILed by the adversarial reviewer for any global rule that should have applied but was not applied, so when in doubt, link rather than omit.
|
|
176
|
+
- No task may describe work that creates, modifies, deletes, or renames files inside contracts/, inside rules/, or inside plans/ (the bounded checkbox/metrics update that the implement command holds is not available to tasks — see shared/spec-folder-write-authority.md).
|
|
177
|
+
|
|
178
|
+
### Contract and rule compliance
|
|
179
|
+
|
|
180
|
+
Never produce a plan that violates any contract or rule on the canonical lists.
|
|
181
|
+
|
|
182
|
+
## Post-write verification
|
|
183
|
+
|
|
184
|
+
After writing the plan file, re-read it and verify:
|
|
185
|
+
- The file exists at the expected path inside plans/ and is non-empty.
|
|
186
|
+
- Every task line follows the checkbox shape defined above (every list item carrying a task identifier has a valid \`[ ]\` or \`[x]\` checkbox; no malformed variants).
|
|
187
|
+
- Every leaf task line carries a metrics object literally equal to \`{"it":0,"ot":0,"t":0}\`. The verification re-parses each metrics object with strict JSON, so the check is byte-exact — no extra spaces, no reordered keys, no trailing commas.
|
|
188
|
+
- At least one task line was produced.
|
|
189
|
+
|
|
190
|
+
If any check fails, fix the file and re-verify instead of leaving a malformed plan on disk.
|
|
191
|
+
|
|
192
|
+
## Summary
|
|
193
|
+
|
|
194
|
+
After successful verification, print a summary in chat containing:
|
|
195
|
+
- The plan file path.
|
|
196
|
+
- The plan file's character size.
|
|
197
|
+
- The plan file's total line count.
|
|
198
|
+
- The total number of detected tasks.
|
|
199
|
+
|
|
200
|
+
## Output language
|
|
201
|
+
|
|
202
|
+
Write the plan file in the same natural language as the input request, unless the user says otherwise.
|
|
203
|
+
|
|
204
|
+
## Missing contracts or rules
|
|
205
|
+
|
|
206
|
+
If the contracts/ folder is missing or empty, warn the user in chat and produce a plan that includes whatever contracts the request implicitly requires before any implementation work. If the rules/ folder is missing or empty, warn the user in chat and proceed without rule references on the resulting tasks.`;
|
|
207
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tpbGxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NraWxscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBYSxRQUFBLGlCQUFpQixHQUM5Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7aVVBK0NpVSxDQUFDO0FBRXJULFFBQUEsYUFBYSxHQUMxQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzZUQWdENlQsQ0FBQztBQUVqVCxRQUFBLGFBQWEsR0FDMUI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztvVEF5R29ULENBQUMifQ==
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { TimeContext } from "../contexts";
|
|
2
|
+
export type BottomBlockIO = {
|
|
3
|
+
write(text: string): void;
|
|
4
|
+
columns(): number;
|
|
5
|
+
onResize(listener: () => void): () => void;
|
|
6
|
+
};
|
|
7
|
+
export type Activity = "implementing" | "reviewing" | "building" | "testing";
|
|
8
|
+
export type HeaderFields = {
|
|
9
|
+
indexLabel?: string | null;
|
|
10
|
+
iteration?: number | null;
|
|
11
|
+
activity?: Activity | null;
|
|
12
|
+
taskNumber?: string | null;
|
|
13
|
+
title?: string | null;
|
|
14
|
+
};
|
|
15
|
+
export type MetricsFields = {
|
|
16
|
+
task?: {
|
|
17
|
+
tokens: number;
|
|
18
|
+
seconds: number;
|
|
19
|
+
};
|
|
20
|
+
plan?: {
|
|
21
|
+
tokens: number;
|
|
22
|
+
seconds: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export type WaitKind = "rate-limit";
|
|
26
|
+
export type FooterState = {
|
|
27
|
+
kind: "blank";
|
|
28
|
+
} | {
|
|
29
|
+
kind: "working";
|
|
30
|
+
} | {
|
|
31
|
+
kind: "waiting";
|
|
32
|
+
waitKind: WaitKind;
|
|
33
|
+
endTime: number;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "terminal";
|
|
36
|
+
label: TerminalLabel;
|
|
37
|
+
};
|
|
38
|
+
export type TerminalLabel = "Done" | "Hard stop" | "Interrupted" | "Failed";
|
|
39
|
+
export declare class BottomBlock {
|
|
40
|
+
private _io;
|
|
41
|
+
private _time;
|
|
42
|
+
private _mounted;
|
|
43
|
+
private _finalized;
|
|
44
|
+
private _disposed;
|
|
45
|
+
private _header;
|
|
46
|
+
private _metrics;
|
|
47
|
+
private _footer;
|
|
48
|
+
private _animFrame;
|
|
49
|
+
private _animTimer;
|
|
50
|
+
private _countdownTimer;
|
|
51
|
+
private _unsubResize;
|
|
52
|
+
constructor(_io: BottomBlockIO, _time: TimeContext);
|
|
53
|
+
mount(): void;
|
|
54
|
+
isFinalized(): boolean;
|
|
55
|
+
setHeader(fields: HeaderFields): void;
|
|
56
|
+
setMetrics(fields: MetricsFields): void;
|
|
57
|
+
setFooter(state: FooterState): void;
|
|
58
|
+
writeAbove(text: string): void;
|
|
59
|
+
finalize(label: TerminalLabel): void;
|
|
60
|
+
dispose(): void;
|
|
61
|
+
private _cancelTimers;
|
|
62
|
+
private _startFooterTimer;
|
|
63
|
+
private _scheduleAnimTick;
|
|
64
|
+
private _scheduleCountdownTick;
|
|
65
|
+
private _clearBlock;
|
|
66
|
+
private _drawBlock;
|
|
67
|
+
private _renderHeader;
|
|
68
|
+
private _renderMetrics;
|
|
69
|
+
private _renderFooter;
|
|
70
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BottomBlock = void 0;
|
|
4
|
+
const formatters_1 = require("./formatters");
|
|
5
|
+
const WAIT_HEADINGS = {
|
|
6
|
+
"rate-limit": "Waiting rate limit"
|
|
7
|
+
};
|
|
8
|
+
const FRAMES = ["⣋", "⣙", "⣹", "⣸", "⣼", "⣴", "⣦", "⣧", "⣇", "⣏"];
|
|
9
|
+
const FRAME_MS = 200;
|
|
10
|
+
const ORANGE = "\x1b[38;5;208m";
|
|
11
|
+
const RESET = "\x1b[0m";
|
|
12
|
+
const CURSOR_UP_3 = "\x1b[3A";
|
|
13
|
+
const CLEAR_TO_END = "\x1b[J";
|
|
14
|
+
const CR = "\r";
|
|
15
|
+
class BottomBlock {
|
|
16
|
+
_io;
|
|
17
|
+
_time;
|
|
18
|
+
_mounted = false;
|
|
19
|
+
_finalized = false;
|
|
20
|
+
_disposed = false;
|
|
21
|
+
_header = {};
|
|
22
|
+
_metrics = {};
|
|
23
|
+
_footer = { kind: "working" };
|
|
24
|
+
_animFrame = 0;
|
|
25
|
+
_animTimer = null;
|
|
26
|
+
_countdownTimer = null;
|
|
27
|
+
_unsubResize = null;
|
|
28
|
+
constructor(_io, _time) {
|
|
29
|
+
this._io = _io;
|
|
30
|
+
this._time = _time;
|
|
31
|
+
}
|
|
32
|
+
mount() {
|
|
33
|
+
if (this._disposed)
|
|
34
|
+
return;
|
|
35
|
+
if (this._mounted)
|
|
36
|
+
return;
|
|
37
|
+
this._mounted = true;
|
|
38
|
+
this._unsubResize = this._io.onResize(() => {
|
|
39
|
+
this._clearBlock();
|
|
40
|
+
this._drawBlock();
|
|
41
|
+
});
|
|
42
|
+
this._drawBlock();
|
|
43
|
+
this._startFooterTimer();
|
|
44
|
+
}
|
|
45
|
+
isFinalized() {
|
|
46
|
+
return this._finalized;
|
|
47
|
+
}
|
|
48
|
+
setHeader(fields) {
|
|
49
|
+
if (this._disposed || this._finalized)
|
|
50
|
+
return;
|
|
51
|
+
this._header = fields;
|
|
52
|
+
if (this._mounted) {
|
|
53
|
+
this._clearBlock();
|
|
54
|
+
this._drawBlock();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
setMetrics(fields) {
|
|
58
|
+
if (this._disposed || this._finalized)
|
|
59
|
+
return;
|
|
60
|
+
this._metrics = fields;
|
|
61
|
+
if (this._mounted) {
|
|
62
|
+
this._clearBlock();
|
|
63
|
+
this._drawBlock();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
setFooter(state) {
|
|
67
|
+
if (this._disposed || this._finalized)
|
|
68
|
+
return;
|
|
69
|
+
this._cancelTimers();
|
|
70
|
+
this._footer = state;
|
|
71
|
+
if (state.kind === "working") {
|
|
72
|
+
this._animFrame = 0;
|
|
73
|
+
}
|
|
74
|
+
if (this._mounted) {
|
|
75
|
+
this._clearBlock();
|
|
76
|
+
this._drawBlock();
|
|
77
|
+
this._startFooterTimer();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
writeAbove(text) {
|
|
81
|
+
if (this._disposed)
|
|
82
|
+
return;
|
|
83
|
+
if (!this._mounted) {
|
|
84
|
+
this._io.write(text);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this._clearBlock();
|
|
88
|
+
this._io.write(text);
|
|
89
|
+
this._drawBlock();
|
|
90
|
+
}
|
|
91
|
+
finalize(label) {
|
|
92
|
+
if (this._disposed)
|
|
93
|
+
return;
|
|
94
|
+
if (this._finalized)
|
|
95
|
+
return;
|
|
96
|
+
this._finalized = true;
|
|
97
|
+
this._cancelTimers();
|
|
98
|
+
this._footer = { kind: "terminal", label };
|
|
99
|
+
if (this._unsubResize) {
|
|
100
|
+
this._unsubResize();
|
|
101
|
+
this._unsubResize = null;
|
|
102
|
+
}
|
|
103
|
+
if (this._mounted) {
|
|
104
|
+
this._clearBlock();
|
|
105
|
+
this._drawBlock();
|
|
106
|
+
this._io.write("\n");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
dispose() {
|
|
110
|
+
if (this._disposed)
|
|
111
|
+
return;
|
|
112
|
+
this._disposed = true;
|
|
113
|
+
this._cancelTimers();
|
|
114
|
+
if (this._unsubResize) {
|
|
115
|
+
this._unsubResize();
|
|
116
|
+
this._unsubResize = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
_cancelTimers() {
|
|
120
|
+
if (this._animTimer) {
|
|
121
|
+
this._animTimer.cancel();
|
|
122
|
+
this._animTimer = null;
|
|
123
|
+
}
|
|
124
|
+
if (this._countdownTimer) {
|
|
125
|
+
this._countdownTimer.cancel();
|
|
126
|
+
this._countdownTimer = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
_startFooterTimer() {
|
|
130
|
+
if (this._footer.kind === "working") {
|
|
131
|
+
this._scheduleAnimTick();
|
|
132
|
+
}
|
|
133
|
+
else if (this._footer.kind === "waiting") {
|
|
134
|
+
this._scheduleCountdownTick();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
_scheduleAnimTick() {
|
|
138
|
+
this._animTimer = this._time.setTimeout(() => {
|
|
139
|
+
this._animTimer = null;
|
|
140
|
+
if (this._disposed || this._finalized || this._footer.kind !== "working")
|
|
141
|
+
return;
|
|
142
|
+
this._animFrame = (this._animFrame + 1) % FRAMES.length;
|
|
143
|
+
this._clearBlock();
|
|
144
|
+
this._drawBlock();
|
|
145
|
+
this._scheduleAnimTick();
|
|
146
|
+
}, FRAME_MS);
|
|
147
|
+
}
|
|
148
|
+
_scheduleCountdownTick() {
|
|
149
|
+
this._countdownTimer = this._time.setTimeout(() => {
|
|
150
|
+
this._countdownTimer = null;
|
|
151
|
+
if (this._disposed || this._finalized || this._footer.kind !== "waiting")
|
|
152
|
+
return;
|
|
153
|
+
this._clearBlock();
|
|
154
|
+
this._drawBlock();
|
|
155
|
+
this._scheduleCountdownTick();
|
|
156
|
+
}, 1000);
|
|
157
|
+
}
|
|
158
|
+
_clearBlock() {
|
|
159
|
+
this._io.write(CURSOR_UP_3 + CR + CLEAR_TO_END);
|
|
160
|
+
}
|
|
161
|
+
_drawBlock() {
|
|
162
|
+
const cols = Math.max(0, this._io.columns());
|
|
163
|
+
const separator = formatters_1.SEPARATOR_GLYPH.repeat(cols);
|
|
164
|
+
const header = this._renderHeader(cols);
|
|
165
|
+
const metrics = this._renderMetrics(cols);
|
|
166
|
+
const footer = this._renderFooter();
|
|
167
|
+
this._io.write(separator + "\n" + header + "\n" + metrics + "\n" + footer);
|
|
168
|
+
}
|
|
169
|
+
_renderHeader(cols) {
|
|
170
|
+
return (0, formatters_1.formatHeaderLine)(this._header.indexLabel ?? null, this._header.iteration ?? null, this._header.activity ?? null, this._header.taskNumber ?? null, this._header.title ?? null, cols);
|
|
171
|
+
}
|
|
172
|
+
_renderMetrics(cols) {
|
|
173
|
+
return (0, formatters_1.formatMetricsLine)(this._metrics.task, this._metrics.plan, cols);
|
|
174
|
+
}
|
|
175
|
+
_renderFooter() {
|
|
176
|
+
switch (this._footer.kind) {
|
|
177
|
+
case "blank":
|
|
178
|
+
return "";
|
|
179
|
+
case "working":
|
|
180
|
+
return `${ORANGE}${FRAMES[this._animFrame]} Working${RESET}`;
|
|
181
|
+
case "waiting": {
|
|
182
|
+
const remaining = Math.max(0, this._footer.endTime - this._time.now());
|
|
183
|
+
const dateStr = (0, formatters_1.formatDateTime)(new Date(this._footer.endTime));
|
|
184
|
+
const countdown = (0, formatters_1.formatCountdown)(remaining);
|
|
185
|
+
return `${ORANGE}${WAIT_HEADINGS[this._footer.waitKind]} — ${dateStr} — ${countdown}${RESET}`;
|
|
186
|
+
}
|
|
187
|
+
case "terminal":
|
|
188
|
+
return `${ORANGE}${this._footer.label}${RESET}`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.BottomBlock = BottomBlock;
|
|
193
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQm90dG9tQmxvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdWkvQm90dG9tQmxvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsNkNBQXFIO0FBeUJySCxNQUFNLGFBQWEsR0FBNEI7SUFDM0MsWUFBWSxFQUFFLG9CQUFvQjtDQUNyQyxDQUFDO0FBVUYsTUFBTSxNQUFNLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUNsRSxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUM7QUFDckIsTUFBTSxNQUFNLEdBQUcsZ0JBQWdCLENBQUM7QUFDaEMsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDO0FBQ3hCLE1BQU0sV0FBVyxHQUFHLFNBQVMsQ0FBQztBQUM5QixNQUFNLFlBQVksR0FBRyxRQUFRLENBQUM7QUFDOUIsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBRWhCLE1BQWEsV0FBVztJQVlBO0lBQTJCO0lBWHZDLFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDakIsVUFBVSxHQUFHLEtBQUssQ0FBQztJQUNuQixTQUFTLEdBQUcsS0FBSyxDQUFDO0lBQ2xCLE9BQU8sR0FBZ0IsRUFBRSxDQUFDO0lBQzFCLFFBQVEsR0FBaUIsRUFBRSxDQUFDO0lBQzVCLE9BQU8sR0FBZSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUMxQyxVQUFVLEdBQUcsQ0FBQyxDQUFDO0lBQ2YsVUFBVSxHQUFzQixJQUFJLENBQUM7SUFDckMsZUFBZSxHQUFzQixJQUFJLENBQUM7SUFDMUMsWUFBWSxHQUFxQixJQUFJLENBQUM7SUFFOUMsWUFBb0IsR0FBaUIsRUFBVSxLQUFpQjtRQUE1QyxRQUFHLEdBQUgsR0FBRyxDQUFjO1FBQVUsVUFBSyxHQUFMLEtBQUssQ0FBWTtJ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
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const CYAN = "\u001B[36m";
|
|
2
|
+
export declare const YELLOW = "\u001B[33m";
|
|
3
|
+
export declare const MAGENTA = "\u001B[35m";
|
|
4
|
+
export declare const GREEN = "\u001B[32m";
|
|
5
|
+
export declare const BLUE = "\u001B[34m";
|
|
6
|
+
export declare const DIM = "\u001B[2m";
|
|
7
|
+
export declare const RESET = "\u001B[0m";
|
|
8
|
+
export declare const SEPARATOR_GLYPH = "\u2500";
|
|
9
|
+
export declare function colorize(text: string, code: string): string;
|
|
10
|
+
export type Segment = {
|
|
11
|
+
text: string;
|
|
12
|
+
color?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function renderSegments(segments: Segment[]): string;
|
|
15
|
+
export declare function renderSegmentsToWidth(segments: Segment[], cols: number): string;
|
|
16
|
+
export declare function formatCountdown(remainingMs: number): string;
|
|
17
|
+
export declare function formatDateTime(date: Date): string;
|
|
18
|
+
export declare function truncateToWidth(text: string, cols: number): string;
|
|
19
|
+
export declare function formatTokens(n: number): string;
|
|
20
|
+
export declare function formatActiveTime(seconds: number): string;
|
|
21
|
+
export declare function formatHeaderLine(indexLabel: string | null, iteration: number | null, activity: string | null, taskNumber: string | null | undefined, title: string | null, cols: number): string;
|
|
22
|
+
export type MetricsPair = {
|
|
23
|
+
tokens: number;
|
|
24
|
+
seconds: number;
|
|
25
|
+
};
|
|
26
|
+
export declare function formatMetricsLine(task: MetricsPair | undefined, plan: MetricsPair | undefined, cols: number): string;
|
|
27
|
+
export declare function formatSnapshotHeader(indexLabel: string, iteration: number, taskNumber: string | undefined, title: string): string;
|
|
28
|
+
export declare function formatSnapshotMetrics(taskTokens: number, taskSeconds: number, planTokens: number, planSeconds: number): string;
|
|
29
|
+
export declare function formatSnapshotBlock(indexLabel: string, iteration: number, taskNumber: string | undefined, title: string, taskTokens: number, taskSeconds: number, planTokens: number, planSeconds: number, cols: number): string;
|