@alisio/alisio-code 0.1.0-alpha.4 → 0.1.0-alpha.6
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/main.js +23 -2
- package/dist/tui/app.js +7 -5
- package/dist/tui/components.d.ts +3 -3
- package/dist/tui/components.js +6 -6
- package/dist/tui/state.d.ts +12 -2
- package/dist/tui/state.js +29 -10
- package/package.json +9 -9
package/dist/main.js
CHANGED
|
@@ -389,11 +389,12 @@ for (const name of ["list", "validate"]) {
|
|
|
389
389
|
}
|
|
390
390
|
const plugins = program.command("plugins");
|
|
391
391
|
plugins.command("list").action(async (_opts, cmd) => {
|
|
392
|
-
const { discoverPlugins } = await import("@alisio/core");
|
|
392
|
+
const { discoverPlugins, installedNpmPlugins } = await import("@alisio/core");
|
|
393
393
|
const { configHome } = await import("@alisio/core");
|
|
394
394
|
const { join, resolve } = await import("node:path");
|
|
395
|
+
const global = join(configHome(), "plugins");
|
|
395
396
|
console.log(JSON.stringify({
|
|
396
|
-
global: await discoverPlugins(
|
|
397
|
+
global: [...(await discoverPlugins(global)), ...(await installedNpmPlugins(global))],
|
|
397
398
|
project: await discoverPlugins(join(resolve(options(cmd).cwd ?? process.cwd()), ".alisio", "plugins")),
|
|
398
399
|
explicit: options(cmd).plugin ?? [],
|
|
399
400
|
}, null, 2));
|
|
@@ -468,6 +469,26 @@ mcp
|
|
|
468
469
|
await app.close();
|
|
469
470
|
}
|
|
470
471
|
});
|
|
472
|
+
program
|
|
473
|
+
.command("install")
|
|
474
|
+
.description("Install an npm plugin package into the global plugins directory (~/.config/alisio/plugins)")
|
|
475
|
+
.argument("<spec>", 'npm package spec, e.g. "npm:plugin-openrouter" or "plugin-openrouter@1.2.3"')
|
|
476
|
+
.option("-y, --yes", "Skip the pre-install confirmation (npm may run lifecycle scripts)")
|
|
477
|
+
.option("--trust-plugin", "Explicit trust for this global install (same as --yes)")
|
|
478
|
+
.option("--update", "Refresh an already-installed plugin to the latest version, keeping its name")
|
|
479
|
+
.action(async (spec, _options, cmd) => {
|
|
480
|
+
const { cliInstall, configHome } = await import("@alisio/core");
|
|
481
|
+
const o = options(cmd);
|
|
482
|
+
await cliInstall({
|
|
483
|
+
spec,
|
|
484
|
+
configHome: configHome(),
|
|
485
|
+
yes: !!o.yes || !!o.trustPlugin,
|
|
486
|
+
update: !!o.update,
|
|
487
|
+
readOnly: !!o.readOnly,
|
|
488
|
+
json: !!o.json,
|
|
489
|
+
interactive: !!process.stdin.isTTY && !!process.stdout.isTTY && !o.json,
|
|
490
|
+
});
|
|
491
|
+
});
|
|
471
492
|
try {
|
|
472
493
|
await program.parseAsync();
|
|
473
494
|
}
|
package/dist/tui/app.js
CHANGED
|
@@ -183,8 +183,10 @@ export async function runTui(options) {
|
|
|
183
183
|
path.unshift(cur.label);
|
|
184
184
|
const siblings = nodes.filter((n) => n.parentId === node.parentId);
|
|
185
185
|
const child = viewedState();
|
|
186
|
-
const
|
|
187
|
-
const pct = child?.context &&
|
|
186
|
+
const budget = app.contextBudget(child?.model ?? view.model);
|
|
187
|
+
const pct = child?.context && budget
|
|
188
|
+
? ` · ctx ${Math.round((child.context.used / budget.total) * 100)}%`
|
|
189
|
+
: "";
|
|
188
190
|
const tokens = child
|
|
189
191
|
? ` · ↑${formatTokens(child.stats.input)} ↓${formatTokens(child.stats.output)}`
|
|
190
192
|
: "";
|
|
@@ -195,7 +197,7 @@ export async function runTui(options) {
|
|
|
195
197
|
: panelState.focus === "view"
|
|
196
198
|
? viewHint()
|
|
197
199
|
: undefined;
|
|
198
|
-
const footer = new Footer(() => viewedState() ?? view, () => app.
|
|
200
|
+
const footer = new Footer(() => viewedState() ?? view, () => app.contextBudget((viewedState() ?? view).model), () => panelHint() ?? hint, () => [...app.plugins.status.values()].map((s) => s.text));
|
|
199
201
|
const treePanel = new TreePanel(() => {
|
|
200
202
|
const entry = panelEntry();
|
|
201
203
|
if (!entry)
|
|
@@ -866,7 +868,7 @@ export async function runTui(options) {
|
|
|
866
868
|
};
|
|
867
869
|
const statsReport = () => {
|
|
868
870
|
const s = view.stats;
|
|
869
|
-
const
|
|
871
|
+
const budget = app.contextBudget(view.model);
|
|
870
872
|
const tools = Object.entries(s.tools)
|
|
871
873
|
.map(([name, t]) => `| \`${name}\` | ${t.calls} | ${t.errors} |`)
|
|
872
874
|
.join("\n");
|
|
@@ -878,7 +880,7 @@ export async function runTui(options) {
|
|
|
878
880
|
`- Tokens: in ${formatTokens(s.input)} · out ${formatTokens(s.output)} · cached ${formatTokens(s.cached)}`,
|
|
879
881
|
`- Runs: ${s.runs} · turns: ${s.turns}${s.lastRunMs !== undefined ? ` · last run ${formatDuration(s.lastRunMs)}` : ""}`,
|
|
880
882
|
`- Duration: ${formatDuration(Date.now() - s.startedAt)}`,
|
|
881
|
-
`- Context: ${formatContext(view.context?.used ?? 0,
|
|
883
|
+
`- Context: ${formatContext(view.context?.used ?? 0, budget?.total, view.context?.estimated ?? true, budget?.basis)}`,
|
|
882
884
|
"",
|
|
883
885
|
tools ? `| Tool | Calls | Errors |\n| --- | --- | --- |\n${tools}` : "No tool calls yet.",
|
|
884
886
|
"",
|
package/dist/tui/components.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { PanelNode } from "@alisio/sdk";
|
|
|
2
2
|
import { type Component, Container } from "@earendil-works/pi-tui";
|
|
3
3
|
import { type PendingAttachment } from "./attachments.ts";
|
|
4
4
|
import { type QuestionPanelState, type QuestionSpec } from "./questions.ts";
|
|
5
|
-
import { type TranscriptItem, type ViewState } from "./state.ts";
|
|
5
|
+
import { type ContextBudget, type TranscriptItem, type ViewState } from "./state.ts";
|
|
6
6
|
export declare const SPINNER: string[];
|
|
7
7
|
/** Shared animation clock advanced by the app while work is running. */
|
|
8
8
|
export declare const clock: {
|
|
@@ -31,10 +31,10 @@ export declare class Header implements Component {
|
|
|
31
31
|
}
|
|
32
32
|
export declare class Footer implements Component {
|
|
33
33
|
private view;
|
|
34
|
-
private
|
|
34
|
+
private budget;
|
|
35
35
|
private hint;
|
|
36
36
|
private statuses;
|
|
37
|
-
constructor(view: () => ViewState,
|
|
37
|
+
constructor(view: () => ViewState, budget: () => ContextBudget | undefined, hint: () => string | undefined, statuses?: () => string[]);
|
|
38
38
|
invalidate(): void;
|
|
39
39
|
render(width: number): string[];
|
|
40
40
|
}
|
package/dist/tui/components.js
CHANGED
|
@@ -60,20 +60,20 @@ export class Header {
|
|
|
60
60
|
}
|
|
61
61
|
export class Footer {
|
|
62
62
|
view;
|
|
63
|
-
|
|
63
|
+
budget;
|
|
64
64
|
hint;
|
|
65
65
|
statuses;
|
|
66
|
-
constructor(view,
|
|
66
|
+
constructor(view, budget, hint, statuses = () => []) {
|
|
67
67
|
this.view = view;
|
|
68
|
-
this.
|
|
68
|
+
this.budget = budget;
|
|
69
69
|
this.hint = hint;
|
|
70
70
|
this.statuses = statuses;
|
|
71
71
|
}
|
|
72
72
|
invalidate() { }
|
|
73
73
|
render(width) {
|
|
74
|
-
const v = this.view(),
|
|
74
|
+
const v = this.view(), budget = this.budget(), total = budget?.total, used = v.context?.used ?? 0, estimated = v.context?.estimated ?? true;
|
|
75
75
|
const pct = contextPercent(used, total);
|
|
76
|
-
const level = contextLevel(pct ?? 0);
|
|
76
|
+
const level = contextLevel(pct ?? 0, budget?.compactionAt ?? 85);
|
|
77
77
|
const cells = 10, filled = pct === undefined ? 0 : Math.min(cells, Math.round((pct / 100) * cells));
|
|
78
78
|
const bar = pct === undefined
|
|
79
79
|
? style.gray("░".repeat(cells))
|
|
@@ -86,7 +86,7 @@ export class Footer {
|
|
|
86
86
|
const s = v.stats;
|
|
87
87
|
const segments = [
|
|
88
88
|
{
|
|
89
|
-
text: `ctx ${formatContext(used, total, estimated)}`,
|
|
89
|
+
text: `ctx ${formatContext(used, total, estimated, budget?.basis)}`,
|
|
90
90
|
priority: 10,
|
|
91
91
|
paint: pct === undefined ? style.gray : levelColor(level),
|
|
92
92
|
},
|
package/dist/tui/state.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface PluginCatalogView {
|
|
|
38
38
|
diagnostic?: string;
|
|
39
39
|
}
|
|
40
40
|
/** Text markers remain meaningful without color: [x] active, [ ] inactive, [!] failed, [*] pending. */
|
|
41
|
+
/** Group headings are derived from the primary category (or "General") of each plugin. */
|
|
41
42
|
export declare function pluginCatalogItems(entries: PluginCatalogView[]): {
|
|
42
43
|
value: string;
|
|
43
44
|
label: string;
|
|
@@ -84,9 +85,18 @@ export declare function configuredProviderModelItems(catalogs: ProviderCatalogVi
|
|
|
84
85
|
provider: string;
|
|
85
86
|
model: string;
|
|
86
87
|
}): ConfiguredProviderModelItem[];
|
|
87
|
-
export declare function contextLevel(pct: number): Level;
|
|
88
|
+
export declare function contextLevel(pct: number, compactionAt?: number): Level;
|
|
88
89
|
export declare function contextPercent(used: number, total: number | undefined): number | undefined;
|
|
89
|
-
|
|
90
|
+
/** The effective total the context bar measures against, and what it is derived from. */
|
|
91
|
+
export interface ContextBudget {
|
|
92
|
+
/** Effective total in tokens (model window, or the char budget converted to tokens). */
|
|
93
|
+
total: number;
|
|
94
|
+
/** Basis of the total: the model's context window, or the char-budget fallback. */
|
|
95
|
+
basis: "window" | "chars";
|
|
96
|
+
/** Percentage of `total` at which the engine auto-compacts; the bar turns red there. */
|
|
97
|
+
compactionAt: number;
|
|
98
|
+
}
|
|
99
|
+
export declare function formatContext(used: number, total: number | undefined, estimated: boolean, basis?: "window" | "chars"): string;
|
|
90
100
|
export declare function formatDuration(ms: number): string;
|
|
91
101
|
export declare const textWidth: (text: string) => number;
|
|
92
102
|
export declare function truncatePlain(text: string, width: number): string;
|
package/dist/tui/state.js
CHANGED
|
@@ -23,6 +23,7 @@ export function providerModelItems(provider, models, current) {
|
|
|
23
23
|
}));
|
|
24
24
|
}
|
|
25
25
|
/** Text markers remain meaningful without color: [x] active, [ ] inactive, [!] failed, [*] pending. */
|
|
26
|
+
/** Group headings are derived from the primary category (or "General") of each plugin. */
|
|
26
27
|
export function pluginCatalogItems(entries) {
|
|
27
28
|
const marker = (entry) => entry.status === "active"
|
|
28
29
|
? "[x]"
|
|
@@ -31,11 +32,16 @@ export function pluginCatalogItems(entries) {
|
|
|
31
32
|
: entry.status === "failed"
|
|
32
33
|
? "[!]"
|
|
33
34
|
: "[*]";
|
|
34
|
-
|
|
35
|
+
const grouped = new Map();
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
const primary = entry.categories[0] ?? "General";
|
|
38
|
+
grouped.set(primary, [...(grouped.get(primary) ?? []), entry]);
|
|
39
|
+
}
|
|
40
|
+
return [...grouped].flatMap(([title, group]) => group.map((entry, index) => ({
|
|
35
41
|
value: entry.id,
|
|
36
|
-
label: `${marker(entry)} ${entry.name} · ${entry.builtin ? "built-in" : entry.source}`,
|
|
42
|
+
label: `${index === 0 ? `${title} · ` : ""}${marker(entry)} ${entry.name} · ${entry.builtin ? "built-in" : entry.source}`,
|
|
37
43
|
description: `${entry.status}${entry.categories.length ? ` · ${entry.categories.join(", ")}` : ""} · ${entry.description}`,
|
|
38
|
-
}));
|
|
44
|
+
})));
|
|
39
45
|
}
|
|
40
46
|
export const pluginToggleNeedsConfirmation = (entry) => !entry.builtin;
|
|
41
47
|
const mcpSourceTitle = (kind) => ({
|
|
@@ -117,18 +123,23 @@ export function configuredProviderModelItems(catalogs, current) {
|
|
|
117
123
|
}
|
|
118
124
|
return items;
|
|
119
125
|
}
|
|
120
|
-
export function contextLevel(pct) {
|
|
121
|
-
|
|
126
|
+
export function contextLevel(pct, compactionAt = 85) {
|
|
127
|
+
// Warning band starts a quarter below the auto-compaction point; danger is exactly there.
|
|
128
|
+
const warn = Math.max(0, compactionAt - 25);
|
|
129
|
+
return pct < warn ? "ok" : pct < compactionAt ? "warn" : "danger";
|
|
122
130
|
}
|
|
123
131
|
export function contextPercent(used, total) {
|
|
124
132
|
return total && total > 0 ? (used / total) * 100 : undefined;
|
|
125
133
|
}
|
|
126
|
-
export function formatContext(used, total, estimated) {
|
|
134
|
+
export function formatContext(used, total, estimated, basis) {
|
|
127
135
|
const prefix = `${estimated ? "~" : ""}${formatTokens(used)} / `;
|
|
128
136
|
const pct = contextPercent(used, total);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
if (pct === undefined || !total)
|
|
138
|
+
return `${prefix}unknown`;
|
|
139
|
+
// The `~` marks an estimate; the "char budget" suffix tells the user the bar is measured
|
|
140
|
+
// against the fallback (est. tokens from limits.maxContextChars), not a model window.
|
|
141
|
+
const basisSuffix = basis === "chars" ? " char budget" : "";
|
|
142
|
+
return `${prefix}${formatTokens(total)} (${Math.round(pct)}%)${basisSuffix}`;
|
|
132
143
|
}
|
|
133
144
|
export function formatDuration(ms) {
|
|
134
145
|
if (ms < 1000)
|
|
@@ -461,6 +472,9 @@ export function reduceEvent(state, event) {
|
|
|
461
472
|
const checkpoint = typeof d.summarizedTokens === "number" && typeof d.checkpointTokens === "number"
|
|
462
473
|
? ` · checkpoint ~${formatTokens(d.summarizedTokens)} → ~${formatTokens(d.checkpointTokens)} tokens`
|
|
463
474
|
: "";
|
|
475
|
+
const partial = d.partial
|
|
476
|
+
? " · partial: the summary was cut by max output tokens; consider raising compaction.maxOutputTokens"
|
|
477
|
+
: "";
|
|
464
478
|
return addItem({
|
|
465
479
|
...state,
|
|
466
480
|
compacting: false,
|
|
@@ -468,7 +482,7 @@ export function reduceEvent(state, event) {
|
|
|
468
482
|
}, {
|
|
469
483
|
kind: "notice",
|
|
470
484
|
text: [
|
|
471
|
-
`Context compacted (${String(d.reason ?? "manual")}): ${String(d.replaced ?? 0)} messages summarized, ~${formatTokens(Number(d.before ?? 0))} → ~${formatTokens(Number(d.after ?? 0))} tokens${checkpoint}`,
|
|
485
|
+
`Context compacted (${String(d.reason ?? "manual")}): ${String(d.replaced ?? 0)} messages summarized, ~${formatTokens(Number(d.before ?? 0))} → ~${formatTokens(Number(d.after ?? 0))} tokens${checkpoint}${partial}`,
|
|
472
486
|
...reports,
|
|
473
487
|
].join("\n"),
|
|
474
488
|
});
|
|
@@ -487,6 +501,11 @@ export function reduceEvent(state, event) {
|
|
|
487
501
|
return addItem({ ...state, compacting: false }, { kind: "notice", text: `Compaction skipped: ${String(d.detail ?? "nothing to compact")}` });
|
|
488
502
|
case "compaction_failed":
|
|
489
503
|
return addItem({ ...state, compacting: false }, { kind: "error", text: `Compaction failed: ${String(d.error ?? "unknown error")}` });
|
|
504
|
+
case "response_truncated":
|
|
505
|
+
return addItem(state, {
|
|
506
|
+
kind: "notice",
|
|
507
|
+
text: "Response cut by max output tokens — the answer may be incomplete. Raise limits.maxOutputTokens to allow longer answers.",
|
|
508
|
+
});
|
|
490
509
|
case "model_changed":
|
|
491
510
|
return {
|
|
492
511
|
...state,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alisio/alisio-code",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.6",
|
|
4
4
|
"description": "Alisio: an extensible, provider-agnostic coding-agent harness for your terminal. TUI, OpenAI-compatible providers, permissioned local tools, context compaction, persistent memory and a typed plugin SDK.",
|
|
5
5
|
"author": "Gustavo Gutiérrez",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"alisio": "./dist/main.js"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@alisio/core": "0.1.0-alpha.
|
|
49
|
-
"@alisio/plugin-deepseek": "0.1.0-alpha.
|
|
50
|
-
"@alisio/plugin-memory": "0.1.0-alpha.
|
|
51
|
-
"@alisio/plugin-openai-compatible": "0.1.0-alpha.
|
|
52
|
-
"@alisio/plugin-opencode": "0.1.0-alpha.
|
|
53
|
-
"@alisio/plugin-opencode-go": "0.1.0-alpha.
|
|
54
|
-
"@alisio/plugin-subagents": "0.1.0-alpha.
|
|
55
|
-
"@alisio/sdk": "0.1.0-alpha.
|
|
48
|
+
"@alisio/core": "0.1.0-alpha.5",
|
|
49
|
+
"@alisio/plugin-deepseek": "0.1.0-alpha.5",
|
|
50
|
+
"@alisio/plugin-memory": "0.1.0-alpha.5",
|
|
51
|
+
"@alisio/plugin-openai-compatible": "0.1.0-alpha.5",
|
|
52
|
+
"@alisio/plugin-opencode": "0.1.0-alpha.5",
|
|
53
|
+
"@alisio/plugin-opencode-go": "0.1.0-alpha.5",
|
|
54
|
+
"@alisio/plugin-subagents": "0.1.0-alpha.5",
|
|
55
|
+
"@alisio/sdk": "0.1.0-alpha.5",
|
|
56
56
|
"@earendil-works/pi-tui": "0.87.1",
|
|
57
57
|
"commander": "15.0.0"
|
|
58
58
|
},
|