Skip to content
Merged
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
67 changes: 67 additions & 0 deletions __tests__/supercli-args.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,71 @@ describe("supercli args parser", () => {
expect(plan.args.foo).toBe("bar")
expect(plan.args.baz).toBe("")
})

test("bare -- does not create empty flag key", () => {
const out = execSync(`node ${CLI} plan testns testres testact -- --foo bar --json`, {
env,
encoding: "utf-8"
})
const plan = JSON.parse(out)
// bare -- should be ignored as a flag; --foo should still parse
expect(plan.args.foo).toBe("bar")
expect(Object.keys(plan.args)).not.toContain("")
})

test("--flag at end of args defaults to true", () => {
const out = execSync(`node ${CLI} plan testns testres testact --flag --json`, {
env,
encoding: "utf-8"
})
const plan = JSON.parse(out)
expect(plan.args.flag).toBe(true)
})

test("--flag --other does not consume --other as value", () => {
const out = execSync(`node ${CLI} plan testns testres testact --flag --other value --json`, {
env,
encoding: "utf-8"
})
const plan = JSON.parse(out)
expect(plan.args.flag).toBe(true)
expect(plan.args.other).toBe("value")
})

test("--flag= with empty string value", () => {
const out = execSync(`node ${CLI} plan testns testres testact --empty= --json`, {
env,
encoding: "utf-8"
})
const plan = JSON.parse(out)
expect(plan.args.empty).toBe("")
})
})
const plan = JSON.parse(out)
expect(plan.args.foo).toBe("bar")
expect(plan.args.baz).toBe("")
})
Comment on lines +103 to +107
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove the duplicated trailing test bodies

These leftover lines sit after the describe block has already been closed, leaving unmatched braces and making the test file fail to parse before Jest can run anything; node --check __tests__/supercli-args.test.js reports SyntaxError: Unexpected token '}' at the duplicated block. Until this is removed, the unit test suite is broken in any environment that loads this file.

Useful? React with 👍 / 👎.


test("error for unknown namespace goes to stderr", () => {
const cmd = `node ${CLI} nonexistent --json`
expect(() => execSync(cmd, { env, encoding: "utf-8" })).toThrow()
})

test("inspect with missing args errors on stderr not stdout", () => {
try {
execSync(`node ${CLI} inspect --json`, { env, stdio: ["pipe", "pipe", "pipe"] })
} catch (e) {
expect(e.stderr.toString()).toContain("Usage: supercli inspect")
expect(e.stdout.toString()).toBe("")
}
})

test("unknown namespace exits with non-zero code", () => {
try {
execSync(`node ${CLI} nonexistent --json`, { env, stdio: ["pipe", "pipe", "pipe"] })
} catch (e) {
expect(e.status).toBeGreaterThan(0)
expect(e.stderr.toString()).toContain("not found")
}
})
})
74 changes: 74 additions & 0 deletions __tests__/supercli-version.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
const { execSync } = require("child_process")
const path = require("path")

const CLI = path.join(__dirname, "..", "cli", "supercli.js")

describe("supercli --version", () => {
test("--version --human returns version text", () => {
const out = execSync(`node ${CLI} --version --human`, {
encoding: "utf-8"
})
expect(out).toContain("SuperCLI v")
expect(out).toContain("JavaScript")
})

test("--version --json returns version envelope", () => {
const out = execSync(`node ${CLI} --version --json`, {
encoding: "utf-8"
})
const data = JSON.parse(out)
expect(data.name).toBe("SuperCLI")
expect(data.implementation).toBe("JavaScript")
expect(data.version).toBeDefined()
expect(data.node_version).toBeDefined()
})

test("--version alone produces exit code 0 with JSON (non-TTY)", () => {
const out = execSync(`node ${CLI} --version`, {
encoding: "utf-8"
})
const data = JSON.parse(out)
expect(data.name).toBe("SuperCLI")
expect(data.version).toBe("1.31.1")
})

test("--version --compact returns compact version envelope", () => {
const out = execSync(`node ${CLI} --version --compact`, {
encoding: "utf-8"
})
const data = JSON.parse(out)
expect(data.n).toBe("SuperCLI")
expect(data.v).toBe("1.31.1")
})
})
expect(out).toContain("SuperCLI v")
expect(out).toContain("JavaScript")
})
Comment on lines +43 to +46
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove the duplicated version test tail

The file closes the describe block and then repeats fragments of the old tests, which leaves unmatched braces and prevents the test suite from parsing; node --check __tests__/supercli-version.test.js reports SyntaxError: Unexpected token '}' at this duplicated tail. This blocks any Jest run that includes the new version tests.

Useful? React with 👍 / 👎.


