praana 0.5.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/LICENSE +21 -0
- package/README.md +124 -0
- package/bin/praana.js +17 -0
- package/bin/pran.js +17 -0
- package/dist/app-banner.d.ts +11 -0
- package/dist/app-banner.js +161 -0
- package/dist/app-controller.d.ts +44 -0
- package/dist/app-controller.js +143 -0
- package/dist/app-identity.d.ts +18 -0
- package/dist/app-identity.js +52 -0
- package/dist/auto-compact.d.ts +16 -0
- package/dist/auto-compact.js +101 -0
- package/dist/cli-args.d.ts +14 -0
- package/dist/cli-args.js +69 -0
- package/dist/compile-classic.d.ts +21 -0
- package/dist/compile-classic.js +106 -0
- package/dist/compiler.d.ts +75 -0
- package/dist/compiler.js +406 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +433 -0
- package/dist/context-engine/activity-log.d.ts +9 -0
- package/dist/context-engine/activity-log.js +109 -0
- package/dist/context-engine/artifact-store.d.ts +32 -0
- package/dist/context-engine/artifact-store.js +272 -0
- package/dist/context-engine/bm25.d.ts +3 -0
- package/dist/context-engine/bm25.js +32 -0
- package/dist/context-engine/checkpoint.d.ts +34 -0
- package/dist/context-engine/checkpoint.js +430 -0
- package/dist/context-engine/classify.d.ts +3 -0
- package/dist/context-engine/classify.js +60 -0
- package/dist/context-engine/db.d.ts +73 -0
- package/dist/context-engine/db.js +505 -0
- package/dist/context-engine/distiller.d.ts +30 -0
- package/dist/context-engine/distiller.js +67 -0
- package/dist/context-engine/engine-compiler.d.ts +23 -0
- package/dist/context-engine/engine-compiler.js +297 -0
- package/dist/context-engine/error-tracker.d.ts +21 -0
- package/dist/context-engine/error-tracker.js +74 -0
- package/dist/context-engine/event-lineage.d.ts +26 -0
- package/dist/context-engine/event-lineage.js +120 -0
- package/dist/context-engine/extraction.d.ts +26 -0
- package/dist/context-engine/extraction.js +83 -0
- package/dist/context-engine/index.d.ts +82 -0
- package/dist/context-engine/index.js +238 -0
- package/dist/context-engine/scoring.d.ts +13 -0
- package/dist/context-engine/scoring.js +47 -0
- package/dist/context-engine/state-snapshot.d.ts +8 -0
- package/dist/context-engine/state-snapshot.js +50 -0
- package/dist/context-engine/summarize.d.ts +6 -0
- package/dist/context-engine/summarize.js +32 -0
- package/dist/context-engine/telemetry.d.ts +25 -0
- package/dist/context-engine/telemetry.js +64 -0
- package/dist/context-engine/turn-digest.d.ts +50 -0
- package/dist/context-engine/turn-digest.js +250 -0
- package/dist/context-engine/turn-ledger.d.ts +18 -0
- package/dist/context-engine/turn-ledger.js +184 -0
- package/dist/context-engine/turn-recorder.d.ts +24 -0
- package/dist/context-engine/turn-recorder.js +88 -0
- package/dist/context-engine/types.d.ts +201 -0
- package/dist/context-engine/types.js +4 -0
- package/dist/context-pressure.d.ts +19 -0
- package/dist/context-pressure.js +36 -0
- package/dist/distillers/generic.d.ts +14 -0
- package/dist/distillers/generic.js +93 -0
- package/dist/distillers/git-diff.d.ts +8 -0
- package/dist/distillers/git-diff.js +119 -0
- package/dist/distillers/index.d.ts +2 -0
- package/dist/distillers/index.js +16 -0
- package/dist/distillers/npm-test.d.ts +8 -0
- package/dist/distillers/npm-test.js +50 -0
- package/dist/distillers/rg-results.d.ts +8 -0
- package/dist/distillers/rg-results.js +28 -0
- package/dist/distillers/tsc-errors.d.ts +8 -0
- package/dist/distillers/tsc-errors.js +52 -0
- package/dist/event-log.d.ts +56 -0
- package/dist/event-log.js +214 -0
- package/dist/llm.d.ts +29 -0
- package/dist/llm.js +155 -0
- package/dist/logger.d.ts +94 -0
- package/dist/logger.js +287 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +54 -0
- package/dist/memory/confidence.d.ts +7 -0
- package/dist/memory/confidence.js +37 -0
- package/dist/memory/consolidation.d.ts +26 -0
- package/dist/memory/consolidation.js +166 -0
- package/dist/memory/db.d.ts +40 -0
- package/dist/memory/db.js +283 -0
- package/dist/memory/dedup.d.ts +6 -0
- package/dist/memory/dedup.js +50 -0
- package/dist/memory/embedder-factory.d.ts +3 -0
- package/dist/memory/embedder-factory.js +81 -0
- package/dist/memory/embeddings.d.ts +15 -0
- package/dist/memory/embeddings.js +67 -0
- package/dist/memory/index.d.ts +9 -0
- package/dist/memory/index.js +11 -0
- package/dist/memory/ollama-summarizer.d.ts +19 -0
- package/dist/memory/ollama-summarizer.js +72 -0
- package/dist/memory/openai-summarizer.d.ts +21 -0
- package/dist/memory/openai-summarizer.js +51 -0
- package/dist/memory/store.d.ts +61 -0
- package/dist/memory/store.js +502 -0
- package/dist/memory/summarizer-factory.d.ts +3 -0
- package/dist/memory/summarizer-factory.js +69 -0
- package/dist/memory/summarizer.d.ts +4 -0
- package/dist/memory/summarizer.js +112 -0
- package/dist/memory/types.d.ts +87 -0
- package/dist/memory/types.js +17 -0
- package/dist/model-context.d.ts +15 -0
- package/dist/model-context.js +212 -0
- package/dist/project-detector.d.ts +37 -0
- package/dist/project-detector.js +604 -0
- package/dist/render.d.ts +15 -0
- package/dist/render.js +46 -0
- package/dist/session.d.ts +118 -0
- package/dist/session.js +809 -0
- package/dist/skills/index.d.ts +69 -0
- package/dist/skills/index.js +885 -0
- package/dist/skills/types.d.ts +93 -0
- package/dist/skills/types.js +8 -0
- package/dist/slash-commands.d.ts +14 -0
- package/dist/slash-commands.js +301 -0
- package/dist/state-graph.d.ts +38 -0
- package/dist/state-graph.js +255 -0
- package/dist/status-bar.d.ts +54 -0
- package/dist/status-bar.js +184 -0
- package/dist/thinking-display.d.ts +21 -0
- package/dist/thinking-display.js +37 -0
- package/dist/tool-summary.d.ts +4 -0
- package/dist/tool-summary.js +67 -0
- package/dist/tools/index.d.ts +925 -0
- package/dist/tools/index.js +86 -0
- package/dist/tools/knowledge.d.ts +140 -0
- package/dist/tools/knowledge.js +260 -0
- package/dist/tools/memory.d.ts +39 -0
- package/dist/tools/memory.js +300 -0
- package/dist/tools/search-code.d.ts +134 -0
- package/dist/tools/search-code.js +390 -0
- package/dist/tools/system.d.ts +16 -0
- package/dist/tools/system.js +499 -0
- package/dist/tools/tool-def.d.ts +6 -0
- package/dist/tools/tool-def.js +3 -0
- package/dist/turn-control.d.ts +51 -0
- package/dist/turn-control.js +210 -0
- package/dist/turn.d.ts +20 -0
- package/dist/turn.js +624 -0
- package/dist/types.d.ts +233 -0
- package/dist/types.js +4 -0
- package/dist/ui/readline-ui.d.ts +2 -0
- package/dist/ui/readline-ui.js +176 -0
- package/dist/ui/tui/app.d.ts +13 -0
- package/dist/ui/tui/app.js +270 -0
- package/dist/ui/tui/busy-indicator.d.ts +2 -0
- package/dist/ui/tui/busy-indicator.js +13 -0
- package/dist/ui/tui/components/gutter-rule.d.ts +5 -0
- package/dist/ui/tui/components/gutter-rule.js +9 -0
- package/dist/ui/tui/components/inline-tool-row.d.ts +10 -0
- package/dist/ui/tui/components/inline-tool-row.js +8 -0
- package/dist/ui/tui/components/prompt-input.d.ts +20 -0
- package/dist/ui/tui/components/prompt-input.js +120 -0
- package/dist/ui/tui/components/system-line.d.ts +5 -0
- package/dist/ui/tui/components/system-line.js +6 -0
- package/dist/ui/tui/components/thinking-block.d.ts +11 -0
- package/dist/ui/tui/components/thinking-block.js +31 -0
- package/dist/ui/tui/components/toast-line.d.ts +4 -0
- package/dist/ui/tui/components/toast-line.js +8 -0
- package/dist/ui/tui/components/tool-result-line.d.ts +5 -0
- package/dist/ui/tui/components/tool-result-line.js +6 -0
- package/dist/ui/tui/components/turn-footer.d.ts +5 -0
- package/dist/ui/tui/components/turn-footer.js +7 -0
- package/dist/ui/tui/components/user-block.d.ts +6 -0
- package/dist/ui/tui/components/user-block.js +6 -0
- package/dist/ui/tui/logo-banner.d.ts +5 -0
- package/dist/ui/tui/logo-banner.js +8 -0
- package/dist/ui/tui/markdown-render.d.ts +16 -0
- package/dist/ui/tui/markdown-render.js +218 -0
- package/dist/ui/tui/palette.d.ts +12 -0
- package/dist/ui/tui/palette.js +13 -0
- package/dist/ui/tui/reasoning-summary.d.ts +12 -0
- package/dist/ui/tui/reasoning-summary.js +27 -0
- package/dist/ui/tui/reducer.d.ts +92 -0
- package/dist/ui/tui/reducer.js +260 -0
- package/dist/ui/tui/run.d.ts +3 -0
- package/dist/ui/tui/run.js +40 -0
- package/dist/ui/tui/sink.d.ts +4 -0
- package/dist/ui/tui/sink.js +89 -0
- package/dist/ui/tui/status-bar-view.d.ts +5 -0
- package/dist/ui/tui/status-bar-view.js +44 -0
- package/dist/ui/tui/terminal-height.d.ts +12 -0
- package/dist/ui/tui/terminal-height.js +20 -0
- package/dist/ui/tui/terminal-width.d.ts +2 -0
- package/dist/ui/tui/terminal-width.js +5 -0
- package/dist/ui/tui/tool-display.d.ts +23 -0
- package/dist/ui/tui/tool-display.js +217 -0
- package/dist/ui/tui/transcript-line.d.ts +12 -0
- package/dist/ui/tui/transcript-line.js +43 -0
- package/dist/ui/tui/transcript-replay.d.ts +12 -0
- package/dist/ui/tui/transcript-replay.js +117 -0
- package/dist/ui-events.d.ts +39 -0
- package/dist/ui-events.js +33 -0
- package/dist/ui.d.ts +77 -0
- package/dist/ui.js +179 -0
- package/package.json +73 -0
- package/praana.config.example.toml +231 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { printDebug, printDebugBlock, printMemoryBanner, printToolCall, startSpinner, stopSpinner, } from "./ui.js";
|
|
2
|
+
/** Default sink: streaming callbacks + legacy terminal helpers. */
|
|
3
|
+
export function createDefaultTurnSink(options) {
|
|
4
|
+
return {
|
|
5
|
+
onTextDelta: (delta) => {
|
|
6
|
+
if (options?.onTextDelta)
|
|
7
|
+
options.onTextDelta(delta);
|
|
8
|
+
else
|
|
9
|
+
process.stdout.write(delta);
|
|
10
|
+
},
|
|
11
|
+
onThinkingDelta: (delta) => options?.onThinkingDelta?.(delta),
|
|
12
|
+
onToolCallsStart: () => options?.onToolCallsStart?.(),
|
|
13
|
+
onToolCall: (toolName, args) => printToolCall(toolName, args),
|
|
14
|
+
onToolResult: () => {
|
|
15
|
+
/* terminal mode doesn't need a separate result block; it streams naturally */
|
|
16
|
+
},
|
|
17
|
+
onDebug: (message) => printDebug(message),
|
|
18
|
+
onDebugBlock: (stepIndex, toolCalls, toolResults) => printDebugBlock(stepIndex, toolCalls, toolResults),
|
|
19
|
+
onMemoryBanner: (stats) => printMemoryBanner(stats),
|
|
20
|
+
onSpinnerStart: (text) => startSpinner(text),
|
|
21
|
+
onSpinnerStop: () => stopSpinner(),
|
|
22
|
+
onNewline: () => process.stdout.write("\n"),
|
|
23
|
+
onFallback: (text) => process.stdout.write(text + "\n"),
|
|
24
|
+
onError: (entry) => {
|
|
25
|
+
if (entry.level === "error" || entry.level === "warn") {
|
|
26
|
+
process.stderr.write(`[${entry.domain}] ${entry.message}\n`);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function hasTurnUiSink(sink) {
|
|
32
|
+
return sink !== undefined;
|
|
33
|
+
}
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal UI helpers for ARIA.
|
|
3
|
+
* Tool/status output goes to stderr; stdout gets blank-line breaks so
|
|
4
|
+
* agent text and debug blocks don't run together in the terminal.
|
|
5
|
+
*
|
|
6
|
+
* Color output is managed via chalk (respects NO_COLOR / non-TTY automatically).
|
|
7
|
+
* Spinner (ora) is only activated when stderr is a real TTY.
|
|
8
|
+
*
|
|
9
|
+
* Rich Markdown rendering is provided by marked + marked-terminal.
|
|
10
|
+
* Boxed layouts use boxen.
|
|
11
|
+
*/
|
|
12
|
+
type UiWriters = {
|
|
13
|
+
stderr: (line: string) => void;
|
|
14
|
+
breakStdout: () => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function setUiWriters(overrides?: Partial<UiWriters>): void;
|
|
17
|
+
/** Write a line to the UI stderr channel (tool diffs, ancillary output). */
|
|
18
|
+
export declare function writeUiStderr(line: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Start a spinner on stderr with the given text.
|
|
21
|
+
* No-op when stderr is not a TTY (tests, CI, piped output).
|
|
22
|
+
*/
|
|
23
|
+
export declare function startSpinner(text: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Stop and clear the active spinner.
|
|
26
|
+
* Safe to call when no spinner is running.
|
|
27
|
+
*/
|
|
28
|
+
export declare function stopSpinner(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Render content inside a styled box (using boxen).
|
|
31
|
+
* Output goes to stderr.
|
|
32
|
+
*/
|
|
33
|
+
export declare function printBox(content: string, options?: {
|
|
34
|
+
title?: string;
|
|
35
|
+
padding?: number;
|
|
36
|
+
borderColor?: "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray";
|
|
37
|
+
}): void;
|
|
38
|
+
/**
|
|
39
|
+
* Render Markdown text to terminal (writes to stderr).
|
|
40
|
+
*/
|
|
41
|
+
export declare function printMarkdown(text: string): void;
|
|
42
|
+
/** Compact tool indicator — shown in normal mode. */
|
|
43
|
+
export declare function printToolCall(toolName: string, args: Record<string, unknown>): void;
|
|
44
|
+
/**
|
|
45
|
+
* Render a complete debug block with all tool calls and results in a single
|
|
46
|
+
* styled box (using boxen). This is the preferred debug display — replaces
|
|
47
|
+
* the manual start/content/end sequence for a polished look.
|
|
48
|
+
*/
|
|
49
|
+
export declare function printDebugBlock(stepIndex: number, toolCalls: Array<{
|
|
50
|
+
toolName: string;
|
|
51
|
+
args: Record<string, unknown>;
|
|
52
|
+
}>, toolResults: Array<{
|
|
53
|
+
toolName: string;
|
|
54
|
+
result: unknown;
|
|
55
|
+
}>): void;
|
|
56
|
+
/** Debug block header before a batch of tool calls in a step. */
|
|
57
|
+
export declare function printToolBlockStart(stepIndex: number): void;
|
|
58
|
+
/** Debug tool call with full args. */
|
|
59
|
+
export declare function printToolCallDebug(toolName: string, args: Record<string, unknown>): void;
|
|
60
|
+
/** Debug tool result. */
|
|
61
|
+
export declare function printToolResultDebug(toolName: string, result: unknown): void;
|
|
62
|
+
/** Debug block footer. */
|
|
63
|
+
export declare function printToolBlockEnd(): void;
|
|
64
|
+
/** General debug message (prompt saved, etc.). */
|
|
65
|
+
export declare function printDebug(message: string): void;
|
|
66
|
+
/** Compact per-turn memory banner after each response. */
|
|
67
|
+
export declare function printMemoryBanner(stats: {
|
|
68
|
+
activeState: number;
|
|
69
|
+
totalState: number;
|
|
70
|
+
digestLen: number;
|
|
71
|
+
recallCalls: number;
|
|
72
|
+
recallHits: number;
|
|
73
|
+
autoHydrated: number;
|
|
74
|
+
promptTokens: number;
|
|
75
|
+
outputTokens: number;
|
|
76
|
+
}): void;
|
|
77
|
+
export {};
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal UI helpers for ARIA.
|
|
3
|
+
* Tool/status output goes to stderr; stdout gets blank-line breaks so
|
|
4
|
+
* agent text and debug blocks don't run together in the terminal.
|
|
5
|
+
*
|
|
6
|
+
* Color output is managed via chalk (respects NO_COLOR / non-TTY automatically).
|
|
7
|
+
* Spinner (ora) is only activated when stderr is a real TTY.
|
|
8
|
+
*
|
|
9
|
+
* Rich Markdown rendering is provided by marked + marked-terminal.
|
|
10
|
+
* Boxed layouts use boxen.
|
|
11
|
+
*/
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import ora from "ora";
|
|
14
|
+
import boxen from "boxen";
|
|
15
|
+
import { renderMarkdown } from "./render.js";
|
|
16
|
+
import { summarizeArgs, summarizeResult } from "./tool-summary.js";
|
|
17
|
+
const defaultWriters = {
|
|
18
|
+
stderr: (line) => process.stderr.write(line),
|
|
19
|
+
breakStdout: () => process.stdout.write("\n"),
|
|
20
|
+
};
|
|
21
|
+
let writers = defaultWriters;
|
|
22
|
+
let activeSpinner = null;
|
|
23
|
+
function stderr(line) {
|
|
24
|
+
writers.stderr(line);
|
|
25
|
+
}
|
|
26
|
+
/** Break the agent text flow on stdout before/after ancillary output. */
|
|
27
|
+
function breakStdout() {
|
|
28
|
+
writers.breakStdout();
|
|
29
|
+
}
|
|
30
|
+
export function setUiWriters(overrides) {
|
|
31
|
+
if (!overrides) {
|
|
32
|
+
writers = defaultWriters;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
writers = {
|
|
36
|
+
stderr: overrides.stderr ?? defaultWriters.stderr,
|
|
37
|
+
breakStdout: overrides.breakStdout ?? defaultWriters.breakStdout,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Write a line to the UI stderr channel (tool diffs, ancillary output). */
|
|
41
|
+
export function writeUiStderr(line) {
|
|
42
|
+
writers.stderr(line.endsWith("\n") ? line : line + "\n");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Start a spinner on stderr with the given text.
|
|
46
|
+
* No-op when stderr is not a TTY (tests, CI, piped output).
|
|
47
|
+
*/
|
|
48
|
+
export function startSpinner(text) {
|
|
49
|
+
if (!process.stderr.isTTY)
|
|
50
|
+
return;
|
|
51
|
+
stopSpinner();
|
|
52
|
+
activeSpinner = ora({ text, stream: process.stderr, discardStdin: false }).start();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Stop and clear the active spinner.
|
|
56
|
+
* Safe to call when no spinner is running.
|
|
57
|
+
*/
|
|
58
|
+
export function stopSpinner() {
|
|
59
|
+
if (!activeSpinner)
|
|
60
|
+
return;
|
|
61
|
+
activeSpinner.stop();
|
|
62
|
+
activeSpinner = null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Render content inside a styled box (using boxen).
|
|
66
|
+
* Output goes to stderr.
|
|
67
|
+
*/
|
|
68
|
+
export function printBox(content, options) {
|
|
69
|
+
if (!content)
|
|
70
|
+
return;
|
|
71
|
+
const opts = {
|
|
72
|
+
padding: options?.padding ?? 1,
|
|
73
|
+
margin: 0,
|
|
74
|
+
borderStyle: "round",
|
|
75
|
+
title: options?.title,
|
|
76
|
+
titleAlignment: "left",
|
|
77
|
+
...(options?.borderColor ? { borderColor: options.borderColor } : {}),
|
|
78
|
+
};
|
|
79
|
+
stderr(boxen(content, opts) + "\n");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Render Markdown text to terminal (writes to stderr).
|
|
83
|
+
*/
|
|
84
|
+
export function printMarkdown(text) {
|
|
85
|
+
if (!text)
|
|
86
|
+
return;
|
|
87
|
+
const rendered = renderMarkdown(text);
|
|
88
|
+
stderr(rendered);
|
|
89
|
+
if (!rendered.endsWith("\n"))
|
|
90
|
+
stderr("\n");
|
|
91
|
+
}
|
|
92
|
+
/** Compact tool indicator — shown in normal mode. */
|
|
93
|
+
export function printToolCall(toolName, args) {
|
|
94
|
+
breakStdout();
|
|
95
|
+
const summary = summarizeArgs(toolName, args);
|
|
96
|
+
stderr(`\n${chalk.dim("[tool]")} ${chalk.cyan(toolName)}${summary ? ` ${chalk.dim(`:: ${summary}`)}` : ""}\n`);
|
|
97
|
+
breakStdout();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Render a complete debug block with all tool calls and results in a single
|
|
101
|
+
* styled box (using boxen). This is the preferred debug display — replaces
|
|
102
|
+
* the manual start/content/end sequence for a polished look.
|
|
103
|
+
*/
|
|
104
|
+
export function printDebugBlock(stepIndex, toolCalls, toolResults) {
|
|
105
|
+
breakStdout();
|
|
106
|
+
const lines = [];
|
|
107
|
+
for (const tc of toolCalls) {
|
|
108
|
+
const argsJson = JSON.stringify(tc.args);
|
|
109
|
+
const display = argsJson.length > 200 ? argsJson.slice(0, 197) + "..." : argsJson;
|
|
110
|
+
lines.push(`${chalk.yellow("▸")} ${chalk.cyan(tc.toolName)}(${chalk.dim(display)})`);
|
|
111
|
+
}
|
|
112
|
+
for (const tr of toolResults) {
|
|
113
|
+
const summary = summarizeResult(tr.result);
|
|
114
|
+
lines.push(` ${chalk.green("◂")} ${chalk.cyan(tr.toolName)} ${chalk.dim(summary)}`);
|
|
115
|
+
}
|
|
116
|
+
if (lines.length === 0)
|
|
117
|
+
return;
|
|
118
|
+
stderr(boxen(lines.join("\n"), {
|
|
119
|
+
padding: { top: 0, bottom: 0, left: 1, right: 1 },
|
|
120
|
+
margin: 0,
|
|
121
|
+
borderStyle: "round",
|
|
122
|
+
borderColor: "yellow",
|
|
123
|
+
title: `step ${stepIndex} tools`,
|
|
124
|
+
titleAlignment: "left",
|
|
125
|
+
}) + "\n");
|
|
126
|
+
breakStdout();
|
|
127
|
+
}
|
|
128
|
+
/** Debug block header before a batch of tool calls in a step. */
|
|
129
|
+
export function printToolBlockStart(stepIndex) {
|
|
130
|
+
breakStdout();
|
|
131
|
+
stderr(`\n${chalk.yellow("[debug]")} ${chalk.dim(`┌ step ${stepIndex} tool execution ${"─".repeat(18)}┐`)}\n`);
|
|
132
|
+
}
|
|
133
|
+
/** Debug tool call with full args. */
|
|
134
|
+
export function printToolCallDebug(toolName, args) {
|
|
135
|
+
const argsJson = JSON.stringify(args);
|
|
136
|
+
const display = argsJson.length > 200 ? argsJson.slice(0, 197) + "..." : argsJson;
|
|
137
|
+
stderr(` ${chalk.yellow(">")} ${toolName}(${display})\n`);
|
|
138
|
+
}
|
|
139
|
+
/** Debug tool result. */
|
|
140
|
+
export function printToolResultDebug(toolName, result) {
|
|
141
|
+
const summary = summarizeResult(result);
|
|
142
|
+
stderr(` ${chalk.green("<")} ${toolName} ${chalk.dim(summary)}\n`);
|
|
143
|
+
}
|
|
144
|
+
/** Debug block footer. */
|
|
145
|
+
export function printToolBlockEnd() {
|
|
146
|
+
stderr(`${chalk.dim(`└${"─".repeat(46)}┘`)}\n`);
|
|
147
|
+
breakStdout();
|
|
148
|
+
}
|
|
149
|
+
/** General debug message (prompt saved, etc.). */
|
|
150
|
+
export function printDebug(message) {
|
|
151
|
+
stderr(`\n${chalk.yellow("[debug]")} ${message}\n`);
|
|
152
|
+
breakStdout();
|
|
153
|
+
}
|
|
154
|
+
/** Compact per-turn memory banner after each response. */
|
|
155
|
+
export function printMemoryBanner(stats) {
|
|
156
|
+
if (stats.activeState === 0 &&
|
|
157
|
+
stats.recallCalls === 0 &&
|
|
158
|
+
stats.autoHydrated === 0 &&
|
|
159
|
+
stats.digestLen === 0 &&
|
|
160
|
+
!stats.promptTokens &&
|
|
161
|
+
!stats.outputTokens)
|
|
162
|
+
return;
|
|
163
|
+
const parts = [];
|
|
164
|
+
if (stats.activeState > 0 || stats.totalState > 0)
|
|
165
|
+
parts.push(`${stats.activeState}/${stats.totalState} state`);
|
|
166
|
+
if (stats.digestLen > 0)
|
|
167
|
+
parts.push(`digest ${stats.digestLen}c`);
|
|
168
|
+
if (stats.recallCalls > 0)
|
|
169
|
+
parts.push(`recall ${stats.recallHits}h`);
|
|
170
|
+
if (stats.autoHydrated > 0)
|
|
171
|
+
parts.push(`auto+${stats.autoHydrated}`);
|
|
172
|
+
if (stats.promptTokens && stats.promptTokens > 0)
|
|
173
|
+
parts.push(`prompt ~${stats.promptTokens}t`);
|
|
174
|
+
if (stats.outputTokens && stats.outputTokens > 0)
|
|
175
|
+
parts.push(`out ~${stats.outputTokens}t`);
|
|
176
|
+
if (parts.length === 0)
|
|
177
|
+
return;
|
|
178
|
+
stderr(`\n${chalk.dim(`[state] ${parts.join(" | ")}`)}\n`);
|
|
179
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "praana",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Terminal coding agent with adaptive context and cross-session memory",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/amitkumardubey/praana.git"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"cli",
|
|
12
|
+
"ai",
|
|
13
|
+
"coding-agent",
|
|
14
|
+
"terminal",
|
|
15
|
+
"assistant",
|
|
16
|
+
"llm"
|
|
17
|
+
],
|
|
18
|
+
"type": "module",
|
|
19
|
+
"bin": {
|
|
20
|
+
"praana": "bin/praana.js",
|
|
21
|
+
"pran": "bin/pran.js"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"dist",
|
|
26
|
+
"praana.config.example.toml"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsc",
|
|
33
|
+
"prepublishOnly": "rm -rf dist && tsc",
|
|
34
|
+
"start": "node dist/main.js",
|
|
35
|
+
"dev": "tsx src/main.ts",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"test:coverage": "vitest run --coverage"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@earendil-works/pi-ai": "^0.75.5",
|
|
42
|
+
"better-sqlite3": "^11.0.0",
|
|
43
|
+
"boxen": "^8.0.1",
|
|
44
|
+
"chalk": "^5.6.2",
|
|
45
|
+
"cli-highlight": "^2.1.11",
|
|
46
|
+
"cliui": "^9.0.1",
|
|
47
|
+
"ink": "^7.0.5",
|
|
48
|
+
"ink-text-input": "^6.0.0",
|
|
49
|
+
"js-yaml": "^4.2.0",
|
|
50
|
+
"marked": "^15.0.12",
|
|
51
|
+
"marked-terminal": "^7.3.0",
|
|
52
|
+
"ora": "^9.4.0",
|
|
53
|
+
"pino": "^10.3.1",
|
|
54
|
+
"pino-pretty": "^13.1.3",
|
|
55
|
+
"pino-roll": "^4.0.0",
|
|
56
|
+
"react": "^19.2.7",
|
|
57
|
+
"sqlite-vec": "^0.1.6",
|
|
58
|
+
"strip-ansi": "^7.2.0",
|
|
59
|
+
"toml": "^3.0.0",
|
|
60
|
+
"ulid": "^2.3.0",
|
|
61
|
+
"zod": "^3.23.0",
|
|
62
|
+
"zod-to-json-schema": "^3.25.2"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
66
|
+
"@types/js-yaml": "^4.0.9",
|
|
67
|
+
"@types/node": "^22.0.0",
|
|
68
|
+
"@types/react": "^19.2.17",
|
|
69
|
+
"tsx": "^4.16.0",
|
|
70
|
+
"typescript": "^5.5.0",
|
|
71
|
+
"vitest": "^4.1.7"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# PRAANA Configuration — Example
|
|
2
|
+
#
|
|
3
|
+
# Copy this file to `praana.config.toml` in your project root and customize:
|
|
4
|
+
# cp praana.config.example.toml praana.config.toml
|
|
5
|
+
#
|
|
6
|
+
# All values shown are the built-in defaults. You only need to
|
|
7
|
+
# override what you want to change. PRAANA works with zero config.
|
|
8
|
+
#
|
|
9
|
+
# Config is loaded from (later overrides earlier):
|
|
10
|
+
# 1. ~/.praana/praana.config.json
|
|
11
|
+
# 2. ~/.praana/config.toml
|
|
12
|
+
# 3. ./praana.config.json
|
|
13
|
+
# 4. ./praana.config.toml
|
|
14
|
+
|
|
15
|
+
[llm]
|
|
16
|
+
# LLM provider. Supported values:
|
|
17
|
+
# openrouter — OpenRouter (default). Works with 200+ models from any provider.
|
|
18
|
+
# openai — OpenAI API (gpt-4o, o3, etc.)
|
|
19
|
+
# anthropic — Anthropic API (claude-sonnet-4, claude-3.5, etc.)
|
|
20
|
+
# google — Google Gemini API (gemini-2.5-flash, gemini-2.5-pro)
|
|
21
|
+
# deepseek — DeepSeek API (deepseek-v4-pro, deepseek-chat)
|
|
22
|
+
# groq — Groq API (fast inference on open models)
|
|
23
|
+
# mistral — Mistral AI API
|
|
24
|
+
# xai — xAI API (grok)
|
|
25
|
+
# fireworks — Fireworks AI
|
|
26
|
+
# together — Together AI
|
|
27
|
+
# ollama — Local Ollama (no API key needed)
|
|
28
|
+
# opencode — OpenCode Zen (big-pickle, minimax-m2.5, etc.)
|
|
29
|
+
# amazon-bedrock — AWS Bedrock (uses AWS credentials, not an API key)
|
|
30
|
+
provider = "openrouter"
|
|
31
|
+
|
|
32
|
+
# Model name passed to the provider.
|
|
33
|
+
# For OpenRouter: use "provider/model" format, e.g. "deepseek/deepseek-v4-pro"
|
|
34
|
+
# For native providers: use the model name directly, e.g. "claude-sonnet-4-20250514"
|
|
35
|
+
# Override at runtime via PRAANA_MODEL env var (legacy: ARIA_MODEL) or /model command.
|
|
36
|
+
model = "deepseek/deepseek-v4-flash:free"
|
|
37
|
+
|
|
38
|
+
# Optional: override detected model context window (input tokens) for pressure and compaction
|
|
39
|
+
# context_window = 128000
|
|
40
|
+
|
|
41
|
+
# Free tier — zero cost, good for getting started.
|
|
42
|
+
# Upgrade to a paid model for better results on complex tasks:
|
|
43
|
+
# deepseek/deepseek-v4-pro
|
|
44
|
+
# anthropic/claude-sonnet-4-5
|
|
45
|
+
# openai/gpt-4o
|
|
46
|
+
|
|
47
|
+
# Optional: Custom API base URL (e.g., for self-hosted or proxy)
|
|
48
|
+
# base_url = "https://my-proxy.example.com/v1"
|
|
49
|
+
|
|
50
|
+
# API keys are read from environment variables:
|
|
51
|
+
# openrouter → OPENROUTER_API_KEY
|
|
52
|
+
# openai → OPENAI_API_KEY
|
|
53
|
+
# anthropic → ANTHROPIC_API_KEY
|
|
54
|
+
# google → GOOGLE_GENERATIVE_AI_API_KEY
|
|
55
|
+
# deepseek → DEEPSEEK_API_KEY
|
|
56
|
+
# groq → GROQ_API_KEY
|
|
57
|
+
# mistral → MISTRAL_API_KEY
|
|
58
|
+
# xai → XAI_API_KEY
|
|
59
|
+
# fireworks → FIREWORKS_API_KEY
|
|
60
|
+
# together → TOGETHER_API_KEY
|
|
61
|
+
# ollama → (none — runs locally)
|
|
62
|
+
# opencode → OPENCODE_API_KEY (from https://opencode.ai/auth)
|
|
63
|
+
# amazon-bedrock → (none — uses AWS credentials chain)
|
|
64
|
+
|
|
65
|
+
[memory]
|
|
66
|
+
# Enable/disable Cognitive Memory (SQLite-backed cross-session persistence)
|
|
67
|
+
enabled = true
|
|
68
|
+
|
|
69
|
+
# Session-end learning extraction (runs on /exit):
|
|
70
|
+
# disabled — no LLM call; no cross-session learnings saved
|
|
71
|
+
# ollama — local Ollama chat API (no API key; see ollama_* below)
|
|
72
|
+
# openrouter — OpenRouter (OPENROUTER_API_KEY)
|
|
73
|
+
# openai — OpenAI API (OPENAI_API_KEY)
|
|
74
|
+
summarizer = "openrouter"
|
|
75
|
+
|
|
76
|
+
# Path to the SQLite memory database (~ is expanded to $HOME)
|
|
77
|
+
db_path = "~/.praana/memory.db"
|
|
78
|
+
|
|
79
|
+
# --- Embeddings (semantic /recall) ---
|
|
80
|
+
# auto — try Ollama, fall back to hash with a warning
|
|
81
|
+
# ollama — require Ollama (fall back to hash if unavailable)
|
|
82
|
+
# transformers — @huggingface/transformers (npm install, ~266MB)
|
|
83
|
+
# llama-cpp — node-llama-cpp (native, requires build tools)
|
|
84
|
+
# hash — deterministic offline vectors (non-semantic)
|
|
85
|
+
embedder = "auto"
|
|
86
|
+
|
|
87
|
+
# Ollama daemon URL (embeddings + summarizer when summarizer = "ollama")
|
|
88
|
+
ollama_url = "http://localhost:11434"
|
|
89
|
+
|
|
90
|
+
# Embedding model — must be installed: `ollama pull nomic-embed-text`
|
|
91
|
+
ollama_model = "nomic-embed-text"
|
|
92
|
+
|
|
93
|
+
# Chat model for summarizer = "ollama" (session-end learnings).
|
|
94
|
+
# Must be a chat model from `ollama list`, not an embedding model.
|
|
95
|
+
# Ways to set it (pick one):
|
|
96
|
+
# 1. ollama_summarizer_model below (recommended)
|
|
97
|
+
# 2. PRAANA_SUMMARIZER_MODEL env var (e.g. export PRAANA_SUMMARIZER_MODEL=qwen3.5:4b)
|
|
98
|
+
# 3. Leave unset — auto-picks the first non-embedding model from `ollama list`
|
|
99
|
+
# ollama_summarizer_model = "qwen3.5:4b"
|
|
100
|
+
|
|
101
|
+
# --- Example: fully local memory (no OpenRouter for summarizer) ---
|
|
102
|
+
# summarizer = "ollama"
|
|
103
|
+
# embedder = "ollama"
|
|
104
|
+
# ollama_url = "http://localhost:11434"
|
|
105
|
+
# ollama_model = "nomic-embed-text"
|
|
106
|
+
# ollama_summarizer_model = "qwen3.5:4b"
|
|
107
|
+
|
|
108
|
+
# Summarizer model override for openrouter/openai (ignored when summarizer = "ollama"):
|
|
109
|
+
# export PRAANA_SUMMARIZER_MODEL=google/gemini-2.5-flash
|
|
110
|
+
|
|
111
|
+
[compiler]
|
|
112
|
+
# Total estimated token budget for the compiled prompt
|
|
113
|
+
token_budget = 100000
|
|
114
|
+
|
|
115
|
+
# Number of recent user/agent turns to include in context
|
|
116
|
+
recent_turns = 10
|
|
117
|
+
|
|
118
|
+
# Token budget reserved specifically for the Recent Turns section
|
|
119
|
+
recent_turns_token_budget = 30000
|
|
120
|
+
|
|
121
|
+
# Max share of token_budget for AGENTS.md / project context in the system frame
|
|
122
|
+
agents_budget_ratio = 0.3
|
|
123
|
+
|
|
124
|
+
# Tokens reserved for model output when measuring context pressure
|
|
125
|
+
# reserved_output_tokens = 0
|
|
126
|
+
|
|
127
|
+
# Auto-compact when prompt fill reaches this ratio of the model context window (classic mode)
|
|
128
|
+
auto_compact_at = 0.75
|
|
129
|
+
|
|
130
|
+
# Disarm compaction hysteresis below this ratio (classic mode)
|
|
131
|
+
auto_compact_clear_at = 0.55
|
|
132
|
+
|
|
133
|
+
# Fraction of oldest transcript events to summarise per compaction (classic mode)
|
|
134
|
+
compact_chunk_fraction = 0.25
|
|
135
|
+
|
|
136
|
+
# Classic mode: never auto-compact (full verbatim until model limit)
|
|
137
|
+
# verbatim_only = false
|
|
138
|
+
|
|
139
|
+
[skills]
|
|
140
|
+
# Discover and load SKILL.md files (https://agentskills.io/)
|
|
141
|
+
enabled = true
|
|
142
|
+
|
|
143
|
+
# Max share of token_budget for the skills section in the compiled prompt
|
|
144
|
+
max_token_budget_ratio = 0.2
|
|
145
|
+
|
|
146
|
+
# Residency: hot skills demote to warm after this many idle turns
|
|
147
|
+
active_skill_idle_turns = 5
|
|
148
|
+
|
|
149
|
+
# Warm skills evict to cold after this many idle turns
|
|
150
|
+
warm_skill_eviction_turns = 20
|
|
151
|
+
|
|
152
|
+
# Max directory depth when scanning skill search paths
|
|
153
|
+
max_depth = 6
|
|
154
|
+
|
|
155
|
+
[tiers]
|
|
156
|
+
# Turns of inactivity before active → soft tier demotion
|
|
157
|
+
idle_soft_after_turns = 20
|
|
158
|
+
|
|
159
|
+
# Turns of inactivity before active/soft → hard tier demotion
|
|
160
|
+
idle_hard_after_turns = 50
|
|
161
|
+
|
|
162
|
+
[session]
|
|
163
|
+
# Directory where session event logs are stored
|
|
164
|
+
log_dir = "~/.praana/sessions"
|
|
165
|
+
|
|
166
|
+
[search_code]
|
|
167
|
+
# Optional: absolute path to the ripgrep binary.
|
|
168
|
+
# Leave unset to resolve "rg" from $PATH (default).
|
|
169
|
+
# Useful when ripgrep is installed outside PATH (e.g. a vendored copy).
|
|
170
|
+
# rg_path = "/opt/homebrew/bin/rg"
|
|
171
|
+
|
|
172
|
+
[context_engine]
|
|
173
|
+
# Context engine: structured checkpoint, distillation, and scored compilation.
|
|
174
|
+
# When disabled (false), PRAANA runs in classic mode — full verbatim conversation
|
|
175
|
+
# history, no StateGraph tools, skills loaded as metadata catalog only.
|
|
176
|
+
# If enabled but initialization fails, PRAANA falls back to the same classic mode.
|
|
177
|
+
#
|
|
178
|
+
# When enabled, SessionCheckpoint is reconciled after every turn from a
|
|
179
|
+
# deterministic TurnDigest (never by the LLM). Checkpoint sections:
|
|
180
|
+
# - Active request (latest user intent)
|
|
181
|
+
# - Session narrative (rolling prose from digest data, max 20 entries)
|
|
182
|
+
# - Plan (current + superseded plans with completion hints)
|
|
183
|
+
# - Constraints (append-only, never dropped)
|
|
184
|
+
# - Decisions (rationale retained even after age-based compaction)
|
|
185
|
+
# - Files in play, findings, errors, recent activity
|
|
186
|
+
# Casual user preferences ("we use pnpm", "let's use JWT") are NOT captured by regex —
|
|
187
|
+
# the system prompt nudge directs the LLM to call add_constraint for those.
|
|
188
|
+
# Only the unambiguous "not X, Y" correction pattern is regex-captured.
|
|
189
|
+
enabled = false
|
|
190
|
+
|
|
191
|
+
# Write telemetry even when engine is off (debug / comparison during development)
|
|
192
|
+
measurement_mode = false
|
|
193
|
+
|
|
194
|
+
# Tool outputs at or below this token count appear verbatim in the prompt
|
|
195
|
+
artifact_inline_threshold = 400
|
|
196
|
+
|
|
197
|
+
# Turns without access before an artifact is eligible for eviction
|
|
198
|
+
artifact_ttl_turns = 50
|
|
199
|
+
|
|
200
|
+
# Enable structured SessionCheckpoint in the prompt (see section list above).
|
|
201
|
+
# Section token budgets are fixed in code: narrative/plan 400 each, decisions 800,
|
|
202
|
+
# findings 300. No per-section config yet.
|
|
203
|
+
checkpoint_enabled = true
|
|
204
|
+
|
|
205
|
+
# Use LLM for ambiguous userIntent extraction (default: first 120 chars)
|
|
206
|
+
llm_digest = false
|
|
207
|
+
|
|
208
|
+
# Max rolling activity entries kept for checkpoint preview
|
|
209
|
+
activity_log_max_entries = 15
|
|
210
|
+
|
|
211
|
+
[context_engine.distiller]
|
|
212
|
+
# Distiller compression intensity: "lite" (conservative) or "full" (aggressive)
|
|
213
|
+
default_intensity = "full"
|
|
214
|
+
|
|
215
|
+
[context_engine.scoring]
|
|
216
|
+
# Scoring weights for context unit selection (3-term model: pin + recency + relevance)
|
|
217
|
+
w_pin = 1.0
|
|
218
|
+
w_recency = 0.5
|
|
219
|
+
w_relevance = 0.3
|
|
220
|
+
|
|
221
|
+
[context_engine.pressure]
|
|
222
|
+
# Prompt pressure ratio triggers compaction
|
|
223
|
+
compact_at = 0.70
|
|
224
|
+
# Emergency: drop all turn digests, keep checkpoint + verbatim last 2 turns
|
|
225
|
+
emergency_at = 0.85
|
|
226
|
+
|
|
227
|
+
[ui]
|
|
228
|
+
# Terminal interface: "tui" (Ink chat shell) or "readline" (classic CLI)
|
|
229
|
+
mode = "tui"
|
|
230
|
+
# Screen mode for TUI: "preserve" keeps scrollback (default), "alternate" uses full-screen buffer
|
|
231
|
+
screen = "preserve"
|