@danypops/pi-papyrus 0.45.8 → 0.45.10
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/extension/src/{artifact-browser.ts → artifact/artifact-browser.ts} +1 -1
- package/extension/src/{artifact-detail-view.ts → artifact/artifact-detail-view.ts} +2 -2
- package/extension/src/{context-budget.ts → context/context-budget.ts} +1 -1
- package/extension/src/{context-injection-telemetry.ts → context/context-injection-telemetry.ts} +2 -2
- package/extension/src/{discuss-live-follow-up.ts → discuss/discuss-live-follow-up.ts} +1 -1
- package/extension/src/{discuss.ts → discuss/discuss.ts} +4 -4
- package/extension/src/{discussion-detail-view.ts → discuss/discussion-detail-view.ts} +4 -4
- package/extension/src/{docs.ts → docs/docs.ts} +3 -3
- package/extension/src/domain-tools.ts +2 -2
- package/extension/src/index.ts +24 -24
- package/extension/src/{notes.ts → note/notes.ts} +3 -3
- package/extension/src/{playbook-bridge.ts → playbook/playbook-bridge.ts} +1 -1
- package/extension/src/{playbooks.ts → playbook/playbooks.ts} +4 -4
- package/extension/src/{rules.ts → rules/rules.ts} +3 -3
- package/extension/src/{task-detail-format.ts → task/task-detail-format.ts} +1 -1
- package/extension/src/{task-detail-view.ts → task/task-detail-view.ts} +2 -2
- package/extension/src/{task-graph.ts → task/task-graph.ts} +1 -1
- package/extension/src/{tasks.ts → task/tasks.ts} +2 -2
- package/extension/src/tool-rendering/artifact-card.ts +30 -9
- package/extension/src/tool-rendering/render-model.ts +3 -0
- package/extension/src/{vehicle-artifact-renderers.ts → tools/vehicle-artifact-renderers.ts} +62 -72
- package/extension/src/{vehicle-notes-client.ts → tools/vehicle-notes-client.ts} +7 -7
- package/package.json +3 -3
- /package/extension/src/{artifact-detail-format.ts → artifact/artifact-detail-format.ts} +0 -0
- /package/extension/src/{artifact-format.ts → artifact/artifact-format.ts} +0 -0
- /package/extension/src/{artifact-relationship-lines.ts → artifact/artifact-relationship-lines.ts} +0 -0
- /package/extension/src/{artifact-status-presentation.ts → artifact/artifact-status-presentation.ts} +0 -0
- /package/extension/src/{context-hub-contribution.ts → context/context-hub-contribution.ts} +0 -0
- /package/extension/src/{skill-catalog-footprint.ts → context/skill-catalog-footprint.ts} +0 -0
- /package/extension/src/{discuss-ask-layout.ts → discuss/discuss-ask-layout.ts} +0 -0
- /package/extension/src/{discuss-ask-view.ts → discuss/discuss-ask-view.ts} +0 -0
- /package/extension/src/{note-widget.ts → note/note-widget.ts} +0 -0
- /package/extension/src/{active-task-continuation.ts → task/active-task-continuation.ts} +0 -0
- /package/extension/src/{task-focus-events.ts → task/task-focus-events.ts} +0 -0
- /package/extension/src/{task-presentation.ts → task/task-presentation.ts} +0 -0
- /package/extension/src/{task-widget.ts → task/task-widget.ts} +0 -0
|
@@ -2,9 +2,9 @@ import { type Artifact, type OperationName, SEED_RELATIONS } from "@danypops/pap
|
|
|
2
2
|
import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { DynamicBorder, rawKeyHint } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { Container, Input, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
5
|
+
import { callService } from "../service-client.ts";
|
|
5
6
|
import { showArtifactDetailView } from "./artifact-detail-view.ts";
|
|
6
7
|
import type { StatusPresentation } from "./artifact-status-presentation.ts";
|
|
7
|
-
import { callService } from "./service-client.ts";
|
|
8
8
|
|
|
9
9
|
export { artifactDetailsText } from "./artifact-detail-format.ts";
|
|
10
10
|
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { matchesKey, sliceByColumn, type TUI, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
11
11
|
import { buildDetailLines, type DetailField, type DetailSection } from "malevich-tui-components";
|
|
12
|
+
import { BeautifulMermaidRenderer } from "../beautiful-mermaid-renderer.ts";
|
|
13
|
+
import { type ActiveTheme, renderMarkdownBody } from "../markdown.ts";
|
|
12
14
|
import { type ArtifactDetailContent, artifactDetailContent, artifactDetailsText } from "./artifact-detail-format.ts";
|
|
13
15
|
import { buildArtifactRelationshipLines } from "./artifact-relationship-lines.ts";
|
|
14
|
-
import { BeautifulMermaidRenderer } from "./beautiful-mermaid-renderer.ts";
|
|
15
|
-
import { type ActiveTheme, renderMarkdownBody } from "./markdown.ts";
|
|
16
16
|
|
|
17
17
|
interface ArtifactDetailLine {
|
|
18
18
|
text: string;
|
|
@@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import type { ContextSegmentItem } from "@danypops/jittor";
|
|
4
4
|
import { type Artifact, CONTEXT_ESTIMATE_CHARACTERS_PER_TOKEN, CONTEXT_TREE_MAX_NODES, type TaskGraph } from "@danypops/papyrus";
|
|
5
|
-
import { ruleInjectionPreview } from "
|
|
5
|
+
import { ruleInjectionPreview } from "../rules/rules.ts";
|
|
6
6
|
import { discoverSkillDirectories, type SkillCatalogFootprint, scanSkillCatalogFootprint } from "./skill-catalog-footprint.ts";
|
|
7
7
|
|
|
8
8
|
/**
|
package/extension/src/{context-injection-telemetry.ts → context/context-injection-telemetry.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { type Artifact, CONTEXT_ESTIMATE_CHARACTERS_PER_TOKEN, PAPYRUS_CONTEXT_INJECTION_SCHEMA } from "@danypops/papyrus";
|
|
3
|
-
import { playbookInjectionPreview } from "
|
|
4
|
-
import { ruleInjectionPreview } from "
|
|
3
|
+
import { playbookInjectionPreview } from "../playbook/playbook-bridge.ts";
|
|
4
|
+
import { ruleInjectionPreview } from "../rules/rules.ts";
|
|
5
5
|
|
|
6
6
|
export interface ContextPayloadSize {
|
|
7
7
|
characters: number;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* discuss.open/discuss.reply's live:true synchronous human round-trip -- the
|
|
3
3
|
* one piece of discuss's own retired pi.registerTool() the plain
|
|
4
4
|
* request/response Vehicle projection can't express on its own, wired in via
|
|
5
|
-
* vehicle-client-pi's interactiveFollowUps hook (vehicle-notes-client.ts).
|
|
5
|
+
* vehicle-client-pi's interactiveFollowUps hook (../tools/vehicle-notes-client.ts).
|
|
6
6
|
*
|
|
7
7
|
* The primary invoke() always durably records the round first, exactly like
|
|
8
8
|
* every other Vehicle operation -- this only ever adds an OPTIONAL synchronous
|
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
* surfaces history count, both also not the row glyph.
|
|
11
11
|
*
|
|
12
12
|
* Creating a new Discussion is left to the agent (discuss_open, Vehicle-projected -- see
|
|
13
|
-
* vehicle-notes-client.ts), matching docs.ts/rules.ts/playbooks.ts precedent -- Notes is the one
|
|
13
|
+
* ../tools/vehicle-notes-client.ts), matching docs.ts/rules.ts/playbooks.ts precedent -- Notes is the one
|
|
14
14
|
* kind with a human-facing creation command (/note), because Notes exists specifically as a
|
|
15
15
|
* human-authored inbox.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { type Artifact, type DiscussionAndRounds, readDiscussionExtra } from "@danypops/papyrus";
|
|
19
19
|
import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
20
|
-
import { showArtifactBrowser } from "
|
|
21
|
-
import { DISCUSSION_STATE_PRESENTATION, DOC_STATUS_PRESENTATION } from "
|
|
20
|
+
import { showArtifactBrowser } from "../artifact/artifact-browser.ts";
|
|
21
|
+
import { DISCUSSION_STATE_PRESENTATION, DOC_STATUS_PRESENTATION } from "../artifact/artifact-status-presentation.ts";
|
|
22
|
+
import { callService } from "../service-client.ts";
|
|
22
23
|
import { askQuestion } from "./discuss-ask-view.ts";
|
|
23
24
|
import { discussionRoundCountOf, discussionStateOf, showDiscussionDetailView } from "./discussion-detail-view.ts";
|
|
24
|
-
import { callService } from "./service-client.ts";
|
|
25
25
|
|
|
26
26
|
const SOURCE = "discuss-tui";
|
|
27
27
|
const ACTOR = "human";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* discussion-detail-view.ts — the transcript view for a single Discussion.
|
|
3
3
|
*
|
|
4
|
-
* The generic artifact detail view (artifact-detail-view.ts) formats an artifact's own
|
|
4
|
+
* The generic artifact detail view (artifact/artifact-detail-view.ts) formats an artifact's own
|
|
5
5
|
* fields (title, body, extra as JSON, edges); it has no way to show a Discussion's rounds,
|
|
6
6
|
* since those live in a dedicated child table fetched separately (discuss.show / discuss.rounds),
|
|
7
7
|
* not in the artifact row itself. Tasks needed the same kind of dedicated view for the same
|
|
8
|
-
* underlying reason (task-detail-view.ts) -- this mirrors that scrolling-viewport idiom rather
|
|
8
|
+
* underlying reason (task/task-detail-view.ts) -- this mirrors that scrolling-viewport idiom rather
|
|
9
9
|
* than inventing a new one.
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
} from "@danypops/papyrus";
|
|
20
20
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
21
21
|
import { matchesKey, type TUI, truncateToWidth } from "@earendil-works/pi-tui";
|
|
22
|
-
import { DISCUSSION_STATE_PRESENTATION } from "
|
|
23
|
-
import { type ActiveTheme, renderMarkdownBody } from "
|
|
22
|
+
import { DISCUSSION_STATE_PRESENTATION } from "../artifact/artifact-status-presentation.ts";
|
|
23
|
+
import { type ActiveTheme, renderMarkdownBody } from "../markdown.ts";
|
|
24
24
|
|
|
25
25
|
interface TranscriptLine {
|
|
26
26
|
text: string;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Artifact } from "@danypops/papyrus";
|
|
2
2
|
import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { showArtifactBrowser, showArtifactDetails } from "
|
|
4
|
-
import { DOC_STATUS_PRESENTATION } from "
|
|
5
|
-
import { callService } from "
|
|
3
|
+
import { showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
4
|
+
import { DOC_STATUS_PRESENTATION } from "../artifact/artifact-status-presentation.ts";
|
|
5
|
+
import { callService } from "../service-client.ts";
|
|
6
6
|
|
|
7
7
|
const DOC_ACTIONS: Record<string, string[]> = {
|
|
8
8
|
draft: ["Activate", "Archive"],
|
|
@@ -111,7 +111,7 @@ export async function resolveNameFields(
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// notes.*, rules.*, docs.*, playbooks.*, tasks.*, discuss.*, and the shared artifact.*
|
|
114
|
-
// are all registered as Vehicles (see
|
|
115
|
-
// src/
|
|
114
|
+
// are all registered as Vehicles (see ./tools/vehicle-notes-client.ts and @danypops/papyrus's
|
|
115
|
+
// src/handlers/registry.ts) -- no domain has a hand-rolled pi.registerTool() left
|
|
116
116
|
// in this file. What remains here (above) is name-resolution machinery still used
|
|
117
117
|
// directly by index.ts's own low-level tools (papyrus_graph).
|
package/extension/src/index.ts
CHANGED
|
@@ -25,26 +25,26 @@ import type { PushChannelClient } from "@danypops/vehicle-client/daemon-client";
|
|
|
25
25
|
import type { ExtensionAPI, ExtensionContext, ExtensionUIContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
26
26
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
27
27
|
import { Type } from "typebox";
|
|
28
|
+
import { formatMetadata } from "./artifact/artifact-format.ts";
|
|
29
|
+
import { BoundedPoll } from "./bounded-poll.ts";
|
|
30
|
+
import { buildTaskItemTree, computeContextBudget } from "./context/context-budget.ts";
|
|
31
|
+
import { PAPYRUS_CONTEXT_HUB_PRODUCER_NAME, papyrusContextSegment } from "./context/context-hub-contribution.ts";
|
|
32
|
+
import { buildContextInjection } from "./context/context-injection-telemetry.ts";
|
|
33
|
+
import { ensureTypingCourtesyTracking, isLiveAskPending } from "./discuss/discuss-ask-view.ts";
|
|
34
|
+
import { resolveNameFields } from "./domain-tools.ts";
|
|
35
|
+
import { renderNoteWidgetLines } from "./note/note-widget.ts";
|
|
36
|
+
import { PLAYBOOK_BRIDGE_MAX_PLAYBOOKS, registerPlaybookBridge } from "./playbook/playbook-bridge.ts";
|
|
37
|
+
import { callService, subscribeTaskPushChannel } from "./service-client.ts";
|
|
38
|
+
import { cacheSessionSecret, forgetSessionSecret, sessionSecretField } from "./session-identity.ts";
|
|
28
39
|
import {
|
|
29
40
|
ActiveTaskContinuation,
|
|
30
41
|
type ActiveTaskMarker,
|
|
31
42
|
automaticPauseReason,
|
|
32
43
|
shouldResumeFocusOnHumanInput,
|
|
33
|
-
} from "./active-task-continuation.ts";
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import { PAPYRUS_CONTEXT_HUB_PRODUCER_NAME, papyrusContextSegment } from "./context-hub-contribution.ts";
|
|
38
|
-
import { buildContextInjection } from "./context-injection-telemetry.ts";
|
|
39
|
-
import { ensureTypingCourtesyTracking, isLiveAskPending } from "./discuss-ask-view.ts";
|
|
40
|
-
import { resolveNameFields } from "./domain-tools.ts";
|
|
41
|
-
import { renderNoteWidgetLines } from "./note-widget.ts";
|
|
42
|
-
import { PLAYBOOK_BRIDGE_MAX_PLAYBOOKS, registerPlaybookBridge } from "./playbook-bridge.ts";
|
|
43
|
-
import { callService, subscribeTaskPushChannel } from "./service-client.ts";
|
|
44
|
-
import { cacheSessionSecret, forgetSessionSecret, sessionSecretField } from "./session-identity.ts";
|
|
45
|
-
import { emitTaskFocusEvent, setTaskFocusEventBus } from "./task-focus-events.ts";
|
|
46
|
-
import { TASK_STATUS_PRESENTATION, taskTreeConnector } from "./task-presentation.ts";
|
|
47
|
-
import { buildTaskWidgetProjection, type TaskWidgetProjection } from "./task-widget.ts";
|
|
44
|
+
} from "./task/active-task-continuation.ts";
|
|
45
|
+
import { emitTaskFocusEvent, setTaskFocusEventBus } from "./task/task-focus-events.ts";
|
|
46
|
+
import { TASK_STATUS_PRESENTATION, taskTreeConnector } from "./task/task-presentation.ts";
|
|
47
|
+
import { buildTaskWidgetProjection, type TaskWidgetProjection } from "./task/task-widget.ts";
|
|
48
48
|
import { renderPapyrusToolCall, renderPapyrusToolResult } from "./tool-rendering/index.ts";
|
|
49
49
|
import {
|
|
50
50
|
createArtifactDetails,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
createModelContent,
|
|
54
54
|
createPreviewDetails,
|
|
55
55
|
} from "./tool-rendering/render-model.ts";
|
|
56
|
-
import { registerNotesVehicle } from "./vehicle-notes-client.ts";
|
|
56
|
+
import { registerNotesVehicle } from "./tools/vehicle-notes-client.ts";
|
|
57
57
|
|
|
58
58
|
function text(value: string, details: unknown = {}) {
|
|
59
59
|
const modelContent = createModelContent(value);
|
|
@@ -357,7 +357,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
357
357
|
let previousContextInjectionFingerprint: string | undefined;
|
|
358
358
|
let logTurnSequence = 0;
|
|
359
359
|
// Papyrus's own Context Hub contribution (rules/tasks/Pi's own skill catalog, bundled into one segment --
|
|
360
|
-
// see context-hub-contribution.ts) re-emits every turn alongside the existing injection
|
|
360
|
+
// see context/context-hub-contribution.ts) re-emits every turn alongside the existing injection
|
|
361
361
|
// observation, its own independent monotonic sequence, same cadence and shape as
|
|
362
362
|
// contextInjectionSequence but on a different channel/schema.
|
|
363
363
|
let contextHubContributionSequence = 0;
|
|
@@ -371,7 +371,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
371
371
|
// ctx.isIdle() means "not streaming a model response" -- it reads true while a live discuss
|
|
372
372
|
// ask is still genuinely pending, blocked on the human. Queuing a "continue the active task"
|
|
373
373
|
// nudge here would start a second, concurrent turn reasoning about the very Discussion this
|
|
374
|
-
// live ask is already resolving. See discuss-ask-view.ts's isLiveAskPending() doc comment.
|
|
374
|
+
// live ask is already resolving. See discuss/discuss-ask-view.ts's isLiveAskPending() doc comment.
|
|
375
375
|
if (isLiveAskPending()) return;
|
|
376
376
|
try {
|
|
377
377
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
@@ -627,12 +627,12 @@ export default async function (pi: ExtensionAPI) {
|
|
|
627
627
|
|
|
628
628
|
// Lazy imports keep TUI components out of non-interactive startup paths.
|
|
629
629
|
const [tasksModule, docsModule, notesModule, rulesModule, playbooksModule, discussModule] = await Promise.all([
|
|
630
|
-
import("./tasks.ts"),
|
|
631
|
-
import("./docs.ts"),
|
|
632
|
-
import("./notes.ts"),
|
|
633
|
-
import("./rules.ts"),
|
|
634
|
-
import("./playbooks.ts"),
|
|
635
|
-
import("./discuss.ts"),
|
|
630
|
+
import("./task/tasks.ts"),
|
|
631
|
+
import("./docs/docs.ts"),
|
|
632
|
+
import("./note/notes.ts"),
|
|
633
|
+
import("./rules/rules.ts"),
|
|
634
|
+
import("./playbook/playbooks.ts"),
|
|
635
|
+
import("./discuss/discuss.ts"),
|
|
636
636
|
]);
|
|
637
637
|
let overlay: TaskOverlay | undefined;
|
|
638
638
|
let noteOverlay: NoteOverlay | undefined;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type Artifact, NOTE_DISPOSITIONS, NOTE_LIST_MAX_LIMIT } from "@danypops/papyrus";
|
|
2
2
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { showArtifactBrowser, showArtifactDetails } from "
|
|
4
|
-
import { NOTE_STATUS_PRESENTATION } from "
|
|
5
|
-
import { callService } from "
|
|
3
|
+
import { showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
4
|
+
import { NOTE_STATUS_PRESENTATION } from "../artifact/artifact-status-presentation.ts";
|
|
5
|
+
import { callService } from "../service-client.ts";
|
|
6
6
|
|
|
7
7
|
export function noteRowMeta(note: Artifact): string {
|
|
8
8
|
const history = Array.isArray(note.extra.noteHistory) ? note.extra.noteHistory.length : 0;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import type { Artifact } from "@danypops/papyrus";
|
|
22
22
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
23
|
-
import { callService } from "
|
|
23
|
+
import { callService } from "../service-client.ts";
|
|
24
24
|
|
|
25
25
|
export const PLAYBOOK_BRIDGE_MAX_PLAYBOOKS = 100;
|
|
26
26
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Artifact } from "@danypops/papyrus";
|
|
2
2
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import type { AutocompleteItem } from "@earendil-works/pi-tui";
|
|
4
|
-
import { showArtifactBrowser, showArtifactDetails } from "
|
|
5
|
-
import { PLAYBOOK_STATUS_PRESENTATION } from "
|
|
6
|
-
import { matchArtifactByName } from "
|
|
7
|
-
import { callService } from "
|
|
4
|
+
import { showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
5
|
+
import { PLAYBOOK_STATUS_PRESENTATION } from "../artifact/artifact-status-presentation.ts";
|
|
6
|
+
import { matchArtifactByName } from "../domain-tools.ts";
|
|
7
|
+
import { callService } from "../service-client.ts";
|
|
8
8
|
|
|
9
9
|
const PLAYBOOK_COMPLETION_MAX_CANDIDATES = 100;
|
|
10
10
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Artifact } from "@danypops/papyrus";
|
|
2
2
|
import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { showArtifactBrowser, showArtifactDetails } from "
|
|
4
|
-
import { RULE_STATUS_PRESENTATION, severityColor } from "
|
|
5
|
-
import { callService } from "
|
|
3
|
+
import { showArtifactBrowser, showArtifactDetails } from "../artifact/artifact-browser.ts";
|
|
4
|
+
import { RULE_STATUS_PRESENTATION, severityColor } from "../artifact/artifact-status-presentation.ts";
|
|
5
|
+
import { callService } from "../service-client.ts";
|
|
6
6
|
|
|
7
7
|
export function ruleRowMeta(rule: Artifact, theme: Theme): string {
|
|
8
8
|
const severity = typeof rule.extra.severity === "string" ? rule.extra.severity : "info";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Artifact, checklistEntries, type ProofReference, type TaskEvent } from "@danypops/papyrus";
|
|
2
|
-
import { formatMetadata } from "
|
|
2
|
+
import { formatMetadata } from "../artifact/artifact-format.ts";
|
|
3
3
|
|
|
4
4
|
const TASK_STATUS_GLYPHS: Record<string, string> = {
|
|
5
5
|
todo: "○",
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import { matchesKey, sliceByColumn, type TUI, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
14
14
|
import { buildDetailLines, type DetailField, type DetailSection } from "malevich-tui-components";
|
|
15
|
-
import { BeautifulMermaidRenderer } from "
|
|
16
|
-
import { type ActiveTheme, renderMarkdownBody } from "
|
|
15
|
+
import { BeautifulMermaidRenderer } from "../beautiful-mermaid-renderer.ts";
|
|
16
|
+
import { type ActiveTheme, renderMarkdownBody } from "../markdown.ts";
|
|
17
17
|
import { type TaskDetailContent, taskDetailContent, taskDetailsText } from "./task-detail-format.ts";
|
|
18
18
|
import { TASK_STATUS_PRESENTATION } from "./task-presentation.ts";
|
|
19
19
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "@danypops/papyrus";
|
|
11
11
|
import type { ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { matchesKey, sliceByColumn, type TUI, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
13
|
-
import { BeautifulMermaidRenderer } from "
|
|
13
|
+
import { BeautifulMermaidRenderer } from "../beautiful-mermaid-renderer.ts";
|
|
14
14
|
import { TASK_STATUS_PRESENTATION } from "./task-presentation.ts";
|
|
15
15
|
|
|
16
16
|
const GRAPH_VIEWS: TaskGraphView[] = ["execution", "dependencies", "composition"];
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { DynamicBorder, rawKeyHint } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Container, Input, matchesKey, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
9
|
-
import { callService } from "
|
|
10
|
-
import { sessionSecretField } from "
|
|
9
|
+
import { callService } from "../service-client.ts";
|
|
10
|
+
import { sessionSecretField } from "../session-identity.ts";
|
|
11
11
|
import { showTaskDetails } from "./task-detail-view.ts";
|
|
12
12
|
import { emitTaskFocusEvent } from "./task-focus-events.ts";
|
|
13
13
|
import { showTaskGraph } from "./task-graph.ts";
|
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { type Component, truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
-
import { buildDetailLines, type DetailField, type DetailSection } from "malevich-tui-components";
|
|
2
|
+
import { type Component, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
3
|
+
import { buildDetailLines, type DetailField, type DetailSection, type DetailViewTheme, type TextMeasure } from "malevich-tui-components";
|
|
4
4
|
import type { ArtifactToolDetails } from "./render-model.ts";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Real ANSI-aware measurement/wrapping -- without this, buildDetailLines falls back to
|
|
8
|
+
* Malevich's own asciiTextMeasure, which is documented as having "no ANSI-code awareness"
|
|
9
|
+
* and is only meant for short, unstyled labels. A themed multi-paragraph body (this file's
|
|
10
|
+
* own Body: section) styles the whole text once before wrapping; the ASCII fallback then
|
|
11
|
+
* slices that already-colored string by raw character index, so only the first and last
|
|
12
|
+
* resulting physical lines keep the color escape -- every line in between renders in the
|
|
13
|
+
* terminal's default color. wrapTextWithAnsi re-injects the active codes on every wrapped
|
|
14
|
+
* line instead.
|
|
15
|
+
*/
|
|
16
|
+
const measure: TextMeasure = { visibleWidth, truncateToWidth, wrapTextWithAnsi };
|
|
17
|
+
|
|
18
|
+
/** Shared by every buildDetailLines caller in this extension (ArtifactCard, and
|
|
19
|
+
* tools/vehicle-artifact-renderers.ts's discuss/tasks.complete renderers) -- one Theme -> DetailViewTheme
|
|
20
|
+
* mapping instead of the same four-field object literal re-typed at each call site. */
|
|
21
|
+
export function detailViewTheme(theme: Theme): DetailViewTheme {
|
|
22
|
+
return {
|
|
23
|
+
field: (s) => theme.fg("text", s),
|
|
24
|
+
heading: (s) => theme.fg("toolTitle", theme.bold(s)),
|
|
25
|
+
byline: (s) => theme.fg("muted", s),
|
|
26
|
+
body: (s) => theme.fg("text", s),
|
|
27
|
+
line: (s) => theme.fg("warning", s),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
6
31
|
const KIND_GLYPHS: Readonly<Record<string, string>> = {
|
|
7
32
|
task: "◇",
|
|
8
33
|
doc: "▤",
|
|
@@ -100,6 +125,7 @@ export class ArtifactCard implements Component {
|
|
|
100
125
|
const status = this.theme.fg(statusColor(artifact.status), `${statusGlyph(artifact.status)} ${artifact.status}`);
|
|
101
126
|
return [
|
|
102
127
|
{ label: "Title", value: artifact.title },
|
|
128
|
+
{ label: "Alias", value: artifact.alias ?? artifact.id },
|
|
103
129
|
{ label: "Kind", value: `${kindGlyph(artifact.kind)} ${artifact.kind}` },
|
|
104
130
|
{ label: "Status", value: status },
|
|
105
131
|
...(this.expanded ? [{ label: "ID", value: artifact.id }] : []),
|
|
@@ -128,13 +154,8 @@ export class ArtifactCard implements Component {
|
|
|
128
154
|
fields: this.fields(),
|
|
129
155
|
sections: this.sections(),
|
|
130
156
|
alignFields: true,
|
|
131
|
-
theme:
|
|
132
|
-
|
|
133
|
-
heading: (s) => theme.fg("toolTitle", theme.bold(s)),
|
|
134
|
-
byline: (s) => theme.fg("muted", s),
|
|
135
|
-
body: (s) => theme.fg("text", s),
|
|
136
|
-
line: (s) => theme.fg("warning", s),
|
|
137
|
-
},
|
|
157
|
+
theme: detailViewTheme(theme),
|
|
158
|
+
measure,
|
|
138
159
|
});
|
|
139
160
|
|
|
140
161
|
if (this.details.focus) {
|
|
@@ -18,6 +18,8 @@ export interface ResultCompleteness {
|
|
|
18
18
|
|
|
19
19
|
export interface ToolArtifactSummary {
|
|
20
20
|
id: string;
|
|
21
|
+
/** Optional only for a detail object persisted before this field existed -- ArtifactCard falls back to id when absent. */
|
|
22
|
+
alias?: string;
|
|
21
23
|
kind: string;
|
|
22
24
|
title: string;
|
|
23
25
|
status: string;
|
|
@@ -154,6 +156,7 @@ function boundedText(value: string, maximum: number): { value: string; completen
|
|
|
154
156
|
function artifactSummary(artifact: Artifact): ToolArtifactSummary {
|
|
155
157
|
return {
|
|
156
158
|
id: artifact.id,
|
|
159
|
+
alias: artifact.alias,
|
|
157
160
|
kind: artifact.kind,
|
|
158
161
|
title: artifact.title,
|
|
159
162
|
status: artifact.status,
|
|
@@ -20,9 +20,9 @@ import type { VehicleOperationDescriptor } from "@danypops/vehicle-core";
|
|
|
20
20
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
21
21
|
import { type Component, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
22
22
|
import { buildDetailLines, type DagEdge, type DagNode, DagView, type DetailField, type DetailSection } from "malevich-tui-components";
|
|
23
|
-
import { ArtifactCard, expandHint, statusColor, statusGlyph } from "
|
|
24
|
-
import { ArtifactListCard } from "
|
|
25
|
-
import { type ArtifactFocusAnnotation, createArtifactDetails, createArtifactListDetails } from "
|
|
23
|
+
import { ArtifactCard, detailViewTheme, expandHint, statusColor, statusGlyph } from "../tool-rendering/artifact-card.ts";
|
|
24
|
+
import { ArtifactListCard } from "../tool-rendering/artifact-list.ts";
|
|
25
|
+
import { type ArtifactFocusAnnotation, createArtifactDetails, createArtifactListDetails } from "../tool-rendering/render-model.ts";
|
|
26
26
|
|
|
27
27
|
function isArtifact(value: unknown): value is Artifact {
|
|
28
28
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -261,13 +261,6 @@ function isDiscussionListOutput(value: unknown): value is DiscussionListOutput {
|
|
|
261
261
|
return isArtifactArray(row.discussions);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
const discussTheme = (theme: Theme) => ({
|
|
265
|
-
field: (s: string) => theme.fg("text", s),
|
|
266
|
-
heading: (s: string) => theme.fg("toolTitle", theme.bold(s)),
|
|
267
|
-
byline: (s: string) => theme.fg("muted", s),
|
|
268
|
-
body: (s: string) => theme.fg("text", s),
|
|
269
|
-
});
|
|
270
|
-
|
|
271
264
|
function roundsSection(rounds: readonly DiscussionRoundOutput[]): DetailSection {
|
|
272
265
|
return {
|
|
273
266
|
heading: `Rounds (${rounds.length}):`,
|
|
@@ -275,35 +268,35 @@ function roundsSection(rounds: readonly DiscussionRoundOutput[]): DetailSection
|
|
|
275
268
|
};
|
|
276
269
|
}
|
|
277
270
|
|
|
271
|
+
/** A one-shot render function with nothing to invalidate -- every buildDetailLines-based
|
|
272
|
+
* renderer in this file (unlike ArtifactCard/DagView) has no cache to clear. */
|
|
273
|
+
function statelessComponent(render: (width: number) => string[]): Component {
|
|
274
|
+
return { render, invalidate: () => {} };
|
|
275
|
+
}
|
|
276
|
+
|
|
278
277
|
function renderDiscussionAndRounds(output: DiscussionAndRoundsOutput, theme: Theme, expanded: boolean): Component {
|
|
279
278
|
const discussion = output.discussion;
|
|
280
|
-
return {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
},
|
|
295
|
-
invalidate: () => {},
|
|
296
|
-
};
|
|
279
|
+
return statelessComponent((width) => {
|
|
280
|
+
const safeWidth = Math.max(1, width);
|
|
281
|
+
const fields: DetailField[] = [
|
|
282
|
+
{ label: "Title", value: discussion.title },
|
|
283
|
+
{ label: "Status", value: theme.fg(statusColor(discussion.status), `${statusGlyph(discussion.status)} ${discussion.status}`) },
|
|
284
|
+
];
|
|
285
|
+
const sections: DetailSection[] = expanded && output.rounds.length > 0 ? [roundsSection(output.rounds)] : [];
|
|
286
|
+
const lines = buildDetailLines(safeWidth, { fields, sections, alignFields: true, theme: detailViewTheme(theme) });
|
|
287
|
+
if (!expanded && output.rounds.length > 0) {
|
|
288
|
+
const count = output.rounds.length;
|
|
289
|
+
lines.push(truncateToWidth(theme.fg("dim", `${count} round${count === 1 ? "" : "s"} · ${expandHint()}`), safeWidth));
|
|
290
|
+
}
|
|
291
|
+
return lines;
|
|
292
|
+
});
|
|
297
293
|
}
|
|
298
294
|
|
|
299
295
|
function renderDiscussionRoundsOnly(output: DiscussionRoundsOnlyOutput, theme: Theme): Component {
|
|
300
|
-
return {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
},
|
|
305
|
-
invalidate: () => {},
|
|
306
|
-
};
|
|
296
|
+
return statelessComponent((width) => {
|
|
297
|
+
const sections: DetailSection[] = output.rounds.length > 0 ? [roundsSection(output.rounds)] : [{ lines: ["No rounds."] }];
|
|
298
|
+
return buildDetailLines(Math.max(1, width), { sections, theme: detailViewTheme(theme) });
|
|
299
|
+
});
|
|
307
300
|
}
|
|
308
301
|
|
|
309
302
|
/** tasks.complete's own TaskCompletion shape -- a completed (or rejected) task plus its own
|
|
@@ -350,45 +343,42 @@ function isTaskCompletion(value: unknown): value is TaskCompletionOutput {
|
|
|
350
343
|
|
|
351
344
|
function renderTaskCompletion(result: TaskCompletionOutput, theme: Theme, expanded: boolean): Component {
|
|
352
345
|
const task = result.artifact;
|
|
353
|
-
return {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
`${entry.accepted ? "✓" : "✗"} ${entry.item}${entry.reason ? ` — ${entry.reason}` : ""}`,
|
|
374
|
-
),
|
|
346
|
+
return statelessComponent((width) => {
|
|
347
|
+
const safeWidth = Math.max(1, width);
|
|
348
|
+
const fields: DetailField[] = [
|
|
349
|
+
{ label: "Title", value: task.title },
|
|
350
|
+
{ label: "Status", value: theme.fg(statusColor(task.status), `${statusGlyph(task.status)} ${task.status}`) },
|
|
351
|
+
];
|
|
352
|
+
const sections: DetailSection[] = [];
|
|
353
|
+
if (result.gates.length > 0) {
|
|
354
|
+
sections.push({
|
|
355
|
+
heading: "Gates:",
|
|
356
|
+
lines: result.gates.map((gate) => theme.fg(gate.passed ? "success" : "error", `${gate.passed ? "✓" : "✗"} ${gate.output}`)),
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
if (result.checklist.length > 0) {
|
|
360
|
+
sections.push({
|
|
361
|
+
heading: "Checklist:",
|
|
362
|
+
lines: result.checklist.map((entry) =>
|
|
363
|
+
theme.fg(
|
|
364
|
+
entry.accepted ? "success" : "error",
|
|
365
|
+
`${entry.accepted ? "✓" : "✗"} ${entry.item}${entry.reason ? ` — ${entry.reason}` : ""}`,
|
|
375
366
|
),
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
})
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
};
|
|
367
|
+
),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
if (result.blocked.length > 0) {
|
|
371
|
+
sections.push({
|
|
372
|
+
heading: "Still blocked:",
|
|
373
|
+
lines: result.blocked.map((entry) => theme.fg("warning", `◼ ${entry.artifact.title}`)),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const lines = buildDetailLines(safeWidth, { fields, sections, alignFields: true, theme: detailViewTheme(theme) });
|
|
377
|
+
if (result.focused && expanded) {
|
|
378
|
+
lines.push(truncateToWidth(theme.fg("accent", `▶ focus ${result.focused.title}`), safeWidth));
|
|
379
|
+
}
|
|
380
|
+
return lines;
|
|
381
|
+
});
|
|
392
382
|
}
|
|
393
383
|
|
|
394
384
|
export function papyrusVehicleRenderers(descriptor: VehicleOperationDescriptor): VehicleToolRenderers {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registers every Vehicle-projected domain (notes.*, rules.*, docs.*, playbooks.*,
|
|
3
3
|
* tasks.*, discuss.*, artifact.*) as real Pi tools -- see @danypops/papyrus's
|
|
4
|
-
* src/
|
|
4
|
+
* src/handlers/registry.ts. discuss.* is the last of the six domains to
|
|
5
5
|
* migrate off pi-papyrus's own retired hand-rolled pi.registerTool() mega-tool.
|
|
6
6
|
*
|
|
7
7
|
* Fails silently on a stale/unreachable daemon handle instead of aborting extension
|
|
@@ -24,10 +24,10 @@ import { createReconnectingVehicleClient } from "@danypops/vehicle-client/daemon
|
|
|
24
24
|
import { RemoteVehicleClient } from "@danypops/vehicle-client/http";
|
|
25
25
|
import { registerVehicleTools } from "@danypops/vehicle-client-pi";
|
|
26
26
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
27
|
-
import { discussLiveFollowUp } from "
|
|
28
|
-
import { currentVehicleClientTarget } from "
|
|
29
|
-
import { sessionSecretField } from "
|
|
30
|
-
import { emitTaskFocusEvent } from "
|
|
27
|
+
import { discussLiveFollowUp } from "../discuss/discuss-live-follow-up.ts";
|
|
28
|
+
import { currentVehicleClientTarget } from "../service-client.ts";
|
|
29
|
+
import { sessionSecretField } from "../session-identity.ts";
|
|
30
|
+
import { emitTaskFocusEvent } from "../task/task-focus-events.ts";
|
|
31
31
|
import { papyrusVehicleRenderers } from "./vehicle-artifact-renderers.ts";
|
|
32
32
|
|
|
33
33
|
const REGISTERED_PERMISSIONS = [
|
|
@@ -66,7 +66,7 @@ export async function registerNotesVehicle(pi: ExtensionAPI): Promise<void> {
|
|
|
66
66
|
// playbooks.invoke's own module handler, and tasks.focus/pause/unpause/clear_focus's
|
|
67
67
|
// own module handlers, authorize an internal Task Focus write via
|
|
68
68
|
// sessionIdentity.assertAuthorized(session_id, session_secret) -- see
|
|
69
|
-
// @danypops/papyrus's src/
|
|
69
|
+
// @danypops/papyrus's src/handlers/playbooks.ts and tasks.ts. That
|
|
70
70
|
// secret must never be a model-visible input field (the model has no business
|
|
71
71
|
// knowing or supplying it), so it travels here instead, in principal.claims, from
|
|
72
72
|
// this extension's own already-cached secret (registered at session_start -- see
|
|
@@ -97,7 +97,7 @@ export async function registerNotesVehicle(pi: ExtensionAPI): Promise<void> {
|
|
|
97
97
|
// -- has no Vehicle-transport equivalent, so it's emitted here, client-side, rather
|
|
98
98
|
// than from the operation's own output.
|
|
99
99
|
// discuss.open/discuss.reply's own live:true synchronous human round-trip --
|
|
100
|
-
// see discuss-live-follow-up.ts. Every other operation's resolver call
|
|
100
|
+
// see discuss/discuss-live-follow-up.ts. Every other operation's resolver call
|
|
101
101
|
// returns undefined, meaning zero behavior change for the other 5 domains.
|
|
102
102
|
interactiveFollowUps: (descriptor) =>
|
|
103
103
|
descriptor.name === "discuss.open" || descriptor.name === "discuss.reply" ? discussLiveFollowUp : undefined,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-papyrus",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.10",
|
|
4
4
|
"description": "Pi host extension for Papyrus: native tools, TUI panels, and context injection over the daemon-backed graph store",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": ["pi-package"],
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"@danypops/papyrus": "^0.44.0",
|
|
22
22
|
"@danypops/vehicle-client": "^0.5.0",
|
|
23
23
|
"@danypops/vehicle-client-pi": "^0.16.8",
|
|
24
|
-
"@danypops/vehicle-core": "^0.
|
|
25
|
-
"@danypops/vehicle-server": "^0.
|
|
24
|
+
"@danypops/vehicle-core": "^0.12.3",
|
|
25
|
+
"@danypops/vehicle-server": "^0.18.1",
|
|
26
26
|
"beautiful-mermaid": "1.1.3",
|
|
27
27
|
"malevich-tui-components": "^0.21.2"
|
|
28
28
|
},
|
|
File without changes
|
|
File without changes
|
/package/extension/src/{artifact-relationship-lines.ts → artifact/artifact-relationship-lines.ts}
RENAMED
|
File without changes
|
/package/extension/src/{artifact-status-presentation.ts → artifact/artifact-status-presentation.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|