jeopi-cli 16.2.13 → 16.2.15
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 +21 -3
- package/README.md +1 -1
- package/dist/cli.js +3087 -3060
- package/dist/types/advisor/watchdog.d.ts +2 -2
- package/dist/types/autolearn/managed-skills.d.ts +2 -2
- package/dist/types/capability/types.d.ts +2 -2
- package/dist/types/cli/commands/migrate-legacy.d.ts +1 -0
- package/dist/types/cli/config-cli.d.ts +1 -1
- package/dist/types/cli/flag-tables.d.ts +1 -1
- package/dist/types/cli/grievances-cli.d.ts +1 -1
- package/dist/types/cli/models-cli.d.ts +1 -1
- package/dist/types/cli/update-cli.d.ts +2 -2
- package/dist/types/cli/worktree-cli.d.ts +1 -1
- package/dist/types/commit/index.d.ts +1 -1
- package/dist/types/commit/pipeline.d.ts +1 -1
- package/dist/types/commit/types.d.ts +1 -1
- package/dist/types/config/prompt-templates.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +16 -6
- package/dist/types/config.d.ts +7 -7
- package/dist/types/discovery/helpers.d.ts +8 -8
- package/dist/types/discovery/omp-extension-roots.d.ts +2 -2
- package/dist/types/eval/py/runtime.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/loader.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +1 -1
- package/dist/types/extensibility/hooks/loader.d.ts +1 -1
- package/dist/types/extensibility/plugins/loader.d.ts +2 -2
- package/dist/types/extensibility/plugins/types.d.ts +13 -3
- package/dist/types/lsp/config.d.ts +2 -2
- package/dist/types/mcp/client.d.ts +1 -1
- package/dist/types/mcp/config-writer.d.ts +1 -1
- package/dist/types/mcp/oauth-flow.d.ts +1 -1
- package/dist/types/mcp/transports/http.d.ts +1 -1
- package/dist/types/memories/okf.d.ts +36 -0
- package/dist/types/modes/components/__tests__/assistant-agent-label.test.d.ts +1 -0
- package/dist/types/modes/components/assistant-message.d.ts +4 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/sdk.d.ts +5 -5
- package/dist/types/session/agent-session.d.ts +3 -0
- package/dist/types/session/auth-broker-config.d.ts +4 -4
- package/dist/types/session/snapcompact-savings-journal.d.ts +1 -1
- package/dist/types/task/commands.d.ts +1 -1
- package/dist/types/task/critic-gate.d.ts +76 -0
- package/dist/types/task/critic-gate.test.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +1 -1
- package/dist/types/task/executor.d.ts +1 -1
- package/dist/types/tools/acp-bridge.d.ts +2 -2
- package/dist/types/tools/browser/tab-protocol.d.ts +1 -1
- package/dist/types/tools/index.d.ts +6 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -0
- package/dist/types/tools/report-tool-issue.d.ts +1 -1
- package/dist/types/utils/changelog.d.ts +2 -2
- package/dist/types/utils/image-loading.d.ts +4 -4
- package/dist/types/utils/image-resize.d.ts +1 -1
- package/dist/types/web/search/index.d.ts +1 -1
- package/examples/custom-tools/README.md +1 -1
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/plan-mode.ts +1 -1
- package/examples/extensions/tools.ts +1 -1
- package/examples/hooks/README.md +1 -1
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/06-extensions.ts +3 -3
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/examples/sdk/12-redis-sessions.ts +1 -1
- package/examples/sdk/13-sql-sessions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/package.json +12 -12
- package/scripts/jeopi +1 -1
- package/src/advisor/__tests__/config.test.ts +4 -4
- package/src/advisor/watchdog.ts +8 -8
- package/src/autolearn/managed-skills.ts +4 -4
- package/src/autoresearch/storage.ts +2 -2
- package/src/capability/types.ts +2 -2
- package/src/cli/agents-cli.ts +2 -2
- package/src/cli/args.ts +3 -3
- package/src/cli/auth-broker-cli.ts +5 -5
- package/src/cli/auth-gateway-cli.ts +4 -4
- package/src/cli/claude-trace-cli.ts +1 -1
- package/src/cli/commands/migrate-legacy.ts +24 -0
- package/src/cli/config-cli.ts +20 -5
- package/src/cli/flag-tables.ts +1 -1
- package/src/cli/gallery-fixtures/misc.ts +7 -7
- package/src/cli/gallery-screenshot.ts +2 -2
- package/src/cli/grievances-cli.ts +1 -1
- package/src/cli/models-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +2 -2
- package/src/cli/profile-alias.ts +8 -8
- package/src/cli/read-cli.ts +1 -1
- package/src/cli/ssh-cli.ts +2 -2
- package/src/cli/update-cli.ts +27 -27
- package/src/cli/worktree-cli.ts +3 -3
- package/src/cli.ts +5 -5
- package/src/commands/agents.ts +2 -2
- package/src/commands/auth-broker.ts +1 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/dry-balance.ts +6 -6
- package/src/commands/launch.ts +2 -2
- package/src/commands/models.ts +1 -1
- package/src/commands/say.ts +1 -1
- package/src/commands/token.ts +5 -5
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/worktree.ts +3 -3
- package/src/commit/agentic/prompts/system.md +1 -1
- package/src/commit/cli.ts +1 -1
- package/src/commit/index.ts +1 -1
- package/src/commit/pipeline.ts +1 -1
- package/src/commit/types.ts +1 -1
- package/src/config/mcp-schema.json +5 -5
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/prompt-templates.ts +2 -2
- package/src/config/settings-schema.ts +20 -8
- package/src/config/settings.ts +1 -1
- package/src/config.ts +10 -10
- package/src/dap/session.ts +2 -2
- package/src/debug/protocol-probe.ts +1 -1
- package/src/debug/raw-sse-buffer.ts +3 -3
- package/src/debug/raw-sse.ts +2 -2
- package/src/discovery/builtin.ts +9 -9
- package/src/discovery/helpers.ts +13 -13
- package/src/discovery/omp-extension-roots.ts +4 -4
- package/src/discovery/omp-plugins.ts +2 -2
- package/src/discovery/substitute-plugin-root.ts +3 -3
- package/src/eval/py/runtime.ts +1 -1
- package/src/eval/rb/runner.rb +6 -6
- package/src/export/custom-share.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/extensions/loader.ts +4 -4
- package/src/extensibility/hooks/loader.ts +1 -1
- package/src/extensibility/plugins/loader.ts +16 -10
- package/src/extensibility/plugins/manager.ts +10 -9
- package/src/extensibility/plugins/types.ts +15 -4
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/lsp/config.ts +4 -4
- package/src/lsp/index.ts +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/client.ts +1 -1
- package/src/mcp/config-writer.ts +2 -2
- package/src/mcp/manager.ts +1 -1
- package/src/mcp/oauth-flow.ts +1 -1
- package/src/mcp/timeout.ts +2 -2
- package/src/mcp/transports/http.ts +1 -1
- package/src/memories/index.ts +82 -11
- package/src/memories/okf.ts +129 -0
- package/src/modes/components/__tests__/assistant-agent-label.test.ts +87 -0
- package/src/modes/components/assistant-message.ts +27 -0
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/controllers/mcp-command-controller.ts +2 -2
- package/src/modes/controllers/omfg-controller.ts +2 -2
- package/src/modes/controllers/ssh-command-controller.ts +2 -2
- package/src/modes/interactive-mode.ts +3 -1
- package/src/modes/setup-wizard/index.ts +1 -1
- package/src/prompts/agents/jeo.md +20 -10
- package/src/prompts/system/autolearn-guidance.md +2 -2
- package/src/prompts/system/system-prompt.md +10 -1
- package/src/prompts/tools/learn.md +1 -1
- package/src/prompts/tools/manage-skill.md +1 -1
- package/src/sdk.ts +8 -6
- package/src/secrets/index.ts +2 -2
- package/src/session/agent-session.ts +86 -6
- package/src/session/auth-broker-config.ts +6 -6
- package/src/session/snapcompact-savings-journal.ts +1 -1
- package/src/slash-commands/helpers/mcp.ts +1 -1
- package/src/system-prompt.test.ts +14 -14
- package/src/task/commands.ts +1 -1
- package/src/task/critic-gate.test.ts +181 -0
- package/src/task/critic-gate.ts +148 -0
- package/src/task/discovery.ts +5 -5
- package/src/task/executor.ts +1 -1
- package/src/task/index.ts +35 -2
- package/src/tools/acp-bridge.ts +2 -2
- package/src/tools/browser/launch.ts +1 -1
- package/src/tools/browser/tab-protocol.ts +1 -1
- package/src/tools/grep.ts +2 -2
- package/src/tools/index.ts +6 -1
- package/src/tools/plan-mode-guard.ts +16 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/report-tool-issue.ts +2 -2
- package/src/utils/changelog.ts +2 -2
- package/src/utils/image-loading.ts +4 -4
- package/src/utils/image-resize.ts +5 -5
- package/src/web/search/index.ts +1 -1
package/src/commands/config.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Args, Command, Flags } from "jeopi-utils/cli";
|
|
|
5
5
|
import { type ConfigAction, type ConfigCommandArgs, runConfigCommand } from "../cli/config-cli";
|
|
6
6
|
import { initTheme } from "../modes/theme/theme";
|
|
7
7
|
|
|
8
|
-
const ACTIONS: ConfigAction[] = ["list", "get", "set", "reset", "path", "init-xdg"];
|
|
8
|
+
const ACTIONS: ConfigAction[] = ["list", "get", "set", "reset", "path", "init-xdg", "migrate-legacy"];
|
|
9
9
|
|
|
10
10
|
export default class Config extends Command {
|
|
11
11
|
static description = "Manage configuration settings";
|
|
@@ -12,7 +12,7 @@ export default class DryBalance extends Command {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
static flags = {
|
|
15
|
-
model: Flags.string({ description: "Model selector (same syntax as --model on
|
|
15
|
+
model: Flags.string({ description: "Model selector (same syntax as --model on jeopi)" }),
|
|
16
16
|
count: Flags.integer({ description: "Number of random session ids to try", default: 100 }),
|
|
17
17
|
concurrency: Flags.integer({ description: "Maximum concurrent credential resolutions", default: 32 }),
|
|
18
18
|
json: Flags.boolean({ description: "Output JSON" }),
|
|
@@ -20,11 +20,11 @@ export default class DryBalance extends Command {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
static examples = [
|
|
23
|
-
"# Dry-run the configured default model with 100 random session ids\n
|
|
24
|
-
"# Dry-run a specific model\n
|
|
25
|
-
"# Larger run with bounded concurrency\n
|
|
26
|
-
"# Benchmark every OAuth account in parallel\n
|
|
27
|
-
"# Machine-readable output\n
|
|
23
|
+
"# Dry-run the configured default model with 100 random session ids\n jeopi dry-balance",
|
|
24
|
+
"# Dry-run a specific model\n jeopi dry-balance anthropic/claude-sonnet-4-5",
|
|
25
|
+
"# Larger run with bounded concurrency\n jeopi dry-balance --model openai-codex/gpt-5-codex --count 1000 --concurrency 64",
|
|
26
|
+
"# Benchmark every OAuth account in parallel\n jeopi dry-balance --bench",
|
|
27
|
+
"# Machine-readable output\n jeopi dry-balance --json",
|
|
28
28
|
];
|
|
29
29
|
|
|
30
30
|
async run(): Promise<void> {
|
package/src/commands/launch.ts
CHANGED
|
@@ -166,10 +166,10 @@ export default class Index extends Command {
|
|
|
166
166
|
`# Include files in initial message\n ${APP_NAME} @prompt.md @image.png "What color is the sky?"`,
|
|
167
167
|
`# Non-interactive mode (process and exit)\n ${APP_NAME} -p "List all .ts files in src/"`,
|
|
168
168
|
`# Continue previous session\n ${APP_NAME} --continue "What did we discuss?"`,
|
|
169
|
-
`# Create a shell shortcut for a work profile\n ${APP_NAME} --profile work --alias
|
|
169
|
+
`# Create a shell shortcut for a work profile\n ${APP_NAME} --profile work --alias jeopi-work`,
|
|
170
170
|
`# Use different model (fuzzy matching)\n ${APP_NAME} --model opus "Help me refactor this code"`,
|
|
171
171
|
`# Limit model cycling to specific models\n ${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o`,
|
|
172
|
-
`# Export a session file to HTML\n ${APP_NAME} --export ~/.
|
|
172
|
+
`# Export a session file to HTML\n ${APP_NAME} --export ~/.jeopi/agent/sessions/--path--/session.jsonl`,
|
|
173
173
|
];
|
|
174
174
|
|
|
175
175
|
static strict = false;
|
package/src/commands/models.ts
CHANGED
|
@@ -39,7 +39,7 @@ export default class Models extends Command {
|
|
|
39
39
|
`# List every available model, grouped by provider\n ${APP_NAME} models`,
|
|
40
40
|
`# List one provider's models (any provider name works)\n ${APP_NAME} models openai-codex`,
|
|
41
41
|
`# Find models by substring\n ${APP_NAME} models find minimax`,
|
|
42
|
-
`# Force a fresh catalog fetch (replaces rm -rf ~/.
|
|
42
|
+
`# Force a fresh catalog fetch (replaces rm -rf ~/.jeopi/models.db)\n ${APP_NAME} models refresh`,
|
|
43
43
|
`# Machine-readable output\n ${APP_NAME} models --json`,
|
|
44
44
|
];
|
|
45
45
|
|
package/src/commands/say.ts
CHANGED
|
@@ -79,7 +79,7 @@ export default class Say extends Command {
|
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
const tmp = path.join(os.tmpdir(), `
|
|
82
|
+
const tmp = path.join(os.tmpdir(), `jeopi-say-${Snowflake.next()}.wav`);
|
|
83
83
|
await Bun.write(tmp, wav);
|
|
84
84
|
try {
|
|
85
85
|
await playAudioFile(tmp);
|
package/src/commands/token.ts
CHANGED
|
@@ -40,11 +40,11 @@ export default class Token extends Command {
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
static examples = [
|
|
43
|
-
"# Get API key for Anthropic\n
|
|
44
|
-
"# Get raw Copilot credential JSON\n
|
|
45
|
-
"# Force refresh and get Gemini CLI token\n
|
|
46
|
-
"# List Anthropic OAuth accounts\n
|
|
47
|
-
"# Get the 2nd Anthropic OAuth account's token\n
|
|
43
|
+
"# Get API key for Anthropic\n jeopi token anthropic",
|
|
44
|
+
"# Get raw Copilot credential JSON\n jeopi token github-copilot --raw",
|
|
45
|
+
"# Force refresh and get Gemini CLI token\n jeopi token google-gemini-cli --force-refresh",
|
|
46
|
+
"# List Anthropic OAuth accounts\n jeopi token anthropic --list",
|
|
47
|
+
"# Get the 2nd Anthropic OAuth account's token\n jeopi token anthropic --account 2",
|
|
48
48
|
];
|
|
49
49
|
|
|
50
50
|
async run(): Promise<void> {
|
package/src/commands/ttsr.ts
CHANGED
|
@@ -64,12 +64,12 @@ export default class Ttsr extends Command {
|
|
|
64
64
|
"jeopi ttsr test src/foo.ts",
|
|
65
65
|
"jeopi ttsr test --file src/foo.ts",
|
|
66
66
|
"jeopi ttsr test --file src/foo.ts --source text",
|
|
67
|
-
"jeopi ttsr test --rule .
|
|
67
|
+
"jeopi ttsr test --rule .jeopi/rules/no-any.md --source tool --path src/foo.ts 'const x: any = 1'",
|
|
68
68
|
"echo 'Box::leak(&mut v)' | jeopi ttsr test --file - --path src/lib.rs",
|
|
69
69
|
"jeopi ttsr test --source tool --tool edit --path src/foo.ts 'const x: any = 1'",
|
|
70
70
|
"jeopi ttsr scan",
|
|
71
71
|
"jeopi ttsr scan src/",
|
|
72
|
-
"jeopi ttsr scan -r .
|
|
72
|
+
"jeopi ttsr scan -r .jeopi/rules/no-any.md src/",
|
|
73
73
|
];
|
|
74
74
|
|
|
75
75
|
async run(): Promise<void> {
|
package/src/commands/worktree.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* List and clean up agent-managed git worktrees under `~/.
|
|
2
|
+
* List and clean up agent-managed git worktrees under `~/.jeopi/wt`.
|
|
3
3
|
*/
|
|
4
4
|
import { getProjectDir } from "jeopi-utils";
|
|
5
5
|
import { Args, Command, Flags } from "jeopi-utils/cli";
|
|
@@ -7,7 +7,7 @@ import { clearWorktrees, listWorktrees } from "../cli/worktree-cli";
|
|
|
7
7
|
import { Settings } from "../config/settings";
|
|
8
8
|
|
|
9
9
|
export default class Worktree extends Command {
|
|
10
|
-
static description = "List or clear agent-managed git worktrees (~/.
|
|
10
|
+
static description = "List or clear agent-managed git worktrees (~/.jeopi/wt)";
|
|
11
11
|
|
|
12
12
|
static aliases = ["wt"];
|
|
13
13
|
|
|
@@ -46,7 +46,7 @@ export default class Worktree extends Command {
|
|
|
46
46
|
async run(): Promise<void> {
|
|
47
47
|
const { args, flags } = await this.parse(Worktree);
|
|
48
48
|
// Load settings so the `worktree.base` override is applied before we scan
|
|
49
|
-
// — otherwise this command would inspect ~/.
|
|
49
|
+
// — otherwise this command would inspect ~/.jeopi/wt while the agent created
|
|
50
50
|
// its worktrees under the configured base.
|
|
51
51
|
await Settings.init({ cwd: getProjectDir() });
|
|
52
52
|
if (args.action === "clear") {
|
package/src/commit/cli.ts
CHANGED
|
@@ -70,7 +70,7 @@ export function parseCommitArgs(args: string[]): CommitCommandArgs | undefined {
|
|
|
70
70
|
export function printCommitHelp(): void {
|
|
71
71
|
const lines = [
|
|
72
72
|
"Usage:",
|
|
73
|
-
"
|
|
73
|
+
" jeopi commit [options]",
|
|
74
74
|
"",
|
|
75
75
|
"Options:",
|
|
76
76
|
" --push Push after committing",
|
package/src/commit/index.ts
CHANGED
package/src/commit/pipeline.ts
CHANGED
|
@@ -29,7 +29,7 @@ let typesDescription: string | undefined;
|
|
|
29
29
|
const TYPES_DESCRIPTION = (): string => (typesDescription ??= prompt.render(typesDescriptionPrompt));
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Execute the
|
|
32
|
+
* Execute the jeopi commit pipeline for staged changes.
|
|
33
33
|
*/
|
|
34
34
|
export async function runCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
35
35
|
if (args.legacy) {
|
package/src/commit/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://raw.githubusercontent.com/
|
|
4
|
-
"title": "
|
|
5
|
-
"description": "Schema for mcp.json, .mcp.json, .
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/akillness/jeopi/main/packages/coding-agent/src/config/mcp-schema.json",
|
|
4
|
+
"title": "jeopi MCP configuration",
|
|
5
|
+
"description": "Schema for mcp.json, .mcp.json, .jeopi/mcp.json, and ~/.jeopi/agent/mcp.json used by jeopi.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"properties": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"type": {
|
|
55
55
|
"type": "string",
|
|
56
56
|
"enum": ["oauth", "apikey"],
|
|
57
|
-
"description": "Auth strategy understood by
|
|
57
|
+
"description": "Auth strategy understood by jeopi."
|
|
58
58
|
},
|
|
59
59
|
"credentialId": {
|
|
60
60
|
"type": "string",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"properties": {
|
|
112
112
|
"enabled": {
|
|
113
113
|
"type": "boolean",
|
|
114
|
-
"description": "Whether
|
|
114
|
+
"description": "Whether jeopi should try to connect this server."
|
|
115
115
|
},
|
|
116
116
|
"timeout": {
|
|
117
117
|
"type": "number",
|
|
@@ -269,7 +269,7 @@ const ProviderConfigSchema = type({
|
|
|
269
269
|
"modelOverrides?": { "[string]": ModelOverrideSchema },
|
|
270
270
|
"disableStrictTools?": "boolean",
|
|
271
271
|
/**
|
|
272
|
-
* Streaming transport override. When set to `"pi-native"`,
|
|
272
|
+
* Streaming transport override. When set to `"pi-native"`, jeopi dispatches
|
|
273
273
|
* every model under this provider via the auth-gateway's
|
|
274
274
|
* `POST /v1/pi/stream` endpoint instead of the per-provider SDK. The
|
|
275
275
|
* provider's `baseUrl` must point at a compatible `jeopi auth-gateway`
|
|
@@ -153,7 +153,7 @@ export interface LoadPromptTemplatesOptions {
|
|
|
153
153
|
/**
|
|
154
154
|
* Load all prompt templates from:
|
|
155
155
|
* 1. Global: agentDir/prompts/
|
|
156
|
-
* 2. Project: cwd/.
|
|
156
|
+
* 2. Project: cwd/.jeopi/prompts/
|
|
157
157
|
*/
|
|
158
158
|
export async function loadPromptTemplates(options: LoadPromptTemplatesOptions = {}): Promise<PromptTemplate[]> {
|
|
159
159
|
const resolvedCwd = options.cwd ?? getProjectDir();
|
|
@@ -166,7 +166,7 @@ export async function loadPromptTemplates(options: LoadPromptTemplatesOptions =
|
|
|
166
166
|
const globalPromptsDir = options.agentDir ? path.join(options.agentDir, "prompts") : resolvedAgentDir;
|
|
167
167
|
templates.push(...(await loadTemplatesFromDir(globalPromptsDir, "user")));
|
|
168
168
|
|
|
169
|
-
// 2. Load project templates from cwd/.
|
|
169
|
+
// 2. Load project templates from cwd/.jeopi/prompts/
|
|
170
170
|
const projectPromptsDir = getProjectPromptsDir(resolvedCwd);
|
|
171
171
|
templates.push(...(await loadTemplatesFromDir(projectPromptsDir, "project")));
|
|
172
172
|
|
|
@@ -341,7 +341,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
341
341
|
|
|
342
342
|
// Auth broker — credentials proxied through a remote `jeopi auth-broker serve`
|
|
343
343
|
// host. Hidden from the UI; populate via env vars or hand-edited config.yml.
|
|
344
|
-
// Env (`
|
|
344
|
+
// Env (`JEOPI_AUTH_BROKER_URL` / `JEOPI_AUTH_BROKER_TOKEN`) takes precedence so
|
|
345
345
|
// per-machine overrides remain trivial.
|
|
346
346
|
"auth.broker.url": { type: "string", default: undefined },
|
|
347
347
|
"auth.broker.token": { type: "string", default: undefined },
|
|
@@ -473,7 +473,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
473
473
|
group: "Services",
|
|
474
474
|
label: "Max In-Flight Requests",
|
|
475
475
|
description:
|
|
476
|
-
'Maximum concurrent LLM requests per provider id (for example "openai" or "anthropic"), shared across local
|
|
476
|
+
'Maximum concurrent LLM requests per provider id (for example "openai" or "anthropic"), shared across local jeopi processes with this config root. Omitted providers are unlimited.',
|
|
477
477
|
},
|
|
478
478
|
},
|
|
479
479
|
|
|
@@ -950,6 +950,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
950
950
|
},
|
|
951
951
|
},
|
|
952
952
|
|
|
953
|
+
"ui.agentLabel": {
|
|
954
|
+
type: "string",
|
|
955
|
+
default: "jeo",
|
|
956
|
+
ui: {
|
|
957
|
+
tab: "appearance",
|
|
958
|
+
group: "Display",
|
|
959
|
+
label: "Agent Label",
|
|
960
|
+
description:
|
|
961
|
+
"Identity name led on its own line before assistant replies and thinking blocks (jeo-code style). Empty disables the label",
|
|
962
|
+
},
|
|
963
|
+
},
|
|
953
964
|
hideThinkingBlock: {
|
|
954
965
|
type: "boolean",
|
|
955
966
|
default: false,
|
|
@@ -2366,7 +2377,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2366
2377
|
},
|
|
2367
2378
|
|
|
2368
2379
|
// Auto-Learn (experimental): post-stop nudge to capture lessons to memory
|
|
2369
|
-
// and mint/enhance isolated managed skills under ~/.
|
|
2380
|
+
// and mint/enhance isolated managed skills under ~/.jeopi/agent/managed-skills.
|
|
2370
2381
|
// Master flag is default-off → zero footprint; sub-flags gate behaviour.
|
|
2371
2382
|
"autolearn.enabled": {
|
|
2372
2383
|
type: "boolean",
|
|
@@ -2742,7 +2753,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
2742
2753
|
},
|
|
2743
2754
|
"hindsight.retainEveryNTurns": { type: "number", default: 3 },
|
|
2744
2755
|
"hindsight.retainOverlapTurns": { type: "number", default: 2 },
|
|
2745
|
-
"hindsight.retainContext": { type: "string", default: "
|
|
2756
|
+
"hindsight.retainContext": { type: "string", default: "jeopi" },
|
|
2746
2757
|
|
|
2747
2758
|
"hindsight.recallBudget": {
|
|
2748
2759
|
type: "enum",
|
|
@@ -3591,7 +3602,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
3591
3602
|
tab: "tools",
|
|
3592
3603
|
group: "GitHub",
|
|
3593
3604
|
label: "GitHub View Cache",
|
|
3594
|
-
description:
|
|
3605
|
+
description:
|
|
3606
|
+
"Cache rendered issue/PR view output in ~/.jeopi/cache/github-cache.db so repeated reads are free",
|
|
3595
3607
|
},
|
|
3596
3608
|
},
|
|
3597
3609
|
|
|
@@ -4022,7 +4034,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
4022
4034
|
group: "Isolation",
|
|
4023
4035
|
label: "Worktree Base Directory",
|
|
4024
4036
|
description:
|
|
4025
|
-
"Base directory for agent-managed worktrees — task-isolation copies, `github` PR checkouts, and `jeopi worktree` cleanup all live here. Unset uses ~/.
|
|
4037
|
+
"Base directory for agent-managed worktrees — task-isolation copies, `github` PR checkouts, and `jeopi worktree` cleanup all live here. Unset uses ~/.jeopi/wt. Must be an absolute or ~-relative path; relative paths are ignored. The JEOPI_WORKTREE_DIR env var overrides this.",
|
|
4026
4038
|
},
|
|
4027
4039
|
},
|
|
4028
4040
|
|
|
@@ -4888,12 +4900,12 @@ export const SETTINGS_SCHEMA = {
|
|
|
4888
4900
|
|
|
4889
4901
|
"dev.autoqaPush.endpoint": {
|
|
4890
4902
|
type: "string",
|
|
4891
|
-
default: "https://qa.
|
|
4903
|
+
default: "https://qa.jeopi.sh/v1/grievances" as const,
|
|
4892
4904
|
ui: {
|
|
4893
4905
|
tab: "tools",
|
|
4894
4906
|
group: "Developer",
|
|
4895
4907
|
label: "Auto QA Push Endpoint",
|
|
4896
|
-
description: "Full URL receiving Auto QA JSON reports (default https://qa.
|
|
4908
|
+
description: "Full URL receiving Auto QA JSON reports (default https://qa.jeopi.sh/v1/grievances)",
|
|
4897
4909
|
},
|
|
4898
4910
|
},
|
|
4899
4911
|
|
package/src/config/settings.ts
CHANGED
package/src/config.ts
CHANGED
|
@@ -49,8 +49,8 @@ export function walkUpForPackageDir(startDir: string): string | undefined {
|
|
|
49
49
|
*
|
|
50
50
|
* Callers MUST treat `undefined` as "no package assets available" and skip the
|
|
51
51
|
* lookup. NEVER fall back to the user's `cwd` here: that conflates the host
|
|
52
|
-
* project with
|
|
53
|
-
* project's `CHANGELOG.md` rendered as
|
|
52
|
+
* project with jeopi's own assets and was the source of issue #1423 (the host
|
|
53
|
+
* project's `CHANGELOG.md` rendered as jeopi's startup changelog).
|
|
54
54
|
*/
|
|
55
55
|
export function getPackageDir(): string | undefined {
|
|
56
56
|
const envDir = process.env.PI_PACKAGE_DIR;
|
|
@@ -61,7 +61,7 @@ export function getPackageDir(): string | undefined {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
* Path to
|
|
64
|
+
* Path to jeopi's own `CHANGELOG.md`, or `undefined` when the package directory
|
|
65
65
|
* cannot be resolved (e.g. inside `bun --compile` binaries that don't bundle
|
|
66
66
|
* package assets). Callers MUST skip changelog parsing when this is undefined;
|
|
67
67
|
* see issue #1423.
|
|
@@ -77,8 +77,8 @@ export function getChangelogPath(): string | undefined {
|
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Config directory bases in priority order (highest first).
|
|
80
|
-
* User-level: ~/.
|
|
81
|
-
* Project-level: .
|
|
80
|
+
* User-level: ~/.jeopi/agent, ~/.claude, ~/.codex, ~/.gemini
|
|
81
|
+
* Project-level: .jeopi, .claude, .codex, .gemini
|
|
82
82
|
*/
|
|
83
83
|
const USER_CONFIG_BASES = priorityList.map(({ dir, globalAgentDir }) => ({
|
|
84
84
|
base: () => path.join(os.homedir(), globalAgentDir ? globalAgentDir() : dir),
|
|
@@ -92,14 +92,14 @@ const PROJECT_CONFIG_BASES = priorityList.map(({ dir }) => ({
|
|
|
92
92
|
|
|
93
93
|
export interface ConfigDirEntry {
|
|
94
94
|
path: string;
|
|
95
|
-
source: string; // e.g., ".
|
|
95
|
+
source: string; // e.g., ".jeopi", ".claude"
|
|
96
96
|
level: "user" | "project";
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
export interface GetConfigDirsOptions {
|
|
100
|
-
/** Include user-level directories (~/.
|
|
100
|
+
/** Include user-level directories (~/.jeopi/agent/...). Default: true */
|
|
101
101
|
user?: boolean;
|
|
102
|
-
/** Include project-level directories (.
|
|
102
|
+
/** Include project-level directories (.jeopi/...). Default: true */
|
|
103
103
|
project?: boolean;
|
|
104
104
|
/** Current working directory for project paths. Default: getProjectDir() */
|
|
105
105
|
cwd?: string;
|
|
@@ -117,7 +117,7 @@ export interface GetConfigDirsOptions {
|
|
|
117
117
|
* @example
|
|
118
118
|
* // Get all command directories
|
|
119
119
|
* getConfigDirs("commands")
|
|
120
|
-
* // → [{ path: "~/.
|
|
120
|
+
* // → [{ path: "~/.jeopi/agent/commands", source: ".jeopi", level: "user" }, ...]
|
|
121
121
|
*
|
|
122
122
|
* @example
|
|
123
123
|
* // Get only existing project skill directories
|
|
@@ -207,7 +207,7 @@ export function findConfigFileWithMeta(
|
|
|
207
207
|
|
|
208
208
|
/**
|
|
209
209
|
* Find all nearest config directories by walking up from cwd.
|
|
210
|
-
* Returns one entry per config base (.
|
|
210
|
+
* Returns one entry per config base (.jeopi, .claude) - the nearest one found.
|
|
211
211
|
* Results are in priority order (highest first).
|
|
212
212
|
*/
|
|
213
213
|
export function findAllNearestProjectConfigDirs(subpath: string, cwd: string = getProjectDir()): ConfigDirEntry[] {
|
package/src/dap/session.ts
CHANGED
|
@@ -1153,8 +1153,8 @@ export class DapSessionManager {
|
|
|
1153
1153
|
|
|
1154
1154
|
#buildInitializeArguments(adapter: DapResolvedAdapter): DapInitializeArguments {
|
|
1155
1155
|
return {
|
|
1156
|
-
clientID: "
|
|
1157
|
-
clientName: "
|
|
1156
|
+
clientID: "jeopi",
|
|
1157
|
+
clientName: "jeopi",
|
|
1158
1158
|
adapterID: adapter.name,
|
|
1159
1159
|
locale: "en-US",
|
|
1160
1160
|
linesStartAt1: true,
|
|
@@ -217,7 +217,7 @@ export class ProtocolProbeComponent extends Container {
|
|
|
217
217
|
new Text(
|
|
218
218
|
[
|
|
219
219
|
`${theme.fg("muted", "Hyperlinks (OSC 8)")} — ${yesNo(hyperlinksOn)}`,
|
|
220
|
-
` \x1b]8;;https://github.com/
|
|
220
|
+
` \x1b]8;;https://github.com/akillness/jeopi\x07jeopi repo\x1b]8;;\x07`,
|
|
221
221
|
].join("\n"),
|
|
222
222
|
1,
|
|
223
223
|
0,
|
|
@@ -77,7 +77,7 @@ function trimRawLines(raw: string[]): TrimResult {
|
|
|
77
77
|
chars += slice.length + 1;
|
|
78
78
|
remaining = 0;
|
|
79
79
|
}
|
|
80
|
-
const tail = `:
|
|
80
|
+
const tail = `: jeopi-debug-truncated originalChars=${originalChars}`;
|
|
81
81
|
trimmed.push(tail);
|
|
82
82
|
chars += tail.length + 1;
|
|
83
83
|
return { raw: trimmed, truncated: true, originalChars, chars };
|
|
@@ -89,7 +89,7 @@ export function formatRawSseIsoTime(timestamp: number): string {
|
|
|
89
89
|
|
|
90
90
|
export function formatRawSseResponseComment(record: Extract<RawSseDebugRecord, { kind: "response" }>): string {
|
|
91
91
|
const fields = [
|
|
92
|
-
"
|
|
92
|
+
"jeopi-response",
|
|
93
93
|
`ts=${formatRawSseIsoTime(record.timestamp)}`,
|
|
94
94
|
`status=${record.status}`,
|
|
95
95
|
record.provider ? `provider=${record.provider}` : undefined,
|
|
@@ -204,7 +204,7 @@ export class RawSseDebugBuffer {
|
|
|
204
204
|
const live = this.#head === 0 ? this.#records : this.#records.slice(this.#head);
|
|
205
205
|
const body = live.map(rawRecordText).join("\n");
|
|
206
206
|
if (this.#droppedRecords === 0) return body;
|
|
207
|
-
const dropped = `:
|
|
207
|
+
const dropped = `: jeopi-debug-dropped records=${this.#droppedRecords} chars=${this.#droppedChars}\n\n`;
|
|
208
208
|
return body.length > 0 ? `${dropped}${body}` : dropped;
|
|
209
209
|
}
|
|
210
210
|
|
package/src/debug/raw-sse.ts
CHANGED
|
@@ -222,7 +222,7 @@ export class RawSseViewerComponent implements Component {
|
|
|
222
222
|
lines.push(
|
|
223
223
|
theme.fg(
|
|
224
224
|
"warning",
|
|
225
|
-
`:
|
|
225
|
+
`: jeopi-debug-dropped records=${snapshot.droppedRecords} chars=${snapshot.droppedChars}`,
|
|
226
226
|
),
|
|
227
227
|
);
|
|
228
228
|
lines.push("");
|
|
@@ -233,7 +233,7 @@ export class RawSseViewerComponent implements Component {
|
|
|
233
233
|
lines.push(sanitizeFrameLine(line, innerWidth));
|
|
234
234
|
}
|
|
235
235
|
if (record.kind === "event" && record.truncated) {
|
|
236
|
-
lines.push(theme.fg("warning", `:
|
|
236
|
+
lines.push(theme.fg("warning", `: jeopi-debug-event-truncated originalChars=${record.originalChars}`));
|
|
237
237
|
}
|
|
238
238
|
lines.push("");
|
|
239
239
|
}
|
package/src/discovery/builtin.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Builtin Provider (.
|
|
2
|
+
* Builtin Provider (.jeopi)
|
|
3
3
|
*
|
|
4
4
|
* Primary provider for OMP native configs. Supports all capabilities.
|
|
5
5
|
*/
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
|
|
38
38
|
const PROVIDER_ID = "native";
|
|
39
39
|
const DISPLAY_NAME = "OMP";
|
|
40
|
-
const DESCRIPTION = "Native OMP configuration from ~/.
|
|
40
|
+
const DESCRIPTION = "Native OMP configuration from ~/.jeopi and .jeopi/";
|
|
41
41
|
const PRIORITY = 100;
|
|
42
42
|
|
|
43
43
|
const PATHS = SOURCE_PATHS.native;
|
|
@@ -62,7 +62,7 @@ async function getConfigDirs(ctx: LoadContext): Promise<Array<{ dir: string; lev
|
|
|
62
62
|
result.push({ dir: projectDir, level: "project" });
|
|
63
63
|
}
|
|
64
64
|
// Native user config is profile-scoped: getAgentDir() points at the active
|
|
65
|
-
// profile's agent dir (~/.
|
|
65
|
+
// profile's agent dir (~/.jeopi/profiles/<name>/agent), like sessions and MCP.
|
|
66
66
|
const userDir = await ifNonEmptyDir(getAgentDir());
|
|
67
67
|
if (userDir) {
|
|
68
68
|
result.push({ dir: userDir, level: "user" });
|
|
@@ -270,7 +270,7 @@ registerProvider<SystemPrompt>(systemPromptCapability.id, {
|
|
|
270
270
|
|
|
271
271
|
// Skills
|
|
272
272
|
async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
273
|
-
// Walk up from cwd finding .
|
|
273
|
+
// Walk up from cwd finding .jeopi/skills/ in ancestors (closest first)
|
|
274
274
|
const ancestors = getAncestorDirs(ctx.cwd, ctx.repoRoot ?? ctx.home);
|
|
275
275
|
const projectScans = ancestors.map(({ dir }) =>
|
|
276
276
|
scanSkillsFromDir(ctx, {
|
|
@@ -281,7 +281,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
281
281
|
}),
|
|
282
282
|
);
|
|
283
283
|
|
|
284
|
-
// User-level scan from ~/.
|
|
284
|
+
// User-level scan from ~/.jeopi/agent/skills/
|
|
285
285
|
const userScan = scanSkillsFromDir(ctx, {
|
|
286
286
|
dir: path.join(getAgentDir(), "skills"),
|
|
287
287
|
providerId: PROVIDER_ID,
|
|
@@ -321,7 +321,7 @@ registerProvider<Skill>(skillCapability.id, {
|
|
|
321
321
|
registerProvider<Skill>(skillCapability.id, {
|
|
322
322
|
id: MANAGED_SKILLS_PROVIDER_ID,
|
|
323
323
|
displayName: "Managed Skills (auto-learn)",
|
|
324
|
-
description: "Auto-generated managed skills from ~/.
|
|
324
|
+
description: "Auto-generated managed skills from ~/.jeopi/agent/managed-skills",
|
|
325
325
|
priority: MANAGED_SKILLS_PRIORITY,
|
|
326
326
|
load: loadManagedSkills,
|
|
327
327
|
});
|
|
@@ -377,8 +377,8 @@ async function loadRules(ctx: LoadContext): Promise<LoadResult<Rule>> {
|
|
|
377
377
|
// Top-level RULES.md is a sticky always-apply rule. Documented in
|
|
378
378
|
// https://omp.sh/docs/context-files as the file that gets "re-injected near
|
|
379
379
|
// the current turn so they keep hold across long conversations".
|
|
380
|
-
// User scope: ~/.
|
|
381
|
-
// Project scope: nearest .
|
|
380
|
+
// User scope: ~/.jeopi/agent/RULES.md
|
|
381
|
+
// Project scope: nearest .jeopi/RULES.md walking up from cwd to repoRoot
|
|
382
382
|
const userRulesFile = path.join(getAgentDir(), "RULES.md");
|
|
383
383
|
const userRule = await loadStickyRulesFile(userRulesFile, "user");
|
|
384
384
|
if (userRule) items.push(userRule);
|
|
@@ -928,7 +928,7 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
|
|
|
928
928
|
registerProvider<ContextFile>(contextFileCapability.id, {
|
|
929
929
|
id: PROVIDER_ID,
|
|
930
930
|
displayName: DISPLAY_NAME,
|
|
931
|
-
description: "Load AGENTS.md from .
|
|
931
|
+
description: "Load AGENTS.md from .jeopi/ directories",
|
|
932
932
|
priority: PRIORITY,
|
|
933
933
|
load: loadContextFiles,
|
|
934
934
|
});
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -553,8 +553,8 @@ async function readExtensionModuleManifest(
|
|
|
553
553
|
const content = await readFile(packageJsonPath);
|
|
554
554
|
if (!content) return null;
|
|
555
555
|
|
|
556
|
-
const pkg = tryParseJson<{
|
|
557
|
-
const manifest = pkg?.
|
|
556
|
+
const pkg = tryParseJson<{ jeopi?: ExtensionModuleManifest; pi?: ExtensionModuleManifest }>(content);
|
|
557
|
+
const manifest = pkg?.jeopi ?? pkg?.pi;
|
|
558
558
|
if (manifest && typeof manifest === "object") {
|
|
559
559
|
return manifest;
|
|
560
560
|
}
|
|
@@ -762,18 +762,18 @@ export function parseClaudePluginsRegistry(content: string): ClaudePluginsRegist
|
|
|
762
762
|
* Resolve the active project registry path by walking up from `cwd`.
|
|
763
763
|
*
|
|
764
764
|
* Walk order:
|
|
765
|
-
* 1. Walk up from `cwd` looking for the nearest directory containing `.
|
|
766
|
-
* The first match returns `<dir>/.
|
|
767
|
-
* 2. If no `.
|
|
768
|
-
* The git root is used as an anchor: `<gitRoot>/.
|
|
765
|
+
* 1. Walk up from `cwd` looking for the nearest directory containing `.jeopi/`.
|
|
766
|
+
* The first match returns `<dir>/.jeopi/plugins/installed_plugins.json`.
|
|
767
|
+
* 2. If no `.jeopi/` is found, rescan from `cwd` upward looking for `.git`.
|
|
768
|
+
* The git root is used as an anchor: `<gitRoot>/.jeopi/plugins/installed_plugins.json`.
|
|
769
769
|
* 3. If neither is found, return `null` — no project context is active.
|
|
770
770
|
*
|
|
771
771
|
* This is the single source of truth for "active project root" used by install,
|
|
772
772
|
* uninstall, list, upgrade, discovery, and doctor. Deterministic for a given `cwd`.
|
|
773
773
|
*/
|
|
774
774
|
export async function resolveActiveProjectRegistryPath(cwd: string): Promise<string | null> {
|
|
775
|
-
// Pass 1: walk up looking for an existing .
|
|
776
|
-
// Stop before os.homedir() — ~/.
|
|
775
|
+
// Pass 1: walk up looking for an existing .jeopi/ directory (nearest wins).
|
|
776
|
+
// Stop before os.homedir() — ~/.jeopi/ is the user-level config dir, not a project root.
|
|
777
777
|
const homeDir = os.homedir();
|
|
778
778
|
let dir = path.resolve(cwd);
|
|
779
779
|
while (dir !== homeDir) {
|
|
@@ -808,11 +808,11 @@ export async function resolveActiveProjectRegistryPath(cwd: string): Promise<str
|
|
|
808
808
|
}
|
|
809
809
|
|
|
810
810
|
/**
|
|
811
|
-
* Like resolveActiveProjectRegistryPath, but falls back to `<cwd>/.
|
|
812
|
-
* when no project anchor (.
|
|
811
|
+
* Like resolveActiveProjectRegistryPath, but falls back to `<cwd>/.jeopi/plugins/installed_plugins.json`
|
|
812
|
+
* when no project anchor (.jeopi/ or .git/) is found.
|
|
813
813
|
*
|
|
814
814
|
* Use this when the caller accepts an explicit --scope project so that installing into a freshly
|
|
815
|
-
* bootstrapped directory (no .
|
|
815
|
+
* bootstrapped directory (no .jeopi/ or .git/ yet) works: writeInstalledPluginsRegistry auto-creates
|
|
816
816
|
* the directory tree on first write.
|
|
817
817
|
*
|
|
818
818
|
* Returns undefined when cwd is os.homedir() — that path is already the user registry and must
|
|
@@ -832,7 +832,7 @@ const pluginRootsCache = new Map<string, { roots: ClaudePluginRoot[]; warnings:
|
|
|
832
832
|
|
|
833
833
|
/**
|
|
834
834
|
* List all installed Claude Code plugin roots from the plugin cache.
|
|
835
|
-
* Reads ~/.claude/plugins/installed_plugins.json and ~/.
|
|
835
|
+
* Reads ~/.claude/plugins/installed_plugins.json and ~/.jeopi/plugins/installed_plugins.json,
|
|
836
836
|
* and optionally the nearest project-scoped registry resolved from `cwd`.
|
|
837
837
|
*
|
|
838
838
|
* Results are cached per `home:resolvedProjectPath` key to avoid repeated parsing.
|
|
@@ -945,7 +945,7 @@ export async function listClaudePluginRoots(
|
|
|
945
945
|
}
|
|
946
946
|
|
|
947
947
|
// ── Project-scoped OMP registry ────────────────────────────────────────
|
|
948
|
-
// Loaded from the nearest .
|
|
948
|
+
// Loaded from the nearest .jeopi/plugins/installed_plugins.json relative to cwd.
|
|
949
949
|
// Project entries take precedence over user entries for the same plugin ID.
|
|
950
950
|
if (resolvedProjectPath) {
|
|
951
951
|
const projectContent = await readFile(resolvedProjectPath);
|