# Stop Copying LICENSE Files from GitHub — Build a Generator CLI Instead

# Stop Copying LICENSE Files from GitHub — Build a Generator CLI Instead

Every open-source project needs a LICENSE file. Yet most developers either forget to add one, copy-paste from another repo, or click through GitHub's template picker without thinking twice. The problem? A project without a license isn't "free to use" — it's actually **all rights reserved** by default.

Let's fix that by building a CLI tool that generates LICENSE files automatically using GitHub's public Licenses API.

## Why Every Project Needs a LICENSE

Here's something that surprises many developers: **publishing code on GitHub does not make it open source.** Without an explicit license, copyright law applies by default. That means:

- **No one can legally copy your code.** Even if your repo is public, others cannot fork it, modify it, or include it in their projects without your permission.
- **No one can distribute your code.** Companies that audit their dependencies will flag unlicensed packages and remove them.
- **Contributors have no protection.** Without a license, there's no contributor agreement. If someone submits a PR to your unlicensed repo, the legal ownership of that contribution is ambiguous.
- **Your project can't be used in production.** Any organization with a legal team will reject dependencies that lack a license. This means your brilliant library might never get adopted.

The MIT License is just 21 lines. The Apache 2.0 License covers patent grants. The GPL ensures derivative works stay open. Each serves a different purpose, but **any license is better than no license.**

GitHub even warns you about this. When you create a repo without a license, the "About" sidebar conspicuously omits the license badge. That missing badge is a red flag for anyone evaluating your project.

## GitHub's Licenses API

Most developers don't know this, but GitHub provides a free, public API for license templates. No authentication required.

### List all available licenses

```bash
curl https://api.github.com/licenses
```

This returns a JSON array with license keys, names, and URLs:

```json
[
  {
    "key": "mit",
    "name": "MIT License",
    "spdx_id": "MIT",
    "url": "https://api.github.com/licenses/mit"
  },
  {
    "key": "apache-2.0",
    "name": "Apache License 2.0",
    "spdx_id": "Apache-2.0",
    "url": "https://api.github.com/licenses/apache-2.0"
  }
]
```

### Get a specific license template

```bash
curl https://api.github.com/licenses/mit
```

The response includes a `body` field with the full license text, complete with placeholder tokens like `[year]` and `[fullname]` that you can replace programmatically.

```json
{
  "key": "mit",
  "name": "MIT License",
  "body": "MIT License\n\nCopyright (c) [year] [fullname]\n\nPermission is hereby granted..."
}
```

This is the foundation of our CLI. Instead of maintaining a local library of license templates, we can fetch them on-demand from GitHub and fill in the blanks.

## Building the CLI

Let's build `licensegen` — a Node.js CLI that fetches license templates, populates them with your details, and writes a LICENSE file to your project root.

### Project setup

```bash
mkdir licensegen && cd licensegen
npm init -y
```

### The core logic

Create `index.js`:

```javascript
#!/usr/bin/env node

const https = require('https');
const fs = require('fs');
const path = require('path');
const readline = require('readline');

const API_BASE = 'https://api.github.com/licenses';

function fetch(url) {
  return new Promise((resolve, reject) => {
    https.get(url, { headers: { 'User-Agent': 'licensegen-cli' } }, (res) => {
      let data = '';
      res.on('data', (chunk) => (data += chunk));
      res.on('end', () => resolve(JSON.parse(data)));
      res.on('error', reject);
    });
  });
}

async function listLicenses() {
  const licenses = await fetch(API_BASE);
  console.log('\nAvailable licenses:\n');
  licenses.forEach((l) => {
    console.log(`  ${l.key.padEnd(20)} ${l.name}`);
  });
  console.log('');
}

async function generateLicense(key, fullname, year) {
  const license = await fetch(`${API_BASE}/${key}`);

  if (license.message === 'Not Found') {
    console.error(`License "${key}" not found. Run "licensegen --list" to see options.`);
    process.exit(1);
  }

  let body = license.body;
  body = body.replace(/\[year\]/g, year || new Date().getFullYear().toString());
  body = body.replace(/\[fullname\]/g, fullname);

  const outputPath = path.join(process.cwd(), 'LICENSE');
  fs.writeFileSync(outputPath, body);
  console.log(`\n${license.name} written to ${outputPath}`);
}
```

### Parsing arguments

Add argument handling at the bottom of `index.js`:

```javascript
const args = process.argv.slice(2);

if (args.includes('--list') || args.includes('-l')) {
  listLicenses();
} else if (args.includes('--help') || args.includes('-h')) {
  console.log(`
Usage: licensegen [options]

