spacecode 2.8.1 → 2.10.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({
@@ -7007,6 +7252,28 @@ var init_trust = __esm({
7007
7252
  }
7008
7253
  });
7009
7254
 
7255
+ // ../agent/src/import/source-changes.ts
7256
+ function fingerprintSources(files, hash) {
7257
+ const fingerprint = {};
7258
+ for (const [path12, content] of Object.entries(files)) fingerprint[path12] = hash(content);
7259
+ return fingerprint;
7260
+ }
7261
+ function diffSources(before, after) {
7262
+ const has2 = (o, k2) => Object.hasOwn(o, k2);
7263
+ const added = Object.keys(after).filter((k2) => !has2(before, k2)).sort();
7264
+ const removed = Object.keys(before).filter((k2) => !has2(after, k2)).sort();
7265
+ const modified = Object.keys(after).filter((k2) => has2(before, k2) && before[k2] !== after[k2]).sort();
7266
+ return { added, modified, removed, changed: added.length + modified.length + removed.length > 0 };
7267
+ }
7268
+ function formatSourceChanges(c) {
7269
+ return [...c.added.map((p) => `added: ${p}`), ...c.modified.map((p) => `modified: ${p}`), ...c.removed.map((p) => `removed: ${p}`)];
7270
+ }
7271
+ var init_source_changes = __esm({
7272
+ "../agent/src/import/source-changes.ts"() {
7273
+ "use strict";
7274
+ }
7275
+ });
7276
+
7010
7277
  // ../agent/src/import/imports-config.ts
7011
7278
  function allOffConfig() {
7012
7279
  return {
@@ -7741,6 +8008,7 @@ __export(src_exports, {
7741
8008
  createSkillTool: () => createSkillTool,
7742
8009
  createTodoTool: () => createTodoTool,
7743
8010
  defaultModeFor: () => defaultModeFor,
8011
+ diffSources: () => diffSources,
7744
8012
  estimateTokens: () => estimateTokens,
7745
8013
  estimateTotalTokens: () => estimateTotalTokens,
7746
8014
  expandCommand: () => expandCommand,
@@ -7748,8 +8016,10 @@ __export(src_exports, {
7748
8016
  expandServerEnv: () => expandServerEnv,
7749
8017
  extractRuleLines: () => extractRuleLines,
7750
8018
  findUnsupported: () => findUnsupported,
8019
+ fingerprintSources: () => fingerprintSources,
7751
8020
  flattenMessageText: () => flattenMessageText,
7752
8021
  formatSkillBody: () => formatSkillBody,
8022
+ formatSourceChanges: () => formatSourceChanges,
7753
8023
  formatToolResult: () => formatToolResult,
7754
8024
  guardHost: () => guardHost,
7755
8025
  isReadOnlyCommand: () => isReadOnlyCommand,
@@ -7815,6 +8085,7 @@ var init_src = __esm({
7815
8085
  init_adapt_plan();
7816
8086
  init_instructions_tool();
7817
8087
  init_trust();
8088
+ init_source_changes();
7818
8089
  init_imports_config();
7819
8090
  init_permission_rules();
7820
8091
  init_command_args();
@@ -9386,7 +9657,7 @@ var require_signal_exit = __commonJS({
9386
9657
  }
9387
9658
  assert.equal(typeof cb, "function", "a callback must be provided for exit handler");
9388
9659
  if (loaded === false) {
9389
- load();
9660
+ load2();
9390
9661
  }
9391
9662
  var ev = "exit";
9392
9663
  if (opts && opts.alwaysLast) {
@@ -9446,7 +9717,7 @@ var require_signal_exit = __commonJS({
9446
9717
  return signals;
9447
9718
  };
9448
9719
  loaded = false;
9449
- load = function load2() {
9720
+ load2 = function load3() {
9450
9721
  if (loaded || !processOk(global.process)) {
9451
9722
  return;
9452
9723
  }
@@ -9463,7 +9734,7 @@ var require_signal_exit = __commonJS({
9463
9734
  process13.emit = processEmit;
9464
9735
  process13.reallyExit = processReallyExit;
9465
9736
  };
9466
- module.exports.load = load;
9737
+ module.exports.load = load2;
9467
9738
  originalProcessReallyExit = process13.reallyExit;
9468
9739
  processReallyExit = function processReallyExit2(code) {
9469
9740
  if (!processOk(global.process)) {
@@ -9499,7 +9770,7 @@ var require_signal_exit = __commonJS({
9499
9770
  var emit;
9500
9771
  var sigListeners;
9501
9772
  var loaded;
9502
- var load;
9773
+ var load2;
9503
9774
  var originalProcessReallyExit;
9504
9775
  var processReallyExit;
9505
9776
  var originalProcessEmit;
@@ -44082,7 +44353,7 @@ var require_core = __commonJS({
44082
44353
  return match && match.index === 0;
44083
44354
  }
44084
44355
  var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
44085
- function join13(regexps, separator = "|") {
44356
+ function join14(regexps, separator = "|") {
44086
44357
  let numCaptures = 0;
44087
44358
  return regexps.map((regex2) => {
44088
44359
  numCaptures += 1;
@@ -44386,7 +44657,7 @@ var require_core = __commonJS({
44386
44657
  this.exec = () => null;
44387
44658
  }
44388
44659
  const terminators = this.regexes.map((el) => el[1]);
44389
- this.matcherRe = langRe(join13(terminators), true);
44660
+ this.matcherRe = langRe(join14(terminators), true);
44390
44661
  this.lastIndex = 0;
44391
44662
  }
44392
44663
  /** @param {string} s */
@@ -48163,7 +48434,7 @@ var require_bash = __commonJS({
48163
48434
  return joined;
48164
48435
  }
48165
48436
  function bash(hljs) {
48166
- const VAR = {};
48437
+ const VAR2 = {};
48167
48438
  const BRACED_VAR = {
48168
48439
  begin: /\$\{/,
48169
48440
  end: /\}/,
@@ -48171,12 +48442,12 @@ var require_bash = __commonJS({
48171
48442
  "self",
48172
48443
  {
48173
48444
  begin: /:-/,
48174
- contains: [VAR]
48445
+ contains: [VAR2]
48175
48446
  }
48176
48447
  // default values
48177
48448
  ]
48178
48449
  };
48179
- Object.assign(VAR, {
48450
+ Object.assign(VAR2, {
48180
48451
  className: "variable",
48181
48452
  variants: [
48182
48453
  { begin: concat(
@@ -48212,7 +48483,7 @@ var require_bash = __commonJS({
48212
48483
  end: /"/,
48213
48484
  contains: [
48214
48485
  hljs.BACKSLASH_ESCAPE,
48215
- VAR,
48486
+ VAR2,
48216
48487
  SUBST
48217
48488
  ]
48218
48489
  };
@@ -48232,7 +48503,7 @@ var require_bash = __commonJS({
48232
48503
  contains: [
48233
48504
  { begin: /\d+#[0-9a-f]+/, className: "number" },
48234
48505
  hljs.NUMBER_MODE,
48235
- VAR
48506
+ VAR2
48236
48507
  ]
48237
48508
  };
48238
48509
  const SH_LIKE_SHELLS = [
@@ -48282,7 +48553,7 @@ var require_bash = __commonJS({
48282
48553
  QUOTE_STRING,
48283
48554
  ESCAPED_QUOTE,
48284
48555
  APOS_STRING,
48285
- VAR
48556
+ VAR2
48286
48557
  ]
48287
48558
  };
48288
48559
  }
@@ -67905,7 +68176,7 @@ var require_perl = __commonJS({
67905
68176
  end: /\}/
67906
68177
  // contains defined later
67907
68178
  };
67908
- const VAR = {
68179
+ const VAR2 = {
67909
68180
  variants: [
67910
68181
  {
67911
68182
  begin: /\$\d/
@@ -67927,7 +68198,7 @@ var require_perl = __commonJS({
67927
68198
  const STRING_CONTAINS = [
67928
68199
  hljs.BACKSLASH_ESCAPE,
67929
68200
  SUBST,
67930
- VAR
68201
+ VAR2
67931
68202
  ];
67932
68203
  const REGEX_DELIMS = [
67933
68204
  /!/,
@@ -67962,7 +68233,7 @@ var require_perl = __commonJS({
67962
68233
  );
67963
68234
  };
67964
68235
  const PERL_DEFAULT_CONTAINS = [
67965
- VAR,
68236
+ VAR2,
67966
68237
  hljs.HASH_COMMENT_MODE,
67967
68238
  hljs.COMMENT(
67968
68239
  /^=\w/,
@@ -68429,7 +68700,7 @@ var require_n1ql = __commonJS({
68429
68700
  var require_nginx = __commonJS({
68430
68701
  "../../node_modules/highlight.js/lib/languages/nginx.js"(exports, module) {
68431
68702
  function nginx(hljs) {
68432
- const VAR = {
68703
+ const VAR2 = {
68433
68704
  className: "variable",
68434
68705
  variants: [
68435
68706
  {
@@ -68458,7 +68729,7 @@ var require_nginx = __commonJS({
68458
68729
  className: "string",
68459
68730
  contains: [
68460
68731
  hljs.BACKSLASH_ESCAPE,
68461
- VAR
68732
+ VAR2
68462
68733
  ],
68463
68734
  variants: [
68464
68735
  {
@@ -68477,13 +68748,13 @@ var require_nginx = __commonJS({
68477
68748
  end: "\\s",
68478
68749
  endsWithParent: true,
68479
68750
  excludeEnd: true,
68480
- contains: [VAR]
68751
+ contains: [VAR2]
68481
68752
  },
68482
68753
  {
68483
68754
  className: "regexp",
68484
68755
  contains: [
68485
68756
  hljs.BACKSLASH_ESCAPE,
68486
- VAR
68757
+ VAR2
68487
68758
  ],
68488
68759
  variants: [
68489
68760
  {
@@ -68518,7 +68789,7 @@ var require_nginx = __commonJS({
68518
68789
  begin: "\\b\\d+[kKmMgGdshdwy]*\\b",
68519
68790
  relevance: 0
68520
68791
  },
68521
- VAR
68792
+ VAR2
68522
68793
  ]
68523
68794
  };
68524
68795
  return {
@@ -69984,7 +70255,7 @@ var require_powershell = __commonJS({
69984
70255
  begin: "`[\\s\\S]",
69985
70256
  relevance: 0
69986
70257
  };
69987
- const VAR = {
70258
+ const VAR2 = {
69988
70259
  className: "variable",
69989
70260
  variants: [
69990
70261
  {
@@ -70017,7 +70288,7 @@ var require_powershell = __commonJS({
70017
70288
  ],
70018
70289
  contains: [
70019
70290
  BACKTICK_ESCAPE,
70020
- VAR,
70291
+ VAR2,
70021
70292
  {
70022
70293
  className: "variable",
70023
70294
  begin: /\$[A-z]/,
@@ -70108,7 +70379,7 @@ var require_powershell = __commonJS({
70108
70379
  end: /\)/,
70109
70380
  className: "params",
70110
70381
  relevance: 0,
70111
- contains: [VAR]
70382
+ contains: [VAR2]
70112
70383
  }
70113
70384
  // CMDLETS
70114
70385
  ]
@@ -70179,7 +70450,7 @@ var require_powershell = __commonJS({
70179
70450
  APOS_STRING,
70180
70451
  // PS_NEW_OBJECT_TYPE,
70181
70452
  CMDLETS,
70182
- VAR,
70453
+ VAR2,
70183
70454
  LITERAL,
70184
70455
  HASH_SIGNS
70185
70456
  ];
@@ -70301,7 +70572,7 @@ var require_prolog = __commonJS({
70301
70572
  begin: /[a-z][A-Za-z0-9_]*/,
70302
70573
  relevance: 0
70303
70574
  };
70304
- const VAR = {
70575
+ const VAR2 = {
70305
70576
  className: "symbol",
70306
70577
  variants: [
70307
70578
  {
@@ -70350,7 +70621,7 @@ var require_prolog = __commonJS({
70350
70621
  };
70351
70622
  const inner = [
70352
70623
  ATOM,
70353
- VAR,
70624
+ VAR2,
70354
70625
  PARENTED,
70355
70626
  PRED_OP,
70356
70627
  LIST,
@@ -71895,7 +72166,7 @@ var require_routeros = __commonJS({
71895
72166
  const COMMON_COMMANDS = "add remove enable disable set get print export edit find run debug error info warning";
71896
72167
  const LITERALS = "true false yes no nothing nil null";
71897
72168
  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 = {
72169
+ const VAR2 = {
71899
72170
  className: "variable",
71900
72171
  variants: [
71901
72172
  {
@@ -71912,7 +72183,7 @@ var require_routeros = __commonJS({
71912
72183
  end: /"/,
71913
72184
  contains: [
71914
72185
  hljs.BACKSLASH_ESCAPE,
71915
- VAR,
72186
+ VAR2,
71916
72187
  {
71917
72188
  className: "variable",
71918
72189
  begin: /\$\(/,
@@ -71962,7 +72233,7 @@ var require_routeros = __commonJS({
71962
72233
  hljs.COMMENT("^#", "$"),
71963
72234
  QUOTE_STRING,
71964
72235
  APOS_STRING,
71965
- VAR,
72236
+ VAR2,
71966
72237
  // attribute=value
71967
72238
  {
71968
72239
  // > is to avoid matches with => in other grammars
@@ -71981,7 +72252,7 @@ var require_routeros = __commonJS({
71981
72252
  contains: [
71982
72253
  QUOTE_STRING,
71983
72254
  APOS_STRING,
71984
- VAR,
72255
+ VAR2,
71985
72256
  {
71986
72257
  className: "literal",
71987
72258
  begin: "\\b(" + LITERALS.split(" ").join("|") + ")\\b"
@@ -78263,7 +78534,7 @@ var require_xquery = __commonJS({
78263
78534
  begin: /\bxquery version "[13]\.[01]"\s?(?:encoding ".+")?/,
78264
78535
  end: /;/
78265
78536
  };
78266
- const VAR = {
78537
+ const VAR2 = {
78267
78538
  className: "variable",
78268
78539
  begin: /[$][\w\-:]+/
78269
78540
  };
@@ -78332,7 +78603,7 @@ var require_xquery = __commonJS({
78332
78603
  ]
78333
78604
  };
78334
78605
  const CONTAINS = [
78335
- VAR,
78606
+ VAR2,
78336
78607
  BUILT_IN,
78337
78608
  STRING,
78338
78609
  NUMBER,
@@ -103199,6 +103470,7 @@ var init_slash_commands = __esm({
103199
103470
  { name: "turns", description: "Browse earlier turns and open their steps (shortcut: ctrl+o)" },
103200
103471
  { name: "commands", description: "List the slash commands imported from Claude Code" },
103201
103472
  { name: "skills", description: "List imported skills; /skills show <name> [file], /skills delete <name>" },
103473
+ { name: "import", description: "Re-check imported Claude Code sources: /import reload shows changes; /import reload trust applies them" },
103202
103474
  { name: "tools", description: "List the tools available to the agent" },
103203
103475
  { name: "exit", description: "Quit the REPL (alias: /quit)" },
103204
103476
  { name: "quit", description: "Quit the REPL" }
@@ -103855,6 +104127,65 @@ var init_project_card_runtime = __esm({
103855
104127
  }
103856
104128
  });
103857
104129
 
104130
+ // src/prompt-runtime.ts
104131
+ import { join as join8 } from "node:path";
104132
+ async function load(deps) {
104133
+ if (deps.env.SPACECODE_PROMPTS === "off") return { templates: {}, statusLine: "prompts: built-in defaults (SPACECODE_PROMPTS=off)" };
104134
+ const dir = deps.env.SPACECODE_CONFIG_DIR?.trim() || join8(deps.homedir(), ".spacecode");
104135
+ const file = join8(dir, "prompts-cache.json");
104136
+ const fromDisk = async (why) => {
104137
+ try {
104138
+ const raw = JSON.parse(await deps.readFile(file));
104139
+ const templates = raw.templates ?? {};
104140
+ return { templates, statusLine: `prompts: ${Object.keys(templates).length} tuned (STALE: served from cache; ${why})` };
104141
+ } catch {
104142
+ return { templates: {}, statusLine: `prompts: built-in defaults (${why})` };
104143
+ }
104144
+ };
104145
+ let apiKey;
104146
+ try {
104147
+ apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
104148
+ } catch (err) {
104149
+ return fromDisk(err instanceof Error ? err.message : String(err));
104150
+ }
104151
+ if (!apiKey) return fromDisk("not signed in");
104152
+ try {
104153
+ const res = await deps.fetch(`${backendUrl(deps.env)}/api/prompts`, {
104154
+ headers: { Authorization: `Bearer ${apiKey}`, "x-api-key": apiKey },
104155
+ signal: AbortSignal.timeout(3e3)
104156
+ });
104157
+ if (!res.ok) return fromDisk(`prompt API returned ${res.status}`);
104158
+ const { prompts } = await res.json();
104159
+ const templates = Object.fromEntries(prompts.filter((p) => p.source === "db").map((p) => [p.key, p.body]));
104160
+ try {
104161
+ await deps.mkdir(dir);
104162
+ await deps.writeFile(file, JSON.stringify({ fetchedAt: new Date((deps.now ?? Date.now)()).toISOString(), templates }));
104163
+ } catch (err) {
104164
+ console.error("[prompts] could not write the prompt cache", err instanceof Error ? err.message : String(err));
104165
+ }
104166
+ const n = Object.keys(templates).length;
104167
+ return { templates, statusLine: n ? `prompts: ${n} tuned override(s): ${Object.keys(templates).join(", ")}` : "prompts: built-in defaults (none tuned)" };
104168
+ } catch (err) {
104169
+ return fromDisk(`fetch failed: ${err instanceof Error ? err.message : String(err)}`);
104170
+ }
104171
+ }
104172
+ function loadPromptTemplates(deps) {
104173
+ const now = (deps.now ?? Date.now)();
104174
+ if (cached2 && now - cached2.at < TTL_MS2) return cached2.pending;
104175
+ const pending = load(deps);
104176
+ cached2 = { at: now, pending };
104177
+ return pending;
104178
+ }
104179
+ var TTL_MS2, cached2;
104180
+ var init_prompt_runtime = __esm({
104181
+ "src/prompt-runtime.ts"() {
104182
+ "use strict";
104183
+ init_api_key();
104184
+ init_profile_runtime();
104185
+ TTL_MS2 = 10 * 6e4;
104186
+ }
104187
+ });
104188
+
103858
104189
  // src/shell-turn.ts
103859
104190
  function shellCommandOf(prompt) {
103860
104191
  return prompt.startsWith("!") ? prompt.slice(1).trim() : null;
@@ -104279,7 +104610,7 @@ var init_recording_chunks = __esm({
104279
104610
  // src/recording/recorder.ts
104280
104611
  import { createHash as createHash4 } from "node:crypto";
104281
104612
  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";
104613
+ import { join as join9 } from "node:path";
104283
104614
  function projectHash(root) {
104284
104615
  return createHash4("sha256").update(root).digest("hex");
104285
104616
  }
@@ -104290,24 +104621,24 @@ function createRecorder(deps) {
104290
104621
  let inflight = null;
104291
104622
  let retryAttempt = 0;
104292
104623
  let timerArmed = false;
104293
- const stateFile = (sessionId) => join8(deps.dir, `${sessionId}.json`);
104624
+ const stateFile = (sessionId) => join9(deps.dir, `${sessionId}.json`);
104294
104625
  async function save(sessionId, st) {
104295
104626
  await mkdir4(deps.dir, { recursive: true, mode: 448 });
104296
104627
  const tmp = `${stateFile(sessionId)}.${deps.randomId()}.tmp`;
104297
104628
  await writeFile4(tmp, JSON.stringify(st), { mode: 384 });
104298
104629
  await rename4(tmp, stateFile(sessionId));
104299
104630
  }
104300
- async function load(sessionId) {
104301
- const cached2 = states.get(sessionId);
104302
- if (cached2) return cached2;
104631
+ async function load2(sessionId) {
104632
+ const cached3 = states.get(sessionId);
104633
+ if (cached3) return cached3;
104303
104634
  let st;
104304
104635
  try {
104305
104636
  st = JSON.parse(await readFile6(stateFile(sessionId), "utf8"));
104306
104637
  } catch (e) {
104307
104638
  if (e.code !== "ENOENT") {
104308
- const bad = join8(deps.dir, "bad");
104639
+ const bad = join9(deps.dir, "bad");
104309
104640
  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)}`));
104641
+ await rename4(stateFile(sessionId), join9(bad, `${sessionId}.${deps.now()}.json`)).catch((err) => deps.log(`recording: could not quarantine ${sessionId}: ${String(err)}`));
104311
104642
  lastError = `recording state for ${sessionId} was unreadable and was quarantined`;
104312
104643
  deps.log(`recording: ${lastError}`);
104313
104644
  }
@@ -104324,7 +104655,7 @@ function createRecorder(deps) {
104324
104655
  async function record(sessionId, messages, meta) {
104325
104656
  try {
104326
104657
  if (off2) return;
104327
- const st = await load(sessionId);
104658
+ const st = await load2(sessionId);
104328
104659
  if (st.stopped) return;
104329
104660
  if (st.hashes.length === 0 && st.nextSeq === 1 && meta.parentRecordingId) st.parentRecordingId = meta.parentRecordingId;
104330
104661
  st.meta = meta;
@@ -104477,7 +104808,7 @@ function createRecorder(deps) {
104477
104808
  for (const n of names) {
104478
104809
  if (!n.endsWith(".json")) continue;
104479
104810
  const id = n.slice(0, -5);
104480
- const st = await load(id);
104811
+ const st = await load2(id);
104481
104812
  if (st.queue.length === 0) states.delete(id);
104482
104813
  }
104483
104814
  if ([...states.values()].some((s) => s.queue.length > 0)) void flush();
@@ -104498,7 +104829,7 @@ var init_recorder = __esm({
104498
104829
 
104499
104830
  // src/recording/recording-runtime.ts
104500
104831
  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";
104832
+ import { join as join10, sep as sep2 } from "node:path";
104502
104833
  function matchesExclude(pattern, root) {
104503
104834
  const re3 = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, `[^${sep2 === "\\" ? "\\\\" : "/"}]*`).replace(/\u0000/g, ".*");
104504
104835
  return new RegExp(`^${re3}(?:[/\\\\].*)?$`).test(root);
@@ -104513,8 +104844,8 @@ async function exists(path12) {
104513
104844
  }
104514
104845
  }
104515
104846
  function createRecordingRuntime(deps) {
104516
- const dir = join9(deps.configDir, "recordings");
104517
- const noticeFile = join9(dir, ".notice");
104847
+ const dir = join10(deps.configDir, "recordings");
104848
+ const noticeFile = join10(dir, ".notice");
104518
104849
  let recorder = null;
104519
104850
  let standingOff;
104520
104851
  let sessionOff = false;
@@ -104522,7 +104853,7 @@ function createRecordingRuntime(deps) {
104522
104853
  if (deps.env.SPACECODE_RECORD?.trim().toLowerCase() === "off") return "SPACECODE_RECORD=off";
104523
104854
  let cfg = {};
104524
104855
  try {
104525
- cfg = JSON.parse(await readFile7(join9(deps.configDir, "config.json"), "utf8"));
104856
+ cfg = JSON.parse(await readFile7(join10(deps.configDir, "config.json"), "utf8"));
104526
104857
  } catch (e) {
104527
104858
  if (e.code !== "ENOENT") return `~/.spacecode/config.json is unreadable (${e instanceof Error ? e.message : String(e)}); fix it to record`;
104528
104859
  }
@@ -104530,7 +104861,7 @@ function createRecordingRuntime(deps) {
104530
104861
  const excludes = Array.isArray(cfg.recording?.exclude) ? cfg.recording.exclude.filter((x2) => typeof x2 === "string") : [];
104531
104862
  const hit = excludes.find((p) => matchesExclude(p, deps.projectRoot));
104532
104863
  if (hit) return `this folder matches recording.exclude (${hit})`;
104533
- if (await exists(join9(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104864
+ if (await exists(join10(deps.projectRoot, ".spacecode", "norecord"))) return "this repo has .spacecode/norecord";
104534
104865
  return null;
104535
104866
  }
104536
104867
  async function noticeShown() {
@@ -104672,7 +105003,7 @@ var init_skills_command = __esm({
104672
105003
 
104673
105004
  // src/permission-rules-loader.ts
104674
105005
  import { readFile as readFile8 } from "node:fs/promises";
104675
- import { join as join10 } from "node:path";
105006
+ import { join as join11 } from "node:path";
104676
105007
  async function readOrNull2(path12) {
104677
105008
  try {
104678
105009
  return await readFile8(path12, "utf8");
@@ -104683,7 +105014,7 @@ async function readOrNull2(path12) {
104683
105014
  }
104684
105015
  async function loadPermissionRules(deps) {
104685
105016
  const none = (line) => ({ rules: void 0, defaultMode: void 0, statusLines: [line] });
104686
- const configPath = join10(deps.configDir, "imports.json");
105017
+ const configPath = join11(deps.configDir, "imports.json");
104687
105018
  try {
104688
105019
  const loaded = parseImportsConfig(await readOrNull2(configPath), configPath);
104689
105020
  if (loaded.error) return none("permission rules: off (imports.json unreadable)");
@@ -104692,13 +105023,13 @@ async function loadPermissionRules(deps) {
104692
105023
  return none(`permission rules: off (imports.json not read: ${err instanceof Error ? err.message : String(err)})`);
104693
105024
  }
104694
105025
  const lines2 = [];
104695
- const files = [{ scope: "user", path: join10(deps.homeDir, ".claude", "settings.json") }];
105026
+ const files = [{ scope: "user", path: join11(deps.homeDir, ".claude", "settings.json") }];
104696
105027
  try {
104697
105028
  const sources2 = await readRepoSources(deps.cwd);
104698
105029
  if (sources2[".claude/settings.json"] !== void 0 || sources2[".claude/settings.local.json"] !== void 0) {
104699
105030
  const status = await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, sources2);
104700
105031
  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") });
105032
+ files.push({ scope: "repo", path: join11(deps.cwd, ".claude", "settings.json") }, { scope: "local", path: join11(deps.cwd, ".claude", "settings.local.json") });
104702
105033
  } else lines2.push(`permission rules: repo settings NOT loaded (${status === "never" ? "marked never trust" : "untrusted repo"})`);
104703
105034
  }
104704
105035
  } catch (err) {
@@ -104739,8 +105070,43 @@ var init_permission_rules_loader = __esm({
104739
105070
  }
104740
105071
  });
104741
105072
 
105073
+ // src/import-reload.ts
105074
+ async function checkRepoSources(deps, loaded) {
105075
+ const files = await readRepoSources(deps.cwd);
105076
+ const current = fingerprintSources(files, sha2562);
105077
+ const status = Object.keys(files).length === 0 ? "untrusted" : await createNodeTrustStore(deps.configDir).repoStatus(deps.cwd, files);
105078
+ return { current, changes: diffSources(loaded, current), trusted: status === "trusted" || status === "session" };
105079
+ }
105080
+ async function describeReload(deps, loaded) {
105081
+ const check = await checkRepoSources(deps, loaded);
105082
+ if (!check.changes.changed) return ["No change in the repo import sources since they were loaded."];
105083
+ const files = await readRepoSources(deps.cwd);
105084
+ return [
105085
+ "Repo import sources changed since they were loaded:",
105086
+ ...formatSourceChanges(check.changes).map((l) => ` ${l}`),
105087
+ "",
105088
+ "Trusting the new state would load:",
105089
+ ...describeRepoSources(files).length ? describeRepoSources(files) : [" (nothing)"],
105090
+ "",
105091
+ "Run /import reload trust to trust it and apply the rules and commands. MCP servers and skills apply on the next start."
105092
+ ];
105093
+ }
105094
+ async function trustCurrentSources(deps) {
105095
+ const files = await readRepoSources(deps.cwd);
105096
+ await createNodeTrustStore(deps.configDir).setRepo(deps.cwd, files, "trusted");
105097
+ return fingerprintSources(files, sha2562);
105098
+ }
105099
+ var init_import_reload = __esm({
105100
+ "src/import-reload.ts"() {
105101
+ "use strict";
105102
+ init_src();
105103
+ init_import_gate();
105104
+ init_repo_trust_prompt();
105105
+ }
105106
+ });
105107
+
104742
105108
  // src/command-loader.ts
104743
- import { join as join11 } from "node:path";
105109
+ import { join as join12 } from "node:path";
104744
105110
  async function userCommands(deps, lines2) {
104745
105111
  try {
104746
105112
  const apiKey = await resolveApiKey({ env: deps.env, readFile: deps.readFile, homedir: deps.homedir });
@@ -104795,7 +105161,7 @@ async function repoCommands(deps, lines2) {
104795
105161
  }
104796
105162
  }
104797
105163
  async function loadCommands(deps) {
104798
- const configPath = join11(deps.configDir, "imports.json");
105164
+ const configPath = join12(deps.configDir, "imports.json");
104799
105165
  try {
104800
105166
  const loaded = parseImportsConfig(await deps.readFile(configPath).catch((err) => {
104801
105167
  if (err.code === "ENOENT") return null;
@@ -104830,7 +105196,7 @@ var init_command_loader = __esm({
104830
105196
  // src/useReplController.ts
104831
105197
  import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir6 } from "node:fs/promises";
104832
105198
  import { homedir as homedir2 } from "node:os";
104833
- import { join as join12 } from "node:path";
105199
+ import { join as join13 } from "node:path";
104834
105200
  import { execFile as execFile4 } from "node:child_process";
104835
105201
  function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mentionsOpt, sessions, resumeLatest: resumeLatest2, onViewTurns, onViewResume, isEscOwned }) {
104836
105202
  const { exit } = use_app_default();
@@ -104852,8 +105218,13 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104852
105218
  return () => clearInterval(timer);
104853
105219
  }, [turnStartedAt]);
104854
105220
  const projectRoot = (0, import_react30.useMemo)(() => process.cwd(), []);
105221
+ const promptTemplates = (0, import_react30.useCallback)(
105222
+ () => 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 }),
105223
+ []
105224
+ );
104855
105225
  (0, import_react30.useEffect)(() => {
104856
105226
  void loadProjectCard(projectRoot);
105227
+ void promptTemplates();
104857
105228
  }, [projectRoot]);
104858
105229
  const sessionIdRef = (0, import_react30.useRef)(null);
104859
105230
  const sessionCreatedAtRef = (0, import_react30.useRef)(null);
@@ -104864,7 +105235,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104864
105235
  const recordingRef = (0, import_react30.useRef)(null);
104865
105236
  const recording = () => recordingRef.current ??= createRecordingRuntime({
104866
105237
  env: process.env,
104867
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
105238
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode"),
104868
105239
  projectRoot,
104869
105240
  fetch,
104870
105241
  baseUrl: backendUrl(process.env),
@@ -104887,11 +105258,12 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
104887
105258
  const pendingRef = (0, import_react30.useRef)(null);
104888
105259
  const toolsetRef = (0, import_react30.useRef)(null);
104889
105260
  const rulesRef = (0, import_react30.useRef)(void 0);
105261
+ const loadedSourcesRef = (0, import_react30.useRef)(null);
104890
105262
  const [commandsLoaded, setCommandsLoaded] = (0, import_react30.useState)({ commands: [], statusLines: [] });
104891
105263
  (0, import_react30.useEffect)(() => {
104892
105264
  let live2 = true;
104893
105265
  loadCommands({
104894
- configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode"),
105266
+ configDir: process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode"),
104895
105267
  cwd: process.cwd(),
104896
105268
  env: process.env,
104897
105269
  readFile: (p) => readFile9(p, "utf-8"),
@@ -105111,6 +105483,29 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105111
105483
  case "context":
105112
105484
  notice(formatContextReport({ contextTokens: estimateTotalTokens("", history), maxContextTokens: DEFAULT_MAX_CONTEXT_TOKENS }));
105113
105485
  return;
105486
+ case "import": {
105487
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105488
+ const deps = { configDir, cwd: process.cwd() };
105489
+ const [sub, ...rest] = slash.args.split(/\s+/).filter(Boolean);
105490
+ if (sub !== "reload" || rest.length > 1 || rest[0] !== void 0 && rest[0] !== "trust") {
105491
+ notice("Usage: /import reload (show changes to the repo import sources) or /import reload trust (trust the new state and apply it).");
105492
+ return;
105493
+ }
105494
+ try {
105495
+ const baseline = loadedSourcesRef.current ?? fingerprintSources(await readRepoSources(process.cwd()), sha2562);
105496
+ if (rest[0] === "trust") {
105497
+ loadedSourcesRef.current = await trustCurrentSources(deps);
105498
+ const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105499
+ rulesRef.current = loaded.rules;
105500
+ const cmds = await loadCommands({ configDir, cwd: process.cwd(), env: process.env, readFile: (p) => readFile9(p, "utf-8"), homedir: homedir2, fetch, builtins: SLASH_COMMANDS.map((c) => c.name) });
105501
+ setCommandsLoaded(cmds);
105502
+ notice(["Trusted the repo import sources as they are now.", ...loaded.statusLines, ...cmds.statusLines, "MCP servers and skills apply on the next start."].join("\n"));
105503
+ } else notice((await describeReload(deps, baseline)).join("\n"));
105504
+ } catch (err) {
105505
+ notice(`/import reload failed (${err instanceof Error ? err.message : String(err)}); nothing was changed.`);
105506
+ }
105507
+ return;
105508
+ }
105114
105509
  case "record":
105115
105510
  notice(await recording().command(slash.args));
105116
105511
  return;
@@ -105188,7 +105583,7 @@ function useReplController({ repoName: repoName2, yolo: yolo2, mentions: mention
105188
105583
  return;
105189
105584
  }
105190
105585
  notice("Compacting conversation\u2026");
105191
- const summary = await summarizeConversation(history, { apiUrl: apiUrl(), apiKey: apiKey2 });
105586
+ const summary = await summarizeConversation(history, { apiUrl: apiUrl(), apiKey: apiKey2, templates: (await promptTemplates()).templates });
105192
105587
  const compacted = buildCompactedHistory(summary.summaryText);
105193
105588
  const before = estimateTotalTokens("", history);
105194
105589
  const after = estimateTotalTokens("", compacted);
@@ -105296,6 +105691,22 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105296
105691
  let failed = false;
105297
105692
  let keepForRetry = false;
105298
105693
  try {
105694
+ if (loadedSourcesRef.current) {
105695
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105696
+ try {
105697
+ const check = await checkRepoSources({ configDir, cwd: process.cwd() }, loadedSourcesRef.current);
105698
+ if (check.changes.changed) {
105699
+ const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105700
+ rulesRef.current = loaded.rules;
105701
+ loadedSourcesRef.current = check.current;
105702
+ const lead = check.trusted ? "Repo import sources changed since they were loaded; the new state is already trusted, so its permission rules now apply:" : "Repo import sources changed since they were loaded; repo permission rules are dropped until you trust the new state:";
105703
+ notice([lead, ...formatSourceChanges(check.changes).map((l) => ` ${l}`), ...check.trusted ? [] : ["Run /import reload to review, /import reload trust to apply."]].join("\n"));
105704
+ }
105705
+ } catch (err) {
105706
+ rulesRef.current = void 0;
105707
+ notice(`Repo import source check failed (${err instanceof Error ? err.message : String(err)}); repo permission rules are dropped.`);
105708
+ }
105709
+ }
105299
105710
  profilePromiseRef.current ??= prepareProfile({
105300
105711
  env: process.env,
105301
105712
  readFile: (p) => readFile9(p, "utf-8"),
@@ -105308,17 +105719,19 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105308
105719
  cwd: process.cwd(),
105309
105720
  gitRoot: gitTopLevel,
105310
105721
  importGate: async () => {
105311
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105722
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105723
+ const fingerprint = fingerprintSources(await readRepoSources(process.cwd()), sha2562);
105312
105724
  const gate = await importGateStatus({ configDir, cwd: process.cwd(), interactive: Boolean(process.stdin.isTTY) });
105313
105725
  const loaded = await loadPermissionRules({ configDir, homeDir: homedir2(), cwd: process.cwd() });
105314
105726
  rulesRef.current = loaded.rules;
105727
+ loadedSourcesRef.current = fingerprint;
105315
105728
  if (loaded.defaultMode && !yolo2) permissions.set(loaded.defaultMode);
105316
105729
  return [...gate, ...loaded.statusLines];
105317
105730
  }
105318
105731
  });
105319
105732
  profileRef.current = await profilePromiseRef.current;
105320
105733
  if (mcpRef.current === null && (profileRef.current?.mcpDefs?.length ?? 0) > 0) {
105321
- const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join12(homedir2(), ".spacecode");
105734
+ const configDir = process.env.SPACECODE_CONFIG_DIR?.trim() || join13(homedir2(), ".spacecode");
105322
105735
  const trust = createNodeTrustStore(configDir);
105323
105736
  const manager = new McpManager({
105324
105737
  defs: profileRef.current.mcpDefs,
@@ -105328,7 +105741,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105328
105741
  ...rulesRef.current ? { rules: rulesRef.current } : {},
105329
105742
  permissions,
105330
105743
  confirm: askUser,
105331
- audit: new FileAuditLog(join12(homedir2(), ".spacecode", "audit.jsonl")),
105744
+ audit: new FileAuditLog(join13(homedir2(), ".spacecode", "audit.jsonl")),
105332
105745
  clientVersion: process.env.SPACECODE_VERSION ?? "0"
105333
105746
  });
105334
105747
  mcpRef.current = manager;
@@ -105337,6 +105750,7 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105337
105750
  const toolset = createToolset();
105338
105751
  toolsetRef.current = toolset;
105339
105752
  const projectCard = await loadProjectCard(projectRoot);
105753
+ const prompts = await promptTemplates();
105340
105754
  const config = {
105341
105755
  apiUrl: apiUrl(),
105342
105756
  apiKey,
@@ -105346,7 +105760,8 @@ ${list2.map((c) => ` /${c.name} (${c.scope})${c.description ? ` ${c.descripti
105346
105760
  localCwd: projectRoot,
105347
105761
  commitGuidance: true,
105348
105762
  hasGitHubTools: toolset.hasGitHubTools,
105349
- ...projectCard.text ? { projectCard: projectCard.text } : {}
105763
+ ...projectCard.text ? { projectCard: projectCard.text } : {},
105764
+ templates: prompts.templates
105350
105765
  }) + (profileRef.current?.promptSection ?? ""),
105351
105766
  tools: toolset.tools,
105352
105767
  maxTurns: 100,
@@ -105458,6 +105873,7 @@ var init_useReplController = __esm({
105458
105873
  init_session_commands();
105459
105874
  init_local_toolset();
105460
105875
  init_project_card_runtime();
105876
+ init_prompt_runtime();
105461
105877
  init_shell_turn();
105462
105878
  init_node_mentions();
105463
105879
  init_api_key();
@@ -105468,6 +105884,8 @@ var init_useReplController = __esm({
105468
105884
  init_mcp_manager();
105469
105885
  init_import_gate();
105470
105886
  init_permission_rules_loader();
105887
+ init_import_reload();
105888
+ init_src();
105471
105889
  init_command_loader();
105472
105890
  init_slash_commands();
105473
105891
  init_useEventAccumulator();