🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

getsuperpower

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

getsuperpower - npm Package Compare versions

Comparing version
0.3.3
to
0.3.4
+16
-8
examples/workflows/development-design-delivery/workflow.json

@@ -11,24 +11,32 @@ {

{
"source": "superpowers:brainstorming"
"source": "superpowers:brainstorming",
"repo": "obra/superpowers"
},
{
"source": "mattpocock:design-an-interface"
"source": "mattpocock:design-an-interface",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:grill-with-docs"
"source": "mattpocock:grill-with-docs",
"repo": "mattpocock/skills"
},
{
"source": "superpowers:writing-plans"
"source": "superpowers:writing-plans",
"repo": "obra/superpowers"
},
{
"source": "mattpocock:codebase-design"
"source": "mattpocock:codebase-design",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:tdd"
"source": "mattpocock:tdd",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:diagnosing-bugs"
"source": "mattpocock:diagnosing-bugs",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:review"
"source": "mattpocock:review",
"repo": "mattpocock/skills"
},

@@ -35,0 +43,0 @@ {

@@ -11,9 +11,12 @@ {

{
"source": "mattpocock:grilling"
"source": "mattpocock:grilling",
"repo": "mattpocock/skills"
},
{
"source": "superpowers:brainstorming"
"source": "superpowers:brainstorming",
"repo": "obra/superpowers"
},
{
"source": "superpowers:writing-plans"
"source": "superpowers:writing-plans",
"repo": "obra/superpowers"
}

@@ -20,0 +23,0 @@ ],

@@ -14,9 +14,12 @@ {

{
"source": "superpowers:brainstorming"
"source": "superpowers:brainstorming",
"repo": "obra/superpowers"
},
{
"source": "superpowers:writing-plans"
"source": "superpowers:writing-plans",
"repo": "obra/superpowers"
},
{
"source": "mattpocock:tdd"
"source": "mattpocock:tdd",
"repo": "mattpocock/skills"
},

@@ -23,0 +26,0 @@ {

@@ -14,18 +14,24 @@ {

{
"source": "superpowers:brainstorming"
"source": "superpowers:brainstorming",
"repo": "obra/superpowers"
},
{
"source": "superpowers:writing-plans"
"source": "superpowers:writing-plans",
"repo": "obra/superpowers"
},
{
"source": "mattpocock:grill-with-docs"
"source": "mattpocock:grill-with-docs",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:tdd"
"source": "mattpocock:tdd",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:codebase-design"
"source": "mattpocock:codebase-design",
"repo": "mattpocock/skills"
},
{
"source": "mattpocock:diagnosing-bugs"
"source": "mattpocock:diagnosing-bugs",
"repo": "mattpocock/skills"
}

@@ -32,0 +38,0 @@ ],

@@ -8,3 +8,4 @@ {

{
"source": "superpowers:brainstorming"
"source": "superpowers:brainstorming",
"repo": "obra/superpowers"
},

@@ -15,3 +16,4 @@ {

{
"source": "superpowers:writing-plans"
"source": "superpowers:writing-plans",
"repo": "obra/superpowers"
},

@@ -18,0 +20,0 @@ {

{
"name": "getsuperpower",
"version": "0.3.3",
"version": "0.3.4",
"type": "module",

@@ -5,0 +5,0 @@ "repository": {

+22
-10

@@ -14,5 +14,11 @@ <img src="/assets/getsupwerpower.jpg" alt="GetSuperpower" width="640" />

Install a workflow example from the GetSuperpower git repository:
Install a workflow example by alias:
```bash
npx getsuperpower@latest install openspec-superpowers
```
The alias is shorthand for the checked-in workflow path:
```bash
npx getsuperpower@latest install 'https://github.com/0xroylee/getsuperpower.git#examples/workflows/openspec-superpowers'

@@ -24,5 +30,5 @@ ```

```bash
npx getsuperpower@latest install 'https://github.com/0xroylee/getsuperpower.git#examples/workflows/release-review'
npx getsuperpower@latest clone 'https://github.com/0xroylee/getsuperpower.git#examples/workflows/real-engineering'
npx getsuperpower@latest install 'https://github.com/0xroylee/getsuperpower.git#examples/workflows/development-design-delivery'
npx getsuperpower@latest install release-review
npx getsuperpower@latest install real-engineering
npx getsuperpower@latest install development-design-delivery
```

@@ -93,4 +99,4 @@

```bash
npx getsuperpower@latest validate release-review
npx getsuperpower@latest deps release-review
npx getsuperpower@latest validate ./release-review
npx getsuperpower@latest deps ./release-review
```

@@ -109,4 +115,11 @@

GetSuperpower install automatically uses the Skills CLI to fetch missing `mattpocock:*` dependencies. If that automatic bootstrap fails, run the same package install through the CLI and retry:
GetSuperpower install automatically uses each workflow skill's `repo` metadata
to fetch missing external skills through the Skills CLI. For example,
`{ "source": "superpowers:brainstorming", "repo": "obra/superpowers" }`
keeps the original skill name in `source` and installs it with
`npx skills add obra/superpowers --skill brainstorming`.
If automatic bootstrap fails, run the same package install through the CLI and
retry:
```bash

@@ -120,8 +133,7 @@ npx getsuperpower@latest skills install mattpocock/skills

- `npx getsuperpower@latest install <path-or-git-url>`
- `npx getsuperpower@latest clone <path-or-git-url>`
- `npx getsuperpower@latest install <alias-or-path-or-git-url>`
- `npx getsuperpower@latest deps <source>`
- `npx getsuperpower@latest list`
- `npx getsuperpower@latest init <name>`
- `npx getsuperpower@latest validate <path>`
- `npx getsuperpower@latest validate <source>`
- `npx getsuperpower@latest skills install`

@@ -128,0 +140,0 @@ - `npx getsuperpower@latest skills install mattpocock/skills`

@@ -54,3 +54,3 @@ import pc from "picocolors";

` ${commandText("getsuperpower validate ./release-review")}`,
` ${commandText("getsuperpower clone https://github.com/acme/release-review.git")}`,
` ${commandText("getsuperpower install openspec-superpowers")}`,
` ${commandText("getsuperpower install ./release-review")}`,

@@ -57,0 +57,0 @@ "",

@@ -24,3 +24,3 @@ #!/usr/bin/env bun

const CLI_VERSION = "0.3.3";
const CLI_VERSION = "0.3.4";

@@ -27,0 +27,0 @@ interface CommanderVersionInternals {

@@ -15,3 +15,3 @@ import { existsSync } from "node:fs";

createWorkflowBundleScaffold,
getWorkflowSkillInstallSources,
getWorkflowSkillInstallDependencies,
installWorkflowBundle,

@@ -55,2 +55,3 @@ listInstalledWorkflowBundles,

source: string;
repo?: string;
homeDir: string;

@@ -112,4 +113,2 @@ runCommand?: GetSuperpowerExternalSkillCommandRunner;

type GetSuperpowerInstallVerb = "install" | "clone";
interface GetSuperpowerInstallCommandOptions {

@@ -135,3 +134,3 @@ dir: string;

.description("Compatibility alias for GetSuperpower install and list commands.");
configureInstallCommand(workflowCommand, options, { includeClone: false });
configureInstallCommand(workflowCommand, options);
configureListCommand(workflowCommand, options.rootDir);

@@ -199,19 +198,10 @@

options: ConfigureGetSuperpowerCommandOptions,
commandOptions: { includeClone?: boolean } = {},
): void {
configureInstallLikeCommand(command, options, "install");
if (commandOptions.includeClone !== false) {
configureInstallLikeCommand(command, options, "clone");
}
}
function configureInstallLikeCommand(
command: Command,
options: ConfigureGetSuperpowerCommandOptions,
verb: GetSuperpowerInstallVerb,
): void {
command
.command(verb)
.command("install")
.description("Install a GetSuperpower and its skills.")
.argument("<source>", "local GetSuperpower path, workflow.json path, or public git source")
.argument(
"<source>",
"workflow alias, local GetSuperpower path, workflow.json path, or public git source",
)
.option(

@@ -250,6 +240,7 @@ "--dir <dir>",

try {
for (const skillSource of getWorkflowSkillInstallSources(bundle)) {
for (const skillDependency of getWorkflowSkillInstallDependencies(bundle)) {
const skillResult = await installGetSuperpowerSkillDependency({
rootDir: targetDir,
source: skillSource,
source: skillDependency.source,
...(skillDependency.repo ? { repo: skillDependency.repo } : {}),
homeDir,

@@ -280,2 +271,3 @@ agents: installAgents,

source: string;
repo?: string;
homeDir: string;

@@ -290,3 +282,7 @@ agents: ReturnType<typeof parseSkillInstallAgents>;

} catch (error) {
const externalPackage = getSkillsCliPackageForMissingDependency(input.source, error);
const externalPackage = getSkillsCliPackageForMissingDependency(
input.source,
input.repo,
error,
);
if (!externalPackage) {

@@ -296,9 +292,13 @@ throw error;

if (!input.installedExternalPackages.has(externalPackage)) {
const externalInstallKey =
getSkillsCliInstallKeyForSource(input.source, input.repo) ?? externalPackage;
if (!input.installedExternalPackages.has(externalInstallKey)) {
console.log(keyValue("Installing external skill dependency", externalPackage));
await input.installExternalSkillDependency({
source: input.source,
...(input.repo ? { repo: input.repo } : {}),
homeDir: input.homeDir,
});
input.installedExternalPackages.add(externalPackage);
input.installedExternalPackages.add(externalInstallKey);
}

@@ -339,3 +339,7 @@

function getSkillsCliPackageForMissingDependency(source: string, error: unknown): string | null {
function getSkillsCliPackageForMissingDependency(
source: string,
repo: string | undefined,
error: unknown,
): string | null {
if (!isMissingBootstrappableSkillError(error)) {

@@ -345,3 +349,3 @@ return null;

return getSkillsCliPackageForSource(source);
return getSkillsCliPackageForDependency(source, repo);
}

@@ -383,2 +387,55 @@

function getSkillsCliPackageForDependency(source: string, repo: string | undefined): string | null {
return normalizeSkillsCliRepoSource(repo) ?? getSkillsCliPackageForSource(source);
}
function normalizeSkillsCliRepoSource(repo: string | undefined): string | null {
const trimmed = repo?.trim();
if (!trimmed) {
return null;
}
const markdownLinkMatch = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(trimmed);
if (!markdownLinkMatch) {
return trimmed;
}
const label = markdownLinkMatch[1]?.trim();
const url = markdownLinkMatch[2]?.trim();
if (label && isBareSkillsCliPackage(label)) {
return label;
}
return url || null;
}
function getSkillsCliInstallKeyForSource(source: string, repo?: string): string | null {
const skillName = getSkillsCliSkillNameForSource(source);
if (!skillName) {
return getSkillsCliPackageForDependency(source, repo);
}
const packageName = getSkillsCliPackageForDependency(source, repo);
return packageName ? `${packageName}:${skillName}` : null;
}
function getSkillsCliSkillNameForSource(source: string): string | null {
if (source.startsWith("superpowers:")) {
return source.slice("superpowers:".length).trim() || null;
}
if (source.startsWith("mattpocock:")) {
return source.slice("mattpocock:".length).trim() || null;
}
const mattPocockGithubPrefix = "github:mattpocock/skills/";
if (source.startsWith(mattPocockGithubPrefix)) {
const suffix = source.slice(mattPocockGithubPrefix.length);
const skillPath = suffix.startsWith("skills/") ? suffix.slice("skills/".length) : suffix;
return skillPath.trim() || null;
}
return null;
}
function isBareSkillsCliPackage(source: string): boolean {

@@ -391,3 +448,3 @@ return /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(source);

): Promise<void> {
const packageName = getSkillsCliPackageForSource(input.source);
const packageName = getSkillsCliPackageForDependency(input.source, input.repo);
if (!packageName) {

@@ -397,5 +454,11 @@ throw new Error(`No skills CLI package is known for dependency: ${input.source}`);

const args = ["--yes", "skills@latest", "add", packageName, "--yes", "--global"];
const skillName = getSkillsCliSkillNameForSource(input.source);
if (skillName) {
args.push("--skill", skillName, "--agent", "codex");
}
const result = await (input.runCommand ?? runExternalSkillCommand)({
executable: "npx",
args: ["--yes", "skills@latest", "add", packageName, "--yes", "--global"],
args,
cwd: input.homeDir,

@@ -402,0 +465,0 @@ env: {

@@ -9,5 +9,9 @@ import { existsSync } from "node:fs";

const workflowStoreDir = ".getsuperpower/workflows";
const canonicalExamplesGitUrl = "https://github.com/0xroylee/getsuperpower.git";
const canonicalExamplesWorkflowPath = "examples/workflows";
const workflowAliasPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
const WorkflowSkillSchema = z.object({
source: z.string().min(1),
repo: z.string().min(1).optional(),
optional: z.boolean().optional(),

@@ -103,2 +107,7 @@ });

export interface WorkflowSkillInstallDependency {
source: string;
repo?: string;
}
export interface InstalledWorkflowBundle extends WorkflowBundleManifest {

@@ -146,2 +155,7 @@ source: WorkflowBundleSource;

await resolvedSource.cleanup?.();
if (resolvedSource.alias && resolvedSource.source.kind === "git" && isMissingFileError(error)) {
throw new Error(
`GetSuperpower workflow alias not found: ${resolvedSource.alias}\nChecked: ${resolvedSource.source.url}`,
);
}
if (resolvedSource.source.kind === "git" && isMissingFileError(error)) {

@@ -230,7 +244,17 @@ throw new Error(

export function getWorkflowSkillInstallSources(bundle: WorkflowBundle): string[] {
return getWorkflowSkillInstallDependencies(bundle).map((skill) => skill.source);
}
export function getWorkflowSkillInstallDependencies(
bundle: WorkflowBundle,
): WorkflowSkillInstallDependency[] {
return bundle.manifest.skills.map((skill) => {
if (skill.source.startsWith("./") || skill.source.startsWith("../")) {
return resolve(bundle.sourceDir, skill.source);
}
return skill.source;
const source =
skill.source.startsWith("./") || skill.source.startsWith("../")
? resolve(bundle.sourceDir, skill.source)
: skill.source;
return {
source,
...(skill.repo ? { repo: skill.repo } : {}),
};
});

@@ -254,5 +278,5 @@ }

{ source: `./skills/${name}` },
{ source: "superpowers:brainstorming" },
{ source: "superpowers:brainstorming", repo: "obra/superpowers" },
{ source: "./skills/custom-review" },
{ source: "superpowers:writing-plans" },
{ source: "superpowers:writing-plans", repo: "obra/superpowers" },
],

@@ -332,2 +356,3 @@ steps: [

cleanup?: () => Promise<void>;
alias?: string;
}

@@ -343,2 +368,7 @@

): Promise<ResolvedWorkflowBundleSource> {
const aliasSource = parseWorkflowAliasSource(source);
if (aliasSource) {
return cloneGitWorkflowSource(aliasSource, options);
}
const gitSource = parseGitWorkflowSource(source);

@@ -355,6 +385,20 @@ if (gitSource) {

throw new Error(
`Unsupported GetSuperpower source: ${source}. Use a local path, workflow.json path, or public git URL.`,
`Unsupported GetSuperpower source: ${source}. Use a local path, workflow.json path, public git URL, or lowercase workflow alias.`,
);
}
function parseWorkflowAliasSource(source: string): GitWorkflowSource | null {
if (!workflowAliasPattern.test(source)) {
return null;
}
const url = `${canonicalExamplesGitUrl}#${canonicalExamplesWorkflowPath}/${source}`;
const gitSource = parseGitWorkflowSource(url);
if (!gitSource) {
throw new Error(`Could not build canonical GetSuperpower workflow alias URL: ${source}`);
}
return { ...gitSource, alias: source };
}
function isLocalWorkflowSource(source: string): boolean {

@@ -376,2 +420,3 @@ return (

subdirectory?: string;
alias?: string;
}

@@ -477,2 +522,3 @@

cleanup,
...(source.alias ? { alias: source.alias } : {}),
};

@@ -479,0 +525,0 @@ } catch (error) {

@@ -237,3 +237,3 @@ import { mkdir, mkdtemp, stat, writeFile } from "node:fs/promises";

passed: !(await pathExists(workflowRecord)),
detail: ".getsuperpower workflow records should not exist before install/clone",
detail: ".getsuperpower workflow records should not exist before install",
},

@@ -263,3 +263,3 @@ {

commands: [
["clone", input.workflowSourcePath, "--home", input.homeDir, "--agents", "codex"],
["install", input.workflowSourcePath, "--home", input.homeDir, "--agents", "codex"],
["list"],

@@ -283,3 +283,3 @@ ],

mode: "workflow",
label: "After workflow clone/use",
label: "After workflow install/use",
projectDir: input.projectDir,

@@ -306,3 +306,3 @@ homeDir: input.homeDir,

passed: logs.some((line) => stripAnsi(line).includes("openspec-delivery 0.1.0")),
detail: "list should show the workflow installed by clone",
detail: "list should show the workflow installed by install",
},

@@ -309,0 +309,0 @@ {

Sorry, the diff of this file is too big to display