@owlburtoe/pi-cc-tools 1.0.0 → 1.0.1
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/README.md +29 -11
- package/config/config.example.json +8 -1
- package/extensions/index.ts +277 -35
- package/extensions/message-chrome.ts +142 -0
- package/extensions/spinner.ts +25 -3
- package/package.json +12 -8
package/README.md
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
# pi-
|
|
2
|
-
|
|
3
|
-
## Install
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
pi install npm:@owlburtoe/pi-cc-tools
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
Run `/reload` or restart Pi after installation.
|
|
10
|
-
|
|
1
|
+
# pi-claude-style-tools
|
|
11
2
|
|
|
12
3
|
Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots, branch connectors, file icons, and configurable output modes.
|
|
13
4
|
|
|
@@ -20,6 +11,7 @@ Claude Code inspired tool rendering for Pi — Shiki-powered diffs, status dots,
|
|
|
20
11
|
- **Diff stat bar** with colored add/remove summary and hunk metadata
|
|
21
12
|
- **Progressive collapsed diff hints** that shorten on narrow terminals
|
|
22
13
|
- **Thinking labels** during streaming and final messages, with context sanitization
|
|
14
|
+
- **Claude-style transcript grammar controls** for assistant/thinking prefixes, message spacing, hidden thinking labels, and active working tips
|
|
23
15
|
- **MCP-aware rendering** with hidden, summary, and preview modes
|
|
24
16
|
- **Configurable output modes** for read, search, bash, and MCP results
|
|
25
17
|
- **Transparent tool backgrounds** in `transparent` or `border` mode
|
|
@@ -45,7 +37,14 @@ Set in `.pi/settings.json` or `~/.pi/settings.json`:
|
|
|
45
37
|
"diffTheme": "github-dark",
|
|
46
38
|
"spinnerColor": "borderAccent",
|
|
47
39
|
"spinnerVerbs": ["Reviewing", "Polishing"],
|
|
48
|
-
"spinnerVerbMode": "append"
|
|
40
|
+
"spinnerVerbMode": "append",
|
|
41
|
+
"messageStyle": "claude",
|
|
42
|
+
"assistantPrefix": "●",
|
|
43
|
+
"thinkingPrefix": "✻",
|
|
44
|
+
"messageSpacing": "comfortable",
|
|
45
|
+
"workingTipEnabled": true,
|
|
46
|
+
"workingTipText": "Run /install-github-app to tag @claude right from your GitHub issues and PRs",
|
|
47
|
+
"hiddenThinkingLabel": "Pondering..."
|
|
49
48
|
}
|
|
50
49
|
```
|
|
51
50
|
|
|
@@ -100,6 +99,25 @@ The spinner glyph and verb text (e.g. `✻ Cooking…`) share `borderAccent` by
|
|
|
100
99
|
|
|
101
100
|
Color selections are persisted as `spinnerColor` / `spinnerStatusColor` in `~/.pi/settings.json` and applied on the next spinner tick. Older `spinnerVerbColor` settings still work as a backward-compatible alias. Custom verbs are persisted as `spinnerVerbs` and `spinnerVerbMode`; they are picked at the next turn start. `/cc-spinner verbs ...` writes user settings in `~/.pi/settings.json`; project-level custom verbs can be set manually in `.pi/settings.json`. When both project and user spinner settings exist, the spinner reader applies project settings first and user settings second.
|
|
102
101
|
|
|
102
|
+
#### Tune assistant/thinking transcript chrome with `/cc-message`
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
/cc-message # show current message chrome settings
|
|
106
|
+
/cc-message style claude # screenshot-style transcript rhythm
|
|
107
|
+
/cc-message style classic # older package spacing/prefix behavior
|
|
108
|
+
/cc-message spacing comfortable # keep one blank line between paragraphs
|
|
109
|
+
/cc-message spacing compact # remove blank lines inside assistant/thinking blocks
|
|
110
|
+
/cc-message assistant-prefix ● # set assistant paragraph prefix
|
|
111
|
+
/cc-message thinking-prefix ✻ # set visible thinking prefix
|
|
112
|
+
/cc-message hidden-thinking-label Pondering...
|
|
113
|
+
/cc-message tip on # show active working tip line
|
|
114
|
+
/cc-message tip off # hide active working tip line
|
|
115
|
+
/cc-message tip text Run /help for tips # set active working tip text
|
|
116
|
+
/cc-message reset # restore message chrome defaults
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`messageStyle: "claude"` trims leading/trailing blank render lines, collapses paragraph gaps, and aligns wrapped assistant/thinking lines under the message body, matching Claude Code's sparse transcript grammar. `messageStyle: "classic"` keeps the previous package behavior. The active working tip is rendered as a subordinate `└─ Tip:` line under the spinner when supported by the Pi loader.
|
|
120
|
+
|
|
103
121
|
### Tool background modes
|
|
104
122
|
|
|
105
123
|
| Value | Behavior |
|
|
@@ -14,5 +14,12 @@
|
|
|
14
14
|
"diffColors": {},
|
|
15
15
|
"spinnerColor": "borderAccent",
|
|
16
16
|
"spinnerVerbs": ["Reviewing", "Polishing"],
|
|
17
|
-
"spinnerVerbMode": "append"
|
|
17
|
+
"spinnerVerbMode": "append",
|
|
18
|
+
"messageStyle": "claude",
|
|
19
|
+
"assistantPrefix": "●",
|
|
20
|
+
"thinkingPrefix": "✻",
|
|
21
|
+
"messageSpacing": "comfortable",
|
|
22
|
+
"workingTipEnabled": true,
|
|
23
|
+
"workingTipText": "Run /install-github-app to tag @claude right from your GitHub issues and PRs",
|
|
24
|
+
"hiddenThinkingLabel": "Pondering..."
|
|
18
25
|
}
|
package/extensions/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
GrepToolDetails,
|
|
10
10
|
ReadToolDetails,
|
|
11
11
|
Theme,
|
|
12
|
-
} from "@
|
|
12
|
+
} from "@mariozechner/pi-coding-agent";
|
|
13
13
|
import {
|
|
14
14
|
AssistantMessageComponent,
|
|
15
15
|
CustomMessageComponent,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
createLsTool,
|
|
23
23
|
createReadTool,
|
|
24
24
|
createWriteTool,
|
|
25
|
-
} from "@
|
|
25
|
+
} from "@mariozechner/pi-coding-agent";
|
|
26
26
|
import {
|
|
27
27
|
Box,
|
|
28
28
|
Container,
|
|
@@ -36,11 +36,21 @@ import {
|
|
|
36
36
|
truncateToWidth,
|
|
37
37
|
visibleWidth,
|
|
38
38
|
wrapTextWithAnsi,
|
|
39
|
-
} from "@
|
|
39
|
+
} from "@mariozechner/pi-tui";
|
|
40
40
|
|
|
41
41
|
import * as Diff from "diff";
|
|
42
42
|
import type { BundledLanguage, BundledTheme } from "shiki";
|
|
43
43
|
|
|
44
|
+
import {
|
|
45
|
+
DEFAULT_HIDDEN_THINKING_LABEL,
|
|
46
|
+
formatTranscriptLines,
|
|
47
|
+
resolveMessageChromeSettings,
|
|
48
|
+
sanitizeMessagePrefix,
|
|
49
|
+
type MessageChromeSettings,
|
|
50
|
+
type MessageSpacing,
|
|
51
|
+
type MessageStyle,
|
|
52
|
+
} from "./message-chrome.ts";
|
|
53
|
+
|
|
44
54
|
const RESET = "\x1b[0m";
|
|
45
55
|
const TRANSPARENT_BG = "\x1b[49m";
|
|
46
56
|
const TRANSPARENT_RESET = `${RESET}${TRANSPARENT_BG}`;
|
|
@@ -105,6 +115,20 @@ interface SettingsFile {
|
|
|
105
115
|
spinnerVerbs?: string[];
|
|
106
116
|
/** Whether custom spinnerVerbs append to or replace the default verb list. */
|
|
107
117
|
spinnerVerbMode?: SpinnerVerbMode;
|
|
118
|
+
/** Assistant/thinking transcript chrome style. "claude" trims spacing like Claude Code; "classic" keeps the older package rhythm. */
|
|
119
|
+
messageStyle?: MessageStyle;
|
|
120
|
+
/** Prefix glyph/text for assistant paragraphs. Defaults to Claude Code-style "●". */
|
|
121
|
+
assistantPrefix?: string;
|
|
122
|
+
/** Prefix glyph/text for visible thinking paragraphs. Defaults to "✻". */
|
|
123
|
+
thinkingPrefix?: string;
|
|
124
|
+
/** Blank-line normalization in assistant/thinking transcript blocks. */
|
|
125
|
+
messageSpacing?: MessageSpacing;
|
|
126
|
+
/** Whether the active working spinner may show a subordinate Claude-style tip line. */
|
|
127
|
+
workingTipEnabled?: boolean;
|
|
128
|
+
/** Custom subordinate tip line text for the active working spinner. */
|
|
129
|
+
workingTipText?: string;
|
|
130
|
+
/** Label shown when thinking blocks are hidden by the Pi UI. */
|
|
131
|
+
hiddenThinkingLabel?: string;
|
|
108
132
|
}
|
|
109
133
|
|
|
110
134
|
let _settingsCache: { value: SettingsFile; timestamp: number } | null = null;
|
|
@@ -144,7 +168,13 @@ function bustSpinnerSettingsCache(): void {
|
|
|
144
168
|
(globalThis as any)[SPINNER_BUST_KEY] = current + 1;
|
|
145
169
|
}
|
|
146
170
|
|
|
171
|
+
let _mergedSettingsCache: { value: SettingsFile; timestamp: number } | null = null;
|
|
172
|
+
|
|
147
173
|
function readMergedSettings(): SettingsFile {
|
|
174
|
+
const now = Date.now();
|
|
175
|
+
if (_mergedSettingsCache && now - _mergedSettingsCache.timestamp < SETTINGS_CACHE_TTL_MS) {
|
|
176
|
+
return _mergedSettingsCache.value;
|
|
177
|
+
}
|
|
148
178
|
const paths = [`${process.cwd()}/.pi/settings.json`, `${process.env.HOME ?? ""}/.pi/settings.json`];
|
|
149
179
|
const merged: SettingsFile = {};
|
|
150
180
|
for (const path of paths) {
|
|
@@ -156,9 +186,20 @@ function readMergedSettings(): SettingsFile {
|
|
|
156
186
|
// ignore invalid settings files
|
|
157
187
|
}
|
|
158
188
|
}
|
|
189
|
+
_mergedSettingsCache = { value: merged, timestamp: now };
|
|
159
190
|
return merged;
|
|
160
191
|
}
|
|
161
192
|
|
|
193
|
+
function getMessageChromeSettings(): MessageChromeSettings {
|
|
194
|
+
return resolveMessageChromeSettings(readMergedSettings());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function applyHiddenThinkingLabel(ctx: any): void {
|
|
198
|
+
if (!ctx?.hasUI || typeof ctx.ui?.setHiddenThinkingLabel !== "function") return;
|
|
199
|
+
const label = getMessageChromeSettings().hiddenThinkingLabel;
|
|
200
|
+
ctx.ui.setHiddenThinkingLabel(label || DEFAULT_HIDDEN_THINKING_LABEL);
|
|
201
|
+
}
|
|
202
|
+
|
|
162
203
|
const MAX_CUSTOM_SPINNER_VERBS = 200;
|
|
163
204
|
const MAX_SPINNER_VERB_LENGTH = 48;
|
|
164
205
|
const ANSI_ESCAPE_SEQUENCE_RE = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
@@ -196,6 +237,7 @@ function getSpinnerVerbMode(settings: SettingsFile): SpinnerVerbMode {
|
|
|
196
237
|
|
|
197
238
|
function writeSettingsKey(key: string, value: unknown): void {
|
|
198
239
|
_settingsCache = null; // invalidate cache on write
|
|
240
|
+
_mergedSettingsCache = null;
|
|
199
241
|
const home = process.env.HOME ?? "";
|
|
200
242
|
if (!home) return;
|
|
201
243
|
const dir = `${home}/.pi`;
|
|
@@ -474,9 +516,43 @@ function appendWorkedDurationLine(message: any, durationMs: number): void {
|
|
|
474
516
|
lastText.text = `${text.trimEnd()}\n\n${inlineWorkedDurationText(durationMs)}`;
|
|
475
517
|
}
|
|
476
518
|
|
|
519
|
+
function messageChromeCacheKey(settings: MessageChromeSettings, kind: "assistant" | "thinking"): string {
|
|
520
|
+
return [
|
|
521
|
+
settings.messageStyle,
|
|
522
|
+
settings.messageSpacing,
|
|
523
|
+
kind === "assistant" ? settings.assistantPrefix : settings.thinkingPrefix,
|
|
524
|
+
].join(":");
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function renderClassicPrefixedLines(lines: string[], prefix: string, normalizeChecks = true): string[] {
|
|
528
|
+
let prefixPlaced = false;
|
|
529
|
+
return lines.map((line: string) => {
|
|
530
|
+
const displayLine = normalizeChecks ? normalizeLeadingCheckGlyph(line) : line;
|
|
531
|
+
if (!prefixPlaced && stripAnsi(displayLine).trim()) {
|
|
532
|
+
prefixPlaced = true;
|
|
533
|
+
return ` ${prefix} ${displayLine}`;
|
|
534
|
+
}
|
|
535
|
+
return ` ${displayLine}`;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function colorFirstTranscriptPrefix(lines: string[], prefixGlyph: string, coloredPrefixGlyph: string): string[] {
|
|
540
|
+
const plainPrefix = ` ${prefixGlyph} `;
|
|
541
|
+
const coloredPrefix = ` ${coloredPrefixGlyph} `;
|
|
542
|
+
let replaced = false;
|
|
543
|
+
return lines.map((line) => {
|
|
544
|
+
if (!replaced && line.startsWith(plainPrefix)) {
|
|
545
|
+
replaced = true;
|
|
546
|
+
return `${coloredPrefix}${line.slice(plainPrefix.length)}`;
|
|
547
|
+
}
|
|
548
|
+
return line;
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
477
552
|
class DottedParagraph {
|
|
478
553
|
private md: InstanceType<typeof Markdown>;
|
|
479
554
|
private cachedWidth?: number;
|
|
555
|
+
private cachedChromeKey?: string;
|
|
480
556
|
private cachedLines?: string[];
|
|
481
557
|
|
|
482
558
|
constructor(text: string, markdownTheme: ConstructorParameters<typeof Markdown>[3]) {
|
|
@@ -485,31 +561,35 @@ class DottedParagraph {
|
|
|
485
561
|
|
|
486
562
|
invalidate(): void {
|
|
487
563
|
this.cachedWidth = undefined;
|
|
564
|
+
this.cachedChromeKey = undefined;
|
|
488
565
|
this.cachedLines = undefined;
|
|
489
566
|
this.md.invalidate();
|
|
490
567
|
}
|
|
491
568
|
|
|
492
569
|
render(width: number): string[] {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
570
|
+
const settings = getMessageChromeSettings();
|
|
571
|
+
const chromeKey = messageChromeCacheKey(settings, "assistant");
|
|
572
|
+
if (this.cachedLines && this.cachedWidth === width && this.cachedChromeKey === chromeKey) return this.cachedLines;
|
|
573
|
+
const prefixGlyph = settings.messageStyle === "classic" ? "●" : settings.assistantPrefix;
|
|
574
|
+
const prefixWidth = Math.max(1, visibleWidth(` ${prefixGlyph} `));
|
|
575
|
+
if (width <= prefixWidth) {
|
|
497
576
|
this.cachedWidth = width;
|
|
498
|
-
this.
|
|
577
|
+
this.cachedChromeKey = chromeKey;
|
|
578
|
+
this.cachedLines = [` ${prefixGlyph} `];
|
|
499
579
|
return this.cachedLines;
|
|
500
580
|
}
|
|
501
|
-
const lines = sanitizeRenderedTextBlockLines(this.md.render(width -
|
|
581
|
+
const lines = sanitizeRenderedTextBlockLines(this.md.render(width - prefixWidth));
|
|
502
582
|
const looksLikeTaskStatus = lines.some((line) => /\b(?:transcript:|No output\.|Wrapped up)/.test(stripAnsi(line)));
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
});
|
|
583
|
+
const rendered = settings.messageStyle === "classic"
|
|
584
|
+
? renderClassicPrefixedLines(lines, "●", looksLikeTaskStatus)
|
|
585
|
+
: formatTranscriptLines(lines, {
|
|
586
|
+
prefix: settings.assistantPrefix,
|
|
587
|
+
spacing: settings.messageSpacing,
|
|
588
|
+
normalizeChecks: looksLikeTaskStatus,
|
|
589
|
+
visibleWidth,
|
|
590
|
+
});
|
|
512
591
|
this.cachedWidth = width;
|
|
592
|
+
this.cachedChromeKey = chromeKey;
|
|
513
593
|
this.cachedLines = rendered;
|
|
514
594
|
return rendered;
|
|
515
595
|
}
|
|
@@ -518,6 +598,7 @@ class DottedParagraph {
|
|
|
518
598
|
class ThinkingParagraph {
|
|
519
599
|
private md: InstanceType<typeof Markdown>;
|
|
520
600
|
private cachedWidth?: number;
|
|
601
|
+
private cachedChromeKey?: string;
|
|
521
602
|
private cachedLines?: string[];
|
|
522
603
|
|
|
523
604
|
constructor(
|
|
@@ -559,30 +640,39 @@ class ThinkingParagraph {
|
|
|
559
640
|
|
|
560
641
|
invalidate(): void {
|
|
561
642
|
this.cachedWidth = undefined;
|
|
643
|
+
this.cachedChromeKey = undefined;
|
|
562
644
|
this.cachedLines = undefined;
|
|
563
645
|
this.md.invalidate();
|
|
564
646
|
}
|
|
565
647
|
|
|
566
648
|
render(width: number): string[] {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
|
|
649
|
+
const settings = getMessageChromeSettings();
|
|
650
|
+
const chromeKey = messageChromeCacheKey(settings, "thinking");
|
|
651
|
+
if (this.cachedLines && this.cachedWidth === width && this.cachedChromeKey === chromeKey) return this.cachedLines;
|
|
652
|
+
const prefixGlyph = settings.messageStyle === "classic" ? "✻" : settings.thinkingPrefix;
|
|
653
|
+
const coloredPrefixGlyph = `${WORKED_LINE_FG}${prefixGlyph}${RESET}`;
|
|
654
|
+
const prefixWidth = Math.max(1, visibleWidth(` ${prefixGlyph} `));
|
|
655
|
+
if (width <= prefixWidth) {
|
|
572
656
|
this.cachedWidth = width;
|
|
573
|
-
this.
|
|
657
|
+
this.cachedChromeKey = chromeKey;
|
|
658
|
+
this.cachedLines = [` ${coloredPrefixGlyph} `];
|
|
574
659
|
return this.cachedLines;
|
|
575
660
|
}
|
|
576
|
-
const lines = sanitizeRenderedTextBlockLines(this.md.render(width -
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
661
|
+
const lines = sanitizeRenderedTextBlockLines(this.md.render(width - prefixWidth));
|
|
662
|
+
const rendered = settings.messageStyle === "classic"
|
|
663
|
+
? renderClassicPrefixedLines(lines, coloredPrefixGlyph, false)
|
|
664
|
+
: colorFirstTranscriptPrefix(
|
|
665
|
+
formatTranscriptLines(lines, {
|
|
666
|
+
prefix: prefixGlyph,
|
|
667
|
+
spacing: settings.messageSpacing,
|
|
668
|
+
normalizeChecks: false,
|
|
669
|
+
visibleWidth,
|
|
670
|
+
}),
|
|
671
|
+
prefixGlyph,
|
|
672
|
+
coloredPrefixGlyph,
|
|
673
|
+
);
|
|
585
674
|
this.cachedWidth = width;
|
|
675
|
+
this.cachedChromeKey = chromeKey;
|
|
586
676
|
this.cachedLines = rendered;
|
|
587
677
|
return rendered;
|
|
588
678
|
}
|
|
@@ -2641,13 +2731,14 @@ function stripThinkingPresentationArtifacts(text: string): string {
|
|
|
2641
2731
|
}
|
|
2642
2732
|
|
|
2643
2733
|
function prefixThinkingLine(text: string, _theme: Theme | undefined): string {
|
|
2644
|
-
|
|
2734
|
+
const settings = getMessageChromeSettings();
|
|
2735
|
+
if (settings.messageStyle === "classic" && !ANSI_PRESENT_RE.test(text) && text.startsWith("Thinking: ") && !/^Thinking:\s*thinking:\s*/i.test(text)) {
|
|
2645
2736
|
return text;
|
|
2646
2737
|
}
|
|
2647
2738
|
const normalized = stripThinkingPresentationArtifacts(text).trim();
|
|
2648
2739
|
if (!normalized) return text;
|
|
2649
2740
|
// Plain text — no ANSI colors, no theme. The ThinkingParagraph handles styling.
|
|
2650
|
-
return `Thinking: ${normalized}
|
|
2741
|
+
return settings.messageStyle === "classic" ? `Thinking: ${normalized}` : normalized;
|
|
2651
2742
|
}
|
|
2652
2743
|
|
|
2653
2744
|
function registerThinkingLabels(pi: ExtensionAPI): void {
|
|
@@ -3920,16 +4011,167 @@ export default function (pi: ExtensionAPI) {
|
|
|
3920
4011
|
},
|
|
3921
4012
|
});
|
|
3922
4013
|
|
|
4014
|
+
const MESSAGE_COMMANDS = ["style", "spacing", "assistant-prefix", "thinking-prefix", "tip", "hidden-thinking-label", "reset", "status"] as const;
|
|
4015
|
+
pi.registerCommand("cc-message", {
|
|
4016
|
+
description: "Configure Claude-style assistant/thinking transcript chrome",
|
|
4017
|
+
getArgumentCompletions(prefix: string) {
|
|
4018
|
+
const parts = prefix.split(/\s+/);
|
|
4019
|
+
if (parts.length <= 1) {
|
|
4020
|
+
const first = (parts[0] ?? "").toLowerCase();
|
|
4021
|
+
return MESSAGE_COMMANDS
|
|
4022
|
+
.filter((c) => c.startsWith(first))
|
|
4023
|
+
.map((c) => ({
|
|
4024
|
+
value: c,
|
|
4025
|
+
label: c,
|
|
4026
|
+
description:
|
|
4027
|
+
c === "style" ? "Set transcript style: claude or classic"
|
|
4028
|
+
: c === "spacing" ? "Set blank-line rhythm: compact or comfortable"
|
|
4029
|
+
: c === "assistant-prefix" ? "Set assistant paragraph prefix glyph/text"
|
|
4030
|
+
: c === "thinking-prefix" ? "Set visible thinking prefix glyph/text"
|
|
4031
|
+
: c === "tip" ? "Enable, disable, or customize active working tip line"
|
|
4032
|
+
: c === "hidden-thinking-label" ? "Set label shown when thinking is hidden"
|
|
4033
|
+
: c === "reset" ? "Reset message chrome settings"
|
|
4034
|
+
: "Show current message chrome settings",
|
|
4035
|
+
}));
|
|
4036
|
+
}
|
|
4037
|
+
if (parts[0] === "style") {
|
|
4038
|
+
const valuePrefix = (parts[1] ?? "").toLowerCase();
|
|
4039
|
+
return ["claude", "classic"]
|
|
4040
|
+
.filter((v) => v.startsWith(valuePrefix))
|
|
4041
|
+
.map((v) => ({ value: `style ${v}`, label: v, description: v === "claude" ? "Screenshot-style transcript rhythm" : "Older package spacing/prefix behavior" }));
|
|
4042
|
+
}
|
|
4043
|
+
if (parts[0] === "spacing") {
|
|
4044
|
+
const valuePrefix = (parts[1] ?? "").toLowerCase();
|
|
4045
|
+
return ["comfortable", "compact"]
|
|
4046
|
+
.filter((v) => v.startsWith(valuePrefix))
|
|
4047
|
+
.map((v) => ({ value: `spacing ${v}`, label: v, description: v === "comfortable" ? "Preserve one blank line between paragraphs" : "Remove blank lines inside assistant/thinking blocks" }));
|
|
4048
|
+
}
|
|
4049
|
+
if (parts[0] === "tip") {
|
|
4050
|
+
const valuePrefix = (parts[1] ?? "").toLowerCase();
|
|
4051
|
+
return ["on", "off", "text", "reset"]
|
|
4052
|
+
.filter((v) => v.startsWith(valuePrefix))
|
|
4053
|
+
.map((v) => ({ value: `tip ${v}`, label: v, description: v === "text" ? "Set custom active working tip text" : `${v} working tip line` }));
|
|
4054
|
+
}
|
|
4055
|
+
return [];
|
|
4056
|
+
},
|
|
4057
|
+
async handler(args: string, ctx: any) {
|
|
4058
|
+
const rawArgs = args.trim();
|
|
4059
|
+
const parts = rawArgs.split(/\s+/).filter((p: string) => p.length > 0);
|
|
4060
|
+
const sub = (parts[0] ?? "status").toLowerCase();
|
|
4061
|
+
const current = getMessageChromeSettings();
|
|
4062
|
+
const notifyStatus = () => {
|
|
4063
|
+
if (!ctx.hasUI) return;
|
|
4064
|
+
ctx.ui.notify([
|
|
4065
|
+
`Message style: ${current.messageStyle}`,
|
|
4066
|
+
`Assistant prefix: ${current.assistantPrefix}`,
|
|
4067
|
+
`Thinking prefix: ${current.thinkingPrefix}`,
|
|
4068
|
+
`Spacing: ${current.messageSpacing}`,
|
|
4069
|
+
`Working tip: ${current.workingTipEnabled ? "on" : "off"}`,
|
|
4070
|
+
`Tip text: ${current.workingTipText}`,
|
|
4071
|
+
`Hidden thinking label: ${current.hiddenThinkingLabel}`,
|
|
4072
|
+
].join("\n"), "info");
|
|
4073
|
+
};
|
|
4074
|
+
|
|
4075
|
+
if (!rawArgs || sub === "status") {
|
|
4076
|
+
notifyStatus();
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
if (sub === "reset") {
|
|
4081
|
+
for (const key of ["messageStyle", "assistantPrefix", "thinkingPrefix", "messageSpacing", "workingTipEnabled", "workingTipText", "hiddenThinkingLabel"]) {
|
|
4082
|
+
writeSettingsKey(key, undefined);
|
|
4083
|
+
}
|
|
4084
|
+
bustSpinnerSettingsCache();
|
|
4085
|
+
applyHiddenThinkingLabel(ctx);
|
|
4086
|
+
if (ctx.hasUI) ctx.ui.notify("Message chrome reset to Claude-style defaults", "info");
|
|
4087
|
+
return;
|
|
4088
|
+
}
|
|
4089
|
+
|
|
4090
|
+
if (sub === "style") {
|
|
4091
|
+
const value = (parts[1] ?? "").toLowerCase();
|
|
4092
|
+
if (value !== "claude" && value !== "classic") {
|
|
4093
|
+
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message style claude|classic", "error");
|
|
4094
|
+
return;
|
|
4095
|
+
}
|
|
4096
|
+
writeSettingsKey("messageStyle", value);
|
|
4097
|
+
writeSettingsKey("workingTipEnabled", value === "classic" ? false : undefined);
|
|
4098
|
+
bustSpinnerSettingsCache();
|
|
4099
|
+
if (ctx.hasUI) ctx.ui.notify(`Message style → ${value}`, "info");
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4102
|
+
|
|
4103
|
+
if (sub === "spacing") {
|
|
4104
|
+
const value = (parts[1] ?? "").toLowerCase();
|
|
4105
|
+
if (value !== "compact" && value !== "comfortable") {
|
|
4106
|
+
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message spacing compact|comfortable", "error");
|
|
4107
|
+
return;
|
|
4108
|
+
}
|
|
4109
|
+
writeSettingsKey("messageSpacing", value);
|
|
4110
|
+
if (ctx.hasUI) ctx.ui.notify(`Message spacing → ${value}`, "info");
|
|
4111
|
+
return;
|
|
4112
|
+
}
|
|
4113
|
+
|
|
4114
|
+
if (sub === "assistant-prefix" || sub === "thinking-prefix") {
|
|
4115
|
+
const rawValue = rawArgs.replace(new RegExp(`^${sub}\\s*`, "i"), "");
|
|
4116
|
+
const fallback = sub === "assistant-prefix" ? "●" : "✻";
|
|
4117
|
+
const value = sanitizeMessagePrefix(rawValue, fallback);
|
|
4118
|
+
writeSettingsKey(sub === "assistant-prefix" ? "assistantPrefix" : "thinkingPrefix", value);
|
|
4119
|
+
if (ctx.hasUI) ctx.ui.notify(`${sub === "assistant-prefix" ? "Assistant" : "Thinking"} prefix → ${value}`, "info");
|
|
4120
|
+
return;
|
|
4121
|
+
}
|
|
4122
|
+
|
|
4123
|
+
if (sub === "tip") {
|
|
4124
|
+
const action = (parts[1] ?? "").toLowerCase();
|
|
4125
|
+
if (action === "on" || action === "off") {
|
|
4126
|
+
writeSettingsKey("workingTipEnabled", action === "on");
|
|
4127
|
+
bustSpinnerSettingsCache();
|
|
4128
|
+
if (ctx.hasUI) ctx.ui.notify(`Working tip → ${action}`, "info");
|
|
4129
|
+
return;
|
|
4130
|
+
}
|
|
4131
|
+
if (action === "reset") {
|
|
4132
|
+
writeSettingsKey("workingTipEnabled", undefined);
|
|
4133
|
+
writeSettingsKey("workingTipText", undefined);
|
|
4134
|
+
bustSpinnerSettingsCache();
|
|
4135
|
+
if (ctx.hasUI) ctx.ui.notify("Working tip reset", "info");
|
|
4136
|
+
return;
|
|
4137
|
+
}
|
|
4138
|
+
if (action === "text") {
|
|
4139
|
+
const textArg = rawArgs.match(/^tip\s+text(?:\s+(.+))?$/i)?.[1] ?? "";
|
|
4140
|
+
const value = resolveMessageChromeSettings({ workingTipText: textArg }).workingTipText;
|
|
4141
|
+
writeSettingsKey("workingTipText", value);
|
|
4142
|
+
bustSpinnerSettingsCache();
|
|
4143
|
+
if (ctx.hasUI) ctx.ui.notify(`Working tip text → ${value}`, "info");
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4146
|
+
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message tip on|off|reset|text <text>", "error");
|
|
4147
|
+
return;
|
|
4148
|
+
}
|
|
4149
|
+
|
|
4150
|
+
if (sub === "hidden-thinking-label") {
|
|
4151
|
+
const textArg = rawArgs.match(/^hidden-thinking-label(?:\s+(.+))?$/i)?.[1] ?? "";
|
|
4152
|
+
const value = resolveMessageChromeSettings({ hiddenThinkingLabel: textArg }).hiddenThinkingLabel;
|
|
4153
|
+
writeSettingsKey("hiddenThinkingLabel", value);
|
|
4154
|
+
applyHiddenThinkingLabel(ctx);
|
|
4155
|
+
if (ctx.hasUI) ctx.ui.notify(`Hidden thinking label → ${value}`, "info");
|
|
4156
|
+
return;
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4159
|
+
if (ctx.hasUI) ctx.ui.notify("Usage: /cc-message style|spacing|assistant-prefix|thinking-prefix|tip|hidden-thinking-label|reset|status", "error");
|
|
4160
|
+
},
|
|
4161
|
+
});
|
|
4162
|
+
|
|
3923
4163
|
pi.on("session_start", async (_event, ctx) => {
|
|
3924
4164
|
if (!ctx.hasUI) return;
|
|
3925
4165
|
applyToolBackgroundMode(ctx.ui.theme);
|
|
3926
4166
|
applyThemePaletteIfNeeded(ctx.ui.theme);
|
|
4167
|
+
applyHiddenThinkingLabel(ctx);
|
|
3927
4168
|
});
|
|
3928
4169
|
|
|
3929
4170
|
pi.on("turn_start", async (_event, ctx) => {
|
|
3930
4171
|
if (!ctx.hasUI) return;
|
|
3931
4172
|
applyToolBackgroundMode(ctx.ui.theme);
|
|
3932
4173
|
applyThemePaletteIfNeeded(ctx.ui.theme);
|
|
4174
|
+
applyHiddenThinkingLabel(ctx);
|
|
3933
4175
|
});
|
|
3934
4176
|
|
|
3935
4177
|
const cwd = process.cwd();
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export type MessageStyle = "classic" | "claude";
|
|
2
|
+
export type MessageSpacing = "compact" | "comfortable";
|
|
3
|
+
|
|
4
|
+
export interface MessageChromeInput {
|
|
5
|
+
messageStyle?: unknown;
|
|
6
|
+
assistantPrefix?: unknown;
|
|
7
|
+
thinkingPrefix?: unknown;
|
|
8
|
+
messageSpacing?: unknown;
|
|
9
|
+
workingTipEnabled?: unknown;
|
|
10
|
+
workingTipText?: unknown;
|
|
11
|
+
hiddenThinkingLabel?: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MessageChromeSettings {
|
|
15
|
+
messageStyle: MessageStyle;
|
|
16
|
+
assistantPrefix: string;
|
|
17
|
+
thinkingPrefix: string;
|
|
18
|
+
messageSpacing: MessageSpacing;
|
|
19
|
+
workingTipEnabled: boolean;
|
|
20
|
+
workingTipText: string;
|
|
21
|
+
hiddenThinkingLabel: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TranscriptLineOptions {
|
|
25
|
+
prefix: string;
|
|
26
|
+
spacing?: MessageSpacing;
|
|
27
|
+
normalizeChecks?: boolean;
|
|
28
|
+
visibleWidth?: (text: string) => number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const DEFAULT_WORKING_TIP_TEXT = "Run /install-github-app to tag @claude right from your GitHub issues and PRs";
|
|
32
|
+
export const DEFAULT_HIDDEN_THINKING_LABEL = "Pondering...";
|
|
33
|
+
|
|
34
|
+
const DEFAULT_MESSAGE_CHROME: MessageChromeSettings = {
|
|
35
|
+
messageStyle: "claude",
|
|
36
|
+
assistantPrefix: "●",
|
|
37
|
+
thinkingPrefix: "✻",
|
|
38
|
+
messageSpacing: "comfortable",
|
|
39
|
+
workingTipEnabled: true,
|
|
40
|
+
workingTipText: DEFAULT_WORKING_TIP_TEXT,
|
|
41
|
+
hiddenThinkingLabel: DEFAULT_HIDDEN_THINKING_LABEL,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const ANSI_ESCAPE_SEQUENCE_RE = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
|
|
45
|
+
const ANSI_SGR_RE = /\x1b\[[0-9;]*m/g;
|
|
46
|
+
const CONTROL_CHARS_RE = /[\u0000-\u001F\u007F-\u009F]/g;
|
|
47
|
+
const MAX_PREFIX_GRAPHEMES = 8;
|
|
48
|
+
const MAX_TIP_GRAPHEMES = 160;
|
|
49
|
+
const MAX_LABEL_GRAPHEMES = 80;
|
|
50
|
+
|
|
51
|
+
function stripAnsi(text: string): string {
|
|
52
|
+
return text.replace(ANSI_ESCAPE_SEQUENCE_RE, "").replace(ANSI_SGR_RE, "");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function stripControlChars(text: string): string {
|
|
56
|
+
return text.replace(CONTROL_CHARS_RE, "");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function takeGraphemes(text: string, max: number): string {
|
|
60
|
+
return Array.from(text).slice(0, max).join("");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fallbackVisibleWidth(text: string): number {
|
|
64
|
+
return Array.from(stripAnsi(text)).length;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function isBlankLine(text: string): boolean {
|
|
68
|
+
return stripAnsi(text).trim().length === 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function sanitizeTextSetting(value: unknown, fallback: string, maxGraphemes: number): string {
|
|
72
|
+
if (typeof value !== "string") return fallback;
|
|
73
|
+
const cleaned = takeGraphemes(stripControlChars(stripAnsi(value)).trim(), maxGraphemes);
|
|
74
|
+
return cleaned || fallback;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function sanitizeMessagePrefix(value: unknown, fallback: string): string {
|
|
78
|
+
return sanitizeTextSetting(value, fallback, MAX_PREFIX_GRAPHEMES);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function resolveMessageChromeSettings(input: MessageChromeInput = {}): MessageChromeSettings {
|
|
82
|
+
const messageStyle: MessageStyle = input.messageStyle === "classic" ? "classic" : "claude";
|
|
83
|
+
const messageSpacing: MessageSpacing = input.messageSpacing === "compact" ? "compact" : "comfortable";
|
|
84
|
+
const defaultWorkingTipEnabled = messageStyle === "classic" ? false : DEFAULT_MESSAGE_CHROME.workingTipEnabled;
|
|
85
|
+
return {
|
|
86
|
+
messageStyle,
|
|
87
|
+
assistantPrefix: sanitizeMessagePrefix(input.assistantPrefix, DEFAULT_MESSAGE_CHROME.assistantPrefix),
|
|
88
|
+
thinkingPrefix: sanitizeMessagePrefix(input.thinkingPrefix, DEFAULT_MESSAGE_CHROME.thinkingPrefix),
|
|
89
|
+
messageSpacing,
|
|
90
|
+
workingTipEnabled: typeof input.workingTipEnabled === "boolean" ? input.workingTipEnabled : defaultWorkingTipEnabled,
|
|
91
|
+
workingTipText: sanitizeTextSetting(input.workingTipText, DEFAULT_MESSAGE_CHROME.workingTipText, MAX_TIP_GRAPHEMES),
|
|
92
|
+
hiddenThinkingLabel: sanitizeTextSetting(input.hiddenThinkingLabel, DEFAULT_MESSAGE_CHROME.hiddenThinkingLabel, MAX_LABEL_GRAPHEMES),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function normalizeTranscriptRenderedLines(lines: string[], spacing: MessageSpacing): string[] {
|
|
97
|
+
let start = 0;
|
|
98
|
+
while (start < lines.length && isBlankLine(lines[start])) start++;
|
|
99
|
+
let end = lines.length - 1;
|
|
100
|
+
while (end >= start && isBlankLine(lines[end])) end--;
|
|
101
|
+
if (start > end) return [];
|
|
102
|
+
|
|
103
|
+
const normalized: string[] = [];
|
|
104
|
+
let pendingBlank = false;
|
|
105
|
+
for (const line of lines.slice(start, end + 1)) {
|
|
106
|
+
if (isBlankLine(line)) {
|
|
107
|
+
if (spacing === "comfortable" && normalized.length > 0) pendingBlank = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (pendingBlank) {
|
|
111
|
+
normalized.push("");
|
|
112
|
+
pendingBlank = false;
|
|
113
|
+
}
|
|
114
|
+
normalized.push(line);
|
|
115
|
+
}
|
|
116
|
+
return normalized;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function normalizeLeadingCheckGlyph(line: string): string {
|
|
120
|
+
return line.replace(/^((?:\x1b\[[0-9;]*m|[ \t])*)[✓✔](?=\s)/, "$1●");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function formatTranscriptLines(lines: string[], options: TranscriptLineOptions): string[] {
|
|
124
|
+
const prefixGlyph = sanitizeMessagePrefix(options.prefix, DEFAULT_MESSAGE_CHROME.assistantPrefix);
|
|
125
|
+
const spacing = options.spacing ?? DEFAULT_MESSAGE_CHROME.messageSpacing;
|
|
126
|
+
const normalized = normalizeTranscriptRenderedLines(lines, spacing);
|
|
127
|
+
const prefix = ` ${prefixGlyph} `;
|
|
128
|
+
const measureWidth = options.visibleWidth ?? fallbackVisibleWidth;
|
|
129
|
+
const continuation = " ".repeat(Math.max(1, measureWidth(prefix)));
|
|
130
|
+
|
|
131
|
+
if (normalized.length === 0) return [prefix];
|
|
132
|
+
|
|
133
|
+
let prefixPlaced = false;
|
|
134
|
+
return normalized.map((line) => {
|
|
135
|
+
const displayLine = options.normalizeChecks === false ? line : normalizeLeadingCheckGlyph(line);
|
|
136
|
+
if (!prefixPlaced && !isBlankLine(displayLine)) {
|
|
137
|
+
prefixPlaced = true;
|
|
138
|
+
return `${prefix}${displayLine}`;
|
|
139
|
+
}
|
|
140
|
+
return `${continuation}${displayLine}`;
|
|
141
|
+
});
|
|
142
|
+
}
|
package/extensions/spinner.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import type { ExtensionAPI } from "@
|
|
4
|
-
import { Loader } from "@
|
|
3
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
4
|
+
import { Loader } from "@mariozechner/pi-tui";
|
|
5
|
+
|
|
6
|
+
import { resolveMessageChromeSettings } from "./message-chrome.ts";
|
|
5
7
|
|
|
6
8
|
// ---------------------------------------------------------------------------
|
|
7
9
|
// Patch built-in Loader with Claude/OpenBrawd-style glyphs.
|
|
@@ -26,6 +28,8 @@ interface SpinnerSettings {
|
|
|
26
28
|
verbColor: string;
|
|
27
29
|
statusColor: string;
|
|
28
30
|
verbs: readonly string[];
|
|
31
|
+
workingTipEnabled: boolean;
|
|
32
|
+
workingTipText: string;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
let _spinnerSettingsCache: { value: SpinnerSettings; expires: number } | null = null;
|
|
@@ -96,6 +100,9 @@ function readSpinnerSettings(): SpinnerSettings {
|
|
|
96
100
|
let statusColor = "muted";
|
|
97
101
|
let customVerbs: string[] | null = null;
|
|
98
102
|
let verbMode: SpinnerVerbMode = "append";
|
|
103
|
+
let messageStyle: string | undefined;
|
|
104
|
+
let workingTipEnabled: boolean | undefined;
|
|
105
|
+
let workingTipText: string | undefined;
|
|
99
106
|
const paths = [`${process.cwd()}/.pi/settings.json`, `${process.env.HOME ?? ""}/.pi/settings.json`];
|
|
100
107
|
for (const p of paths) {
|
|
101
108
|
try {
|
|
@@ -108,10 +115,21 @@ function readSpinnerSettings(): SpinnerSettings {
|
|
|
108
115
|
if (typeof raw.spinnerStatusColor === "string" && raw.spinnerStatusColor.length > 0) statusColor = raw.spinnerStatusColor;
|
|
109
116
|
if (raw.spinnerVerbMode === "append" || raw.spinnerVerbMode === "replace") verbMode = raw.spinnerVerbMode;
|
|
110
117
|
if (Array.isArray(raw.spinnerVerbs)) customVerbs = sanitizeSpinnerVerbs(raw.spinnerVerbs);
|
|
118
|
+
if (typeof raw.messageStyle === "string") messageStyle = raw.messageStyle;
|
|
119
|
+
if (typeof raw.workingTipEnabled === "boolean") workingTipEnabled = raw.workingTipEnabled;
|
|
120
|
+
if (typeof raw.workingTipText === "string") workingTipText = raw.workingTipText;
|
|
111
121
|
}
|
|
112
122
|
} catch { /* ignore */ }
|
|
113
123
|
}
|
|
114
|
-
const
|
|
124
|
+
const messageChrome = resolveMessageChromeSettings({ messageStyle, workingTipEnabled, workingTipText });
|
|
125
|
+
const value: SpinnerSettings = {
|
|
126
|
+
adaptive,
|
|
127
|
+
verbColor,
|
|
128
|
+
statusColor,
|
|
129
|
+
verbs: resolveSpinnerVerbs(customVerbs, verbMode),
|
|
130
|
+
workingTipEnabled: messageChrome.workingTipEnabled,
|
|
131
|
+
workingTipText: messageChrome.workingTipText,
|
|
132
|
+
};
|
|
115
133
|
_spinnerSettingsCache = { value, expires: now + SPINNER_SETTINGS_TTL_MS };
|
|
116
134
|
return value;
|
|
117
135
|
}
|
|
@@ -549,6 +567,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
549
567
|
if (statusParts.length > 0) {
|
|
550
568
|
message += statusText(` (${statusParts.join(" · ")})`);
|
|
551
569
|
}
|
|
570
|
+
const { workingTipEnabled, workingTipText } = readSpinnerSettings();
|
|
571
|
+
if (workingTipEnabled && workingTipText) {
|
|
572
|
+
message += `\n${STATUS_DIM} └─ Tip: ${workingTipText}${RESET}`;
|
|
573
|
+
}
|
|
552
574
|
return message;
|
|
553
575
|
}
|
|
554
576
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlburtoe/pi-cc-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Claude Code-style tool rows for pi with Ctrl+O image previews and consistent built-in, MCP, and custom tool rendering",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"diff"
|
|
15
15
|
],
|
|
16
16
|
"license": "MIT",
|
|
17
|
-
"author": "
|
|
17
|
+
"author": "FammasMaz",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "git+https://forgejo.owlburtoe.dev/owlburtoe/pi-claude-style-tools.git"
|
|
@@ -26,19 +26,22 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"extensions/index.ts",
|
|
28
28
|
"extensions/spinner.ts",
|
|
29
|
+
"extensions/message-chrome.ts",
|
|
29
30
|
"config/config.example.json",
|
|
30
31
|
"README.md"
|
|
31
32
|
],
|
|
32
33
|
"scripts": {
|
|
33
|
-
"bench:tools": "bun scripts/benchmark-tools.ts"
|
|
34
|
+
"bench:tools": "bun scripts/benchmark-tools.ts",
|
|
35
|
+
"test:message-chrome": "bun scripts/test-message-chrome.ts"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
38
|
"@shikijs/cli": "^4.0.2",
|
|
37
39
|
"diff": "^7.0.0"
|
|
38
40
|
},
|
|
39
41
|
"peerDependencies": {
|
|
40
|
-
"@
|
|
41
|
-
"@
|
|
42
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
43
|
+
"@mariozechner/pi-tui": "*",
|
|
44
|
+
"@sinclair/typebox": "*"
|
|
42
45
|
},
|
|
43
46
|
"pi": {
|
|
44
47
|
"extensions": [
|
|
@@ -47,10 +50,11 @@
|
|
|
47
50
|
]
|
|
48
51
|
},
|
|
49
52
|
"devDependencies": {
|
|
53
|
+
"@mariozechner/pi-coding-agent": "latest",
|
|
54
|
+
"@mariozechner/pi-tui": "latest",
|
|
55
|
+
"@sinclair/typebox": "latest",
|
|
50
56
|
"@types/diff": "^7.0.2",
|
|
51
57
|
"@types/node": "^25.6.0",
|
|
52
|
-
"typescript": "^6.0.2"
|
|
53
|
-
"@earendil-works/pi-coding-agent": "latest",
|
|
54
|
-
"@earendil-works/pi-tui": "latest"
|
|
58
|
+
"typescript": "^6.0.2"
|
|
55
59
|
}
|
|
56
60
|
}
|