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
5 changes: 5 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ jobs:
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Publish alias packages
run: node scripts/publish-aliases.mjs
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "failproofai",
"version": "0.0.1-beta.7",
"version": "0.0.1-beta.8",
"description": "Open-source hooks, policies, and project visualization for Claude Code & Agents SDK",
"bin": {
"failproofai": "./bin/failproofai.mjs"
Expand Down
18 changes: 18 additions & 0 deletions scripts/alias-proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env node
'use strict';
const { spawnSync } = require('child_process');
const path = require('path');
const isWin = process.platform === 'win32';

// Use the npm-generated bin wrapper so the bun shebang is handled correctly
// on all platforms (including the .cmd wrapper on Windows).
const binary = path.join(
__dirname, '..', 'node_modules', '.bin',
isWin ? 'failproofai.cmd' : 'failproofai'
);

const result = spawnSync(binary, process.argv.slice(2), {
stdio: 'inherit',
shell: isWin,
});
process.exit(result.status ?? 1);
65 changes: 65 additions & 0 deletions scripts/publish-aliases.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env node
import { execSync } from 'child_process';
import { readFileSync, writeFileSync, mkdirSync, rmSync, cpSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const rootPkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
const VERSION = rootPkg.version;
const DRY_RUN = process.argv.includes('--dry-run');

const ALIASES = [
// Formatting variants — no "ai", or hyphen/underscore separators
'failproof',
'failproof-ai',
'fail-proof-ai',
'failproof_ai',
'fail_proof_ai',
'fail-proofai',
// Missing one 'o' from "proof" — common single-char slip
'failprof',
'failprof-ai',
'failprofai',
'fail-prof-ai',
'failprof_ai',
// 'a'/'i' transposition — common keyboard slip
'faliproof',
'faliproof-ai',
'faliproofai',
];

for (const name of ALIASES) {
const tmpDir = join('/tmp', `npm-alias-${name}-${Date.now()}`);
const binDir = join(tmpDir, 'bin');
mkdirSync(binDir, { recursive: true });

const pkg = {
name,
version: VERSION,
description: `Alias for failproofai — installs if you typed '${name}' instead of 'failproofai'`,
bin: { [name]: './bin/proxy.js' },
files: ['bin/'],
dependencies: { failproofai: VERSION },
publishConfig: { access: 'public' },
repository: rootPkg.repository,
homepage: rootPkg.homepage,
bugs: rootPkg.bugs,
license: rootPkg.license,
};

writeFileSync(join(tmpDir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
cpSync(join(__dirname, 'alias-proxy.js'), join(binDir, 'proxy.js'));

if (DRY_RUN) {
console.log(`[dry-run] Would publish ${name}@${VERSION}`);
console.log(JSON.stringify(pkg, null, 2));
console.log('---');
} else {
console.log(`Publishing ${name}@${VERSION}...`);
execSync('npm publish', { cwd: tmpDir, stdio: 'inherit' });
console.log(`Done: ${name}`);
}

rmSync(tmpDir, { recursive: true, force: true });
}