@garyr/pt-cli 0.30.1 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,3 +119,12 @@ pt init ./new-project --file my-template.json --yes
119
119
  An official agent skill is included in this repository: [`skills/agency-pt-operator/SKILL.md`](skills/agency-pt-operator/SKILL.md).
120
120
 
121
121
  Equipping your agent with this skill allows it to automatically use `pt-cli` to lay down standardized boilerplate and capture new architectures you develop together.
122
+ ## Warning
123
+
124
+ **⚠️ Beta Software Warning**: This project is in beta stage. While stable for most use cases, please make regular backups of your template configurations before running updates or major changes.
125
+
126
+ **💾 Backup Paths**:
127
+ - **Linux/macOS**: `~/.pt/` or `/home/username/.pt/` and `/Users/username/.pt/`
128
+ - **Windows**: `%USERPROFILE%\.pt\` (typically `C:\Users\Username\.pt\`)
129
+
130
+ Always back up these directories before installing new versions or making significant changes.
@@ -252,57 +252,72 @@ export async function learn(sourcePath, updateTemplate = null, options = {}) {
252
252
  selectedFolders = rootDirs.filter(d => ['APP', 'scripts', 'bin'].some(p => d === p)); // Only copy specific ones recursively
253
253
  }
254
254
  else {
255
- const filesResponse = await inquirer.prompt({
256
- type: 'checkbox',
257
- name: 'selectedFiles',
258
- message: 'Select root files to include as boilerplate:',
259
- loop: false,
260
- theme: {
261
- icon: {
262
- checked: chalk.green('[x] '),
263
- unchecked: '[ ] ',
264
- }
265
- },
266
- choices: rootFiles.map(f => ({
267
- name: f,
268
- checked: ['.makerc', 'readme.md', 'README.md', '.gitattributes', '.gitignore', 'Makefile', 'makefile', 'package.json'].some(p => f.toLowerCase() === p.toLowerCase())
269
- }))
270
- });
271
- selectedFiles = filesResponse.selectedFiles;
272
- const foldersResponse = await inquirer.prompt({
273
- type: 'checkbox',
274
- name: 'selectedStructure',
275
- message: 'Select folders to include in the template structure (skeleton):',
276
- loop: false,
277
- theme: {
278
- icon: {
279
- checked: chalk.green('[x] '),
280
- unchecked: '[ ] ',
281
- }
282
- },
283
- choices: rootDirs.map(d => ({
284
- name: d,
285
- checked: true // Include all in structure by default
286
- }))
287
- });
288
- selectedStructure = foldersResponse.selectedStructure;
289
- const copyFoldersResponse = await inquirer.prompt({
290
- type: 'checkbox',
291
- name: 'selectedFolders',
292
- message: 'Select folders to copy RECURSIVELY as boilerplate (with contents):',
293
- loop: false,
294
- theme: {
295
- icon: {
296
- checked: chalk.green('[x] '),
297
- unchecked: '[ ] ',
298
- }
299
- },
300
- choices: selectedStructure.map((d) => ({
301
- name: d,
302
- checked: ['APP', 'scripts', 'bin'].some(p => d === p)
303
- }))
304
- });
305
- selectedFolders = copyFoldersResponse.selectedFolders;
255
+ if (rootFiles.length > 0) {
256
+ const filesResponse = await inquirer.prompt({
257
+ type: 'checkbox',
258
+ name: 'selectedFiles',
259
+ message: 'Select root files to include as boilerplate:',
260
+ loop: false,
261
+ theme: {
262
+ icon: {
263
+ checked: chalk.green('[x] '),
264
+ unchecked: '[ ] ',
265
+ }
266
+ },
267
+ choices: rootFiles.map(f => ({
268
+ name: f,
269
+ checked: ['.makerc', 'readme.md', 'README.md', '.gitattributes', '.gitignore', 'Makefile', 'makefile', 'package.json'].some(p => f.toLowerCase() === p.toLowerCase())
270
+ }))
271
+ });
272
+ selectedFiles = filesResponse.selectedFiles;
273
+ }
274
+ else {
275
+ selectedFiles = [];
276
+ }
277
+ if (rootDirs.length > 0) {
278
+ const foldersResponse = await inquirer.prompt({
279
+ type: 'checkbox',
280
+ name: 'selectedStructure',
281
+ message: 'Select folders to include in the template structure (skeleton):',
282
+ loop: false,
283
+ theme: {
284
+ icon: {
285
+ checked: chalk.green('[x] '),
286
+ unchecked: '[ ] ',
287
+ }
288
+ },
289
+ choices: rootDirs.map(d => ({
290
+ name: d,
291
+ checked: true // Include all in structure by default
292
+ }))
293
+ });
294
+ selectedStructure = foldersResponse.selectedStructure;
295
+ }
296
+ else {
297
+ selectedStructure = [];
298
+ }
299
+ if (selectedStructure.length > 0) {
300
+ const copyFoldersResponse = await inquirer.prompt({
301
+ type: 'checkbox',
302
+ name: 'selectedFolders',
303
+ message: 'Select folders to copy RECURSIVELY as boilerplate (with contents):',
304
+ loop: false,
305
+ theme: {
306
+ icon: {
307
+ checked: chalk.green('[x] '),
308
+ unchecked: '[ ] ',
309
+ }
310
+ },
311
+ choices: selectedStructure.map((d) => ({
312
+ name: d,
313
+ checked: ['APP', 'scripts', 'bin'].some(p => d === p)
314
+ }))
315
+ });
316
+ selectedFolders = copyFoldersResponse.selectedFolders;
317
+ }
318
+ else {
319
+ selectedFolders = [];
320
+ }
306
321
  }
307
322
  const copy_files = [];
308
323
  if (fileTemplateConfig.copy_files && Array.isArray(fileTemplateConfig.copy_files)) {
package/doc/testing.md ADDED
@@ -0,0 +1,48 @@
1
+ # Testing pt-cli
2
+
3
+ This guide explains how to run, write, and manage the test suite for `pt-cli`.
4
+
5
+ ## Overview
6
+
7
+ The test suite uses Node.js's native test runner (`node:test`) and assertion library (`node:assert`). It is configured to run files ending with `.test.ts` in the `tests/` directory.
8
+
9
+ ## Running Tests
10
+
11
+ ### 1. Run the Entire Test Suite
12
+ To execute all tests:
13
+ ```bash
14
+ npm test
15
+ ```
16
+ This runs the underlying command:
17
+ ```bash
18
+ node --import tsx --test tests/**/*.test.ts
19
+ ```
20
+
21
+ ### 2. Run Individual Test Files
22
+ To run a specific test suite, use `tsx`:
23
+ ```bash
24
+ npx tsx --test tests/config.test.ts
25
+ npx tsx --test tests/learn.test.ts
26
+ npx tsx --test tests/substitute.test.ts
27
+ ```
28
+
29
+ ### 3. Run with Test Coverage
30
+ To generate a test coverage report directly in the terminal:
31
+ ```bash
32
+ node --experimental-test-coverage --import tsx --test tests/**/*.test.ts
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Writing Tests
38
+
39
+ When writing new tests, please adhere to these guidelines:
40
+
41
+ 1. **Use Native imports**: Import `test` from `node:test` and `assert` from `node:assert`. Do not use external testing frameworks (like Mocha, Jest, or Vitest).
42
+ 2. **ESM Imports**: Since this is an ESM (ECMAScript Modules) project, file imports within tests must use the `.js` extension (e.g., `import { learn } from '../src/commands/learnCommand.js';`).
43
+ 3. **Environment Isolation**: The configuration path relies on `process.env.HOME`. To prevent tests from polluting your user config directory, override the home directory before importing any CLI files:
44
+ ```typescript
45
+ const testHome = path.join(process.cwd(), '.test-home-custom');
46
+ process.env.HOME = testHome;
47
+ ```
48
+ 4. **Cleanup**: Always ensure temporary files, workspace directories, and test home directories are deleted after tests complete (e.g., in a `finally` block or `after` hook).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garyr/pt-cli",
3
- "version": "0.30.1",
3
+ "version": "0.32.0",
4
4
  "description": "Project Template CLI - Learn structures and initialize projects",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  "start": "tsx src/index.ts",