test("--version --json returns version envelope", () => {
const out = execSync(`node ${CLI} --version --json`, {
encoding: "utf-8"
})
const data = JSON.parse(out)
expect(data.name).toBe("SuperCLI")
expect(data.implementation).toBe("JavaScript")
expect(data.version).toBeDefined()
expect(data.node_version).toBeDefined()
})

test("--version alone produces exit code 0", () => {
const out = execSync(`node ${CLI} --version`, {
encoding: "utf-8"
})
expect(out).toBeTruthy()
})

test("--version --compact returns compact version envelope", () => {
const out = execSync(`node ${CLI} --version --compact`, {
encoding: "utf-8"
})
const data = JSON.parse(out)
expect(data.name).toBe("SuperCLI")
expect(data.v).toBeDefined()
})
})
25 changes: 24 additions & 1 deletion cli/supercli.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ for (let i = 0; i < rawArgs.length; i++) {
const arg = rawArgs[i];
if (arg.startsWith("--")) {
const kv = arg.slice(2);
if (kv === "") continue; // bare --, skip (prevents flags[""] = ...)
const eqIdx = kv.indexOf("=");
if (eqIdx !== -1) {
const key = kv.slice(0, eqIdx);
Expand Down Expand Up @@ -71,12 +72,15 @@ const RESERVED_FLAGS = [
"schema",
"help-json",
"help",
"version",
"no-color",
"show-dag",
"format",
"on-conflict",
];

const CLI_VERSION = "1.31.1";

function compactKeys(obj) {
if (Array.isArray(obj)) return obj.map(compactKeys);
if (obj && typeof obj === "object") {
Expand All @@ -94,6 +98,7 @@ function compactKeys(obj) {
error: "err",
message: "msg",
suggestions: "sug",
name: "n",
};
const out = {};
for (const [k, v] of Object.entries(obj)) {
Expand Down Expand Up @@ -176,7 +181,7 @@ function outputError(error) {
);
}
} else {
console.log(JSON.stringify(compactMode ? compactKeys(envelope) : envelope));
process.stderr.write(JSON.stringify(compactMode ? compactKeys(envelope) : envelope) + "\n");
}
process.exit(envelope.error.code);
}
Expand Down Expand Up @@ -325,6 +330,24 @@ async function main() {
}
}

// Early exit for --version (align with Zig CLI behavior)
if (flags.version) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scope --version to the top-level invocation

When any command includes a user argument named --version, this new global check runs before command dispatch and returns the SuperCLI version instead of executing the requested command; I verified bundled commands such as plugins/asdf/plugin.json's asdf tool install and plugins/gvm/plugin.json's gvm version install/uninstall define a real version argument. This means calls like supercli asdf tool install --name node --version 20.0.0 --json are hijacked by the global version path rather than reaching the plugin.

Useful? React with 👍 / 👎.

if (humanMode) {
console.log(`SuperCLI v${CLI_VERSION}`);
console.log("Implementation: JavaScript (Node.js)");
console.log("Binary: supercli");
} else {
output({
name: "SuperCLI",
implementation: "JavaScript",
version: CLI_VERSION,
node_version: process.version,
binary_name: "supercli",
});
}
return;
}

// Check for namespace passthrough before handling --help
// This allows commands like "cline --help --json" to pass through
{
Expand Down
2 changes: 1 addition & 1 deletion plugins/dotbot/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}
],
"install_guidance": {
"plugin": "dotbot",
"plugin": ["dotbot"],
"binary": "dotbot",
"check": "which dotbot",
"install_steps": [
Expand Down
4 changes: 2 additions & 2 deletions plugins/gum/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}
],
"install_guidance": {
"plugin": "gum",
"plugin": ["gum"],
"binary": "gum",
"check": "which gum",
"install_steps": [
Expand Down Expand Up @@ -247,7 +247,7 @@
"namespace": "gum",
"resource": "write",
"action": "text",
"description": "Write text from stdin to a file",
"description": "Interactive TUI multi-line text input with syntax highlighting",
"adapter": "process",
"adapterConfig": {
"command": "gum",
Expand Down
2 changes: 1 addition & 1 deletion plugins/gum/skills/quickstart/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ A tool for glamorous shell scripts — style, format, and interact like a charm.
- `gum format render` — Format text using markdown, template, code, or emoji formatters
- `gum table render` — Render a table from CSV/TSV or stdin data
- `gum join text` — Join text vertically or horizontally
- `gum write text` — Write text from stdin to a file with a text area
- `gum write text` — Interactive TUI multi-line text input with syntax highlighting
- `gum file pick` — Pick a file from the filesystem

## Usage Examples
Expand Down
Loading