@ryuu-reinzz/ryuu-cli 0.4.3 → 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/CHANGELOG.md +70 -0
- package/README.md +26 -0
- package/docs/drive.md +484 -0
- package/package.json +4 -1
- package/plugins/google-drive/auth.js +57 -0
- package/plugins/google-drive/backup.js +282 -0
- package/plugins/google-drive/command.js +266 -0
- package/plugins/google-drive/config.js +252 -0
- package/plugins/google-drive/credentials.js +171 -0
- package/plugins/google-drive/drive-api.js +304 -0
- package/plugins/google-drive/index.js +26 -0
- package/plugins/google-drive/manager.js +507 -0
- package/plugins/google-drive/oauth-backend.js +161 -0
- package/plugins/google-drive/secret.js +50 -0
- package/plugins/google-drive/token-client.js +80 -0
- package/plugins/google-drive/tool-helpers.js +46 -0
- package/plugins/google-drive/tools-backup.js +204 -0
- package/plugins/google-drive/tools-files.js +315 -0
- package/plugins/google-drive/tools.js +13 -0
- package/plugins/google-drive/zip-reader.js +217 -0
- package/plugins/google-drive/zip-writer.js +204 -0
- package/plugins/index.js +56 -0
- package/src/agent/agent.js +12 -2
- package/src/cli/app.js +16 -0
- package/src/cli/commands.js +19 -0
- package/src/cli/markdown.js +10 -5
- package/src/cli/renderer.js +38 -5
- package/src/cli/tui.js +80 -9
- package/src/tools/registry.js +13 -0
package/src/cli/tui.js
CHANGED
|
@@ -31,7 +31,8 @@ const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇',
|
|
|
31
31
|
/**
|
|
32
32
|
* One colour per kind of line, so a glance tells blocks apart:
|
|
33
33
|
* prose white, Markdown accents and tables sky blue, tool names yellow,
|
|
34
|
-
*
|
|
34
|
+
* plugin tool names sky blue in bold, reasoning green, failures red.
|
|
35
|
+
* This palette is the only place colours live.
|
|
35
36
|
*/
|
|
36
37
|
const PALETTE = {
|
|
37
38
|
white: '#e2e8f0',
|
|
@@ -556,6 +557,35 @@ function buildRows(item, width, { c, color }) {
|
|
|
556
557
|
return rows;
|
|
557
558
|
}
|
|
558
559
|
|
|
560
|
+
if (item.kind === 'code') {
|
|
561
|
+
// A framed monospace block with a `⧉ copy` chip. The whole box is the click
|
|
562
|
+
// target, so a long URL is copied in one click instead of a text selection.
|
|
563
|
+
const body = String(item.text ?? '');
|
|
564
|
+
const label = String(item.label ?? '');
|
|
565
|
+
if (/(^|\n)\s*```/.test(body)) {
|
|
566
|
+
// A body that itself carries a fence cannot be framed safely: print it plain.
|
|
567
|
+
for (const line of wrapAnsi(body, inner)) rows.push(line ? ` ${line}` : '');
|
|
568
|
+
return rows;
|
|
569
|
+
}
|
|
570
|
+
const lang = label.replace(/[^A-Za-z0-9_+#.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
571
|
+
const copies = new Map();
|
|
572
|
+
const offset = rows.length;
|
|
573
|
+
const lines = renderMarkdown('```' + `${lang}\n${body}\n` + '```', {
|
|
574
|
+
width: Math.max(20, width - 2),
|
|
575
|
+
c,
|
|
576
|
+
color,
|
|
577
|
+
base: (text) => paint('white', text),
|
|
578
|
+
accent: (text) => paint('sky', text),
|
|
579
|
+
onCodeBlock: ({ start, end, code }) => {
|
|
580
|
+
const target = { lang: label, code: String(code ?? '').slice(0, MAX_CODE_COPY_CHARS) };
|
|
581
|
+
for (let row = start; row <= end; row += 1) copies.set(offset + row, target);
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
for (const line of lines) rows.push(line ? ` ${line}` : '');
|
|
585
|
+
if (copies.size > 0) rows.copies = copies;
|
|
586
|
+
return rows;
|
|
587
|
+
}
|
|
588
|
+
|
|
559
589
|
if (item.kind === 'answer') {
|
|
560
590
|
// Every block carries a small label, like reasoning does, so white response
|
|
561
591
|
// text is never mistaken for the tail of a tool or a notice.
|
|
@@ -564,6 +594,9 @@ function buildRows(item, width, { c, color }) {
|
|
|
564
594
|
// Every rendered row of a fenced code block is remembered so a click on its
|
|
565
595
|
// `⧉ copy` chip (or anywhere in the block) copies exactly that code.
|
|
566
596
|
const copies = new Map();
|
|
597
|
+
// Code-block row numbers are relative to this item's own rows, not to the
|
|
598
|
+
// transcript, so the click map keeps working after scrolling.
|
|
599
|
+
const base = rows.length;
|
|
567
600
|
const lines = renderMarkdown(item.text || '', {
|
|
568
601
|
width: Math.max(20, width - 2),
|
|
569
602
|
c,
|
|
@@ -572,7 +605,7 @@ function buildRows(item, width, { c, color }) {
|
|
|
572
605
|
accent: (text) => paint('sky', text),
|
|
573
606
|
onCodeBlock: ({ lang, code, start, end }) => {
|
|
574
607
|
const target = { lang, code: String(code ?? '').slice(0, MAX_CODE_COPY_CHARS) };
|
|
575
|
-
for (let row = start; row <= end; row += 1) copies.set(row, target);
|
|
608
|
+
for (let row = start; row <= end; row += 1) copies.set(base + row, target);
|
|
576
609
|
},
|
|
577
610
|
});
|
|
578
611
|
for (const line of lines) rows.push(line ? ` ${line}` : '');
|
|
@@ -583,7 +616,13 @@ function buildRows(item, width, { c, color }) {
|
|
|
583
616
|
if (item.kind === 'tool') {
|
|
584
617
|
const failed = item.ok === false;
|
|
585
618
|
const mark = failed ? paint('red', '✕') : paint('green', '✓');
|
|
586
|
-
|
|
619
|
+
// Plugin tools wear their own colour — sky blue and bold — so an installed
|
|
620
|
+
// integration never looks like built-in tooling. A failure is still red.
|
|
621
|
+
const name = failed
|
|
622
|
+
? c.bold(paint('red', item.name))
|
|
623
|
+
: item.plugin
|
|
624
|
+
? c.bold(paint('sky', item.name))
|
|
625
|
+
: paint('yellow', item.name);
|
|
587
626
|
if (failed) {
|
|
588
627
|
pushHeader(`${mark} ${name}${item.durationMs ? ` ${dim(`· ${formatSeconds(item.durationMs)}`)}` : ''}`);
|
|
589
628
|
pushWrapped(item.summary || 'tool failed', ' ', 'redSoft', { dimmed: true });
|
|
@@ -730,7 +769,7 @@ export class Tui {
|
|
|
730
769
|
step: null,
|
|
731
770
|
maxSteps: null,
|
|
732
771
|
tool: '',
|
|
733
|
-
|
|
772
|
+
toolPlugin: null,
|
|
734
773
|
completionTokens: 0,
|
|
735
774
|
frame: 0,
|
|
736
775
|
output: '',
|
|
@@ -891,7 +930,29 @@ export class Tui {
|
|
|
891
930
|
}
|
|
892
931
|
|
|
893
932
|
lines(text = '') {
|
|
894
|
-
|
|
933
|
+
// One command usually prints many lines. They belong to a single block: a
|
|
934
|
+
// rule between every line is what made command output look ragged.
|
|
935
|
+
const value = String(text ?? '');
|
|
936
|
+
const items = this.transcript.items;
|
|
937
|
+
const last = items[items.length - 1];
|
|
938
|
+
if (last && last.kind === 'info' && last.fromLines) {
|
|
939
|
+
last.text = `${last.text}\n${value}`;
|
|
940
|
+
last.version += 1;
|
|
941
|
+
} else {
|
|
942
|
+
const item = this.transcript.message(value);
|
|
943
|
+
item.fromLines = true;
|
|
944
|
+
}
|
|
945
|
+
this.requestRender();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* A monospace block with a `⧉ copy` chip (renderMarkdown draws the frame).
|
|
950
|
+
* The whole block is a click target, like a code block in an answer.
|
|
951
|
+
*/
|
|
952
|
+
showCode({ label = '', text = '' } = {}) {
|
|
953
|
+
const body = String(text ?? '');
|
|
954
|
+
if (!body) return;
|
|
955
|
+
this.transcript.add({ kind: 'code', label: String(label ?? ''), text: body, version: 0 });
|
|
895
956
|
this.requestRender();
|
|
896
957
|
}
|
|
897
958
|
|
|
@@ -1067,14 +1128,17 @@ export class Tui {
|
|
|
1067
1128
|
this.requestRender();
|
|
1068
1129
|
}
|
|
1069
1130
|
|
|
1070
|
-
toolStart({ name = '', detail = '' } = {}) {
|
|
1131
|
+
toolStart({ name = '', detail = '', plugin = null } = {}) {
|
|
1071
1132
|
this.status.tool = name;
|
|
1133
|
+
// Plugin tools keep their own colour in the status bar too.
|
|
1134
|
+
this.status.toolPlugin = plugin || null;
|
|
1072
1135
|
this.requestRender();
|
|
1073
1136
|
}
|
|
1074
1137
|
|
|
1075
|
-
toolEnd({ name = '', ok = true, durationMs = 0, summary = '' } = {}) {
|
|
1138
|
+
toolEnd({ name = '', ok = true, durationMs = 0, summary = '', plugin = null } = {}) {
|
|
1076
1139
|
this.status.tool = '';
|
|
1077
|
-
this.
|
|
1140
|
+
this.status.toolPlugin = null;
|
|
1141
|
+
this.transcript.add({ kind: 'tool', name, ok, durationMs, summary, plugin: plugin || null, version: 0 });
|
|
1078
1142
|
this.requestRender();
|
|
1079
1143
|
}
|
|
1080
1144
|
|
|
@@ -1433,7 +1497,14 @@ export class Tui {
|
|
|
1433
1497
|
this.paint('blue', `step ${this.status.step}${this.status.maxSteps ? `/${this.status.maxSteps}` : ''}`),
|
|
1434
1498
|
);
|
|
1435
1499
|
}
|
|
1436
|
-
if (this.status.tool)
|
|
1500
|
+
if (this.status.tool) {
|
|
1501
|
+
// Plugin tools are sky blue and bold here too, matching the transcript.
|
|
1502
|
+
parts.push(
|
|
1503
|
+
this.status.toolPlugin
|
|
1504
|
+
? this.c.bold(this.paint('sky', this.status.tool))
|
|
1505
|
+
: this.paint('yellow', this.status.tool),
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1437
1508
|
if (this.toast && Date.now() <= this.toast.until) parts.push(this.paint('green', this.toast.text));
|
|
1438
1509
|
if (this.status.promptTokens || this.status.completionTokens) {
|
|
1439
1510
|
parts.push(
|
package/src/tools/registry.js
CHANGED
|
@@ -76,6 +76,19 @@ export class ToolRegistry {
|
|
|
76
76
|
return tool;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* The plugin a tool belongs to, or null for a built-in tool.
|
|
81
|
+
*
|
|
82
|
+
* The CLI uses this to give plugin tools their own colour, so an integration
|
|
83
|
+
* the user installed never looks like core tooling.
|
|
84
|
+
*/
|
|
85
|
+
pluginFor(name) {
|
|
86
|
+
for (const plugin of this.plugins.values()) {
|
|
87
|
+
if (plugin.toolNames.includes(name)) return plugin;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
79
92
|
has(name) {
|
|
80
93
|
return this.tools.has(name);
|
|
81
94
|
}
|