@garyr/pt-cli 0.38.0 → 0.40.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/dist/commands/addCommand.js +98 -8
- package/dist/commands/configCommand.js +11 -4
- package/dist/commands/learnCommand.js +8 -4
- package/dist/commands/updateCommand.js +784 -0
- package/dist/config.js +12 -2
- package/dist/index.js +9 -4
- package/doc/configuration.md +18 -8
- package/doc/usage.md +9 -0
- package/package.json +1 -1
- package/skills/agency-pt-operator/SKILL.md +6 -2
- package/src/commands/addCommand.ts +109 -8
- package/src/commands/configCommand.ts +11 -4
- package/src/commands/learnCommand.ts +12 -7
- package/src/commands/updateCommand.ts +835 -0
- package/src/config.ts +14 -2
- package/src/index.ts +9 -4
package/dist/config.js
CHANGED
|
@@ -84,12 +84,22 @@ export function loadConfig() {
|
|
|
84
84
|
catch (err) {
|
|
85
85
|
const error = err;
|
|
86
86
|
console.error(chalk.red(`\nError loading config: ${error.message}`));
|
|
87
|
-
// If we have a backup, maybe suggest using it
|
|
88
87
|
const backupPath = getConfigPath() + '.bak';
|
|
89
88
|
if (fs.existsSync(backupPath)) {
|
|
90
89
|
console.error(chalk.yellow(`A backup exists at ${backupPath}. You may want to restore it.`));
|
|
91
90
|
}
|
|
92
|
-
|
|
91
|
+
// Allow the event loop to flush console streams out to Godot before dying
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}, 5);
|
|
95
|
+
// 👇 Add this return statement to satisfy the TypeScript compiler
|
|
96
|
+
// The application will terminate before this empty config can be used.
|
|
97
|
+
return {
|
|
98
|
+
version: '3.0',
|
|
99
|
+
templates: {},
|
|
100
|
+
default_post_config: [],
|
|
101
|
+
variables: []
|
|
102
|
+
};
|
|
93
103
|
}
|
|
94
104
|
}
|
|
95
105
|
export function normalizeVariable(v) {
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Command } from 'commander';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
// Command imports
|
|
5
5
|
import { learn } from './commands/learnCommand.js';
|
|
6
|
+
import { update } from './commands/updateCommand.js';
|
|
6
7
|
import { init } from './commands/initCommand.js';
|
|
7
8
|
import { configCommand } from './commands/configCommand.js';
|
|
8
9
|
import { ignoreCommand } from './commands/ignoreCommand.js';
|
|
@@ -32,15 +33,18 @@ program
|
|
|
32
33
|
}
|
|
33
34
|
catch (err) {
|
|
34
35
|
if (options.json) {
|
|
35
|
-
|
|
36
|
+
// Fix: Ensure the error JSON payload isn't truncated before exiting
|
|
37
|
+
process.stdout.write(JSON.stringify({
|
|
36
38
|
type: 'error',
|
|
37
39
|
message: err.message || String(err)
|
|
38
|
-
}))
|
|
40
|
+
}) + '\n', () => {
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
39
43
|
}
|
|
40
44
|
else {
|
|
41
45
|
console.error(chalk.red(`Error: ${err.message || err}`));
|
|
46
|
+
process.exit(1);
|
|
42
47
|
}
|
|
43
|
-
process.exit(1);
|
|
44
48
|
}
|
|
45
49
|
});
|
|
46
50
|
program
|
|
@@ -49,9 +53,10 @@ program
|
|
|
49
53
|
.option('--ignore <patterns>', 'Folder patterns to ignore (comma-separated)')
|
|
50
54
|
.option('-y, --yes', 'Automatically confirm prompts')
|
|
51
55
|
.option('--desc <description>', 'Template description (skip prompt)')
|
|
56
|
+
.option('--no-diff', 'Disable additive mode, show full list')
|
|
52
57
|
.action(async (templateName, sourcePath, options) => {
|
|
53
58
|
try {
|
|
54
|
-
await
|
|
59
|
+
await update(sourcePath || '.', templateName, options);
|
|
55
60
|
}
|
|
56
61
|
catch (err) {
|
|
57
62
|
console.error(chalk.red(`Error: ${err.message || err}`));
|
package/doc/configuration.md
CHANGED
|
@@ -39,17 +39,20 @@ project=MyProject
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
During initialization, the system will:
|
|
42
|
+
|
|
42
43
|
1. Load `prefix='rst_{{ project }}'` from `.env`
|
|
43
44
|
2. Detect that `prefix` contains a `{{ project }}` placeholder
|
|
44
45
|
3. Resolve `{{ project }}` to `MyProject`
|
|
45
46
|
4. Set `prefix` to `rst_MyProject`
|
|
46
47
|
|
|
47
48
|
This is particularly useful for:
|
|
49
|
+
|
|
48
50
|
- **Project naming conventions**: `prefix='app_{{ env }}'` + `env=prod` → `app_prod`
|
|
49
51
|
- **Path templates**: `template_path='docs/{{ project }}'` + `project=wiki` → `docs/wiki`
|
|
50
52
|
- **Multi-level configurations**: Combine multiple `.env` files with nested references
|
|
51
53
|
|
|
52
54
|
**How it works:**
|
|
55
|
+
|
|
53
56
|
- Variables are expanded iteratively (up to 10 passes) to prevent infinite loops
|
|
54
57
|
- Circular references are detected and stopped gracefully
|
|
55
58
|
- Missing nested variables remain as `{{ variable }}` placeholders
|
|
@@ -64,6 +67,7 @@ This is particularly useful for:
|
|
|
64
67
|
- **Team collaboration**: Share common variable values across team projects
|
|
65
68
|
|
|
66
69
|
**Example:**
|
|
70
|
+
|
|
67
71
|
```bash
|
|
68
72
|
# Project structure:
|
|
69
73
|
my-project/
|
|
@@ -75,6 +79,7 @@ my-project/
|
|
|
75
79
|
When you run `pt init my-template sub-project`, the `prefix` variable will be pre-filled with `rst_` from the parent `.env` file.
|
|
76
80
|
|
|
77
81
|
**Behavior:**
|
|
82
|
+
|
|
78
83
|
- Scans from the current directory up to 3 parent levels
|
|
79
84
|
- Uses values from `.env` as defaults (still prompts if not in `.env`)
|
|
80
85
|
- `--vars` CLI option overrides `.env` values
|
|
@@ -109,6 +114,7 @@ If the directory contains a `.pt-template.json` or `template.json` file with a `
|
|
|
109
114
|
3. Alternatively, initialize a temporary project from your learned template (`pt init`), refine it manually, and then use `pt update` from that directory to "re-learn" the refined state.
|
|
110
115
|
|
|
111
116
|
**Security Note:** All post-config commands are subject to security validation:
|
|
117
|
+
|
|
112
118
|
- Dangerous commands (e.g., `curl`, `python`, `chmod`) trigger warnings with 5-second cancellation
|
|
113
119
|
- Absolute blocks (e.g., `sudo`, `rm -rf`, `dd`) are never allowed
|
|
114
120
|
- Rate limiting prevents runaway execution (50 commands per run)
|
|
@@ -177,7 +183,7 @@ Each task supports:
|
|
|
177
183
|
|
|
178
184
|
## Default Post-Config
|
|
179
185
|
|
|
180
|
-
Default post-config tasks are defined at the top level of `~/.pt/config.yaml` under `default_post_config`. They serve as suggestions when creating or updating templates via `pt learn`.
|
|
186
|
+
Default post-config tasks are defined at the top level of `~/.pt/config.yaml` under `default_post_config`. They serve as suggestions when creating or updating templates via `pt learn`.
|
|
181
187
|
|
|
182
188
|
Unlike previous versions, default tasks are **not** automatically applied during `pt init`. Instead, you select which ones to include when learning a template, and those selections are baked into the template's `post_config` list. This eliminates the need to repeat boilerplate setup (e.g. `git init`) across templates while keeping each template fully self-contained.
|
|
183
189
|
|
|
@@ -189,21 +195,21 @@ default_post_config:
|
|
|
189
195
|
description: "Initialize git repository"
|
|
190
196
|
- command: "git add -A && git commit -m 'Initial commit'"
|
|
191
197
|
description: "Initial git commit"
|
|
192
|
-
checked: false
|
|
198
|
+
checked: false # default on, but user must manually check
|
|
193
199
|
- command: "git lfs install"
|
|
194
200
|
description: "Install git-lfs hooks"
|
|
195
|
-
type: "godot"
|
|
201
|
+
type: "godot" # only applies to godot projects
|
|
196
202
|
```
|
|
197
203
|
|
|
198
204
|
### Fields
|
|
199
205
|
|
|
200
206
|
Each default task supports the same fields as template post-config:
|
|
201
207
|
|
|
202
|
-
| Field | Description
|
|
203
|
-
| ------------- |
|
|
204
|
-
| `command` | Shell command to run
|
|
205
|
-
| `description` | Shown to user during interactive selection
|
|
206
|
-
| `checked` | Default checkbox state (`true` by default); set `false` to require manual opt-in
|
|
208
|
+
| Field | Description |
|
|
209
|
+
| ------------- | -------------------------------------------------------------------------------------------------- |
|
|
210
|
+
| `command` | Shell command to run |
|
|
211
|
+
| `description` | Shown to user during interactive selection |
|
|
212
|
+
| `checked` | Default checkbox state (`true` by default); set `false` to require manual opt-in |
|
|
207
213
|
| `type` | Filter by project type (e.g. `"javascript"`); if set, task only applies when template type matches |
|
|
208
214
|
|
|
209
215
|
### Behavior
|
|
@@ -215,6 +221,7 @@ Each default task supports the same fields as template post-config:
|
|
|
215
221
|
|
|
216
222
|
You can view current default tasks using `pt config` or `pt default-post-config`.
|
|
217
223
|
To update default tasks programmatically or via CLI, use the `pt default-post-config` command:
|
|
224
|
+
|
|
218
225
|
- `pt default-post-config`: List current default post-config tasks.
|
|
219
226
|
- `pt default-post-config --set --json '...'`: Replace the default post-config tasks list via a JSON string or file.
|
|
220
227
|
|
|
@@ -299,6 +306,7 @@ Each entry supports:
|
|
|
299
306
|
A plausible scenario for customizing a new project's `package.json` and `README.md`:
|
|
300
307
|
|
|
301
308
|
**1. Define in `config.yaml`**:
|
|
309
|
+
|
|
302
310
|
```yaml
|
|
303
311
|
templates:
|
|
304
312
|
node_web_app:
|
|
@@ -317,6 +325,7 @@ templates:
|
|
|
317
325
|
```
|
|
318
326
|
|
|
319
327
|
**2. Template source (`templates/package.json.tmpl`)**:
|
|
328
|
+
|
|
320
329
|
```json
|
|
321
330
|
{
|
|
322
331
|
"name": "{{project_name}}",
|
|
@@ -327,6 +336,7 @@ templates:
|
|
|
327
336
|
|
|
328
337
|
**3. Resulting project file**:
|
|
329
338
|
If the user enters `my-service` and `Jane Doe`, the file `package.json` will be created with:
|
|
339
|
+
|
|
330
340
|
```json
|
|
331
341
|
{
|
|
332
342
|
"name": "my-service",
|
package/doc/usage.md
CHANGED
|
@@ -7,9 +7,13 @@
|
|
|
7
7
|
pt learn /path/to/PROJECT
|
|
8
8
|
|
|
9
9
|
# Update an existing template (use current directory if no path given)
|
|
10
|
+
# (Runs in additive difference mode by default: shows only new items, auto-selected, for de-selection)
|
|
10
11
|
pt update <template_name>
|
|
11
12
|
pt update <template_name> /path/to/PROJECT
|
|
12
13
|
|
|
14
|
+
# Use full mode (original behavior, presents all items)
|
|
15
|
+
pt update <template_name> /path/to/PROJECT --no-diff
|
|
16
|
+
|
|
13
17
|
# Ignore specific folders during learning
|
|
14
18
|
pt learn /path/to/PROJECT --ignore=DAILIES/*,PARKING_LOT/*,REFERENCE/*
|
|
15
19
|
|
|
@@ -60,12 +64,14 @@ pt init my-template my-new-project
|
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
**Key Features:**
|
|
67
|
+
|
|
63
68
|
- **Automatic Scanning:** Scans up to 3 parent directories for `.env` files
|
|
64
69
|
- **Variable Pre-filling:** Values from `.env` are used as defaults in prompts
|
|
65
70
|
- **Nested Variables:** Supports nested placeholders like `prefix='app_{{ env }}'`
|
|
66
71
|
- **Override Support:** Use `--vars` to override `.env` values: `--vars project=OverriddenProject`
|
|
67
72
|
|
|
68
73
|
**Example with Nested Variables:**
|
|
74
|
+
|
|
69
75
|
```bash
|
|
70
76
|
# .env file
|
|
71
77
|
prefix='app_{{ env }}'
|
|
@@ -79,6 +85,7 @@ project=MyApp
|
|
|
79
85
|
```
|
|
80
86
|
|
|
81
87
|
**Security Note:** `.env` files are not committed to version control. Use `.gitignore` to exclude them:
|
|
88
|
+
|
|
82
89
|
```bash
|
|
83
90
|
# .gitignore
|
|
84
91
|
.env
|
|
@@ -102,11 +109,13 @@ project=MyProject
|
|
|
102
109
|
```
|
|
103
110
|
|
|
104
111
|
**Use Cases:**
|
|
112
|
+
|
|
105
113
|
- **Project naming conventions:** `prefix='app_{{ env }}'` + `env=prod` → `app_prod`
|
|
106
114
|
- **Path templates:** `template_path='docs/{{ project }}'` + `project=wiki` → `docs/wiki`
|
|
107
115
|
- **Multi-level configurations:** Combine multiple `.env` files with nested references
|
|
108
116
|
|
|
109
117
|
**How it works:**
|
|
118
|
+
|
|
110
119
|
- Variables are expanded iteratively (up to 10 passes) to prevent infinite loops
|
|
111
120
|
- Circular references are detected and stopped gracefully
|
|
112
121
|
- Missing nested variables remain as `{{ variable }}` placeholders
|
package/package.json
CHANGED
|
@@ -27,7 +27,11 @@ As an agent equipped with this skill, you have the ability to rapidly scaffold,
|
|
|
27
27
|
3. **Capturing Knowledge (`pt learn`):**
|
|
28
28
|
If you spend time establishing a new, complex directory structure or configuration (e.g., a specific flavor of an Express backend with testing hooks), save it! Remote URLs (GitHub, Gitea, etc.) are automatically translated to tarball downloads.
|
|
29
29
|
- **Command:** `pt learn <source_path> --name <template_name> --desc "<Description>" --yes`
|
|
30
|
-
- **Update existing template:** Update an existing template with new structure/files: `pt update <template_name> <source_path> --yes
|
|
30
|
+
- **Update existing template:** Update an existing template with new structure/files: `pt update <template_name> <source_path> --yes`.
|
|
31
|
+
- **Additive Difference Mode (Default):** By default, `pt update` operates in an additive mode that only presents *new* folders, files, and variables from the target directory to the user for de-selection. Existing items and their configurations (such as `substitute_variables` and `post_copy` settings) are preserved as-is.
|
|
32
|
+
- **Editing Metadata:** During update, description and templateRoot are presented for editing with the existing template values pre-filled as defaults.
|
|
33
|
+
- **Global/Default Variables:** Default/global variables require the user to explicitly opt-in via checkboxes, rather than being automatically added.
|
|
34
|
+
- **Full/Original Mode:** To review all items manually instead of showing differences only, pass the `--no-diff` flag: `pt update <template_name> <source_path> --no-diff`
|
|
31
35
|
- **Remote Templates:** Learn from a remote Git repository or archive URL directly! Pass the HTTP/HTTPS URL as the `<source_path>`:
|
|
32
36
|
`pt learn https://github.com/username/my-template --name my_template --desc "Description" --yes`
|
|
33
37
|
- Explain to the user that you've captured this template for future use.
|
|
@@ -191,7 +195,7 @@ For more details, see the [Security Guide](security.md).
|
|
|
191
195
|
| Command | Description |
|
|
192
196
|
|---------|-------------|
|
|
193
197
|
| `pt learn <path>` | Learn a project structure from an existing directory |
|
|
194
|
-
| `pt update <template> [path]` | Update an existing template
|
|
198
|
+
| `pt update <template> [path]` | Update an existing template (additive difference mode by default, use --no-diff for full mode) |
|
|
195
199
|
| `pt init [template] [dest]` | Initialize a new project from a learned template |
|
|
196
200
|
| `pt config [template]` | Show current config location and list templates, or export a specific template |
|
|
197
201
|
| `pt variables [pairs]` | View or set global variables (comma-separated key=value) |
|
|
@@ -7,23 +7,124 @@ export interface AddOptions {
|
|
|
7
7
|
file?: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Validate JSON file exists and contains valid JSON
|
|
12
|
+
*/
|
|
13
|
+
function validateJsonFile(filePath: string): { valid: boolean; data?: any; error?: string } {
|
|
14
|
+
try {
|
|
15
|
+
// Check if file exists
|
|
16
|
+
if (!fs.existsSync(filePath)) {
|
|
17
|
+
return {
|
|
18
|
+
valid: false,
|
|
19
|
+
error: `File not found: ${filePath}`
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check file size (reasonable limit to prevent reading huge files)
|
|
24
|
+
const stats = fs.statSync(filePath);
|
|
25
|
+
if (stats.size > 10 * 1024 * 1024) { // 10MB limit
|
|
26
|
+
return {
|
|
27
|
+
valid: false,
|
|
28
|
+
error: `File too large (${stats.size} bytes). Maximum size is 10MB.`
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Read and parse JSON
|
|
33
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
34
|
+
if (!content.trim()) {
|
|
35
|
+
return {
|
|
36
|
+
valid: false,
|
|
37
|
+
error: 'File is empty'
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = JSON.parse(content);
|
|
42
|
+
return { valid: true, data };
|
|
43
|
+
} catch (e) {
|
|
44
|
+
const error = e as Error;
|
|
45
|
+
return {
|
|
46
|
+
valid: false,
|
|
47
|
+
error: `JSON parse error: ${error.message}`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Validate template structure
|
|
54
|
+
*/
|
|
55
|
+
function validateTemplateStructure(data: any): { valid: boolean; error?: string } {
|
|
56
|
+
if (!data || typeof data !== 'object') {
|
|
57
|
+
return {
|
|
58
|
+
valid: false,
|
|
59
|
+
error: 'Template data must be a JSON object'
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Basic structure validation
|
|
64
|
+
if (data.description && typeof data.description !== 'string') {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
error: 'Template description must be a string'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (data.variables && Array.isArray(data.variables)) {
|
|
72
|
+
for (let i = 0; i < data.variables.length; i++) {
|
|
73
|
+
const v = data.variables[i];
|
|
74
|
+
if (!v.name || typeof v.name !== 'string') {
|
|
75
|
+
return {
|
|
76
|
+
valid: false,
|
|
77
|
+
error: `Variable at index ${i} must have a string 'name' field`
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { valid: true };
|
|
84
|
+
}
|
|
85
|
+
|
|
10
86
|
export function addCommand(name: string, jsonStr: string | undefined, options: AddOptions = {}) {
|
|
11
87
|
const config = loadConfig();
|
|
88
|
+
|
|
89
|
+
// Determine if we're reading from file or string
|
|
90
|
+
const isFile = !!options.file;
|
|
91
|
+
let data: any;
|
|
92
|
+
|
|
12
93
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const filePath = path.resolve(options.file);
|
|
16
|
-
|
|
94
|
+
if (isFile) {
|
|
95
|
+
// Validate file first
|
|
96
|
+
const filePath = path.resolve(options.file!);
|
|
97
|
+
const validation = validateJsonFile(filePath);
|
|
98
|
+
|
|
99
|
+
if (!validation.valid) {
|
|
100
|
+
console.error(chalk.red(`Error: ${validation.error}`));
|
|
101
|
+
console.error(chalk.gray(`File: ${filePath}`));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
data = validation.data;
|
|
17
106
|
} else if (jsonStr) {
|
|
18
|
-
|
|
107
|
+
// Parse JSON string directly
|
|
108
|
+
try {
|
|
109
|
+
data = JSON.parse(jsonStr);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
const error = e as Error;
|
|
112
|
+
console.error(chalk.red(`Error: Invalid JSON string - ${error.message}`));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
19
115
|
} else {
|
|
20
116
|
console.error('Error: Either a JSON string or --file <path> must be provided.');
|
|
21
117
|
process.exit(1);
|
|
22
118
|
}
|
|
23
119
|
|
|
24
|
-
|
|
120
|
+
// Validate template structure
|
|
121
|
+
const structureValidation = validateTemplateStructure(data);
|
|
122
|
+
if (!structureValidation.valid) {
|
|
123
|
+
console.error(chalk.red(`Error: Invalid template structure - ${structureValidation.error}`));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
25
126
|
|
|
26
|
-
//
|
|
127
|
+
// Check for full config object
|
|
27
128
|
if (data && data.templates && typeof data.templates === 'object') {
|
|
28
129
|
console.error(chalk.red('Error: The provided JSON appears to be a full configuration file, not a single template.'));
|
|
29
130
|
console.error(chalk.gray('If you want to import a specific template from it, extract that template object first.'));
|
|
@@ -35,7 +136,7 @@ export function addCommand(name: string, jsonStr: string | undefined, options: A
|
|
|
35
136
|
console.log(chalk.green(`✓ Template "${name}" saved successfully.`));
|
|
36
137
|
} catch (e) {
|
|
37
138
|
const error = e as Error;
|
|
38
|
-
console.error(chalk.red(`Failed to
|
|
139
|
+
console.error(chalk.red(`Failed to process template: ${error.message}`));
|
|
39
140
|
process.exit(1);
|
|
40
141
|
}
|
|
41
142
|
}
|
|
@@ -15,13 +15,20 @@ export function configCommand(templateName: string | undefined, options: ConfigO
|
|
|
15
15
|
name: templateName,
|
|
16
16
|
...config.templates[templateName]
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
// Safely drain stdout before allowing the process to close
|
|
19
|
+
process.stdout.write(JSON.stringify(output, null, 2) + '\n', () => {
|
|
20
|
+
process.exit(0);
|
|
21
|
+
});
|
|
19
22
|
} else {
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
process.stderr.write(chalk.red(`Error: Template "${templateName}" not found.\n`), () => {
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
22
26
|
}
|
|
23
27
|
} else {
|
|
24
|
-
|
|
28
|
+
// Safely drain the entire global config payload
|
|
29
|
+
process.stdout.write(JSON.stringify(config, null, 2) + '\n', () => {
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
25
32
|
}
|
|
26
33
|
return;
|
|
27
34
|
}
|
|
@@ -542,11 +542,16 @@ export async function learn(sourcePath: string, updateTemplate: string | null =
|
|
|
542
542
|
}
|
|
543
543
|
|
|
544
544
|
if (options.json) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
545
|
+
const output = {
|
|
546
|
+
name: targetName,
|
|
547
|
+
...templateConfig
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// Force the application to wait until every single byte of this JSON string
|
|
551
|
+
// safely clears the operating system's pipe buffer before letting the process die.
|
|
552
|
+
process.stdout.write(JSON.stringify(output, null, 2) + '\n', () => {
|
|
553
|
+
process.exit(0);
|
|
554
|
+
});
|
|
550
555
|
return;
|
|
551
556
|
}
|
|
552
557
|
|
|
@@ -588,8 +593,8 @@ function extractStructure(dirPath: string, rootPath: string, ignorePatterns?: st
|
|
|
588
593
|
let info = "";
|
|
589
594
|
const gitkeepPath = path.join(fullPath, '.gitkeep.md');
|
|
590
595
|
const infoPath = path.join(fullPath, '.info.md');
|
|
591
|
-
if (fs.existsSync(
|
|
592
|
-
else if (fs.existsSync(
|
|
596
|
+
if (fs.existsSync(infoPath)) info = fs.readFileSync(infoPath, 'utf-8').trim();
|
|
597
|
+
else if (fs.existsSync(gitkeepPath)) info = fs.readFileSync(gitkeepPath, 'utf-8').trim();
|
|
593
598
|
nodes.push({ name: entry.name, info: info, children: children });
|
|
594
599
|
}
|
|
595
600
|
} catch (e) {}
|