@evandu/super-pi 0.0.0-stage → 0.1.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.
Files changed (59) hide show
  1. package/README.md +79 -2
  2. package/agents/coder.md +10 -0
  3. package/agents/critic.md +20 -0
  4. package/agents/patcher.md +23 -0
  5. package/agents/planner.md +18 -0
  6. package/agents/reviewer.md +13 -0
  7. package/agents/scout.md +23 -0
  8. package/agents/super-pi-task/core.ts +485 -0
  9. package/agents/super-pi-task/index.ts +141 -0
  10. package/agents/verifier.md +21 -0
  11. package/bin/super-pi.js +17 -0
  12. package/dist/client/assets/index-BMQsEdio.js +24 -0
  13. package/dist/client/assets/index-CsbZzuMy.css +1 -0
  14. package/dist/client/index.html +14 -0
  15. package/dist/extension.d.ts +15 -0
  16. package/dist/extension.js +111 -0
  17. package/dist/server/agents.d.ts +177 -0
  18. package/dist/server/agents.js +484 -0
  19. package/dist/server/application/catalog-service.d.ts +44 -0
  20. package/dist/server/application/catalog-service.js +94 -0
  21. package/dist/server/application/conversation-service.d.ts +73 -0
  22. package/dist/server/application/conversation-service.js +202 -0
  23. package/dist/server/application/extension-ui-service.d.ts +28 -0
  24. package/dist/server/application/extension-ui-service.js +73 -0
  25. package/dist/server/application/run-service.d.ts +115 -0
  26. package/dist/server/application/run-service.js +341 -0
  27. package/dist/server/application/workspace-service.d.ts +45 -0
  28. package/dist/server/application/workspace-service.js +106 -0
  29. package/dist/server/command-handler.d.ts +22 -0
  30. package/dist/server/command-handler.js +137 -0
  31. package/dist/server/files.d.ts +202 -0
  32. package/dist/server/files.js +629 -0
  33. package/dist/server/http-server.d.ts +20 -0
  34. package/dist/server/http-server.js +313 -0
  35. package/dist/server/index.d.ts +1 -0
  36. package/dist/server/index.js +103 -0
  37. package/dist/server/pi-command.d.ts +65 -0
  38. package/dist/server/pi-command.js +183 -0
  39. package/dist/server/runtime.d.ts +819 -0
  40. package/dist/server/runtime.js +1470 -0
  41. package/dist/server/server-context.d.ts +46 -0
  42. package/dist/server/server-context.js +1 -0
  43. package/dist/server/store.d.ts +424 -0
  44. package/dist/server/store.js +1121 -0
  45. package/dist/server/subagent-runs.d.ts +142 -0
  46. package/dist/server/subagent-runs.js +315 -0
  47. package/dist/server/subagents.d.ts +194 -0
  48. package/dist/server/subagents.js +542 -0
  49. package/dist/server/task-tool.d.ts +196 -0
  50. package/dist/server/task-tool.js +285 -0
  51. package/dist/server/tool-calls.d.ts +62 -0
  52. package/dist/server/tool-calls.js +141 -0
  53. package/dist/server/types.d.ts +920 -0
  54. package/dist/server/types.js +11 -0
  55. package/dist/server/upload.d.ts +37 -0
  56. package/dist/server/upload.js +169 -0
  57. package/dist/server/ws-server.d.ts +45 -0
  58. package/dist/server/ws-server.js +116 -0
  59. package/package.json +115 -4
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Above this a read is refused rather than served. 5MB is roughly a large source file or a long document,
3
+ * and it is deliberately far below the point where an editor tab stops being useful: this panel is for
4
+ * reading and editing code and markdown, and a file too big for that has not become more editable by
5
+ * being loaded whole. The size is checked from `stat` BEFORE the read, so an oversized file costs one
6
+ * `stat` rather than a full buffer.
7
+ */
8
+ export declare const MAX_FILE_BYTES: number;
9
+ /** Cap on one directory listing, so a `node_modules` or a generated assets folder cannot produce a
10
+ * multi-megabyte response. The count is reported as `truncated` rather than applied silently. */
11
+ export declare const MAX_DIR_ENTRIES = 2000;
12
+ /** Cap on a recursive filename search, for the same reason: a scan of a large repository has to end
13
+ * somewhere. The UI says the list was cut rather than pretending it is complete. */
14
+ export declare const MAX_SEARCH_RESULTS = 500;
15
+ /** How deep the recursive search walks. Twelve levels is past any real source tree; the cap is what
16
+ * turns a pathologically deep dependency tree from a hang into a result. */
17
+ export declare const MAX_SEARCH_DEPTH = 12;
18
+ /**
19
+ * Extensions this panel OPENS without needing a sniff. It is a hint used only to label a file in the tree
20
+ * before it is read, and never as a permission: a `.png` is refused by name even if a sniffing pass would
21
+ * have liked it, and a `.txt` holding PNG bytes is still refused by the sniff.
22
+ */
23
+ export declare const TEXT_EXTENSIONS: Set<string>;
24
+ /** The HTTP failure, carried as a status so the route can render it without a second switch. `details` is
25
+ * for the cases where the CLIENT can act on more than the sentence - today that is a conflict, where the
26
+ * current etag is what lets the UI offer a deliberate "overwrite anyway" instead of a blind one. */
27
+ export declare class FilesError extends Error {
28
+ readonly status: number;
29
+ readonly code?: string | undefined;
30
+ readonly details?: unknown;
31
+ constructor(status: number, message: string, code?: string | undefined, details?: unknown);
32
+ }
33
+ /**
34
+ * Whether `candidate` is `root` itself or something under it.
35
+ *
36
+ * THE COMPARISON IS NOT `startsWith(root)`, because that is the traversal bug this whole file exists to
37
+ * avoid: with root `/home/u/proj`, the string `/home/u/proj-secrets` also starts with `/home/u/proj`.
38
+ * `relative()` asks the question in the form that can be answered exactly - it returns a path RELATIVE to
39
+ * the root, so "escaped" becomes "the relative path begins by going up", which is true for
40
+ * `/home/u/proj/../etc` and false for `/home/u/proj/etc`. `isAbsolute(relative)` catches the case
41
+ * `relative` cannot express: two different Windows drives, where there is no relative path between them
42
+ * and it hands back an absolute one.
43
+ */
44
+ export declare function contains(root: string, candidate: string): boolean;
45
+ /** A guard bound to one workspace root. `resolveInsideRoot` is the one-shot form and is what callers are
46
+ * meant to use; the factory exists for the two places that check many paths against the SAME root in one
47
+ * request, so the canonical form of the root is resolved once instead of once per entry. */
48
+ export declare function createRootGuard(root: string): (requested: string | null | undefined) => Promise<string>;
49
+ /**
50
+ * THE CONTAINMENT CHECK. Every read, write, listing and search in this file goes through it, and the
51
+ * route layer is not allowed to do its own path arithmetic.
52
+ *
53
+ * `requested` arrives already percent-decoded, because `URLSearchParams` decodes once when the query is
54
+ * parsed. That is the right number of decodes: `%2e%2e%2f` is `../` by the time it gets here and is
55
+ * refused by the lexical test, while decoding a SECOND time would turn the harmless literal `%2e%2e%2f`
56
+ * into a live traversal - inventing an attack the URL parser does not actually have. The route decodes
57
+ * nothing further and never passes the raw query string down.
58
+ */
59
+ export declare function resolveInsideRoot(root: string, requested: string | null | undefined): Promise<string>;
60
+ /**
61
+ * The identity of a file's CURRENT bytes, as far as a filesystem can cheaply say.
62
+ *
63
+ * Built from size and the nanosecond modification time rather than a content hash: hashing every file on
64
+ * every read would make opening a tab cost a full pass over the file, and the two fields together already
65
+ * change on any edit a human would call an edit. `bigint: true` is what makes this exact - the
66
+ * millisecond float `stat` reports by default rounds two edits inside the same millisecond into one
67
+ * value, and an external-change check that misses a change is worse than no check at all, because the
68
+ * user has been told it is safe to save.
69
+ */
70
+ export declare function fileEtag(path: string): Promise<string>;
71
+ export type FileMeta = {
72
+ etag: string;
73
+ size: number;
74
+ modifiedAt: string;
75
+ };
76
+ export declare function fileMeta(path: string): Promise<FileMeta>;
77
+ export declare function looksBinary(sample: Uint8Array): boolean;
78
+ /**
79
+ * Whether the NAME alone says this is binary.
80
+ *
81
+ * `endsWith` is the whole test, and the dot is what makes it a suffix test rather than a substring one:
82
+ * `"tool.bin".endsWith(".bin")` is true and `"foobin".endsWith(".bin")` is false, because the dot has to be
83
+ * one of the characters being matched. An earlier version added a "the character before the suffix must be
84
+ * a dot" guard on top of this, which was wrong - it compared the index of the dot AGAINST ITSELF minus
85
+ * one, so `"photo.png"` was checked at the `o` and came back false. Every binary file was therefore
86
+ * reaching the byte sniff instead of being refused by name, and the tests only ever passed because the
87
+ * fixture binaries also contained NUL bytes. A check that is dead code while a second check quietly does
88
+ * its job is exactly the kind of thing that survives review, which is why `files-api.test.mjs` asserts
89
+ * the extension path with a fixture whose CONTENT is text.
90
+ */
91
+ export declare function hasBinaryExtension(name: string): boolean;
92
+ /** Whether a file is worth putting in an editor tab at all, decided from its NAME alone, for the tree's
93
+ * own labelling. The authoritative answer comes from `readWorkspaceFile`, which reads bytes. */
94
+ export declare function isTextName(name: string): boolean;
95
+ /** What a caller is told about one entry in a directory. */
96
+ export type DirEntry = {
97
+ /** The path RELATIVE to the workspace root, always `/`-separated, never with a leading slash. This is
98
+ * the form every client sends back, so there is exactly one spelling of "a file" on the wire and no
99
+ * round trip can invent a second one. */
100
+ path: string;
101
+ name: string;
102
+ kind: "directory" | "file" | "symlink";
103
+ /** Whether a file is worth opening, decided from its name. `null` for a directory. */
104
+ text: boolean | null;
105
+ /** Set when this entry is a link, and `kind` becomes `symlink` when it points OUT of the workspace. The
106
+ * tree can then say "cannot be opened" before the request comes back 403 rather than after. */
107
+ symlink?: boolean;
108
+ /** Set when a link points outside: the containment check's own verdict, surfaced rather than swallowed. */
109
+ outside?: boolean;
110
+ };
111
+ export type DirListing = {
112
+ /** The directory that was listed, relative to the root. `""` is the root itself. */
113
+ path: string;
114
+ /** Its parent, or `null` at the root - the tree uses this to decide whether to draw an "up" affordance. */
115
+ parent: string | null;
116
+ entries: DirEntry[];
117
+ truncated: boolean;
118
+ };
119
+ /** The `/`-separated relative form used on the wire and in every comparison. */
120
+ export declare function toRelative(root: string, absolute: string): string;
121
+ /** One directory's contents. Directories sort before files, then case-insensitively by name with numeric
122
+ * ordering, so `src2` lands between `src` and `src10` rather than after every `srcN`. */
123
+ export declare function listDirectory(root: string, requested: string | null | undefined): Promise<DirListing>;
124
+ export type SearchHit = {
125
+ path: string;
126
+ name: string;
127
+ directory: string;
128
+ };
129
+ export type SearchResult = {
130
+ hits: SearchHit[];
131
+ truncated: boolean;
132
+ skipped: string[];
133
+ };
134
+ /**
135
+ * A recursive FILENAME search. Filename only, on purpose: content search is a different feature with
136
+ * different costs - it reads every candidate file, honours the size cap on each one, and it is the thing
137
+ * that turns a panel into a hang - and the requirement is explicit that this round does not need it.
138
+ *
139
+ * Every directory it descends into goes through the SAME guard as every other path here, so the search
140
+ * cannot be walked out of the workspace by a link, and it stops at `MAX_SEARCH_DEPTH` and
141
+ * `MAX_SEARCH_RESULTS` so a repository with a vendored dependency tree returns a truncated answer rather
142
+ * than never returning.
143
+ */
144
+ export declare function searchByName(root: string, query: string): Promise<SearchResult>;
145
+ export type FileContents = {
146
+ path: string;
147
+ content: string;
148
+ meta: FileMeta;
149
+ /** Newline convention read off the bytes, so a CRLF file is saved back as CRLF instead of silently
150
+ * rewritten to LF - which on Windows is a whole-file diff the user did not ask for. */
151
+ eol: "\n" | "\r\n";
152
+ };
153
+ /**
154
+ * Read one file, or refuse it with a reason a person can act on.
155
+ *
156
+ * ORDER MATTERS and is the whole reason the refusals are trustworthy: extension, then SIZE from `stat`,
157
+ * then a sniff, then a fatal decode. The size check precedes the read so an oversized file costs a `stat`
158
+ * rather than a buffer, and the sniff precedes the decode so binary bytes are never handed to
159
+ * `TextDecoder` as if they were text - a decode that "succeeds" on binary produces U+FFFD throughout,
160
+ * which is mojibake, and mojibake that then gets SAVED is how a text editor destroys a binary file.
161
+ */
162
+ export declare function readWorkspaceFile(root: string, requested: string): Promise<FileContents>;
163
+ export type WriteOutcome = {
164
+ path: string;
165
+ meta: FileMeta;
166
+ created: false;
167
+ };
168
+ /**
169
+ * Write one EXISTING text file. No creation, no deletion, no rename, no directories.
170
+ *
171
+ * THE SCOPE CALL, with its reasoning, because it is a decision and not an omission:
172
+ *
173
+ * This panel's stated job is to BROWSE and EDIT what is in the workspace. Creation is refused even though
174
+ * the brief leaves it open, because creation is the operation where a mistake has no undo: a bad `path`
175
+ * parameter or a bug that loses one character writes a file the user never asked for and never has to
176
+ * clean up, whereas a refused write leaves nothing behind. Deletion is deferred by the brief and is
177
+ * deferred here for the same reason plus the one that matters more - it is irreversible.
178
+ *
179
+ * The symmetry is deliberate: create and delete are the two ends of the same axis, and shipping one
180
+ * without the other is how a "conservative, no surprises" surface becomes a half-built file manager.
181
+ *
182
+ * What IS refused regardless: a write to a file that does not exist (428), a write with no `baseEtag` at
183
+ * all (428 - without one the server cannot tell a save from a blind overwrite, and "may I overwrite" is
184
+ * not a question this endpoint is allowed to skip), a write whose `baseEtag` no longer matches the file
185
+ * on disk (409 `stale`), and a write that would push the file past the size cap.
186
+ *
187
+ * The write itself is a whole-file replace through a temporary file in the SAME directory and a rename,
188
+ * so an interrupted save cannot leave a half-written file where a whole one used to be. The temporary name
189
+ * is derived from the target's OWN name rather than living in the system temp directory, because a rename
190
+ * across filesystems is not atomic and would silently become a copy.
191
+ */
192
+ export declare function writeWorkspaceFile(root: string, requested: string, content: string, baseEtag: string | null | undefined): Promise<WriteOutcome>;
193
+ /** Every `FilesError` carries the status it should be rendered with; anything else is a real fault and
194
+ * becomes a 500 rather than being dressed up as a refusal the user is asked to understand. */
195
+ export declare function describe(error: unknown): {
196
+ status: number;
197
+ body: {
198
+ error: string;
199
+ code?: string;
200
+ [key: string]: unknown;
201
+ };
202
+ };