13
13
  "dev": "tsx src/index.ts",
14
14
  "test": "node --import tsx --test tests/**/*.test.ts",
15
+ "test:sequential": "node --import tsx --test tests/config.test.ts && node --import tsx --test tests/init.test.ts && node --import tsx --test tests/learn.test.ts && node --import tsx --test tests/substitute.test.ts && node --import tsx --test tests/config-utils.test.ts",
15
16
  "prepublishOnly": "npm run build",
16
17
  "build:linux": "bun build ./src/index.ts --compile --target=bun-linux-x64 --outfile ../BUILD/pt-cli/pt-linux",
17
18
  "build:macos": "bun build ./src/index.ts --compile --target=bun-darwin-x64 --outfile ../BUILD/pt-cli/pt-macos",
@@ -262,59 +262,71 @@ export async function learn(sourcePath: string, updateTemplate: string | null =
262
262
  selectedStructure = rootDirs; // Include all folders in structure
263
263
  selectedFolders = rootDirs.filter(d => ['APP', 'scripts', 'bin'].some(p => d === p)); // Only copy specific ones recursively
264
264
  } else {
265
- const filesResponse = await inquirer.prompt({
266
- type: 'checkbox',
267
- name: 'selectedFiles',
268
- message: 'Select root files to include as boilerplate:',
269
- loop: false,
270
- theme: {
271
- icon: {
272
- checked: chalk.green('[x] '),
273
- unchecked: '[ ] ',
274
- }
275
- },
276
- choices: rootFiles.map(f => ({
277
- name: f,
278
- checked: ['.makerc', 'readme.md', 'README.md', '.gitattributes', '.gitignore', 'Makefile', 'makefile', 'package.json'].some(p => f.toLowerCase() === p.toLowerCase())
279
- }))
280
- });
281
- selectedFiles = filesResponse.selectedFiles;
282
-
283
- const foldersResponse = await inquirer.prompt({
284
- type: 'checkbox',
285
- name: 'selectedStructure',
286
- message: 'Select folders to include in the template structure (skeleton):',
287
- loop: false,
288
- theme: {
289
- icon: {
290
- checked: chalk.green('[x] '),
291
- unchecked: '[ ] ',
292
- }
293
- },
294
- choices: rootDirs.map(d => ({
295
- name: d,
296
- checked: true // Include all in structure by default
297
- }))
298
- });
299
- selectedStructure = foldersResponse.selectedStructure;
300
-
301
- const copyFoldersResponse = await inquirer.prompt({
302
- type: 'checkbox',
303
- name: 'selectedFolders',
304
- message: 'Select folders to copy RECURSIVELY as boilerplate (with contents):',
305
- loop: false,
306
- theme: {
307
- icon: {
308
- checked: chalk.green('[x] '),
309
- unchecked: '[ ] ',
310
- }
311
- },
312
- choices: selectedStructure.map((d: string) => ({
313
- name: d,
314
- checked: ['APP', 'scripts', 'bin'].some(p => d === p)
315
- }))
316
- });
317
- selectedFolders = copyFoldersResponse.selectedFolders;
265
+ if (rootFiles.length > 0) {
266
+ const filesResponse = await inquirer.prompt({
267
+ type: 'checkbox',
268
+ name: 'selectedFiles',
269
+ message: 'Select root files to include as boilerplate:',
270
+ loop: false,
271
+ theme: {
272
+ icon: {
273
+ checked: chalk.green('[x] '),
274
+ unchecked: '[ ] ',
275
+ }
276
+ },
277
+ choices: rootFiles.map(f => ({
278
+ name: f,
279
+ checked: ['.makerc', 'readme.md', 'README.md', '.gitattributes', '.gitignore', 'Makefile', 'makefile', 'package.json'].some(p => f.toLowerCase() === p.toLowerCase())
280
+ }))
281
+ });
282
+ selectedFiles = filesResponse.selectedFiles;
283
+ } else {
284
+ selectedFiles = [];
285
+ }
286
+
287
+ if (rootDirs.length > 0) {
288
+ const foldersResponse = await inquirer.prompt({
289
+ type: 'checkbox',
290
+ name: 'selectedStructure',
291
+ message: 'Select folders to include in the template structure (skeleton):',
292
+ loop: false,
293
+ theme: {
294
+ icon: {
295
+ checked: chalk.green('[x] '),
296
+ unchecked: '[ ] ',
297
+ }
298
+ },
299
+ choices: rootDirs.map(d => ({
300
+ name: d,
301
+ checked: true // Include all in structure by default
302
+ }))
303
+ });
304
+ selectedStructure = foldersResponse.selectedStructure;
305
+ } else {
306
+ selectedStructure = [];
307
+ }
308
+
309
+ if (selectedStructure.length > 0) {
310
+ const copyFoldersResponse = await inquirer.prompt({
311
+ type: 'checkbox',
312
+ name: 'selectedFolders',
313
+ message: 'Select folders to copy RECURSIVELY as boilerplate (with contents):',
314
+ loop: false,
315
+ theme: {
316
+ icon: {
317
+ checked: chalk.green('[x] '),
318
+ unchecked: '[ ] ',
319
+ }
320
+ },
321
+ choices: selectedStructure.map((d: string) => ({
322
+ name: d,
323
+ checked: ['APP', 'scripts', 'bin'].some(p => d === p)
324
+ }))
325
+ });
326
+ selectedFolders = copyFoldersResponse.selectedFolders;
327
+ } else {
328
+ selectedFolders = [];
329
+ }
318
330
  }
319
331
 
320
332
  const copy_files: CopyFileEntry[] = [];