Skip to content

Conversation

@appboypov
Copy link
Owner

Summary

  • Add YAML frontmatter parsing for tracked issues in proposal.md
  • Display issue identifiers in openspec list output (e.g., change-name (SM-123) [tasks])
  • Include trackedIssues in openspec show --json output
  • Report tracked issues when archiving changes
  • Add "External Issue Tracking" section to AGENTS.md template for agent guidance

Frontmatter Format

---
tracked-issues:
  - tracker: linear
    id: SM-123
    url: https://linear.app/team/issue/SM-123
---

Test plan

  • Run openspec list with a proposal containing tracked-issues frontmatter - should show issue ID in parentheses
  • Run openspec show <change> --json - should include trackedIssues array
  • Run openspec archive <change> - should report issue IDs in success message
  • Run openspec update in a project - should include External Issue Tracking section in AGENTS.md

TabishB and others added 4 commits December 20, 2025 23:01
…-AI#378)

Prioritize XDG_CONFIG_HOME on Windows to fix test environment overrides.
Previously, Windows would always use APPDATA regardless of XDG_CONFIG_HOME,
causing tests to fail. Now XDG_CONFIG_HOME is checked first on all platforms
before falling back to platform-specific defaults.

Also update the Windows APPDATA test to explicitly clear XDG_CONFIG_HOME
when testing the fallback behavior.
…ission-AI#380)

Fixes Fission-AI#367

The CLI was hanging when run as a pre-commit hook because @inquirer/prompts
was statically imported at module load time. Even when prompts were never
called (e.g., `openspec validate --specs --no-interactive`), the import
itself could set up stdin references that prevented clean process exit
when stdin was piped.

Changes:
- Convert all static `@inquirer/prompts` imports to dynamic imports
- Dynamically import `InitCommand` (which uses `@inquirer/core`)
- Update `isInteractive()` to accept options object with both
  `noInteractive` and Commander's negated `interactive` property
- Handle empty validation queue with proper exit code

Now when running in non-interactive mode, the inquirer modules are never
loaded, allowing the process to exit cleanly after completion.
- Add `plx` as an alias command alongside `openspec`
- Create OpenSplx pixel art logo assets (light/dark themes)
- Update README with fork notice and Quick Start section
- Make CLI command name dynamic based on invocation
- Update completion system to support both command names
- Add command-name utility for detecting invoked command
- Add YAML frontmatter parsing for tracked issues in proposal.md
- Display issue identifiers in `openspec list` output
- Include trackedIssues in `openspec show --json` output
- Report tracked issues when archiving changes
- Add External Issue Tracking section to AGENTS.md template
- Add TrackedIssue schema and type exports
Copilot AI review requested due to automatic review settings December 21, 2025 21:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds external issue tracking support to the OpenSpec CLI, enabling proposals to reference external issues (Linear, GitHub, Jira, etc.) via YAML frontmatter. It also includes rebranding changes to support the OpenSplx fork with a plx command alias, along with several refactoring improvements for better code organization and user experience.

Key changes include:

  • External Issue Tracking: YAML frontmatter parsing in proposal.md to store tracked issue references (tracker, id, url), with display support across CLI commands (list, show, archive)
  • Branding Updates: Addition of plx command alias, new logo assets, and updated README with OpenSplx fork documentation
  • CLI Improvements: Dynamic imports for interactive prompts, improved command name detection, XDG Base Directory compliance for global config, and better handling of empty validation queues

Reviewed changes

