@elyracode/coding-agent 0.5.7 → 0.5.9
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/CHANGELOG.md +14 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +33 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +19 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +6 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +199 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/norwegian-midnight.json +95 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +2 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/docs/usage.md +8 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -9,7 +9,7 @@ import * as path from "node:path";
|
|
|
9
9
|
import { getProviders, } from "@elyracode/ai";
|
|
10
10
|
import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@elyracode/tui";
|
|
11
11
|
import { spawn, spawnSync } from "child_process";
|
|
12
|
-
import { APP_NAME, APP_TITLE, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
|
|
12
|
+
import { APP_NAME, APP_TITLE, getAgentDir, getAuthPath, getBlueprintsDir, getDebugLogPath, getDocsPath, getProjectBlueprintsDir, getShareViewerUrl, VERSION, } from "../../config.js";
|
|
13
13
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
14
14
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
15
15
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
@@ -1946,6 +1946,36 @@ export class InteractiveMode {
|
|
|
1946
1946
|
this.editor.setText("");
|
|
1947
1947
|
return;
|
|
1948
1948
|
}
|
|
1949
|
+
if (text === "/cost") {
|
|
1950
|
+
this.editor.setText("");
|
|
1951
|
+
this.handleCostCommand();
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
if (text === "/diff") {
|
|
1955
|
+
this.editor.setText("");
|
|
1956
|
+
await this.handleDiffCommand();
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
if (text === "/pin" || text.startsWith("/pin ")) {
|
|
1960
|
+
this.editor.setText("");
|
|
1961
|
+
this.handlePinCommand(text.startsWith("/pin ") ? text.slice(5).trim() : undefined);
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
if (text === "/unpin" || text.startsWith("/unpin ")) {
|
|
1965
|
+
this.editor.setText("");
|
|
1966
|
+
this.handleUnpinCommand(text.startsWith("/unpin ") ? text.slice(7).trim() : undefined);
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1969
|
+
if (text === "/pins") {
|
|
1970
|
+
this.editor.setText("");
|
|
1971
|
+
this.handlePinsCommand();
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (text === "/blueprint" || text.startsWith("/blueprint ")) {
|
|
1975
|
+
this.editor.setText("");
|
|
1976
|
+
this.handleBlueprintCommand(text.startsWith("/blueprint ") ? text.slice(11).trim() : undefined);
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1949
1979
|
if (text === "/theme" || text.startsWith("/theme ")) {
|
|
1950
1980
|
const themeName = text.startsWith("/theme ") ? text.slice(7).trim() : undefined;
|
|
1951
1981
|
this.editor.setText("");
|
|
@@ -3259,6 +3289,174 @@ export class InteractiveMode {
|
|
|
3259
3289
|
return { component: selector, focus: selector.getSettingsList() };
|
|
3260
3290
|
});
|
|
3261
3291
|
}
|
|
3292
|
+
handleCostCommand() {
|
|
3293
|
+
const stats = this.session.getSessionStats();
|
|
3294
|
+
const fmt = (n) => n.toLocaleString();
|
|
3295
|
+
const cost = stats.cost > 0 ? `$${stats.cost.toFixed(4)}` : "$0.00";
|
|
3296
|
+
let info = `${theme.bold("Session Cost")}\n\n`;
|
|
3297
|
+
info += `${theme.fg("dim", "Input:")} ${fmt(stats.tokens.input)} tokens\n`;
|
|
3298
|
+
info += `${theme.fg("dim", "Output:")} ${fmt(stats.tokens.output)} tokens\n`;
|
|
3299
|
+
info += `${theme.fg("dim", "Cache read:")} ${fmt(stats.tokens.cacheRead)} tokens\n`;
|
|
3300
|
+
info += `${theme.fg("dim", "Cache write:")} ${fmt(stats.tokens.cacheWrite)} tokens\n`;
|
|
3301
|
+
info += `${theme.fg("dim", "Total:")} ${fmt(stats.tokens.total)} tokens\n\n`;
|
|
3302
|
+
info += `${theme.fg("dim", "Estimated cost:")} ${theme.fg("accent", cost)}\n`;
|
|
3303
|
+
if (stats.contextUsage) {
|
|
3304
|
+
const ctxTokens = stats.contextUsage.tokens ?? 0;
|
|
3305
|
+
const pct = stats.contextUsage.contextWindow > 0 ? Math.round((ctxTokens / stats.contextUsage.contextWindow) * 100) : 0;
|
|
3306
|
+
info += `${theme.fg("dim", "Context:")} ${fmt(ctxTokens)} / ${fmt(stats.contextUsage.contextWindow)} (${pct}%)`;
|
|
3307
|
+
}
|
|
3308
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3309
|
+
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
3310
|
+
this.ui.requestRender();
|
|
3311
|
+
}
|
|
3312
|
+
async handleDiffCommand() {
|
|
3313
|
+
const cwd = this.sessionManager.getCwd();
|
|
3314
|
+
try {
|
|
3315
|
+
const { execSync } = await import("node:child_process");
|
|
3316
|
+
const diff = execSync("git diff", { cwd, encoding: "utf-8", maxBuffer: 1024 * 1024 });
|
|
3317
|
+
if (!diff.trim()) {
|
|
3318
|
+
this.showStatus("No uncommitted changes");
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3322
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3323
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Uncommitted Changes")), 1, 0));
|
|
3324
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3325
|
+
this.chatContainer.addChild(new Markdown(`\`\`\`diff\n${diff}\n\`\`\``, 1, 1, this.getMarkdownThemeWithSettings()));
|
|
3326
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3327
|
+
this.ui.requestRender();
|
|
3328
|
+
}
|
|
3329
|
+
catch {
|
|
3330
|
+
this.showError("Failed to run git diff. Is this a git repository?");
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
handlePinCommand(filePath) {
|
|
3334
|
+
if (!filePath) {
|
|
3335
|
+
this.showError("Usage: /pin <file-path>");
|
|
3336
|
+
return;
|
|
3337
|
+
}
|
|
3338
|
+
const cwd = this.sessionManager.getCwd();
|
|
3339
|
+
const resolved = path.resolve(cwd, filePath);
|
|
3340
|
+
if (!fs.existsSync(resolved)) {
|
|
3341
|
+
this.showError(`File not found: ${filePath}`);
|
|
3342
|
+
return;
|
|
3343
|
+
}
|
|
3344
|
+
const rel = path.relative(cwd, resolved);
|
|
3345
|
+
this.settingsManager.addPinnedFile(rel);
|
|
3346
|
+
this.showStatus(`Pinned: ${rel}`);
|
|
3347
|
+
}
|
|
3348
|
+
handleUnpinCommand(filePath) {
|
|
3349
|
+
if (!filePath) {
|
|
3350
|
+
this.showError("Usage: /unpin <file-path>");
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3353
|
+
const cwd = this.sessionManager.getCwd();
|
|
3354
|
+
const rel = path.relative(cwd, path.resolve(cwd, filePath));
|
|
3355
|
+
const pinned = this.settingsManager.getPinnedFiles();
|
|
3356
|
+
if (!pinned.includes(rel)) {
|
|
3357
|
+
this.showError(`Not pinned: ${rel}`);
|
|
3358
|
+
return;
|
|
3359
|
+
}
|
|
3360
|
+
this.settingsManager.removePinnedFile(rel);
|
|
3361
|
+
this.showStatus(`Unpinned: ${rel}`);
|
|
3362
|
+
}
|
|
3363
|
+
handlePinsCommand() {
|
|
3364
|
+
const pinned = this.settingsManager.getPinnedFiles();
|
|
3365
|
+
if (pinned.length === 0) {
|
|
3366
|
+
this.showStatus("No pinned files");
|
|
3367
|
+
return;
|
|
3368
|
+
}
|
|
3369
|
+
let info = `${theme.bold("Pinned Files")}\n\n`;
|
|
3370
|
+
for (const file of pinned) {
|
|
3371
|
+
info += ` ${theme.fg("accent", file)}\n`;
|
|
3372
|
+
}
|
|
3373
|
+
info += `\n${theme.fg("dim", `${pinned.length} file(s) pinned to context`)}`;
|
|
3374
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3375
|
+
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
3376
|
+
this.ui.requestRender();
|
|
3377
|
+
}
|
|
3378
|
+
handleBlueprintCommand(name) {
|
|
3379
|
+
const cwd = this.sessionManager.getCwd();
|
|
3380
|
+
const dirs = [getProjectBlueprintsDir(cwd), getBlueprintsDir()];
|
|
3381
|
+
const blueprints = new Map();
|
|
3382
|
+
for (const dir of dirs) {
|
|
3383
|
+
if (!fs.existsSync(dir))
|
|
3384
|
+
continue;
|
|
3385
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
3386
|
+
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
3387
|
+
continue;
|
|
3388
|
+
const bpName = entry.name.slice(0, -3);
|
|
3389
|
+
if (!blueprints.has(bpName)) {
|
|
3390
|
+
blueprints.set(bpName, path.resolve(dir, entry.name));
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
if (!name) {
|
|
3395
|
+
if (blueprints.size === 0) {
|
|
3396
|
+
this.showStatus("No blueprints found. Add .md files to .elyra/blueprints/ or ~/.elyra/agent/blueprints/");
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3399
|
+
let info = `${theme.bold("Available Blueprints")}\n\n`;
|
|
3400
|
+
for (const [bpName] of blueprints) {
|
|
3401
|
+
info += ` ${theme.fg("accent", bpName)}\n`;
|
|
3402
|
+
}
|
|
3403
|
+
info += `\n${theme.fg("dim", "Usage: /blueprint <name>")}`;
|
|
3404
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3405
|
+
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
3406
|
+
this.ui.requestRender();
|
|
3407
|
+
return;
|
|
3408
|
+
}
|
|
3409
|
+
const bpPath = blueprints.get(name);
|
|
3410
|
+
if (!bpPath) {
|
|
3411
|
+
const available = Array.from(blueprints.keys()).join(", ");
|
|
3412
|
+
this.showError(`Blueprint not found: "${name}". Available: ${available || "none"}`);
|
|
3413
|
+
return;
|
|
3414
|
+
}
|
|
3415
|
+
try {
|
|
3416
|
+
const content = fs.readFileSync(bpPath, "utf-8");
|
|
3417
|
+
// Parse optional YAML frontmatter
|
|
3418
|
+
let body = content;
|
|
3419
|
+
const pins = [];
|
|
3420
|
+
if (content.startsWith("---\n")) {
|
|
3421
|
+
const endIdx = content.indexOf("\n---\n", 4);
|
|
3422
|
+
if (endIdx !== -1) {
|
|
3423
|
+
const frontmatter = content.slice(4, endIdx);
|
|
3424
|
+
body = content.slice(endIdx + 5);
|
|
3425
|
+
for (const line of frontmatter.split("\n")) {
|
|
3426
|
+
const pinMatch = line.match(/^pin:\s*(.+)$/);
|
|
3427
|
+
if (pinMatch) {
|
|
3428
|
+
pins.push(...pinMatch[1]
|
|
3429
|
+
.split(",")
|
|
3430
|
+
.map((s) => s.trim())
|
|
3431
|
+
.filter(Boolean));
|
|
3432
|
+
}
|
|
3433
|
+
const pinsMatch = line.match(/^pins:\s*\[(.+)\]$/);
|
|
3434
|
+
if (pinsMatch) {
|
|
3435
|
+
pins.push(...pinsMatch[1]
|
|
3436
|
+
.split(",")
|
|
3437
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ""))
|
|
3438
|
+
.filter(Boolean));
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
// Apply pins from frontmatter
|
|
3444
|
+
for (const pin of pins) {
|
|
3445
|
+
const resolved = path.resolve(cwd, pin);
|
|
3446
|
+
if (fs.existsSync(resolved)) {
|
|
3447
|
+
const rel = path.relative(cwd, resolved);
|
|
3448
|
+
this.settingsManager.addPinnedFile(rel);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
// Send blueprint content as user message to the agent
|
|
3452
|
+
this.session.prompt(body.trim());
|
|
3453
|
+
const pinMsg = pins.length > 0 ? ` (pinned ${pins.length} file(s))` : "";
|
|
3454
|
+
this.showStatus(`Blueprint: ${name}${pinMsg}`);
|
|
3455
|
+
}
|
|
3456
|
+
catch {
|
|
3457
|
+
this.showError(`Failed to read blueprint: ${bpPath}`);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3262
3460
|
handleThemeCommand(themeName) {
|
|
3263
3461
|
if (!themeName) {
|
|
3264
3462
|
this.showThemeSelector();
|