@timurproko/a1 0.1.8-dev.563 → 0.1.8-dev.572
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 +13 -11
- package/dist/app/session-shell/session-shell-root.js +6 -3
- package/dist/app/session-shell/session-shell.js +7 -1
- package/dist/app/session-shell/session-viewport-controller.js +48 -30
- package/dist/cli/dispatch.d.ts +3 -1
- package/dist/cli/dispatch.js +56 -56
- package/dist/cli/package-messages.d.ts +4 -3
- package/dist/cli/package-messages.js +23 -11
- package/dist/integrations/pi/components/shell-footer-status.d.ts +4 -3
- package/dist/integrations/pi/components/shell-footer-status.js +53 -15
- package/dist/integrations/pi/components/shell-shared-facade.d.ts +9 -1
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +2 -2
- package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +3 -2
- package/dist/integrations/pi/engine/compaction-progress.d.ts +1 -1
- package/dist/integrations/pi/engine/compaction-progress.js +1 -0
- package/dist/integrations/pi/engine/repository-pr.d.ts +3 -3
- package/dist/integrations/pi/engine/repository-pr.js +6 -6
- package/dist/integrations/pi/engine/session-runtime.js +5 -4
- package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -0
- package/dist/integrations/pi/tui-runtime/adapter.js +5 -0
- package/dist/native/darwin-arm64/manifest.json +1 -1
- package/dist/native/linux-x64/manifest.json +1 -1
- package/dist/native/win32-x64/manifest.json +3 -3
- package/dist/native/win32-x64/process-guardian.exe +0 -0
- package/dist/ui/components/index.d.ts +2 -1
- package/dist/ui/components/index.js +1 -1
- package/dist/ui/components/progress-status.d.ts +9 -2
- package/dist/ui/components/progress-status.js +24 -3
- package/dist/ui/components/transcript-viewport.d.ts +9 -3
- package/dist/ui/components/transcript-viewport.js +90 -132
- package/docs/architecture/ui-reference-provenance.md +8 -5
- package/docs/openspec-archive-automation.md +2 -2
- package/docs/validation.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,13 +24,15 @@ npm install -g @timurproko/a1@0.1.8-dev.107
|
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
26
|
a1 # launch A1 (profile: ~/.a1/agent)
|
|
27
|
-
a1
|
|
28
|
-
a1
|
|
27
|
+
a1 help # show all commands
|
|
28
|
+
a1 version # show the version
|
|
29
29
|
a1 update # install the newest stable release
|
|
30
30
|
a1 update --develop # install the current development preview
|
|
31
31
|
a1 update --develop 107 # install numbered preview 107
|
|
32
32
|
a1 update --develop 0.1.8-dev.107 # install that exact preview
|
|
33
33
|
a1 update --models # refresh A1's model catalogs
|
|
34
|
+
a1 update --extensions # update every installed package
|
|
35
|
+
a1 update npm:pi-mcp-adapter # update one package
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
Stable builds print only their installed version. Development builds also show the
|
|
@@ -47,17 +49,17 @@ Unsupported commands exit quietly without launching anything. The removed
|
|
|
47
49
|
|
|
48
50
|
## Extensions
|
|
49
51
|
|
|
50
|
-
Pi extension packages install into A1's own profile (`~/.a1/agent`), so
|
|
51
|
-
loads them and `a1 pi` does not. Sources
|
|
52
|
-
local
|
|
52
|
+
Pi-compatible extension packages install into A1's own profile (`~/.a1/agent`), so
|
|
53
|
+
bare `a1` loads them and the `a1 pi` comparison profile does not. Sources use Pi's
|
|
54
|
+
`npm:`, git, or local-path grammar.
|
|
53
55
|
|
|
54
56
|
```sh
|
|
55
|
-
a1
|
|
56
|
-
a1
|
|
57
|
-
a1
|
|
58
|
-
a1
|
|
59
|
-
a1
|
|
60
|
-
a1
|
|
57
|
+
a1 install npm:pi-mcp-adapter # install a package
|
|
58
|
+
a1 remove npm:pi-mcp-adapter # remove it (alias: a1 uninstall)
|
|
59
|
+
a1 list # list installed packages
|
|
60
|
+
a1 update --extensions # update every installed package
|
|
61
|
+
a1 update npm:pi-mcp-adapter # update one
|
|
62
|
+
a1 update --models # refresh A1's model catalogs
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
A1 pins the Pi runtime carried by each release, so Pi self-update forms are refused.
|
|
@@ -3,7 +3,7 @@ import { caretCell } from "../../ui/components/line-input.js";
|
|
|
3
3
|
import { PromptInput, promptArrow } from "../../ui/components/prompt-input.js";
|
|
4
4
|
import { composeSubmittedPromptRows, formatSubmittedPromptTime, submittedPromptLayout, } from "../../ui/components/submitted-prompt.js";
|
|
5
5
|
import { backgroundSgrSpan, heldNativeHyperlinkStyle, hyperlinkSgrSpan, nativeHyperlinkStyle, overlaySpan, } from "../../ui/components/spans.js";
|
|
6
|
-
import { progressStatusText } from "../../ui/components/progress-status.js";
|
|
6
|
+
import { progressStatusFrame, progressStatusText } from "../../ui/components/progress-status.js";
|
|
7
7
|
import { displayWidth, faint, stripAnsi } from "../../ui/components/text.js";
|
|
8
8
|
import { PINNED_PI_HIDDEN_COMMAND_NAMES, PINNED_PI_WORKFLOW_COMMAND_NAMES } from "../../integrations/pi/engine/workflows.js";
|
|
9
9
|
import { createPiExtensionUiBridge } from "../../integrations/pi/components/shell-extension-ui.js";
|
|
@@ -106,10 +106,12 @@ export class OwnedUiSessionShellRoot {
|
|
|
106
106
|
...(this.#customViewport ? { getKeybindings: () => this.editor.keybindingConfig() } : {}),
|
|
107
107
|
});
|
|
108
108
|
this.resources = createPiShellLoadedResources(startup.resources ?? [], startup.expanded ?? false);
|
|
109
|
-
this.#status = createPiShellStatus(view,
|
|
109
|
+
this.#status = createPiShellStatus(view, {
|
|
110
|
+
text: (message, mode) => progressStatusText(message, mode === "custom-viewport" ? "…" : "..."),
|
|
111
|
+
frame: progressStatusFrame,
|
|
112
|
+
}, handlers);
|
|
110
113
|
this.#status.setProgressPresentation(this.#customViewport ? "custom-viewport" : "pinned");
|
|
111
114
|
this.#footer = createPiShellFooter(this.#viewWithExtensionStatuses(view), cwd, this.#customViewport ? "a1" : "pi", () => this.#footerLevel);
|
|
112
|
-
this.#queued = createPiQueuedInputStatus(view.editor.queuedSubmissions, this.#customViewport ? "custom-viewport" : "pinned");
|
|
113
115
|
this.editor = createPiShellEditor({
|
|
114
116
|
...handlers,
|
|
115
117
|
keybindingProfile: this.#customViewport ? "a1" : "pi",
|
|
@@ -183,6 +185,7 @@ export class OwnedUiSessionShellRoot {
|
|
|
183
185
|
},
|
|
184
186
|
...(handlers.onPromptSuggestionAccepted === undefined ? {} : { onPromptSuggestionAccepted: handlers.onPromptSuggestionAccepted }),
|
|
185
187
|
});
|
|
188
|
+
this.#queued = createPiQueuedInputStatus(view.editor.queuedSubmissions, this.#customViewport ? "custom-viewport" : "pinned", this.#customViewport ? () => this.editor.keybindingConfig() : undefined);
|
|
186
189
|
this.#viewportController = new SessionViewportController({
|
|
187
190
|
enabled: this.#customViewport,
|
|
188
191
|
editor: this.editor,
|
|
@@ -356,7 +356,13 @@ export class OwnedUiSessionShell {
|
|
|
356
356
|
}
|
|
357
357
|
const routed = this.root.handleViewportPreInput(data, true, Date.now(), this.root.usesDefaultInputSurface() && !this.runtime.hasFocusedOverlay());
|
|
358
358
|
if (routed.copySelection !== undefined) {
|
|
359
|
-
|
|
359
|
+
const snapshot = routed.copySelection;
|
|
360
|
+
void this.#responseCopy?.submit(snapshot, pendingClipboardWrite).then(result => {
|
|
361
|
+
if (this.#disposed || !runtime.active
|
|
362
|
+
|| result.outcome !== "delivered" && result.outcome !== "submitted-unverified")
|
|
363
|
+
return;
|
|
364
|
+
runtime.showFlash(`Copied ${snapshot.sourceUnits} character${snapshot.sourceUnits === 1 ? "" : "s"} to clipboard`);
|
|
365
|
+
});
|
|
360
366
|
}
|
|
361
367
|
if (!routed.consumed)
|
|
362
368
|
return routed.data === data ? undefined : { data: routed.data };
|
|
@@ -28,12 +28,14 @@ export class SessionViewportController {
|
|
|
28
28
|
#inputSurfacePending = false;
|
|
29
29
|
#overlays = [];
|
|
30
30
|
#pointerOwner;
|
|
31
|
-
// Invariant: a
|
|
31
|
+
// Invariant: a right-click editor gesture remains owned by the dock.
|
|
32
32
|
#dockPointerSuppressed = false;
|
|
33
|
-
// Invariant: a
|
|
33
|
+
// Invariant: a gesture begun on an explicit control remains owned by that control.
|
|
34
34
|
#tailPointerSuppressed = false;
|
|
35
35
|
// Invariant: editor selection remains true only while its left button is held.
|
|
36
36
|
#editorPointerSelecting = false;
|
|
37
|
+
// Invariant: no-drag editor presses replay on release; distinct motion promotes frame selection.
|
|
38
|
+
#pendingEditorClick;
|
|
37
39
|
// Invariant: pointer routing uses the most recently composed editor rows.
|
|
38
40
|
#editorPointerFrame;
|
|
39
41
|
#selectionAutoScrollTimer;
|
|
@@ -112,9 +114,19 @@ export class SessionViewportController {
|
|
|
112
114
|
this.#cancelGesture();
|
|
113
115
|
this.#viewport.setConfig(this.#config);
|
|
114
116
|
const selectionRevision = this.#viewport.selectionRevision;
|
|
117
|
+
const coveredSurfaces = [
|
|
118
|
+
...(this.#overlays ?? []),
|
|
119
|
+
...(this.#inputSurface === undefined ? [] : [this.#inputSurface]),
|
|
120
|
+
];
|
|
121
|
+
const coveredCells = coveredSurfaces.flatMap(surface => Array.from({ length: Math.max(0, surface.rowEnd - surface.rowStart + 1) }, (_value, index) => ({
|
|
122
|
+
row: surface.rowStart + index,
|
|
123
|
+
columnStart: surface.columnStart,
|
|
124
|
+
columnEnd: surface.columnEnd,
|
|
125
|
+
})));
|
|
115
126
|
const frame = this.#viewport.compose({
|
|
116
127
|
...input,
|
|
117
128
|
...(this.#pointerPosition === undefined ? {} : { pointerPosition: this.#pointerPosition }),
|
|
129
|
+
...(coveredCells.length === 0 ? {} : { coveredCells }),
|
|
118
130
|
});
|
|
119
131
|
// Concurrency: if selection changed during composition, this frame truthfully carries
|
|
120
132
|
// the older revision and exactly one ordinary follow-up render publishes the latest.
|
|
@@ -167,6 +179,7 @@ export class SessionViewportController {
|
|
|
167
179
|
this.#clearActivityTimer();
|
|
168
180
|
this.#stopSelectionAutoScroll();
|
|
169
181
|
this.#editorPointerSelecting = false;
|
|
182
|
+
this.#pendingEditorClick = undefined;
|
|
170
183
|
this.#editorPointerFrame = undefined;
|
|
171
184
|
this.#pointerPosition = undefined;
|
|
172
185
|
this.#hoveredHyperlinkKey = undefined;
|
|
@@ -183,6 +196,7 @@ export class SessionViewportController {
|
|
|
183
196
|
this.#dockPointerSuppressed = false;
|
|
184
197
|
this.#tailPointerSuppressed = false;
|
|
185
198
|
this.#editorPointerSelecting = false;
|
|
199
|
+
this.#pendingEditorClick = undefined;
|
|
186
200
|
this.#editorPointerFrame = undefined;
|
|
187
201
|
this.#pointerPosition = undefined;
|
|
188
202
|
this.#hoveredHyperlinkKey = undefined;
|
|
@@ -302,6 +316,7 @@ export class SessionViewportController {
|
|
|
302
316
|
let repaint = false;
|
|
303
317
|
let forceRepaint = false;
|
|
304
318
|
let activity = false;
|
|
319
|
+
let completedCopy;
|
|
305
320
|
const routed = routeMouseInput(data, (event, report) => {
|
|
306
321
|
const hits = frame.hits;
|
|
307
322
|
const overModal = this.#modalAt(event.column, event.row);
|
|
@@ -375,6 +390,10 @@ export class SessionViewportController {
|
|
|
375
390
|
return true;
|
|
376
391
|
if (this.#viewport.selectionActive) {
|
|
377
392
|
this.#viewport.extendSelection(event.column, event.row, now, false);
|
|
393
|
+
if (this.#pendingEditorClick !== undefined
|
|
394
|
+
&& (event.column !== this.#pendingEditorClick.column || event.row !== this.#pendingEditorClick.row)) {
|
|
395
|
+
this.#pendingEditorClick = undefined;
|
|
396
|
+
}
|
|
378
397
|
this.#updateSelectionAutoScroll(event.column, event.row, hits.viewportHeight);
|
|
379
398
|
activity = true;
|
|
380
399
|
return true;
|
|
@@ -415,17 +434,20 @@ export class SessionViewportController {
|
|
|
415
434
|
return false;
|
|
416
435
|
this.#dockPointerSuppressed = false;
|
|
417
436
|
this.#tailPointerSuppressed = false;
|
|
437
|
+
this.#pendingEditorClick = undefined;
|
|
418
438
|
this.#stopSelectionAutoScroll();
|
|
419
439
|
if (this.#viewport.clearSelection())
|
|
420
440
|
repaint = true;
|
|
421
441
|
if (overBottom) {
|
|
422
442
|
this.#viewport.scrollToEnd(now);
|
|
443
|
+
this.#tailPointerSuppressed = true;
|
|
423
444
|
activity = true;
|
|
424
445
|
repaint = true;
|
|
425
446
|
return true;
|
|
426
447
|
}
|
|
427
448
|
if (overSticky && hits.sticky !== null) {
|
|
428
449
|
this.#viewport.scrollTo(hits.sticky.target, now);
|
|
450
|
+
this.#tailPointerSuppressed = true;
|
|
429
451
|
activity = true;
|
|
430
452
|
repaint = true;
|
|
431
453
|
return true;
|
|
@@ -439,35 +461,19 @@ export class SessionViewportController {
|
|
|
439
461
|
}
|
|
440
462
|
else {
|
|
441
463
|
this.#viewport.scrollTo(scrollForTrackPage(geometry, trackRow, frame.scrollTop, hits.viewportHeight), now);
|
|
464
|
+
this.#tailPointerSuppressed = true;
|
|
442
465
|
}
|
|
443
466
|
activity = true;
|
|
444
467
|
repaint = true;
|
|
445
468
|
return true;
|
|
446
469
|
}
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
button: event.button,
|
|
451
|
-
column: event.column,
|
|
452
|
-
row: event.row - editorFrame.rowStart + 1,
|
|
453
|
-
});
|
|
454
|
-
if (!handled)
|
|
455
|
-
this.#dockPointerSuppressed = true;
|
|
456
|
-
this.#editorPointerSelecting = handled;
|
|
457
|
-
repaint = true;
|
|
458
|
-
return true;
|
|
459
|
-
}
|
|
460
|
-
if (event.row > hits.viewportHeight) {
|
|
461
|
-
this.#dockPointerSuppressed = true;
|
|
462
|
-
return true;
|
|
463
|
-
}
|
|
464
|
-
if (event.row >= 1 && event.row <= hits.viewportHeight && event.column <= frame.contentWidth) {
|
|
465
|
-
if (!this.#viewport.pressSelection(event.column, event.row, now)) {
|
|
466
|
-
// Invariant: empty rows and transient tail rows must never start Pi selection.
|
|
467
|
-
this.#tailPointerSuppressed = true;
|
|
468
|
-
repaint = true;
|
|
469
|
-
return true;
|
|
470
|
+
if (event.row >= 1 && event.row <= frame.rows.length && event.column <= frame.descriptor.width) {
|
|
471
|
+
if (editorFrame !== undefined && event.row >= editorFrame.rowStart && event.row <= editorFrame.rowEnd) {
|
|
472
|
+
this.#pendingEditorClick = { column: event.column, row: event.row };
|
|
470
473
|
}
|
|
474
|
+
// Invariant: frame selection includes transient and dock rows without changing
|
|
475
|
+
// semantic ownership; no-drag editor presses replay on release.
|
|
476
|
+
this.#viewport.pressSelection(event.column, event.row, now);
|
|
471
477
|
repaint = true;
|
|
472
478
|
return true;
|
|
473
479
|
}
|
|
@@ -490,6 +496,17 @@ export class SessionViewportController {
|
|
|
490
496
|
}
|
|
491
497
|
if (this.#viewport.releaseSelection()) {
|
|
492
498
|
this.#stopSelectionAutoScroll();
|
|
499
|
+
const pendingEditorClick = this.#pendingEditorClick;
|
|
500
|
+
this.#pendingEditorClick = undefined;
|
|
501
|
+
if (pendingEditorClick !== undefined && this.#editorPointerFrame !== undefined) {
|
|
502
|
+
this.#viewport.clearSelection();
|
|
503
|
+
const editorRow = pendingEditorClick.row - this.#editorPointerFrame.rowStart + 1;
|
|
504
|
+
this.#editor.handlePointer({ kind: "press", button: 0, column: pendingEditorClick.column, row: editorRow });
|
|
505
|
+
this.#editor.handlePointer({ kind: "release", button: 0, column: pendingEditorClick.column, row: editorRow });
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
completedCopy = this.#viewport.captureSelectedText() ?? undefined;
|
|
509
|
+
}
|
|
493
510
|
// Platform: restore OSC 8 links only after the held-button selection paint has
|
|
494
511
|
// ended, then overwrite any terminal-cached hover cells immediately.
|
|
495
512
|
forceRepaint = true;
|
|
@@ -519,7 +536,7 @@ export class SessionViewportController {
|
|
|
519
536
|
this.#lastRequestedSelectionRevision = this.#viewport.selectionRevision;
|
|
520
537
|
this.#requestRender(forceRepaint);
|
|
521
538
|
}
|
|
522
|
-
return routed;
|
|
539
|
+
return completedCopy === undefined ? routed : { ...routed, copySelection: completedCopy };
|
|
523
540
|
}
|
|
524
541
|
#modalAt(column, row) {
|
|
525
542
|
const contains = (surface) => column >= surface.columnStart && column <= surface.columnEnd
|
|
@@ -534,13 +551,13 @@ export class SessionViewportController {
|
|
|
534
551
|
.find(candidate => column - 1 >= candidate.from && column - 1 < candidate.to);
|
|
535
552
|
return range === undefined ? undefined : JSON.stringify([row, range]);
|
|
536
553
|
}
|
|
537
|
-
#updateSelectionAutoScroll(column, row,
|
|
538
|
-
const beyondEdge = row <= 1 || row
|
|
554
|
+
#updateSelectionAutoScroll(column, row, frameHeight) {
|
|
555
|
+
const beyondEdge = row <= 1 || row >= frameHeight;
|
|
539
556
|
if (!beyondEdge) {
|
|
540
557
|
this.#stopSelectionAutoScroll();
|
|
541
558
|
return;
|
|
542
559
|
}
|
|
543
|
-
this.#selectionAutoScrollPointer = { column, row };
|
|
560
|
+
this.#selectionAutoScrollPointer = { column, row, direction: row <= 1 ? -1 : 1 };
|
|
544
561
|
if (this.#selectionAutoScrollTimer !== undefined)
|
|
545
562
|
return;
|
|
546
563
|
this.#selectionAutoScrollTimer = setTimeout(() => this.#selectionAutoScrollTick(), SELECTION_AUTO_SCROLL_INTERVAL_MS);
|
|
@@ -557,7 +574,8 @@ export class SessionViewportController {
|
|
|
557
574
|
const rowsPerTick = scrollbarSelectionRows(this.#config.scrollbarSpeed);
|
|
558
575
|
for (let row = 0; row < rowsPerTick; row += 1) {
|
|
559
576
|
const previous = this.#viewport.scrollTop;
|
|
560
|
-
this.#viewport.
|
|
577
|
+
this.#viewport.scrollBy(pointer.direction);
|
|
578
|
+
this.#viewport.extendSelection(pointer.column, pointer.row, undefined, false);
|
|
561
579
|
if (this.#viewport.scrollTop === previous)
|
|
562
580
|
break;
|
|
563
581
|
}
|
package/dist/cli/dispatch.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type InteractiveLaunchIntent, type LaunchProfileId, type SessionSelecti
|
|
|
2
2
|
import type { CliCapabilities } from "./capabilities.js";
|
|
3
3
|
import type { PackageCommandRequest } from "./packages.js";
|
|
4
4
|
import type { SessionContextRequest } from "./session-context.js";
|
|
5
|
-
import { type PackageCliVerb, type PackageSyntaxDiagnostic } from "./package-messages.js";
|
|
5
|
+
import { type PackageCliVerb, type PackageCommandNamespace, type PackageSyntaxDiagnostic } from "./package-messages.js";
|
|
6
6
|
export type UpdateChannel = "stable" | "next";
|
|
7
7
|
export interface CliHandlers {
|
|
8
8
|
readonly launch: (intent: InteractiveLaunchIntent) => Promise<number>;
|
|
@@ -25,9 +25,11 @@ export type CliCommand = {
|
|
|
25
25
|
} | {
|
|
26
26
|
readonly kind: "package-help";
|
|
27
27
|
readonly verb: PackageCliVerb;
|
|
28
|
+
readonly namespace: PackageCommandNamespace;
|
|
28
29
|
} | {
|
|
29
30
|
readonly kind: "package-error";
|
|
30
31
|
readonly diagnostic: PackageSyntaxDiagnostic;
|
|
32
|
+
readonly namespace: PackageCommandNamespace;
|
|
31
33
|
} | {
|
|
32
34
|
readonly kind: "launch";
|
|
33
35
|
readonly profileId: LaunchProfileId;
|
package/dist/cli/dispatch.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { interactiveLaunchIntent, parseSessionSelection } from "../features/launch/index.js";
|
|
2
|
-
import { packageCommandHelp, renderPackageSyntax } from "./package-messages.js";
|
|
2
|
+
import { packageCommandHelp, renderPackageSyntax, } from "./package-messages.js";
|
|
3
3
|
import { PRODUCT_TEXT } from "../product-identity.js";
|
|
4
4
|
export function cliUsage(capabilities) {
|
|
5
5
|
return PRODUCT_TEXT.usage([
|
|
@@ -7,22 +7,19 @@ export function cliUsage(capabilities) {
|
|
|
7
7
|
"--session <path|id>",
|
|
8
8
|
"--session-dir <dir> --session <path|id>",
|
|
9
9
|
...(capabilities.developmentComparison ? ["pi"] : []),
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"--version",
|
|
13
|
-
"-v",
|
|
10
|
+
"help",
|
|
11
|
+
"version",
|
|
14
12
|
"update",
|
|
15
13
|
"update --develop [preview-or-version]",
|
|
16
14
|
"update --models",
|
|
15
|
+
"update --extensions",
|
|
16
|
+
"update <source>",
|
|
17
|
+
"install <source>",
|
|
18
|
+
"remove <source>",
|
|
19
|
+
"uninstall <source>",
|
|
20
|
+
"list",
|
|
17
21
|
"session link-worktree <path>",
|
|
18
22
|
"session unlink-worktree",
|
|
19
|
-
"pi install <source>",
|
|
20
|
-
"pi remove <source>",
|
|
21
|
-
"pi uninstall <source>",
|
|
22
|
-
"pi list",
|
|
23
|
-
"pi update --extensions",
|
|
24
|
-
"pi update --models",
|
|
25
|
-
"pi update <source>",
|
|
26
23
|
]);
|
|
27
24
|
}
|
|
28
25
|
export function cliHelp(capabilities) {
|
|
@@ -33,29 +30,26 @@ export function cliHelp(capabilities) {
|
|
|
33
30
|
` ${command} --session <path|id>`,
|
|
34
31
|
` ${command} --session-dir <dir> --session <path|id>`,
|
|
35
32
|
...(capabilities.developmentComparison ? [` ${command} pi`] : []),
|
|
36
|
-
` ${command}
|
|
37
|
-
` ${command}
|
|
38
|
-
` ${command} --version`,
|
|
39
|
-
` ${command} -v`,
|
|
33
|
+
` ${command} help`,
|
|
34
|
+
` ${command} version`,
|
|
40
35
|
"",
|
|
41
36
|
"Update:",
|
|
42
37
|
` ${command} update`,
|
|
43
38
|
` ${command} update --develop [preview-or-version]`,
|
|
44
39
|
` ${command} update --models`,
|
|
40
|
+
` ${command} update --extensions`,
|
|
41
|
+
` ${command} update <source>`,
|
|
42
|
+
"",
|
|
43
|
+
"Packages:",
|
|
44
|
+
` ${command} install <source>`,
|
|
45
|
+
` ${command} remove <source>`,
|
|
46
|
+
` ${command} uninstall <source>`,
|
|
47
|
+
` ${command} list`,
|
|
45
48
|
"",
|
|
46
49
|
"Session context:",
|
|
47
50
|
` ${command} session link-worktree <path>`,
|
|
48
51
|
` ${command} session unlink-worktree`,
|
|
49
52
|
"",
|
|
50
|
-
"Pi-compatible packages:",
|
|
51
|
-
` ${command} pi install <source>`,
|
|
52
|
-
` ${command} pi remove <source>`,
|
|
53
|
-
` ${command} pi uninstall <source>`,
|
|
54
|
-
` ${command} pi list`,
|
|
55
|
-
` ${command} pi update --extensions`,
|
|
56
|
-
` ${command} pi update --models`,
|
|
57
|
-
` ${command} pi update <source>`,
|
|
58
|
-
"",
|
|
59
53
|
].join("\n");
|
|
60
54
|
}
|
|
61
55
|
const PROFILE_WORDS = new Set(["pi"]);
|
|
@@ -68,11 +62,11 @@ export async function dispatchCli(arguments_, handlers, output, capabilities) {
|
|
|
68
62
|
return 0;
|
|
69
63
|
}
|
|
70
64
|
if (command.kind === "package-help") {
|
|
71
|
-
output.stdout(packageCommandHelp(command.verb));
|
|
65
|
+
output.stdout(packageCommandHelp(command.verb, command.namespace));
|
|
72
66
|
return 0;
|
|
73
67
|
}
|
|
74
68
|
if (command.kind === "package-error") {
|
|
75
|
-
output.stderr(renderPackageSyntax(command.diagnostic));
|
|
69
|
+
output.stderr(renderPackageSyntax(command.diagnostic, command.namespace));
|
|
76
70
|
return 2;
|
|
77
71
|
}
|
|
78
72
|
if (command.kind === "error") {
|
|
@@ -102,13 +96,16 @@ export function parseCliCommand(arguments_, capabilities) {
|
|
|
102
96
|
return { kind: "error", message: PRODUCT_TEXT.diagnostic(`could not parse session launch: ${error instanceof Error ? error.message : String(error)}`) };
|
|
103
97
|
}
|
|
104
98
|
}
|
|
105
|
-
if (command === "--help" || command === "-h")
|
|
99
|
+
if (command === "help" || command === "--help" || command === "-h")
|
|
106
100
|
return withoutArguments(rest, { kind: "help" });
|
|
107
|
-
if (command === "--version" || command === "-v")
|
|
101
|
+
if (command === "version" || command === "--version" || command === "-v")
|
|
108
102
|
return withoutArguments(rest, { kind: "version" });
|
|
103
|
+
if (command === "install" || command === "remove" || command === "uninstall" || command === "list") {
|
|
104
|
+
return parsePackageCommand(arguments_, "direct");
|
|
105
|
+
}
|
|
109
106
|
if (command === "pi") {
|
|
110
107
|
if (rest.length > 0)
|
|
111
|
-
return
|
|
108
|
+
return parsePackageCommand(rest, "pi");
|
|
112
109
|
return capabilities.developmentComparison ? { kind: "launch", profileId: "pi" } : { kind: "noop" };
|
|
113
110
|
}
|
|
114
111
|
if (command === "update")
|
|
@@ -143,6 +140,9 @@ function parseSessionContext(rest) {
|
|
|
143
140
|
* operation that cannot be combined with either update channel.
|
|
144
141
|
*/
|
|
145
142
|
function parseUpdate(rest) {
|
|
143
|
+
if (rest.some(value => value === "--help" || value === "-h")) {
|
|
144
|
+
return { kind: "package-help", verb: "update", namespace: "direct" };
|
|
145
|
+
}
|
|
146
146
|
if (rest.length === 0)
|
|
147
147
|
return { kind: "update", channel: "stable" };
|
|
148
148
|
const [selector, ...values] = rest;
|
|
@@ -167,10 +167,9 @@ function parseUpdate(rest) {
|
|
|
167
167
|
}
|
|
168
168
|
if (selector === "pi")
|
|
169
169
|
return pinnedPiUpdateError();
|
|
170
|
-
if (selector
|
|
170
|
+
if (selector === "--self" || selector === "--all")
|
|
171
171
|
return unknownOption(selector, "update");
|
|
172
|
-
|
|
173
|
-
return { kind: "noop" };
|
|
172
|
+
return parsePackageUpdate(rest, "direct");
|
|
174
173
|
}
|
|
175
174
|
function isDevelopmentTarget(target) {
|
|
176
175
|
return /^[1-9]\d*$/.test(target) || /^\d+\.\d+\.\d+-dev\.[1-9]\d*$/.test(target);
|
|
@@ -178,48 +177,49 @@ function isDevelopmentTarget(target) {
|
|
|
178
177
|
function updateGrammarError(detail) {
|
|
179
178
|
return { kind: "error", message: PRODUCT_TEXT.diagnostic(`could not parse update: ${detail}`) };
|
|
180
179
|
}
|
|
181
|
-
function
|
|
180
|
+
function parsePackageCommand(arguments_, namespace) {
|
|
182
181
|
const [verb, ...rest] = arguments_;
|
|
183
182
|
if ((verb === "install" || verb === "remove" || verb === "uninstall" || verb === "list" || verb === "update")
|
|
184
183
|
&& rest.some(value => value === "--help" || value === "-h")) {
|
|
185
|
-
return { kind: "package-help", verb: verb === "uninstall" ? "remove" : verb };
|
|
184
|
+
return { kind: "package-help", verb: verb === "uninstall" ? "remove" : verb, namespace };
|
|
186
185
|
}
|
|
187
186
|
if (verb === "install" || verb === "remove" || verb === "uninstall") {
|
|
188
|
-
return parseSourceCommand(verb === "install" ? "install" : "remove", rest);
|
|
187
|
+
return parseSourceCommand(verb === "install" ? "install" : "remove", rest, namespace);
|
|
189
188
|
}
|
|
190
189
|
if (verb === "list") {
|
|
191
190
|
const flag = rest.find(argument => argument.startsWith("-"));
|
|
192
191
|
if (flag !== undefined)
|
|
193
|
-
return packageOptionError(flag, "list");
|
|
192
|
+
return packageOptionError(flag, "list", namespace);
|
|
194
193
|
if (rest[0] !== undefined)
|
|
195
|
-
return packageSyntax({ verb: "list", kind: "unexpected-argument", value: rest[0] });
|
|
194
|
+
return packageSyntax({ verb: "list", kind: "unexpected-argument", value: rest[0] }, namespace);
|
|
196
195
|
return { kind: "packages", request: { verb: "list", source: null } };
|
|
197
196
|
}
|
|
198
197
|
if (verb === "update")
|
|
199
|
-
return
|
|
198
|
+
return parsePackageUpdate(rest, namespace);
|
|
200
199
|
return { kind: "noop" };
|
|
201
200
|
}
|
|
202
|
-
function
|
|
201
|
+
function parsePackageUpdate(rest, namespace) {
|
|
203
202
|
if (rest.length === 0)
|
|
204
203
|
return pinnedPiUpdateError();
|
|
205
204
|
if (rest.length > 1) {
|
|
206
205
|
const unknown = rest.find(value => value.startsWith("-") && !["--extensions", "--models", "--self", "--all"].includes(value));
|
|
207
206
|
if (unknown !== undefined)
|
|
208
|
-
return packageOptionError(unknown, "update");
|
|
207
|
+
return packageOptionError(unknown, "update", namespace);
|
|
209
208
|
if (rest.includes("--self") || rest.includes("--all") || rest.includes("self") || rest.includes("pi"))
|
|
210
209
|
return pinnedPiUpdateError();
|
|
211
210
|
if (rest.every(value => value === rest[0]) && rest[0]?.startsWith("-")) {
|
|
212
|
-
|
|
211
|
+
const command = namespace === "direct" ? "update" : "pi update";
|
|
212
|
+
return { kind: "error", message: PRODUCT_TEXT.diagnostic(`${command} accepts one target.`) };
|
|
213
213
|
}
|
|
214
214
|
const sources = rest.filter(value => !value.startsWith("-"));
|
|
215
215
|
if (sources[1] !== undefined)
|
|
216
|
-
return packageSyntax({ verb: "update", kind: "unexpected-argument", value: sources[1] });
|
|
216
|
+
return packageSyntax({ verb: "update", kind: "unexpected-argument", value: sources[1] }, namespace);
|
|
217
217
|
const message = rest.includes("--models")
|
|
218
218
|
? rest.includes("--extensions")
|
|
219
219
|
? "--models cannot be combined with --self, --extensions, --all, or --extension"
|
|
220
220
|
: "--models cannot be combined with a positional source"
|
|
221
221
|
: "positional update targets cannot be combined with --self, --extensions, or --all";
|
|
222
|
-
return packageSyntax({ verb: "update", kind: "conflict", message });
|
|
222
|
+
return packageSyntax({ verb: "update", kind: "conflict", message }, namespace);
|
|
223
223
|
}
|
|
224
224
|
const [target] = rest;
|
|
225
225
|
if (target === "--extensions")
|
|
@@ -229,7 +229,7 @@ function parsePiPackageUpdate(rest) {
|
|
|
229
229
|
if (target === "--self" || target === "--all" || target === "self" || target === "pi")
|
|
230
230
|
return pinnedPiUpdateError();
|
|
231
231
|
if (target === undefined || target.startsWith("-"))
|
|
232
|
-
return packageOptionError(target ?? "", "update");
|
|
232
|
+
return packageOptionError(target ?? "", "update", namespace);
|
|
233
233
|
return { kind: "packages", request: { verb: "update", source: target } };
|
|
234
234
|
}
|
|
235
235
|
function pinnedPiUpdateError() {
|
|
@@ -239,35 +239,35 @@ function pinnedPiUpdateError() {
|
|
|
239
239
|
PRODUCT_TEXT.diagnostic("pins its certified Pi runtime and cannot update it independently."),
|
|
240
240
|
"Use:",
|
|
241
241
|
` ${PRODUCT_TEXT.commandName} update Update a1`,
|
|
242
|
-
` ${PRODUCT_TEXT.commandName}
|
|
243
|
-
` ${PRODUCT_TEXT.commandName}
|
|
242
|
+
` ${PRODUCT_TEXT.commandName} update --extensions Update Pi-compatible packages`,
|
|
243
|
+
` ${PRODUCT_TEXT.commandName} update --models Refresh model catalogs`,
|
|
244
244
|
].join("\n"),
|
|
245
245
|
};
|
|
246
246
|
}
|
|
247
|
-
function parseSourceCommand(verb, rest) {
|
|
247
|
+
function parseSourceCommand(verb, rest, namespace) {
|
|
248
248
|
const flag = rest.find(argument => argument.startsWith("-"));
|
|
249
249
|
if (flag !== undefined)
|
|
250
|
-
return packageOptionError(flag, verb);
|
|
250
|
+
return packageOptionError(flag, verb, namespace);
|
|
251
251
|
const [source, extra] = rest;
|
|
252
252
|
if (extra !== undefined)
|
|
253
|
-
return packageSyntax({ verb, kind: "unexpected-argument", value: extra });
|
|
253
|
+
return packageSyntax({ verb, kind: "unexpected-argument", value: extra }, namespace);
|
|
254
254
|
if (source === undefined)
|
|
255
|
-
return packageSyntax({ verb, kind: "missing-source" });
|
|
255
|
+
return packageSyntax({ verb, kind: "missing-source" }, namespace);
|
|
256
256
|
if (PROFILE_WORDS.has(source))
|
|
257
257
|
return profileRejection(verb);
|
|
258
258
|
return { kind: "packages", request: { verb, source } };
|
|
259
259
|
}
|
|
260
|
-
function packageSyntax(diagnostic) {
|
|
261
|
-
return { kind: "package-error", diagnostic };
|
|
260
|
+
function packageSyntax(diagnostic, namespace) {
|
|
261
|
+
return { kind: "package-error", diagnostic, namespace };
|
|
262
262
|
}
|
|
263
|
-
function packageOptionError(option, verb) {
|
|
263
|
+
function packageOptionError(option, verb, namespace) {
|
|
264
264
|
if (option.startsWith("--profile") || option === "-l" || option === "--local")
|
|
265
265
|
return profileRejection(verb);
|
|
266
266
|
// Compatibility: these recognized Pi flags remain outside A1's supported subset.
|
|
267
267
|
if (["--extension", "--approve", "--no-approve", "-a", "-na", "--force"].includes(option)) {
|
|
268
|
-
return unknownOption(option, `pi ${verb}`);
|
|
268
|
+
return unknownOption(option, namespace === "direct" ? verb : `pi ${verb}`);
|
|
269
269
|
}
|
|
270
|
-
return packageSyntax({ verb, kind: "unknown-option", value: option });
|
|
270
|
+
return packageSyntax({ verb, kind: "unknown-option", value: option }, namespace);
|
|
271
271
|
}
|
|
272
272
|
function withoutArguments(rest, command) {
|
|
273
273
|
if (rest.length === 0)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
/** Supported subset of pinned Pi's package-manager CLI presentation. */
|
|
3
3
|
export type PackageCliVerb = "install" | "remove" | "list" | "update";
|
|
4
|
+
export type PackageCommandNamespace = "direct" | "pi";
|
|
4
5
|
export type PackageSyntaxDiagnostic = {
|
|
5
6
|
readonly verb: PackageCliVerb;
|
|
6
7
|
readonly kind: "missing-source";
|
|
@@ -18,9 +19,9 @@ export type PackageSyntaxDiagnostic = {
|
|
|
18
19
|
readonly message: string;
|
|
19
20
|
};
|
|
20
21
|
type MessageStyle = Pick<typeof chalk, "red" | "dim" | "bold">;
|
|
21
|
-
export declare function packageCommandUsage(verb: PackageCliVerb): string;
|
|
22
|
+
export declare function packageCommandUsage(verb: PackageCliVerb, namespace?: PackageCommandNamespace): string;
|
|
22
23
|
/** Diagnostic facts stay unstyled until the terminal boundary decides color support. */
|
|
23
|
-
export declare function renderPackageSyntax(diagnostic: PackageSyntaxDiagnostic, style?: MessageStyle): string;
|
|
24
|
+
export declare function renderPackageSyntax(diagnostic: PackageSyntaxDiagnostic, namespace?: PackageCommandNamespace, style?: MessageStyle): string;
|
|
24
25
|
/** Pi's help layout, with unsupported options and examples deliberately absent. */
|
|
25
|
-
export declare function packageCommandHelp(verb: PackageCliVerb, style?: MessageStyle): string;
|
|
26
|
+
export declare function packageCommandHelp(verb: PackageCliVerb, namespace?: PackageCommandNamespace, style?: MessageStyle): string;
|
|
26
27
|
export {};
|
|
@@ -2,21 +2,26 @@
|
|
|
2
2
|
// at 914cf1472e715297caa30db4b9535d534a9eb718 (MIT); grammar is restricted to A1's supported subset.
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { PRODUCT_TEXT } from "../product-identity.js";
|
|
5
|
-
export function packageCommandUsage(verb) {
|
|
6
|
-
const command =
|
|
5
|
+
export function packageCommandUsage(verb, namespace = "pi") {
|
|
6
|
+
const command = namespace === "direct"
|
|
7
|
+
? `${PRODUCT_TEXT.commandName} ${verb}`
|
|
8
|
+
: `${PRODUCT_TEXT.commandName} pi ${verb}`;
|
|
7
9
|
if (verb === "install" || verb === "remove")
|
|
8
10
|
return `${command} <source>`;
|
|
11
|
+
if (verb === "update" && namespace === "direct") {
|
|
12
|
+
return `${command} [--develop [preview-or-version]|--models|--extensions|source]`;
|
|
13
|
+
}
|
|
9
14
|
if (verb === "update")
|
|
10
15
|
return `${command} [source|--extensions|--models]`;
|
|
11
16
|
return command;
|
|
12
17
|
}
|
|
13
18
|
/** Diagnostic facts stay unstyled until the terminal boundary decides color support. */
|
|
14
|
-
export function renderPackageSyntax(diagnostic, style = chalk) {
|
|
15
|
-
const usage = packageCommandUsage(diagnostic.verb);
|
|
19
|
+
export function renderPackageSyntax(diagnostic, namespace = "pi", style = chalk) {
|
|
20
|
+
const usage = packageCommandUsage(diagnostic.verb, namespace);
|
|
16
21
|
let message;
|
|
17
22
|
if (diagnostic.kind === "unknown-option") {
|
|
18
23
|
return `${style.red(`Unknown option ${diagnostic.value} for "${diagnostic.verb}".`)}\n`
|
|
19
|
-
+ `${style.dim(`Use "${PRODUCT_TEXT.commandName}
|
|
24
|
+
+ `${style.dim(`Use "${PRODUCT_TEXT.commandName} help" or "${usage}".`)}\n`;
|
|
20
25
|
}
|
|
21
26
|
if (diagnostic.kind === "missing-source")
|
|
22
27
|
message = `Missing ${diagnostic.verb} source.`;
|
|
@@ -27,9 +32,16 @@ export function renderPackageSyntax(diagnostic, style = chalk) {
|
|
|
27
32
|
return `${style.red(message)}\n${style.dim(`Usage: ${usage}`)}\n`;
|
|
28
33
|
}
|
|
29
34
|
/** Pi's help layout, with unsupported options and examples deliberately absent. */
|
|
30
|
-
export function packageCommandHelp(verb, style = chalk) {
|
|
31
|
-
const command = `${PRODUCT_TEXT.commandName} pi`;
|
|
32
|
-
const header =
|
|
35
|
+
export function packageCommandHelp(verb, namespace = "pi", style = chalk) {
|
|
36
|
+
const command = namespace === "direct" ? PRODUCT_TEXT.commandName : `${PRODUCT_TEXT.commandName} pi`;
|
|
37
|
+
const header = verb === "update" && namespace === "direct"
|
|
38
|
+
? `${style.bold("Usage:")}\n`
|
|
39
|
+
+ ` ${command} update\n`
|
|
40
|
+
+ ` ${command} update --develop [preview-or-version]\n`
|
|
41
|
+
+ ` ${command} update --models\n`
|
|
42
|
+
+ ` ${command} update --extensions\n`
|
|
43
|
+
+ ` ${command} update <source>\n\n`
|
|
44
|
+
: `${style.bold("Usage:")}\n ${packageCommandUsage(verb, namespace)}\n\n`;
|
|
33
45
|
switch (verb) {
|
|
34
46
|
case "install":
|
|
35
47
|
return header + `Install a package and add it to settings.
|
|
@@ -55,14 +67,14 @@ Examples:
|
|
|
55
67
|
case "list":
|
|
56
68
|
return header + "List installed packages from user settings.\n\n";
|
|
57
69
|
case "update":
|
|
58
|
-
return header +
|
|
70
|
+
return header + `${namespace === "direct" ? "Update A1, installed packages, or model catalogs." : "Update installed packages or model catalogs."}
|
|
59
71
|
|
|
60
72
|
Options:
|
|
61
|
-
--extensions Update installed packages only
|
|
73
|
+
${namespace === "direct" ? " --develop [target] Update to the development channel or one preview\n" : ""} --extensions Update installed packages only
|
|
62
74
|
--models Refresh model catalogs only
|
|
63
75
|
|
|
64
76
|
Short forms:
|
|
65
|
-
${command} update --extensions Update installed packages only
|
|
77
|
+
${namespace === "direct" ? ` ${command} update Update A1 to the stable release\n ${command} update --develop Update A1 to the development preview\n` : ""} ${command} update --extensions Update installed packages only
|
|
66
78
|
${command} update --models Refresh model catalogs only
|
|
67
79
|
${command} update <source> Update one package
|
|
68
80
|
|