@garyr/pt-cli 0.41.0 → 1.0.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/CHANGELOG.md +247 -0
- package/README.md +100 -1
- package/dist/config.js +36 -17
- package/doc/exclusions.md +15 -9
- package/doc/testing.md +41 -14
- package/doc/variable_substitution_example.md +11 -1
- package/package.json +1 -1
- package/src/config.ts +27 -17
- package/tests/config-utils.test.ts +15 -13
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [1.0.0] - 2026-07-31
|
|
11
|
+
|
|
12
|
+
### 🎉 First Stable Release
|
|
13
|
+
|
|
14
|
+
This release locks the public API for the 1.x series. See [README](README.md#10-release--api-stability) for stability guarantees.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Version bump**: 0.42.0 → 1.0.0
|
|
19
|
+
- **Documentation overhaul**: Added API stability promise, versioning policy, migration guide, and locked CLI/config/JSON schemas to README
|
|
20
|
+
- **Config schema v3.0 locked**: No breaking changes to `~/.pt/config.yaml` or `.pt-template.json` in 1.x
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- All documentation files updated to reflect current implementation (exclusions, testing, variable substitution)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## [0.42.0] - 2026-07-25
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Security enhancements and audit logging improvements
|
|
33
|
+
- Additional security test coverage
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Consistent key ordering in config.yaml output (name → prompt → default → required)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## [0.41.0] - 2026-07-20
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- Shared utility modules for template parsing
|
|
46
|
+
- Comprehensive test suites for config-utils, learn, and substitute commands
|
|
47
|
+
- Testing guide documentation
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Learn and update checkbox interaction bugs
|
|
52
|
+
- Config test isolation by making HOME_DIR dynamic
|
|
53
|
+
- Critical issue where config was being wiped during test execution
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## [0.40.0] - 2026-07-15
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **Remote template learning**: `pt learn https://github.com/user/repo` downloads and learns from Git repositories and tarball URLs
|
|
62
|
+
- Trusted source verification with `--allow-untrusted` override
|
|
63
|
+
- Automatic URL translation for GitHub/Gitea to tarball endpoints
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Improved template sharing and portability workflow
|
|
68
|
+
- Strip trailing `.git` from repository URLs
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## [0.39.0] - 2026-07-10
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- JSON template config files (`.pt-template.json`, `template.json`) take precedence over `.info.md` and shell scripts
|
|
77
|
+
- Full template metadata auto-detection from JSON config files
|
|
78
|
+
- JSON export/import for template sharing
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- JSON variables take precedence during update
|
|
83
|
+
- Update docs for remote learn
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## [0.38.0] - 2026-07-05
|
|
88
|
+
|
|
89
|
+
### Added
|
|
90
|
+
|
|
91
|
+
- **Additive diff mode for `pt update`** (default): Shows only new folders/files/variables for de-selection
|
|
92
|
+
- `--no-diff` flag to restore full/original update mode
|
|
93
|
+
- Preserves existing `substitute_variables` and `post_copy` settings during updates
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
|
|
97
|
+
- Default post-config tasks renamed from `global_post_config` → `default_post_config`
|
|
98
|
+
- Default tasks now baked into template at learn time (not auto-applied at init)
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## [0.37.0] - 2026-06-28
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- Security model: command validation, blocklists, dangerous command warnings with 5-second countdown
|
|
107
|
+
- Rate limiting (50 commands per init session)
|
|
108
|
+
- Execution timeout (30 seconds per command)
|
|
109
|
+
- Audit logging to `~/.pt/security-audit.log`
|
|
110
|
+
- Trusted sources for remote template downloads
|
|
111
|
+
- `pt security-response` command for GUI integration
|
|
112
|
+
|
|
113
|
+
### Fixed
|
|
114
|
+
|
|
115
|
+
- Security warning loop issues
|
|
116
|
+
- Variables not replaced during init; missing chmod for scripts
|
|
117
|
+
- Selecting "N" during learn with untrusted URL now exits cleanly
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## [0.36.0] - 2026-06-20
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
|
|
125
|
+
- **Nested variable expansion**: Variables can contain other `{{ variable }}` placeholders resolved iteratively (up to 10 passes)
|
|
126
|
+
- **Parent directory `.env` file scanning**: Automatic defaults from `.env` files up to 3 levels up
|
|
127
|
+
- Circular reference detection and graceful handling
|
|
128
|
+
- Whitespace preservation for unresolved placeholders
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## [0.35.0] - 2026-06-15
|
|
133
|
+
|
|
134
|
+
### Added
|
|
135
|
+
|
|
136
|
+
- Default post-config tasks (`default_post_config`) with `checked`, `type` filter fields
|
|
137
|
+
- `pt default-post-config` command for managing default tasks via JSON
|
|
138
|
+
- Template-specific post-config with `always_prompt`, `script`, `cross_platform` fields
|
|
139
|
+
- Auto-detection of post-config from `post_config.sh`/`post_config.bat`
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## [0.34.0] - 2026-06-10
|
|
144
|
+
|
|
145
|
+
### Added
|
|
146
|
+
|
|
147
|
+
- Global variables in config.yaml with `name`, `prompt`, `default`, `required` fields
|
|
148
|
+
- `pt variables` command for managing global variables (`--set`, `--delete`, `--json`)
|
|
149
|
+
- Variable suggestions during `pt learn`/`pt update`
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## [0.33.0] - 2026-06-05
|
|
154
|
+
|
|
155
|
+
### Added
|
|
156
|
+
|
|
157
|
+
- `pt add` command for importing templates from JSON string/file
|
|
158
|
+
- `pt config --json` for full config export
|
|
159
|
+
- `pt config <template> --json` for single template export
|
|
160
|
+
- Direct JSON scaffolding: `pt init --file template.json`
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## [0.32.0] - 2026-05-28
|
|
165
|
+
|
|
166
|
+
### Added
|
|
167
|
+
|
|
168
|
+
- Comprehensive test suite (114+ tests passing)
|
|
169
|
+
- Test isolation with dynamic HOME directory
|
|
170
|
+
- Sequential test run option (`npm run test:sequential`)
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## [0.31.0] - 2026-05-20
|
|
175
|
+
|
|
176
|
+
### Added
|
|
177
|
+
|
|
178
|
+
- Config version 3.0 migration (auto-migrates from v2.0)
|
|
179
|
+
- Renames `name` → `description`, removes `type` field
|
|
180
|
+
- Migrates `global_post_config` → `default_post_config`
|
|
181
|
+
- Normalizes variables from Record → TemplateVariable[]
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## [0.30.0] - 2026-05-10
|
|
186
|
+
|
|
187
|
+
### Added
|
|
188
|
+
|
|
189
|
+
- Automatic variable detection from `{{ var }}` placeholders during `pt learn`/`pt update`
|
|
190
|
+
- Auto-detection of executable scripts for `post_copy`
|
|
191
|
+
- Folder ignore patterns with glob support (`DAILIES/*`, `**/FOLDER/`, etc.)
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## [0.20.0 - 0.29.0] - 2026-04 to 2026-05
|
|
196
|
+
|
|
197
|
+
### Added (Incremental)
|
|
198
|
+
|
|
199
|
+
- Core `pt learn`, `pt init`, `pt update`, `pt config`, `pt remove` commands
|
|
200
|
+
- Template structure learning with folder hierarchy
|
|
201
|
+
- Variable substitution in `copy_files` with `substitute_variables`
|
|
202
|
+
- Post-copy executable handling
|
|
203
|
+
- Remote template infrastructure
|
|
204
|
+
- Cross-platform binary builds (Linux, macOS, Windows via Bun)
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## [0.10.0] - 2026-03-15
|
|
209
|
+
|
|
210
|
+
### Added
|
|
211
|
+
|
|
212
|
+
- Initial project structure
|
|
213
|
+
- TypeScript + Commander.js CLI framework
|
|
214
|
+
- Basic template learning and initialization
|
|
215
|
+
- YAML config storage at `~/.pt/config.yaml`
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Migration Guides
|
|
220
|
+
|
|
221
|
+
### 0.x → 1.0.0
|
|
222
|
+
|
|
223
|
+
**No manual action required.** Run any `pt` command and your config auto-migrates from v2.0 → v3.0 with a `.bak` backup created.
|
|
224
|
+
|
|
225
|
+
Key 0.x breaking changes already applied in earlier versions:
|
|
226
|
+
|
|
227
|
+
- Config v3.0 (0.31+): `name`→`description`, `global_post_config`→`default_post_config`
|
|
228
|
+
- Additive update mode (0.38+): `pt update` shows diffs by default
|
|
229
|
+
- Nested variables (0.36+): `.env` scanning and iterative expansion
|
|
230
|
+
- Security model (0.37+): Command validation, audit logging
|
|
231
|
+
|
|
232
|
+
### 0.30 → 0.31 (Config v3.0)
|
|
233
|
+
|
|
234
|
+
Auto-migration handles:
|
|
235
|
+
|
|
236
|
+
- `template.name` → `template.description`
|
|
237
|
+
- `template.type` removed
|
|
238
|
+
- `global_post_config` → `default_post_config`
|
|
239
|
+
- `variables` Record → `TemplateVariable[]`
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Links
|
|
244
|
+
|
|
245
|
+
- [GitHub Releases](https://github.com/garyritchie/pt-cli/releases)
|
|
246
|
+
- [npm Package](https://www.npmjs.com/package/@garyr/pt-cli)
|
|
247
|
+
- [Documentation](doc/)
|
package/README.md
CHANGED
|
@@ -67,7 +67,9 @@ Standardization is key to lowering the friction of starting new work. By ensurin
|
|
|
67
67
|
|
|
68
68
|
### 📦 Sharing is Caring
|
|
69
69
|
|
|
70
|
-
Templates can be exported as
|
|
70
|
+
Templates can be exported as JSON configuration files via `pt config <name> --json > .pt-template.json`. This exports the template's structure (folders, files to copy), variables, and post-config tasks — but *not* the actual file contents.
|
|
71
|
+
|
|
72
|
+
For fully self-contained distribution, commit the template's source directory alongside its JSON config.
|
|
71
73
|
|
|
72
74
|
### 🤖 Agentic and API Friendly
|
|
73
75
|
|
|
@@ -156,3 +158,100 @@ Because `pt-cli` is built around flexibility, the app purposefully avoids imposi
|
|
|
156
158
|
|
|
157
159
|
* **[Example Templates](https://github.com/search?q=topic%3Atemplate-project+org%3Agaryritchie&type=Repositories):** We have provided a few templates based on our own workflows to get you started. These include helpful Python scripts for streamlining common tasks, such as downloading the latest version of Blender or pruning unused folders from a project.
|
|
158
160
|
* **[Share Your Own](https://github.com/garyritchie/pt-cli/discussions):** Have you built a project structure that works perfectly for your niche? Join us in GitHub Discussions to share your templates and see how others are organizing their work.
|
|
161
|
+
|
|
162
|
+
## 1.0 Release & API Stability
|
|
163
|
+
|
|
164
|
+
**pt-cli v1.0.0** marks the first stable release with a locked public API. This means:
|
|
165
|
+
|
|
166
|
+
### 🔒 Stability Guarantee (1.x series)
|
|
167
|
+
|
|
168
|
+
- **No breaking changes** to CLI command signatures, flags, or config schema (`~/.pt/config.yaml`) within the 1.x series
|
|
169
|
+
- **No breaking changes** to the JSON template format (`.pt-template.json` / `template.json`)
|
|
170
|
+
- **No breaking changes** to the Node.js programmatic API (if used as a library)
|
|
171
|
+
|
|
172
|
+
### 📦 Versioning Policy
|
|
173
|
+
|
|
174
|
+
| Version | Meaning |
|
|
175
|
+
|---------|---------|
|
|
176
|
+
| **MAJOR** (1.0 → 2.0) | Breaking changes to CLI, config schema, or JSON template format |
|
|
177
|
+
| **MINOR** (1.0 → 1.1) | New features, commands, or config options (backward compatible) |
|
|
178
|
+
| **PATCH** (1.0 → 1.0.1) | Bug fixes, security patches, documentation updates |
|
|
179
|
+
|
|
180
|
+
### 📋 What's Locked in 1.0
|
|
181
|
+
|
|
182
|
+
**CLI Commands & Flags:**
|
|
183
|
+
```bash
|
|
184
|
+
pt learn [path] [--ignore] [--name] [--desc] [--yes] [--json] [--allow-untrusted] [--no-diff]
|
|
185
|
+
pt init [template] [dest] [--file] [--skip-post-config] [--dry-run] [--yes] [--vars]
|
|
186
|
+
pt update <template> [path] [--ignore] [--desc] [--yes] [--no-diff]
|
|
187
|
+
pt config [template] [--json]
|
|
188
|
+
pt add <name> [--file] [json]
|
|
189
|
+
pt remove <template> [--yes] # alias: pt rm
|
|
190
|
+
pt variables [--set] [--delete] [--json]
|
|
191
|
+
pt default-post-config [--set --json]
|
|
192
|
+
pt ignore [patterns] [--set]
|
|
193
|
+
pt security-response <response>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Config Schema (`~/.pt/config.yaml` v3.0):**
|
|
197
|
+
```yaml
|
|
198
|
+
version: "3.0"
|
|
199
|
+
templates: { <name>: TemplateConfig }
|
|
200
|
+
default_post_config: PostConfigTask[]
|
|
201
|
+
ignore: string[]
|
|
202
|
+
variables: TemplateVariable[]
|
|
203
|
+
security: SecurityPolicy # optional
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**TemplateConfig (per-template):**
|
|
207
|
+
```yaml
|
|
208
|
+
description: string
|
|
209
|
+
templateRoot?: string
|
|
210
|
+
variables?: TemplateVariable[]
|
|
211
|
+
folders: FolderNode[]
|
|
212
|
+
exclude?: string[]
|
|
213
|
+
copy_files?: CopyFileEntry[]
|
|
214
|
+
post_copy?: PostCopyFile[]
|
|
215
|
+
post_config?: PostConfigTask[]
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**JSON Template Format (`.pt-template.json`):**
|
|
219
|
+
```json
|
|
220
|
+
{
|
|
221
|
+
"name": "template-name",
|
|
222
|
+
"description": "Template description",
|
|
223
|
+
"variables": [{ "name": "", "prompt": "", "default": "", "required": false }],
|
|
224
|
+
"folders": [{ "name": "", "info": "", "children": [] }],
|
|
225
|
+
"copy_files": [{ "src": "", "dest": "", "substitute_variables": false, "chmod": "" }],
|
|
226
|
+
"post_config": [{ "command": "", "description": "", "type": "", "always_prompt": false, "script": "", "cross_platform": false, "checked": true }],
|
|
227
|
+
"post_copy": [{ "src": "", "dest": "" }]
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 📖 Migration from 0.x to 1.0
|
|
232
|
+
|
|
233
|
+
If you're upgrading from a 0.x version:
|
|
234
|
+
|
|
235
|
+
1. **Config auto-migrates** — `pt` automatically upgrades your `~/.pt/config.yaml` from v2.0 → v3.0 on first run (renames `name` → `description`, removes `type`, migrates `global_post_config` → `default_post_config`, normalizes variables)
|
|
236
|
+
2. **No action needed** — Just run any `pt` command; migration happens silently with a backup (`.bak`) created
|
|
237
|
+
3. **CLI flags unchanged** — All 0.x flags work identically in 1.0
|
|
238
|
+
|
|
239
|
+
**Breaking changes from 0.x already landed in 0.30+:**
|
|
240
|
+
- Config version 3.0 (v0.30+)
|
|
241
|
+
- `default_post_config` replaces `global_post_config` (v0.30+)
|
|
242
|
+
- Additive diff mode for `pt update` (v0.38+)
|
|
243
|
+
- Nested variable expansion (v0.36+)
|
|
244
|
+
- `.env` file scanning for defaults (v0.36+)
|
|
245
|
+
|
|
246
|
+
If you skipped intermediate 0.x versions, the auto-migration handles everything.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Documentation
|
|
251
|
+
|
|
252
|
+
* **[Detailed Usage](doc/usage.md)** - Learn, Initialize, Update, and Remove commands.
|
|
253
|
+
* **[Configuration Guide](doc/configuration.md)** - Template variables, post-config tasks, file copying, and more.
|
|
254
|
+
* **[Security Guide](doc/security.md)** - Command validation, trusted sources, audit logging.
|
|
255
|
+
* **[Testing Guide](doc/testing.md)** - Test suite structure and running tests.
|
|
256
|
+
* **[Exclusions Reference](doc/exclusions.md)** - Default ignored files and custom patterns.
|
|
257
|
+
* **[Variable Substitution Example](doc/variable_substitution_example.md)** - Practical examples.
|
package/dist/config.js
CHANGED
|
@@ -109,6 +109,20 @@ export function normalizeVariable(v) {
|
|
|
109
109
|
result.required = v.required;
|
|
110
110
|
return result;
|
|
111
111
|
}
|
|
112
|
+
export function normalizeFolders(folders) {
|
|
113
|
+
return folders.map(folder => {
|
|
114
|
+
const normalized = { name: folder.name, info: folder.info || '' };
|
|
115
|
+
if (folder.children !== undefined && folder.children.length > 0) {
|
|
116
|
+
normalized.children = normalizeFolders(folder.children);
|
|
117
|
+
}
|
|
118
|
+
else if (folder.children !== undefined) {
|
|
119
|
+
normalized.children = [];
|
|
120
|
+
}
|
|
121
|
+
if (folder.is_file !== undefined)
|
|
122
|
+
normalized.is_file = folder.is_file;
|
|
123
|
+
return normalized;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
112
126
|
export function saveConfig(config) {
|
|
113
127
|
ensureConfigDir();
|
|
114
128
|
// Normalize variable key ordering (forces 'name' to be first in serialized YAML)
|
|
@@ -121,6 +135,28 @@ export function saveConfig(config) {
|
|
|
121
135
|
if (template.variables && Array.isArray(template.variables)) {
|
|
122
136
|
template.variables = template.variables.map(normalizeVariable);
|
|
123
137
|
}
|
|
138
|
+
if (template.folders && Array.isArray(template.folders)) {
|
|
139
|
+
template.folders = normalizeFolders(template.folders);
|
|
140
|
+
}
|
|
141
|
+
// Create a new object with keys in the desired order for consistent YAML output
|
|
142
|
+
const orderedTemplate = {};
|
|
143
|
+
if (template.description !== undefined)
|
|
144
|
+
orderedTemplate.description = template.description;
|
|
145
|
+
if (template.templateRoot !== undefined)
|
|
146
|
+
orderedTemplate.templateRoot = template.templateRoot;
|
|
147
|
+
if (template.variables !== undefined)
|
|
148
|
+
orderedTemplate.variables = template.variables;
|
|
149
|
+
if (template.folders !== undefined)
|
|
150
|
+
orderedTemplate.folders = template.folders;
|
|
151
|
+
if (template.exclude !== undefined)
|
|
152
|
+
orderedTemplate.exclude = template.exclude;
|
|
153
|
+
if (template.copy_files !== undefined)
|
|
154
|
+
orderedTemplate.copy_files = template.copy_files;
|
|
155
|
+
if (template.post_copy !== undefined)
|
|
156
|
+
orderedTemplate.post_copy = template.post_copy;
|
|
157
|
+
if (template.post_config !== undefined)
|
|
158
|
+
orderedTemplate.post_config = template.post_config;
|
|
159
|
+
config.templates[key] = orderedTemplate;
|
|
124
160
|
}
|
|
125
161
|
}
|
|
126
162
|
const content = YAML.stringify(config);
|
|
@@ -183,17 +219,12 @@ export function getSecurityPolicy(config) {
|
|
|
183
219
|
// Default exclusions for template scanning
|
|
184
220
|
export const DEFAULT_EXCLUDES = [
|
|
185
221
|
'.git',
|
|
186
|
-
'.gitea',
|
|
187
|
-
'.vscode',
|
|
188
222
|
'node_modules',
|
|
189
223
|
'dist',
|
|
190
224
|
'build',
|
|
191
225
|
'bin',
|
|
192
226
|
'.DS_Store',
|
|
193
227
|
'Thumbs.db',
|
|
194
|
-
'.stignore',
|
|
195
|
-
'.stfolder',
|
|
196
|
-
'.stversions',
|
|
197
228
|
];
|
|
198
229
|
// Check if a path should be excluded
|
|
199
230
|
export function shouldExclude(dirPath, fullPath, excludes) {
|
|
@@ -291,22 +322,10 @@ export function shouldExcludeFile(fileName) {
|
|
|
291
322
|
'*.swo',
|
|
292
323
|
'*~',
|
|
293
324
|
'.bak',
|
|
294
|
-
'*.md',
|
|
295
|
-
'*.txt',
|
|
296
|
-
'*.json',
|
|
297
|
-
'*.yaml',
|
|
298
|
-
'*.yml',
|
|
299
|
-
'*.ini',
|
|
300
|
-
'*.conf',
|
|
301
|
-
'*.config',
|
|
302
|
-
'.gitconfig',
|
|
303
|
-
'.makerc',
|
|
304
325
|
'Gemfile.lock',
|
|
305
|
-
'package.json',
|
|
306
326
|
'package-lock.json',
|
|
307
327
|
'yarn.lock',
|
|
308
328
|
'pnpm-lock.yaml',
|
|
309
|
-
'composer.json',
|
|
310
329
|
'composer.lock',
|
|
311
330
|
];
|
|
312
331
|
for (const pattern of excludePatterns) {
|
package/doc/exclusions.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# Exclusions
|
|
2
2
|
|
|
3
|
-
The following are excluded by default when learning templates:
|
|
3
|
+
The following are excluded by default when learning templates (from `src/config.ts`):
|
|
4
4
|
|
|
5
|
-
- `.git
|
|
6
|
-
-
|
|
7
|
-
- `.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
- `.git`
|
|
6
|
+
- `node_modules`, `dist`, `build`, `bin`
|
|
7
|
+
- `.DS_Store`, `Thumbs.db`
|
|
8
|
+
|
|
9
|
+
Additionally, these **file patterns** are excluded from `copy_files`/`post_copy` scanning:
|
|
10
|
+
|
|
11
|
+
- Compiled/binary: `*.pyc`, `*.pyo`, `*.pyd`, `.Python`, `*.egg-info`, `*.egg`, `*.whl`, `*.so`, `*.dll`, `*.dylib`, `*.exe`, `*.o`, `*.a`, `*.lib`, `*.class`, `*.jar`, `*.war`, `*.ear`
|
|
12
|
+
- Logs/temp: `*.log`, `*.tmp`, `*.swp`, `*.swo`, `*~`, `.bak`
|
|
13
|
+
- Lockfiles: `Gemfile.lock`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `composer.lock`
|
|
14
|
+
|
|
15
|
+
> **Note:** Configuration files (`*.json`, `*.yaml`, `*.yml`, `*.ini`, `*.conf`, `*.config`, `.gitconfig`, `.makerc`, `package.json`, `composer.json`), documentation (`*.md`, `*.txt`), and editor folders (`.vscode`, `.gitea`, `.stignore`, etc.) are **NOT** excluded by default. Use the `ignore` config or `--ignore` flag to exclude them if needed.
|
|
11
16
|
|
|
12
17
|
## Ignore Patterns
|
|
13
18
|
|
|
@@ -26,10 +31,11 @@ Patterns use wildcards for clarity:
|
|
|
26
31
|
| ------------ | ---------------------------------------------------------------------------- |
|
|
27
32
|
| `DAILIES/*` | Ignore all contents of DAILIES (DAILIES itself is kept as a template folder) |
|
|
28
33
|
| `DAILIES/**` | Same as `DAILIES/*` (deep match) |
|
|
29
|
-
|
|
|
34
|
+
| `**/FOLDER/` | Ignore any folder named FOLDER at any depth |
|
|
35
|
+
| `FOLDER` | Ignore this specific folder (at root or by name) |
|
|
30
36
|
|
|
31
37
|
The CLI flag `--ignore=DAILIES/*,PARKING_LOT/*` merges with the config patterns (one-shot, not persistent).
|
|
32
38
|
|
|
33
39
|
## Custom exclusions
|
|
34
40
|
|
|
35
|
-
Additional patterns can be added to `DEFAULT_EXCLUDES` in `src/config.ts
|
|
41
|
+
Additional patterns can be added to `DEFAULT_EXCLUDES` in `src/config.ts` or via the `--ignore` flag / config `ignore` array.
|
package/doc/testing.md
CHANGED
|
@@ -6,24 +6,28 @@ This guide explains how to run, write, and manage the test suite for `pt-cli`.
|
|
|
6
6
|
|
|
7
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
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
## Test Suite Summary (as of v1.0.0)
|
|
10
|
+
|
|
11
|
+
| Test File | Focus | Test Count |
|
|
12
|
+
|-----------|-------|------------|
|
|
13
|
+
| `config.test.ts` | Config loading, saving, migration, atomic writes | ~30 |
|
|
14
|
+
| `init.test.ts` | Project initialization, variables, dry-run, copy_files, post_copy | ~20 |
|
|
15
|
+
| `learn.test.ts` | Template learning, updates, variable detection, JSON output | ~25 |
|
|
16
|
+
| `update.test.ts` | Additive diff mode, full mode, post-config, JSON config | ~15 |
|
|
17
|
+
| `substitute.test.ts` | Variable substitution, nested expansion, edge cases | ~15 |
|
|
18
|
+
| `safety.test.ts` | Security validation, command blocking, audit logging | ~15 |
|
|
19
|
+
| `remote.test.ts` | Remote template download, trusted sources | ~10 |
|
|
20
|
+
| `config-utils.test.ts` | Ignore patterns, path sanitization, exclusions | ~10 |
|
|
21
|
+
| `nested-variable-expansion.test.ts` | Nested variable resolution, circular refs | ~5 |
|
|
22
|
+
| `env-scanning.test.ts` | Parent directory `.env` file scanning | ~5 |
|
|
23
|
+
| `final-rst-verification.test.ts` | End-to-end integration | ~5 |
|
|
24
|
+
|
|
25
|
+
**Total: ~146 tests, all passing**
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
## Running Tests
|
|
22
28
|
|
|
23
29
|
### 1. Run the Entire Test Suite
|
|
24
30
|
|
|
25
|
-
To execute all tests:
|
|
26
|
-
|
|
27
31
|
```bash
|
|
28
32
|
npm test
|
|
29
33
|
```
|
|
@@ -40,6 +44,7 @@ To run a specific test suite, use `tsx`:
|
|
|
40
44
|
|
|
41
45
|
```bash
|
|
42
46
|
npx tsx --test tests/config.test.ts
|
|
47
|
+
npx tsx --test tests/init.test.ts
|
|
43
48
|
npx tsx --test tests/learn.test.ts
|
|
44
49
|
npx tsx --test tests/substitute.test.ts
|
|
45
50
|
```
|
|
@@ -52,6 +57,28 @@ To generate a test coverage report directly in the terminal:
|
|
|
52
57
|
node --experimental-test-coverage --import tsx --test tests/**/*.test.ts
|
|
53
58
|
```
|
|
54
59
|
|
|
60
|
+
### 4. Sequential Test Run (for debugging)
|
|
61
|
+
|
|
62
|
+
Some tests modify shared state (HOME directory). To run sequentially:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run test:sequential
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Security Testing
|
|
71
|
+
|
|
72
|
+
Security features can be tested by:
|
|
73
|
+
|
|
74
|
+
1. **Testing command blocks**: Try running templates with dangerous commands like `sudo rm -rf` or `dd`
|
|
75
|
+
2. **Testing remote downloads**: Use untrusted URLs to verify source verification
|
|
76
|
+
3. **Testing rate limiting**: Execute more than 50 commands in a single init session
|
|
77
|
+
4. **Testing timeouts**: Run commands that hang to verify timeout behavior
|
|
78
|
+
5. **Reviewing audit logs**: Check `~/.pt/security-audit.log` for security events
|
|
79
|
+
|
|
80
|
+
For more details, see the [Security Guide](security.md).
|
|
81
|
+
|
|
55
82
|
---
|
|
56
83
|
|
|
57
84
|
## Writing Tests
|
|
@@ -27,7 +27,7 @@ templates:
|
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
> [!TIP]
|
|
30
|
-
> **
|
|
30
|
+
> **Automatic detection (v0.16+):** You no longer need to manually define the `variables` section. During `pt learn` or `pt update`, the tool will automatically detect `{{ variable_name }}` placeholders in your files and add them to the configuration for you.
|
|
31
31
|
|
|
32
32
|
## 2. Create Template Files
|
|
33
33
|
|
|
@@ -140,3 +140,13 @@ project=wiki
|
|
|
140
140
|
Result: `template_path` becomes `docs/wiki`
|
|
141
141
|
|
|
142
142
|
**Important:** Missing nested variables remain as `{{ variable }}` placeholders (with preserved whitespace) to help identify configuration issues.
|
|
143
|
+
|
|
144
|
+
## 5. CLI Variable Overrides
|
|
145
|
+
|
|
146
|
+
Pass variables non-interactively with `--vars`:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pt init node_web_app my-project --yes --vars project_name=my-service,author_name="Jane Doe"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Or use a `.env` file in the destination directory for persistent defaults.
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -174,6 +174,19 @@ export function normalizeVariable(v: TemplateVariable): TemplateVariable {
|
|
|
174
174
|
return result;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
export function normalizeFolders(folders: FolderNode[]): FolderNode[] {
|
|
178
|
+
return folders.map(folder => {
|
|
179
|
+
const normalized: FolderNode = { name: folder.name, info: folder.info || '' };
|
|
180
|
+
if (folder.children !== undefined && folder.children.length > 0) {
|
|
181
|
+
normalized.children = normalizeFolders(folder.children);
|
|
182
|
+
} else if (folder.children !== undefined) {
|
|
183
|
+
normalized.children = [];
|
|
184
|
+
}
|
|
185
|
+
if (folder.is_file !== undefined) normalized.is_file = folder.is_file;
|
|
186
|
+
return normalized;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
177
190
|
export function saveConfig(config: PtConfig) {
|
|
178
191
|
ensureConfigDir();
|
|
179
192
|
|
|
@@ -187,6 +200,20 @@ export function saveConfig(config: PtConfig) {
|
|
|
187
200
|
if (template.variables && Array.isArray(template.variables)) {
|
|
188
201
|
template.variables = template.variables.map(normalizeVariable);
|
|
189
202
|
}
|
|
203
|
+
if (template.folders && Array.isArray(template.folders)) {
|
|
204
|
+
template.folders = normalizeFolders(template.folders);
|
|
205
|
+
}
|
|
206
|
+
// Create a new object with keys in the desired order for consistent YAML output
|
|
207
|
+
const orderedTemplate: Partial<TemplateConfig> = {};
|
|
208
|
+
if (template.description !== undefined) orderedTemplate.description = template.description;
|
|
209
|
+
if (template.templateRoot !== undefined) orderedTemplate.templateRoot = template.templateRoot;
|
|
210
|
+
if (template.variables !== undefined) orderedTemplate.variables = template.variables;
|
|
211
|
+
if (template.folders !== undefined) orderedTemplate.folders = template.folders;
|
|
212
|
+
if (template.exclude !== undefined) orderedTemplate.exclude = template.exclude;
|
|
213
|
+
if (template.copy_files !== undefined) orderedTemplate.copy_files = template.copy_files;
|
|
214
|
+
if (template.post_copy !== undefined) orderedTemplate.post_copy = template.post_copy;
|
|
215
|
+
if (template.post_config !== undefined) orderedTemplate.post_config = template.post_config;
|
|
216
|
+
config.templates[key] = orderedTemplate as TemplateConfig;
|
|
190
217
|
}
|
|
191
218
|
}
|
|
192
219
|
|
|
@@ -254,17 +281,12 @@ export function getSecurityPolicy(config: PtConfig): SecurityPolicy {
|
|
|
254
281
|
// Default exclusions for template scanning
|
|
255
282
|
export const DEFAULT_EXCLUDES = [
|
|
256
283
|
'.git',
|
|
257
|
-
'.gitea',
|
|
258
|
-
'.vscode',
|
|
259
284
|
'node_modules',
|
|
260
285
|
'dist',
|
|
261
286
|
'build',
|
|
262
287
|
'bin',
|
|
263
288
|
'.DS_Store',
|
|
264
289
|
'Thumbs.db',
|
|
265
|
-
'.stignore',
|
|
266
|
-
'.stfolder',
|
|
267
|
-
'.stversions',
|
|
268
290
|
];
|
|
269
291
|
|
|
270
292
|
// Check if a path should be excluded
|
|
@@ -370,22 +392,10 @@ export function shouldExcludeFile(fileName: string): boolean {
|
|
|
370
392
|
'*.swo',
|
|
371
393
|
'*~',
|
|
372
394
|
'.bak',
|
|
373
|
-
'*.md',
|
|
374
|
-
'*.txt',
|
|
375
|
-
'*.json',
|
|
376
|
-
'*.yaml',
|
|
377
|
-
'*.yml',
|
|
378
|
-
'*.ini',
|
|
379
|
-
'*.conf',
|
|
380
|
-
'*.config',
|
|
381
|
-
'.gitconfig',
|
|
382
|
-
'.makerc',
|
|
383
395
|
'Gemfile.lock',
|
|
384
|
-
'package.json',
|
|
385
396
|
'package-lock.json',
|
|
386
397
|
'yarn.lock',
|
|
387
398
|
'pnpm-lock.yaml',
|
|
388
|
-
'composer.json',
|
|
389
399
|
'composer.lock',
|
|
390
400
|
];
|
|
391
401
|
|
|
@@ -148,8 +148,10 @@ test('DEFAULT_EXCLUDES contains expected patterns', () => {
|
|
|
148
148
|
assert.ok(DEFAULT_EXCLUDES.includes('dist'), 'Should include dist');
|
|
149
149
|
assert.ok(DEFAULT_EXCLUDES.includes('build'), 'Should include build');
|
|
150
150
|
assert.ok(DEFAULT_EXCLUDES.includes('.DS_Store'), 'Should include .DS_Store');
|
|
151
|
-
assert.ok(DEFAULT_EXCLUDES.includes('.vscode'), 'Should include .vscode');
|
|
152
151
|
assert.ok(DEFAULT_EXCLUDES.includes('Thumbs.db'), 'Should include Thumbs.db');
|
|
152
|
+
assert.ok(!DEFAULT_EXCLUDES.includes('.vscode'), 'Should NOT include .vscode');
|
|
153
|
+
assert.ok(!DEFAULT_EXCLUDES.includes('.gitea'), 'Should NOT include .gitea');
|
|
154
|
+
assert.ok(!DEFAULT_EXCLUDES.includes('.stignore'), 'Should NOT include .stignore');
|
|
153
155
|
});
|
|
154
156
|
|
|
155
157
|
// ─── shouldExclude ───────────────────────────────────────────────────────────
|
|
@@ -357,14 +359,6 @@ test('shouldExcludeFile excludes wildcard extension patterns', () => {
|
|
|
357
359
|
'file.swp',
|
|
358
360
|
'file.swo',
|
|
359
361
|
'backup~',
|
|
360
|
-
'readme.md',
|
|
361
|
-
'notes.txt',
|
|
362
|
-
'data.json',
|
|
363
|
-
'config.yaml',
|
|
364
|
-
'settings.yml',
|
|
365
|
-
'setup.ini',
|
|
366
|
-
'app.conf',
|
|
367
|
-
'lint.config',
|
|
368
362
|
];
|
|
369
363
|
|
|
370
364
|
for (const file of wildcardExcludes) {
|
|
@@ -379,14 +373,10 @@ test('shouldExcludeFile excludes exact match files', () => {
|
|
|
379
373
|
const exactExcludes = [
|
|
380
374
|
'.Python',
|
|
381
375
|
'.bak',
|
|
382
|
-
'.gitconfig',
|
|
383
|
-
'.makerc',
|
|
384
376
|
'Gemfile.lock',
|
|
385
|
-
'package.json',
|
|
386
377
|
'package-lock.json',
|
|
387
378
|
'yarn.lock',
|
|
388
379
|
'pnpm-lock.yaml',
|
|
389
|
-
'composer.json',
|
|
390
380
|
'composer.lock',
|
|
391
381
|
];
|
|
392
382
|
|
|
@@ -415,6 +405,18 @@ test('shouldExcludeFile does NOT exclude normal source files', () => {
|
|
|
415
405
|
'Component.jsx',
|
|
416
406
|
'handler.py',
|
|
417
407
|
'server.rb',
|
|
408
|
+
'readme.md',
|
|
409
|
+
'notes.txt',
|
|
410
|
+
'data.json',
|
|
411
|
+
'config.yaml',
|
|
412
|
+
'settings.yml',
|
|
413
|
+
'setup.ini',
|
|
414
|
+
'app.conf',
|
|
415
|
+
'lint.config',
|
|
416
|
+
'.gitconfig',
|
|
417
|
+
'.makerc',
|
|
418
|
+
'package.json',
|
|
419
|
+
'composer.json',
|
|
418
420
|
];
|
|
419
421
|
|
|
420
422
|
for (const file of normalFiles) {
|