Options:
  -l, --list           List available licenses
  -k, --key <key>      License key (e.g., mit, apache-2.0)
  -n, --name <name>    Copyright holder name
  -y, --year <year>    Copyright year (defaults to current year)
  -a, --auto           Auto-detect from package.json
  -h, --help           Show help
  `);
} else {
  const keyIdx = args.indexOf('--key') !== -1 ? args.indexOf('--key') : args.indexOf('-k');
  const nameIdx = args.indexOf('--name') !== -1 ? args.indexOf('--name') : args.indexOf('-n');
  const yearIdx = args.indexOf('--year') !== -1 ? args.indexOf('--year') : args.indexOf('-y');

  const key = keyIdx !== -1 ? args[keyIdx + 1] : 'mit';
  const name = nameIdx !== -1 ? args[nameIdx + 1] : null;
  const year = yearIdx !== -1 ? args[yearIdx + 1] : null;

  if (!name && !args.includes('--auto') && !args.includes('-a')) {
    console.error('Please provide --name "Your Name" or use --auto to read from package.json');
    process.exit(1);
  }

  if (args.includes('--auto') || args.includes('-a')) {
    autoGenerate(key, year);
  } else {
    generateLicense(key, name, year);
  }
}
```

## Auto-Detecting from package.json

This is where the CLI gets smart. Most Node.js projects already declare a license type and author in `package.json`. Why make the user type it again?

```javascript
async function autoGenerate(keyOverride, yearOverride) {
  const pkgPath = path.join(process.cwd(), 'package.json');

  if (!fs.existsSync(pkgPath)) {
    console.error('No package.json found. Use --key and --name instead.');
    process.exit(1);
  }

  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));

  // Extract license key from package.json
  const key = keyOverride || (pkg.license ? pkg.license.toLowerCase() : 'mit');

  // Extract author name — handle both string and object formats
  let authorName = 'Unknown';
  if (typeof pkg.author === 'string') {
    // "John Doe <john@example.com>" → "John Doe"
    authorName = pkg.author.replace(/<.*>/, '').trim();
  } else if (pkg.author && pkg.author.name) {
    authorName = pkg.author.name;
  }

  const year = yearOverride || new Date().getFullYear().toString();

  console.log(`Detected: license=${key}, author=${authorName}, year=${year}`);
  await generateLicense(key, authorName, year);
}
```

Now running `licensegen --auto` inside any Node.js project will:

1. Read the `license` field from `package.json` (e.g., `"MIT"` becomes the API key `"mit"`)
2. Parse the `author` field (handling both `"Jane Doe"` and `{ "name": "Jane Doe" }` formats)
3. Use the current year as default
4. Fetch the template and write the file

## Supporting Multiple License Types

The GitHub API supports over a dozen licenses. Here are the most common ones your CLI can generate:

| Key | License | Best For |
|-----|---------|----------|
| `mit` | MIT License | Maximum permissiveness, minimal restrictions |
| `apache-2.0` | Apache License 2.0 | When you need explicit patent grants |
| `gpl-3.0` | GNU GPL v3 | Ensuring derivative works stay open source |
| `bsd-2-clause` | BSD 2-Clause | Similar to MIT but with different heritage |
| `mpl-2.0` | Mozilla Public License 2.0 | File-level copyleft (a middle ground) |
| `lgpl-2.1` | GNU LGPL v2.1 | Libraries that can be used in proprietary software |
| `unlicense` | The Unlicense | Dedicating work to the public domain |

To support multi-license projects (some repos include both MIT and Apache-2.0), you can extend the CLI:

```javascript
async function generateMultiple(keys, fullname, year) {
  for (const key of keys) {
    const license = await fetch(`${API_BASE}/${key}`);
    let body = license.body;
    body = body.replace(/\[year\]/g, year || new Date().getFullYear().toString());
    body = body.replace(/\[fullname\]/g, fullname);

    const filename = keys.length > 1 ? `LICENSE-${key.toUpperCase()}` : 'LICENSE';
    const outputPath = path.join(process.cwd(), filename);
    fs.writeFileSync(outputPath, body);
    console.log(`${license.name} written to ${outputPath}`);
  }
}

// Usage: licensegen --key mit --key apache-2.0 --name "Your Name"
```

This creates `LICENSE-MIT` and `LICENSE-APACHE-2.0` files — a pattern used by many Rust ecosystem projects.

## Making It Global

Add the `bin` field to your `package.json`:

```json
{
  "name": "licensegen",
  "version": "1.0.0",
  "bin": {
    "licensegen": "./index.js"
  }
}
```

Then install globally:

```bash
npm link
# or publish
npm publish
```

Now you can run `licensegen --auto` in any project directory.

## What We Built

In under 100 lines of code, we built a CLI that:

- Fetches real license templates from GitHub's API (always up-to-date)
- Auto-detects license type and author from `package.json`
- Supports every license GitHub offers
- Handles multi-license projects
- Requires zero configuration for most use cases

The broader takeaway: **GitHub's API is full of useful endpoints that most developers never explore.** The Licenses API is just one example. Before building something from scratch, check if there's an API that does the heavy lifting for you.

Next time you `npm init` a project, don't forget the LICENSE file. Better yet, automate it so you never have to think about it again.

---

*If you found this useful, check out [licensegen on npm](https://www.npmjs.com/package/licensegen) — a production-ready version of what we built here.*

