diff --git a/server/internal/daemon/execenv/runtime_config_kind_test.go b/server/internal/daemon/execenv/runtime_config_kind_test.go index 815e0eb6f2..d8c5d841ce 100644 --- a/server/internal/daemon/execenv/runtime_config_kind_test.go +++ b/server/internal/daemon/execenv/runtime_config_kind_test.go @@ -111,10 +111,6 @@ func TestBuildMetaSkillContentSlimKindMatrix(t *testing.T) { kindCommentTriggered: true, kindAssignmentTriggered: true, kindAutopilotRunOnly: true, kindChat: true, }}, - {"## Repository Setup Preflight", map[taskKind]bool{ - kindCommentTriggered: true, kindAssignmentTriggered: true, - kindAutopilotRunOnly: true, kindChat: true, - }}, {"## Issue Metadata", issueKinds}, {"## Instruction Precedence", map[taskKind]bool{kindAssignmentTriggered: true}}, {"## Sub-issue Creation", issueKinds}, @@ -156,51 +152,6 @@ func TestBuildMetaSkillContentSlimKindMatrix(t *testing.T) { } } -func TestRepositorySetupPreflightIsStackAgnostic(t *testing.T) { - t.Parallel() - - var b strings.Builder - writeRepositorySetupPreflight(&b) - out := b.String() - - for _, want := range []string{ - "after `multica repo checkout`", - "Before editing code or running build/test commands", - "`AGENTS.md`, `README`, development docs", - "dependency manifests and lockfiles", - "missing, stale, or readiness is uncertain", - "documented, reproducible setup command", - "Do not reinstall when the existing environment is demonstrably ready", - "did not unexpectedly modify dependency manifests or lockfiles", - "Do not use a failing build or test run", - } { - if !strings.Contains(out, want) { - t.Errorf("repository setup preflight missing %q\n---\n%s", want, out) - } - } - - for _, packageManager := range []string{"pnpm", "npm install", "yarn", "bun install", "pip install", "poetry", "cargo"} { - if strings.Contains(strings.ToLower(out), packageManager) { - t.Errorf("repository setup preflight must stay stack-agnostic; found %q\n---\n%s", packageManager, out) - } - } - - brief := buildMetaSkillContent("codex", TaskContextForEnv{ - IssueID: "issue-1", - Repos: []RepoContextForEnv{{URL: "https://example.com/repo.git"}}, - }) - repositoriesIndex := strings.Index(brief, "\n## Repositories\n") - preflightIndex := strings.Index(brief, "\n## Repository Setup Preflight\n") - if repositoriesIndex == -1 || preflightIndex <= repositoriesIndex { - t.Errorf("repository setup preflight must follow repository checkout guidance\n---\n%s", brief) - } - - localBrief := buildMetaSkillContent("codex", TaskContextForEnv{IssueID: "issue-1"}) - if !strings.Contains(localBrief, "\n## Repository Setup Preflight\n") { - t.Errorf("repository setup preflight must also cover an existing local working directory\n---\n%s", localBrief) - } -} - // TestSlimQuickCreateAvailableCommands locks the minimal-variant content // for quick-create's Available Commands: `issue create` present, every // other Core command absent (the hard guardrails forbid the call). diff --git a/server/internal/daemon/execenv/runtime_config_sections.go b/server/internal/daemon/execenv/runtime_config_sections.go index 3bf72b957c..db390a722b 100644 --- a/server/internal/daemon/execenv/runtime_config_sections.go +++ b/server/internal/daemon/execenv/runtime_config_sections.go @@ -30,8 +30,8 @@ import ( // 2. Per-section prose compression — Available Commands, Issue // Metadata, Mentions, Sub-issue Creation, Comment Formatting, // Always Use CLI, Background Task Safety, Task Initiator, -// Repositories, Repository Setup Preflight, Output are all -// tightened. Every test-asserted phrase stays. +// Repositories, Output are all tightened. Every test-asserted phrase +// stays. // // Background Task Safety is emitted by `writeBackgroundTaskSafetySlim` // below. @@ -299,20 +299,6 @@ func writeRepositories(b *strings.Builder, ctx TaskContextForEnv) { b.WriteString("\n") } -// writeRepositorySetupPreflight emits a stack-agnostic readiness check for -// every task surface that can perform repository work. It deliberately asks -// the agent to infer the repository's own setup contract instead of naming a -// package manager or forcing a reinstall on a warm workdir. -func writeRepositorySetupPreflight(b *strings.Builder) { - b.WriteString("## Repository Setup Preflight\n\n") - b.WriteString("Before editing code or running build/test commands in a repository (after `multica repo checkout`, or immediately when working in an existing local directory):\n\n") - b.WriteString("- Read the repository instructions and setup documentation (`AGENTS.md`, `README`, development docs), plus the relevant dependency manifests and lockfiles.\n") - b.WriteString("- Identify the stack and package/dependency manager, then determine whether the required dependencies and tools are already usable.\n") - b.WriteString("- If dependencies are missing, stale, or readiness is uncertain, run the repository's documented, reproducible setup command before proceeding. Do not reinstall when the existing environment is demonstrably ready.\n") - b.WriteString("- Check that setup did not unexpectedly modify dependency manifests or lockfiles; investigate unexpected changes before continuing.\n") - b.WriteString("- Do not use a failing build or test run as the way to discover that dependencies were not prepared.\n\n") -} - // writeProjectContext emits the Project Context section when the task carries // an active project. Project context is independent of the task surface: an // issue inherits it from its project, while a chat receives it from the @@ -654,7 +640,6 @@ func writeOutput(b *strings.Builder, kind taskKind, ctx TaskContextForEnv) { // Comment Formatting | ✓ | ✓ | — | — | — // Repositories | △ | △ | △ | — | △ // Project Context | △ | △ | △ | △ | △ -// Repository Preflight | ✓ | ✓ | ✓ | — | ✓ // Issue Metadata | ✓ | ✓ | — | — | — // Instruction Precedence| — | ✓ | — | — | — // Sub-issue Creation | ✓ | ✓ | — | — | — @@ -696,10 +681,6 @@ func buildMetaSkillContentSlim(provider string, ctx TaskContextForEnv) string { writeProjectContext(&b, ctx) - if kind != kindQuickCreate { - writeRepositorySetupPreflight(&b) - } - if kind.hasIssueContext() { writeIssueMetadata(&b) } diff --git a/server/internal/service/builtin_skills/multica-focused-testing/SKILL.md b/server/internal/service/builtin_skills/multica-focused-testing/SKILL.md deleted file mode 100644 index 5a4e3a6ce4..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: multica-focused-testing -description: "Use when selecting or running a focused or targeted test in any user repository. Detect the repository's stack and runner first, prefer its commands, and verify discovery before execution. Not for an explicitly requested full-suite run." ---- - -# Focused testing - -Run the narrowest runner-native scope without expanding it to a full suite. - -## Workflow - -1. Read repository instructions and test configuration. Verify the target, - ownership, build tool, and runner. -2. Prefer repository Agent configuration, then a dedicated repository script, - then a runner-native focused selector confirmed by configuration or local - `--help`. Infer only as a last resort. -3. Keep the target as a distinct argument. Do not guess whether a wrapper - forwards separators or positional arguments, and do not add a separator - unless the detected tool requires it. -4. Use list, collect, discovery, or dry-run mode first when available. Compare - it with the runner-native scope: file, package plus case, target, class, - module, or project. Never impose - `expected_file_count=1` on a runner that does not discover by file. -5. Run only when scope is confirmed. If discovery is broader or the target, - ownership, runner, or forwarding contract remains unclear, stop and correct - or report the gap rather than guessing. - -## Stack reference - -After detection, read exactly the matching reference: - -- JavaScript or TypeScript: `references/javascript-typescript.md` -- Go: `references/go.md` -- Python: `references/python.md` -- Rust: `references/rust.md` -- JVM (Gradle or Maven): `references/jvm.md` -- .NET: `references/dotnet.md` -- Ruby: `references/ruby.md` - -If no reference matches, use the repository's own instructions and the detected -runner's local help. Do not borrow a template from another stack. diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/dotnet.md b/server/internal/service/builtin_skills/multica-focused-testing/references/dotnet.md deleted file mode 100644 index c8c126d013..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/dotnet.md +++ /dev/null @@ -1,39 +0,0 @@ -# .NET - -Identify the owning project or solution and inspect repository build scripts -before calling `dotnet test`. Prefer a project file over a whole solution for a -focused run. Check the SDK and `global.json` first: .NET 10 can select either -VSTest or Microsoft.Testing.Platform (MTP), and their CLI contracts differ. - -For VSTest, after confirming the adapter's filter support: - -```text -["dotnet", "test", "path/to/project.csproj", "--filter", "FullyQualifiedName=Namespace.ClassName.MethodName"] -``` - -Verify the same filter without executing tests: - -```text -["dotnet", "test", "path/to/project.csproj", "--list-tests", "--filter", "FullyQualifiedName=Namespace.ClassName.MethodName"] -``` - -`FullyQualifiedName` is available in the popular VSTest adapters, but the exact -value format and other filter properties vary. Confirm the discovered name from -`--list-tests`. - -Do not reuse that argv for MTP. With the .NET 10 MTP driver, project selection -uses `--project`, while test-related arguments come from the registered -framework extensions and should be placed after a literal `--` when forwarding -would otherwise be ambiguous. Inspect `global.json`, the test framework, and -the local `dotnet test --help` / test-application help before constructing the -filter. Do not assume MSTest, NUnit, xUnit, or custom MTP extensions share a -universal selector. - -## Official documentation - -- Runner selection in `dotnet test`: - https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test -- VSTest `--filter`, `--list-tests`, and filter properties: - https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test-vstest -- .NET 10 MTP project selection and argument forwarding: - https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test-mtp diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/go.md b/server/internal/service/builtin_skills/multica-focused-testing/references/go.md deleted file mode 100644 index dda118f9fb..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/go.md +++ /dev/null @@ -1,32 +0,0 @@ -# Go - -Go tests are compiled and selected by package and test name, not reliably by -running one `_test.go` file in isolation. Identify the owning module and package -from `go.mod` and the target path. - -Use the package plus an anchored `-run` expression: - -```text -["go", "test", "./path/to/package", "-run", "^TestName$", "-count=1"] -``` - -For a top-level test, verify discovery without running it: - -```text -["go", "test", "./path/to/package", "-list", "^TestName$"] -``` - -`-list` does not enumerate subtests. For a subtest, use an anchored -parent/subtest expression, such as `^TestName$/^SubtestName$`, after confirming -the exact names from source or verbose output. Go splits `-run` expressions at -unbracketed `/` characters and may run a matching parent to discover its -subtests. - -Do not pass a single `_test.go` file merely to imitate file-oriented runners: -that can omit package files, shared test helpers, or build-tag behavior. The -expected scope is the owning package with the requested test selected. - -## Official documentation - -- Go testing flags (`-run`, `-list`, and `-count`): - https://pkg.go.dev/cmd/go#hdr-Testing_flags diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/javascript-typescript.md b/server/internal/service/builtin_skills/multica-focused-testing/references/javascript-typescript.md deleted file mode 100644 index d2c30c60f3..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/javascript-typescript.md +++ /dev/null @@ -1,46 +0,0 @@ -# JavaScript and TypeScript - -Inspect the nearest `package.json`, the workspace manifest, the lockfile, and -the runner config. Use the owning package's package-relative test path when a -workspace command changes the runner's working directory. - -For pnpm plus Vitest, when repository configuration does not provide a -dedicated command, use this direct runner shape from the repository root: - -```text -["pnpm", "--filter", "", "exec", "vitest", "run", ""] -``` - -`pnpm exec` runs the dependency command in the selected project's scope and -passes options after `exec` to that command. Vitest treats a positional value -as a substring filter over test-file paths, so pass the full package-relative -path and verify that it uniquely identifies the requested file. - -Do not use this shape for a focused run: - -```text -["pnpm", "--filter", "", "test", "--", ""] -``` - -pnpm passes arguments after the script name to the executed script. In this -shape Vitest receives the literal separator before the path; that is not the -direct file-filter argv above and can expand discovery to the whole package. - -For other package managers or runners, inspect the repository script and local -runner help. Do not translate the pnpm/Vitest argv mechanically. - -If the installed Vitest version exposes `list` and `--filesOnly`, verify the -same filter before execution: - -```text -["pnpm", "--filter", "", "exec", "vitest", "list", "", "--filesOnly"] -``` - -Require exactly one discovered test file. - -## Official documentation - -- pnpm filtering: https://pnpm.io/filtering -- pnpm exec and option forwarding: https://pnpm.io/cli/exec -- pnpm run argument forwarding: https://pnpm.io/cli/run -- Vitest CLI file filters, `run`, and `list`: https://vitest.dev/guide/cli diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/jvm.md b/server/internal/service/builtin_skills/multica-focused-testing/references/jvm.md deleted file mode 100644 index e0a6f6eaab..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/jvm.md +++ /dev/null @@ -1,39 +0,0 @@ -# JVM - -Use the repository wrapper (`gradlew` or `mvnw`) and identify the owning module -before selecting a test. Build configuration may add plugins, profiles, or -environment required by the suite. - -For Gradle, after confirming the task path: - -```text -["./gradlew", ":module:test", "--tests", "package.ClassName.methodName"] -``` - -Use `gradlew.bat` on Windows. If the repository's wrapper exposes -`--test-dry-run`, add it first to inspect the selected tests without executing -them. Filters configured in the build script still apply alongside `--tests`. - -For Maven Surefire, after confirming the module and plugin: - -```text -["./mvnw", "-pl", "module", "-Dtest=ClassName#methodName", "test"] -``` - -Use `mvnw.cmd` on Windows. The current Surefire documentation scopes its -`ClassName#methodName` example to JUnit 4.x and TestNG. For JUnit 5 or another -provider, use a method selector only after the repository's installed plugin -and provider confirm support. - -Do not assume these selectors apply to custom Gradle tasks, Maven Failsafe, -integration-test source sets, parameterized cases, or other JVM runners. -Inspect the build files and task/plugin help first. - -## Official documentation - -- Gradle wrapper, project task paths, and task options: - https://docs.gradle.org/current/userguide/command_line_interface.html -- Gradle `--tests` filtering and `--test-dry-run`: - https://docs.gradle.org/current/userguide/java_testing.html#test_filtering -- Maven Surefire single-class and method selectors: - https://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/python.md b/server/internal/service/builtin_skills/multica-focused-testing/references/python.md deleted file mode 100644 index 6b38f34050..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/python.md +++ /dev/null @@ -1,33 +0,0 @@ -# Python - -Inspect `pyproject.toml`, `pytest.ini`, `tox.ini`, `setup.cfg`, and repository -scripts to identify the environment wrapper and runner. Preserve repository -wrappers such as tox, nox, uv, or Poetry when they establish dependencies or -environment variables. - -For direct pytest usage, a file target is: - -```text -["python", "-m", "pytest", "path/to/test_file.py"] -``` - -A single case uses its pytest node id: - -```text -["python", "-m", "pytest", "path/to/test_file.py::TestClass::test_name"] -``` - -Verify the node id before executing: - -```text -["python", "-m", "pytest", "--collect-only", "-q", "path/to/test_file.py::TestClass::test_name"] -``` - -Parameterized cases add their generated id in brackets; copy it from collection -output instead of guessing it. Do not assume a unittest, Django, or custom -runner accepts pytest selectors; use the detected runner's own syntax. - -## Official documentation - -- pytest invocation, node ids, `python -m pytest`, and `--collect-only`: - https://docs.pytest.org/en/stable/how-to/usage.html diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/ruby.md b/server/internal/service/builtin_skills/multica-focused-testing/references/ruby.md deleted file mode 100644 index cfcda7eb45..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/ruby.md +++ /dev/null @@ -1,35 +0,0 @@ -# Ruby - -Inspect `Gemfile`, repository scripts, and runner configuration. Use -`bundle exec` when the repository is Bundler-managed so the selected runner -version and plugins match the project. - -For RSpec, a file target is: - -```text -["bundle", "exec", "rspec", "spec/path/to/example_spec.rb"] -``` - -RSpec can select an example or group by a line in the file: - -```text -["bundle", "exec", "rspec", "spec/path/to/example_spec.rb:"] -``` - -Do not apply RSpec syntax to Minitest, Rails test tasks, or custom runners. -For example, Rails' Minitest runner has its own file-and-line form: - -```text -["bin/rails", "test", "test/models/user_test.rb:"] -``` - -Follow the repository's runner-specific command and expected scope. - -## Official documentation - -- Bundler execution context and its RSpec example: - https://bundler.io/man/bundle-exec.1.html -- RSpec file-and-line selection: - https://rspec.info/features/3-12/rspec-core/command-line/line-number-appended-to-path/ -- Rails Minitest file, name, and line selection: - https://guides.rubyonrails.org/testing.html#the-rails-test-runner diff --git a/server/internal/service/builtin_skills/multica-focused-testing/references/rust.md b/server/internal/service/builtin_skills/multica-focused-testing/references/rust.md deleted file mode 100644 index 42e7f37d09..0000000000 --- a/server/internal/service/builtin_skills/multica-focused-testing/references/rust.md +++ /dev/null @@ -1,40 +0,0 @@ -# Rust - -Inspect the workspace `Cargo.toml` and the target crate's manifest. Rust's -focused unit scope is usually a package plus a test name; an integration test -file is a named Cargo test target. - -For an integration-test target: - -```text -["cargo", "test", "-p", "", "--test", ""] -``` - -List the libtest names in that target without running them: - -```text -["cargo", "test", "-p", "", "--test", "", "--", "--list"] -``` - -Then run one case using the full path printed by libtest: - -```text -["cargo", "test", "-p", "", "--test", "", "--", "", "--exact"] -``` - -For a library unit test, replace `--test ` with `--lib`. -Here the separator is required because the filter, `--list`, and `--exact` -belong to the compiled libtest harness, not Cargo. `--exact` matches only a full -path such as `module::tests::test_name`; a short function name can select zero -tests. Confirm the target name with Cargo metadata or the manifest instead of -deriving it only from a filesystem path. - -These libtest arguments do not apply when the target declares -`harness = false`; use that target's own CLI. - -## Official documentation - -- Cargo package/target selection and test-argument forwarding: - https://doc.rust-lang.org/cargo/commands/cargo-test.html -- libtest filters, `--list`, and `--exact`: - https://doc.rust-lang.org/rustc/tests/ diff --git a/server/internal/service/builtin_skills_test.go b/server/internal/service/builtin_skills_test.go index afb90ad44b..9f993703b6 100644 --- a/server/internal/service/builtin_skills_test.go +++ b/server/internal/service/builtin_skills_test.go @@ -518,121 +518,6 @@ func TestProjectsAndResourcesSkillCoversDurableContext(t *testing.T) { } } -func TestFocusedTestingSkillUsesStackSpecificProgressiveDisclosure(t *testing.T) { - skill, ok := findSkill(t, "multica-focused-testing") - if !ok { - return - } - fm, body, _ := splitFrontmatter(skill.Content) - - description := fm["description"] - for _, want := range []string{ - "focused or targeted test", - "any user repository", - "Detect the repository's stack and runner first", - "Not for an explicitly requested full-suite run", - } { - if !strings.Contains(description, want) { - t.Errorf("focused-testing description missing trigger text %q", want) - } - } - - for _, want := range []string{ - "repository Agent configuration", - "dedicated repository script", - "runner-native focused selector", - "Do not guess whether a wrapper", - "forwards separators or positional arguments", - "Never impose", - "`expected_file_count=1` on a runner that does not discover by file", - "read exactly the matching reference", - "Do not borrow a template from another stack", - } { - if !strings.Contains(body, want) { - t.Errorf("focused-testing skill missing stack-neutral rule %q", want) - } - } - - // Concrete runner commands belong behind one-level-deep references. The - // body is loaded for every focused-test task; keeping templates out of it - // prevents a Go/Python/Rust repository from inheriting frontend noise. - for _, runnerSpecific := range []string{ - "pnpm --filter", - "go test", - "python -m pytest", - "cargo test", - "./gradlew", - "dotnet test", - "bundle exec rspec", - } { - if strings.Contains(body, runnerSpecific) { - t.Errorf("focused-testing body must stay stack-neutral; found %q", runnerSpecific) - } - } - - references := map[string][]string{ - "references/javascript-typescript.md": { - `["pnpm", "--filter", "", "exec", "vitest", "run", ""]`, - `["pnpm", "--filter", "", "exec", "vitest", "list", "", "--filesOnly"]`, - `["pnpm", "--filter", "", "test", "--", ""]`, - "exactly one discovered test file", - "https://pnpm.io/cli/exec", - "https://vitest.dev/guide/cli", - }, - "references/go.md": { - `["go", "test", "./path/to/package", "-run", "^TestName$", "-count=1"]`, - `["go", "test", "./path/to/package", "-list", "^TestName$"]`, - "not reliably by\nrunning one `_test.go` file in isolation", - "https://pkg.go.dev/cmd/go#hdr-Testing_flags", - }, - "references/python.md": { - `["python", "-m", "pytest", "path/to/test_file.py::TestClass::test_name"]`, - `["python", "-m", "pytest", "--collect-only", "-q", "path/to/test_file.py::TestClass::test_name"]`, - "https://docs.pytest.org/en/stable/how-to/usage.html", - }, - "references/rust.md": { - `["cargo", "test", "-p", "", "--test", ""]`, - `["cargo", "test", "-p", "", "--test", "", "--", "", "--exact"]`, - "the separator is required", - "`harness = false`", - "https://doc.rust-lang.org/rustc/tests/", - }, - "references/jvm.md": { - `["./gradlew", ":module:test", "--tests", "package.ClassName.methodName"]`, - `["./mvnw", "-pl", "module", "-Dtest=ClassName#methodName", "test"]`, - "JUnit 4.x and TestNG", - "https://docs.gradle.org/current/userguide/java_testing.html#test_filtering", - "https://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html", - }, - "references/dotnet.md": { - `["dotnet", "test", "path/to/project.csproj", "--filter", "FullyQualifiedName=Namespace.ClassName.MethodName"]`, - `["dotnet", "test", "path/to/project.csproj", "--list-tests", "--filter", "FullyQualifiedName=Namespace.ClassName.MethodName"]`, - "Do not reuse that argv for MTP", - "https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-test-mtp", - }, - "references/ruby.md": { - `["bundle", "exec", "rspec", "spec/path/to/example_spec.rb"]`, - `["bundle", "exec", "rspec", "spec/path/to/example_spec.rb:"]`, - `["bin/rails", "test", "test/models/user_test.rb:"]`, - "Do not apply RSpec syntax to Minitest", - "https://bundler.io/man/bundle-exec.1.html", - "https://guides.rubyonrails.org/testing.html#the-rails-test-runner", - }, - } - for path, mustContain := range references { - content, ok := skillFileContent(skill, path) - if !ok { - t.Errorf("focused-testing skill missing supporting file %s", path) - continue - } - for _, want := range mustContain { - if !strings.Contains(content, want) { - t.Errorf("%s missing %q", path, want) - } - } - } -} - func findSkill(t *testing.T, name string) (AgentSkillData, bool) { t.Helper() for _, s := range loadBuiltinSkills() { @@ -644,15 +529,6 @@ func findSkill(t *testing.T, name string) (AgentSkillData, bool) { return AgentSkillData{}, false } -func skillFileContent(skill AgentSkillData, path string) (string, bool) { - for _, f := range skill.Files { - if f.Path == path { - return f.Content, true - } - } - return "", false -} - func skillHasFile(skill AgentSkillData, path string) bool { for _, f := range skill.Files { if f.Path == path {