@yagni-app/code-staging 0.2.1-staging.1055.1 → 0.3.0-staging.1061.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
* — useful reference for what data to replicate and how to format it.
|
|
40
40
|
*/
|
|
41
41
|
import type { ExtensionContext, ReadonlyFooterDataProvider, Theme } from "@earendil-works/pi-coding-agent";
|
|
42
|
+
import type { ModeHolder, PermissionMode } from "./permission.js";
|
|
42
43
|
export declare const BRANCH_MAX_WIDTH = 60;
|
|
43
44
|
/**
|
|
44
45
|
* Resolve the status bar's left pad from the launcher's `YAGNI_PAD_X` env so it
|
|
@@ -90,6 +91,8 @@ export declare function detectGitInfo(cwd: string, home: string | undefined): Gi
|
|
|
90
91
|
export declare function renderFooterLines(input: {
|
|
91
92
|
git: GitInfo;
|
|
92
93
|
model: string;
|
|
94
|
+
/** Current permission mode; shown on line 2 to the left of the model as "<mode> mode". */
|
|
95
|
+
mode?: PermissionMode | null;
|
|
93
96
|
usage: UsageTotals;
|
|
94
97
|
contextPercent: number | null;
|
|
95
98
|
statuses: string[];
|
|
@@ -99,7 +102,7 @@ export declare function renderFooterLines(input: {
|
|
|
99
102
|
* and returns the component `setFooter` expects. Called from the
|
|
100
103
|
* `session_start` handler in index.ts.
|
|
101
104
|
*/
|
|
102
|
-
export declare function createYagniFooterFactory(ctx: ExtensionContext): (_tui: unknown, theme: Theme, footerData: ReadonlyFooterDataProvider) => {
|
|
105
|
+
export declare function createYagniFooterFactory(ctx: ExtensionContext, modeHolder?: ModeHolder): (_tui: unknown, theme: Theme, footerData: ReadonlyFooterDataProvider) => {
|
|
103
106
|
render(width: number): string[];
|
|
104
107
|
invalidate(): void;
|
|
105
108
|
dispose(): void;
|
package/dist/extension/footer.js
CHANGED
|
@@ -221,7 +221,7 @@ export function renderFooterLines(input, theme, width, padX = 0) {
|
|
|
221
221
|
line1Parts.push(theme.fg("border", truncateEnd(input.git.branch, BRANCH_MAX_WIDTH)));
|
|
222
222
|
}
|
|
223
223
|
const line1 = pad + truncateToWidth(line1Parts.join(sep), contentWidth, dim("…"));
|
|
224
|
-
// Line 2: model · ↑in ↓out $cost · ctx%
|
|
224
|
+
// Line 2: [mode ·] model · ↑in ↓out $cost · ctx%
|
|
225
225
|
const statParts = [];
|
|
226
226
|
if (input.usage.input)
|
|
227
227
|
statParts.push(`↑${formatTokens(input.usage.input)}`);
|
|
@@ -231,7 +231,10 @@ export function renderFooterLines(input, theme, width, padX = 0) {
|
|
|
231
231
|
statParts.push(`$${input.usage.cost.toFixed(3)}`);
|
|
232
232
|
const stats = statParts.join(" ");
|
|
233
233
|
const percentText = input.contextPercent === null ? "?" : `${Math.round(input.contextPercent)}%`;
|
|
234
|
-
const line2Parts = [
|
|
234
|
+
const line2Parts = [];
|
|
235
|
+
if (input.mode)
|
|
236
|
+
line2Parts.push(dim(`${input.mode} mode`));
|
|
237
|
+
line2Parts.push(dim(input.model));
|
|
235
238
|
if (stats)
|
|
236
239
|
line2Parts.push(dim(stats));
|
|
237
240
|
line2Parts.push(theme.fg(contextColor(input.contextPercent), percentText));
|
|
@@ -249,7 +252,7 @@ export function renderFooterLines(input, theme, width, padX = 0) {
|
|
|
249
252
|
* and returns the component `setFooter` expects. Called from the
|
|
250
253
|
* `session_start` handler in index.ts.
|
|
251
254
|
*/
|
|
252
|
-
export function createYagniFooterFactory(ctx) {
|
|
255
|
+
export function createYagniFooterFactory(ctx, modeHolder) {
|
|
253
256
|
return (_tui, theme, footerData) => {
|
|
254
257
|
// Recompute git/worktree info only when the branch actually changes. Optional-
|
|
255
258
|
// chained: onBranchChange is typed on ReadonlyFooterDataProvider, but the runtime
|
|
@@ -273,6 +276,7 @@ export function createYagniFooterFactory(ctx) {
|
|
|
273
276
|
return renderFooterLines({
|
|
274
277
|
git: gitInfo(),
|
|
275
278
|
model: ctx.model?.id ?? "no-model",
|
|
279
|
+
mode: modeHolder?.get() ?? null,
|
|
276
280
|
usage: collectUsage(ctx.sessionManager),
|
|
277
281
|
contextPercent: ctx.getContextUsage()?.percent ?? null,
|
|
278
282
|
statuses,
|
package/dist/extension/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { isInitDone as defaultIsInitDone, markInitDone as defaultMarkInitDone }
|
|
|
23
23
|
import { fetchMcpServers as defaultFetchMcpServers, registerMcpCommand, registerMcpTools, } from "./mcpTools.js";
|
|
24
24
|
import { registerGoCommand } from "./pipeline/goCommand.js";
|
|
25
25
|
import { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
26
|
-
import { DEFAULT_PERMISSION_POLICY, registerPermissionGate } from "./permission.js";
|
|
26
|
+
import { DEFAULT_PERMISSION_POLICY, createModeHolder, registerPermissionGate } from "./permission.js";
|
|
27
27
|
import { registerSubagents } from "./subagents.js";
|
|
28
28
|
import { registerTodos } from "./todos.js";
|
|
29
29
|
import { registerDecisionCommands } from "./decisions.js";
|
|
@@ -220,7 +220,9 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
220
220
|
// drafts a decision on "don't ask again". Default auto, so still additive.
|
|
221
221
|
// Mutating MCP tools join write/edit/bash in the gate policy: plan mode
|
|
222
222
|
// holds them, review mode confirms them.
|
|
223
|
+
const modeHolder = createModeHolder();
|
|
223
224
|
registerPermissionGate(pi, {
|
|
225
|
+
modeHolder,
|
|
224
226
|
...(mcpMutatingTools.length > 0
|
|
225
227
|
? {
|
|
226
228
|
policy: {
|
|
@@ -504,7 +506,7 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
504
506
|
// context % on line 2, and extension statuses (brand, todos, mode) on
|
|
505
507
|
// line 3. The factory captures ctx so the footer can read session data
|
|
506
508
|
// (token stats, context usage) that isn't on the footerData provider.
|
|
507
|
-
ctx.ui?.setFooter?.((tui, theme, footerData) => createYagniFooterFactory(ctx)(tui, theme, footerData));
|
|
509
|
+
ctx.ui?.setFooter?.((tui, theme, footerData) => createYagniFooterFactory(ctx, modeHolder)(tui, theme, footerData));
|
|
508
510
|
}
|
|
509
511
|
// Best-effort, once at session start: if the token is at or near expiry, say
|
|
510
512
|
// so via a single notice so a long session does not silently start 401-ing
|
|
@@ -29,6 +29,17 @@
|
|
|
29
29
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
30
30
|
import { type BlessStore } from "./bless.js";
|
|
31
31
|
export type PermissionMode = "auto" | "plan" | "review";
|
|
32
|
+
/**
|
|
33
|
+
* A shared, mutable holder for the current permission mode. Both
|
|
34
|
+
* `registerPermissionGate` (which writes on /mode) and the footer factory
|
|
35
|
+
* (which reads on every render) hold a reference, so mode changes appear
|
|
36
|
+
* immediately in the footer's line 2.
|
|
37
|
+
*/
|
|
38
|
+
export interface ModeHolder {
|
|
39
|
+
get(): PermissionMode;
|
|
40
|
+
set(m: PermissionMode): void;
|
|
41
|
+
}
|
|
42
|
+
export declare function createModeHolder(initial?: PermissionMode): ModeHolder;
|
|
32
43
|
/** Which tools each tier acts on, plus the optional grounding-bless predicate. */
|
|
33
44
|
export interface PermissionPolicy {
|
|
34
45
|
/** Tools blocked outright in plan mode (write/exec). */
|
|
@@ -76,6 +87,8 @@ export interface RegisterPermissionDeps {
|
|
|
76
87
|
* never blocks the approved tool call.
|
|
77
88
|
*/
|
|
78
89
|
onBlessRemember?: (ctx: ExtensionContext, info: BlessRememberInfo) => void | Promise<void>;
|
|
90
|
+
/** Shared holder so the footer can read the live mode on every render. */
|
|
91
|
+
modeHolder?: ModeHolder;
|
|
79
92
|
}
|
|
80
93
|
/** The customType tag on injected plan-mode context (filterable later). */
|
|
81
94
|
export declare const PLAN_CONTEXT_TYPE = "yagni-plan-context";
|
|
@@ -27,6 +27,13 @@
|
|
|
27
27
|
* the context so the model doesn't keep believing it is restricted.
|
|
28
28
|
*/
|
|
29
29
|
import { makeBlessStore as defaultMakeBlessStore } from "./bless.js";
|
|
30
|
+
export function createModeHolder(initial = "auto") {
|
|
31
|
+
let current = initial;
|
|
32
|
+
return {
|
|
33
|
+
get: () => current,
|
|
34
|
+
set: (m) => { current = m; },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
30
37
|
export const DEFAULT_PERMISSION_POLICY = {
|
|
31
38
|
planBlockTools: ["write", "edit", "bash", "file_ticket", "update_ticket_status"],
|
|
32
39
|
reviewConfirmTools: ["write", "edit", "bash", "file_ticket", "update_ticket_status"],
|
|
@@ -232,6 +239,7 @@ export function registerPermissionGate(pi, deps = {}) {
|
|
|
232
239
|
return { messages: filtered };
|
|
233
240
|
});
|
|
234
241
|
const paintMode = (ctx) => {
|
|
242
|
+
deps.modeHolder?.set(mode);
|
|
235
243
|
try {
|
|
236
244
|
if (ctx.hasUI)
|
|
237
245
|
ctx.ui.setStatus?.("yagni-mode", MODE_STATUS[mode]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-staging.1061.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@earendil-works/pi-tui": "0.84.1",
|
|
39
39
|
"typebox": "^1.3.11"
|
|
40
40
|
},
|
|
41
|
-
"yagniSourceSha": "
|
|
41
|
+
"yagniSourceSha": "44711af698e076c2e718fdd813f4e8ab4b875a0a"
|
|
42
42
|
}
|