killeros 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/Killeros.ts +81 -129
- package/README.md +7 -11
- package/package.json +3 -2
package/Killeros.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import {
|
|
14
14
|
Container,
|
|
15
|
+
decodeKittyPrintable,
|
|
15
16
|
Editor,
|
|
16
17
|
Key,
|
|
17
18
|
matchesKey,
|
|
@@ -35,30 +36,17 @@ const FOOTER_REFRESH_INTERVAL_MS = 1_000;
|
|
|
35
36
|
|
|
36
37
|
const brand = (text: string): string => `\x1B[38;2;${BRAND_RGB}m${text}\x1B[39m`;
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
...Array.from({ length: 3 }, (_, ay): LogoFrame => ({ phase: 1, active: "top", ax: 2, ay, flash: false, white: false })),
|
|
50
|
-
...Array.from({ length: 5 }, (_, ay): LogoFrame => ({ phase: 2, active: "right", ax: 5, ay, flash: false, white: false })),
|
|
51
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
52
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: true, white: false },
|
|
53
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
54
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: true, white: false },
|
|
55
|
-
{ phase: 4, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
56
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
57
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: true },
|
|
58
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
59
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: true },
|
|
60
|
-
{ phase: 6, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
61
|
-
];
|
|
39
|
+
const ORCA_ART = [
|
|
40
|
+
".....g......",
|
|
41
|
+
"....ggg.....",
|
|
42
|
+
".gggggggg..g",
|
|
43
|
+
"gbggwwgggggg",
|
|
44
|
+
".ggwwgggg..g",
|
|
45
|
+
"...gggg.....",
|
|
46
|
+
".....gg.....",
|
|
47
|
+
] as const;
|
|
48
|
+
const ORCA_WIDTH = ORCA_ART[0].length;
|
|
49
|
+
const LOGO_FRAME_COUNT = ORCA_WIDTH + 3;
|
|
62
50
|
|
|
63
51
|
function extractProvider(model: ExtensionContext["model"]): string {
|
|
64
52
|
return model?.provider ?? "";
|
|
@@ -89,71 +77,27 @@ function padRight(text: string, width: number): string {
|
|
|
89
77
|
return clipped + " ".repeat(Math.max(0, width - visibleWidth(clipped)));
|
|
90
78
|
}
|
|
91
79
|
|
|
92
|
-
|
|
93
|
-
return cells.split(" ").includes(`${y},${x}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function hasPiece(y: number, x: number, py: number, px: number, cells: string): boolean {
|
|
97
|
-
return cells.split(" ").some((item) => {
|
|
98
|
-
const [dy, dx] = item.split(",").map(Number);
|
|
99
|
-
return y === py + dy && x === px + dx;
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
type LogoColor = "cyan" | "red" | "green" | "orange" | "flash" | "white" | "brand" | "panel";
|
|
80
|
+
type LogoColor = "body" | "white" | "brand" | "panel";
|
|
104
81
|
|
|
105
82
|
function colorCell(color: LogoColor): string {
|
|
106
83
|
switch (color) {
|
|
107
|
-
case "
|
|
108
|
-
case "red": return `\x1B[31m${LOGO_CELL}\x1B[39m`;
|
|
109
|
-
case "green": return `\x1B[32m${LOGO_CELL}\x1B[39m`;
|
|
110
|
-
case "orange":
|
|
111
|
-
case "flash": return `\x1B[33m${LOGO_CELL}\x1B[39m`;
|
|
84
|
+
case "body": return `\x1B[90m${LOGO_CELL}\x1B[39m`;
|
|
112
85
|
case "white": return `\x1B[97m${LOGO_CELL}\x1B[39m`;
|
|
113
86
|
case "brand": return brand(LOGO_CELL);
|
|
114
87
|
default: return " ".repeat(LOGO_CELL.length);
|
|
115
88
|
}
|
|
116
89
|
}
|
|
117
90
|
|
|
118
|
-
function logoCellColor(frame: LogoFrame, y: number, x: number): LogoColor {
|
|
119
|
-
if (frame.white) {
|
|
120
|
-
return hasCell(y, x, "3,2 3,3 3,4 4,2 4,4 5,2 5,3 5,5 6,2 6,5") ? "white" : "panel";
|
|
121
|
-
}
|
|
122
|
-
if (frame.flash && y === 6 && x >= 1 && x <= 6) return "flash";
|
|
123
|
-
if (frame.active === "left" && hasPiece(y, x, frame.ay, frame.ax, "0,0 1,0 1,1 2,0")) return "red";
|
|
124
|
-
if (frame.active === "top" && hasPiece(y, x, frame.ay, frame.ax, "0,0 0,1 0,2 1,2")) return "cyan";
|
|
125
|
-
if (frame.active === "right" && hasPiece(y, x, frame.ay, frame.ax, "0,0 1,0 2,0 2,1")) return "green";
|
|
126
|
-
if (frame.phase === 6) {
|
|
127
|
-
return hasCell(y, x, "3,2 3,3 3,4 4,4 4,2 5,2 5,3 5,5 6,2 6,5") ? "brand" : "panel";
|
|
128
|
-
}
|
|
129
|
-
if (frame.phase === 4) {
|
|
130
|
-
if (hasCell(y, x, "2,2 2,3 2,4 3,4")) return "cyan";
|
|
131
|
-
if (hasCell(y, x, "3,2 4,2 4,3 5,2")) return "red";
|
|
132
|
-
if (hasCell(y, x, "4,5 5,5")) return "green";
|
|
133
|
-
return "panel";
|
|
134
|
-
}
|
|
135
|
-
if (frame.phase >= 5) {
|
|
136
|
-
if (hasCell(y, x, "3,2 3,3 3,4 4,4")) return "cyan";
|
|
137
|
-
if (hasCell(y, x, "4,2 5,2 5,3 6,2")) return "red";
|
|
138
|
-
if (hasCell(y, x, "5,5 6,5")) return "green";
|
|
139
|
-
return "panel";
|
|
140
|
-
}
|
|
141
|
-
if (frame.phase <= 3 && hasCell(y, x, "6,1 6,2 6,3 6,4")) return "orange";
|
|
142
|
-
if (frame.phase >= 2 && hasCell(y, x, "2,2 2,3 2,4 3,4")) return "cyan";
|
|
143
|
-
if (frame.phase >= 1 && hasCell(y, x, "3,2 4,2 4,3 5,2")) return "red";
|
|
144
|
-
if (frame.phase >= 3 && hasCell(y, x, "4,5 5,5 6,5 6,6")) return "green";
|
|
145
|
-
return "panel";
|
|
146
|
-
}
|
|
147
|
-
|
|
148
91
|
function piLogoFrame(frameIndex: number): string[] {
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
92
|
+
const visibleColumns = Math.min(ORCA_WIDTH, frameIndex + 1);
|
|
93
|
+
const eyeColor: LogoColor = frameIndex === ORCA_WIDTH ? "white" : "brand";
|
|
94
|
+
return ORCA_ART.map((row) => [...row].map((cell, index) => {
|
|
95
|
+
if (index >= visibleColumns) return colorCell("panel");
|
|
96
|
+
if (cell === "g") return colorCell("body");
|
|
97
|
+
if (cell === "w") return colorCell("white");
|
|
98
|
+
if (cell === "b") return colorCell(eyeColor);
|
|
99
|
+
return colorCell("panel");
|
|
100
|
+
}).join(""));
|
|
157
101
|
}
|
|
158
102
|
|
|
159
103
|
function borderLine(left: string, label: string, right: string, width: number): string {
|
|
@@ -248,6 +192,9 @@ function getTipLines(index: number, theme: Theme): string[] {
|
|
|
248
192
|
}
|
|
249
193
|
|
|
250
194
|
class PiStartupHeader {
|
|
195
|
+
private readonly pi: ExtensionAPI;
|
|
196
|
+
private readonly ctx: ExtensionContext;
|
|
197
|
+
private readonly tui: TUI;
|
|
251
198
|
private frame = 0;
|
|
252
199
|
private tipIndex = 0;
|
|
253
200
|
private animationTimer?: ReturnType<typeof setInterval>;
|
|
@@ -255,19 +202,22 @@ class PiStartupHeader {
|
|
|
255
202
|
private disposed = false;
|
|
256
203
|
|
|
257
204
|
constructor(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
205
|
+
pi: ExtensionAPI,
|
|
206
|
+
ctx: ExtensionContext,
|
|
207
|
+
tui: TUI,
|
|
261
208
|
) {
|
|
209
|
+
this.pi = pi;
|
|
210
|
+
this.ctx = ctx;
|
|
211
|
+
this.tui = tui;
|
|
262
212
|
this.animationTimer = setInterval(() => {
|
|
263
213
|
if (this.disposed) return;
|
|
264
|
-
if (this.frame >=
|
|
214
|
+
if (this.frame >= LOGO_FRAME_COUNT - 1) {
|
|
265
215
|
this.stopAnimation();
|
|
266
216
|
return;
|
|
267
217
|
}
|
|
268
218
|
this.frame += 1;
|
|
269
219
|
this.tui.requestRender();
|
|
270
|
-
if (this.frame >=
|
|
220
|
+
if (this.frame >= LOGO_FRAME_COUNT - 1) this.stopAnimation();
|
|
271
221
|
}, LOGO_ANIMATION_INTERVAL_MS);
|
|
272
222
|
this.animationTimer.unref?.();
|
|
273
223
|
|
|
@@ -301,7 +251,7 @@ class PiStartupHeader {
|
|
|
301
251
|
const cwd = formatCwd(this.ctx.cwd);
|
|
302
252
|
const leftWidth = isTwoColumn ? Math.min(LEFT_PANEL_WIDTH, Math.floor(innerWidth * 0.55)) : innerWidth;
|
|
303
253
|
const rightWidth = isTwoColumn ? innerWidth - leftWidth - 3 : 0;
|
|
304
|
-
const logoLines = leftWidth >=
|
|
254
|
+
const logoLines = leftWidth >= ORCA_WIDTH * visibleWidth(LOGO_CELL)
|
|
305
255
|
? piLogoFrame(this.frame).map((line) => center(line, leftWidth))
|
|
306
256
|
: ["", "", center(brand(theme.bold("Pi Coding Agent")), leftWidth), "", "", "", ""];
|
|
307
257
|
const modelText = leftWidth < 34 ? `${model} (${effort})` : `${model} with ${effort} effort`;
|
|
@@ -348,8 +298,11 @@ function isBorderLine(line: string): boolean {
|
|
|
348
298
|
}
|
|
349
299
|
|
|
350
300
|
class PiCodeEditor extends CustomEditor {
|
|
351
|
-
|
|
301
|
+
private readonly appKeybindings: KeybindingsManager;
|
|
302
|
+
|
|
303
|
+
constructor(tui: TUI, theme: EditorTheme, appKeybindings: KeybindingsManager) {
|
|
352
304
|
super(tui, theme, appKeybindings);
|
|
305
|
+
this.appKeybindings = appKeybindings;
|
|
353
306
|
}
|
|
354
307
|
|
|
355
308
|
override handleInput(data: string): void {
|
|
@@ -514,7 +467,34 @@ function rememberCustomInput(value: string): void {
|
|
|
514
467
|
}
|
|
515
468
|
|
|
516
469
|
function isPrintableInput(data: string): boolean {
|
|
517
|
-
return data.length > 0 && !/[\u0000-\u001F\u007F]/u.test(data);
|
|
470
|
+
return data.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/u.test(data);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
474
|
+
|
|
475
|
+
function decodeQuestionFilterInput(data: string): string | undefined {
|
|
476
|
+
const kittyPrintable = decodeKittyPrintable(data);
|
|
477
|
+
if (kittyPrintable !== undefined) return isPrintableInput(kittyPrintable) ? kittyPrintable : undefined;
|
|
478
|
+
|
|
479
|
+
const pasteStart = "\x1B[200~";
|
|
480
|
+
const pasteEnd = "\x1B[201~";
|
|
481
|
+
const startIndex = data.indexOf(pasteStart);
|
|
482
|
+
const endIndex = data.indexOf(pasteEnd, startIndex + pasteStart.length);
|
|
483
|
+
if (startIndex >= 0 && endIndex >= 0) {
|
|
484
|
+
return data
|
|
485
|
+
.slice(startIndex + pasteStart.length, endIndex)
|
|
486
|
+
.replace(/\r\n|\r|\n/gu, "")
|
|
487
|
+
.replace(/\t/gu, " ")
|
|
488
|
+
.replace(/[\u0000-\u001F\u007F-\u009F]/gu, "");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return isPrintableInput(data) ? data : undefined;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function removeLastGrapheme(value: string): string {
|
|
495
|
+
const segments = [...graphemeSegmenter.segment(value)];
|
|
496
|
+
const last = segments.at(-1);
|
|
497
|
+
return last ? value.slice(0, last.index) : "";
|
|
518
498
|
}
|
|
519
499
|
|
|
520
500
|
function registerQuestionTool(pi: ExtensionAPI): void {
|
|
@@ -552,8 +532,6 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
552
532
|
let optionIndex = 0;
|
|
553
533
|
let editMode = false;
|
|
554
534
|
let filterQuery = "";
|
|
555
|
-
let historyIndex = -1;
|
|
556
|
-
let savedDraft = "";
|
|
557
535
|
let cachedWidth: number | undefined;
|
|
558
536
|
let cachedLines: string[] | undefined;
|
|
559
537
|
let completed = false;
|
|
@@ -576,6 +554,7 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
576
554
|
},
|
|
577
555
|
};
|
|
578
556
|
const editor = new Editor(tui, editorTheme);
|
|
557
|
+
customInputHistory.forEach((value) => editor.addToHistory(value));
|
|
579
558
|
|
|
580
559
|
const filteredOptions = (): DisplayOption[] => {
|
|
581
560
|
const query = filterQuery.trim().toLocaleLowerCase();
|
|
@@ -605,15 +584,11 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
605
584
|
}
|
|
606
585
|
editMode = false;
|
|
607
586
|
editor.setText("");
|
|
608
|
-
historyIndex = -1;
|
|
609
|
-
savedDraft = "";
|
|
610
587
|
refresh();
|
|
611
588
|
};
|
|
612
589
|
|
|
613
590
|
const enterCustomMode = (): void => {
|
|
614
591
|
editMode = true;
|
|
615
|
-
historyIndex = -1;
|
|
616
|
-
savedDraft = "";
|
|
617
592
|
refresh();
|
|
618
593
|
};
|
|
619
594
|
|
|
@@ -622,30 +597,6 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
622
597
|
if (matchesKey(data, Key.escape)) {
|
|
623
598
|
editMode = false;
|
|
624
599
|
editor.setText("");
|
|
625
|
-
historyIndex = -1;
|
|
626
|
-
savedDraft = "";
|
|
627
|
-
refresh();
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
if (matchesKey(data, Key.up) && customInputHistory.length > 0) {
|
|
631
|
-
if (historyIndex < 0) {
|
|
632
|
-
savedDraft = editor.getText();
|
|
633
|
-
historyIndex = customInputHistory.length - 1;
|
|
634
|
-
} else if (historyIndex > 0) {
|
|
635
|
-
historyIndex -= 1;
|
|
636
|
-
}
|
|
637
|
-
editor.setText(customInputHistory[historyIndex] ?? "");
|
|
638
|
-
refresh();
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
if (matchesKey(data, Key.down) && historyIndex >= 0) {
|
|
642
|
-
if (historyIndex < customInputHistory.length - 1) {
|
|
643
|
-
historyIndex += 1;
|
|
644
|
-
editor.setText(customInputHistory[historyIndex] ?? "");
|
|
645
|
-
} else {
|
|
646
|
-
historyIndex = -1;
|
|
647
|
-
editor.setText(savedDraft);
|
|
648
|
-
}
|
|
649
600
|
refresh();
|
|
650
601
|
return;
|
|
651
602
|
}
|
|
@@ -685,21 +636,23 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
685
636
|
}
|
|
686
637
|
if (matchesKey(data, Key.backspace)) {
|
|
687
638
|
if (filterQuery) {
|
|
688
|
-
filterQuery =
|
|
639
|
+
filterQuery = removeLastGrapheme(filterQuery);
|
|
689
640
|
optionIndex = 0;
|
|
690
641
|
refresh();
|
|
691
642
|
}
|
|
692
643
|
return;
|
|
693
644
|
}
|
|
694
|
-
|
|
695
|
-
|
|
645
|
+
const printableInput = decodeQuestionFilterInput(data);
|
|
646
|
+
const isPasteInput = data.includes("\x1B[200~");
|
|
647
|
+
if (!isPasteInput && printableInput && /^[1-9]$/.test(printableInput)) {
|
|
648
|
+
const selected = visibleOptions[Number(printableInput) - 1];
|
|
696
649
|
if (!selected) return;
|
|
697
650
|
if (selected.isOther) enterCustomMode();
|
|
698
651
|
else finish({ kind: "selected", answer: selected.label, originalIndex: selected.originalIndex });
|
|
699
652
|
return;
|
|
700
653
|
}
|
|
701
|
-
if (
|
|
702
|
-
filterQuery +=
|
|
654
|
+
if (printableInput) {
|
|
655
|
+
filterQuery += printableInput;
|
|
703
656
|
optionIndex = 0;
|
|
704
657
|
refresh();
|
|
705
658
|
}
|
|
@@ -848,7 +801,7 @@ function registerAliases(pi: ExtensionAPI): void {
|
|
|
848
801
|
await ctx.newSession();
|
|
849
802
|
};
|
|
850
803
|
pi.registerCommand("clear", { description: "Start a new session after confirmation", handler: startNewSession });
|
|
851
|
-
pi.registerCommand("
|
|
804
|
+
pi.registerCommand("exit", {
|
|
852
805
|
description: "Quit Pi gracefully",
|
|
853
806
|
handler: async (_args, ctx) => ctx.shutdown(),
|
|
854
807
|
});
|
|
@@ -891,13 +844,9 @@ const COMMAND_SYNTAX_HINTS: Readonly<Record<string, string>> = {
|
|
|
891
844
|
model: "/model [provider/model]",
|
|
892
845
|
"scoped-models": "/scoped-models",
|
|
893
846
|
login: "/login [provider]",
|
|
894
|
-
logout: "/logout [provider]",
|
|
895
847
|
export: "/export [filename]",
|
|
896
848
|
import: "/import [path]",
|
|
897
849
|
name: "/name [session-name]",
|
|
898
|
-
fork: "/fork [name]",
|
|
899
|
-
clone: "/clone [name]",
|
|
900
|
-
resume: "/resume [session-id]",
|
|
901
850
|
};
|
|
902
851
|
|
|
903
852
|
interface TaggedAutocompleteItem extends AutocompleteItem {
|
|
@@ -1173,9 +1122,12 @@ export function formatContextProgress(tokensUsed: number | null, contextWindow:
|
|
|
1173
1122
|
|
|
1174
1123
|
function sumSessionCost(ctx: ExtensionContext): number {
|
|
1175
1124
|
let total = 0;
|
|
1176
|
-
for (const entry of ctx.sessionManager.
|
|
1177
|
-
if (entry.type === "message" && entry.message.role === "assistant")
|
|
1125
|
+
for (const entry of ctx.sessionManager.getEntries()) {
|
|
1126
|
+
if (entry.type === "message" && entry.message.role === "assistant") total += entry.message.usage.cost.total;
|
|
1127
|
+
else if (entry.type === "message" && entry.message.role === "toolResult" && entry.message.usage) {
|
|
1178
1128
|
total += entry.message.usage.cost.total;
|
|
1129
|
+
} else if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage) {
|
|
1130
|
+
total += entry.usage.cost.total;
|
|
1179
1131
|
}
|
|
1180
1132
|
}
|
|
1181
1133
|
return total;
|
package/README.md
CHANGED
|
@@ -14,8 +14,6 @@ The extension is strict TypeScript and uses only packages provided by Pi.
|
|
|
14
14
|
|
|
15
15
|
### npm
|
|
16
16
|
|
|
17
|
-
After the first npm release:
|
|
18
|
-
|
|
19
17
|
```bash
|
|
20
18
|
pi install npm:killeros
|
|
21
19
|
```
|
|
@@ -44,7 +42,7 @@ Add `-l` to either command for a project-only install. Restart Pi after installi
|
|
|
44
42
|
- `/variants` selector and direct reasoning-level arguments
|
|
45
43
|
- `question` tool with filtering, keyboard selection, custom answers, history, cancellation, and resize-safe rendering
|
|
46
44
|
- Mid-prompt slash completion with current Pi `0.82.1` commands, extensions, prompts, and skills
|
|
47
|
-
- `/clear` for a confirmed new session, plus `/
|
|
45
|
+
- `/clear` for a confirmed new session, plus `/exit` for graceful shutdown
|
|
48
46
|
- Concise system-prompt guidance without modifying completed assistant messages
|
|
49
47
|
|
|
50
48
|
## Commands
|
|
@@ -53,7 +51,7 @@ Add `-l` to either command for a project-only install. Restart Pi after installi
|
|
|
53
51
|
/variants Open the reasoning-level selector
|
|
54
52
|
/variants high Set a reasoning level directly
|
|
55
53
|
/clear Start a new session after confirmation
|
|
56
|
-
/
|
|
54
|
+
/exit Quit Pi gracefully
|
|
57
55
|
```
|
|
58
56
|
|
|
59
57
|
Supported reasoning levels are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. KillerOS limits choices to levels supported by the current model.
|
|
@@ -83,6 +81,7 @@ Before release, run:
|
|
|
83
81
|
```bash
|
|
84
82
|
npm ci
|
|
85
83
|
npm run check
|
|
84
|
+
npm test
|
|
86
85
|
npm pack --dry-run
|
|
87
86
|
pi -e . --mode rpc
|
|
88
87
|
```
|
|
@@ -93,16 +92,13 @@ The package manifest lists Pi’s built-in modules as peer dependencies, so npm
|
|
|
93
92
|
|
|
94
93
|
The [`pi-package`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/packages.md) keyword makes a published npm release visible in Pi’s package catalog.
|
|
95
94
|
|
|
95
|
+
Choose `patch`, `minor`, or `major` for the release, then publish and push the version commit and tag:
|
|
96
|
+
|
|
96
97
|
```bash
|
|
97
98
|
npm login
|
|
99
|
+
npm version patch
|
|
98
100
|
npm publish
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Create and push a matching Git tag after publication:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
git tag v1.0.0
|
|
105
|
-
git push origin main v1.0.0
|
|
101
|
+
git push origin main --follow-tags
|
|
106
102
|
```
|
|
107
103
|
|
|
108
104
|
## Security
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "killeros",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A production-hardened TUI and workflow extension for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"node": ">=22.19.0"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
"check": "tsc --noEmit"
|
|
30
|
+
"check": "tsc --noEmit",
|
|
31
|
+
"test": "node --test --experimental-strip-types test/*.test.js"
|
|
31
32
|
},
|
|
32
33
|
"pi": {
|
|
33
34
|
"extensions": [
|