@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.11
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/CHANGELOG.md +40 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-d9xenpn9.md} +40 -0
- package/dist/cli.js +3025 -3016
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/discovery/agent-plugin-format.d.ts +143 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +34 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/mcp/transports/header-policy.d.ts +45 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/secrets/index.d.ts +23 -1
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +7 -2
- package/dist/types/task/index.d.ts +2 -0
- package/dist/types/tools/shell-tokenize.d.ts +16 -0
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-registry.ts +21 -1
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +46 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +4 -1
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +52 -1
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session.ts +33 -14
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +5 -1
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +94 -72
- package/src/slash-commands/builtin-registry.ts +12 -8
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/read.ts +5 -4
- package/src/tools/shell-tokenize.ts +98 -0
- package/src/web/search/providers/exa.ts +1 -1
|
@@ -68,8 +68,9 @@ export type AssistantErrorPresentation = {
|
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
70
|
* Resolve the turn-ending assistant error presentation, if any.
|
|
71
|
-
* Silent and user-interrupt aborts yield no label. Recovered
|
|
72
|
-
*
|
|
71
|
+
* Silent and user-interrupt aborts yield no label. Recovered retry attempts
|
|
72
|
+
* render a compact note; attempts superseded by an exhausted budget are hidden
|
|
73
|
+
* while the final terminal error keeps its full presentation.
|
|
73
74
|
*/
|
|
74
75
|
export declare function resolveAssistantErrorPresentation(message: AssistantAgentMessage, retryAttempt?: number): AssistantErrorPresentation;
|
|
75
76
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SecretEntry } from "./obfuscator.js";
|
|
1
|
+
import { type SecretEntry, SecretObfuscator } from "./obfuscator.js";
|
|
2
2
|
/**
|
|
3
3
|
* Per-install secret key for the placeholder digest. Persisted under XDG state
|
|
4
4
|
* and never sent to a provider, so model-visible placeholders cannot be reversed
|
|
@@ -40,3 +40,25 @@ export declare function collectEnvSecrets(): SecretEntry[];
|
|
|
40
40
|
* transparent because the round trip is lossless.
|
|
41
41
|
*/
|
|
42
42
|
export declare function builtinCredentialSecretEntries(): SecretEntry[];
|
|
43
|
+
/**
|
|
44
|
+
* Build the session secret obfuscator from every configured source: secrets.yml
|
|
45
|
+
* (project + global), secret-shaped environment variables, and the built-in
|
|
46
|
+
* credential patterns. Callers gate on `secrets.enabled`.
|
|
47
|
+
*
|
|
48
|
+
* Only CONFIGURED entries force startup key creation: a configured
|
|
49
|
+
* obfuscate-mode secret — or a default (no custom `replacement`) replace-mode
|
|
50
|
+
* regex whose key-derived idempotent fallback marker needs a stable key across
|
|
51
|
+
* restarts (see `secretEntryNeedsPlaceholderKey`) — mints placeholders as soon
|
|
52
|
+
* as the obfuscator is built. The built-in credential-pattern entry matches
|
|
53
|
+
* dynamically, so it resolves the persisted key lazily on first match instead
|
|
54
|
+
* of creating the key file for every secrets-enabled session.
|
|
55
|
+
*
|
|
56
|
+
* When no configured entry produced an active secret but a persisted key
|
|
57
|
+
* exists, returns a redaction-only obfuscator so a tool read of the key file
|
|
58
|
+
* does not ship the reusable HMAC key to the provider. Returns undefined when
|
|
59
|
+
* there is nothing to protect.
|
|
60
|
+
*
|
|
61
|
+
* `keyDir` is the explicit agent dir override for the placeholder-key file
|
|
62
|
+
* (default XDG/agent location when omitted).
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildSecretObfuscator(cwd: string, agentDir: string, keyDir?: string): Promise<SecretObfuscator | undefined>;
|
|
@@ -2,7 +2,7 @@ import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
2
2
|
import type { CompactionResult } from "@oh-my-pi/pi-agent-core/compaction";
|
|
3
3
|
import type { Effort } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import type { Rule } from "../capability/rule.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { RetryErrorUpdate } from "../extensibility/shared-events.js";
|
|
6
6
|
import type { Goal, GoalModeState } from "../goals/state.js";
|
|
7
7
|
import type { ConfiguredThinkingLevel } from "../thinking.js";
|
|
8
8
|
import type { TodoItem } from "../tools/todo.js";
|
|
@@ -40,7 +40,7 @@ export type AgentSessionEvent = Exclude<AgentEvent, {
|
|
|
40
40
|
success: boolean;
|
|
41
41
|
attempt: number;
|
|
42
42
|
finalError?: string;
|
|
43
|
-
|
|
43
|
+
retryErrors?: RetryErrorUpdate[];
|
|
44
44
|
} | {
|
|
45
45
|
type: "retry_fallback_applied";
|
|
46
46
|
from: string;
|
|
@@ -83,7 +83,7 @@ export declare class AgentSession {
|
|
|
83
83
|
/**
|
|
84
84
|
* Arm prewalk outside the normal startup path so an explicit slash command starts immediately.
|
|
85
85
|
*/
|
|
86
|
-
armPrewalk(target: Model, thinkingLevel?: ConfiguredThinkingLevel):
|
|
86
|
+
armPrewalk(target: Model, thinkingLevel?: ConfiguredThinkingLevel): boolean;
|
|
87
87
|
/** Validate the active plan artifact and shape an `xd://propose` result for review-mode hosts. */
|
|
88
88
|
preparePlanForReview(title: string): Promise<AgentToolResult<PlanApprovalDetails>>;
|
|
89
89
|
constructor(config: AgentSessionConfig);
|
|
@@ -12,6 +12,7 @@ import type { OutputMeta } from "../tools/output-meta.js";
|
|
|
12
12
|
export declare const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
|
|
13
13
|
export declare const LSP_LATE_DIAGNOSTIC_MESSAGE_TYPE = "lsp-late-diagnostic";
|
|
14
14
|
export declare const BACKGROUND_TAN_DISPATCH_MESSAGE_TYPE = "background-tan-dispatch";
|
|
15
|
+
export declare const PREWALK_PLAN_MESSAGE_TYPE = "prewalk-plan";
|
|
15
16
|
/**
|
|
16
17
|
* Logs provider-error turns so their actual cause is available outside the
|
|
17
18
|
* session transcript. No-op for non-error stop reasons.
|
|
@@ -6,6 +6,8 @@ import { type ConfiguredThinkingLevel } from "../thinking.js";
|
|
|
6
6
|
import type { PlanProposalHandler } from "../tools/resolve.js";
|
|
7
7
|
import type { PlanYolo, Prewalk } from "./agent-session-types.js";
|
|
8
8
|
import type { SessionManager } from "./session-manager.js";
|
|
9
|
+
/** Hidden plan steering is consumed within the live run and must not reappear after a context rebuild. */
|
|
10
|
+
export declare function isPrewalkPlanNudge(message: AgentMessage): boolean;
|
|
9
11
|
/** Capabilities the prewalk coordinator borrows from its owning session. */
|
|
10
12
|
export interface PrewalkCoordinatorHost {
|
|
11
13
|
agent: Agent;
|
|
@@ -41,7 +43,7 @@ export declare class PrewalkCoordinator {
|
|
|
41
43
|
/** Advances the one-way prewalk switch at a completed assistant-turn boundary. */
|
|
42
44
|
advanceAtTurnEnd(liveMessages: AgentMessage[], context: AgentTurnEndContext | undefined): Promise<void>;
|
|
43
45
|
/** Arms a prewalk immediately for an explicit slash-command request. */
|
|
44
|
-
arm(target: Model, thinkingLevel?: ConfiguredThinkingLevel):
|
|
46
|
+
arm(target: Model, thinkingLevel?: ConfiguredThinkingLevel): boolean;
|
|
45
47
|
/** Lazily enables plan-yolo's plan phase before the first prompt is built. */
|
|
46
48
|
armPlanYoloIfNeeded(): Promise<void>;
|
|
47
49
|
}
|
|
@@ -108,6 +108,14 @@ export declare class SessionTools {
|
|
|
108
108
|
get baseSystemPrompt(): string[];
|
|
109
109
|
/** Replaces the controller-owned base prompt without applying it to the agent. */
|
|
110
110
|
setBaseSystemPrompt(prompt: string[]): void;
|
|
111
|
+
/**
|
|
112
|
+
* Registers the per-turn `before_agent_start` system-prompt override and
|
|
113
|
+
* applies it to the agent. Base rebuilds during the turn preserve it until
|
|
114
|
+
* {@link clearTurnSystemPromptOverride}.
|
|
115
|
+
*/
|
|
116
|
+
setTurnSystemPromptOverride(prompt: string[]): void;
|
|
117
|
+
/** Drops the active per-turn override; later rebuilds fall back to the base prompt. */
|
|
118
|
+
clearTurnSystemPromptOverride(): void;
|
|
111
119
|
/** Skills currently rendered into the system prompt. */
|
|
112
120
|
get skills(): Skill[];
|
|
113
121
|
/** Diagnostics produced while loading the current skills. */
|
|
@@ -100,8 +100,13 @@ export declare class TurnRecovery {
|
|
|
100
100
|
autoContinue: boolean;
|
|
101
101
|
triggerContextTokens?: number;
|
|
102
102
|
}): Promise<RecoveryCompactionResult>;
|
|
103
|
-
/**
|
|
104
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Restores the configured primary after fallback cooldown expiry.
|
|
105
|
+
* @returns true when the active model was actually switched back to the
|
|
106
|
+
* primary, so callers can re-run the pre-send context-fit check against the
|
|
107
|
+
* reverted (possibly smaller) window before issuing the next request.
|
|
108
|
+
*/
|
|
109
|
+
maybeRestoreRetryFallbackPrimary(): Promise<boolean>;
|
|
105
110
|
/** Applies model fallback policy from live usage health before a turn starts. */
|
|
106
111
|
maybeApplyUsageAwareFallback(signal: AbortSignal, confirmer?: UsageFallbackConfirmer): Promise<boolean>;
|
|
107
112
|
/** Applies automatic retry, credential rotation, and model fallback policy. */
|
|
@@ -49,6 +49,8 @@ export declare function composeSpawnAdvisory(args: {
|
|
|
49
49
|
willRunAsync: boolean;
|
|
50
50
|
scoutAvailable?: boolean;
|
|
51
51
|
}): string | undefined;
|
|
52
|
+
/** Rescan one cwd and publish its definitions to existing and future task tools. */
|
|
53
|
+
export declare function refreshAgentDiscovery(cwd: string): Promise<void>;
|
|
52
54
|
/**
|
|
53
55
|
* Task tool - Delegate tasks to specialized agents.
|
|
54
56
|
*
|
|
@@ -41,3 +41,19 @@ export interface FlatShellCommandSegment {
|
|
|
41
41
|
* complete input in that case.
|
|
42
42
|
*/
|
|
43
43
|
export declare function extractFlatShellCommandSegments(command: string): FlatShellCommandSegment[];
|
|
44
|
+
/**
|
|
45
|
+
* Parses a leading `cd <path> && ...` prefix so the bash tool can route the
|
|
46
|
+
* target through its structured `cwd` parameter when the model omits it.
|
|
47
|
+
*
|
|
48
|
+
* Returns the single path token (quotes and backslash escapes resolved to their
|
|
49
|
+
* literal value) and the command remainder after the top-level `&&`, or `null`
|
|
50
|
+
* when the command does not begin with exactly `cd`, one path token, and a
|
|
51
|
+
* top-level `&&`. The scanner deliberately bails on anything else in the prefix
|
|
52
|
+
* — redirects (`cd /tmp 2>/dev/null && ...`), extra arguments, or paths needing
|
|
53
|
+
* shell expansion (`$`, backticks, `(`) — leaving the whole command for the
|
|
54
|
+
* shell instead of absorbing shell syntax into `cwd`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function extractLeadingCdTarget(command: string): {
|
|
57
|
+
path: string;
|
|
58
|
+
rest: string;
|
|
59
|
+
} | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.11",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -50,18 +50,18 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@babel/parser": "^7.29.7",
|
|
53
|
-
"@oh-my-pi/hashline": "17.2.
|
|
54
|
-
"@oh-my-pi/omp-stats": "17.2.
|
|
55
|
-
"@oh-my-pi/omptype": "17.2.
|
|
56
|
-
"@oh-my-pi/pi-agent-core": "17.2.
|
|
57
|
-
"@oh-my-pi/pi-ai": "17.2.
|
|
58
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
59
|
-
"@oh-my-pi/pi-mnemopi": "17.2.
|
|
60
|
-
"@oh-my-pi/pi-natives": "17.2.
|
|
61
|
-
"@oh-my-pi/pi-tui": "17.2.
|
|
62
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
63
|
-
"@oh-my-pi/pi-wire": "17.2.
|
|
64
|
-
"@oh-my-pi/snapcompact": "17.2.
|
|
53
|
+
"@oh-my-pi/hashline": "17.2.11",
|
|
54
|
+
"@oh-my-pi/omp-stats": "17.2.11",
|
|
55
|
+
"@oh-my-pi/omptype": "17.2.11",
|
|
56
|
+
"@oh-my-pi/pi-agent-core": "17.2.11",
|
|
57
|
+
"@oh-my-pi/pi-ai": "17.2.11",
|
|
58
|
+
"@oh-my-pi/pi-catalog": "17.2.11",
|
|
59
|
+
"@oh-my-pi/pi-mnemopi": "17.2.11",
|
|
60
|
+
"@oh-my-pi/pi-natives": "17.2.11",
|
|
61
|
+
"@oh-my-pi/pi-tui": "17.2.11",
|
|
62
|
+
"@oh-my-pi/pi-utils": "17.2.11",
|
|
63
|
+
"@oh-my-pi/pi-wire": "17.2.11",
|
|
64
|
+
"@oh-my-pi/snapcompact": "17.2.11",
|
|
65
65
|
"@opentelemetry/api": "^1.9.1",
|
|
66
66
|
"@opentelemetry/api-logs": "^0.220.0",
|
|
67
67
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
@@ -151,7 +151,10 @@ export async function collectBundledPiEntries(): Promise<BundledPiEntry[]> {
|
|
|
151
151
|
const glob = new Bun.Glob(`**/*${pattern.sourceSuffix}`);
|
|
152
152
|
const matches: string[] = [];
|
|
153
153
|
for await (const match of glob.scan({ cwd: sourceDir, onlyFiles: true })) {
|
|
154
|
-
|
|
154
|
+
// Bun.Glob yields host separators; the export keys and generated
|
|
155
|
+
// identifiers below are `/`-shaped. Same normalization as
|
|
156
|
+
// `generate-docs-index.ts`.
|
|
157
|
+
matches.push(match.split(path.sep).join("/"));
|
|
155
158
|
}
|
|
156
159
|
matches.sort();
|
|
157
160
|
for (const match of matches) {
|
package/src/capability/mcp.ts
CHANGED
|
@@ -27,12 +27,23 @@ export interface MCPServer {
|
|
|
27
27
|
args?: string[];
|
|
28
28
|
/** Environment variables */
|
|
29
29
|
env?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* `literal`: env values are opaque plugin package data (Agent Plugins
|
|
32
|
+
* §§4.1/9.2) — exempt from env-name lookup and `!command` resolution.
|
|
33
|
+
*/
|
|
34
|
+
envPolicy?: "literal";
|
|
30
35
|
/** Working directory for stdio transport */
|
|
31
36
|
cwd?: string;
|
|
32
37
|
/** URL (for HTTP/SSE transport) */
|
|
33
38
|
url?: string;
|
|
34
39
|
/** HTTP headers (for HTTP transport) */
|
|
35
40
|
headers?: Record<string, string>;
|
|
41
|
+
/**
|
|
42
|
+
* `origin-locked`: configured headers are literal package data pinned to the
|
|
43
|
+
* configured URL's origin (Agent Plugins §7.2.1) — never expanded, never
|
|
44
|
+
* forwarded cross-origin, and client-generated headers win case-insensitively.
|
|
45
|
+
*/
|
|
46
|
+
headerPolicy?: "origin-locked";
|
|
36
47
|
/** Authentication configuration */
|
|
37
48
|
auth?: {
|
|
38
49
|
type: "oauth" | "apikey";
|
package/src/capability/skill.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface Skill {
|
|
|
43
43
|
content: string;
|
|
44
44
|
/** Parsed frontmatter */
|
|
45
45
|
frontmatter?: SkillFrontmatter;
|
|
46
|
+
/**
|
|
47
|
+
* Filesystem-resolved plugin root this skill was packaged in (Agent Plugins
|
|
48
|
+
* §4.1). When set, every `skill://` resource access must realpath-resolve
|
|
49
|
+
* within this directory; symlinks may target other files inside it.
|
|
50
|
+
*/
|
|
51
|
+
containRoot?: string;
|
|
46
52
|
/** Source level */
|
|
47
53
|
level: "user" | "project";
|
|
48
54
|
/** Source metadata */
|
package/src/cli/command-help.ts
CHANGED
|
@@ -72,6 +72,10 @@ export const sayHelp = {
|
|
|
72
72
|
|
|
73
73
|
export const searchHelp = { description: "Test web search providers" } satisfies CommandMetadata;
|
|
74
74
|
|
|
75
|
+
export const shareHelp = {
|
|
76
|
+
description: "Share a saved session via an encrypted link (same as /share)",
|
|
77
|
+
} satisfies CommandMetadata;
|
|
78
|
+
|
|
75
79
|
export const setupHelp = {
|
|
76
80
|
description: "Run onboarding setup or install dependencies for optional features",
|
|
77
81
|
} satisfies CommandMetadata;
|
package/src/cli/gallery-cli.ts
CHANGED
|
@@ -153,7 +153,7 @@ export async function renderGalleryState(
|
|
|
153
153
|
const component = new ToolExecutionComponent(
|
|
154
154
|
componentName,
|
|
155
155
|
streamingArgs,
|
|
156
|
-
{ showImages: false },
|
|
156
|
+
{ showImages: false, useBuiltInRenderer: !fixture.customRendered },
|
|
157
157
|
tool,
|
|
158
158
|
ui,
|
|
159
159
|
getProjectDir(),
|
package/src/cli-commands.ts
CHANGED
|
@@ -120,6 +120,11 @@ export const commands: CommandEntry[] = [
|
|
|
120
120
|
load: () => import("./commands/say").then(m => m.default),
|
|
121
121
|
help: commandHelp.sayHelp,
|
|
122
122
|
},
|
|
123
|
+
{
|
|
124
|
+
name: "share",
|
|
125
|
+
load: () => import("./commands/share").then(m => m.default),
|
|
126
|
+
help: commandHelp.shareHelp,
|
|
127
|
+
},
|
|
123
128
|
{
|
|
124
129
|
name: "setup",
|
|
125
130
|
load: () => import("./commands/setup").then(m => m.default),
|
package/src/commands/commit.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { postmortem } from "@oh-my-pi/pi-utils";
|
|
6
6
|
import { Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
7
7
|
import { commitHelp as commandHelp } from "../cli/command-help";
|
|
8
|
-
import { runCommitCommand } from "../commit";
|
|
8
|
+
import { CommitAbortedError, runCommitCommand } from "../commit";
|
|
9
9
|
import type { CommitCommandArgs } from "../commit/types";
|
|
10
10
|
import { initTheme } from "../modes/theme/theme";
|
|
11
11
|
|
|
@@ -41,7 +41,20 @@ export default class Commit extends Command {
|
|
|
41
41
|
// is already written. Mirror the `runPrintMode` exit pattern from
|
|
42
42
|
// `main.ts` so the CLI returns to the shell instead of stranding the user
|
|
43
43
|
// on Ctrl+C (issue #1041).
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
let exitCode = 0;
|
|
45
|
+
try {
|
|
46
|
+
const { usedFallback } = await runCommitCommand(cmd);
|
|
47
|
+
// A commit written by the mechanical fallback is a degraded outcome:
|
|
48
|
+
// the agent did not do the work it was asked to do, so the command
|
|
49
|
+
// reports non-zero so callers can distinguish it from a real
|
|
50
|
+
// single-commit decision (issue #7835).
|
|
51
|
+
if (usedFallback) exitCode = 1;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (!(error instanceof CommitAbortedError)) throw error;
|
|
54
|
+
// Failure already reported with a readable message; exit non-zero
|
|
55
|
+
// without letting the runtime dump a stack/minified-source blob.
|
|
56
|
+
exitCode = 1;
|
|
57
|
+
}
|
|
58
|
+
await postmortem.quit(exitCode);
|
|
46
59
|
}
|
|
47
60
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Share a saved session as an encrypted link without launching the agent.
|
|
3
|
+
*
|
|
4
|
+
* `omp share <session>` accepts a session id (prefix) or a path to a session
|
|
5
|
+
* `.jsonl` and uploads the sealed snapshot exactly like the `/share` slash
|
|
6
|
+
* command, honoring `share.serverUrl`, `share.store`, and
|
|
7
|
+
* `share.redactSecrets`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getAgentDir } from "@oh-my-pi/pi-utils";
|
|
11
|
+
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
12
|
+
import { shareHelp as commandHelp } from "../cli/command-help";
|
|
13
|
+
import { Settings } from "../config/settings";
|
|
14
|
+
import { shareSession } from "../export/share";
|
|
15
|
+
import { buildSecretObfuscator } from "../secrets";
|
|
16
|
+
import { resolveResumableSession } from "../session/session-listing";
|
|
17
|
+
import { SessionManager } from "../session/session-manager";
|
|
18
|
+
|
|
19
|
+
export default class Share extends Command {
|
|
20
|
+
static description = commandHelp.description;
|
|
21
|
+
static args = {
|
|
22
|
+
session: Args.string({
|
|
23
|
+
description: "Session id (prefix) or path to a session .jsonl",
|
|
24
|
+
required: true,
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
static flags = {
|
|
28
|
+
gist: Flags.boolean({
|
|
29
|
+
description: "Upload to a secret GitHub gist instead of the share server",
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
async run(): Promise<void> {
|
|
35
|
+
const { args, flags } = await this.parse(Share);
|
|
36
|
+
|
|
37
|
+
const sessionArg = args.session ?? "";
|
|
38
|
+
let sessionPath = sessionArg;
|
|
39
|
+
if (!sessionArg.includes("/") && !sessionArg.includes("\\") && !sessionArg.endsWith(".jsonl")) {
|
|
40
|
+
const match = await resolveResumableSession(sessionArg, process.cwd());
|
|
41
|
+
if (!match) {
|
|
42
|
+
process.stderr.write(`Session "${sessionArg}" not found.\n`);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
sessionPath = match.session.path;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const sm = await SessionManager.open(sessionPath);
|
|
50
|
+
// Settings resolve against the session's own project so its
|
|
51
|
+
// share.redactSecrets/secrets.enabled policy governs, not the invoking cwd's.
|
|
52
|
+
const settings = await Settings.loadReadOnly({ cwd: sm.getCwd() });
|
|
53
|
+
// Same leak boundary as /share: a share blob leaves the machine, so honor
|
|
54
|
+
// share.redactSecrets with the full obfuscator built against the session's
|
|
55
|
+
// own project directory (its secrets.yml, not the invoking cwd's).
|
|
56
|
+
const obfuscator =
|
|
57
|
+
settings.get("share.redactSecrets") && settings.get("secrets.enabled")
|
|
58
|
+
? await buildSecretObfuscator(sm.getCwd(), getAgentDir())
|
|
59
|
+
: undefined;
|
|
60
|
+
|
|
61
|
+
const result = await shareSession(sm, {
|
|
62
|
+
serverUrl: settings.get("share.serverUrl"),
|
|
63
|
+
store: flags.gist ? "gist" : settings.get("share.store"),
|
|
64
|
+
obfuscator,
|
|
65
|
+
});
|
|
66
|
+
const lines = [`Share URL: ${result.url}`];
|
|
67
|
+
if (result.gistUrl) lines.push(`Gist: ${result.gistUrl}`);
|
|
68
|
+
if (result.truncated) lines.push("Note: large content was trimmed to fit the share size limit.");
|
|
69
|
+
console.log(lines.join("\n"));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -11,6 +11,7 @@ import { ModelRegistry } from "../../config/model-registry";
|
|
|
11
11
|
import { Settings } from "../../config/settings";
|
|
12
12
|
import { discoverAuthStorage, discoverContextFiles, loadCliExtensionProviders } from "../../sdk";
|
|
13
13
|
import * as git from "../../utils/git";
|
|
14
|
+
import { abortOnGitFailure, pushOrAbort } from "../execute";
|
|
14
15
|
import { type ExistingChangelogEntries, runCommitAgentSession } from "./agent";
|
|
15
16
|
import { generateFallbackProposal } from "./fallback";
|
|
16
17
|
import { assignLockFilesToPlan } from "./lock-files";
|
|
@@ -25,7 +26,7 @@ interface CommitExecutionContext {
|
|
|
25
26
|
push: boolean;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
export async function runAgenticCommit(args: CommitCommandArgs): Promise<
|
|
29
|
+
export async function runAgenticCommit(args: CommitCommandArgs): Promise<{ usedFallback: boolean }> {
|
|
29
30
|
const cwd = getProjectDir();
|
|
30
31
|
const [settings, authStorage] = await Promise.all([Settings.init({ cwd }), discoverAuthStorage()]);
|
|
31
32
|
|
|
@@ -56,8 +57,13 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
56
57
|
);
|
|
57
58
|
|
|
58
59
|
if (stagedFiles.length === 0) {
|
|
60
|
+
if (args.push) {
|
|
61
|
+
process.stdout.write("No changes to commit; pushing existing commits...\n");
|
|
62
|
+
await pushOrAbort(cwd);
|
|
63
|
+
return { usedFallback: false };
|
|
64
|
+
}
|
|
59
65
|
process.stderr.write("No changes to commit.\n");
|
|
60
|
-
return;
|
|
66
|
+
return { usedFallback: false };
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
if (!args.noChangelog) {
|
|
@@ -94,7 +100,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
94
100
|
process.stdout.write("● Forcing fallback commit generation...\n");
|
|
95
101
|
const fallbackProposal = generateFallbackProposal(numstat);
|
|
96
102
|
await runSingleCommit(fallbackProposal, { cwd, dryRun: args.dryRun, push: args.push });
|
|
97
|
-
return;
|
|
103
|
+
return { usedFallback: true };
|
|
98
104
|
}
|
|
99
105
|
|
|
100
106
|
const trivialChange = detectTrivialChange(diff);
|
|
@@ -111,7 +117,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
111
117
|
warnings: [],
|
|
112
118
|
};
|
|
113
119
|
await runSingleCommit(trivialProposal, { cwd, dryRun: args.dryRun, push: args.push });
|
|
114
|
-
return;
|
|
120
|
+
return { usedFallback: false };
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
let existingChangelogEntries: ExistingChangelogEntries[] | undefined;
|
|
@@ -124,6 +130,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
124
130
|
|
|
125
131
|
process.stdout.write("● Starting commit agent...\n");
|
|
126
132
|
let agentSessionCompleted = false;
|
|
133
|
+
let usedFallback = false;
|
|
127
134
|
|
|
128
135
|
try {
|
|
129
136
|
await runCommitAgentSession({
|
|
@@ -141,7 +148,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
141
148
|
existingChangelogEntries,
|
|
142
149
|
onComplete: async commitState => {
|
|
143
150
|
agentSessionCompleted = true;
|
|
144
|
-
await completeAgentCommitState(commitState, {
|
|
151
|
+
usedFallback = await completeAgentCommitState(commitState, {
|
|
145
152
|
cwd,
|
|
146
153
|
dryRun: args.dryRun,
|
|
147
154
|
push: args.push,
|
|
@@ -151,7 +158,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
151
158
|
});
|
|
152
159
|
},
|
|
153
160
|
});
|
|
154
|
-
return;
|
|
161
|
+
return { usedFallback };
|
|
155
162
|
} catch (error) {
|
|
156
163
|
if (agentSessionCompleted) {
|
|
157
164
|
throw error;
|
|
@@ -164,7 +171,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
164
171
|
process.stdout.write("● Using fallback commit generation...\n");
|
|
165
172
|
const fallbackProposal = generateFallbackProposal(numstat);
|
|
166
173
|
await runSingleCommit(fallbackProposal, { cwd, dryRun: args.dryRun, push: args.push });
|
|
167
|
-
return;
|
|
174
|
+
return { usedFallback: true };
|
|
168
175
|
}
|
|
169
176
|
}
|
|
170
177
|
|
|
@@ -175,7 +182,7 @@ async function completeAgentCommitState(
|
|
|
175
182
|
changelogTargets: string[];
|
|
176
183
|
numstat: NumstatEntry[];
|
|
177
184
|
},
|
|
178
|
-
): Promise<
|
|
185
|
+
): Promise<boolean> {
|
|
179
186
|
let usedFallback = false;
|
|
180
187
|
if (!commitState.proposal && !commitState.splitProposal) {
|
|
181
188
|
if ($env.PI_COMMIT_NO_FALLBACK?.toLowerCase() !== "true") {
|
|
@@ -211,7 +218,7 @@ async function completeAgentCommitState(
|
|
|
211
218
|
|
|
212
219
|
if (commitState.proposal) {
|
|
213
220
|
await runSingleCommit(commitState.proposal, ctx);
|
|
214
|
-
return;
|
|
221
|
+
return usedFallback;
|
|
215
222
|
}
|
|
216
223
|
|
|
217
224
|
if (commitState.splitProposal) {
|
|
@@ -221,7 +228,7 @@ async function completeAgentCommitState(
|
|
|
221
228
|
push: ctx.push,
|
|
222
229
|
additionalFiles: updatedChangelogFiles,
|
|
223
230
|
});
|
|
224
|
-
return;
|
|
231
|
+
return usedFallback;
|
|
225
232
|
}
|
|
226
233
|
|
|
227
234
|
throw new Error("Commit agent did not provide a proposal.");
|
|
@@ -238,12 +245,14 @@ async function runSingleCommit(proposal: CommitProposal, ctx: CommitExecutionCon
|
|
|
238
245
|
return;
|
|
239
246
|
}
|
|
240
247
|
process.stdout.write("● Creating commit...\n");
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
248
|
+
try {
|
|
249
|
+
await git.commit(ctx.cwd, commitMessage);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof git.GitCommandError) abortOnGitFailure("Commit failed", error);
|
|
252
|
+
throw error;
|
|
246
253
|
}
|
|
254
|
+
process.stdout.write("Commit created.\n");
|
|
255
|
+
if (ctx.push) await pushOrAbort(ctx.cwd);
|
|
247
256
|
}
|
|
248
257
|
|
|
249
258
|
async function runSplitCommit(
|
|
@@ -296,7 +305,7 @@ async function runSplitCommit(
|
|
|
296
305
|
process.stdout.write("● Creating split commits...\n");
|
|
297
306
|
const stagedDiff = await git.diff(ctx.cwd, { cached: true, binary: true });
|
|
298
307
|
await git.stage.reset(ctx.cwd);
|
|
299
|
-
for (const commitIndex of order) {
|
|
308
|
+
for (const [position, commitIndex] of order.entries()) {
|
|
300
309
|
const commit = plan.commits[commitIndex];
|
|
301
310
|
await git.stage.hunks(ctx.cwd, commit.changes, { rawDiff: stagedDiff, diffCached: true });
|
|
302
311
|
const analysis: ConventionalAnalysis = {
|
|
@@ -306,14 +315,23 @@ async function runSplitCommit(
|
|
|
306
315
|
issueRefs: commit.issueRefs,
|
|
307
316
|
};
|
|
308
317
|
const message = formatCommitMessage(analysis, commit.summary);
|
|
309
|
-
|
|
318
|
+
try {
|
|
319
|
+
await git.commit(ctx.cwd, message);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (error instanceof git.GitCommandError) {
|
|
322
|
+
const stagedNow = await git.diff.changedFiles(ctx.cwd, { cached: true });
|
|
323
|
+
abortOnGitFailure(
|
|
324
|
+
`Commit ${position + 1} of ${order.length} failed`,
|
|
325
|
+
error,
|
|
326
|
+
`${position} of ${order.length} commits created; ${stagedNow.length} file(s) remain staged. No changes were lost.`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
310
331
|
await git.stage.reset(ctx.cwd);
|
|
311
332
|
}
|
|
312
333
|
process.stdout.write("Split commits created.\n");
|
|
313
|
-
if (ctx.push)
|
|
314
|
-
await git.push(ctx.cwd);
|
|
315
|
-
process.stdout.write("Pushed to remote.\n");
|
|
316
|
-
}
|
|
334
|
+
if (ctx.push) await pushOrAbort(ctx.cwd);
|
|
317
335
|
}
|
|
318
336
|
|
|
319
337
|
function appendFilesToLastCommit(plan: SplitCommitPlan, files: string[]): void {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared commit-execution helpers for the agentic and legacy `omp commit`
|
|
3
|
+
* pipelines: readable failure reporting for refusing git hooks and a push
|
|
4
|
+
* wrapper that keeps a requested `--push` honest.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as git from "../utils/git";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A commit or push failure that has already been reported to the user with a
|
|
11
|
+
* readable message. It is thrown so the CLI exits non-zero without the runtime
|
|
12
|
+
* dumping a stack trace — in a bundled build that trace is several kilobytes of
|
|
13
|
+
* minified source, which buries the one line the user needs (issue #7834).
|
|
14
|
+
*
|
|
15
|
+
* `runCommitCommand`'s caller (`commands/commit.ts`) maps this to exit code 1.
|
|
16
|
+
*/
|
|
17
|
+
export class CommitAbortedError extends Error {
|
|
18
|
+
constructor() {
|
|
19
|
+
super("commit aborted");
|
|
20
|
+
this.name = "CommitAbortedError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Print a git-command failure — most often a `pre-commit`/`commit-msg` hook
|
|
26
|
+
* that exited non-zero — as an indented message under `context`, then abort.
|
|
27
|
+
*
|
|
28
|
+
* @param context Label for the failed step, e.g. `"Commit 1 of 2 failed"`.
|
|
29
|
+
* @param error The failure to surface; its captured stderr/stdout is shown.
|
|
30
|
+
* @param note Optional trailing status line (split-plan progress, recovery).
|
|
31
|
+
*/
|
|
32
|
+
export function abortOnGitFailure(context: string, error: git.GitCommandError, note?: string): never {
|
|
33
|
+
const detail = error.result.stderr.trim() || error.result.stdout.trim() || error.message;
|
|
34
|
+
const body = detail
|
|
35
|
+
.split("\n")
|
|
36
|
+
.map(line => ` ${line}`)
|
|
37
|
+
.join("\n");
|
|
38
|
+
process.stderr.write(`✗ ${context}:\n${body}\n`);
|
|
39
|
+
if (note) process.stderr.write(` ${note}\n`);
|
|
40
|
+
throw new CommitAbortedError();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Push the current branch, reporting a refused push (missing upstream, rejected
|
|
45
|
+
* ref) through {@link abortOnGitFailure} instead of letting the raw
|
|
46
|
+
* `GitCommandError` escape. Prints the success line on completion.
|
|
47
|
+
*/
|
|
48
|
+
export async function pushOrAbort(cwd: string): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
await git.push(cwd);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof git.GitCommandError) abortOnGitFailure("Push failed", error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
process.stdout.write("Pushed to remote.\n");
|
|
56
|
+
}
|
package/src/commit/index.ts
CHANGED