Copilot reviewed 30 out of 33 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/core/parsers/markdown-parser.ts Adds YAML frontmatter extraction with simple parser for tracked-issues field
src/core/parsers/change-parser.ts Integrates frontmatter parsing to include trackedIssues in Change objects
src/core/schemas/change.schema.ts Defines TrackedIssue schema (tracker, id, url) and adds optional trackedIssues array to Change schema
src/core/schemas/index.ts Exports TrackedIssueSchema and TrackedIssue type
src/core/list.ts Displays first tracked issue ID in parentheses after change name
src/commands/change.ts Includes trackedIssues in JSON output and long format display
src/core/archive.ts Reports tracked issues when archiving changes, uses dynamic imports for prompts
src/commands/validate.ts Adds empty queue handling, uses dynamic imports for prompts
src/commands/show.ts Uses dynamic imports for prompts
src/commands/spec.ts Uses dynamic imports for prompts
src/commands/completion.ts Uses command name from utility for dynamic completion generation
src/utils/interactive.ts Refactors to support both boolean and options object parameters for better flexibility
src/utils/command-name.ts New utility to detect CLI command name (openspec vs plx) from invocation path
src/core/global-config.ts Moves XDG_CONFIG_HOME check to take precedence on all platforms
src/core/completions/types.ts Adds optional commandName parameter to CompletionGenerator.generate()
src/core/completions/generators/zsh-generator.ts Supports dynamic command name in generated completion scripts
src/core/templates/agents-template.ts Adds External Issue Tracking section with guidance for agents
src/cli/index.ts Uses dynamic command name and converts --no-color flag check to opts.color === false
scripts/postinstall.js Updates completion installation tips to mention both openspec and plx commands
bin/plx.js New entry point for plx command alias
package.json Adds plx binary entry
assets/opensplx_pixel_*.svg New logo assets for OpenSplx branding
README.md Adds OpenSplx fork notice and quick start guide
openspec/AGENTS.md Includes External Issue Tracking guidance for agents
openspec/changes/add-external-issue-tracking/* Spec files, tasks, and proposal documenting the external issue tracking feature
test/core/global-config.test.ts Updates test description for Windows XDG_CONFIG_HOME behavior
test/core/completions/generators/zsh-generator.test.ts Updates test expectation for lowercase 'openspec' in comment

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/core/list.ts Outdated
Comment on lines 76 to 78
const issueDisplay = change.trackedIssue ? ` (${change.trackedIssue})` : '';
const status = formatTaskStatus({ total: change.totalTasks, completed: change.completedTasks });
console.log(`${padding}${paddedName} ${status}`);
console.log(`${padding}${paddedName}${issueDisplay} ${status}`);
Copy link

Copilot AI Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The padding calculation doesn't account for the tracked issue display length. When different changes have different length issue IDs (or some have no issues), the alignment of the status column will be inconsistent. Consider calculating nameWidth to include the issue display, or adjusting the padding after adding the issue display.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Width calculation now includes the full display name with issue identifier. Added unit tests to verify alignment with varying issue ID lengths.

Comment on lines +106 to +108
if (currentIssue && currentIssue.tracker && currentIssue.id && currentIssue.url) {
trackedIssues.push(currentIssue as TrackedIssue);
}
Copy link

Copilot AI Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The YAML parser doesn't validate that all three required fields (tracker, id, url) are present before pushing to trackedIssues. While the schema validation will catch this later, it would be better to handle malformed YAML more gracefully by either logging a warning or skipping incomplete issue entries during parsing.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping as-is. The code already validates all three fields before pushing: if (currentIssue.tracker && currentIssue.id && currentIssue.url). Malformed entries are silently skipped, which is intentional for graceful degradation.

Comment on lines 9 to 10
const scriptName = path.basename(scriptPath).replace(/\.js$/, '');
return scriptName === 'plx' ? 'plx' : 'openspec';
Copy link

Copilot AI Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command name detection only checks if the script name equals 'plx' but doesn't handle other potential edge cases like 'plx.exe' on Windows, or if the script is executed through npx/pnpm exec. Consider normalizing the script name more robustly or using a case-insensitive comparison.

Suggested change
const scriptName = path.basename(scriptPath).replace(/\.js$/, '');
return scriptName === 'plx' ? 'plx' : 'openspec';
const rawScriptName = path.basename(scriptPath);
const normalizedScriptName = rawScriptName
.toLowerCase()
.replace(/\.(js|cjs|mjs|ts|cmd|exe|bat)$/, '');
return normalizedScriptName === 'plx' ? 'plx' : 'openspec';

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Now handles Windows extensions (.exe, .cmd, .bat), Node.js module extensions (.cjs, .mjs, .ts), case-insensitive matching, and cross-platform path separators. Added 19 unit tests covering all scenarios.

// Check if shell is supported
if (!CompletionFactory.isSupported(shell)) {
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
console.log(`\nTip: Run 'openspec completion install' or 'plx completion install' for shell completions`);
Copy link

Copilot AI Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generator.generate() call doesn't specify a commandName parameter, so it defaults to 'openspec'. This means completions generated during postinstall won't work for the 'plx' command. Consider detecting which command name to use or generating completions for both command names.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. postinstall.js now generates and installs completions for both 'openspec' and 'plx' command names. Updated ZshInstaller.install() and getInstallationPath() to accept a commandName parameter. Added unit tests for multi-command installation.

const issueText = change.trackedIssues && change.trackedIssues.length > 0
? ` (${change.trackedIssues[0].id})`
: '';
console.log(`${changeName}${issueText}: ${title}${deltaCountText}${taskStatusText}`);
Copy link

Copilot AI Dec 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracked issue display format is inconsistent across commands. In list.ts, it appears after the name and before the status. In change.ts long format, it appears after the name but before the colon and title. While not a critical issue, consider standardizing the format across all commands for better consistency (e.g., always after the name in parentheses).

Suggested change
console.log(`${changeName}${issueText}: ${title}${deltaCountText}${taskStatusText}`);
console.log(`${changeName}: ${title}${issueText}${deltaCountText}${taskStatusText}`);

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Issue display now appears after the title in long format: changeName: title (ISSUE) [deltas] [tasks]. This keeps the name:title pair together while still showing the issue reference. Added unit tests to verify the format order.

- Fix list.ts alignment to include issue display in width calculation
- Fix command-name.ts to handle Windows extensions and cross-platform paths
- Fix postinstall.js to install completions for both openspec and plx
- Fix change.ts issue display format (after title in long format)
- Add comprehensive unit tests for all fixes
- Archive add-external-issue-tracking change with spec updates
Keep improved cross-platform command-name detection and dual-command
completion installation from this branch.
@appboypov appboypov merged commit d5156cd into main Dec 21, 2025
appboypov added a commit that referenced this pull request Dec 21, 2025
* fix(global-config): respect XDG_CONFIG_HOME on all platforms (Fission-AI#378)

Prioritize XDG_CONFIG_HOME on Windows to fix test environment overrides.
Previously, Windows would always use APPDATA regardless of XDG_CONFIG_HOME,
causing tests to fail. Now XDG_CONFIG_HOME is checked first on all platforms
before falling back to platform-specific defaults.

Also update the Windows APPDATA test to explicitly clear XDG_CONFIG_HOME
when testing the fallback behavior.

* fix(cli): prevent hang in pre-commit hooks by using dynamic imports (Fission-AI#380)

Fixes Fission-AI#367

The CLI was hanging when run as a pre-commit hook because @inquirer/prompts
was statically imported at module load time. Even when prompts were never
called (e.g., `openspec validate --specs --no-interactive`), the import
itself could set up stdin references that prevented clean process exit
when stdin was piped.

Changes:
- Convert all static `@inquirer/prompts` imports to dynamic imports
- Dynamically import `InitCommand` (which uses `@inquirer/core`)
- Update `isInteractive()` to accept options object with both
  `noInteractive` and Commander's negated `interactive` property
- Handle empty validation queue with proper exit code

Now when running in non-interactive mode, the inquirer modules are never
loaded, allowing the process to exit cleanly after completion.

* feat(cli): add plx command alias and rebrand as OpenSplx (OpenSplx-#1)

- Add `plx` as an alias command alongside `openspec`
- Create OpenSplx pixel art logo assets (light/dark themes)
- Update README with fork notice and Quick Start section
- Make CLI command name dynamic based on invocation
- Update completion system to support both command names
- Add command-name utility for detecting invoked command

* feat(cli): add external issue tracking support

- Add YAML frontmatter parsing for tracked issues in proposal.md
- Display issue identifiers in `openspec list` output
- Include trackedIssues in `openspec show --json` output
- Report tracked issues when archiving changes
- Add External Issue Tracking section to AGENTS.md template
- Add TrackedIssue schema and type exports

* fix(cli): address PR review feedback and archive external issue tracking

- Fix list.ts alignment to include issue display in width calculation
- Fix command-name.ts to handle Windows extensions and cross-platform paths
- Fix postinstall.js to install completions for both openspec and plx
- Fix change.ts issue display format (after title in long format)
- Add comprehensive unit tests for all fixes
- Archive add-external-issue-tracking change with spec updates

---------

Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
@appboypov appboypov deleted the add-external-issue-tracking branch December 24, 2025 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants