oh-my-opencode 4.14.0 → 4.14.2
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/.agents/skills/codex-qa/references/isolation.md +1 -1
- package/README.md +1 -1
- package/dist/cli/doctor/framework/types.d.ts +2 -0
- package/dist/cli/index.js +223 -1447
- package/dist/cli-node/index.js +223 -1447
- package/dist/index.js +4255 -714
- package/dist/plugin/native-skills.d.ts +9 -0
- package/dist/plugin/system-transform.d.ts +1 -1
- package/dist/skills/frontend/ATTRIBUTION.md +7 -1
- package/dist/skills/frontend/SKILL.md +4 -3
- package/dist/skills/frontend/references/design/README.md +4 -3
- package/dist/skills/frontend/references/design/_INDEX.md +5 -3
- package/dist/skills/frontend/references/design/aside.md +209 -0
- package/dist/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/dist/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/dist/skills/lcx-doctor/SKILL.md +48 -13
- package/dist/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/dist/skills/lcx-report-bug/SKILL.md +44 -13
- package/dist/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/dist/skills/visual-qa/SKILL.md +5 -5
- package/dist/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/dist/skills/visual-qa/scripts/cli.test.ts +7 -2
- package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
- package/dist/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +2 -0
- package/dist/tools/skill/types.d.ts +1 -0
- package/package.json +26 -13
- package/packages/git-bash-mcp/dist/cli.js +2 -0
- package/packages/lsp-daemon/dist/cli.js +15 -4
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +1 -0
- package/packages/lsp-daemon/dist/ensure-daemon.js +9 -3
- package/packages/lsp-daemon/dist/index.js +15 -4
- package/packages/lsp-daemon/dist/paths.d.ts +1 -0
- package/packages/lsp-daemon/dist/paths.js +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -7
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +123 -10
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +123 -10
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +126 -9
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +141 -0
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +90 -25
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +2 -0
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +2 -0
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +56 -0
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/cli.ts +2 -5
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +2 -0
- package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +65 -0
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +6 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +8 -106
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +4 -17
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +17 -5
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +63 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +80 -50
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +4 -12
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +7 -33
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +4 -12
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/sample-quality-gate.json +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +3 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-doc.test.ts +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +7 -6
- package/packages/omo-codex/plugin/package-lock.json +16 -56
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +4 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +5 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +209 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +48 -13
- package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +44 -13
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +17 -5
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +63 -1
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +80 -50
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +5 -5
- package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/packages/omo-codex/plugin/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +23 -0
- package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +38 -2
- package/packages/omo-codex/plugin/test/component-bin-names.test.mjs +12 -11
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +99 -1
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +62 -8
- package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +102 -0
- package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +41 -1
- package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-bin-links.test.mjs +6 -0
- package/packages/omo-codex/scripts/install-cache-copy.test.mjs +28 -0
- package/packages/omo-codex/scripts/install-config-autonomous-features.test.mjs +43 -3
- package/packages/omo-codex/scripts/install-delegated-command.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -41
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +21 -2
- package/packages/omo-codex/scripts/install-local.test.mjs +0 -1
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +4 -3
- package/packages/shared-skills/skills/frontend/references/design/README.md +4 -3
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +5 -3
- package/packages/shared-skills/skills/frontend/references/design/aside.md +209 -0
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +48 -13
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +44 -13
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/visual-qa/SKILL.md +5 -5
- package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/packages/shared-skills/skills/visual-qa/scripts/cli.test.ts +7 -2
- package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
- package/packages/shared-skills/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/dist/cli/sparkshell-appserver-websocket.d.ts +0 -4
- package/dist/cli/sparkshell-appserver.d.ts +0 -16
- package/dist/cli/sparkshell-condense.d.ts +0 -10
- package/dist/cli/sparkshell-parse.d.ts +0 -24
- package/dist/cli/sparkshell-session-context.d.ts +0 -20
- package/dist/cli/sparkshell-spark.d.ts +0 -23
- package/dist/cli/sparkshell.d.ts +0 -33
- package/dist/shared/sparkshell-awareness.d.ts +0 -5
- package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +0 -106
- package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +0 -355
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export declare const SPARKSHELL_USAGE: string;
|
|
2
|
-
export type SparkShellFallbackInvocation = {
|
|
3
|
-
readonly kind: "command";
|
|
4
|
-
readonly argv: readonly string[];
|
|
5
|
-
} | {
|
|
6
|
-
readonly kind: "tmux-pane";
|
|
7
|
-
readonly argv: readonly string[];
|
|
8
|
-
};
|
|
9
|
-
type RuntimeEnv = Readonly<Record<string, string | undefined>>;
|
|
10
|
-
export declare function resolveFallbackShellArgv(script: string, options?: {
|
|
11
|
-
readonly platform?: NodeJS.Platform;
|
|
12
|
-
readonly env?: RuntimeEnv;
|
|
13
|
-
readonly commandExists?: (command: string) => boolean;
|
|
14
|
-
}): readonly string[];
|
|
15
|
-
export declare function parseSparkShellFallbackInvocation(rawArgs: readonly string[], options?: {
|
|
16
|
-
readonly platform?: NodeJS.Platform;
|
|
17
|
-
readonly env?: RuntimeEnv;
|
|
18
|
-
readonly commandExists?: (command: string) => boolean;
|
|
19
|
-
}): SparkShellFallbackInvocation;
|
|
20
|
-
export declare function hasTopLevelSparkShellHelpFlag(args: readonly string[]): boolean;
|
|
21
|
-
export declare function parseTopLevelSparkShellBudget(args: readonly string[]): number | null;
|
|
22
|
-
export declare function hasTopLevelSparkShellJsonFlag(args: readonly string[]): boolean;
|
|
23
|
-
export declare function stripTopLevelSparkShellArgs(args: readonly string[]): readonly string[];
|
|
24
|
-
export {};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
type RuntimeEnv = Readonly<Record<string, string | undefined>>;
|
|
2
|
-
export type SessionContextDeps = {
|
|
3
|
-
readonly fileExists?: (path: string) => boolean;
|
|
4
|
-
readonly listDirectory?: (path: string) => readonly string[];
|
|
5
|
-
readonly readTextFile?: (path: string) => string;
|
|
6
|
-
readonly homeDirectory?: () => string;
|
|
7
|
-
};
|
|
8
|
-
export declare const SPARKSHELL_SESSION_CONTEXT_ENV = "OMO_SPARKSHELL_SESSION_CONTEXT";
|
|
9
|
-
export declare const SPARKSHELL_SESSION_ID_ENV = "OMO_SPARKSHELL_SESSION_ID";
|
|
10
|
-
export declare const CODEX_THREAD_ID_ENV = "CODEX_THREAD_ID";
|
|
11
|
-
export declare function resolveCodexSessionId(env: RuntimeEnv): string | null;
|
|
12
|
-
export declare function findRolloutPath(sessionId: string, env: RuntimeEnv, deps?: SessionContextDeps): string | null;
|
|
13
|
-
export type SessionContextDetails = {
|
|
14
|
-
readonly block: string;
|
|
15
|
-
readonly firstUserRequest: string;
|
|
16
|
-
readonly latestUserRequest: string;
|
|
17
|
-
};
|
|
18
|
-
export declare function loadCodexSessionContextDetails(env: RuntimeEnv, deps?: SessionContextDeps): SessionContextDetails | null;
|
|
19
|
-
export declare function loadCodexSessionContext(env: RuntimeEnv, deps?: SessionContextDeps): string;
|
|
20
|
-
export {};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
type RuntimeEnv = Readonly<Record<string, string | undefined>>;
|
|
2
|
-
export declare const SPARKSHELL_SPARK_ENV = "OMO_SPARKSHELL_SPARK";
|
|
3
|
-
export declare const SPARKSHELL_SPARK_MODEL_ENV = "OMO_SPARKSHELL_SPARK_MODEL";
|
|
4
|
-
export declare const SPARKSHELL_SPARK_TIMEOUT_ENV = "OMO_SPARKSHELL_SPARK_TIMEOUT_MS";
|
|
5
|
-
export declare const SPARKSHELL_SPARK_BIN_ENV = "OMO_SPARKSHELL_SPARK_BIN";
|
|
6
|
-
export declare const SPARKSHELL_SPARK_PROFILE_ENV = "OMO_SPARKSHELL_SPARK_PROFILE";
|
|
7
|
-
export declare const DEFAULT_SPARK_MODEL = "gpt-5.3-codex-spark";
|
|
8
|
-
export declare const DEFAULT_SPARK_TIMEOUT_MS = 30000;
|
|
9
|
-
export declare const SPARK_PROMPT_OUTPUT_CAP_CHARS = 24000;
|
|
10
|
-
export type SparkSummaryRequest = {
|
|
11
|
-
readonly commandLine: string;
|
|
12
|
-
readonly text: string;
|
|
13
|
-
readonly budgetChars: number;
|
|
14
|
-
readonly sessionContext: string;
|
|
15
|
-
};
|
|
16
|
-
export type SparkSummarizer = (request: SparkSummaryRequest) => string | null;
|
|
17
|
-
export declare function isSparkSummaryEnabled(env: RuntimeEnv): boolean;
|
|
18
|
-
export declare function resolveSparkModel(env: RuntimeEnv): string;
|
|
19
|
-
export declare function resolveSparkTimeoutMs(env: RuntimeEnv): number;
|
|
20
|
-
export declare function buildSparkSummaryPrompt(request: SparkSummaryRequest): string;
|
|
21
|
-
export declare function buildSparkExecArgs(env: RuntimeEnv, lastMessagePath: string): readonly string[];
|
|
22
|
-
export declare function createDefaultSparkSummarizer(env: RuntimeEnv, cwd: string): SparkSummarizer;
|
|
23
|
-
export {};
|
package/dist/cli/sparkshell.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { type RuntimeEnv, type SparkShellAppServerClient, type SparkShellAppServerCommand, type SparkShellAppServerResult } from "./sparkshell-appserver";
|
|
2
|
-
import { type SessionContextDetails } from "./sparkshell-session-context";
|
|
3
|
-
import { type SparkSummarizer } from "./sparkshell-spark";
|
|
4
|
-
export declare const SPARKSHELL_BIN_ENV = "OMO_SPARKSHELL_BIN";
|
|
5
|
-
export declare const SPARKSHELL_CONDENSE_ENV = "OMO_SPARKSHELL_CONDENSE";
|
|
6
|
-
export declare const SPARKSHELL_CONDENSE_BUDGET_ENV = "OMO_SPARKSHELL_CONDENSE_BUDGET";
|
|
7
|
-
export type { SparkShellAppServerClient, SparkShellAppServerCommand, SparkShellAppServerResult };
|
|
8
|
-
export { parseSparkShellFallbackInvocation, parseTopLevelSparkShellBudget, resolveFallbackShellArgv, SPARKSHELL_USAGE, } from "./sparkshell-parse";
|
|
9
|
-
export { DEFAULT_SPARK_MODEL, SPARKSHELL_SPARK_BIN_ENV, SPARKSHELL_SPARK_ENV, SPARKSHELL_SPARK_MODEL_ENV, SPARKSHELL_SPARK_TIMEOUT_ENV, type SparkSummarizer, type SparkSummaryRequest, } from "./sparkshell-spark";
|
|
10
|
-
export type SparkShellSpawnResult = {
|
|
11
|
-
readonly status?: number | null;
|
|
12
|
-
readonly signal?: string | null;
|
|
13
|
-
readonly stdout?: string;
|
|
14
|
-
readonly stderr?: string;
|
|
15
|
-
readonly error?: Error;
|
|
16
|
-
};
|
|
17
|
-
export type SparkShellSpawn = (command: string, args: readonly string[], options: {
|
|
18
|
-
readonly cwd: string;
|
|
19
|
-
readonly env: RuntimeEnv;
|
|
20
|
-
}) => SparkShellSpawnResult;
|
|
21
|
-
export type SparkShellRunOptions = {
|
|
22
|
-
readonly cwd?: string;
|
|
23
|
-
readonly env?: RuntimeEnv;
|
|
24
|
-
readonly platform?: NodeJS.Platform;
|
|
25
|
-
readonly spawn?: SparkShellSpawn;
|
|
26
|
-
readonly writeStdout?: (value: string) => void;
|
|
27
|
-
readonly writeStderr?: (value: string) => void;
|
|
28
|
-
readonly commandExists?: (command: string) => boolean;
|
|
29
|
-
readonly appServerClient?: SparkShellAppServerClient | null;
|
|
30
|
-
readonly loadSessionContext?: (env: RuntimeEnv) => SessionContextDetails | null;
|
|
31
|
-
readonly sparkSummarize?: SparkSummarizer | null;
|
|
32
|
-
};
|
|
33
|
-
export declare function runSparkShell(args: readonly string[], options?: SparkShellRunOptions): Promise<number>;
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
type RuntimeEnv = Readonly<Record<string, string | undefined>>;
|
|
2
|
-
export declare function isCodexAppServerActive(env?: RuntimeEnv): boolean;
|
|
3
|
-
export declare function getSparkShellRuntimeAwareness(env?: RuntimeEnv): string;
|
|
4
|
-
export declare function hasSparkShellRuntimeAwareness(value: string): boolean;
|
|
5
|
-
export {};
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
type RuntimeEnv = Readonly<Record<string, string | undefined>>;
|
|
5
|
-
|
|
6
|
-
interface OmoResolutionDeps {
|
|
7
|
-
readonly fileExists?: (path: string) => boolean;
|
|
8
|
-
readonly platform?: NodeJS.Platform;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const SPARKSHELL_AWARENESS_MARKER = "## Sparkshell Runtime";
|
|
12
|
-
|
|
13
|
-
export const SPARKSHELL_AWARENESS_DEDUP_KEY = "__omo_sparkshell_awareness__";
|
|
14
|
-
|
|
15
|
-
export function isCodexAppServerActive(env: RuntimeEnv = process.env): boolean {
|
|
16
|
-
const originator = env["CODEX_INTERNAL_ORIGINATOR_OVERRIDE"]?.toLowerCase() ?? "";
|
|
17
|
-
const bundleIdentifier = env["__CFBundleIdentifier"]?.toLowerCase() ?? "";
|
|
18
|
-
const shellActive = isTruthy(env["CODEX_SHELL"]);
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
shellActive &&
|
|
22
|
-
(originator.includes("codex desktop") ||
|
|
23
|
-
originator.includes("codex app") ||
|
|
24
|
-
bundleIdentifier === "com.openai.codex")
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function isSparkShellAppServerConfigured(env: RuntimeEnv = process.env): boolean {
|
|
29
|
-
const codexSocketPath = env["CODEX_APP_SERVER_SOCKET"]?.trim() ?? "";
|
|
30
|
-
const omoSocketPath = env["OMO_SPARKSHELL_APP_SERVER_SOCKET"]?.trim() ?? "";
|
|
31
|
-
return codexSocketPath.length > 0 || omoSocketPath.length > 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function resolveOmoInvocation(env: RuntimeEnv = process.env, deps: OmoResolutionDeps = {}): string | null {
|
|
35
|
-
const fileExists = deps.fileExists ?? existsSync;
|
|
36
|
-
const platform = deps.platform ?? process.platform;
|
|
37
|
-
const binNames = platform === "win32" ? ["omo.cmd", "omo.exe", "omo"] : ["omo"];
|
|
38
|
-
const pathDelimiter = platform === "win32" ? ";" : ":";
|
|
39
|
-
const pathEntries = (env["PATH"] ?? "").split(pathDelimiter).filter((entry) => entry.trim().length > 0);
|
|
40
|
-
for (const pathEntry of pathEntries) {
|
|
41
|
-
for (const binName of binNames) {
|
|
42
|
-
if (fileExists(join(pathEntry, binName))) return "omo";
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
for (const candidateDir of omoCandidateBinDirs(env)) {
|
|
46
|
-
for (const binName of binNames) {
|
|
47
|
-
const candidate = join(candidateDir, binName);
|
|
48
|
-
if (fileExists(candidate)) return candidate;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function omoCandidateBinDirs(env: RuntimeEnv): readonly string[] {
|
|
55
|
-
const dirs: string[] = [];
|
|
56
|
-
const localBinDir = env["CODEX_LOCAL_BIN_DIR"]?.trim() ?? "";
|
|
57
|
-
if (localBinDir.length > 0) dirs.push(localBinDir);
|
|
58
|
-
const home = env["HOME"]?.trim() || env["USERPROFILE"]?.trim() || "";
|
|
59
|
-
const codexHome = env["CODEX_HOME"]?.trim() || (home.length > 0 ? join(home, ".codex") : "");
|
|
60
|
-
if (codexHome.length > 0) dirs.push(join(codexHome, "bin"));
|
|
61
|
-
if (home.length > 0) dirs.push(join(home, ".local", "bin"));
|
|
62
|
-
return dirs;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function getSparkShellRuntimeAwareness(env: RuntimeEnv = process.env, deps: OmoResolutionDeps = {}): string {
|
|
66
|
-
const override = env["OMO_SPARKSHELL_AWARENESS"] ?? env["LAZYCODEX_SPARKSHELL_AWARENESS"];
|
|
67
|
-
if (isFalsy(override)) {
|
|
68
|
-
return "";
|
|
69
|
-
}
|
|
70
|
-
if (!isTruthy(override) && !isCodexAppServerActive(env) && !isSparkShellAppServerConfigured(env)) {
|
|
71
|
-
return "";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const resolved = resolveOmoInvocation(env, deps);
|
|
75
|
-
const invocation = resolved ?? (isTruthy(override) ? "omo" : null);
|
|
76
|
-
if (invocation === null) {
|
|
77
|
-
return "";
|
|
78
|
-
}
|
|
79
|
-
const command = /\s/.test(invocation) ? `"${invocation}"` : invocation;
|
|
80
|
-
|
|
81
|
-
return [
|
|
82
|
-
SPARKSHELL_AWARENESS_MARKER,
|
|
83
|
-
"",
|
|
84
|
-
`- Use \`${command} sparkshell <command> [args...]\` first for repo inspection, CLI smoke tests, git/history checks, and bounded command output. Pass ordinary commands as executable and arguments in separate argv tokens, for example \`${command} sparkshell rg --files\`; not \`${command} sparkshell 'rg --files'\`, because the quoted string is treated as one executable name. Raw \`rg\`/\`grep\`/\`cat\`/\`git\` are fallbacks when Sparkshell is unavailable or too narrow for the task.`,
|
|
85
|
-
`- Use \`${command} sparkshell --shell '<command>'\` only for shell syntax such as metacharacters, pipelines, redirects, command substitution, or variable expansion.`,
|
|
86
|
-
`- Use \`${command} sparkshell --tmux-pane <pane-id> --tail-lines 400\` only to inspect an existing pane, never to launch ordinary commands. Tail lines must stay between 100 and 1000.`,
|
|
87
|
-
"- When no native sidecar or appserver is available, Sparkshell silently falls back to raw command execution. `OMO_SPARKSHELL_BIN` selects a native sidecar path.",
|
|
88
|
-
"- When `CODEX_THREAD_ID` identifies a Codex session, Sparkshell feeds recent session context (first/latest user request + last 5 conversation messages) into oversized-output condensation for relevance ranking, but never appends that context to command output. `OMO_SPARKSHELL_SESSION_CONTEXT=0` disables the lookup.",
|
|
89
|
-
`- Route potentially huge output (full log files, big diffs, \`cat\`/\`grep\` over large artifacts) through \`${command} sparkshell\` instead of reading it raw: oversized output is condensed to a budget while preserving error signatures, repeated patterns, session-goal-relevant lines, and head/tail. Tune with \`--budget <chars>\`; disable with \`OMO_SPARKSHELL_CONDENSE=0\`.`,
|
|
90
|
-
"- Oversized output is first summarized by the spark model (`codex exec`, default `gpt-5.3-codex-spark`) fed with the shell output plus session context: the summary keeps selected output as-is (no masking) and ends with a `[sparkshell caption]` line describing what ran, what the full output contained, and which lines were omitted. `OMO_SPARKSHELL_SPARK=0` skips the model and uses deterministic condensation directly.",
|
|
91
|
-
].join("\n");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function isTruthy(value: string | undefined): boolean {
|
|
95
|
-
if (value === undefined) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function isFalsy(value: string | undefined): boolean {
|
|
102
|
-
if (value === undefined) {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
|
|
106
|
-
}
|
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
// allow: SIZE_OK - rules sparkshell-awareness coverage validates one injected guidance contract; this release adds narrow prompt cases and future growth should split by guidance surface.
|
|
2
|
-
|
|
3
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { join } from "node:path";
|
|
6
|
-
|
|
7
|
-
import { afterAll, describe, expect, it } from "vitest";
|
|
8
|
-
|
|
9
|
-
import { runSessionStartHook, runUserPromptSubmitHook } from "../src/codex-hook.js";
|
|
10
|
-
import { formatAdditionalContextOutput } from "../src/hook-output.js";
|
|
11
|
-
|
|
12
|
-
type HookOutput = {
|
|
13
|
-
readonly hookSpecificOutput?: {
|
|
14
|
-
readonly additionalContext?: string;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function parseAdditionalContext(output: string): string {
|
|
19
|
-
expect(output.trim().length).toBeGreaterThan(0);
|
|
20
|
-
const parsed = parseHookOutput(JSON.parse(output));
|
|
21
|
-
return parsed.hookSpecificOutput?.additionalContext ?? "";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function normalizeGuidance(value: string): string {
|
|
25
|
-
return value.toLowerCase().replace(/\s+/g, " ").trim();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function expectSparkshellFirstContract(value: string): void {
|
|
29
|
-
const guidance = normalizeGuidance(value);
|
|
30
|
-
|
|
31
|
-
expect(value).toContain("`omo sparkshell <command> [args...]`");
|
|
32
|
-
expect(guidance).toMatch(/`omo sparkshell <command> \[args\.\.\.\]`[^.]*\bfirst\b/);
|
|
33
|
-
expect(guidance).toMatch(/\brepo inspection\b/);
|
|
34
|
-
expect(guidance).toMatch(/\bcli smoke tests\b/);
|
|
35
|
-
expect(guidance).toMatch(/\bgit\/history checks\b/);
|
|
36
|
-
expect(guidance).toMatch(/\bbounded command output\b/);
|
|
37
|
-
expect(guidance).toMatch(/\braw\b[^.]*`rg`\/`grep`\/`cat`\/`git`[^.]*\bfallbacks?\b/);
|
|
38
|
-
expect(guidance).toMatch(/\bsparkshell is unavailable\b/);
|
|
39
|
-
expect(guidance).toMatch(/\btoo narrow\b/);
|
|
40
|
-
expect(value).toContain("`omo sparkshell rg --files`");
|
|
41
|
-
expect(guidance).toMatch(/\bseparate argv tokens\b/);
|
|
42
|
-
expect(value).toContain("not `omo sparkshell 'rg --files'`");
|
|
43
|
-
expect(guidance).toMatch(/\bone executable name\b/);
|
|
44
|
-
expect(guidance).toMatch(/`omo sparkshell --shell '<command>'`[^.]*\bmetacharacters\b[^.]*\bpipelines\b/);
|
|
45
|
-
expect(guidance).toMatch(
|
|
46
|
-
/`omo sparkshell --tmux-pane <pane-id> --tail-lines 400`[^.]*\bonly\b[^.]*\binspect\b[^.]*\bexisting pane\b/,
|
|
47
|
-
);
|
|
48
|
-
expect(guidance).toMatch(
|
|
49
|
-
/`omo sparkshell --tmux-pane <pane-id> --tail-lines 400`[^.]*\bnever\b[^.]*\blaunch ordinary commands\b/,
|
|
50
|
-
);
|
|
51
|
-
expect(guidance).not.toMatch(/\bprefer\b[^.]*\bbefore raw shell commands\b/);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseHookOutput(value: unknown): HookOutput {
|
|
55
|
-
if (typeof value !== "object" || value === null) {
|
|
56
|
-
return {};
|
|
57
|
-
}
|
|
58
|
-
const record = value;
|
|
59
|
-
if (!("hookSpecificOutput" in record)) {
|
|
60
|
-
return {};
|
|
61
|
-
}
|
|
62
|
-
const hookSpecificOutput = record.hookSpecificOutput;
|
|
63
|
-
if (typeof hookSpecificOutput !== "object" || hookSpecificOutput === null) {
|
|
64
|
-
return {};
|
|
65
|
-
}
|
|
66
|
-
if (!("additionalContext" in hookSpecificOutput)) {
|
|
67
|
-
return { hookSpecificOutput: {} };
|
|
68
|
-
}
|
|
69
|
-
const additionalContext = hookSpecificOutput.additionalContext;
|
|
70
|
-
if (typeof additionalContext !== "string") {
|
|
71
|
-
return { hookSpecificOutput: {} };
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
hookSpecificOutput: {
|
|
75
|
-
additionalContext,
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const fixtureRoot = mkdtempSync(join(tmpdir(), "codex-sparkshell-omo-bin-"));
|
|
81
|
-
const omoOnPathDir = join(fixtureRoot, "path-bin");
|
|
82
|
-
const emptyHomeDir = join(fixtureRoot, "empty-home");
|
|
83
|
-
const localBinHomeDir = join(fixtureRoot, "local-bin-home");
|
|
84
|
-
mkdirSync(omoOnPathDir, { recursive: true });
|
|
85
|
-
mkdirSync(emptyHomeDir, { recursive: true });
|
|
86
|
-
mkdirSync(join(localBinHomeDir, ".local", "bin"), { recursive: true });
|
|
87
|
-
writeFileSync(join(omoOnPathDir, "omo"), "#!/bin/sh\n");
|
|
88
|
-
writeFileSync(join(localBinHomeDir, ".local", "bin", "omo"), "#!/bin/sh\n");
|
|
89
|
-
|
|
90
|
-
describe("Codex Sparkshell awareness", () => {
|
|
91
|
-
afterAll(() => {
|
|
92
|
-
rmSync(fixtureRoot, { recursive: true, force: true });
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("#given active Codex app server env with omo on PATH #when SessionStart runs #then emits Sparkshell guidance", async () => {
|
|
96
|
-
// given
|
|
97
|
-
const env = {
|
|
98
|
-
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "Codex Desktop",
|
|
99
|
-
CODEX_SHELL: "1",
|
|
100
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
101
|
-
PATH: omoOnPathDir,
|
|
102
|
-
HOME: emptyHomeDir,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// when
|
|
106
|
-
const output = await runSessionStartHook(
|
|
107
|
-
{
|
|
108
|
-
session_id: "session-sparkshell-active",
|
|
109
|
-
transcript_path: null,
|
|
110
|
-
cwd: process.cwd(),
|
|
111
|
-
hook_event_name: "SessionStart",
|
|
112
|
-
model: "gpt-5.5",
|
|
113
|
-
permission_mode: "default",
|
|
114
|
-
source: "startup",
|
|
115
|
-
},
|
|
116
|
-
{ env },
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
// then
|
|
120
|
-
const context = parseAdditionalContext(output);
|
|
121
|
-
expectSparkshellFirstContract(context);
|
|
122
|
-
expect(context).toContain("OMO_SPARKSHELL_SESSION_CONTEXT");
|
|
123
|
-
expect(context).toContain("OMO_SPARKSHELL_CONDENSE");
|
|
124
|
-
expect(context).toContain("OMO_SPARKSHELL_SPARK");
|
|
125
|
-
expect(context).toContain("[sparkshell caption]");
|
|
126
|
-
expect(context).toContain("never appends that context to command output");
|
|
127
|
-
expect(context).toContain("what the full output contained");
|
|
128
|
-
expect(context).not.toContain("[REDACTED]");
|
|
129
|
-
expect(context).not.toContain("appends recent session context");
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("#given inactive env #when SessionStart runs #then emits no Sparkshell guidance", async () => {
|
|
133
|
-
// given
|
|
134
|
-
const env = {
|
|
135
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// when
|
|
139
|
-
const output = await runSessionStartHook(
|
|
140
|
-
{
|
|
141
|
-
session_id: "session-sparkshell-inactive",
|
|
142
|
-
transcript_path: null,
|
|
143
|
-
cwd: process.cwd(),
|
|
144
|
-
hook_event_name: "SessionStart",
|
|
145
|
-
model: "gpt-5.5",
|
|
146
|
-
permission_mode: "default",
|
|
147
|
-
source: "startup",
|
|
148
|
-
},
|
|
149
|
-
{ env },
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
// then
|
|
153
|
-
expect(output).toBe("");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("#given Codex CLI appserver socket env #when SessionStart runs #then emits Sparkshell guidance", async () => {
|
|
157
|
-
// given
|
|
158
|
-
const env = {
|
|
159
|
-
OMO_SPARKSHELL_APP_SERVER_SOCKET: "/tmp/app-server-control.sock",
|
|
160
|
-
CODEX_THREAD_ID: "thread-sparkshell-cli",
|
|
161
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
162
|
-
PATH: omoOnPathDir,
|
|
163
|
-
HOME: emptyHomeDir,
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// when
|
|
167
|
-
const output = await runSessionStartHook(
|
|
168
|
-
{
|
|
169
|
-
session_id: "session-sparkshell-cli-wrapper",
|
|
170
|
-
transcript_path: null,
|
|
171
|
-
cwd: process.cwd(),
|
|
172
|
-
hook_event_name: "SessionStart",
|
|
173
|
-
model: "gpt-5.5",
|
|
174
|
-
permission_mode: "default",
|
|
175
|
-
source: "startup",
|
|
176
|
-
},
|
|
177
|
-
{ env },
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// then
|
|
181
|
-
expect(parseAdditionalContext(output)).toContain("omo sparkshell <command>");
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("#given active Codex app env without a resolvable omo command #when SessionStart runs #then emits no Sparkshell guidance", async () => {
|
|
185
|
-
// given
|
|
186
|
-
const env = {
|
|
187
|
-
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "Codex Desktop",
|
|
188
|
-
CODEX_SHELL: "1",
|
|
189
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
190
|
-
PATH: join(fixtureRoot, "missing-path-entry"),
|
|
191
|
-
HOME: emptyHomeDir,
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
// when
|
|
195
|
-
const output = await runSessionStartHook(
|
|
196
|
-
{
|
|
197
|
-
session_id: "session-sparkshell-unresolvable",
|
|
198
|
-
transcript_path: null,
|
|
199
|
-
cwd: process.cwd(),
|
|
200
|
-
hook_event_name: "SessionStart",
|
|
201
|
-
model: "gpt-5.5",
|
|
202
|
-
permission_mode: "default",
|
|
203
|
-
source: "startup",
|
|
204
|
-
},
|
|
205
|
-
{ env },
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
// then
|
|
209
|
-
expect(output).toBe("");
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("#given omo only under HOME/.local/bin #when SessionStart runs #then emits guidance with the absolute omo path", async () => {
|
|
213
|
-
// given
|
|
214
|
-
const env = {
|
|
215
|
-
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "Codex Desktop",
|
|
216
|
-
CODEX_SHELL: "1",
|
|
217
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
218
|
-
PATH: join(fixtureRoot, "missing-path-entry"),
|
|
219
|
-
HOME: localBinHomeDir,
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
// when
|
|
223
|
-
const output = await runSessionStartHook(
|
|
224
|
-
{
|
|
225
|
-
session_id: "session-sparkshell-local-bin",
|
|
226
|
-
transcript_path: null,
|
|
227
|
-
cwd: process.cwd(),
|
|
228
|
-
hook_event_name: "SessionStart",
|
|
229
|
-
model: "gpt-5.5",
|
|
230
|
-
permission_mode: "default",
|
|
231
|
-
source: "startup",
|
|
232
|
-
},
|
|
233
|
-
{ env },
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
// then
|
|
237
|
-
const context = parseAdditionalContext(output);
|
|
238
|
-
expect(context).toContain(`${join(localBinHomeDir, ".local", "bin", "omo")} sparkshell <command>`);
|
|
239
|
-
expect(context).not.toContain("`omo sparkshell <command>`");
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("#given explicit force-on env #when SessionStart runs #then emits Sparkshell guidance", async () => {
|
|
243
|
-
// given
|
|
244
|
-
const env = {
|
|
245
|
-
OMO_SPARKSHELL_AWARENESS: "1",
|
|
246
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// when
|
|
250
|
-
const output = await runSessionStartHook(
|
|
251
|
-
{
|
|
252
|
-
session_id: "session-sparkshell-force-on",
|
|
253
|
-
transcript_path: null,
|
|
254
|
-
cwd: process.cwd(),
|
|
255
|
-
hook_event_name: "SessionStart",
|
|
256
|
-
model: "gpt-5.5",
|
|
257
|
-
permission_mode: "default",
|
|
258
|
-
source: "startup",
|
|
259
|
-
},
|
|
260
|
-
{ env },
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
// then
|
|
264
|
-
expect(parseAdditionalContext(output)).toContain("omo sparkshell <command>");
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it("#given explicit force-off env with active Codex app context #when SessionStart runs #then emits no Sparkshell guidance", async () => {
|
|
268
|
-
// given
|
|
269
|
-
const env = {
|
|
270
|
-
OMO_SPARKSHELL_AWARENESS: "0",
|
|
271
|
-
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "Codex Desktop",
|
|
272
|
-
CODEX_SHELL: "1",
|
|
273
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
// when
|
|
277
|
-
const output = await runSessionStartHook(
|
|
278
|
-
{
|
|
279
|
-
session_id: "session-sparkshell-force-off",
|
|
280
|
-
transcript_path: null,
|
|
281
|
-
cwd: process.cwd(),
|
|
282
|
-
hook_event_name: "SessionStart",
|
|
283
|
-
model: "gpt-5.5",
|
|
284
|
-
permission_mode: "default",
|
|
285
|
-
source: "startup",
|
|
286
|
-
},
|
|
287
|
-
{ env },
|
|
288
|
-
);
|
|
289
|
-
|
|
290
|
-
// then
|
|
291
|
-
expect(output).toBe("");
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it("#given Sparkshell awareness already emitted for a session #when UserPromptSubmit runs #then emits no duplicate guidance", async () => {
|
|
295
|
-
// given
|
|
296
|
-
const pluginDataRoot = mkdtempSync(join(tmpdir(), "codex-sparkshell-awareness-"));
|
|
297
|
-
const env = {
|
|
298
|
-
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "Codex Desktop",
|
|
299
|
-
CODEX_SHELL: "1",
|
|
300
|
-
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
301
|
-
PATH: omoOnPathDir,
|
|
302
|
-
HOME: emptyHomeDir,
|
|
303
|
-
};
|
|
304
|
-
try {
|
|
305
|
-
const firstOutput = await runSessionStartHook(
|
|
306
|
-
{
|
|
307
|
-
session_id: "session-sparkshell-dedupe",
|
|
308
|
-
transcript_path: null,
|
|
309
|
-
cwd: process.cwd(),
|
|
310
|
-
hook_event_name: "SessionStart",
|
|
311
|
-
model: "gpt-5.5",
|
|
312
|
-
permission_mode: "default",
|
|
313
|
-
source: "startup",
|
|
314
|
-
},
|
|
315
|
-
{ env, pluginDataRoot },
|
|
316
|
-
);
|
|
317
|
-
expect(parseAdditionalContext(firstOutput)).toContain("omo sparkshell <command>");
|
|
318
|
-
|
|
319
|
-
// when
|
|
320
|
-
const secondOutput = await runUserPromptSubmitHook(
|
|
321
|
-
{
|
|
322
|
-
session_id: "session-sparkshell-dedupe",
|
|
323
|
-
turn_id: "turn-1",
|
|
324
|
-
transcript_path: null,
|
|
325
|
-
cwd: process.cwd(),
|
|
326
|
-
hook_event_name: "UserPromptSubmit",
|
|
327
|
-
model: "gpt-5.5",
|
|
328
|
-
permission_mode: "default",
|
|
329
|
-
prompt: "continue",
|
|
330
|
-
},
|
|
331
|
-
{ env, pluginDataRoot },
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
// then
|
|
335
|
-
expect(secondOutput).toBe("");
|
|
336
|
-
} finally {
|
|
337
|
-
rmSync(pluginDataRoot, { recursive: true, force: true });
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it("#given explicit force-on env #when hook output is formatted #then awareness remains valid hook JSON", () => {
|
|
342
|
-
// given
|
|
343
|
-
const context = [
|
|
344
|
-
"## Sparkshell Runtime",
|
|
345
|
-
"",
|
|
346
|
-
"- Prefer `omo sparkshell <command>` for shell-native inspection.",
|
|
347
|
-
].join("\n");
|
|
348
|
-
|
|
349
|
-
// when
|
|
350
|
-
const output = formatAdditionalContextOutput("SessionStart", context);
|
|
351
|
-
|
|
352
|
-
// then
|
|
353
|
-
expect(parseAdditionalContext(output)).toContain("## Sparkshell Runtime");
|
|
354
|
-
});
|
|
355
|
-
});
|