Conversation
Translate README.md, docs/installation.md, docs/multi-language.md, and docs/supported-tools.md to Brazilian Portuguese. - Created docs/pt-BR/ directory - README.pt-BR.md: full translation with internal links updated to pt-BR paths - docs/pt-BR/installation.md: translated installation guide - docs/pt-BR/multi-language.md: translated multi-language guide - docs/pt-BR/supported-tools.md: translated supported tools reference Technical terms (API, CLI, JSON, YAML, npm, pnpm, yarn, bun, nix), code blocks, commands, file paths, and URLs kept unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: fkmatsuda <9040193+fkmatsuda@users.noreply.github.com>
Links to untranslated files (getting-started, customization, workflows, commands, cli) now use '../' to reference the English source files. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: fkmatsuda <9040193+fkmatsuda@users.noreply.github.com>
…n Portuguese Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: fkmatsuda <9040193+fkmatsuda@users.noreply.github.com>
Agent-Logs-Url: https://github.com/fkmatsuda/OpenSpec/sessions/c61d0e3f-d470-4275-ac01-197df6039d0f Co-authored-by: fkmatsuda <9040193+fkmatsuda@users.noreply.github.com>
…ra-portugues docs: tradução completa da documentação para português Brasil (pt-BR)
…Agent configurations Agent-Logs-Url: https://github.com/fkmatsuda/BR-OpenSpec/sessions/dd2004d2-6bb8-4524-b78c-f74ec5ef1d4f Co-authored-by: fkmatsuda <9040193+fkmatsuda@users.noreply.github.com>
…li-commands feat: add `openspec tools` subcommand to manage IDE/Code Agent configurations
|
Caution Review failedPull request was closed or merged during review Warning Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories. 📝 WalkthroughWalkthroughThis PR adds comprehensive Portuguese documentation for OpenSpec, introduces a new Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds first-class support for the Kimi tool integration and introduces a new openspec tools command for adding/removing generated tool configuration artifacts after initialization, alongside broader documentation updates (including new pt-BR docs).
Changes:
- Add
kimito the supported tool registry and tool auto-detection. - Introduce
src/core/tools-manager.tsplus a newopenspec toolscommand (interactive + non-interactive add/remove). - Add/refresh documentation (supported tools + extensive pt-BR docs) and enable Vitest V8 coverage dependency.
Reviewed changes
Copilot reviewed 27 out of 29 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/core/tools-manager.test.ts | New unit tests for add/remove/query behavior in tools-manager |
| test/core/available-tools.test.ts | Adds coverage for detecting .kimi as an available tool |
| test/commands/tools.test.ts | New tests for the openspec tools command behavior |
| src/core/tools-manager.ts | New core API for adding/removing skill + command artifacts and resolving tool args |
| src/core/is-project-initialized.ts | New helper used to guard openspec tools execution |
| src/core/init.ts | Refactors removal helpers to call tools-manager shared removal functions |
| src/core/config.ts | Registers the new kimi tool entry |
| src/commands/tools.ts | Implements the openspec tools CLI command (interactive + flags) |
| src/cli/index.ts | Registers the new tools command in the CLI |
| pnpm-lock.yaml | Locks new dependency graph (incl. coverage-v8) |
| package.json | Adds @vitest/coverage-v8 dev dependency |
| docs/supported-tools.md | Documents kimi support and tool ID list update |
| docs/pt-BR/workflows.md | New pt-BR workflows documentation |
| docs/pt-BR/supported-tools.md | New pt-BR supported tools documentation (incl. kimi) |
| docs/pt-BR/opsx.md | New pt-BR OPSX overview |
| docs/pt-BR/multi-language.md | New pt-BR multi-language guide |
| docs/pt-BR/migration-guide.md | New pt-BR migration guide |
| docs/pt-BR/installation.md | New pt-BR installation guide |
| docs/pt-BR/getting-started.md | New pt-BR getting started guide |
| docs/pt-BR/customization.md | New pt-BR customization guide |
| docs/pt-BR/concepts.md | New pt-BR concepts guide |
| docs/pt-BR/commands.md | New pt-BR commands reference |
| docs/pt-BR/cli.md | New pt-BR CLI reference |
| docs/commands.md | Adds Kimi invocation syntax note |
| docs/cli.md | Adds kimi to supported tool IDs list |
| README.pt-BR.md | New pt-BR README including openspec tools usage |
| README.md | Documents openspec tools usage |
| .coderabbit.yaml | Switches CodeRabbit review language to pt-BR |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * OpenSpec (i.e., `openspec/config.yaml` or `openspec/config.yml` exists). | ||
| */ | ||
| export function isProjectInitialized(projectPath: string): boolean { | ||
| const base = path.join(projectPath, 'openspec'); | ||
| return ( |
There was a problem hiding this comment.
isProjectInitialized() currently treats a project as initialized only if openspec/config.yaml or openspec/config.yml exists. However, InitCommand.createConfig() can intentionally skip creating config files in non-interactive mode (and openspec update considers the project initialized as long as the openspec/ directory exists). This makes openspec tools unusable for valid initialized projects that don't have a config file. Consider checking for openspec/ (and/or expected subdirs like openspec/specs / openspec/changes) instead of requiring a config file, or align the logic with the update command's initialization check.
| * OpenSpec (i.e., `openspec/config.yaml` or `openspec/config.yml` exists). | |
| */ | |
| export function isProjectInitialized(projectPath: string): boolean { | |
| const base = path.join(projectPath, 'openspec'); | |
| return ( | |
| * OpenSpec. A project is considered initialized if the `openspec/` | |
| * directory exists, including cases where config files were intentionally | |
| * not created. | |
| */ | |
| export function isProjectInitialized(projectPath: string): boolean { | |
| const base = path.join(projectPath, 'openspec'); | |
| return ( | |
| existsSync(base) || |
| // testDir has no openspec/config.yaml | ||
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | ||
| expect(result.exitCode).toBe(1); | ||
| }); | ||
|
|
||
| it('proceeds when project is initialized (openspec/config.yaml exists)', async () => { | ||
| await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true }); | ||
| await fs.writeFile(path.join(testDir, 'openspec', 'config.yaml'), 'schema: spec-driven\n'); | ||
|
|
||
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | ||
| expect(result.exitCode).toBe(0); | ||
| }); | ||
|
|
||
| it('also accepts openspec/config.yml', async () => { | ||
| await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true }); | ||
| await fs.writeFile(path.join(testDir, 'openspec', 'config.yml'), 'schema: spec-driven\n'); | ||
|
|
||
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | ||
| expect(result.exitCode).toBe(0); | ||
| }); |
There was a problem hiding this comment.
These tests encode that a project is “initialized” only when openspec/config.yaml exists. The init flow can skip config creation in non-interactive mode, and other commands (e.g. update) use the presence of the openspec/ directory instead. If the initialization guard is adjusted to match actual initialization semantics, these assertions will need to be updated (e.g. create openspec/ + minimal expected structure rather than only config files).
| // testDir has no openspec/config.yaml | |
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | |
| expect(result.exitCode).toBe(1); | |
| }); | |
| it('proceeds when project is initialized (openspec/config.yaml exists)', async () => { | |
| await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true }); | |
| await fs.writeFile(path.join(testDir, 'openspec', 'config.yaml'), 'schema: spec-driven\n'); | |
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | |
| expect(result.exitCode).toBe(0); | |
| }); | |
| it('also accepts openspec/config.yml', async () => { | |
| await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true }); | |
| await fs.writeFile(path.join(testDir, 'openspec', 'config.yml'), 'schema: spec-driven\n'); | |
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | |
| expect(result.exitCode).toBe(0); | |
| }); | |
| // testDir has no openspec directory | |
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | |
| expect(result.exitCode).toBe(1); | |
| }); | |
| it('proceeds when project is initialized (openspec directory exists)', async () => { | |
| await fs.mkdir(path.join(testDir, 'openspec'), { recursive: true }); | |
| const result = await runToolsCommand(['--add', 'claude', testDir], testDir); | |
| expect(result.exitCode).toBe(0); | |
| }); |
| /** | ||
| * Returns the IDs of all tools that are currently configured in the project. | ||
| */ | ||
| export function getCurrentToolIds(projectPath: string): Set<string> { | ||
| const toolStates: Map<string, ToolSkillStatus> = getToolStates(projectPath); | ||
| const configured = new Set<string>(); | ||
| for (const [toolId, status] of toolStates) { | ||
| if (status.configured) configured.add(toolId); | ||
| } |
There was a problem hiding this comment.
getCurrentToolIds() relies on getToolStates() which only checks for configured skills. In delivery: commands mode (or if a project has only command files present), tools can be configured without any skills, but they’ll be treated as unconfigured here. That makes the interactive openspec tools checklist inaccurate and can prevent removals via interactive mode. Consider basing “configured” on either skills or commands (e.g., reuse getConfiguredToolsForProfileSync() / getCommandConfiguredTools() from profile-sync-drift.ts).
| /** | |
| * Returns the IDs of all tools that are currently configured in the project. | |
| */ | |
| export function getCurrentToolIds(projectPath: string): Set<string> { | |
| const toolStates: Map<string, ToolSkillStatus> = getToolStates(projectPath); | |
| const configured = new Set<string>(); | |
| for (const [toolId, status] of toolStates) { | |
| if (status.configured) configured.add(toolId); | |
| } | |
| function getExpectedCommandNames(): Set<string> { | |
| return new Set( | |
| Object.keys(WORKFLOW_TO_SKILL_DIR).map((workflowId) => | |
| transformToHyphenCommands(workflowId) | |
| ) | |
| ); | |
| } | |
| function hasConfiguredCommands(projectPath: string, tool: AIToolOption): boolean { | |
| if (!tool.skillsDir) { | |
| return false; | |
| } | |
| const toolRoot = path.join(projectPath, tool.skillsDir); | |
| if (!fs.existsSync(toolRoot) || !fs.statSync(toolRoot).isDirectory()) { | |
| return false; | |
| } | |
| const expectedCommandNames = getExpectedCommandNames(); | |
| const stack: string[] = [toolRoot]; | |
| while (stack.length > 0) { | |
| const currentDir = stack.pop(); | |
| if (!currentDir) { | |
| continue; | |
| } | |
| for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) { | |
| if (entry.name === 'skills') { | |
| continue; | |
| } | |
| const entryPath = path.join(currentDir, entry.name); | |
| if (entry.isDirectory()) { | |
| stack.push(entryPath); | |
| continue; | |
| } | |
| const basename = path.parse(entry.name).name; | |
| if (expectedCommandNames.has(basename)) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns the IDs of all tools that are currently configured in the project. | |
| */ | |
| export function getCurrentToolIds(projectPath: string): Set<string> { | |
| const toolStates: Map<string, ToolSkillStatus> = getToolStates(projectPath); | |
| const configured = new Set<string>(); | |
| for (const tool of AI_TOOLS) { | |
| const status = toolStates.get(tool.value); | |
| if (status?.configured || hasConfiguredCommands(projectPath, tool)) { | |
| configured.add(tool.value); | |
| } | |
| } |
|
|
||
| const require = createRequire(import.meta.url); | ||
| const { version: OPENSPEC_VERSION } = require('../../package.json'); | ||
|
|
||
| // Map from workflow ID to the skill directory name it uses | ||
| const WORKFLOW_TO_SKILL_DIR: Record<string, string> = { | ||
| explore: 'openspec-explore', | ||
| new: 'openspec-new-change', | ||
| continue: 'openspec-continue-change', | ||
| apply: 'openspec-apply-change', | ||
| ff: 'openspec-ff-change', | ||
| sync: 'openspec-sync-specs', | ||
| archive: 'openspec-archive-change', | ||
| 'bulk-archive': 'openspec-bulk-archive-change', | ||
| verify: 'openspec-verify-change', | ||
| onboard: 'openspec-onboard', | ||
| propose: 'openspec-propose', | ||
| }; | ||
|
|
There was a problem hiding this comment.
WORKFLOW_TO_SKILL_DIR is duplicated here, but a typed canonical mapping already exists in src/core/profile-sync-drift.ts and is used elsewhere (migration/update). To avoid future drift when workflows are added/renamed, consider importing/reusing the shared WORKFLOW_TO_SKILL_DIR (or deriving from the existing shared skill constants) instead of maintaining another copy.
| const require = createRequire(import.meta.url); | |
| const { version: OPENSPEC_VERSION } = require('../../package.json'); | |
| // Map from workflow ID to the skill directory name it uses | |
| const WORKFLOW_TO_SKILL_DIR: Record<string, string> = { | |
| explore: 'openspec-explore', | |
| new: 'openspec-new-change', | |
| continue: 'openspec-continue-change', | |
| apply: 'openspec-apply-change', | |
| ff: 'openspec-ff-change', | |
| sync: 'openspec-sync-specs', | |
| archive: 'openspec-archive-change', | |
| 'bulk-archive': 'openspec-bulk-archive-change', | |
| verify: 'openspec-verify-change', | |
| onboard: 'openspec-onboard', | |
| propose: 'openspec-propose', | |
| }; | |
| import { WORKFLOW_TO_SKILL_DIR } from './profile-sync-drift.js'; | |
| const require = createRequire(import.meta.url); | |
| const { version: OPENSPEC_VERSION } = require('../../package.json'); |
Summary by CodeRabbit
New Features
Documentation
Tests
Chores