@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 +9 -0
- package/dist/commands/learnCommand.js +66 -51
- package/doc/testing.md +48 -0
- package/package.json +2 -1
- package/src/commands/learnCommand.ts +65 -53
- package/tests/config-utils.test.ts +473 -0
- package/tests/config.test.ts +36 -6
- package/tests/init.test.ts +428 -14
- package/tests/learn.test.ts +838 -0
- package/tests/substitute.test.ts +479 -0
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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.
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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[] = [];
|