@vextlabs/theron-cli 0.1.0 → 0.2.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/README.md +85 -34
- package/dist/api.d.ts +6 -0
- package/dist/api.js +4 -0
- package/dist/api.js.map +1 -1
- package/dist/banner.d.ts +8 -1
- package/dist/banner.js +15 -2
- package/dist/banner.js.map +1 -1
- package/dist/connections.js +1 -1
- package/dist/connections.js.map +1 -1
- package/dist/file_refs.d.ts +38 -0
- package/dist/file_refs.js +219 -0
- package/dist/file_refs.js.map +1 -0
- package/dist/import_claude.js +1 -1
- package/dist/import_claude.js.map +1 -1
- package/dist/index.js +114 -4
- package/dist/index.js.map +1 -1
- package/dist/onboard.js +2 -2
- package/dist/onboard.js.map +1 -1
- package/dist/project_memory.d.ts +42 -0
- package/dist/project_memory.js +158 -0
- package/dist/project_memory.js.map +1 -0
- package/dist/repl.d.ts +15 -2
- package/dist/repl.js +493 -46
- package/dist/repl.js.map +1 -1
- package/dist/sessions.d.ts +47 -0
- package/dist/sessions.js +200 -0
- package/dist/sessions.js.map +1 -0
- package/dist/slash_commands.d.ts +47 -0
- package/dist/slash_commands.js +194 -0
- package/dist/slash_commands.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +11 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/stoa.js +8 -8
- package/dist/tools/stoa.js.map +1 -1
- package/package.json +14 -10
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// project_memory — auto-load a per-project instructions file and inject
|
|
2
|
+
// it into every chat turn as system context. Theron's analogue to Claude
|
|
3
|
+
// Code's CLAUDE.md.
|
|
4
|
+
//
|
|
5
|
+
// Why this exists: a CLI that edits your repo should know the repo's
|
|
6
|
+
// rules (build command, code style, "never touch migrations/", the
|
|
7
|
+
// canonical model count) WITHOUT you re-typing them every session. Claude
|
|
8
|
+
// Code reads CLAUDE.md at the project root + a parent-dir cascade; this
|
|
9
|
+
// gives Theron the same superpower.
|
|
10
|
+
//
|
|
11
|
+
// Resolution (mirrors cap_config.ts's two-tier overlay + the verifiers'
|
|
12
|
+
// findTsconfig walk-up):
|
|
13
|
+
// 1. Global — ~/.theron/THERON.md (your cross-project house style).
|
|
14
|
+
// 2. Ancestors — walk UP from cwd (max 8 levels), and at each dir take
|
|
15
|
+
// the first of THERON.md / CLAUDE.md / AGENTS.md that exists. We stop
|
|
16
|
+
// at the FIRST ancestor that has a memory file so a sub-package's
|
|
17
|
+
// THERON.md wins over the monorepo root's (closest-wins, same spirit
|
|
18
|
+
// as project-local caps.json overriding the global one).
|
|
19
|
+
//
|
|
20
|
+
// THERON.md is preferred, then CLAUDE.md, then AGENTS.md — so a repo that
|
|
21
|
+
// already has a CLAUDE.md (most do) gets picked up with zero setup, and a
|
|
22
|
+
// Theron user can add a THERON.md to override it just for Theron.
|
|
23
|
+
//
|
|
24
|
+
// The loaded text rides into the request as a leading note (see repl.ts /
|
|
25
|
+
// api.ts `projectContext`). This is a pure client-side feature: no server
|
|
26
|
+
// change is required — worst case the server ignores the field and the
|
|
27
|
+
// note still travels in the messages array as plain context.
|
|
28
|
+
import fs from "node:fs";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
import process from "node:process";
|
|
31
|
+
/** Filenames we recognize as a project-memory file, in priority order.
|
|
32
|
+
* THERON.md is ours; CLAUDE.md / AGENTS.md are the de-facto conventions
|
|
33
|
+
* most repos already carry, so we read them too for zero-setup pickup. */
|
|
34
|
+
export const MEMORY_FILENAMES = ["THERON.md", "CLAUDE.md", "AGENTS.md"];
|
|
35
|
+
/** Hard cap on injected memory size. A runaway CLAUDE.md (some are huge)
|
|
36
|
+
* shouldn't blow the context window or the request body. We keep the
|
|
37
|
+
* head — the top of these files is almost always the important rules —
|
|
38
|
+
* and mark the truncation so the model knows it didn't see all of it. */
|
|
39
|
+
const MAX_MEMORY_BYTES = 32 * 1024;
|
|
40
|
+
/** How far up the directory tree we walk looking for a memory file.
|
|
41
|
+
* Matches findTsconfig's depth so behavior is consistent across the CLI. */
|
|
42
|
+
const MAX_WALK_DEPTH = 8;
|
|
43
|
+
const EMPTY = { content: "", sources: [], truncated: false };
|
|
44
|
+
/** Read a UTF-8 file, returning null on any error (missing / unreadable /
|
|
45
|
+
* not a regular file). Never throws — a bad memory file must not break
|
|
46
|
+
* the REPL. */
|
|
47
|
+
function readMemorySafe(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
const st = fs.statSync(filePath);
|
|
50
|
+
if (!st.isFile())
|
|
51
|
+
return null;
|
|
52
|
+
return fs.readFileSync(filePath, "utf8");
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Truncate to MAX_MEMORY_BYTES on a UTF-8 boundary, keeping the head. */
|
|
59
|
+
function clampBytes(text) {
|
|
60
|
+
const buf = Buffer.from(text, "utf8");
|
|
61
|
+
if (buf.length <= MAX_MEMORY_BYTES)
|
|
62
|
+
return { text, truncated: false };
|
|
63
|
+
// Slice on a byte boundary then drop a possibly-broken trailing
|
|
64
|
+
// multibyte char by re-decoding loosely.
|
|
65
|
+
const head = buf.subarray(0, MAX_MEMORY_BYTES).toString("utf8");
|
|
66
|
+
return { text: head, truncated: true };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Find the nearest project-memory file by walking up from `startDir`.
|
|
70
|
+
* At each ancestor, the first existing filename in MEMORY_FILENAMES wins.
|
|
71
|
+
* Returns the absolute path, or null if none found within MAX_WALK_DEPTH.
|
|
72
|
+
*
|
|
73
|
+
* Closest ancestor wins (a package's THERON.md beats the repo root's), so
|
|
74
|
+
* a monorepo can give each package its own rules.
|
|
75
|
+
*/
|
|
76
|
+
export function findProjectMemoryFile(startDir) {
|
|
77
|
+
let dir = path.resolve(startDir);
|
|
78
|
+
for (let i = 0; i < MAX_WALK_DEPTH; i++) {
|
|
79
|
+
for (const name of MEMORY_FILENAMES) {
|
|
80
|
+
const candidate = path.join(dir, name);
|
|
81
|
+
try {
|
|
82
|
+
if (fs.statSync(candidate).isFile())
|
|
83
|
+
return candidate;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// not here — keep looking
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const parent = path.dirname(dir);
|
|
90
|
+
if (parent === dir)
|
|
91
|
+
break; // hit filesystem root
|
|
92
|
+
dir = parent;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/** Resolve the path to the global memory file, or null when HOME is unset. */
|
|
97
|
+
function globalMemoryPath() {
|
|
98
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
99
|
+
return home ? path.join(home, ".theron", "THERON.md") : null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Load project memory for a given working directory.
|
|
103
|
+
*
|
|
104
|
+
* Layering (later sources are appended after earlier ones, so the
|
|
105
|
+
* project file — being more specific — has the model's freshest
|
|
106
|
+
* attention): global house style first, then the nearest project file.
|
|
107
|
+
*
|
|
108
|
+
* Always returns a value; on any failure it degrades to EMPTY so the
|
|
109
|
+
* REPL keeps working with no memory injected.
|
|
110
|
+
*/
|
|
111
|
+
export function loadProjectMemory(cwd = process.cwd()) {
|
|
112
|
+
const sources = [];
|
|
113
|
+
const chunks = [];
|
|
114
|
+
let truncatedAny = false;
|
|
115
|
+
const add = (label, filePath) => {
|
|
116
|
+
const raw = readMemorySafe(filePath);
|
|
117
|
+
if (raw === null)
|
|
118
|
+
return;
|
|
119
|
+
const trimmed = raw.trim();
|
|
120
|
+
if (!trimmed)
|
|
121
|
+
return; // empty/whitespace file — nothing to inject
|
|
122
|
+
const { text, truncated } = clampBytes(trimmed);
|
|
123
|
+
if (truncated)
|
|
124
|
+
truncatedAny = true;
|
|
125
|
+
sources.push(filePath);
|
|
126
|
+
chunks.push(`--- ${label}: ${filePath}${truncated ? " (truncated)" : ""} ---\n${text}`);
|
|
127
|
+
};
|
|
128
|
+
const gp = globalMemoryPath();
|
|
129
|
+
if (gp)
|
|
130
|
+
add("global memory", gp);
|
|
131
|
+
const projectFile = findProjectMemoryFile(cwd);
|
|
132
|
+
// Don't double-load if the project file IS the global file (e.g. you're
|
|
133
|
+
// sitting in ~/.theron).
|
|
134
|
+
if (projectFile && projectFile !== gp)
|
|
135
|
+
add("project memory", projectFile);
|
|
136
|
+
if (chunks.length === 0)
|
|
137
|
+
return EMPTY;
|
|
138
|
+
return {
|
|
139
|
+
content: chunks.join("\n\n"),
|
|
140
|
+
sources,
|
|
141
|
+
truncated: truncatedAny,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Wrap loaded memory in a clear system-context header for injection. The
|
|
146
|
+
* header tells the model exactly what this block is and that it's
|
|
147
|
+
* authoritative project guidance, not user chat. Returns "" when there's
|
|
148
|
+
* nothing to inject so callers can cheaply skip.
|
|
149
|
+
*/
|
|
150
|
+
export function formatProjectMemoryForRequest(mem) {
|
|
151
|
+
if (!mem.content)
|
|
152
|
+
return "";
|
|
153
|
+
return ("Project context (auto-loaded from a THERON.md / CLAUDE.md / AGENTS.md " +
|
|
154
|
+
"file — treat as authoritative instructions for this repository, " +
|
|
155
|
+
"the same weight as a system prompt):\n\n" +
|
|
156
|
+
mem.content);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=project_memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project_memory.js","sourceRoot":"","sources":["../src/project_memory.ts"],"names":[],"mappings":"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"}
|
package/dist/repl.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { renderMarkdown } from "./render.js";
|
|
2
1
|
interface ReplOptions {
|
|
3
2
|
apiUrl: string;
|
|
4
3
|
apiKey: string | undefined;
|
|
@@ -8,6 +7,20 @@ interface ReplOptions {
|
|
|
8
7
|
oneShot?: string;
|
|
9
8
|
/** Domain profile slug. Defaults to "code". */
|
|
10
9
|
profile?: string;
|
|
10
|
+
/** Start in plan mode — read-only tools, plan instruction, hard write deny. */
|
|
11
|
+
planMode?: boolean;
|
|
12
|
+
/** Headless mode: suppress interactive chrome; for json, buffer the
|
|
13
|
+
* answer and emit one JSON object at the end. Implies oneShot. */
|
|
14
|
+
headless?: boolean;
|
|
15
|
+
/** Output format for headless mode. */
|
|
16
|
+
outputFormat?: "text" | "json";
|
|
17
|
+
/** Resume the most-recent session for this cwd before the loop. */
|
|
18
|
+
continueSession?: boolean;
|
|
19
|
+
/** Resume a session (with resumeId, that one; else a picker). */
|
|
20
|
+
resumeSession?: boolean;
|
|
21
|
+
resumeId?: string;
|
|
22
|
+
/** Pretty-render the assistant's markdown at end of turn. */
|
|
23
|
+
renderMode?: boolean;
|
|
11
24
|
}
|
|
12
25
|
export declare function runRepl(opts: ReplOptions): Promise<number>;
|
|
13
|
-
export {
|
|
26
|
+
export {};
|