@phren/agent 0.0.3 → 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.
- package/dist/providers/codex.js +1 -3
- package/dist/system-prompt.js +11 -0
- package/dist/tui.js +114 -19
- package/package.json +2 -2
package/dist/providers/codex.js
CHANGED
|
@@ -117,7 +117,7 @@ export class CodexProvider {
|
|
|
117
117
|
maxOutputTokens;
|
|
118
118
|
model;
|
|
119
119
|
constructor(model, maxOutputTokens) {
|
|
120
|
-
this.model = model ?? "gpt-5.
|
|
120
|
+
this.model = model ?? "gpt-5.3-codex";
|
|
121
121
|
this.maxOutputTokens = maxOutputTokens ?? 8192;
|
|
122
122
|
}
|
|
123
123
|
async chat(system, messages, tools) {
|
|
@@ -126,7 +126,6 @@ export class CodexProvider {
|
|
|
126
126
|
model: this.model,
|
|
127
127
|
instructions: system,
|
|
128
128
|
input: toResponsesInput(system, messages),
|
|
129
|
-
max_output_tokens: this.maxOutputTokens,
|
|
130
129
|
store: false,
|
|
131
130
|
stream: true,
|
|
132
131
|
};
|
|
@@ -187,7 +186,6 @@ export class CodexProvider {
|
|
|
187
186
|
model: this.model,
|
|
188
187
|
instructions: system,
|
|
189
188
|
input: toResponsesInput(system, messages),
|
|
190
|
-
max_output_tokens: this.maxOutputTokens,
|
|
191
189
|
store: false,
|
|
192
190
|
stream: true,
|
|
193
191
|
include: ["reasoning.encrypted_content"],
|
package/dist/system-prompt.js
CHANGED
|
@@ -15,6 +15,17 @@ export function buildSystemPrompt(phrenContext, priorSummary) {
|
|
|
15
15
|
"- `phren_add_finding` saves insights for future sessions. Good findings: non-obvious patterns, decisions with rationale, error resolutions, architecture constraints. Bad findings: narration of what you did, obvious facts, secrets.",
|
|
16
16
|
"- `phren_get_tasks` shows tracked work items. Complete tasks with `phren_complete_task` when done.",
|
|
17
17
|
"",
|
|
18
|
+
"## Self-configuration",
|
|
19
|
+
"You ARE phren-agent. You can configure phren itself via shell commands:",
|
|
20
|
+
"- `phren init` — set up phren (MCP server, hooks, profiles)",
|
|
21
|
+
"- `phren add <path>` — register a project directory",
|
|
22
|
+
"- `phren config proactivity <level>` — set proactivity (high/medium/low)",
|
|
23
|
+
"- `phren config policy set <key> <value>` — configure retention, TTL, decay",
|
|
24
|
+
"- `phren hooks enable <tool>` — enable hooks for claude/copilot/cursor/codex",
|
|
25
|
+
"- `phren doctor --fix` — diagnose and self-heal",
|
|
26
|
+
"- `phren status` — check health",
|
|
27
|
+
"If the user asks you to configure phren, set up a project, or fix their install, use the shell tool to run these commands.",
|
|
28
|
+
"",
|
|
18
29
|
"## Rules",
|
|
19
30
|
"- Never write secrets, API keys, or PII to files or findings.",
|
|
20
31
|
"- Prefer `edit_file` over `write_file` for existing files.",
|
package/dist/tui.js
CHANGED
|
@@ -9,7 +9,11 @@ import { handleCommand } from "./commands.js";
|
|
|
9
9
|
import { renderMarkdown } from "./multi/markdown.js";
|
|
10
10
|
import { decodeDiffPayload, renderInlineDiff, DIFF_MARKER } from "./multi/diff-renderer.js";
|
|
11
11
|
import * as os from "os";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
12
13
|
import { loadInputMode, saveInputMode, savePermissionMode } from "./settings.js";
|
|
14
|
+
import { createRequire } from "node:module";
|
|
15
|
+
const _require = createRequire(import.meta.url);
|
|
16
|
+
const AGENT_VERSION = _require("../package.json").version;
|
|
13
17
|
// ── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
14
18
|
const ESC = "\x1b[";
|
|
15
19
|
const s = {
|
|
@@ -144,6 +148,7 @@ export async function startTui(config, spawner) {
|
|
|
144
148
|
let menuListCount = 0;
|
|
145
149
|
let menuFilterActive = false;
|
|
146
150
|
let menuFilterBuf = "";
|
|
151
|
+
let ctrlCCount = 0;
|
|
147
152
|
// ── Menu rendering ─────────────────────────────────────────────────────
|
|
148
153
|
async function renderMenu() {
|
|
149
154
|
const mod = await loadMenuModule();
|
|
@@ -170,7 +175,7 @@ export async function startTui(config, spawner) {
|
|
|
170
175
|
menuFilterBuf = "";
|
|
171
176
|
w.write("\x1b[?1049l"); // leave alternate screen (restores chat)
|
|
172
177
|
statusBar();
|
|
173
|
-
prompt();
|
|
178
|
+
prompt(true); // skip newline — alt screen restore already positioned cursor
|
|
174
179
|
}
|
|
175
180
|
// Print status bar
|
|
176
181
|
function statusBar() {
|
|
@@ -179,12 +184,31 @@ export async function startTui(config, spawner) {
|
|
|
179
184
|
const bar = renderStatusBar(config.provider.name, config.phrenCtx?.project ?? null, session.turns, costStr, config.registry.permissionConfig.mode, spawner?.listAgents().length);
|
|
180
185
|
w.write(`${ESC}s${ESC}H${bar}${ESC}u`); // save cursor, move to top, print, restore
|
|
181
186
|
}
|
|
182
|
-
// Print prompt
|
|
183
|
-
|
|
187
|
+
// Print prompt — bordered input bar at bottom
|
|
188
|
+
let bashMode = false;
|
|
189
|
+
function prompt(skipNewline = false) {
|
|
190
|
+
if (!isTTY)
|
|
191
|
+
return;
|
|
184
192
|
const mode = config.registry.permissionConfig.mode;
|
|
185
193
|
const modeIcon = mode === "full-auto" ? "●" : mode === "auto-confirm" ? "◐" : "○";
|
|
186
194
|
const modeColor = mode === "full-auto" ? s.yellow : mode === "auto-confirm" ? s.green : s.cyan;
|
|
187
|
-
|
|
195
|
+
const rows = process.stdout.rows || 24;
|
|
196
|
+
const c = cols();
|
|
197
|
+
if (!skipNewline)
|
|
198
|
+
w.write("\n");
|
|
199
|
+
// Separator line + prompt on last 2 rows
|
|
200
|
+
const permLabel = PERMISSION_LABELS[mode];
|
|
201
|
+
const separator = s.dim("─".repeat(c));
|
|
202
|
+
const rightHints = s.dim(`${bashMode ? "! bash" : permLabel} · shift+tab to cycle · esc to interrupt`);
|
|
203
|
+
const rightLen = stripAnsi(rightHints).length;
|
|
204
|
+
const sepLine = s.dim("─".repeat(Math.max(1, c - rightLen - 1))) + " " + rightHints;
|
|
205
|
+
w.write(`${ESC}${rows - 1};1H${ESC}2K${sepLine}`);
|
|
206
|
+
if (bashMode) {
|
|
207
|
+
w.write(`${ESC}${rows};1H${ESC}2K${s.yellow("!")} `);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
w.write(`${ESC}${rows};1H${ESC}2K${modeColor(modeIcon)} ${s.dim("▸")} `);
|
|
211
|
+
}
|
|
188
212
|
}
|
|
189
213
|
// Terminal cleanup: restore state on exit
|
|
190
214
|
function cleanupTerminal() {
|
|
@@ -197,10 +221,9 @@ export async function startTui(config, spawner) {
|
|
|
197
221
|
}
|
|
198
222
|
}
|
|
199
223
|
process.on("exit", cleanupTerminal);
|
|
200
|
-
// Setup:
|
|
224
|
+
// Setup: clear screen, status bar at top, content area clean
|
|
201
225
|
if (isTTY) {
|
|
202
|
-
w.write(
|
|
203
|
-
w.write(`${ESC}1;1H`); // move to top
|
|
226
|
+
w.write(`${ESC}2J${ESC}H`); // clear entire screen + home
|
|
204
227
|
statusBar();
|
|
205
228
|
w.write(`${ESC}2;1H`); // move below status bar
|
|
206
229
|
// Startup banner
|
|
@@ -218,7 +241,7 @@ export async function startTui(config, spawner) {
|
|
|
218
241
|
if (artLines.length > 0) {
|
|
219
242
|
// Art on left, info on right
|
|
220
243
|
const info = [
|
|
221
|
-
`${s.brand("◆ phren agent")} ${s.dim("
|
|
244
|
+
`${s.brand("◆ phren agent")} ${s.dim("v${AGENT_VERSION}")}`,
|
|
222
245
|
`${s.dim(config.provider.name)}${project ? s.dim(` · ${project}`) : ""}`,
|
|
223
246
|
`${s.dim(cwd)}`,
|
|
224
247
|
``,
|
|
@@ -236,7 +259,7 @@ export async function startTui(config, spawner) {
|
|
|
236
259
|
}
|
|
237
260
|
else {
|
|
238
261
|
// Fallback: text-only banner
|
|
239
|
-
w.write(`\n ${s.brand("◆ phren agent")} ${s.dim("
|
|
262
|
+
w.write(`\n ${s.brand("◆ phren agent")} ${s.dim("v${AGENT_VERSION}")}\n`);
|
|
240
263
|
w.write(` ${s.dim(config.provider.name)}${project ? s.dim(` · ${project}`) : ""} ${s.dim(cwd)}\n`);
|
|
241
264
|
w.write(` ${modeColor(`${permMode === "full-auto" ? "●" : permMode === "auto-confirm" ? "◐" : "○"} ${permMode}`)} ${s.dim("permissions (shift+tab to cycle)")}\n\n`);
|
|
242
265
|
w.write(` ${s.dim("Tab")} memory ${s.dim("Shift+Tab")} perms ${s.dim("/help")} cmds ${s.dim("Ctrl+D")} exit\n\n`);
|
|
@@ -305,13 +328,12 @@ export async function startTui(config, spawner) {
|
|
|
305
328
|
process.stdin.on("keypress", (_ch, key) => {
|
|
306
329
|
if (!key)
|
|
307
330
|
return;
|
|
308
|
-
// Ctrl+D —
|
|
331
|
+
// Ctrl+D — clean exit
|
|
309
332
|
if (key.ctrl && key.name === "d") {
|
|
310
333
|
if (tuiMode === "menu")
|
|
311
|
-
w.write("\x1b[?1049l");
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
w.write(s.dim("\nSession ended.\n"));
|
|
334
|
+
w.write("\x1b[?1049l");
|
|
335
|
+
cleanupTerminal();
|
|
336
|
+
w.write(`\n${s.dim(`${session.turns} turns, ${session.toolCalls} tool calls.`)}\n`);
|
|
315
337
|
resolve(session);
|
|
316
338
|
return;
|
|
317
339
|
}
|
|
@@ -345,16 +367,57 @@ export async function startTui(config, spawner) {
|
|
|
345
367
|
return;
|
|
346
368
|
}
|
|
347
369
|
// ── Chat mode keys ──────────────────────────────────────────────────
|
|
348
|
-
//
|
|
370
|
+
// Escape — exit bash mode, or clear input
|
|
371
|
+
if (key.name === "escape") {
|
|
372
|
+
if (bashMode) {
|
|
373
|
+
bashMode = false;
|
|
374
|
+
inputLine = "";
|
|
375
|
+
prompt(true);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (inputLine) {
|
|
379
|
+
inputLine = "";
|
|
380
|
+
prompt(true);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Ctrl+C — progressive: cancel → warn → quit
|
|
349
385
|
if (key.ctrl && key.name === "c") {
|
|
350
386
|
if (running) {
|
|
387
|
+
// Cancel current agent turn
|
|
351
388
|
pendingInput = null;
|
|
352
389
|
w.write(s.yellow("\n [interrupted]\n"));
|
|
390
|
+
ctrlCCount = 0;
|
|
391
|
+
return;
|
|
353
392
|
}
|
|
354
|
-
|
|
393
|
+
if (bashMode) {
|
|
394
|
+
bashMode = false;
|
|
355
395
|
inputLine = "";
|
|
356
|
-
|
|
357
|
-
|
|
396
|
+
prompt(true);
|
|
397
|
+
ctrlCCount = 0;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (inputLine) {
|
|
401
|
+
// Clear input
|
|
402
|
+
inputLine = "";
|
|
403
|
+
prompt(true);
|
|
404
|
+
ctrlCCount = 0;
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
// Nothing to cancel — progressive quit
|
|
408
|
+
ctrlCCount++;
|
|
409
|
+
if (ctrlCCount === 1) {
|
|
410
|
+
w.write(s.dim("\n Press Ctrl+C again to exit.\n"));
|
|
411
|
+
prompt(true);
|
|
412
|
+
// Reset after 2 seconds
|
|
413
|
+
setTimeout(() => { ctrlCCount = 0; }, 2000);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
// Actually quit
|
|
417
|
+
if (process.stdin.isTTY)
|
|
418
|
+
process.stdin.setRawMode(false);
|
|
419
|
+
w.write(s.dim("\nSession ended.\n"));
|
|
420
|
+
resolve(session);
|
|
358
421
|
}
|
|
359
422
|
return;
|
|
360
423
|
}
|
|
@@ -367,6 +430,25 @@ export async function startTui(config, spawner) {
|
|
|
367
430
|
prompt();
|
|
368
431
|
return;
|
|
369
432
|
}
|
|
433
|
+
// Bash mode: ! prefix runs shell directly
|
|
434
|
+
if (line.startsWith("!") || bashMode) {
|
|
435
|
+
const cmd = bashMode ? line : line.slice(1).trim();
|
|
436
|
+
bashMode = false;
|
|
437
|
+
if (cmd) {
|
|
438
|
+
try {
|
|
439
|
+
const output = execSync(cmd, { encoding: "utf-8", timeout: 30_000, cwd: process.cwd(), stdio: ["ignore", "pipe", "pipe"] });
|
|
440
|
+
w.write(output);
|
|
441
|
+
if (!output.endsWith("\n"))
|
|
442
|
+
w.write("\n");
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
const e = err;
|
|
446
|
+
w.write(s.red(e.stderr || e.message || "Command failed") + "\n");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
prompt();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
370
452
|
// Slash commands
|
|
371
453
|
if (line === "/mode") {
|
|
372
454
|
inputMode = inputMode === "steering" ? "queue" : "steering";
|
|
@@ -375,7 +457,14 @@ export async function startTui(config, spawner) {
|
|
|
375
457
|
prompt();
|
|
376
458
|
return;
|
|
377
459
|
}
|
|
378
|
-
if (handleCommand(line, {
|
|
460
|
+
if (handleCommand(line, {
|
|
461
|
+
session,
|
|
462
|
+
contextLimit,
|
|
463
|
+
undoStack: [],
|
|
464
|
+
providerName: config.provider.name,
|
|
465
|
+
currentModel: config.provider.model,
|
|
466
|
+
spawner,
|
|
467
|
+
})) {
|
|
379
468
|
prompt();
|
|
380
469
|
return;
|
|
381
470
|
}
|
|
@@ -400,6 +489,12 @@ export async function startTui(config, spawner) {
|
|
|
400
489
|
}
|
|
401
490
|
// Regular character
|
|
402
491
|
if (key.sequence && !key.ctrl && !key.meta) {
|
|
492
|
+
// ! at start of empty input toggles bash mode
|
|
493
|
+
if (key.sequence === "!" && inputLine === "" && !bashMode) {
|
|
494
|
+
bashMode = true;
|
|
495
|
+
prompt(true);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
403
498
|
inputLine += key.sequence;
|
|
404
499
|
w.write(key.sequence);
|
|
405
500
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phren/agent",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Coding agent with persistent memory — powered by phren",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dist"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@phren/cli": "0.0
|
|
16
|
+
"@phren/cli": "0.1.0"
|
|
17
17
|
},
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">=20.0.0"
|