pi-goals 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.pi/extensions/goal/budget.ts +74 -0
- package/.pi/extensions/goal/command.ts +201 -0
- package/.pi/extensions/goal/constants.ts +39 -0
- package/.pi/extensions/goal/continuation.ts +122 -0
- package/.pi/extensions/goal/format.ts +153 -0
- package/.pi/extensions/goal/index.ts +19 -0
- package/.pi/extensions/goal/lifecycle.ts +366 -0
- package/.pi/extensions/goal/model-output.ts +76 -0
- package/.pi/extensions/goal/monitor-prompts.ts +161 -0
- package/.pi/extensions/goal/monitor-report.ts +93 -0
- package/.pi/extensions/goal/monitor-state.ts +77 -0
- package/.pi/extensions/goal/monitor.ts +191 -0
- package/.pi/extensions/goal/prompts.ts +98 -0
- package/.pi/extensions/goal/state.ts +141 -0
- package/.pi/extensions/goal/telemetry.ts +153 -0
- package/.pi/extensions/goal/templates.ts +228 -0
- package/.pi/extensions/goal/tools.ts +279 -0
- package/.pi/extensions/goal/types.ts +168 -0
- package/.pi/extensions/goal/ui.ts +41 -0
- package/.pi/extensions/goal/widget.ts +159 -0
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/package.json +44 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export type GoalStatus = "active" | "paused" | "budgetLimited" | "complete";
|
|
4
|
+
|
|
5
|
+
export type GoalState = {
|
|
6
|
+
goalId: string;
|
|
7
|
+
objective: string;
|
|
8
|
+
status: GoalStatus;
|
|
9
|
+
tokenBudget?: number;
|
|
10
|
+
timeBudgetSeconds?: number;
|
|
11
|
+
tokensUsed: number;
|
|
12
|
+
timeUsedSeconds: number;
|
|
13
|
+
createdAt: number;
|
|
14
|
+
updatedAt: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type TurnOrigin = "user" | "auto" | "budgetWrapUp";
|
|
18
|
+
export type ContinuationReason = "created" | "resumed" | "agentEnd";
|
|
19
|
+
export type ContinuationSkipReason = "notIdle" | "pendingMessages" | "notActive" | "budgetLimited" | "safetyCap";
|
|
20
|
+
export type SafetyPauseReason = "maxAutoTurns" | "noProgress" | "abort";
|
|
21
|
+
export type BudgetLimitReason = "tokenBudget" | "timeBudget";
|
|
22
|
+
export type BudgetWarningReason = "tokenWarning" | "timeWarning";
|
|
23
|
+
export type BudgetHardStopReason = "tokenHardStop" | "timeHardStop";
|
|
24
|
+
export type BudgetPressureKind = "none" | BudgetWarningReason | "tokenReached" | "timeReached" | BudgetHardStopReason;
|
|
25
|
+
export type BudgetPressure = { kind: BudgetPressureKind; remaining?: number };
|
|
26
|
+
|
|
27
|
+
export type GoalTelemetrySnapshot = {
|
|
28
|
+
version: 1;
|
|
29
|
+
goalId: string;
|
|
30
|
+
totalTurns: number;
|
|
31
|
+
userTurns: number;
|
|
32
|
+
autoTurns: number;
|
|
33
|
+
consecutiveAutoTurns: number;
|
|
34
|
+
consecutiveNoProgressTurns: number;
|
|
35
|
+
lastTurnOrigin?: TurnOrigin;
|
|
36
|
+
lastContinuationReason?: ContinuationReason;
|
|
37
|
+
lastSkipReason?: ContinuationSkipReason;
|
|
38
|
+
lastTurnToolCallCount?: number;
|
|
39
|
+
lastTurnToolResultCount?: number;
|
|
40
|
+
lastTurnCompletedGoal?: boolean;
|
|
41
|
+
budgetWrapUpSent?: boolean;
|
|
42
|
+
lastProgressAt?: number;
|
|
43
|
+
lastSafetyPauseReason?: SafetyPauseReason;
|
|
44
|
+
lastBudgetLimitReason?: BudgetLimitReason;
|
|
45
|
+
lastBudgetWarningReason?: BudgetWarningReason;
|
|
46
|
+
lastBudgetHardStopReason?: BudgetHardStopReason;
|
|
47
|
+
tokenBudgetWarningSent?: boolean;
|
|
48
|
+
timeBudgetWarningSent?: boolean;
|
|
49
|
+
updatedAt: number;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type PiGoalEventKind = "set" | "update" | "account" | "telemetry" | "clear";
|
|
53
|
+
export type PiGoalEventReason =
|
|
54
|
+
| "command"
|
|
55
|
+
| "tool"
|
|
56
|
+
| "turn"
|
|
57
|
+
| "budget"
|
|
58
|
+
| "abort"
|
|
59
|
+
| "resume"
|
|
60
|
+
| "reload"
|
|
61
|
+
| "continuation"
|
|
62
|
+
| "safety";
|
|
63
|
+
|
|
64
|
+
export type PiGoalStateEvent = {
|
|
65
|
+
version: 1;
|
|
66
|
+
kind: PiGoalEventKind;
|
|
67
|
+
goalId?: string;
|
|
68
|
+
goal: GoalState | null;
|
|
69
|
+
telemetry?: GoalTelemetrySnapshot | null;
|
|
70
|
+
delta?: { timeUsedSeconds?: number; tokensUsed?: number };
|
|
71
|
+
reason: PiGoalEventReason;
|
|
72
|
+
at: number;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type GoalRuntimeState = {
|
|
76
|
+
goal: GoalState | null;
|
|
77
|
+
telemetry: GoalTelemetrySnapshot | null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type TurnAccountingSnapshot = {
|
|
81
|
+
goalId: string;
|
|
82
|
+
startedAt: number;
|
|
83
|
+
origin: TurnOrigin;
|
|
84
|
+
toolCallCount: number;
|
|
85
|
+
toolResultCount: number;
|
|
86
|
+
progressCount: number;
|
|
87
|
+
completedGoal: boolean;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export type GoalSteeringKind = "continuation" | "budgetLimit" | "pause" | "monitorSteer";
|
|
91
|
+
|
|
92
|
+
export type GoalSteeringDetails = {
|
|
93
|
+
goalId: string;
|
|
94
|
+
kind: GoalSteeringKind;
|
|
95
|
+
promptId: string;
|
|
96
|
+
createdAt: number;
|
|
97
|
+
reason?: ContinuationReason | "budget" | "pause";
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type StreamBudgetSignal = "hardStop" | "reached" | "warning";
|
|
101
|
+
|
|
102
|
+
export type GoalCommandScheduler = (ctx: ExtensionContext, reason: ContinuationReason) => void;
|
|
103
|
+
export type GoalContinuationCanceller = (goalId?: string, reason?: string) => void;
|
|
104
|
+
export type GoalPauseInterrupter = (ctx: ExtensionContext, goal: GoalState) => void;
|
|
105
|
+
export type GoalMonitorScheduler = (ctx: ExtensionContext) => void;
|
|
106
|
+
export type GoalMonitorCanceller = (goalId?: string, reason?: string) => void;
|
|
107
|
+
|
|
108
|
+
export type GoalMonitorAction = "watch" | "steer" | "escalate";
|
|
109
|
+
export type GoalMonitorConfidence = "low" | "medium" | "high";
|
|
110
|
+
|
|
111
|
+
export type GoalMonitorDecision = {
|
|
112
|
+
action: GoalMonitorAction;
|
|
113
|
+
confidence: GoalMonitorConfidence;
|
|
114
|
+
pattern?: string;
|
|
115
|
+
evidence: string[];
|
|
116
|
+
steer?: string;
|
|
117
|
+
logNote: string;
|
|
118
|
+
parseWarnings?: string[];
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type GoalMonitorLogEntry = {
|
|
122
|
+
version: 1;
|
|
123
|
+
goalId: string;
|
|
124
|
+
reportId: string;
|
|
125
|
+
decisionId: string;
|
|
126
|
+
at: number;
|
|
127
|
+
action: GoalMonitorAction;
|
|
128
|
+
confidence: GoalMonitorConfidence;
|
|
129
|
+
pattern?: string;
|
|
130
|
+
evidenceSummary: string;
|
|
131
|
+
steerInjected: boolean;
|
|
132
|
+
logNote: string;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type GoalMonitorRecentEntry = {
|
|
136
|
+
index: number;
|
|
137
|
+
type: string;
|
|
138
|
+
role?: string;
|
|
139
|
+
timestamp?: string | number;
|
|
140
|
+
toolName?: string;
|
|
141
|
+
isError?: boolean;
|
|
142
|
+
summary: string;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export type GoalMonitorReport = {
|
|
146
|
+
version: 1;
|
|
147
|
+
reportId: string;
|
|
148
|
+
goalId: string;
|
|
149
|
+
sentAt: number;
|
|
150
|
+
elapsedSinceGoalStartSeconds: number;
|
|
151
|
+
elapsedSincePreviousReportSeconds?: number;
|
|
152
|
+
goal: GoalState;
|
|
153
|
+
telemetry: GoalTelemetrySnapshot | null;
|
|
154
|
+
session: {
|
|
155
|
+
cwd: string;
|
|
156
|
+
sessionId?: string;
|
|
157
|
+
branchEntryCount: number;
|
|
158
|
+
};
|
|
159
|
+
recentEntries: GoalMonitorRecentEntry[];
|
|
160
|
+
recentLogEntries: GoalMonitorLogEntry[];
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export type MutationResult = {
|
|
164
|
+
ok: boolean;
|
|
165
|
+
goal: GoalState | null;
|
|
166
|
+
telemetry: GoalTelemetrySnapshot | null;
|
|
167
|
+
message?: string;
|
|
168
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { GOAL_USAGE, GOAL_USAGE_HINT, STATUS_UI_KEY, WIDGET_UI_KEY } from "./constants";
|
|
3
|
+
import { footerStatusText, goalSummaryLines, goalUsageSummary, statusLabel } from "./format";
|
|
4
|
+
import type { GoalState } from "./types";
|
|
5
|
+
import { goalWidgetFactory } from "./widget";
|
|
6
|
+
|
|
7
|
+
export function syncGoalUi(ctx: ExtensionContext, goal: GoalState | null): void {
|
|
8
|
+
if (!goal) {
|
|
9
|
+
ctx.ui.setStatus(STATUS_UI_KEY, undefined);
|
|
10
|
+
ctx.ui.setWidget(WIDGET_UI_KEY, undefined);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
ctx.ui.setStatus(STATUS_UI_KEY, footerStatusText(goal));
|
|
14
|
+
ctx.ui.setWidget(WIDGET_UI_KEY, goalWidgetFactory(goal), { placement: "aboveEditor" });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function showNoGoal(ctx: ExtensionContext): void {
|
|
18
|
+
ctx.ui.notify(`${GOAL_USAGE}\n${GOAL_USAGE_HINT}`, "info");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function showGoalSummary(ctx: ExtensionContext, goal: GoalState): void {
|
|
22
|
+
ctx.ui.notify(goalSummaryLines(goal).join("\n"), "info");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function notifyGoal(ctx: ExtensionContext, goal: GoalState, prefix = "Goal"): void {
|
|
26
|
+
ctx.ui.notify(`${prefix} ${statusLabel(goal.status)}\n${goalUsageSummary(goal)}`, "info");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function notifyInfo(ctx: ExtensionContext, message: string): void {
|
|
30
|
+
ctx.ui.notify(message, "info");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function notifyWarning(ctx: ExtensionContext, message: string): void {
|
|
34
|
+
ctx.ui.notify(message, "warning");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function promptResumePausedGoal(ctx: ExtensionContext, goal: GoalState): Promise<boolean> {
|
|
38
|
+
const choice = await ctx.ui.select("Resume paused goal?", ["Resume goal", "Leave paused"]);
|
|
39
|
+
return choice === "Resume goal";
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { commandHint, formatElapsed, formatTokensCompact, goalStatusLabel, objectiveExcerpt } from "./format";
|
|
3
|
+
import type { GoalState, GoalStatus } from "./types";
|
|
4
|
+
|
|
5
|
+
type ThemeColor = "accent" | "success" | "warning" | "error" | "muted" | "dim" | "text" | "border" | "borderAccent";
|
|
6
|
+
|
|
7
|
+
type GoalWidgetTheme = {
|
|
8
|
+
fg(color: ThemeColor, text: string): string;
|
|
9
|
+
bold(text: string): string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type GoalWidgetComponent = {
|
|
13
|
+
render(width: number): string[];
|
|
14
|
+
invalidate(): void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type StatusStyle = {
|
|
18
|
+
icon: string;
|
|
19
|
+
label: string;
|
|
20
|
+
color: ThemeColor;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type ResourceSpec = {
|
|
24
|
+
icon: string;
|
|
25
|
+
label: string;
|
|
26
|
+
used: number;
|
|
27
|
+
budget?: number;
|
|
28
|
+
format(value: number): string;
|
|
29
|
+
suffix?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const BAR_WIDTH = 10;
|
|
33
|
+
const MIN_CARD_WIDTH = 28;
|
|
34
|
+
const MAX_CARD_WIDTH = 72;
|
|
35
|
+
|
|
36
|
+
export function goalWidgetFactory(goal: GoalState): (_tui: unknown, theme: GoalWidgetTheme) => GoalWidgetComponent {
|
|
37
|
+
return (_tui, theme) => new GoalWidget(goal, theme);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function renderGoalWidget(goal: GoalState, theme: GoalWidgetTheme, width: number): string[] {
|
|
41
|
+
if (width < MIN_CARD_WIDTH) return compactLines(goal, theme, width);
|
|
42
|
+
const cardWidth = Math.min(MAX_CARD_WIDTH, Math.max(MIN_CARD_WIDTH, width));
|
|
43
|
+
const contentWidth = cardWidth - 4;
|
|
44
|
+
const style = statusStyle(goal);
|
|
45
|
+
const lines = [
|
|
46
|
+
topBorder(cardWidth, style, theme),
|
|
47
|
+
contentLine(theme.fg("dim", objectiveExcerpt(goal.objective, contentWidth)), contentWidth, theme),
|
|
48
|
+
contentLine(resourceLine(timeResource(goal), theme), contentWidth, theme),
|
|
49
|
+
contentLine(resourceLine(tokenResource(goal), theme), contentWidth, theme),
|
|
50
|
+
contentLine(commandLine(goal.status, theme), contentWidth, theme),
|
|
51
|
+
bottomBorder(cardWidth, theme),
|
|
52
|
+
];
|
|
53
|
+
return lines.map((line) => truncateToWidth(line, cardWidth));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class GoalWidget implements GoalWidgetComponent {
|
|
57
|
+
constructor(private readonly goal: GoalState, private readonly theme: GoalWidgetTheme) {}
|
|
58
|
+
|
|
59
|
+
render(width: number): string[] {
|
|
60
|
+
return renderGoalWidget(this.goal, this.theme, width);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
invalidate(): void {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function compactLines(goal: GoalState, theme: GoalWidgetTheme, width: number): string[] {
|
|
67
|
+
const style = statusStyle(goal);
|
|
68
|
+
const status = theme.fg(style.color, `${style.icon} ${style.label}`);
|
|
69
|
+
return [
|
|
70
|
+
truncateToWidth(`${status} ${objectiveExcerpt(goal.objective, Math.max(6, width - 10))}`, width),
|
|
71
|
+
truncateToWidth(`${timeValue(goal)} ${tokenValue(goal)}`, width),
|
|
72
|
+
truncateToWidth(commandHint(goal.status).replace(/^Commands: /, ""), width),
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function topBorder(width: number, style: StatusStyle, theme: GoalWidgetTheme): string {
|
|
77
|
+
const title = theme.bold(theme.fg(style.color, `${style.icon} pi-goal`));
|
|
78
|
+
const badge = theme.fg(style.color, style.label);
|
|
79
|
+
const left = `╭─ ${title} `;
|
|
80
|
+
const right = ` ${badge} ─╮`;
|
|
81
|
+
const fill = "─".repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(right)));
|
|
82
|
+
return `${left}${theme.fg("border", fill)}${right}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function bottomBorder(width: number, theme: GoalWidgetTheme): string {
|
|
86
|
+
return theme.fg("border", `╰${"─".repeat(Math.max(0, width - 2))}╯`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function contentLine(content: string, width: number, theme: GoalWidgetTheme): string {
|
|
90
|
+
const clipped = padAnsi(truncateToWidth(content, width, "…"), width);
|
|
91
|
+
return `${theme.fg("border", "│")} ${clipped} ${theme.fg("border", "│")}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function commandLine(status: GoalStatus, theme: GoalWidgetTheme): string {
|
|
95
|
+
const commands = commandHint(status).replace(/^Commands: /, "").split(", ");
|
|
96
|
+
return `${theme.fg("muted", "next")} ${commands.map((cmd) => theme.fg("accent", cmd)).join(" ")}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function resourceLine(spec: ResourceSpec, theme: GoalWidgetTheme): string {
|
|
100
|
+
if (spec.budget === undefined) return `${spec.icon} ${theme.fg("muted", spec.label)} ${resourceValue(spec.used, spec.format, spec.suffix)}`;
|
|
101
|
+
const percent = percentage(spec.used, spec.budget);
|
|
102
|
+
return [
|
|
103
|
+
`${spec.icon} ${theme.fg("muted", spec.label.padEnd(6))}`,
|
|
104
|
+
progressBar(percent, theme),
|
|
105
|
+
`${spec.format(spec.used)} / ${spec.format(spec.budget)}`,
|
|
106
|
+
`${percent}%`,
|
|
107
|
+
].join(" ");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function progressBar(percent: number, theme: GoalWidgetTheme): string {
|
|
111
|
+
const filled = Math.round((clamp(percent, 0, 100) / 100) * BAR_WIDTH);
|
|
112
|
+
return `${theme.fg("success", "█".repeat(filled))}${theme.fg("dim", "░".repeat(BAR_WIDTH - filled))}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function timeResource(goal: GoalState): ResourceSpec {
|
|
116
|
+
return { icon: "⏱", label: "Time", used: goal.timeUsedSeconds, budget: goal.timeBudgetSeconds, format: formatElapsed };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function tokenResource(goal: GoalState): ResourceSpec {
|
|
120
|
+
return { icon: "◈", label: "Tokens", used: goal.tokensUsed, budget: goal.tokenBudget, format: formatTokensCompact, suffix: "tokens" };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function timeValue(goal: GoalState): string {
|
|
124
|
+
return goal.timeBudgetSeconds === undefined ? `⏱ ${formatElapsed(goal.timeUsedSeconds)}` : `⏱ ${formatElapsed(goal.timeUsedSeconds)} / ${formatElapsed(goal.timeBudgetSeconds)}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function tokenValue(goal: GoalState): string {
|
|
128
|
+
return goal.tokenBudget === undefined ? `◈ ${formatTokensCompact(goal.tokensUsed)} tokens` : `◈ ${formatTokensCompact(goal.tokensUsed)} / ${formatTokensCompact(goal.tokenBudget)}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function resourceValue(value: number, format: (value: number) => string, suffix?: string): string {
|
|
132
|
+
const formatted = format(value);
|
|
133
|
+
return suffix ? `${formatted} ${suffix}` : formatted;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function percentage(used: number, budget: number): number {
|
|
137
|
+
return clamp(Math.round((Math.max(0, used) / Math.max(1, budget)) * 100), 0, 100);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function clamp(value: number, min: number, max: number): number {
|
|
141
|
+
return Math.min(max, Math.max(min, value));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function padAnsi(text: string, width: number): string {
|
|
145
|
+
return `${text}${" ".repeat(Math.max(0, width - visibleWidth(text)))}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function statusStyle(goal: GoalState): StatusStyle {
|
|
149
|
+
switch (goal.status) {
|
|
150
|
+
case "active":
|
|
151
|
+
return { icon: "🎯", label: "active", color: "accent" };
|
|
152
|
+
case "paused":
|
|
153
|
+
return { icon: "⏸", label: "paused", color: "warning" };
|
|
154
|
+
case "budgetLimited":
|
|
155
|
+
return { icon: "⚠", label: goalStatusLabel(goal), color: "warning" };
|
|
156
|
+
case "complete":
|
|
157
|
+
return { icon: "✓", label: "complete", color: "success" };
|
|
158
|
+
}
|
|
159
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bryan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# pi-goals
|
|
2
|
+
|
|
3
|
+
Persistent goal tracking for [Pi](https://www.npmjs.com/package/@earendil-works/pi-coding-agent), inspired by Codex CLI's `/goal` workflow.
|
|
4
|
+
|
|
5
|
+
> Early preview: this package is being prepared for open-source release. APIs and install ergonomics may change before `1.0.0`.
|
|
6
|
+
|
|
7
|
+
## What it provides
|
|
8
|
+
|
|
9
|
+
- `/goal` command for creating, pausing, resuming, replacing, and clearing a persistent objective.
|
|
10
|
+
- Goal state persisted into the Pi session branch.
|
|
11
|
+
- Goal-aware runtime continuation and monitoring hooks.
|
|
12
|
+
- Model tools for inspecting and updating the active goal.
|
|
13
|
+
- Compact Pi UI status/widget integration.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install pi-goals
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Use with Pi
|
|
22
|
+
|
|
23
|
+
The extension entrypoint is published as TypeScript source at:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
.pi/extensions/goal/index.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Until a packaged loader is finalized, copy or reference the extension directory from `node_modules/pi-goals/.pi/extensions/goal` in your Pi setup.
|
|
30
|
+
|
|
31
|
+
For local validation:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm run quality:goal
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Status
|
|
38
|
+
|
|
39
|
+
This is an early release intended to claim the public package name and make the current implementation available for collaborators and early testers.
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-goals",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Persistent goal tracking extension for Pi, inspired by Codex CLI /goal.",
|
|
5
|
+
"main": ".pi/extensions/goal/index.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
".pi/extensions/goal/*.ts",
|
|
8
|
+
"README.md",
|
|
9
|
+
"LICENSE"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"typecheck:goal": "tsc --noEmit --target ES2022 --module ESNext --moduleResolution node --ignoreDeprecations 6.0 --types node --strict --skipLibCheck .pi/extensions/goal/*.ts",
|
|
13
|
+
"slop:goal": "if rg -n 'as unknown as|as any' .pi/extensions/goal; then exit 1; else exit 0; fi",
|
|
14
|
+
"quality:goal": "sentrux gate .pi/extensions/goal && sentrux check .pi/extensions/goal && npm run slop:goal && npm run typecheck:goal && pi --offline --no-session --no-tools -e .pi/extensions/goal/index.ts --list-models >/tmp/pi-goal-quality-load.txt"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/transcendr/pi-goals.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"pi",
|
|
22
|
+
"pi-extension",
|
|
23
|
+
"agent",
|
|
24
|
+
"goal",
|
|
25
|
+
"coding-agent"
|
|
26
|
+
],
|
|
27
|
+
"author": "Bryan Price-McConnahea (@slopwareindy // withbryan.work)",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"type": "commonjs",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/transcendr/pi-goals/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/transcendr/pi-goals#readme",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@earendil-works/pi-coding-agent": "^0.74.0",
|
|
39
|
+
"@earendil-works/pi-tui": "^0.74.0",
|
|
40
|
+
"@types/node": "^25.6.2",
|
|
41
|
+
"typebox": "^1.1.38",
|
|
42
|
+
"typescript": "^6.0.3"
|
|
43
|
+
}
|
|
44
|
+
}
|