@yagni-app/code 1.0.6 → 1.0.7
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 +84 -46
- package/dist/claudeCompat.d.ts +59 -0
- package/dist/claudeCompat.js +109 -2
- package/dist/claudePlugins.d.ts +45 -5
- package/dist/claudePlugins.js +129 -21
- package/dist/cli.js +16 -4
- package/dist/doctor.d.ts +21 -8
- package/dist/doctor.js +53 -28
- package/dist/extension/condensedTools.d.ts +12 -1
- package/dist/extension/condensedTools.js +17 -9
- package/dist/extension/index.d.ts +13 -0
- package/dist/extension/index.js +230 -43
- package/dist/extension/mcp/cliConfig.d.ts +1 -1
- package/dist/extension/mcp/cliConfig.js +1 -1
- package/dist/extension/mcp/config.d.ts +24 -2
- package/dist/extension/mcp/config.js +75 -3
- package/dist/extension/mcp/manager.d.ts +3 -1
- package/dist/extension/mcp/manager.js +2 -2
- package/dist/extension/mcp/panel.d.ts +0 -1
- package/dist/extension/mcp/panel.js +13 -3
- package/dist/extension/mcp/startup.js +8 -6
- package/dist/extension/permission/gate.d.ts +97 -2
- package/dist/extension/permission/gate.js +375 -26
- package/dist/extension/permissionRules/bashFileArgs.d.ts +39 -0
- package/dist/extension/permissionRules/bashFileArgs.js +236 -0
- package/dist/extension/permissionRules/engine.d.ts +50 -0
- package/dist/extension/permissionRules/engine.js +238 -0
- package/dist/extension/permissionRules/loadConfig.d.ts +53 -0
- package/dist/extension/permissionRules/loadConfig.js +90 -0
- package/dist/extension/permissionRules/parser.d.ts +38 -0
- package/dist/extension/permissionRules/parser.js +136 -0
- package/dist/extension/permissionRules/pathRules.d.ts +58 -0
- package/dist/extension/permissionRules/pathRules.js +120 -0
- package/dist/extension/permissionRules/shellRules.d.ts +52 -0
- package/dist/extension/permissionRules/shellRules.js +221 -0
- package/dist/extension/pipeline/invocation.d.ts +3 -6
- package/dist/extension/pipeline/invocation.js +3 -6
- package/dist/extension/pipeline/runner.d.ts +0 -1
- package/dist/extension/pipeline/runner.js +6 -14
- package/dist/extension/plugins/inventory.d.ts +88 -0
- package/dist/extension/plugins/inventory.js +144 -0
- package/dist/extension/plugins/panel.d.ts +45 -0
- package/dist/extension/plugins/panel.js +293 -0
- package/dist/extension/sandbox/bash.d.ts +99 -0
- package/dist/extension/sandbox/bash.js +190 -0
- package/dist/extension/sandbox/config.d.ts +114 -0
- package/dist/extension/sandbox/config.js +366 -0
- package/dist/extension/sandbox/manager.d.ts +98 -0
- package/dist/extension/sandbox/manager.js +216 -0
- package/dist/extension/sandbox/panel.d.ts +111 -0
- package/dist/extension/sandbox/panel.js +342 -0
- package/dist/extension/sandbox/session.d.ts +85 -0
- package/dist/extension/sandbox/session.js +775 -0
- package/dist/extension/telemetry/attrs.d.ts +96 -0
- package/dist/extension/telemetry/attrs.js +149 -0
- package/dist/extension/telemetry/config.d.ts +99 -0
- package/dist/extension/telemetry/config.js +193 -0
- package/dist/extension/telemetry/index.d.ts +7 -0
- package/dist/extension/telemetry/index.js +7 -0
- package/dist/extension/telemetry/probe.d.ts +29 -0
- package/dist/extension/telemetry/probe.js +122 -0
- package/dist/extension/telemetry/register.d.ts +40 -0
- package/dist/extension/telemetry/register.js +192 -0
- package/dist/extension/telemetry/sdk.d.ts +63 -0
- package/dist/extension/telemetry/sdk.js +207 -0
- package/dist/extension/telemetry/tracker.d.ts +131 -0
- package/dist/extension/telemetry/tracker.js +551 -0
- package/dist/extension/vendor/IGNORE-LICENSE-MIT +21 -0
- package/dist/extension/vendor/ignore.d.ts +86 -0
- package/dist/extension/vendor/ignore.js +788 -0
- package/dist/goHeadless.d.ts +1 -1
- package/dist/goHeadless.js +2 -2
- package/dist/launch.d.ts +4 -3
- package/dist/launch.js +7 -4
- package/dist/mcpCommand.d.ts +10 -1
- package/dist/mcpCommand.js +42 -10
- package/dist/otel.d.ts +67 -90
- package/dist/otel.js +152 -195
- package/dist/paths.d.ts +13 -0
- package/dist/paths.js +18 -0
- package/dist/pluginCommand.d.ts +43 -0
- package/dist/pluginCommand.js +499 -0
- package/dist/pluginStore.d.ts +170 -0
- package/dist/pluginStore.js +554 -0
- package/package.json +19 -3
|
@@ -53,32 +53,34 @@ export async function startMcp(pi, opts) {
|
|
|
53
53
|
registerMcpPanel(pi, outcome.manager, () => outcome.configErrors);
|
|
54
54
|
if (servers.length === 0 && errors.length === 0)
|
|
55
55
|
return outcome;
|
|
56
|
-
// Approval gate for project-scope servers
|
|
56
|
+
// Approval gate for project-scope servers and repo-sourced plugin servers
|
|
57
|
+
// (fail-closed: undecided = skip).
|
|
57
58
|
const { state } = readProjectApproval(repoRoot);
|
|
58
|
-
const
|
|
59
|
+
const needsApproval = (s) => s.scope === "project" || s.gated === true;
|
|
60
|
+
const projectNames = servers.filter(needsApproval).map((s) => s.name);
|
|
59
61
|
const undecided = undecidedProjectServers(state, projectNames);
|
|
60
62
|
outcome.skippedApproval = undecided;
|
|
61
63
|
const gate = (server) => {
|
|
62
|
-
if (server
|
|
64
|
+
if (!needsApproval(server))
|
|
63
65
|
return true;
|
|
64
66
|
return !undecided.includes(server.name);
|
|
65
67
|
};
|
|
66
68
|
// Ask once, interactively, about undecided project servers (CC parity).
|
|
67
69
|
if (undecided.length > 0 && opts.hasUI && opts.notify) {
|
|
68
|
-
opts.notify(
|
|
70
|
+
opts.notify(`This repo's config (.mcp.json or a repo plugin) defines MCP server(s) ${undecided.join(", ")} — not yet approved. ` +
|
|
69
71
|
`Run /mcp to approve them individually, or \`yagni mcp reset-project-choices\` to clear old choices.`);
|
|
70
72
|
}
|
|
71
73
|
const connectable = [];
|
|
72
74
|
for (const server of servers) {
|
|
73
75
|
if (!gate(server)) {
|
|
74
|
-
outcome.manager.register(server.name, server.scope, server.config, "disabled");
|
|
76
|
+
outcome.manager.register(server.name, server.scope, server.config, "disabled", server.gated === true);
|
|
75
77
|
continue;
|
|
76
78
|
}
|
|
77
79
|
connectable.push(server);
|
|
78
80
|
}
|
|
79
81
|
// Connect in parallel; each server fails soft.
|
|
80
82
|
const results = await Promise.all(connectable.map(async (server) => {
|
|
81
|
-
const managed = outcome.manager.register(server.name, server.scope, server.config, "connecting");
|
|
83
|
+
const managed = outcome.manager.register(server.name, server.scope, server.config, "connecting", server.gated === true);
|
|
82
84
|
const connected = await outcome.manager.connect(server.name);
|
|
83
85
|
if (connected?.status === "connected") {
|
|
84
86
|
const tools = await registerServerTools(pi, outcome.manager, server.name, env);
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* When the mode leaves plan, stale plan-context messages are filtered out of
|
|
27
27
|
* the context so the model doesn't keep believing it is restricted.
|
|
28
28
|
*/
|
|
29
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
29
|
+
import type { ExtensionAPI, ExtensionContext, ToolCallEvent, ToolCallEventResult } from "@earendil-works/pi-coding-agent";
|
|
30
30
|
import { type ApprovedPrefixGrant } from "./approvedPrefixes.js";
|
|
31
31
|
import type { HookRunner } from "../hooks.js";
|
|
32
32
|
import { type BlessStore } from "../bless.js";
|
|
@@ -94,6 +94,33 @@ export interface GateDecision {
|
|
|
94
94
|
*/
|
|
95
95
|
classifyJustification?: string;
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* What the hard floors say about an ALLOW-rule verdict — the three invariants
|
|
99
|
+
* that outrank any user/project permission rule. Extracted from the inline
|
|
100
|
+
* allow path so the gate reads linearly and the floors are directly testable.
|
|
101
|
+
* PURE — no I/O, no session state:
|
|
102
|
+
*
|
|
103
|
+
* "allow" no floor applies — the rule's allow short-circuits everything
|
|
104
|
+
* "block" the exec-policy forbidden band holds despite the allow rule
|
|
105
|
+
* "confirm" alwaysConfirmTools keeps its fresh-consent contract
|
|
106
|
+
* "hold" plan mode's no-mutation contract outranks the allow rule —
|
|
107
|
+
* fall through to the normal plan-mode gate
|
|
108
|
+
*
|
|
109
|
+
* Order is deliberate: plan-mode first (its outcome routes to the EXISTING
|
|
110
|
+
* plan-mode machinery, not a bespoke block); then the terminal forbidden-band
|
|
111
|
+
* block; then the confirm deferral.
|
|
112
|
+
*/
|
|
113
|
+
export type AllowRuleFloorOutcome = {
|
|
114
|
+
kind: "allow";
|
|
115
|
+
} | {
|
|
116
|
+
kind: "block";
|
|
117
|
+
reason: string;
|
|
118
|
+
} | {
|
|
119
|
+
kind: "confirm";
|
|
120
|
+
} | {
|
|
121
|
+
kind: "hold";
|
|
122
|
+
};
|
|
123
|
+
export declare function allowRuleFloorVerdict(toolName: string, params: Record<string, unknown>, mode: PermissionMode, policy: PermissionPolicy): AllowRuleFloorOutcome;
|
|
97
124
|
/**
|
|
98
125
|
* Pure permission decision for one tool call under a mode + policy. Auto allows
|
|
99
126
|
* ordinary tools; plan blocks the write/exec set; review marks writes for confirmation
|
|
@@ -105,7 +132,7 @@ export declare function decideGate(toolName: string, params: Record<string, unkn
|
|
|
105
132
|
* is emitted per terminal outcome; `consulted` says whether a Guardian LLM
|
|
106
133
|
* call actually happened (grants/cache hits skip it).
|
|
107
134
|
*/
|
|
108
|
-
export type GuardianGateOutcome = "prefix_allow" | "cached_allow" | "allow" | "deny" | "ask_approved" | "ask_approved_remembered" | "ask_denied" | "ask_headless_blocked" | "breaker_ask_approved" | "breaker_blocked" | GuardianError;
|
|
135
|
+
export type GuardianGateOutcome = "prefix_allow" | "cached_allow" | "sandbox_auto_allow" | "allow" | "deny" | "ask_approved" | "ask_approved_remembered" | "ask_denied" | "ask_headless_blocked" | "breaker_ask_approved" | "breaker_blocked" | GuardianError;
|
|
109
136
|
/**
|
|
110
137
|
* Rich per-decision event for opt-in storage (YAG-510). Carries the RAW
|
|
111
138
|
* command — the wiring layer (index.ts) hashes/redacts per the workspace's
|
|
@@ -129,6 +156,17 @@ export interface GuardianGateEvent {
|
|
|
129
156
|
* error-fallback ask that the user then approved). */
|
|
130
157
|
guardianError?: GuardianError;
|
|
131
158
|
}
|
|
159
|
+
/** Claude Code's decision sources for a tool permission outcome. */
|
|
160
|
+
export type ToolDecisionSource = "config" | "hook" | "user_permanent" | "user_temporary" | "user_abort" | "user_reject";
|
|
161
|
+
/** One terminal tool_call outcome, in Claude Code's telemetry shape. */
|
|
162
|
+
export interface ToolDecisionEvent {
|
|
163
|
+
toolName: string;
|
|
164
|
+
toolCallId?: string;
|
|
165
|
+
decision: "accept" | "reject";
|
|
166
|
+
source: ToolDecisionSource;
|
|
167
|
+
/** For edit/write tools: the target path (language attribution). */
|
|
168
|
+
filePath?: string;
|
|
169
|
+
}
|
|
132
170
|
/** What was blessed with "don't ask again", handed to the capture hook. */
|
|
133
171
|
export interface BlessRememberInfo {
|
|
134
172
|
tool: string;
|
|
@@ -187,12 +225,55 @@ export interface RegisterPermissionDeps {
|
|
|
187
225
|
/** Persist a new grant (fire-and-forget; the in-memory list is updated
|
|
188
226
|
* either way). index.ts wires approvedPrefixes.appendGrant. */
|
|
189
227
|
persistGrant?: (grant: ApprovedPrefixGrant) => void;
|
|
228
|
+
/**
|
|
229
|
+
* persist a user-level permission rule string (e.g.
|
|
230
|
+
* `Bash(git push:*)`) into ~/.yagni-code/config.json permissions.allow.
|
|
231
|
+
* Wired by index.ts; offered as a third option on Guardian ask dialogs.
|
|
232
|
+
* Fail-soft: the in-session approval applies even if the write fails.
|
|
233
|
+
*/
|
|
234
|
+
persistUserRule?: (ruleString: string) => void;
|
|
190
235
|
/**
|
|
191
236
|
* Called (fire-and-forget) at every terminal prompt-band outcome with the
|
|
192
237
|
* rich storage event (raw command — the wiring layer redacts/hashes).
|
|
193
238
|
* Fail-soft; never blocks.
|
|
194
239
|
*/
|
|
195
240
|
onGuardianEvent?: (event: GuardianGateEvent) => void;
|
|
241
|
+
/**
|
|
242
|
+
* Called (fire-and-forget) at EVERY terminal tool_call outcome, for every
|
|
243
|
+
* tool, with the Claude Code-shaped decision (accept/reject + source).
|
|
244
|
+
* Feeds the telemetry `tool_decision` event and the
|
|
245
|
+
* `code_edit_tool.decision` counter. Fail-soft; never blocks.
|
|
246
|
+
*/
|
|
247
|
+
onToolDecision?: (event: ToolDecisionEvent) => void;
|
|
248
|
+
/**
|
|
249
|
+
* settings-based permission rules (user + project config.json).
|
|
250
|
+
* When present, evaluated at the TOP of the tool_call handler, before
|
|
251
|
+
* hooks: deny → ask → allow, first match wins. Deny/ask are final; allow
|
|
252
|
+
* short-circuits the Guardian but can never lift the exec-policy forbidden
|
|
253
|
+
* band or the alwaysConfirmTools contract (both re-checked below).
|
|
254
|
+
*/
|
|
255
|
+
permissionRules?: readonly import("../permissionRules/loadConfig.js").PermissionRule[];
|
|
256
|
+
/** ~/.yagni-code — the user `/`-anchor base for path rules. */
|
|
257
|
+
rulesUserStateHome?: string;
|
|
258
|
+
/** Project root for project-source `/`-anchored path rules; null outside a repo. */
|
|
259
|
+
rulesProjectRoot?: string | null;
|
|
260
|
+
/** Overrides ~ expansion for path rules (tests). */
|
|
261
|
+
rulesHomeDir?: string;
|
|
262
|
+
/** Called (fire-and-soft) after every rule verdict for debug logging. */
|
|
263
|
+
onRuleVerdict?: (event: import("../permissionRules/engine.js").RuleEvaluation & {
|
|
264
|
+
toolName: string;
|
|
265
|
+
cwd: string;
|
|
266
|
+
}) => void;
|
|
267
|
+
/**
|
|
268
|
+
* Sandbox auto-allow (YAGNI Code sandbox): when present and true for this bash call,
|
|
269
|
+
* a prompt-band command runs sandboxed WITHOUT a Guardian consult or user
|
|
270
|
+
* prompt (Claude's autoAllowBashIfSandboxed). Consulted AFTER deny/ask
|
|
271
|
+
* rules, the exec-policy forbidden band, grants, and the session cache —
|
|
272
|
+
* never in plan mode (plan's no-mutation contract outranks it). The
|
|
273
|
+
* caller (session.ts) owns the decision: sandbox initialized + this
|
|
274
|
+
* command will be wrapped + autoAllow enabled.
|
|
275
|
+
*/
|
|
276
|
+
sandboxAutoAllow?: (toolName: string, params: Record<string, unknown>) => boolean;
|
|
196
277
|
/**
|
|
197
278
|
* User-configurable lifecycle hooks (YAG-506). When present, PreToolUse
|
|
198
279
|
* hooks run before decideGate and can short-circuit (allow/deny/ask),
|
|
@@ -217,9 +298,23 @@ export declare function buildModeContextMessage(mode: PermissionMode): string;
|
|
|
217
298
|
export declare function filterStaleModeContext<T>(messages: T[], currentMode?: PermissionMode): T[];
|
|
218
299
|
/** Legacy alias — the original plan-mode filter name. */
|
|
219
300
|
export declare const filterStalePlanContext: typeof filterStaleModeContext;
|
|
301
|
+
/** The decision source of ONE tool_call (telemetry), carried per invocation
|
|
302
|
+
* because tool calls run concurrently: refined by the user dialog, hooks,
|
|
303
|
+
* and Guardian outcomes as the call moves through the gate. */
|
|
304
|
+
interface DecisionSlot {
|
|
305
|
+
source: ToolDecisionSource;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* The pi tool_call handler wiring (crash-honest attribution). Extracted so
|
|
309
|
+
* the telemetry contract is testable against the production path: a thrown
|
|
310
|
+
* gate records reject — never accept — logs a gate_crashed line (metadata
|
|
311
|
+
* only), and rethrows so pi's handling of a crashed handler is unchanged.
|
|
312
|
+
*/
|
|
313
|
+
export declare function wireToolCallGate(pi: Pick<ExtensionAPI, "on">, run: (event: ToolCallEvent, ctx: ExtensionContext, slot: DecisionSlot) => Promise<ToolCallEventResult | undefined>, onToolDecision?: (ev: ToolDecisionEvent) => void): void;
|
|
220
314
|
/**
|
|
221
315
|
* Wire the tool_call gate + the /mode command onto a shared mode holder. Default
|
|
222
316
|
* auto, so absent any /mode this is a no-op over today's behavior.
|
|
223
317
|
*/
|
|
224
318
|
export declare function registerPermissionGate(pi: ExtensionAPI, deps?: RegisterPermissionDeps): void;
|
|
319
|
+
export {};
|
|
225
320
|
//# sourceMappingURL=gate.d.ts.map
|