spacecode 2.8.1 → 2.9.0

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/dist/repl.mjs CHANGED
@@ -3825,50 +3825,193 @@ var init_bash = __esm({
3825
3825
  }
3826
3826
  });
3827
3827
 
3828
- // ../agent/src/prompt.ts
3829
- function buildSystemPrompt(options) {
3830
- const workingDir = options.localCwd ? `Working directory: ${options.localCwd}` : options.repoName ? `Working directory: /home/user/${options.repoName}` : "Working directory: empty workspace";
3831
- const where = options.localCwd ? "on the user's machine" : "in the cloud";
3832
- const toolLines = [
3833
- "- read_file / write_file / edit_file / list_directory \u2014 instant file operations",
3834
- "- search \u2014 semantic code search by concept or keyword",
3835
- "- bash \u2014 shell commands (tests, builds, git, installs)"
3836
- ];
3837
- if (options.hasPreviewTool) {
3838
- toolLines.push("- preview \u2014 start or check a live preview of the app (front-end projects only)");
3839
- }
3840
- if (options.hasGitHubTools) {
3841
- toolLines.push("- create_pull_request \u2014 create a GitHub PR from the current branch");
3842
- }
3843
- if (options.hasSentryTools) {
3844
- toolLines.push("- sentry_list_issues / sentry_get_issue \u2014 view production errors (when connected)");
3845
- }
3846
- if (options.hasCodegenTools) {
3847
- toolLines.push("- build_feature \u2014 generate a complete feature with tests using the platform codegen service. Use for multi-file features or new modules. Files are written to workspace; run tests afterwards.");
3828
+ // ../shared/src/prompts/defaults-core.ts
3829
+ var DEFAULT_PROMPT_LIST;
3830
+ var init_defaults_core = __esm({
3831
+ "../shared/src/prompts/defaults-core.ts"() {
3832
+ "use strict";
3833
+ DEFAULT_PROMPT_LIST = [
3834
+ {
3835
+ key: "pipeline.decompose.system",
3836
+ description: "Splits a request into sub-tasks and enhances each (pipeline decomposer).",
3837
+ vars: [],
3838
+ body: `You are a task decomposition and prompt enhancement engine.
3839
+
3840
+ ## Decomposition Rules
3841
+ Analyze user requests and determine if they contain multiple independent tasks.
3842
+ - Numbered lists, bullet points, or requests joined by "and"/"also" are decomposable
3843
+ - Each sub-task should be independently executable
3844
+
3845
+ ## Prompt Enhancement Principles (MiniMax Best Practices)
3846
+
3847
+ 1. **Be Specific and Detailed**
3848
+ - Transform vague requests into rich, detailed specifications
3849
+ - Less: "Create a visualization website"
3850
+ - More: "Create an enterprise-grade data visualization website with interactive charts, filtering capabilities, export functionality, and responsive design"
3851
+
3852
+ 2. **Explain Intent ("Why")**
3853
+ - Add context about why this task matters
3854
+ - Example: "Your code will be reviewed for security vulnerabilities, so validate all inputs"
3855
+ - This helps the agent understand the purpose and provide more accurate results
3856
+
3857
+ 3. **Provide Examples**
3858
+ - Show good patterns to follow and bad patterns to avoid
3859
+ - Example: "Good: Handle errors with specific messages like 'User not found'. Bad: Use generic try/catch with no message."
3860
+
3861
+ 4. **Define Success Criteria**
3862
+ - What does "done" look like?
3863
+ - Example: "Complete when: tests pass, no linting errors, and manual verification in browser"
3864
+
3865
+ 5. **Specify Constraints**
3866
+ - What to avoid, what not to do
3867
+ - Example: "Do not use external dependencies beyond the existing package.json"
3868
+
3869
+ For each sub-task, enhance the description following these principles. Call the decompose_tasks function with your analysis.`
3870
+ },
3871
+ {
3872
+ key: "pipeline.enhance.system",
3873
+ description: "Rewrites a coding request into a richer prompt (pipeline optimizer).",
3874
+ vars: [],
3875
+ body: `You are a prompt enhancement engine. Transform coding requests into rich, detailed prompts following these principles:
3876
+
3877
+ ## Enhancement Principles (MiniMax Best Practices)
3878
+
3879
+ 1. **Be Specific and Detailed**
3880
+ - Transform vague requests into rich specifications
3881
+ - Less: "Create a visualization website"
3882
+ - More: "Create an enterprise-grade data visualization website with interactive charts, filtering capabilities, export functionality, and responsive design"
3883
+
3884
+ 2. **Explain Intent ("Why")**
3885
+ - Add context about why this task matters
3886
+ - Example: "Your code will be reviewed for security vulnerabilities, so validate all inputs"
3887
+
3888
+ 3. **Provide Examples**
3889
+ - Show good patterns to follow and bad patterns to avoid
3890
+ - Example: "Good: Handle errors with specific messages. Bad: Use generic try/catch"
3891
+
3892
+ 4. **Define Success Criteria**
3893
+ - What does "done" look like?
3894
+ - Example: "Complete when: tests pass, no linting errors"
3895
+
3896
+ 5. **Specify Constraints**
3897
+ - What to avoid
3898
+ - Example: "Do not use external dependencies beyond existing packages"
3899
+
3900
+ Call the enhance_prompt function with your analysis.`
3901
+ },
3902
+ {
3903
+ key: "context.summary",
3904
+ description: "Short conversation summary stored for context reuse.",
3905
+ vars: [],
3906
+ body: `Summarize this conversation concisely in 2-4 sentences. Focus on:
3907
+ - What the user asked for
3908
+ - Key decisions made
3909
+ - Current state of the work
3910
+ - Any important context (file names, technologies, error messages)
3911
+
3912
+ Do NOT include greetings or meta-commentary. Just the facts.`
3913
+ },
3914
+ {
3915
+ key: "context.reasoning_summary",
3916
+ description: "Detailed summary of the earlier conversation for the reasoning tier.",
3917
+ vars: [],
3918
+ body: `Summarize the earlier part of this conversation for a senior AI assistant that will continue the work. Be detailed and preserve:
3919
+ - Specific file paths, function names, and code snippets mentioned
3920
+ - Decisions made and their rationale
3921
+ - Error messages, stack traces, or debugging steps taken
3922
+ - Current state of the task (what's done, what remains)
3923
+ - User preferences or constraints expressed
3924
+
3925
+ Write 3-8 sentences. Be concrete and technical, not vague.`
3926
+ },
3927
+ {
3928
+ key: "profile.adapter.system",
3929
+ description: "Classifies imported instruction sections as keep / on_demand / drop.",
3930
+ vars: [],
3931
+ body: `You prepare a user's personal instruction file (written for the Claude Code CLI) to be used by SpaceCode, a different coding agent that runs locally with these tools: read_file, write_file, edit_file, list_directory, search, bash, todo_write, memory_read, instructions_read. SpaceCode does NOT have: Claude Code hooks, ToolSearch/deferred tools, subagents (Task/Agent), skills or slash commands (/ship etc.), MCP tools (mcp__*), plan mode or worktrees.
3932
+
3933
+ For EACH section decide:
3934
+ - "keep": a short, general standing rule that applies to nearly every task (always loaded). Keep this set small: the total of all kept sections must stay under about 5000 characters.
3935
+ - "on_demand": useful only when working in a specific area (a tool, vendor, workflow). Give a one-line "summary" saying when to read it.
3936
+ - "drop": (the text is kept and stored on demand anyway; this only flags it) only meaningful inside Claude Code (describes its hooks, skills, subagents, ToolSearch, MCP tool names or its own settings) or is obsolete for SpaceCode. Give a short "reason".
3937
+
3938
+ You never rewrite or shorten text; you only classify. When unsure between drop and on_demand choose on_demand. When unsure between keep and on_demand choose on_demand.
3939
+
3940
+ Reply with ONLY a JSON array, one object per input section, in the same order: {"slug": string, "decision": "keep"|"on_demand"|"drop", "reason"?: string, "summary"?: string}. No prose, no code fences.`
3941
+ },
3942
+ {
3943
+ key: "route.code.system",
3944
+ description: 'System prompt of the "code" dynamic route (pipeline helper calls).',
3945
+ vars: [],
3946
+ body: `You are an expert software engineer. Write clean, correct, well-structured code with proper error handling.`
3947
+ },
3948
+ {
3949
+ key: "route.code-review.system",
3950
+ description: 'System prompt of the "code-review" dynamic route (pipeline helper calls).',
3951
+ vars: [],
3952
+ body: `You are a senior code reviewer. Analyze code for bugs, security issues, performance problems, and maintainability. Be specific and actionable.`
3953
+ },
3954
+ {
3955
+ key: "route.debugging.system",
3956
+ description: 'System prompt of the "debugging" dynamic route (pipeline helper calls).',
3957
+ vars: [],
3958
+ body: `You are an expert debugger. Diagnose issues methodically: identify symptoms, trace root causes, and provide concrete fixes. Explain your reasoning step by step.`
3959
+ },
3960
+ {
3961
+ key: "route.architecture.system",
3962
+ description: 'System prompt of the "architecture" dynamic route (pipeline helper calls).',
3963
+ vars: [],
3964
+ body: `You are a software architect. Focus on system design, scalability, trade-offs, and infrastructure decisions. Be concrete and practical.`
3965
+ },
3966
+ {
3967
+ key: "route.planning.system",
3968
+ description: 'System prompt of the "planning" dynamic route (pipeline helper calls).',
3969
+ vars: [],
3970
+ body: `You are a technical project manager. Create structured plans, roadmaps, and specifications. Use clear headings and actionable items.`
3971
+ },
3972
+ {
3973
+ key: "agent.summarizer.system",
3974
+ description: "Extracts decisions and work items from a session transcript (resume summary, /compact).",
3975
+ vars: [],
3976
+ body: `You are extracting structured information from a coding-agent conversation transcript. Identify (a) concrete DECISIONS made during the conversation (architectural choices, approach selections, explicit tradeoffs accepted) and (b) WORK ITEMS \u2014 discrete pieces of work mentioned as done, in-progress, or still needed, each classified as "open" or "done" based on the transcript. Respond with ONLY a JSON object matching exactly this shape, no prose before or after, no markdown code fences: {"decisions": [{"description": string}], "workItems": [{"description": string, "status": "open"|"done"}]}`
3977
+ },
3978
+ {
3979
+ key: "agent.summarizer.user_suffix",
3980
+ description: "Reminder appended after the transcript for the summarizer.",
3981
+ vars: [],
3982
+ body: `Respond with ONLY the JSON object described above \u2014 no prose, no markdown code fences.`
3983
+ }
3984
+ ];
3848
3985
  }
3849
- return `You are SpaceCode Agent, a coding assistant running ${where}.
3986
+ });
3987
+
3988
+ // ../shared/src/prompts/defaults-agent.ts
3989
+ var AGENT_PROMPT_LIST;
3990
+ var init_defaults_agent = __esm({
3991
+ "../shared/src/prompts/defaults-agent.ts"() {
3992
+ "use strict";
3993
+ AGENT_PROMPT_LIST = [
3994
+ {
3995
+ key: "agent.system.core",
3996
+ description: "Core system prompt of the SpaceCode agent (REPL and cloud).",
3997
+ vars: ["where", "tool_lines", "orient_line", "finish_line", "codegen_section", "commit_section", "working_dir", "project_card_section", "resume_section"],
3998
+ body: `You are SpaceCode Agent, a coding assistant running {{where}}.
3850
3999
 
3851
4000
  ## Tools
3852
- ${toolLines.join("\n")}
4001
+ {{tool_lines}}
3853
4002
 
3854
4003
  ## Tool usage
3855
- ${options.projectCard ? "- A project card below already holds the layout, commands, doc titles and excerpts, recent commits, branch and uncommitted changes. Do not re-list directories, re-run git status/log/diff, or re-read a doc just to see what the card shows; open a file only to go beyond its excerpt." : "- Use list_directory to orient yourself in the codebase before diving in."}
4004
+ {{orient_line}}
3856
4005
  - Use search to find files by concept ("auth middleware", "billing webhook").
3857
4006
  - Use bash for running commands, not for reading files. Use read_file instead.
3858
4007
  - Prefer read_file + edit_file over write_file for existing files.
3859
- ${options.hasGitHubTools ? "- After completing work, create a PR with create_pull_request. Commit and push changes first." : "- After completing work, commit your changes with bash (git add + git commit)."}
4008
+ {{finish_line}}
3860
4009
 
3861
4010
  ## Editing
3862
4011
  - Always read_file before edit_file \u2014 you need the current content.
3863
4012
  - old_string must match exactly once. Include surrounding lines for uniqueness.
3864
4013
  - Make small, targeted edits. Avoid rewriting entire files with write_file.
3865
- ${options.hasCodegenTools ? `
3866
- ## Feature generation
3867
- - For multi-file features (3+ new files), prefer build_feature over writing files manually.
3868
- - build_feature leverages a component library \u2014 it reuses existing implementations when possible.
3869
- - After build_feature, always run tests and review the generated code before committing.
3870
- - For single-file changes, bug fixes, or config edits, use edit_file directly.
3871
- ` : ""}
4014
+ {{codegen_section}}
3872
4015
  ## Error recovery
3873
4016
  - If edit_file fails (old_string not found), re-read the file \u2014 it may have changed \u2014 then retry with corrected old_string.
3874
4017
  - If bash fails, read the error output and try a different approach. Never repeat the same failing command.
@@ -3879,24 +4022,125 @@ ${options.hasCodegenTools ? `
3879
4022
  - For an audit or plan request, finish with the audit or plan itself (findings, then ordered next steps). Do not end by asking what to work on next.
3880
4023
  - After changes, run relevant tests (bash: npm test, pytest, etc.) to verify.
3881
4024
  - Commit logical units of work with descriptive messages.
3882
- ${options.commitGuidance ? `
3883
- ## Committing
4025
+ {{commit_section}}
4026
+ {{working_dir}}
4027
+ {{project_card_section}}{{resume_section}}
4028
+ Complete the user's task, then stop. Be concise in responses.`
4029
+ },
4030
+ {
4031
+ key: "agent.fragment.orient_card",
4032
+ description: "Tool-usage line when a project card is present.",
4033
+ vars: [],
4034
+ body: `- A project card below already holds the layout, commands, doc titles and excerpts, recent commits, branch and uncommitted changes. Do not re-list directories, re-run git status/log/diff, or re-read a doc just to see what the card shows; open a file only to go beyond its excerpt.`
4035
+ },
4036
+ {
4037
+ key: "agent.fragment.orient_default",
4038
+ description: "Tool-usage line when there is no project card.",
4039
+ vars: [],
4040
+ body: `- Use list_directory to orient yourself in the codebase before diving in.`
4041
+ },
4042
+ {
4043
+ key: "agent.fragment.finish_github",
4044
+ description: "Closing instruction when GitHub tools are available.",
4045
+ vars: [],
4046
+ body: `- After completing work, create a PR with create_pull_request. Commit and push changes first.`
4047
+ },
4048
+ {
4049
+ key: "agent.fragment.finish_commit",
4050
+ description: "Closing instruction without GitHub tools.",
4051
+ vars: [],
4052
+ body: `- After completing work, commit your changes with bash (git add + git commit).`
4053
+ },
4054
+ {
4055
+ key: "agent.fragment.codegen",
4056
+ description: "Feature-generation section (only when build_feature is registered).",
4057
+ vars: [],
4058
+ body: `## Feature generation
4059
+ - For multi-file features (3+ new files), prefer build_feature over writing files manually.
4060
+ - build_feature leverages a component library \u2014 it reuses existing implementations when possible.
4061
+ - After build_feature, always run tests and review the generated code before committing.
4062
+ - For single-file changes, bug fixes, or config edits, use edit_file directly.`
4063
+ },
4064
+ {
4065
+ key: "agent.fragment.commit",
4066
+ description: "Committing section (only when commit guidance is on).",
4067
+ vars: [],
4068
+ body: `## Committing
3884
4069
  - After completing a logical unit of work, commit changes with a descriptive message using bash (git add + git commit).
3885
4070
  - Use conventional commit format: feat:, fix:, refactor:, test:, docs:
3886
- - Don't commit after every single file change \u2014 group related changes.
3887
- ` : ""}
3888
- ${workingDir}
3889
- ${options.projectCard ? `
3890
- ${options.projectCard}
3891
- ` : ""}${options.resumeSummary ? `
3892
- ## Previously in this session
3893
- ${options.resumeSummary}
3894
- ` : ""}
3895
- Complete the user's task, then stop. Be concise in responses.`;
4071
+ - Don't commit after every single file change \u2014 group related changes.`
4072
+ },
4073
+ {
4074
+ key: "agent.fragment.resume",
4075
+ description: "Prior-turn summary injected on resume.",
4076
+ vars: ["summary"],
4077
+ body: `## Previously in this session
4078
+ {{summary}}`
4079
+ }
4080
+ ];
4081
+ }
4082
+ });
4083
+
4084
+ // ../shared/src/prompts/index.ts
4085
+ function renderPrompt(body, vars = {}) {
4086
+ return body.replace(VAR, (_m, name) => {
4087
+ if (!(name in vars)) throw new Error(`prompt variable {{${name}}} was not provided`);
4088
+ return vars[name];
4089
+ });
4090
+ }
4091
+ var DEFAULT_PROMPTS, VAR;
4092
+ var init_prompts = __esm({
4093
+ "../shared/src/prompts/index.ts"() {
4094
+ init_defaults_core();
4095
+ init_defaults_agent();
4096
+ DEFAULT_PROMPTS = Object.freeze(
4097
+ Object.fromEntries([...DEFAULT_PROMPT_LIST, ...AGENT_PROMPT_LIST].map((p) => [p.key, p]))
4098
+ );
4099
+ VAR = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
4100
+ }
4101
+ });
4102
+
4103
+ // ../agent/src/prompt.ts
4104
+ function buildSystemPrompt(options) {
4105
+ const workingDir = options.localCwd ? `Working directory: ${options.localCwd}` : options.repoName ? `Working directory: /home/user/${options.repoName}` : "Working directory: empty workspace";
4106
+ const where = options.localCwd ? "on the user's machine" : "in the cloud";
4107
+ const toolLines = [
4108
+ "- read_file / write_file / edit_file / list_directory \u2014 instant file operations",
4109
+ "- search \u2014 semantic code search by concept or keyword",
4110
+ "- bash \u2014 shell commands (tests, builds, git, installs)"
4111
+ ];
4112
+ if (options.hasPreviewTool) {
4113
+ toolLines.push("- preview \u2014 start or check a live preview of the app (front-end projects only)");
4114
+ }
4115
+ if (options.hasGitHubTools) {
4116
+ toolLines.push("- create_pull_request \u2014 create a GitHub PR from the current branch");
4117
+ }
4118
+ if (options.hasSentryTools) {
4119
+ toolLines.push("- sentry_list_issues / sentry_get_issue \u2014 view production errors (when connected)");
4120
+ }
4121
+ if (options.hasCodegenTools) {
4122
+ toolLines.push("- build_feature \u2014 generate a complete feature with tests using the platform codegen service. Use for multi-file features or new modules. Files are written to workspace; run tests afterwards.");
4123
+ }
4124
+ const t = (key) => options.templates?.[key] ?? DEFAULT_PROMPTS[key].body;
4125
+ const wrap = (body) => `
4126
+ ${body}
4127
+ `;
4128
+ return renderPrompt(t("agent.system.core"), {
4129
+ where,
4130
+ tool_lines: toolLines.join("\n"),
4131
+ orient_line: options.projectCard ? t("agent.fragment.orient_card") : t("agent.fragment.orient_default"),
4132
+ finish_line: options.hasGitHubTools ? t("agent.fragment.finish_github") : t("agent.fragment.finish_commit"),
4133
+ codegen_section: options.hasCodegenTools ? wrap(t("agent.fragment.codegen")) : "",
4134
+ commit_section: options.commitGuidance ? wrap(t("agent.fragment.commit")) : "",
4135
+ working_dir: workingDir,
4136
+ project_card_section: options.projectCard ? wrap(options.projectCard) : "",
4137
+ resume_section: options.resumeSummary ? wrap(renderPrompt(t("agent.fragment.resume"), { summary: options.resumeSummary })) : ""
4138
+ });
3896
4139
  }
