diff --git a/README.md b/README.md index c60b33c..38202b4 100644 --- a/README.md +++ b/README.md @@ -192,13 +192,13 @@ Registration is the discovery step. This command detects installed verified agents and writes a small adapter for each one: ```bash -npm exec -- yskill register skills/release --root . +npm exec -- yskill register skills/release ``` Select verified agents explicitly when you do not want automatic detection: ```bash -npm exec -- yskill register skills/release --root . \ +npm exec -- yskill register skills/release \ --agent cursor,codex,claude-code ``` @@ -239,8 +239,8 @@ helper: | Language | Command | | ---------- | ------------------------------------------------------------------------------------------------------------------- | -| TypeScript | `npm exec -- yskill helper install --root . --language typescript` | -| Python | `python -m yieldskill helper install --root . --language python` | +| TypeScript | `npm exec -- yskill helper install --language typescript` | +| Python | `python -m yieldskill helper install --language python` | | Rust | `cargo install yieldskill --root .yield --locked`, then `.yield/bin/yskill helper install --root . --language rust` | | Go | `go run github.com/operatorstack/yield/cmd/yskill@latest helper install --root . --language go` | diff --git a/cmd/yskill/agents.go b/cmd/yskill/agents.go index 3cc8dba..d5fb43d 100644 --- a/cmd/yskill/agents.go +++ b/cmd/yskill/agents.go @@ -116,7 +116,7 @@ func cmdRegister(args []string) error { fs := flag.NewFlagSet("register", flag.ContinueOnError) var agents agentListFlag fs.Var(&agents, "agent", "agent id, comma-separated ids, or auto") - root := fs.String("root", "", "repository root (detected from .git by default)") + root := fs.String("root", "", "project root (inferred for supported layouts)") if err := parseOnePositional(fs, args); err != nil { return err } @@ -143,7 +143,7 @@ func cmdRegisterAll(args []string) error { fs := flag.NewFlagSet("register-all", flag.ContinueOnError) var agents agentListFlag fs.Var(&agents, "agent", "agent id, comma-separated ids, or auto") - root := fs.String("root", "", "repository root (detected from .git by default)") + root := fs.String("root", "", "project root (inferred for supported layouts)") dryRun := fs.Bool("dry-run", false, "print the synchronization plan without writing") prune := fs.Bool("prune", false, "remove obsolete generated adapters owned by this workflow directory") if err := parseOnePositional(fs, args); err != nil { @@ -195,6 +195,8 @@ func cmdRegisterAll(args []string) error { if repoRoot == "" { repoRoot, selected = resolvedRoot, selectedAgents parentRel, _ = filepath.Rel(repoRoot, parent) + } else if resolvedRoot != repoRoot { + return fmt.Errorf("all workflows must resolve to the same project root: %s and %s", repoRoot, resolvedRoot) } usesLocalRuntime = usesLocalRuntime || manifest.Language == "go" || manifest.Language == "rust" digest, digestErr := protocol.DigestSkillDir(skillDir) @@ -377,17 +379,21 @@ func registrationInputs(skillArg, rootArg string, requested []string) (string, s if err != nil { return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, err } - repoRoot, err := findRepoRoot(skillDir, rootArg) + skillDir, err = filepath.EvalSymlinks(skillDir) + if err != nil { + return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, fmt.Errorf("resolve skill directory: %w", err) + } + manifest, err := readSkillManifest(skillDir) if err != nil { return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, err } - repoRoot, err = filepath.EvalSymlinks(repoRoot) + repoRoot, err := findWorkflowRoot(skillDir, rootArg, manifest.Language) if err != nil { - return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, fmt.Errorf("resolve repository root: %w", err) + return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, err } - skillDir, err = filepath.EvalSymlinks(skillDir) + repoRoot, err = filepath.EvalSymlinks(repoRoot) if err != nil { - return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, fmt.Errorf("resolve skill directory: %w", err) + return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, fmt.Errorf("resolve repository root: %w", err) } sourceRel, err := filepath.Rel(repoRoot, skillDir) if err != nil || sourceRel == ".." || strings.HasPrefix(sourceRel, ".."+string(filepath.Separator)) { @@ -400,10 +406,6 @@ func registrationInputs(skillArg, rootArg string, requested []string) (string, s if err != nil { return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, err } - manifest, err := readSkillManifest(skillDir) - if err != nil { - return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, err - } if err := verifyWorkflowSDKVersion(manifest, skillDir, repoRoot, runtimeVersion()); err != nil { return "", "", "", skillMetadata{}, skillManifest{}, agentRegistry{}, nil, err } @@ -570,6 +572,29 @@ func findRepoRoot(skillDir, explicit string) (string, error) { return "", fmt.Errorf("cannot find repository root from %s; pass --root", skillDir) } +func findWorkflowRoot(skillDir, explicit, language string) (string, error) { + root, err := findRepoRoot(skillDir, explicit) + if err == nil || explicit != "" || (language != "typescript" && language != "python") { + return root, err + } + cwd, cwdErr := os.Getwd() + if cwdErr != nil { + return "", err + } + cwd, cwdErr = filepath.EvalSymlinks(cwd) + if cwdErr != nil { + return "", fmt.Errorf("resolve current directory: %w", cwdErr) + } + resolvedSkill, skillErr := filepath.EvalSymlinks(skillDir) + if skillErr != nil { + return "", fmt.Errorf("resolve skill directory: %w", skillErr) + } + if !within(cwd, resolvedSkill) { + return "", err + } + return filepath.Clean(cwd), nil +} + func within(parent, child string) bool { rel, err := filepath.Rel(parent, child) return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) @@ -926,7 +951,7 @@ func cmdDoctor(args []string) error { fs := flag.NewFlagSet("doctor", flag.ContinueOnError) var agents agentListFlag fs.Var(&agents, "agent", "agent id, comma-separated ids, or auto") - root := fs.String("root", "", "repository root (detected from .git by default)") + root := fs.String("root", "", "project root (inferred for supported layouts)") runTest := fs.Bool("test", false, "run the workflow fixture after static checks") if err := parseOnePositional(fs, args); err != nil { return err @@ -950,7 +975,7 @@ func cmdDoctor(args []string) error { if err != nil { return err } - packageBoundary, boundaryErr := findRepoRoot(skillDir, *root) + packageBoundary, boundaryErr := findWorkflowRoot(skillDir, *root, manifest.Language) if boundaryErr != nil { if manifest.Language == "go" || manifest.Language == "rust" { return fmt.Errorf("%s workflow needs a repository root for .yield/bin; pass --root: %w", manifest.Language, boundaryErr) diff --git a/cmd/yskill/agents_test.go b/cmd/yskill/agents_test.go index 3a494dd..3d25fad 100644 --- a/cmd/yskill/agents_test.go +++ b/cmd/yskill/agents_test.go @@ -218,6 +218,68 @@ func TestDoctorWorkflowOnlyDoesNotRequireRepository(t *testing.T) { } } +func TestTypeScriptAndPythonInferCurrentDirectoryOutsideGit(t *testing.T) { + for _, language := range []string{"typescript", "python"} { + t.Run(language, func(t *testing.T) { + root := t.TempDir() + var skill string + if language == "typescript" { + skill = createTypeScriptSkill(t, root, "review") + } else { + skill = createPythonSkill(t, root, "review") + } + t.Chdir(root) + if _, err := registerSkill(skill, "", []string{"codex"}); err != nil { + t.Fatalf("register without --root: %v", err) + } + if err := cmdDoctor([]string{skill, "--agent", "codex"}); err != nil { + t.Fatalf("doctor without --root: %v", err) + } + adapter := readTestFile(t, filepath.Join(root, ".agents", "skills", "review", "SKILL.md")) + if !strings.Contains(adapter, "source: skills/review;") { + t.Fatalf("adapter is not rooted at the invocation directory:\n%s", adapter) + } + }) + } +} + +func TestCurrentDirectoryFallbackRejectsOutsideAndSymlinkedWorkflows(t *testing.T) { + root := t.TempDir() + t.Chdir(root) + outside := createTypeScriptSkill(t, t.TempDir(), "outside") + if _, err := registerSkill(outside, "", []string{"codex"}); err == nil || !strings.Contains(err.Error(), "cannot find repository root") { + t.Fatalf("outside current directory error = %v", err) + } + if runtime.GOOS == "windows" { + return + } + link := filepath.Join(root, "skills", "linked") + if err := os.MkdirAll(filepath.Dir(link), 0o755); err != nil { + t.Fatal(err) + } + if err := os.Symlink(outside, link); err != nil { + t.Fatal(err) + } + if _, err := registerSkill(link, "", []string{"codex"}); err == nil || !strings.Contains(err.Error(), "cannot find repository root") { + t.Fatalf("symlinked workflow error = %v", err) + } +} + +func TestGoAndRustWorkflowsDoNotUseCurrentDirectoryFallback(t *testing.T) { + for _, language := range []string{"go", "rust"} { + t.Run(language, func(t *testing.T) { + root := t.TempDir() + t.Chdir(root) + skill := filepath.Join(root, "skills", "review") + writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: review\ndescription: Review a change before it is merged.\n---\n") + writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"`+language+`","run":["run"]}`) + if _, err := registerSkill(skill, "", []string{"codex"}); err == nil || !strings.Contains(err.Error(), "cannot find repository root") { + t.Fatalf("%s workflow used current directory fallback: %v", language, err) + } + }) + } +} + func TestRegisterAllPreflightsAndWritesEveryWorkflow(t *testing.T) { repo := t.TempDir() writeTestFile(t, filepath.Join(repo, ".git"), "gitdir: fixture\n") @@ -249,6 +311,58 @@ func TestRegisterAllPreflightsAndWritesEveryWorkflow(t *testing.T) { } } +func TestRegisterAllInfersOneCurrentDirectoryRootOutsideGit(t *testing.T) { + for _, language := range []string{"typescript", "python"} { + t.Run(language, func(t *testing.T) { + repo := t.TempDir() + if language == "typescript" { + writeTestFile(t, filepath.Join(repo, "package.json"), `{"dependencies":{"@operatorstack/yield":"0.1.19"}}`) + } + for _, name := range []string{"review", "release"} { + skill := filepath.Join(repo, "skills", name) + writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: "+name+"\ndescription: Run "+name+" when the matching project workflow is requested.\n---\n") + writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"`+language+`","run":["run"]}`) + if language == "typescript" { + writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n") + } else { + writeTestFile(t, filepath.Join(skill, "requirements.txt"), "yieldskill==0.1.23\n") + writeTestFile(t, filepath.Join(skill, "main.py"), "print('ok')\n") + } + } + t.Chdir(repo) + if err := cmdRegisterAll([]string{"skills", "--agent", "codex"}); err != nil { + t.Fatal(err) + } + for _, name := range []string{"review", "release"} { + if _, err := os.Stat(filepath.Join(repo, ".agents", "skills", name, "SKILL.md")); err != nil { + t.Fatal(err) + } + } + }) + } +} + +func TestRegisterAllRefusesMixedResolvedRoots(t *testing.T) { + repo := t.TempDir() + writeTestFile(t, filepath.Join(repo, "package.json"), `{"dependencies":{"@operatorstack/yield":"0.1.19"}}`) + for _, name := range []string{"outer", "nested"} { + skill := filepath.Join(repo, "skills", name) + writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: "+name+"\ndescription: Run "+name+" when the matching project workflow is requested.\n---\n") + writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"typescript","run":["node","main.ts"]}`) + writeTestFile(t, filepath.Join(skill, "main.ts"), "export {}\n") + } + writeTestFile(t, filepath.Join(repo, "skills", "nested", ".git"), "gitdir: fixture\n") + writeTestFile(t, filepath.Join(repo, "skills", "nested", "package.json"), `{"dependencies":{"@operatorstack/yield":"0.1.19"}}`) + t.Chdir(repo) + err := cmdRegisterAll([]string{"skills", "--agent", "codex"}) + if err == nil || !strings.Contains(err.Error(), "same project root") { + t.Fatalf("mixed roots error = %v", err) + } + if _, statErr := os.Stat(filepath.Join(repo, ".agents")); !os.IsNotExist(statErr) { + t.Fatalf("mixed-root preflight wrote adapters: %v", statErr) + } +} + func TestRegisterAllPruneRemovesOnlyOwnedAdapterFile(t *testing.T) { repo := t.TempDir() writeTestFile(t, filepath.Join(repo, ".git"), "gitdir: fixture\n") @@ -535,6 +649,16 @@ func createTypeScriptSkill(t *testing.T, repo, name string) string { return skill } +func createPythonSkill(t *testing.T, repo, name string) string { + t.Helper() + skill := filepath.Join(repo, "skills", name) + writeTestFile(t, filepath.Join(skill, "SKILL.md"), "---\nname: "+name+"\ndescription: Review the branch when the user wants code checked before shipping.\n---\n") + writeTestFile(t, filepath.Join(skill, "skill.json"), `{"version":1,"yield_version":"0.1.23","language":"python","run":["python","main.py"]}`) + writeTestFile(t, filepath.Join(skill, "requirements.txt"), "yieldskill==0.1.23\n") + writeTestFile(t, filepath.Join(skill, "main.py"), "print('ok')\n") + return skill +} + func writeTestFile(t *testing.T, path, content string) { t.Helper() if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { diff --git a/cmd/yskill/bootstrap.go b/cmd/yskill/bootstrap.go index ac2341a..15981f9 100644 --- a/cmd/yskill/bootstrap.go +++ b/cmd/yskill/bootstrap.go @@ -115,7 +115,19 @@ func makeBootstrapPlan(rootArg, language string, requested []string) (bootstrapP } root, err := findRepoRoot(cwd, rootArg) if err != nil { - return bootstrapPlan{}, err + if rootArg != "" { + return bootstrapPlan{}, err + } + if language == "" { + language, err = detectBootstrapLanguage(cwd) + if err != nil { + return bootstrapPlan{}, err + } + } + if language != "typescript" && language != "python" { + return bootstrapPlan{}, err + } + root = cwd } root, err = filepath.EvalSymlinks(root) if err != nil { diff --git a/cmd/yskill/bootstrap_test.go b/cmd/yskill/bootstrap_test.go index 8896971..23d33e0 100644 --- a/cmd/yskill/bootstrap_test.go +++ b/cmd/yskill/bootstrap_test.go @@ -81,6 +81,55 @@ func TestHelperInstallUsesBootstrapContract(t *testing.T) { } } +func TestHelperInfersCurrentDirectoryForTypeScriptAndPython(t *testing.T) { + for _, language := range []string{"typescript", "python"} { + t.Run(language, func(t *testing.T) { + withBootstrapTestState(t) + root := t.TempDir() + t.Chdir(root) + if err := cmdHelper([]string{"install", "--language", language, "--agent", "codex", "--dry-run"}); err != nil { + t.Fatal(err) + } + if _, err := os.Stat(filepath.Join(root, "skills")); !os.IsNotExist(err) { + t.Fatalf("helper dry run wrote skills directory: %v", err) + } + if err := cmdHelper([]string{"install", "--language", language, "--agent", "codex", "--yes"}); err != nil { + t.Fatal(err) + } + for _, path := range []string{ + "skills/yield-workflow-builder/SKILL.md", + ".agents/skills/yield-workflow-builder/SKILL.md", + } { + if _, err := os.Stat(filepath.Join(root, filepath.FromSlash(path))); err != nil { + t.Fatalf("missing %s after helper install: %v", path, err) + } + } + }) + } +} + +func TestHelperAutoDetectsSupportedCurrentDirectoryAndRefusesAmbiguity(t *testing.T) { + withBootstrapTestState(t) + root := t.TempDir() + writeTestFile(t, filepath.Join(root, "pyproject.toml"), "[project]\nname = 'example'\n") + t.Chdir(root) + plan, err := makeBootstrapPlan("", "", []string{"codex"}) + if err != nil { + t.Fatal(err) + } + resolvedRoot, err := filepath.EvalSymlinks(root) + if err != nil { + t.Fatal(err) + } + if plan.Root != resolvedRoot || plan.Language != "python" { + t.Fatalf("auto-detected plan = root %q language %q", plan.Root, plan.Language) + } + writeTestFile(t, filepath.Join(root, "package.json"), "{}\n") + if _, err := makeBootstrapPlan("", "", []string{"codex"}); err == nil || !strings.Contains(err.Error(), "multiple project languages") { + t.Fatalf("ambiguous current directory error = %v", err) + } +} + func TestBootstrapCancellationDoesNotWrite(t *testing.T) { withBootstrapTestState(t) bootstrapInput = bytes.NewBufferString("no\n") diff --git a/cmd/yskill/main_test.go b/cmd/yskill/main_test.go index d026910..785faad 100644 --- a/cmd/yskill/main_test.go +++ b/cmd/yskill/main_test.go @@ -19,6 +19,13 @@ import ( "github.com/operatorstack/yield/internal/runlog" ) +func TestMain(m *testing.M) { + // Launcher-sensitive tests control this boundary explicitly. Do not let the + // runtime that invoked `go test` override their test-local launchers. + _ = os.Unsetenv("YIELD_LAUNCHER_PATH") + os.Exit(m.Run()) +} + func stubRustLockfile(t *testing.T) { t.Helper() previous := generateRustLockfile @@ -313,7 +320,7 @@ func TestPackageScaffoldsPrintCreatedWorkflowInNextCommands(t *testing.T) { workflow := shellQuoteForPlatform(dir, runtime.GOOS) for _, line := range []string{ "test: " + tt.launcher + " doctor " + workflow + " --test", - "then: " + tt.launcher + " register " + workflow + " --root .", + "then: " + tt.launcher + " register " + workflow, } { if !strings.Contains(output, line) { t.Fatalf("init output does not contain %q:\n%s", line, output) diff --git a/cmd/yskill/scaffold.go b/cmd/yskill/scaffold.go index b38c3b8..66d12da 100644 --- a/cmd/yskill/scaffold.go +++ b/cmd/yskill/scaffold.go @@ -172,13 +172,7 @@ func scaffoldSkill(dir, language, sdkPath, description string) error { fmt.Printf("init: %s skill %q scaffolded in %s\n", language, name, dir) fmt.Println("next: replace the starter program and fixtures with the described workflow") fmt.Printf("test: %s doctor %s --test\n", launcher, workflow) - rootFlag := "" - if language == "typescript" || language == "python" { - if _, err := findRepoRoot(dir, ""); err != nil { - rootFlag = " --root ." - } - } - fmt.Printf("then: %s register %s%s\n", launcher, workflow, rootFlag) + fmt.Printf("then: %s register %s\n", launcher, workflow) return nil } diff --git a/docs/agent-setup.md b/docs/agent-setup.md index b399ebe..1b0bdea 100644 --- a/docs/agent-setup.md +++ b/docs/agent-setup.md @@ -76,10 +76,10 @@ manual workflow, install the guided helper explicitly from the repository root: ```bash # TypeScript -npm exec -- yskill helper install --root . --language typescript +npm exec -- yskill helper install --language typescript # Python -python -m yieldskill helper install --root . --language python +python -m yieldskill helper install --language python # Rust cargo install yieldskill --root .yield --locked @@ -119,10 +119,10 @@ answer, the adapter uses `yskill respond`; it does not create `response.json`. Use `--value` for a person’s answer and `--result-json` for structured agent work. The file-based `resume --response` command remains available for CI. -Workflow-only `doctor` works without `.git`. A Go or Rust runtime under -`.yield/bin` also identifies the project root for `init`, `doctor`, and -registration. For other non-Git layouts, pass `--root` so Yield knows where -agent adapters belong. +Workflow-only `doctor` works without `.git`. TypeScript and Python commands +use the current directory as the project root when the workflow is contained +inside it. A Go or Rust runtime under `.yield/bin` identifies the project root +for `init`, `doctor`, and registration. Pass `--root` to override inference. ``` diff --git a/docs/convert-existing-skill.md b/docs/convert-existing-skill.md index 17d6af4..5409c69 100644 --- a/docs/convert-existing-skill.md +++ b/docs/convert-existing-skill.md @@ -9,7 +9,7 @@ Package installation does not add the helper. Install it explicitly for the project language. For example: ```bash -npm exec -- yskill helper install --root . --language typescript +npm exec -- yskill helper install --language typescript ``` See the [quickstart](quickstart.md) for Python, Rust, and Go commands. diff --git a/docs/quickstart.md b/docs/quickstart.md index d706f4b..fa125e0 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -49,8 +49,8 @@ This runs the fixture to a terminal outcome without leaving a run journal. ## 4. Register it ```bash -npm exec -- yskill register skills/release --root . -npm exec -- yskill doctor skills/release --root . --agent codex,cursor,claude-code --test +npm exec -- yskill register skills/release +npm exec -- yskill doctor skills/release --agent codex,cursor,claude-code --test ``` Registration creates only small discovery adapters. The canonical workflow, @@ -71,10 +71,10 @@ After learning the manual flow, install guided assistance explicitly: ```bash # TypeScript -npm exec -- yskill helper install --root . --language typescript +npm exec -- yskill helper install --language typescript # Python -python -m yieldskill helper install --root . --language python +python -m yieldskill helper install --language python # Rust .yield/bin/yskill helper install --root . --language rust diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 6e452d7..2db9133 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -22,8 +22,10 @@ set. It installs and tests `skills/yield-workflow-builder`, registers the selected adapters, and verifies them. The installer stores local state under ignored `.yield/`. It refuses paths outside -the repository, symlink escapes, existing destinations, and user-owned adapter -files. Use `--root` for a directory that is not a Git repository. +the project root, symlink escapes, existing destinations, and user-owned adapter +files. TypeScript and Python use the current directory when no Git or local-runtime +root exists. Go and Rust require their project-local runtime. `--root` overrides +inference. The installer can change only these repository locations: @@ -67,6 +69,8 @@ and run state remain in the canonical skill directory. Registration updates only adapters previously generated from the same source. It refuses user-owned files, workflows outside the repository, and canonical workflows stored inside a selected agent's discovery directory. +For TypeScript and Python outside Git, the current directory is the project root +and the workflow must be contained within it. `--root` overrides inference. ## `agents` @@ -88,6 +92,8 @@ Checks the canonical skill workflow and package launcher. `--test` also runs the workflow against `fixtures/responses.json` without leaving a run journal. Adapter checks run only when `--agent` is supplied, and all adapter problems are reported together. +For TypeScript and Python outside Git, adapter checks use the current directory +when the workflow is contained within it. ## `version` @@ -142,7 +148,8 @@ yskill register-all --agent cursor,codex Registers every immediate skill workflow in one directory. It checks all names and destinations before writing. `--prune` removes only obsolete adapters generated -from that workflow directory. Agent-facing names must be unique. +from that workflow directory. Agent-facing names must be unique, and every workflow +must resolve to the same project root. ## `inspect` diff --git a/scripts/readme.test.mjs b/scripts/readme.test.mjs index 0e3def4..e963e9b 100644 --- a/scripts/readme.test.mjs +++ b/scripts/readme.test.mjs @@ -251,7 +251,7 @@ test("Python README presents a public five-step workflow", async () => { assert.match(readme, /python -m pip install yieldskill/) assert.match(readme, /python -m yieldskill init skills\/env-doctor/) assert.match(readme, /python -m yieldskill doctor skills\/env-doctor --test/) - assert.match(readme, /python -m yieldskill register skills\/env-doctor --root \./) + assert.match(readme, /python -m yieldskill register skills\/env-doctor/) assert.match(readme, /^\/env-doctor$/m) assert.match( readme, @@ -371,8 +371,8 @@ test("README and quickstart use the public documentation and package registries" /\[public documentation\]\(https:\/\/yield\.operatorstack\.systems\/docs\/\)/, ) const helperCommands = [ - "npm exec -- yskill helper install --root . --language typescript", - "python -m yieldskill helper install --root . --language python", + "npm exec -- yskill helper install --language typescript", + "python -m yieldskill helper install --language python", ".yield/bin/yskill helper install --root . --language rust", "go run github.com/operatorstack/yield/cmd/yskill@latest helper install --root . --language go", ] diff --git a/sdk/python/README.md b/sdk/python/README.md index bdd73bd..ebd22ea 100644 --- a/sdk/python/README.md +++ b/sdk/python/README.md @@ -143,14 +143,14 @@ fixture. A successful test reaches `completed` without leaving a run journal. Registration lets installed coding agents discover the workflow: ```bash -python -m yieldskill register skills/env-doctor --root . +python -m yieldskill register skills/env-doctor ``` Select the verified agents explicitly when you do not want automatic detection: ```bash -python -m yieldskill register skills/env-doctor --root . \ +python -m yieldskill register skills/env-doctor \ --agent cursor,codex,claude-code ``` @@ -229,7 +229,7 @@ Installing `yieldskill` does not create skills or coding-agent adapters. After learning the manual workflow above, install guided assistance explicitly: ```bash -python -m yieldskill helper install --root . --language python +python -m yieldskill helper install --language python ``` Review the plan and restart the coding agent after installation. The helper