Skip to content
Merged
4 changes: 2 additions & 2 deletions packages/create-gen-app-test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This package provides functionality to clone and cache template repositories for
import { createFromCachedTemplate } from 'create-gen-app-test';

const result = await createFromCachedTemplate({
templateUrl: 'https://github.com/launchql/pgpm-boilerplates',
templateUrl: 'https://github.com/constructive-io/pgpm-boilerplates',
outputDir: './my-new-project',
answers: {
PROJECT_NAME: 'my-project',
Expand Down Expand Up @@ -65,7 +65,7 @@ Create project from cached template.

The package includes comprehensive integration tests that:

1. Clone real repositories from GitHub (default: https://github.com/launchql/pgpm-boilerplates)
1. Clone real repositories from GitHub (default: https://github.com/constructive-io/pgpm-boilerplates)
2. Cache templates using appstash
3. Process templates with variable replacement
4. Snapshot generated files and package.json files
Expand Down
11 changes: 5 additions & 6 deletions packages/create-gen-app-test/dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ pnpm dev --output ./my-generated-template

## What it does

1. **Clones the default repository**: `https://github.com/launchql/pgpm-boilerplates/` (override via `--repo`, select branch via `--branch`)
2. **Lists available templates**: looks for subdirectories inside `--path` (default `.`; typically `module`, `workspace`)
1. **Clones the default repository**: `https://github.com/constructive-io/pgpm-boilerplates/` (override via `--repo`, select branch via `--branch`)
2. **Lists available templates**: looks for subdirectories inside `--path` (default `default`; typically contains `module`, `workspace`)
3. **Prompts for selection**: Uses `inquirerer` to display an interactive list of templates
4. **Processes the template**:
- Extracts variables from the selected folder
Expand All @@ -39,9 +39,9 @@ pnpm dev --output ./my-generated-template

Command-line flags override the defaults below (see `dev/index.ts`):

- `--repo` (`-r`): repository URL to clone (default: `https://github.com/launchql/pgpm-boilerplates/`)
- `--repo` (`-r`): repository URL to clone (default: `https://github.com/constructive-io/pgpm-boilerplates/`)
- `--branch` (`-b`): branch or tag to checkout
- `--path` (`-p`): directory within the repo to scan for templates (default: `.`)
- `--path` (`-p`): directory within the repo to scan for templates (default: `default`)
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't default be ., and in our repo, we're setting it to default because we have the folder there?

Copy link
Contributor Author

@sdqede sdqede Dec 16, 2025

Choose a reason for hiding this comment

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

We originally hardcoded default to stop CI failures after the boilerplate repo moved templates under default/ and the dev harness didn’t read .boilerplates.json. Now the CLI auto-detects the base dir from that file (fallback .), so CI stays green without the hardcoded path

- `--template` (`-t`): template folder to use (e.g., `module`, `workspace`); if omitted, an interactive list appears
- `--output` (`-o`): output directory for generated project (default: `./test-output`)

Expand All @@ -51,7 +51,7 @@ Command-line flags override the defaults below (see `dev/index.ts`):
$ pnpm dev
🚀 create-gen-app development script

Cloning template from https://github.com/launchql/pgpm-boilerplates/...
Cloning template from https://github.com/constructive-io/pgpm-boilerplates/...

Found 2 template(s): module, workspace

Expand All @@ -73,4 +73,3 @@ Extracting template variables...
- The temporary clone directory is automatically cleaned up after generation
- You can test different templates without affecting your workspace


5 changes: 2 additions & 3 deletions packages/create-gen-app-test/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import * as path from "path";

import { cloneRepo, extractVariables, promptUser, replaceVariables } from "create-gen-app";

const DEFAULT_REPO = "https://github.com/launchql/pgpm-boilerplates/";
const DEFAULT_DIRECTORY = ".";
const DEFAULT_REPO = "https://github.com/constructive-io/pgpm-boilerplates/";
const DEFAULT_DIRECTORY = "default";
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is the default, . makes more sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the same as above reply

const OUTPUT_DIR = "./test-output";

const argv = minimist(process.argv.slice(2), {
Expand Down Expand Up @@ -116,4 +116,3 @@ async function main() {

main();


Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`cached template integration tests first clone with variable replacement should snapshot created directory structure 1`] = `
[
".boilerplate.json",
"LICENSE",
"README.md",
"__tests__/",
Expand All @@ -21,7 +22,7 @@ exports[`cached template integration tests first clone with variable replacement
"description": "Integration test module test",
"devDependencies": {
"makage": "0.1.8",
"pgsql-test": "^2.14.12",
"pgsql-test": "^2.16.2",
},
"homepage": "https://github.com/tester-test/integration-test",
"keywords": [],
Expand All @@ -38,7 +39,7 @@ exports[`cached template integration tests first clone with variable replacement
"scripts": {
"lint": "eslint . --fix",
"test": "jest",
"test:watch": "makage test --watch deploy --ext sql",
"test:watch": "jest --watch",
},
"version": "0.0.1",
},
Expand All @@ -47,6 +48,7 @@ exports[`cached template integration tests first clone with variable replacement

exports[`cached template integration tests second clone from cache should snapshot created directory structure from cache 1`] = `
[
".boilerplate.json",
"LICENSE",
"README.md",
"__tests__/",
Expand All @@ -66,7 +68,7 @@ exports[`cached template integration tests second clone from cache should snapsh
"description": "Integration test module cached",
"devDependencies": {
"makage": "0.1.8",
"pgsql-test": "^2.14.12",
"pgsql-test": "^2.16.2",
},
"homepage": "https://github.com/tester-cached/integration-cached",
"keywords": [],
Expand All @@ -83,7 +85,7 @@ exports[`cached template integration tests second clone from cache should snapsh
"scripts": {
"lint": "eslint . --fix",
"test": "jest",
"test:watch": "makage test --watch deploy --ext sql",
"test:watch": "jest --watch",
},
"version": "0.0.1",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as path from 'path';
import { appstash, resolve } from 'appstash';

import { createFromTemplate, CacheManager, GitCloner } from '../index';
import { buildAnswers, TEST_REPO, TEST_TEMPLATE } from '../test-utils/integration-helpers';
import { buildAnswers, TEST_REPO, TEST_TEMPLATE_PATH } from '../test-utils/integration-helpers';

const DEFAULT_TEMPLATE_URL = TEST_REPO;

Expand Down Expand Up @@ -98,7 +98,7 @@ describe('cached template integration tests', () => {
answers: buildAnswers('test'),
toolName: testCacheTool,
noTty: true,
fromPath: TEST_TEMPLATE
fromPath: TEST_TEMPLATE_PATH
});
}, 60000);

Expand Down Expand Up @@ -166,7 +166,7 @@ describe('cached template integration tests', () => {
answers: buildAnswers('warmup'),
toolName: testCacheTool,
noTty: true,
fromPath: TEST_TEMPLATE
fromPath: TEST_TEMPLATE_PATH
});

secondOutputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'second-clone-'));
Expand All @@ -177,7 +177,7 @@ describe('cached template integration tests', () => {
answers: buildAnswers('cached'),
toolName: testCacheTool,
noTty: true,
fromPath: TEST_TEMPLATE
fromPath: TEST_TEMPLATE_PATH
});
}, 60000);

Expand Down
3 changes: 2 additions & 1 deletion packages/create-gen-app-test/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { runCli } from "../cli";
import {
TEST_BRANCH,
TEST_REPO,
TEST_TEMPLATE_DIR,
TEST_TEMPLATE,
buildAnswers,
cleanupWorkspace,
Expand All @@ -24,7 +25,7 @@ describe("CLI integration via create-gen-app-test harness", () => {
"--branch",
TEST_BRANCH,
"--path",
".",
TEST_TEMPLATE_DIR,
"--template",
TEST_TEMPLATE,
"--output",
Expand Down
18 changes: 10 additions & 8 deletions packages/create-gen-app-test/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { Inquirerer, ListQuestion } from "inquirerer";
import minimist, { ParsedArgs } from "minimist";

import { CacheManager, GitCloner, checkNpmVersion } from "create-gen-app";
import { createFromTemplate } from './index';
import { createFromTemplate } from "./index";

const DEFAULT_REPO = "https://github.com/launchql/pgpm-boilerplates.git";
const DEFAULT_PATH = ".";
const DEFAULT_REPO = "https://github.com/constructive-io/pgpm-boilerplates.git";
const DEFAULT_PATH = "default";
Copy link
Contributor

Choose a reason for hiding this comment

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

again this should be .

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the same as above reply

const DEFAULT_OUTPUT_FALLBACK = "create-gen-app-output";
const DEFAULT_TOOL_NAME = "create-gen-app-test";
const DEFAULT_TTL = 604800000; // 1 week
Expand Down Expand Up @@ -134,7 +134,7 @@ export async function runCli(
console.warn(
`⚠️ Cached template expired (last updated: ${new Date(expiredMetadata.lastUpdated).toLocaleString()})`
);
console.log('Updating cache...');
Copy link
Contributor

Choose a reason for hiding this comment

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

@sdqede please use ' not " — you're changing things that make PRs hard to read, and also it's not our preferred style, please use '' (single quotes)

Copy link
Contributor

Choose a reason for hiding this comment

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

and @sdqede while you're working on this repo, can you also fix all the [email protected] and please change it to [email protected] thanks

console.log("Updating cache...");
cacheManager.clear(cacheKey);
}

Expand All @@ -150,7 +150,7 @@ export async function runCli(
gitCloner.clone(normalizedUrl, tempDest, { branch: args.branch, depth: 1 });
cacheManager.set(cacheKey, tempDest);
templateDir = tempDest;
console.log('Template cached for future runs');
console.log("Template cached for future runs");
}

try {
Expand Down Expand Up @@ -209,8 +209,8 @@ export async function runCli(
const answerOverrides = extractAnswerOverrides(args);
const noTty = Boolean(
args["no-tty"] ??
(args as Record<string, unknown>).noTty ??
(args as Record<string, unknown>).tty === false
(args as Record<string, unknown>).noTty ??
(args as Record<string, unknown>).tty === false
);

// Use the createFromTemplate function which will use the same cache
Expand Down Expand Up @@ -330,7 +330,9 @@ if (require.main === module) {
}

function resolveTtlOption(args: ParsedArgs): number | undefined {
const disableTtl = Boolean(args["no-ttl"] ?? (args as Record<string, unknown>).noTtl);
const disableTtl = Boolean(
args["no-ttl"] ?? (args as Record<string, unknown>).noTtl
);
if (disableTtl) {
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import * as path from "path";

export const TEST_REPO =
process.env.CREATE_GEN_TEST_REPO ??
"https://github.com/launchql/pgpm-boilerplates.git";
"https://github.com/constructive-io/pgpm-boilerplates.git";
export const TEST_BRANCH =
process.env.CREATE_GEN_TEST_BRANCH ?? "license";
process.env.CREATE_GEN_TEST_BRANCH ?? "main";
export const TEST_TEMPLATE_DIR =
process.env.CREATE_GEN_TEST_BASE_PATH ?? "default";
Copy link
Contributor

Choose a reason for hiding this comment

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

.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed the hardcoded default here as well. The test helper now mirrors the CLI behavior: it prefers CREATE_GEN_TEST_BASE_PATH, otherwise reads .boilerplates.json to determine the base dir and falls back to .. The earlier default was to keep tests working after templates moved under that folder without reading the config file; auto-detection makes this robust without assumptions.

export const TEST_TEMPLATE =
process.env.CREATE_GEN_TEST_TEMPLATE ?? "module";
export const TEST_TEMPLATE_PATH =
process.env.CREATE_GEN_TEST_TEMPLATE_PATH ??
path.join(TEST_TEMPLATE_DIR, TEST_TEMPLATE);

export interface TempWorkspace {
baseDir: string;
Expand Down
15 changes: 14 additions & 1 deletion packages/inquirerer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,19 @@ Inquirerer comes with several built-in resolvers ready to use:
| `date.now` | ISO timestamp | `"2025-11-23T15:30:45.123Z"` |
| `date.timestamp` | Unix timestamp (ms) | `"1732375845123"` |

#### Workspace (nearest package.json)

| Resolver | Description | Example Output |
|----------|-------------|----------------|
| `workspace.name` | Repo slug from `repository` URL (fallback: `package.json` `name`) | `"dev-utils"` |
| `workspace.repo.name` | Repo name from `repository` URL | `"dev-utils"` |
| `workspace.repo.organization` | Repo org/owner from `repository` URL | `"constructive-io"` |
| `workspace.organization.name` | Alias for `workspace.repo.organization` | `"constructive-io"` |
| `workspace.license` | License field from `package.json` | `"MIT"` |
| `workspace.author` | Author name from `package.json` | `"Constructive"` |
| `workspace.author.name` | Author name from `package.json` | `"Constructive"` |
| `workspace.author.email` | Author email from `package.json` | `"[email protected]"` |
Copy link
Contributor

@pyramation pyramation Dec 16, 2025

Choose a reason for hiding this comment

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

please use this as an example:
([email protected])

Copy link
Contributor Author

Choose a reason for hiding this comment

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

modified


### Priority Order

When resolving default values, inquirerer follows this priority:
Expand Down Expand Up @@ -1047,4 +1060,4 @@ const handler: CommandHandler = async (argv, prompter) => {

const cli = new CLI(handler, options);
await cli.run();
```
```
22 changes: 22 additions & 0 deletions packages/inquirerer/__tests__/resolvers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,13 +499,23 @@ describe('Debug Mode', () => {

describe('Workspace Resolvers', () => {
it('should have workspace resolvers registered by default', () => {
expect(globalResolverRegistry.has('workspace.name')).toBe(true);
expect(globalResolverRegistry.has('workspace.repo.name')).toBe(true);
expect(globalResolverRegistry.has('workspace.repo.organization')).toBe(true);
expect(globalResolverRegistry.has('workspace.organization.name')).toBe(true);
expect(globalResolverRegistry.has('workspace.license')).toBe(true);
expect(globalResolverRegistry.has('workspace.author')).toBe(true);
expect(globalResolverRegistry.has('workspace.author.name')).toBe(true);
expect(globalResolverRegistry.has('workspace.author.email')).toBe(true);
});

it('should resolve workspace.name preferring repo name', async () => {
const result = await globalResolverRegistry.resolve('workspace.name');

// Repository slug of dev-utils package
expect(result).toBe('dev-utils');
});

it('should resolve workspace.repo.name from package.json', async () => {
// This test runs from the dev-utils directory which has a package.json with repository
const result = await globalResolverRegistry.resolve('workspace.repo.name');
Expand All @@ -520,6 +530,18 @@ describe('Workspace Resolvers', () => {
expect(result).toBe('constructive-io');
});

it('should resolve workspace.organization.name from package.json', async () => {
const result = await globalResolverRegistry.resolve('workspace.organization.name');

expect(result).toBe('constructive-io');
});

it('should resolve workspace.license from package.json', async () => {
const result = await globalResolverRegistry.resolve('workspace.license');

expect(result).toBe('MIT');
});

it('should resolve workspace.author from package.json', async () => {
const result = await globalResolverRegistry.resolve('workspace.author');

Expand Down
28 changes: 28 additions & 0 deletions packages/inquirerer/__tests__/setFrom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,34 @@ describe('Inquirerer - setFrom feature', () => {

expect(result).toEqual({ year: '1967' });
});

it('should use workspace resolvers with setFrom', async () => {
const prompter = new Inquirerer({
input: mockInput,
output: mockOutput,
noTty: true
});

const questions: Question[] = [
{
name: 'repoName',
type: 'text',
setFrom: 'workspace.name'
},
{
name: 'license',
type: 'text',
setFrom: 'workspace.license'
}
];

const result = await prompter.prompt({}, questions);

expect(result).toEqual({
repoName: 'dev-utils',
license: 'MIT'
});
});
});

describe('setFrom vs defaultFrom behavior', () => {
Expand Down
26 changes: 26 additions & 0 deletions packages/inquirerer/src/resolvers/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ function parseAuthor(author: string | { name?: string; email?: string; url?: str
* These resolve values from the nearest package.json in the current working directory.
*/
export const workspaceResolvers: ResolverRegistry = {
'workspace.name': () => {
const pkg = findPackageJsonFromCwd();
if (!pkg) return undefined;
const url = getRepositoryUrl(pkg);
// Prefer repo slug when repository is set; fall back to package name
if (url) {
const parsed = parseGitHubUrl(url);
if (parsed.name) return parsed.name;
}
return pkg.name;
},

'workspace.repo.name': () => {
const pkg = findPackageJsonFromCwd();
if (!pkg) return undefined;
Expand All @@ -104,6 +116,20 @@ export const workspaceResolvers: ResolverRegistry = {
return parseGitHubUrl(url).organization;
},

// Alias for repo.organization for template readability
'workspace.organization.name': () => {
const pkg = findPackageJsonFromCwd();
if (!pkg) return undefined;
const url = getRepositoryUrl(pkg);
if (!url) return undefined;
return parseGitHubUrl(url).organization;
},

'workspace.license': () => {
const pkg = findPackageJsonFromCwd();
return pkg?.license;
},

'workspace.author': () => {
const pkg = findPackageJsonFromCwd();
if (!pkg) return undefined;
Expand Down