Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 37 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,52 @@
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec">
<a href="https://github.com/appyboypov/OpenSplx">
<picture>
<source srcset="assets/openspec_pixel_dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="assets/openspec_pixel_light.svg" media="(prefers-color-scheme: light)">
<img src="assets/openspec_pixel_light.svg" alt="OpenSpec logo" height="64">
<source srcset="assets/opensplx_pixel_dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="assets/opensplx_pixel_light.svg" media="(prefers-color-scheme: light)">
<img src="assets/opensplx_pixel_light.svg" alt="OpenSplx logo" height="64">
</picture>
</a>

</p>
<p align="center">Spec-driven development for AI coding assistants.</p>
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Fission-AI/OpenSpec/actions/workflows/ci.yml/badge.svg" /></a>
<a href="https://www.npmjs.com/package/@fission-ai/openspec"><img alt="npm version" src="https://img.shields.io/npm/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="https://github.com/Fission-AI/OpenSpec"><img alt="Fork of OpenSpec" src="https://img.shields.io/badge/Fork%20of-OpenSpec-blue?style=flat-square" /></a>
<a href="https://nodejs.org/"><img alt="node version" src="https://img.shields.io/node/v/@fission-ai/openspec?style=flat-square" /></a>
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" /></a>
<a href="https://conventionalcommits.org"><img alt="Conventional Commits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square" /></a>
<a href="https://discord.gg/YctCnvvshC"><img alt="Discord" src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white&style=flat-square" /></a>
</p>

<p align="center">
<img src="assets/openspec_dashboard.png" alt="OpenSpec dashboard preview" width="90%">
<img src="assets/openspec_dashboard.png" alt="OpenSplx dashboard preview" width="90%">
</p>

# OpenSplx

> **Fork Notice:** OpenSplx is a community fork of [OpenSpec](https://github.com/Fission-AI/OpenSpec).
> It adds the `plx` command alias and extended features while maintaining full compatibility
> with the original OpenSpec workflow.

## What's Different in OpenSplx

| Feature | OpenSpec | OpenSplx |
|---------|----------|----------|
| Command | `openspec` | `openspec` + `plx` alias |
| Install | `npm i -g @fission-ai/openspec` | Clone & `npm link` (local) |

### Quick Start (OpenSplx)

```bash
git clone https://github.com/appyboypov/OpenSplx.git
cd OpenSplx
pnpm install && pnpm build
npm link
plx --version # or openspec --version
```

---

<details>
<summary><strong>Original OpenSpec Documentation</strong> (click to expand)</summary>

<p align="center">
Follow <a href="https://x.com/0xTab">@0xTab on X</a> for updates · Join the <a href="https://discord.gg/YctCnvvshC">OpenSpec Discord</a> for help and questions.
</p>
Expand Down Expand Up @@ -379,3 +404,5 @@ Run `openspec update` whenever someone switches tools so your agents pick up the
## License

MIT

</details>
94 changes: 94 additions & 0 deletions assets/opensplx_pixel_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 94 additions & 0 deletions assets/opensplx_pixel_light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions bin/plx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

import '../dist/cli/index.js';
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
}
},
"bin": {
"openspec": "./bin/openspec.js"
"openspec": "./bin/openspec.js",
"plx": "./bin/plx.js"
},
"files": [
"dist",
Expand Down
20 changes: 15 additions & 5 deletions scripts/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

/**
* Detect the command name from the invocation path
*/
function getCommandName() {
const scriptPath = process.argv[1] || '';
const scriptName = path.basename(scriptPath).replace(/\.js$/, '');
// Default to 'openspec' for postinstall context
return scriptName === 'plx' ? 'plx' : 'openspec';
}
Comment on lines +24 to +29
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The getCommandName() function is defined but never used in this script. During postinstall, the completion script should be generated with the appropriate command name. Line 91 calls generator.generate(COMMAND_REGISTRY) without passing a commandName parameter, which will default to 'openspec'. This means the installed completions will only work for the openspec command, not for plx.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +29
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

In a postinstall context, this function will almost always return 'openspec' since the script is being executed by npm during installation. The logic to detect 'plx' from the script name won't work in this context. Consider if this function serves any useful purpose during postinstall, or if a different approach is needed to handle command name detection during installation.

Copilot uses AI. Check for mistakes.

/**
* Check if we should skip installation
*/
Expand Down Expand Up @@ -72,7 +82,7 @@ async function installCompletions(shell) {

// 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`);
return;
}

Expand All @@ -99,11 +109,11 @@ async function installCompletions(shell) {
}
} else {
// Installation failed, show tip for manual install
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
console.log(`\nTip: Run 'openspec completion install' or 'plx completion install' for shell completions`);
}
} catch (error) {
// Fail gracefully - show tip for manual install
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
console.log(`\nTip: Run 'openspec completion install' or 'plx completion install' for shell completions`);
}
}

