flanders 0.1.0 → 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/lib/ai/AiRunner.d.ts +24 -0
- package/lib/ai/AiRunner.js +100 -0
- package/lib/ai/AiSession.d.ts +32 -0
- package/lib/ai/AiSession.js +143 -0
- package/lib/ai/ClaudeAdapter.d.ts +12 -0
- package/lib/ai/ClaudeAdapter.js +345 -0
- package/lib/ai/CodexAdapter.d.ts +13 -0
- package/lib/ai/CodexAdapter.js +354 -0
- package/lib/ai/ToolAdapter.d.ts +52 -0
- package/lib/ai/ToolAdapter.js +2 -0
- package/lib/cli.js +56 -35
- package/lib/commands/Implement.d.ts +22 -8
- package/lib/commands/Implement.js +296 -171
- package/lib/commands/Install.d.ts +31 -3
- package/lib/commands/Install.js +625 -49
- package/lib/commands/InstallModelProbe.d.ts +11 -0
- package/lib/commands/InstallModelProbe.js +99 -0
- package/lib/contexts.d.ts +5 -4
- package/lib/index.d.ts +5 -3
- package/lib/{PlanFile.d.ts → plan/PlanFile.d.ts} +8 -1
- package/lib/{PlanFile.js → plan/PlanFile.js} +44 -5
- package/lib/prompts/prompts.d.ts +48 -0
- package/lib/prompts/prompts.js +328 -0
- package/lib/prompts/skills.d.ts +3 -0
- package/lib/prompts/skills.js +464 -0
- package/lib/{Git.d.ts → system/Git.d.ts} +4 -2
- package/lib/{Git.js → system/Git.js} +63 -3
- package/lib/{ScriptRunner.d.ts → system/ScriptRunner.d.ts} +1 -1
- package/lib/system/ShellScriptContext.d.ts +36 -0
- package/lib/system/ShellScriptContext.js +70 -0
- package/lib/{fsUtils.d.ts → system/fsUtils.d.ts} +1 -1
- package/lib/{wait.d.ts → system/wait.d.ts} +1 -1
- package/lib/ui/BottomBlock.d.ts +9 -1
- package/lib/ui/BottomBlock.js +30 -14
- package/lib/ui/PromptHelper.d.ts +12 -0
- package/lib/ui/PromptHelper.js +32 -0
- package/lib/ui/TerminalSizeSource.d.ts +19 -0
- package/lib/ui/TerminalSizeSource.js +77 -0
- package/lib/ui/formatters.d.ts +14 -0
- package/lib/ui/formatters.js +65 -2
- package/lib/workspace/FlandersConfig.d.ts +23 -0
- package/lib/workspace/FlandersConfig.js +90 -0
- package/lib/workspace/SpecDiscovery.d.ts +12 -0
- package/lib/workspace/SpecDiscovery.js +40 -0
- package/lib/{Workspace.d.ts → workspace/Workspace.d.ts} +10 -2
- package/lib/{Workspace.js → workspace/Workspace.js} +52 -6
- package/package.json +3 -2
- package/lib/Claude.d.ts +0 -129
- package/lib/Claude.js +0 -387
- package/lib/ClaudeSession.d.ts +0 -34
- package/lib/ClaudeSession.js +0 -292
- package/lib/prompts.d.ts +0 -18
- package/lib/prompts.js +0 -221
- package/lib/skills.d.ts +0 -3
- package/lib/skills.js +0 -267
- /package/lib/{ScriptRunner.js → system/ScriptRunner.js} +0 -0
- /package/lib/{fsUtils.js → system/fsUtils.js} +0 -0
- /package/lib/{wait.js → system/wait.js} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ScriptContext, TimeContext } from "../contexts";
|
|
2
|
+
export type SpecLists = Readonly<{
|
|
3
|
+
contracts: readonly string[];
|
|
4
|
+
rules: readonly string[];
|
|
5
|
+
flanders: readonly string[];
|
|
6
|
+
}>;
|
|
7
|
+
export declare function classifySpecPaths(paths: readonly string[]): {
|
|
8
|
+
contracts: string[];
|
|
9
|
+
rules: string[];
|
|
10
|
+
flanders: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare function discoverSpecs(script: ScriptContext, time: TimeContext, projectRoot: string): Promise<SpecLists>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifySpecPaths = classifySpecPaths;
|
|
4
|
+
exports.discoverSpecs = discoverSpecs;
|
|
5
|
+
const Git_1 = require("../system/Git");
|
|
6
|
+
function classifySpecPaths(paths) {
|
|
7
|
+
const contracts = [];
|
|
8
|
+
const rules = [];
|
|
9
|
+
const flanders = [];
|
|
10
|
+
for (const filePath of paths) {
|
|
11
|
+
const segments = filePath.split("/");
|
|
12
|
+
const docsIndex = segments.indexOf(".docs");
|
|
13
|
+
if (docsIndex === -1) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (segments.length <= docsIndex + 2) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const kind = segments[docsIndex + 1];
|
|
20
|
+
if (kind === "contracts") {
|
|
21
|
+
contracts.push(filePath);
|
|
22
|
+
}
|
|
23
|
+
else if (kind === "rules") {
|
|
24
|
+
rules.push(filePath);
|
|
25
|
+
}
|
|
26
|
+
else if (kind === "flanders") {
|
|
27
|
+
flanders.push(filePath);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { contracts, rules, flanders };
|
|
31
|
+
}
|
|
32
|
+
async function discoverSpecs(script, time, projectRoot) {
|
|
33
|
+
const files = await (0, Git_1.listNonIgnoredFiles)(script, time, projectRoot);
|
|
34
|
+
const { contracts, rules, flanders } = classifySpecPaths(files);
|
|
35
|
+
const ignored = await (0, Git_1.listIgnoredPaths)(script, time, projectRoot, [...contracts, ...rules, ...flanders]);
|
|
36
|
+
const survivingContracts = contracts.filter(p => !ignored.has(p)).sort();
|
|
37
|
+
const survivingRules = rules.filter(p => !ignored.has(p)).sort();
|
|
38
|
+
const survivingFlanders = flanders.filter(p => !ignored.has(p)).sort();
|
|
39
|
+
return { contracts: survivingContracts, rules: survivingRules, flanders: survivingFlanders };
|
|
40
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FsContext } from "
|
|
1
|
+
import type { FsContext } from "../contexts";
|
|
2
2
|
export type WorkspacePaths = Readonly<{
|
|
3
3
|
root: string;
|
|
4
4
|
buildScript: string;
|
|
@@ -9,6 +9,8 @@ export type WorkspacePaths = Readonly<{
|
|
|
9
9
|
buildLog(iter: number): string;
|
|
10
10
|
testLog(iter: number): string;
|
|
11
11
|
reviewerLog(iter: number): string;
|
|
12
|
+
reviewerOutputLog(iter: number, reviewerIndex: number): string;
|
|
13
|
+
reviewerErrorLog(reviewerIndex: number): string;
|
|
12
14
|
}>;
|
|
13
15
|
export interface PlatformContext {
|
|
14
16
|
isWindows(): boolean;
|
|
@@ -21,12 +23,18 @@ export declare class Workspace {
|
|
|
21
23
|
private _disposed;
|
|
22
24
|
private _preserve;
|
|
23
25
|
private _root;
|
|
26
|
+
private _reviewerRoots;
|
|
24
27
|
constructor(_fs: FsContext, _platform: PlatformContext);
|
|
25
28
|
preserveOnDispose(): void;
|
|
26
|
-
setup(): Promise<WorkspacePaths>;
|
|
29
|
+
setup(reviewerCount: number): Promise<WorkspacePaths>;
|
|
27
30
|
paths(): WorkspacePaths;
|
|
28
31
|
private _paths;
|
|
32
|
+
errorLogExists(): Promise<boolean>;
|
|
33
|
+
readErrorLog(): Promise<string>;
|
|
29
34
|
writeErrorLog(content: string): Promise<void>;
|
|
30
35
|
clearErrorLog(): Promise<void>;
|
|
36
|
+
reviewerErrorLogExists(reviewerIndex: number): Promise<boolean>;
|
|
37
|
+
readReviewerErrorLog(reviewerIndex: number): Promise<string>;
|
|
38
|
+
clearReviewerErrorLog(reviewerIndex: number): Promise<void>;
|
|
31
39
|
dispose(): Promise<void>;
|
|
32
40
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Workspace = void 0;
|
|
4
|
-
const fsUtils_1 = require("
|
|
4
|
+
const fsUtils_1 = require("../system/fsUtils");
|
|
5
5
|
class Workspace {
|
|
6
6
|
constructor(_fs, _platform) {
|
|
7
7
|
this._fs = _fs;
|
|
@@ -9,13 +9,14 @@ class Workspace {
|
|
|
9
9
|
this._disposed = false;
|
|
10
10
|
this._preserve = false;
|
|
11
11
|
this._root = null;
|
|
12
|
+
this._reviewerRoots = [];
|
|
12
13
|
}
|
|
13
14
|
preserveOnDispose() {
|
|
14
15
|
if (this._disposed)
|
|
15
16
|
return;
|
|
16
17
|
this._preserve = true;
|
|
17
18
|
}
|
|
18
|
-
async setup() {
|
|
19
|
+
async setup(reviewerCount) {
|
|
19
20
|
if (this._disposed) {
|
|
20
21
|
throw new Error("Workspace disposed");
|
|
21
22
|
}
|
|
@@ -24,15 +25,19 @@ class Workspace {
|
|
|
24
25
|
}
|
|
25
26
|
const prefix = (0, fsUtils_1.joinPath)(this._platform.tmpdir(), "flanders-");
|
|
26
27
|
this._root = await this._fs.mkdtemp(prefix);
|
|
27
|
-
|
|
28
|
+
for (let i = 0; i < reviewerCount; i++) {
|
|
29
|
+
const dir = await this._fs.mkdtemp(prefix);
|
|
30
|
+
this._reviewerRoots.push(dir);
|
|
31
|
+
}
|
|
32
|
+
return this._paths(this._root, this._reviewerRoots);
|
|
28
33
|
}
|
|
29
34
|
paths() {
|
|
30
35
|
if (!this._root) {
|
|
31
36
|
throw new Error("Workspace not set up");
|
|
32
37
|
}
|
|
33
|
-
return this._paths(this._root);
|
|
38
|
+
return this._paths(this._root, this._reviewerRoots);
|
|
34
39
|
}
|
|
35
|
-
_paths(root) {
|
|
40
|
+
_paths(root, reviewerRoots) {
|
|
36
41
|
const isWindows = this._platform.isWindows();
|
|
37
42
|
return {
|
|
38
43
|
root,
|
|
@@ -43,9 +48,22 @@ class Workspace {
|
|
|
43
48
|
workerLog(iter) { return (0, fsUtils_1.joinPath)(root, `worker.${iter}.log`); },
|
|
44
49
|
buildLog(iter) { return (0, fsUtils_1.joinPath)(root, `build.${iter}.log`); },
|
|
45
50
|
testLog(iter) { return (0, fsUtils_1.joinPath)(root, `test.${iter}.log`); },
|
|
46
|
-
reviewerLog(iter) { return (0, fsUtils_1.joinPath)(root, `reviewer.${iter}.log`); }
|
|
51
|
+
reviewerLog(iter) { return (0, fsUtils_1.joinPath)(root, `reviewer.${iter}.log`); },
|
|
52
|
+
reviewerOutputLog(iter, reviewerIndex) { return (0, fsUtils_1.joinPath)(root, `reviewer.${iter}.${reviewerIndex}.log`); },
|
|
53
|
+
reviewerErrorLog(reviewerIndex) { return (0, fsUtils_1.joinPath)(reviewerRoots[reviewerIndex - 1], "error.log"); }
|
|
47
54
|
};
|
|
48
55
|
}
|
|
56
|
+
async errorLogExists() {
|
|
57
|
+
const paths = this.paths();
|
|
58
|
+
return await this._fs.exists(paths.errorLog);
|
|
59
|
+
}
|
|
60
|
+
async readErrorLog() {
|
|
61
|
+
const paths = this.paths();
|
|
62
|
+
if (!await this._fs.exists(paths.errorLog)) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
return await this._fs.readFile(paths.errorLog);
|
|
66
|
+
}
|
|
49
67
|
async writeErrorLog(content) {
|
|
50
68
|
const paths = this.paths();
|
|
51
69
|
await this._fs.writeFile(paths.errorLog, content);
|
|
@@ -56,14 +74,42 @@ class Workspace {
|
|
|
56
74
|
await this._fs.rm(paths.errorLog, { force: true });
|
|
57
75
|
}
|
|
58
76
|
}
|
|
77
|
+
async reviewerErrorLogExists(reviewerIndex) {
|
|
78
|
+
const paths = this.paths();
|
|
79
|
+
return await this._fs.exists(paths.reviewerErrorLog(reviewerIndex));
|
|
80
|
+
}
|
|
81
|
+
async readReviewerErrorLog(reviewerIndex) {
|
|
82
|
+
const paths = this.paths();
|
|
83
|
+
const path = paths.reviewerErrorLog(reviewerIndex);
|
|
84
|
+
if (!await this._fs.exists(path)) {
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
return await this._fs.readFile(path);
|
|
88
|
+
}
|
|
89
|
+
async clearReviewerErrorLog(reviewerIndex) {
|
|
90
|
+
const paths = this.paths();
|
|
91
|
+
const path = paths.reviewerErrorLog(reviewerIndex);
|
|
92
|
+
if (await this._fs.exists(path)) {
|
|
93
|
+
await this._fs.rm(path, { force: true });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
59
96
|
async dispose() {
|
|
60
97
|
if (this._disposed) {
|
|
61
98
|
return;
|
|
62
99
|
}
|
|
63
100
|
this._disposed = true;
|
|
64
101
|
const root = this._root;
|
|
102
|
+
const reviewerRoots = this._reviewerRoots;
|
|
65
103
|
this._root = null;
|
|
104
|
+
this._reviewerRoots = [];
|
|
66
105
|
if (root && !this._preserve) {
|
|
106
|
+
for (let i = reviewerRoots.length - 1; i >= 0; i--) {
|
|
107
|
+
try {
|
|
108
|
+
await this._fs.rm(reviewerRoots[i], { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
67
113
|
try {
|
|
68
114
|
await this._fs.rm(root, { recursive: true, force: true });
|
|
69
115
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flanders",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Stubborn and smart plan implementation looper using AI",
|
|
5
5
|
"main": "lib/",
|
|
6
6
|
"types": "lib/types",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"watch": "npm run clean && npx tsc -p tsconfig.json -watch",
|
|
23
23
|
"build": "npm run clean && npx tsc -p tsconfig.build.json",
|
|
24
24
|
"build-debug": "npm run clean && npx tsc -p tsconfig.json",
|
|
25
|
-
"test": "npm run build-debug && npx aaa"
|
|
25
|
+
"test": "npm run build-debug && npx aaa --folder lib --include-files \"\\.test\\.js$\" --exclude-files node_modules --coverage-exclude node_modules"
|
|
26
26
|
},
|
|
27
27
|
"author": "Llorx",
|
|
28
28
|
"license": "MIT",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"homepage": "https://github.com/Llorx/flanders#readme",
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^22.7.8",
|
|
39
|
+
"@xterm/headless": "^6.0.0",
|
|
39
40
|
"arrange-act-assert": "^2.11.1",
|
|
40
41
|
"typescript": "^6.0.3"
|
|
41
42
|
}
|
package/lib/Claude.d.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import type { ClaudeContext, TimeContext } from "./contexts";
|
|
2
|
-
export type ClaudeResult = Readonly<{
|
|
3
|
-
text: string;
|
|
4
|
-
sessionId: string | null;
|
|
5
|
-
inputTokens: number;
|
|
6
|
-
outputTokens: number;
|
|
7
|
-
}>;
|
|
8
|
-
export type ClaudeContentBlock = Readonly<{
|
|
9
|
-
type?: string;
|
|
10
|
-
text?: string;
|
|
11
|
-
thinking?: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
id?: string;
|
|
14
|
-
input?: Readonly<Record<string, unknown>>;
|
|
15
|
-
tool_use_id?: string;
|
|
16
|
-
content?: string | ReadonlyArray<Readonly<{
|
|
17
|
-
type?: string;
|
|
18
|
-
text?: string;
|
|
19
|
-
}>>;
|
|
20
|
-
is_error?: boolean;
|
|
21
|
-
}>;
|
|
22
|
-
export type ClaudeDelta = Readonly<{
|
|
23
|
-
type?: string;
|
|
24
|
-
text?: string;
|
|
25
|
-
partial_json?: string;
|
|
26
|
-
}>;
|
|
27
|
-
export type ClaudeControlRequestBody = Readonly<{
|
|
28
|
-
subtype?: string;
|
|
29
|
-
request_id?: string;
|
|
30
|
-
tool_name?: string;
|
|
31
|
-
input?: unknown;
|
|
32
|
-
tool_input?: unknown;
|
|
33
|
-
}>;
|
|
34
|
-
export type ClaudeEvent = Readonly<{
|
|
35
|
-
type?: string;
|
|
36
|
-
subtype?: string;
|
|
37
|
-
is_error?: boolean;
|
|
38
|
-
api_error_status?: number | null;
|
|
39
|
-
session_id?: string;
|
|
40
|
-
index?: number;
|
|
41
|
-
request_id?: string;
|
|
42
|
-
request?: ClaudeControlRequestBody;
|
|
43
|
-
message?: Readonly<{
|
|
44
|
-
role?: string;
|
|
45
|
-
content?: ReadonlyArray<ClaudeContentBlock>;
|
|
46
|
-
usage?: Readonly<{
|
|
47
|
-
input_tokens?: number;
|
|
48
|
-
output_tokens?: number;
|
|
49
|
-
cache_creation_input_tokens?: number;
|
|
50
|
-
cache_read_input_tokens?: number;
|
|
51
|
-
}>;
|
|
52
|
-
}>;
|
|
53
|
-
content_block?: ClaudeContentBlock;
|
|
54
|
-
delta?: ClaudeDelta;
|
|
55
|
-
event?: ClaudeEvent;
|
|
56
|
-
error?: Readonly<{
|
|
57
|
-
message?: string;
|
|
58
|
-
}>;
|
|
59
|
-
result?: string;
|
|
60
|
-
usage?: Readonly<{
|
|
61
|
-
input_tokens?: number;
|
|
62
|
-
output_tokens?: number;
|
|
63
|
-
cache_creation_input_tokens?: number;
|
|
64
|
-
cache_read_input_tokens?: number;
|
|
65
|
-
}>;
|
|
66
|
-
rate_limit_info?: Readonly<{
|
|
67
|
-
status?: string;
|
|
68
|
-
resetsAt?: number;
|
|
69
|
-
rateLimitType?: string;
|
|
70
|
-
isUsingOverage?: boolean;
|
|
71
|
-
overageStatus?: string;
|
|
72
|
-
overageResetsAt?: number;
|
|
73
|
-
utilization?: number;
|
|
74
|
-
surpassedThreshold?: number;
|
|
75
|
-
}>;
|
|
76
|
-
}>;
|
|
77
|
-
export type PermissionRequest = Readonly<{
|
|
78
|
-
request_id: string;
|
|
79
|
-
tool_name: string;
|
|
80
|
-
tool_input: unknown;
|
|
81
|
-
}>;
|
|
82
|
-
export type PermissionResponse = Readonly<{
|
|
83
|
-
behavior: "allow";
|
|
84
|
-
updatedInput?: unknown;
|
|
85
|
-
updated_permissions?: readonly unknown[];
|
|
86
|
-
}> | Readonly<{
|
|
87
|
-
behavior: "deny";
|
|
88
|
-
message: string;
|
|
89
|
-
interrupt?: boolean;
|
|
90
|
-
}>;
|
|
91
|
-
export type ClaudeRunOptions = Readonly<{
|
|
92
|
-
prompt: string;
|
|
93
|
-
cwd?: string;
|
|
94
|
-
initialSessionId?: string | null;
|
|
95
|
-
forkFromSessionId?: string | null;
|
|
96
|
-
onEvent?(event: ClaudeEvent): void;
|
|
97
|
-
onStderr?(chunk: string): void;
|
|
98
|
-
onPermissionRequest?(req: PermissionRequest): Promise<PermissionResponse>;
|
|
99
|
-
onLongWaitStart?(kind: "rate-limit", endTimeMs: number): void;
|
|
100
|
-
onLongWaitEnd?(): void;
|
|
101
|
-
}>;
|
|
102
|
-
export declare class NonRetryableError extends Error {
|
|
103
|
-
constructor(message: string);
|
|
104
|
-
}
|
|
105
|
-
export declare class Claude {
|
|
106
|
-
readonly options: ClaudeRunOptions;
|
|
107
|
-
private _context;
|
|
108
|
-
private _time;
|
|
109
|
-
private _disposed;
|
|
110
|
-
private _process;
|
|
111
|
-
private _waitAbort;
|
|
112
|
-
private _runPromise;
|
|
113
|
-
private _sessionId;
|
|
114
|
-
private _forkFromSessionId;
|
|
115
|
-
private _killTimer;
|
|
116
|
-
private _transientAttempt;
|
|
117
|
-
constructor(options: ClaudeRunOptions, _context: ClaudeContext, _time: TimeContext);
|
|
118
|
-
result(): Promise<ClaudeResult>;
|
|
119
|
-
sendUserMessage(content: string): void;
|
|
120
|
-
sendControlResponse(requestId: string, response: PermissionResponse): void;
|
|
121
|
-
endSession(): void;
|
|
122
|
-
private _writeStdin;
|
|
123
|
-
private _run;
|
|
124
|
-
private _runOnce;
|
|
125
|
-
private _attachStreamParser;
|
|
126
|
-
private _scheduleWait;
|
|
127
|
-
private _handleControlRequest;
|
|
128
|
-
dispose(): Promise<void>;
|
|
129
|
-
}
|