Merge branch 'main' into pr/smallflyingpig/36

# Conflicts:
#	src/entrypoints/cli.tsx
This commit is contained in:
claude-code-best
2026-04-02 21:25:53 +08:00
184 changed files with 11136 additions and 1771 deletions

View File

@@ -77,7 +77,7 @@ export async function call(onDone: LocalJSXCommandOnDone, _context: unknown, arg
}
// Redirect base /mcp command to /plugins installed tab for ant users
if (("external" as string) === 'ant') {
if ((process.env.USER_TYPE) === 'ant') {
return <PluginSettings onComplete={onDone} args="manage" showMcpRedirectMessage />;
}
return <MCPSettings onComplete={onDone} />;

View File

@@ -0,0 +1,147 @@
import { describe, expect, test } from "bun:test";
import { parsePluginArgs } from "../parseArgs";
describe("parsePluginArgs", () => {
// No args
test("returns { type: 'menu' } for undefined", () => {
expect(parsePluginArgs(undefined)).toEqual({ type: "menu" });
});
test("returns { type: 'menu' } for empty string", () => {
expect(parsePluginArgs("")).toEqual({ type: "menu" });
});
test("returns { type: 'menu' } for whitespace only", () => {
expect(parsePluginArgs(" ")).toEqual({ type: "menu" });
});
// Help
test("returns { type: 'help' } for 'help'", () => {
expect(parsePluginArgs("help")).toEqual({ type: "help" });
});
test("returns { type: 'help' } for '--help'", () => {
expect(parsePluginArgs("--help")).toEqual({ type: "help" });
});
test("returns { type: 'help' } for '-h'", () => {
expect(parsePluginArgs("-h")).toEqual({ type: "help" });
});
// Install
test("parses 'install my-plugin' -> { type: 'install', plugin: 'my-plugin' }", () => {
expect(parsePluginArgs("install my-plugin")).toEqual({
type: "install",
plugin: "my-plugin",
});
});
test("parses 'install my-plugin@github' with marketplace", () => {
expect(parsePluginArgs("install my-plugin@github")).toEqual({
type: "install",
plugin: "my-plugin",
marketplace: "github",
});
});
test("parses 'install https://github.com/...' as URL marketplace", () => {
expect(parsePluginArgs("install https://github.com/plugins/my-plugin")).toEqual({
type: "install",
marketplace: "https://github.com/plugins/my-plugin",
});
});
test("parses 'i plugin' as install shorthand", () => {
expect(parsePluginArgs("i plugin")).toEqual({
type: "install",
plugin: "plugin",
});
});
test("install without target returns type only", () => {
expect(parsePluginArgs("install")).toEqual({ type: "install" });
});
// Uninstall
test("returns { type: 'uninstall', plugin: '...' }", () => {
expect(parsePluginArgs("uninstall my-plugin")).toEqual({
type: "uninstall",
plugin: "my-plugin",
});
});
// Enable/disable
test("returns { type: 'enable', plugin: '...' }", () => {
expect(parsePluginArgs("enable my-plugin")).toEqual({
type: "enable",
plugin: "my-plugin",
});
});
test("returns { type: 'disable', plugin: '...' }", () => {
expect(parsePluginArgs("disable my-plugin")).toEqual({
type: "disable",
plugin: "my-plugin",
});
});
// Validate
test("returns { type: 'validate', path: '...' }", () => {
expect(parsePluginArgs("validate /path/to/plugin")).toEqual({
type: "validate",
path: "/path/to/plugin",
});
});
// Manage
test("returns { type: 'manage' }", () => {
expect(parsePluginArgs("manage")).toEqual({ type: "manage" });
});
// Marketplace
test("parses 'marketplace add ...'", () => {
expect(parsePluginArgs("marketplace add https://example.com")).toEqual({
type: "marketplace",
action: "add",
target: "https://example.com",
});
});
test("parses 'marketplace remove ...'", () => {
expect(parsePluginArgs("marketplace remove my-source")).toEqual({
type: "marketplace",
action: "remove",
target: "my-source",
});
});
test("parses 'marketplace list'", () => {
expect(parsePluginArgs("marketplace list")).toEqual({
type: "marketplace",
action: "list",
});
});
test("parses 'market' as alias for 'marketplace'", () => {
expect(parsePluginArgs("market list")).toEqual({
type: "marketplace",
action: "list",
});
});
// Boundary
test("handles extra whitespace", () => {
expect(parsePluginArgs(" install my-plugin ")).toEqual({
type: "install",
plugin: "my-plugin",
});
});
test("handles unknown subcommand gracefully", () => {
expect(parsePluginArgs("foobar")).toEqual({ type: "menu" });
});
test("marketplace without action returns type only", () => {
expect(parsePluginArgs("marketplace")).toEqual({ type: "marketplace" });
});
});

View File

@@ -119,7 +119,7 @@ export async function setupTerminal(theme: ThemeName): Promise<string> {
maybeMarkProjectOnboardingComplete();
// Install shell completions (ant-only, since the completion command is ant-only)
if (("external" as string) === 'ant') {
if ((process.env.USER_TYPE) === 'ant') {
result += await setupShellCompletion(theme);
}
return result;

View File

@@ -29,10 +29,10 @@ const INTERNAL_MARKETPLACE_NAME = 'claude-code-marketplace';
const INTERNAL_MARKETPLACE_REPO = 'anthropics/claude-code-marketplace';
const OFFICIAL_MARKETPLACE_REPO = 'anthropics/claude-plugins-official';
function getMarketplaceName(): string {
return ("external" as string) === 'ant' ? INTERNAL_MARKETPLACE_NAME : OFFICIAL_MARKETPLACE_NAME;
return (process.env.USER_TYPE) === 'ant' ? INTERNAL_MARKETPLACE_NAME : OFFICIAL_MARKETPLACE_NAME;
}
function getMarketplaceRepo(): string {
return ("external" as string) === 'ant' ? INTERNAL_MARKETPLACE_REPO : OFFICIAL_MARKETPLACE_REPO;
return (process.env.USER_TYPE) === 'ant' ? INTERNAL_MARKETPLACE_REPO : OFFICIAL_MARKETPLACE_REPO;
}
function getPluginId(): string {
return `thinkback@${getMarketplaceName()}`;

View File

@@ -53,7 +53,7 @@ const DEFAULT_INSTRUCTIONS: string = (typeof _rawPrompt === 'string' ? _rawPromp
// Shell-set env only, so top-level process.env read is fine
// — settings.env never injects this.
/* eslint-disable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs -- ant-only dev override; eager top-level read is the point (crash at startup, not silently inside the slash-command try/catch) */
const ULTRAPLAN_INSTRUCTIONS: string = ("external" as string) === 'ant' && process.env.ULTRAPLAN_PROMPT_FILE ? readFileSync(process.env.ULTRAPLAN_PROMPT_FILE, 'utf8').trimEnd() : DEFAULT_INSTRUCTIONS;
const ULTRAPLAN_INSTRUCTIONS: string = (process.env.USER_TYPE) === 'ant' && process.env.ULTRAPLAN_PROMPT_FILE ? readFileSync(process.env.ULTRAPLAN_PROMPT_FILE, 'utf8').trimEnd() : DEFAULT_INSTRUCTIONS;
/* eslint-enable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs */
/**
@@ -464,7 +464,7 @@ export default {
name: 'ultraplan',
description: `~1030 min · Claude Code on the web drafts an advanced plan you can edit and approve. See ${CCR_TERMS_URL}`,
argumentHint: '<prompt>',
isEnabled: () => ("external" as string) === 'ant',
isEnabled: () => (process.env.USER_TYPE) === 'ant',
load: () => Promise.resolve({
call
})