Expand All @@ -127,7 +137,7 @@ async function main() {
// Detect shell
const shell = await detectShell();
if (!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`);
return;
}

Expand All @@ -136,7 +146,7 @@ async function main() {
} catch (error) {
// Fail gracefully - never break npm install
// Show tip for manual install
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
console.log(`\nTip: Run 'openspec completion install' or 'plx completion install' for shell completions`);
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ import { ValidateCommand } from '../commands/validate.js';
import { ShowCommand } from '../commands/show.js';
import { CompletionCommand } from '../commands/completion.js';

// Import command name detection utility
import { commandName } from '../utils/command-name.js';

const program = new Command();
const require = createRequire(import.meta.url);
const { version } = require('../../package.json');

program
.name('openspec')
.name(commandName)
.description('AI-native system for spec-driven development')
.version(version);

Expand Down
11 changes: 6 additions & 5 deletions src/commands/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { COMMAND_REGISTRY } from '../core/completions/command-registry.js';
import { detectShell, SupportedShell } from '../utils/shell-detection.js';
import { CompletionProvider } from '../core/completions/completion-provider.js';
import { getArchivedChangeIds } from '../utils/item-discovery.js';
import { commandName } from '../utils/command-name.js';

interface GenerateOptions {
shell?: string;
Expand Down Expand Up @@ -59,7 +60,7 @@ export class CompletionCommand {

// No shell specified and cannot auto-detect
console.error('Error: Could not auto-detect shell. Please specify shell explicitly.');
console.error(`Usage: openspec completion ${operationName} [shell]`);
console.error(`Usage: ${commandName} completion ${operationName} [shell]`);
console.error(`Currently supported: ${CompletionFactory.getSupportedShells().join(', ')}`);
process.exitCode = 1;
return null;
Expand Down Expand Up @@ -115,7 +116,7 @@ export class CompletionCommand {
*/
private async generateForShell(shell: SupportedShell): Promise<void> {
const generator = CompletionFactory.createGenerator(shell);
const script = generator.generate(COMMAND_REGISTRY);
const script = generator.generate(COMMAND_REGISTRY, commandName);
console.log(script);
}

Expand All @@ -126,11 +127,11 @@ export class CompletionCommand {
const generator = CompletionFactory.createGenerator(shell);
const installer = CompletionFactory.createInstaller(shell);

const spinner = ora(`Installing ${shell} completion script...`).start();
const spinner = ora(`Installing ${shell} completion script for ${commandName}...`).start();

try {
// Generate the completion script
const script = generator.generate(COMMAND_REGISTRY);
const script = generator.generate(COMMAND_REGISTRY, commandName);

// Install it
const result = await installer.install(script);
Expand Down Expand Up @@ -180,7 +181,7 @@ export class CompletionCommand {
// Prompt for confirmation unless --yes flag is provided
if (!skipConfirmation) {
const confirmed = await confirm({
message: 'Remove OpenSpec configuration from ~/.zshrc?',
message: `Remove ${commandName} configuration from ~/.zshrc?`,
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The confirmation message is hardcoded to mention ~/.zshrc, but this function accepts a shell parameter that could potentially support other shells in the future. Consider making this message dynamic to reference the shell being uninstalled from, e.g., Remove ${commandName} configuration from ${shell} configuration?

Suggested change
message: `Remove ${commandName} configuration from ~/.zshrc?`,
message: `Remove ${commandName} configuration from ${shell} configuration?`,

Copilot uses AI. Check for mistakes.
default: false,
});

Expand Down
Loading