3897
4140
  var init_prompt = __esm({
3898
4141
  "../agent/src/prompt.ts"() {
3899
4142
  "use strict";
4143
+ init_prompts();
3900
4144
  }
3901
4145
  });
3902
4146
 
@@ -4096,9 +4340,9 @@ function createFileTools(store) {
4096
4340
  const handlers = {
4097
4341
  read_file: async (input) => {
4098
4342
  const path12 = input.path;
4099
- const cached2 = readCache.get(path12);
4100
- if (cached2 !== void 0) {
4101
- return { output: cached2 };
4343
+ const cached3 = readCache.get(path12);
4344
+ if (cached3 !== void 0) {
4345
+ return { output: cached3 };
4102
4346
  }
4103
4347
  const content = await store.readFile(path12);
4104
4348
  if (content === null) {
@@ -4158,12 +4402,12 @@ Include more surrounding context to make old_string unique.`,
4158
4402
  },
4159
4403
  list_directory: async (input) => {
4160
4404
  const path12 = input.path;
4161
- const cached2 = listCache.get(path12);
4162
- if (cached2 !== void 0) {
4163
- if (cached2.length === 0) {
4405
+ const cached3 = listCache.get(path12);
4406
+ if (cached3 !== void 0) {
4407
+ if (cached3.length === 0) {
4164
4408
  return { output: `Empty or not found: ${path12}` };
4165
4409
  }
4166
- return { output: cached2.join("\n") };
4410
+ return { output: cached3.join("\n") };
4167
4411
  }
4168
4412
  const entries = await store.listDirectory(path12);
4169
4413
  listCache.set(path12, entries);
@@ -5877,10 +6121,10 @@ async function summarizeConversation(messages, config) {
5877
6121
  return { decisions: [], workItems: [], summaryText: "", inputTokens: 0, outputTokens: 0 };
5878
6122
  }
5879
6123
  const transcript = buildTranscriptForSummarization(messages, MAX_TRANSCRIPT_CHARS);
5880
- const systemPrompt = 'You are extracting structured information from a coding-agent conversation transcript. Identify (a) concrete DECISIONS made during the conversation (architectural choices, approach selections, explicit tradeoffs accepted) and (b) WORK ITEMS \u2014 discrete pieces of work mentioned as done, in-progress, or still needed, each classified as "open" or "done" based on the transcript. Respond with ONLY a JSON object matching exactly this shape, no prose before or after, no markdown code fences: {"decisions": [{"description": string}], "workItems": [{"description": string, "status": "open"|"done"}]}';
6124
+ const systemPrompt = config.templates?.["agent.summarizer.system"] ?? DEFAULT_PROMPTS["agent.summarizer.system"].body;
5881
6125
  const userContent = `${transcript}
5882
6126
 
5883
- Respond with ONLY the JSON object described above \u2014 no prose, no markdown code fences.`;
6127
+ ${config.templates?.["agent.summarizer.user_suffix"] ?? DEFAULT_PROMPTS["agent.summarizer.user_suffix"].body}`;
5884
6128
  const response = await fetch(`${config.apiUrl}/v1/messages`, {
5885
6129
  method: "POST",
5886
6130
  headers: {
@@ -5974,6 +6218,7 @@ var DEFAULT_SUMMARIZER_MODEL, MAX_TRANSCRIPT_CHARS, MAX_TOKENS2;
5974
6218
  var init_summarizer = __esm({
5975
6219
  "../agent/src/summarizer.ts"() {
5976
6220
  "use strict";
6221
+ init_prompts();
5977
6222
  DEFAULT_SUMMARIZER_MODEL = "claude-haiku-4-5";
5978
6223
  MAX_TRANSCRIPT_CHARS = 4e4;
5979
6224
  MAX_TOKENS2 = 4096;
@@ -6650,17 +6895,17 @@ var init_memory_tool = __esm({
6650
6895
  // ../agent/src/import/profile-loader.ts
6651
6896
  async function loadProfile(opts) {
6652
6897
  const { client, cache: cache4, projectKeys } = opts;
6653
- let cached2 = null;
6898
+ let cached3 = null;
6654
6899
  let cacheError;
6655
6900
  try {
6656
- cached2 = await cache4.read();
6901
+ cached3 = await cache4.read();
6657
6902
  } catch (err) {
6658
6903
  cacheError = `profile cache unreadable: ${err instanceof Error ? err.message : String(err)}`;
6659
6904
  }
6660
- const sameProject = cached2 !== null && cached2.projectKey !== null && projectKeys.includes(cached2.projectKey);
6905
+ const sameProject = cached3 !== null && cached3.projectKey !== null && projectKeys.includes(cached3.projectKey);
6661
6906
  try {
6662
- const index = await client.index(sameProject ? cached2.version : void 0);
6663
- if (index === null && sameProject) return { profile: cached2, source: "live" };
6907
+ const index = await client.index(sameProject ? cached3.version : void 0);
6908
+ if (index === null && sameProject) return { profile: cached3, source: "live" };
6664
6909
  if (index === null) throw new Error("profile API answered 304 with no usable cached copy");
6665
6910
  const has2 = (kind, name) => index.assets.some((a) => a.kind === kind && a.name === name);
6666
6911
  const chosen = projectKeys.find((k2) => has2("memory", `${k2}.index`)) ?? null;
@@ -6682,8 +6927,8 @@ async function loadProfile(opts) {
6682
6927
  return { profile, source: "live", ...cacheError ? { error: cacheError } : {} };
6683
6928
  } catch (err) {
6684
6929
  const error = `${err instanceof Error ? err.message : String(err)}${cacheError ? `; ${cacheError}` : ""}`;
6685
- if (!cached2) return { profile: null, source: "none", error };
6686
- return { profile: sameProject ? cached2 : { ...cached2, memoryIndex: null, projectKey: null }, source: "cache", error };
6930
+ if (!cached3) return { profile: null, source: "none", error };
6931
+ return { profile: sameProject ? cached3 : { ...cached3, memoryIndex: null, projectKey: null }, source: "cache", error };
6687
6932
  }
6688
6933
  }
6689
6934
  var init_profile_loader = __esm({
@@ -9386,7 +9631,7 @@ var require_signal_exit = __commonJS({
9386
9631
  }
9387
9632
  assert.equal(typeof cb, "function", "a callback must be provided for exit handler");
9388
9633
  if (loaded === false) {
9389
- load();
9634
+ load2();
9390
9635
  }
9391
9636
  var ev = "exit";
9392
9637
  if (opts && opts.alwaysLast) {
@@ -9446,7 +9691,7 @@ var require_signal_exit = __commonJS({
9446
9691
  return signals;
9447
9692
  };
9448
9693
  loaded = false;
9449
- load = function load2() {
9694
+ load2 = function load3() {
9450
9695
  if (loaded || !processOk(global.process)) {
9451
9696
  return;
9452
9697
  }
@@ -9463,7 +9708,7 @@ var require_signal_exit = __commonJS({
9463
9708
  process13.emit = processEmit;
9464
9709
  process13.reallyExit = processReallyExit;
9465
9710
  };
9466
- module.exports.load = load;
9711
+ module.exports.load = load2;
9467
9712
  originalProcessReallyExit = process13.reallyExit;
9468
9713
  processReallyExit = function processReallyExit2(code) {
9469
9714
  if (!processOk(global.process)) {
@@ -9499,7 +9744,7 @@ var require_signal_exit = __commonJS({
9499
9744
  var emit;
9500
9745
  var sigListeners;
9501
9746
  var loaded;
9502
- var load;
9747
+ var load2;
9503
9748
  var originalProcessReallyExit;
9504
9749
  var processReallyExit;
9505
9750
  var originalProcessEmit;
@@ -44082,7 +44327,7 @@ var require_core = __commonJS({
44082
44327
  return match && match.index === 0;
44083
44328
  }
44084
44329
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
44085
- function join13(regexps, separator = "|") {
44330
+ function join14(regexps, separator = "|") {
44086
44331
  let numCaptures = 0;
44087
44332
  return regexps.map((regex2) => {
44088
44333
  numCaptures += 1;
@@ -44386,7 +44631,7 @@ var require_core = __commonJS({
44386
44631
  this.exec = () => null;
44387
44632
  }
44388
44633
  const terminators = this.regexes.map((el) => el[1]);
44389
- this.matcherRe = langRe(join13(terminators), true);
44634
+ this.matcherRe = langRe(join14(terminators), true);
44390
44635
  this.lastIndex = 0;
44391
44636
  }
44392
44637
  /** @param {string} s */
@@ -48163,7 +48408,7 @@ var require_bash = __commonJS({
48163
48408
  return joined;
48164
48409
  }
48165
48410
  function bash(hljs) {
48166
- const VAR = {};
48411
+ const VAR2 = {};
48167
48412
  const BRACED_VAR = {
48168
48413
  begin: /\$\{/,
48169
48414
  end: /\}/,
@@ -48171,12 +48416,12 @@ var require_bash = __commonJS({
48171
48416
  "self",
48172
48417
  {
48173
48418
  begin: /:-/,
48174
- contains: [VAR]
48419
+ contains: [VAR2]
48175
48420
  }
48176
48421
  // default values
48177
48422
  ]
48178
48423
  };
48179
- Object.assign(VAR, {
48424
+ Object.assign(VAR2, {
48180
48425
  className: "variable",
48181
48426
  variants: [
48182
48427
  { begin: concat(
@@ -48212,7 +48457,7 @@ var require_bash = __commonJS({
48212
48457
  end: /"/,
48213
48458
  contains: [
48214
48459
  hljs.BACKSLASH_ESCAPE,
48215
- VAR,
48460
+ VAR2,
48216
48461
  SUBST
48217
48462
  ]
48218
48463
  };
@@ -48232,7 +48477,7 @@ var require_bash = __commonJS({
48232
48477
  contains: [
48233
48478
  { begin: /\d+#[0-9a-f]+/, className: "number" },
48234
48479
  hljs.NUMBER_MODE,
48235
- VAR
48480
+ VAR2
48236
48481
  ]
48237
48482
  };
48238
48483
  const SH_LIKE_SHELLS = [
@@ -48282,7 +48527,7 @@ var require_bash = __commonJS({
48282
48527
  QUOTE_STRING,
48283
48528
  ESCAPED_QUOTE,
48284
48529
  APOS_STRING,
48285
- VAR
48530
+ VAR2
48286
48531
  ]
48287
48532
  };
48288
48533
  }
@@ -67905,7 +68150,7 @@ var require_perl = __commonJS({
67905
68150
  end: /\}/
67906
68151
  // contains defined later
67907
68152
  };
67908
- const VAR = {
68153
+ const VAR2 = {
67909
68154
  variants: [
67910
68155
  {
67911
68156
  begin: /\$\d/
@@ -67927,7 +68172,7 @@ var require_perl = __commonJS({
67927
68172
  const STRING_CONTAINS = [
67928
68173
  hljs.BACKSLASH_ESCAPE,
67929
68174
  SUBST,
67930
- VAR
68175
+ VAR2
67931
68176
  ];
67932
68177
  const REGEX_DELIMS = [
67933
68178
  /!/,
@@ -67962,7 +68207,7 @@ var require_perl = __commonJS({
67962
68207
  );
67963
68208
  };
67964
68209
  const PERL_DEFAULT_CONTAINS = [
67965
- VAR,
68210
+ VAR2,
67966
68211
  hljs.HASH_COMMENT_MODE,
67967
68212
  hljs.COMMENT(
67968
68213
  /^=\w/,
@@ -68429,7 +68674,7 @@ var require_n1ql = __commonJS({
68429
68674
  var require_nginx = __commonJS({
68430
68675
  "../../node_modules/highlight.js/lib/languages/nginx.js"(exports, module) {
68431
68676
  function nginx(hljs) {
68432
- const VAR = {
68677
+ const VAR2 = {
68433
68678
  className: "variable",
68434
68679
  variants: [
68435
68680
  {
@@ -68458,7 +68703,7 @@ var require_nginx = __commonJS({
68458
68703
  className: "string",
68459
68704
  contains: [
68460
68705
  hljs.BACKSLASH_ESCAPE,
68461
- VAR
68706
+ VAR2
68462
68707
  ],
68463
68708
  variants: [
68464
68709
  {
@@ -68477,13 +68722,13 @@ var require_nginx = __commonJS({
68477
68722
  end: "\\s",
68478
68723
  endsWithParent: true,
68479
68724
  excludeEnd: true,
68480
- contains: [VAR]
68725
+ contains: [VAR2]
68481
68726
  },
68482
68727
  {
68483
68728
  className: "regexp",
68484
68729
  contains: [
68485
68730
  hljs.BACKSLASH_ESCAPE,
68486
- VAR
68731
+ VAR2
68487
68732
  ],
68488
68733
  variants: [
68489
68734
  {
@@ -68518,7 +68763,7 @@ var require_nginx = __commonJS({
68518
68763
  begin: "\\b\\d+[kKmMgGdshdwy]*\\b",
68519
68764
  relevance: 0
68520
68765
  },
68521
- VAR
68766
+ VAR2
68522
68767
  ]
68523
68768
  };
68524
68769
  return {
@@ -69984,7 +70229,7 @@ var require_powershell = __commonJS({
69984
70229
  begin: "`[\\s\\S]",
69985
70230
  relevance: 0
69986
70231
  };
69987
- const VAR = {
70232
+ const VAR2 = {
69988
70233
  className: "variable",
69989
70234
  variants: [
69990
70235
  {
@@ -70017,7 +70262,7 @@ var require_powershell = __commonJS({
70017
70262
  ],
70018
70263
  contains: [
70019
70264
  BACKTICK_ESCAPE,
70020
- VAR,
70265
+ VAR2,
70021
70266
  {
70022
70267
  className: "variable",
70023
70268
  begin: /\$[A-z]/,
@@ -70108,7 +70353,7 @@ var require_powershell = __commonJS({
70108
70353
  end: /\)/,
70109
70354
  className: "params",
70110
70355
  relevance: 0,
70111
- contains: [VAR]
70356
+ contains: [VAR2]
70112
70357
  }
70113
70358
  // CMDLETS
70114
70359
  ]
@@ -70179,7 +70424,7 @@ var require_powershell = __commonJS({
70179
70424
  APOS_STRING,
70180
70425
  // PS_NEW_OBJECT_TYPE,
70181
70426
  CMDLETS,
70182
- VAR,
70427
+ VAR2,
70183
70428
  LITERAL,
70184
70429
  HASH_SIGNS
70185
70430
  ];
@@ -70301,7 +70546,7 @@ var require_prolog = __commonJS({
70301
70546
  begin: /[a-z][A-Za-z0-9_]*/,
70302
70547
  relevance: 0
70303
70548
  };
70304
- const VAR = {
70549
+ const VAR2 = {
70305
70550
  className: "symbol",
70306
70551
  variants: [
70307
70552
  {
@@ -70350,7 +70595,7 @@ var require_prolog = __commonJS({
70350
70595
  };
70351
70596
  const inner = [
70352
70597
  ATOM,
70353
- VAR,
70598
+ VAR2,
70354
70599
  PARENTED,
70355
70600
  PRED_OP,
70356
70601
  LIST,
@@ -71895,7 +72140,7 @@ var require_routeros = __commonJS({
71895
72140
  const COMMON_COMMANDS = "add remove enable disable set get print export edit find run debug error info warning";
71896
72141
  const LITERALS = "true false yes no nothing nil null";
71897
72142
  const OBJECTS = "traffic-flow traffic-generator firewall scheduler aaa accounting address-list address align area bandwidth-server bfd bgp bridge client clock community config connection console customer default dhcp-client dhcp-server discovery dns e-mail ethernet filter firmware gps graphing group hardware health hotspot identity igmp-proxy incoming instance interface ip ipsec ipv6 irq l2tp-server lcd ldp logging mac-server mac-winbox mangle manual mirror mme mpls nat nd neighbor network note ntp ospf ospf-v3 ovpn-server page peer pim ping policy pool port ppp pppoe-client pptp-server prefix profile proposal proxy queue radius resource rip ripng route routing screen script security-profiles server service service-port settings shares smb sms sniffer snmp snooper socks sstp-server system tool tracking type upgrade upnp user-manager users user vlan secret vrrp watchdog web-access wireless pptp pppoe lan wan layer7-protocol lease simple raw";
71898
- const VAR = {
72143
+ const VAR2 = {
71899
72144
  className: "variable",
71900
72145
  variants: [
71901
72146
  {
@@ -71912,7 +72157,7 @@ var require_routeros = __commonJS({
71912
72157
  end: /"/,
71913
72158
  contains: [
71914
72159
  hljs.BACKSLASH_ESCAPE,
71915
- VAR,
72160
+ VAR2,
71916
72161
  {
71917
72162
  className: "variable",
71918
72163
  begin: /\$\(/,
@@ -71962,7 +72207,7 @@ var require_routeros = __commonJS({
71962
72207
  hljs.COMMENT("^#", "$"),
71963
72208
  QUOTE_STRING,
71964
72209
  APOS_STRING,
71965
- VAR,
72210
+ VAR2,
71966
72211
  // attribute=value
71967
72212
  {
71968
72213
  // > is to avoid matches with => in other grammars
@@ -71981,7 +72226,7 @@ var require_routeros = __commonJS({
71981
72226
  contains: [
71982
72227
  QUOTE_STRING,
71983
72228
  APOS_STRING,
71984
- VAR,
72229
+ VAR2,
71985
72230
  {
71986
72231
  className: "literal",
71987
72232
  begin: "\\b(" + LITERALS.split(" ").join("|") + ")\\b"
@@ -78263,7 +78508,7 @@ var require_xquery = __commonJS({
78263
78508
  begin: /\bxquery version "[13]\.[01]"\s?(?:encoding ".+")?/,
78264
78509
  end: /;/
78265
78510
  };
78266
- const VAR = {
78511
+ const VAR2 = {
78267
78512
  className: "variable",
78268
78513
  begin: /[$][\w\-:]+/
78269
78514
  };
@@ -78332,7 +78577,7 @@ var require_xquery = __commonJS({
78332
78577
  ]
78333
78578
  };
78334
78579
  const CONTAINS = [
78335
- VAR,
78580
+ VAR2,
78336
78581
  BUILT_IN,
78337
78582
  STRING,
78338
78583
  NUMBER,
@@ -103855,6 +104100,65 @@ var init_project_card_runtime = __esm({
103855
104100
  }
103856
104101
  });
103857
104102
 
104103
+ // src/prompt-runtime.ts
104104
+ import { join as join8 } from "node:path";
104105
+ async function load(deps) {
104106
+ if (deps.env.SPACECODE_PROMPTS === "off") return { templates: {}, statusLine: "prompts: built-in defaults (SPACECODE_PROMPTS=off)" };
104107
+ const dir = deps.env.SPACECODE_CONFIG_DIR?.trim() || join8(deps.homedir(), ".spacecode");
104108
+ const file = join8(dir, "prompts-cache.json");
104109
+ const fromDisk = async (why) => {
104110
+ try {
104111
+ const raw = JSON.parse(await deps.readFile(file));
104112
+ const templates = raw.templates ?? {};
104113
+ return { templates, statusLine: `prompts: ${Object.keys(templates).length} tuned (STALE: served from cache; ${why})` };
104114
+ } catch {
104115
+ return { templates: {}, statusLine: `prompts: built-in defaults (${why})` };
104116
+ }
104117
+ };
104118
+ let apiKey;
104119
+ try {
104120
+ apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
104121
+ } catch (err) {
104122
+ return fromDisk(err instanceof Error ? err.message : String(err));
104123
+ }
104124
+ if (!apiKey) return fromDisk("not signed in");
104125
+ try {
104126
+ const res = await deps.fetch(`${backendUrl(deps.env)}/api/prompts`, {
104127
+ headers: { Authorization: `Bearer ${apiKey}`, "x-api-key": apiKey },
104128
+ signal: AbortSignal.timeout(3e3)
104129
+ });
104130
+ if (!res.ok) return fromDisk(`prompt API returned ${res.status}`);
104131
+ const { prompts } = await res.json();
104132
+ const templates = Object.fromEntries(prompts.filter((p) => p.source === "db").map((p) => [p.key, p.body]));
104133
+ try {
104134
+ await deps.mkdir(dir);
104135
+ await deps.writeFile(file, JSON.stringify({ fetchedAt: new Date((deps.now ?? Date.now)()).toISOString(), templates }));
104136
+ } catch (err) {
104137
+ console.error("[prompts] could not write the prompt cache", err instanceof Error ? err.message : String(err));
104138
+ }
104139
+ const n = Object.keys(templates).length;
104140
+ return { templates, statusLine: n ? `prompts: ${n} tuned override(s): ${Object.keys(templates).join(", ")}` : "prompts: built-in defaults (none tuned)" };
104141
+ } catch (err) {
104142
+ return fromDisk(`fetch failed: ${err instanceof Error ? err.message : String(err)}`);
104143
+ }
104144
+ }
104145
+ function loadPromptTemplates(deps) {
104146
+ const now = (deps.now ?? Date.now)();
104147
+ if (cached2 && now - cached2.at < TTL_MS2) return cached2.pending;
104148
+ const pending = load(deps);
104149
+ cached2 = { at: now, pending };
104150
+ return pending;
104151
+ }
104152
+ var TTL_MS2, cached2;
104153
+ var init_prompt_runtime = __esm({
104154
+ "src/prompt-runtime.ts"() {
104155
+ "use strict";
104156
+ init_api_key();
104157
+ init_profile_runtime();
104158
+ TTL_MS2 = 10 * 6e4;
104159
+ }
104160
+ });
104161
+
103858
104162
  // src/shell-turn.ts
103859
104163
  function shellCommandOf(prompt) {
103860
104164
  return prompt.startsWith("!") ? prompt.slice(1).trim() : null;
@@ -104279,7 +104583,7 @@ var init_recording_chunks = __esm({
104279
104583
  // src/recording/recorder.ts
104280
104584
  import { createHash as createHash4 } from "node:crypto";
104281
104585
  import { mkdir as mkdir4, readFile as readFile6, rename as rename4, rm as rm2, writeFile as writeFile4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
104282
- import { join as join8 } from "node:path";
104586
+ import { join as join9 } from "node:path";
104283
104587
  function projectHash(root) {
104284
104588
  return createHash4("sha256").update(root).digest("hex");
104285
104589
  }
@@ -104290,24 +104594,24 @@ function createRecorder(deps) {
104290
104594
  let inflight = null;
104291
104595
  let retryAttempt = 0;
104292
104596
  let timerArmed = false;
104293
- const stateFile = (sessionId) => join8(deps.dir, `${sessionId}.json`);
104597
+ const stateFile = (sessionId) => join9(deps.dir, `${sessionId}.json`);
104294
104598
  async function save(sessionId, st) {
104295
104599
  await mkdir4(deps.dir, { recursive: true, mode: 448 });
104296
104600
  const tmp = `${stateFile(sessionId)}.${deps.randomId()}.tmp`;
104297
104601
  await writeFile4(tmp, JSON.stringify(st), { mode: 384 });
104298
104602
  await rename4(tmp, stateFile(sessionId));
104299
104603
  }
104300
- async function load(sessionId) {
104301
- const cached2 = states.get(sessionId);
104302
- if (cached2) return cached2;
104604
+ async function load2(sessionId) {
104605
+ const cached3 = states.get(sessionId);
104606
+ if (cached3) return cached3;
104303
104607
  let st;
104304
104608
  try {
104305
104609
  st = JSON.parse(await readFile6(stateFile(sessionId), "utf8"));
104306
104610
  } catch (e) {
104307
104611
  if (e.code !== "ENOENT") {
104308
- const bad = join8(deps.dir, "bad");
104612
+ const bad = join9(deps.dir, "bad");
104309
104613
  await mkdir4(bad, { recursive: true, mode: 448 });
104310
- await rename4(stateFile(sessionId), join8(bad, `${sessionId}.${deps.now()}.json`)).catch((err) => deps.log(`recording: could not quarantine ${sessionId}: ${String(err)}`));
104614
+ await rename4(stateFile(sessionId), join9(bad, `${sessionId}.${deps.now()}.json`)).catch((err) => deps.log(`recording: could not quarantine ${sessionId}: ${String(err)}`));
104311
104615
  lastError = `recording state for ${sessionId} was unreadable and was quarantined`;
104312
104616
  deps.log(`recording: ${lastError}`);
104313
104617
  }
@@ -104324,7 +104628,7 @@ function createRecorder(deps) {
104324
104628
  async function record(sessionId, messages, meta) {
104325
104629
  try {
104326
104630
  if (off2) return;
104327
- const st = await load(sessionId);
104631
+ const st = await load2(sessionId);
104328
104632
  if (st.stopped) return;
104329
104633
  if (st.hashes.length === 0 && st.nextSeq === 1 && meta.parentRecordingId) st.parentRecordingId = meta.parentRecordingId;
104330
104634
  st.meta = meta;
@@ -104477,7 +104781,7 @@ function createRecorder(deps) {
104477
104781
  for (const n of names) {
104478
104782
  if (!n.endsWith(".json")) continue;
104479
104783
  const id = n.slice(0, -5);
104480
- const st = await load(id);
104784
+ const st = await load2(id);
104481
104785
  if (st.queue.length === 0) states.delete(id);
104482
104786
  }
104483
104787
  if ([...states.values()].some((s) => s.queue.length > 0)) void flush();
@@ -104498,7 +104802,7 @@ var init_recorder = __esm({
104498
104802
 
104499
104803
  // src/recording/recording-runtime.ts
104500
104804
  import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile5, stat as stat4 } from "node:fs/promises";
104501
- import { join as join9, sep as sep2 } from "node:path";
104805
+ import { join as join10, sep as sep2 } from "node:path";
104502
104806
  function matchesExclude(pattern, root) {
104503
104807
  const re3 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, `[^${sep2 === "\\" ? "\\\\" : "/"}]*`).replace(/\u0000/g, ".*");
104504
104808
  return new RegExp(`^${re3}(?:[/\\\\].*)?$`).test(root);
@@ -104513,8 +104817,8 @@ async function exists(path12) {
104513
104817
  }
104514
104818
  }
104515
104819
  function createRecordingRuntime(deps) {
104516
- const dir = join9(deps.configDir, "recordings");
104517
- const noticeFile = join9(dir, ".notice");
104820
+ const dir = join10(deps.configDir, "recordings");
104821
+ const noticeFile = join10(dir, ".notice");
104518
104822
  let recorder = null;
104519
104823
  let standingOff;
104520
104824
  let sessionOff = false;
@@ -104522,7 +104826,7 @@ function createRecordingRuntime(deps) {
104522
104826
  if (deps.env.SPACECODE_RECORD?.trim().toLowerCase() === "off") return "SPACECODE_RECORD=off";
104523
104827
  let cfg = {};
104524
104828
  try {
104525
- cfg = JSON.parse(await readFile7(join9(deps.configDir, "config.json"), "utf8"));
104829
+ cfg = JSON.parse(await readFile7(join10(deps.configDir, "config.json"), "utf8"));
104526
104830
  } catch (e) {
104527
104831
  if (e.code !== "ENOENT") return `~/.spacecode/config.json is unreadable (${e instanceof Error ? e.message : String(e)}); fix it to record`;
104528
104832
  }
@@ -104530,7 +104834,7 @@ function createRecordingRuntime(deps) {
104530
104834
  const excludes = Array.isArray(cfg.recording?.exclude) ? cfg.recording.exclude.filter((x2) => typeof x2 === "string") : [];
104531
104835
  const hit = excludes.find((p) => matchesExclude(p, deps.projectRoot));
104532
104836
  if (hit) return `this folder matches recording.exclude (${hit})`;
104533
- if (await exists(join9(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104837
+ if (await exists(join10(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104534
104838
  return null;
104535
104839
  }
104536
104840
  async function noticeShown() {
@@ -104672,7 +104976,7 @@ var init_skills_command = __esm({
104672
104976
 
104673
104977
  // src/permission-rules-loader.ts
104674
104978
  import { readFile as readFile8 } from "node:fs/promises";
104675
- import { join as join10 } from "node:path";
104979
+ import { join as join11 } from "node:path";
104676
104980
  async function readOrNull2(path12) {
104677
104981
  try {
104678
104982
  return await readFile8(path12, "utf8");
@@ -104683,7 +104987,7 @@ async function readOrNull2(path12) {
104683
104987
  }
104684
104988
  async function loadPermissionRules(deps) {
104685
104989
  const none = (line) => ({ rules: void 0, defaultMode: void 0, statusLines: [line] });
104686
- const configPath = join10(deps.configDir, "imports.json");
104990
+ const configPath = join11(deps.configDir, "imports.json");
104687
104991
  try {
104688
104992
  const loaded = parseImportsConfig(await readOrNull2(configPath), configPath);
104689
104993
  if (loaded.error) return none("permission rules: off (imports.json unreadable)");
@@ -104692,13 +104996,13 @@ async function loadPermissionRules(deps) {
104692
104996
  return none(`permission rules: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
104693
104997
  }
104694
104998
  const lines2 = [];
104695
- const files = [{ scope: "user", path: join10(deps.homeDir, ".claude", "settings.json") }];
104999
+ const files = [{ scope: "user", path: join11(deps.homeDir, ".claude", "settings.json") }];
104696
105000
  try {
104697
105001
  const sources2 = await readRepoSources(deps.cwd);
104698
105002
  if (sources2[".claude/settings.json"] !== void 0 || sources2[".claude/settings.local.json"] !== void 0) {
104699
105003
  const status = await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, sources2);
104700
105004
  if (status === "trusted" || status === "session") {
104701
- files.push({ scope: "repo", path: join10(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join10(deps.cwd, ".claude", "settings.local.json") });
105005
+ files.push({ scope: "repo", path: join11(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join11(deps.cwd, ".claude", "settings.local.json") });
104702
105006
  } else lines2.push(`permission rules: repo settings NOT loaded (${status === "never" ? "marked never trust" : "untrusted repo"})`);
104703
105007
  }
104704
105008
  } catch (err) {
@@ -104740,7 +105044,7 @@ var init_permission_rules_loader = __esm({
104740
105044
  });
104741
105045
 
104742
105046
  // src/command-loader.ts
104743
- import { join as join11 } from "node:path";
105047
+ import { join as join12 } from "node:path";
104744
105048
  async function userCommands(deps, lines2) {
104745
105049
  try {
104746
105050
  const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
@@ -104795,7 +105099,7 @@ async function repoCommands(deps, lines2) {
104795
105099
  }
104796
105100
  }
104797
105101
  async function loadCommands(deps) {
104798
- const configPath = join11(deps.configDir, "imports.json");
105102
+ const configPath = join12(deps.configDir, "imports.json");
104799
105103
  try {
104800
105104
  const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
104801
105105
  if (err.code === "ENOENT") return null;
@@ -104830,7 +105134,7 @@ var init_command_loader = __esm({
104830
105134
  // src/useReplController.ts
104831
105135
  import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104832
105136
  import { homedir as homedir2 } from "node:os";
104833
- import { join as join12 } from "node:path";
105137
+ import { join as join13 } from "node:path";
104834
105138
  import { execFile as execFile4 } from "node:child_process";
104835
105139
  function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
104836
105140
  const { exit } = use_app_default();
@@ -104852,8 +105156,13 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104852
105156
  return () => clearInterval(timer);
104853
105157
  }, [turnStartedAt]);
104854
105158
  const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
105159
+ const promptTemplates = (0, import_react30.useCallback)(
105160
+ () => loadPromptTemplates({ env: process.env, readFile: (p) => readFile9(p, "utf-8"), writeFile: (p, d) => writeFile6(p, d, "utf-8"), mkdir: (p) => mkdir6(p, { recursive: true }).then(() => void 0), homedir: homedir2, fetch }),
105161
+ []
105162
+ );
104855
105163
  (0, import_react30.useEffect)(() => {
104856
105164
  void loadProjectCard(projectRoot);
105165
+ void promptTemplates();
104857
105166
  }, [projectRoot]);
104858
105167
  const sessionIdRef = (0, import_react30.useRef)(null);
104859
105168
  const sessionCreatedAtRef = (0, import_react30.useRef)(null);
@@ -104864,7 +105173,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104864
105173
  const recordingRef = (0, import_react30.useRef)(null);
104865
105174
  const recording = () => recordingRef.current ??= createRecordingRuntime({
104866
105175
  env: process.env,
104867
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
105176
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode"),
104868
105177
  projectRoot,
104869
105178
  fetch,
104870
105179
  baseUrl: backendUrl(process.env),
@@ -104891,7 +105200,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104891
105200
  (0, import_react30.useEffect)(() => {
104892
105201
  let live2 = true;
104893
105202
  loadCommands({
104894
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
105203
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode"),
104895
105204
  cwd: process.cwd(),
104896
105205
  env: process.env,
104897
105206
  readFile: (p) => readFile9(p, "utf-8"),
@@ -105188,7 +105497,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105188
105497
  return;
105189
105498
  }
105190
105499
  notice("Compacting conversation\u2026");
105191
- const summary = await summarizeConversation(history, { apiUrl: apiUrl(), apiKey: apiKey2 });
105500
+ const summary = await summarizeConversation(history, { apiUrl: apiUrl(), apiKey: apiKey2, templates: (await promptTemplates()).templates });
105192
105501
  const compacted = buildCompactedHistory(summary.summaryText);
105193
105502
  const before = estimateTotalTokens("", history);
105194
105503
  const after = estimateTotalTokens("", compacted);
@@ -105308,7 +105617,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105308
105617
  cwd: process.cwd(),
105309
105618
  gitRoot: gitTopLevel,
105310
105619
  importGate: async () => {
105311
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105620
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105312
105621
  const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
105313
105622
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105314
105623
  rulesRef.current = loaded.rules;
@@ -105318,7 +105627,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105318
105627
  });
105319
105628
  profileRef.current = await profilePromiseRef.current;
105320
105629
  if (mcpRef.current === null && (profileRef.current?.mcpDefs?.length ?? 0) > 0) {
105321
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105630
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105322
105631
  const trust = createNodeTrustStore(configDir);
105323
105632
  const manager = new McpManager({
105324
105633
  defs: profileRef.current.mcpDefs,
@@ -105328,7 +105637,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105328
105637
  ...rulesRef.current ? { rules: rulesRef.current } : {},
105329
105638
  permissions,
105330
105639
  confirm: askUser,
105331
- audit: new FileAuditLog(join12(homedir2(), ".spacecode", "audit.jsonl")),
105640
+ audit: new FileAuditLog(join13(homedir2(), ".spacecode", "audit.jsonl")),
105332
105641
  clientVersion: process.env.SPACECODE_VERSION ?? "0"
105333
105642
  });
105334
105643
  mcpRef.current = manager;
@@ -105337,6 +105646,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105337
105646
  const toolset = createToolset();
105338
105647
  toolsetRef.current = toolset;
105339
105648
  const projectCard = await loadProjectCard(projectRoot);
105649
+ const prompts = await promptTemplates();
105340
105650
  const config = {
105341
105651
  apiUrl: apiUrl(),
105342
105652
  apiKey,
@@ -105346,7 +105656,8 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105346
105656
  localCwd: projectRoot,
105347
105657
  commitGuidance: true,
105348
105658
  hasGitHubTools: toolset.hasGitHubTools,
105349
- ...projectCard.text ? { projectCard: projectCard.text } : {}
105659
+ ...projectCard.text ? { projectCard: projectCard.text } : {},
105660
+ templates: prompts.templates
105350
105661
  }) + (profileRef.current?.promptSection ?? ""),
105351
105662
  tools: toolset.tools,
105352
105663
  maxTurns: 100,
@@ -105458,6 +105769,7 @@ var init_useReplController = __esm({
105458
105769
  init_session_commands();
105459
105770
  init_local_toolset();
105460
105771
  init_project_card_runtime();
105772
+ init_prompt_runtime();
105461
105773
  init_shell_turn();
105462
105774
  init_node_mentions();
105463
105775
  init_api_key();