@garyr/pt-cli 0.31.0 → 0.32.1

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,18 @@ 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
+
123
+ ## Warning
124
+
125
+ **⚠️ 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.
126
+
127
+ **💾 Backup Paths**:
128
+
129
+ - **Linux/macOS**: `~/.pt/` or `/home/username/.pt/` and `/Users/username/.pt/`
130
+ - **Windows**: `%USERPROFILE%\.pt\` (typically `C:\Users\Username\.pt\`)
131
+
132
+ Always back up these directories before installing new versions or making significant changes.
133
+
134
+ ## Example Templates
135
+
136
+ There are a couple of [example templates](https://github.com/search?q=topic%3Atemplate-project+org%3Agaryritchie&type=Repositories) for you to try. These include utilities written in python to help streamline common file management tasks.
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { loadConfig, getTemplateNames, CONFIG_PATH } from '../config.js';
2
+ import { loadConfig, getTemplateNames, getConfigPath } from '../config.js';
3
3
  export function configCommand(templateName, options = {}) {
4
4
  const config = loadConfig();
5
5
  if (options.json) {
@@ -22,7 +22,7 @@ export function configCommand(templateName, options = {}) {
22
22
  return;
23
23
  }
24
24
  const names = getTemplateNames(config);
25
- console.log(chalk.cyan('Config Location:'), CONFIG_PATH);
25
+ console.log(chalk.cyan('Config Location:'), getConfigPath());
26
26
  console.log(chalk.cyan('\nLearned Templates:'));
27
27
  if (names.length === 0) {
28
28
  console.log(chalk.gray(' (none)'));
package/dist/config.js CHANGED
@@ -3,16 +3,20 @@ import fs from 'fs';
3
3
  import path from 'path';
4
4
  import os from 'os';
5
5
  import chalk from 'chalk';
6
- export const HOME_DIR = path.join(os.homedir(), '.pt');
7
- export const CONFIG_PATH = path.join(HOME_DIR, 'config.yaml');
6
+ export function getHomeDir() {
7
+ return path.join(os.homedir(), '.pt');
8
+ }
9
+ export function getConfigPath() {
10
+ return path.join(getHomeDir(), 'config.yaml');
11
+ }
8
12
  export function ensureConfigDir() {
9
- if (!fs.existsSync(HOME_DIR)) {
10
- fs.mkdirSync(HOME_DIR, { recursive: true });
13
+ if (!fs.existsSync(getHomeDir())) {
14
+ fs.mkdirSync(getHomeDir(), { recursive: true });
11
15
  }
12
16
  }
13
17
  export function loadConfig() {
14
18
  ensureConfigDir();
15
- if (!fs.existsSync(CONFIG_PATH)) {
19
+ if (!fs.existsSync(getConfigPath())) {
16
20
  const defaultConfig = {
17
21
  version: '3.0',
18
22
  templates: {},
@@ -25,7 +29,7 @@ export function loadConfig() {
25
29
  return defaultConfig;
26
30
  }
27
31
  try {
28
- const content = fs.readFileSync(CONFIG_PATH, 'utf-8');
32
+ const content = fs.readFileSync(getConfigPath(), 'utf-8');
29
33
  if (!content.trim()) {
30
34
  throw new Error("Config file is empty");
31
35
  }
@@ -81,7 +85,7 @@ export function loadConfig() {
81
85
  const error = err;
82
86
  console.error(chalk.red(`\nError loading config: ${error.message}`));
83
87
  // If we have a backup, maybe suggest using it
84
- const backupPath = CONFIG_PATH + '.bak';
88
+ const backupPath = getConfigPath() + '.bak';
85
89
  if (fs.existsSync(backupPath)) {
86
90
  console.error(chalk.yellow(`A backup exists at ${backupPath}. You may want to restore it.`));
87
91
  }
@@ -113,17 +117,17 @@ export function saveConfig(config) {
113
117
  }
114
118
  }
115
119
  const content = YAML.stringify(config);
116
- const tempPath = CONFIG_PATH + '.tmp';
117
- const backupPath = CONFIG_PATH + '.bak';
120
+ const tempPath = getConfigPath() + '.tmp';
121
+ const backupPath = getConfigPath() + '.bak';
118
122
  try {
119
123
  // 1. Create a backup of the current valid config if it exists
120
- if (fs.existsSync(CONFIG_PATH)) {
121
- fs.copyFileSync(CONFIG_PATH, backupPath);
124
+ if (fs.existsSync(getConfigPath())) {
125
+ fs.copyFileSync(getConfigPath(), backupPath);
122
126
  }
123
127
  // 2. Write to a temporary file first (atomic save)
124
128
  fs.writeFileSync(tempPath, content);
125
129
  // 3. Rename temp file to actual config path
126
- fs.renameSync(tempPath, CONFIG_PATH);
130
+ fs.renameSync(tempPath, getConfigPath());
127
131
  }
128
132
  catch (err) {
129
133
  const error = err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garyr/pt-cli",
3
- "version": "0.31.0",
3
+ "version": "0.32.1",
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",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agency-pt-operator
3
- description: Specialist in using pt-cli to scaffold project templates, capture boilerplate, and maintain standardized directory structures. Includes knowledge of default_post_config, global variables, and automatic variable detection.
3
+ description: Specialist in using pt-cli to scaffold project templates, capture boilerplate, and maintain standardized directory structures. Includes knowledge of default_post_config, global variables, automatic variable detection, JSON template configs, and portable template workflows.
4
4
  ---
5
5
 
6
6
  # `pt-cli` Operator Skill
@@ -14,26 +14,30 @@ As an agent equipped with this skill, you have the ability to rapidly scaffold,
14
14
  - Run `pt config` to view available templates, their post-config tasks, and default post-config tasks.
15
15
 
16
16
  2. **Scaffolding (`pt init`):**
17
- When a matching template exists, initialize it using the non-interactive flags.
17
+ When a matching template exists, initialize it using the non-interactive flags. URL targets (GitHub, Gitea, etc.) are automatically translated to tarball downloads.
18
18
  - **Command:** `pt init <template_name> <destination_path> --yes`
19
19
  - If the template requires variables, pass them: `pt init <template_name> <destination_path> --yes --vars key1=value1,key2=value2`
20
20
  - **Direct JSON scaffolding:** To scaffold from a JSON template file without registering it in `config.yaml`:
21
21
  `pt init <destination_path> --file <json_path> --yes`
22
22
  - *Never* run `pt init` without `--yes`, as interactive prompts will block you.
23
+ - **Dry-run:** Preview what would be created without making changes: `pt init <template_name> <destination_path> --yes --dry-run`
24
+ - **Skip post-config:** Skip running post-config tasks: `pt init <template_name> <destination_path> --yes --skip-post-config`
23
25
  - Note any errors from auto-executed post-config tasks (like `npm install` failing) and correct them if necessary.
24
26
 
25
27
  3. **Capturing Knowledge (`pt learn`):**
26
- 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!
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.
27
29
  - **Command:** `pt learn <source_path> --name <template_name> --desc "<Description>" --yes`
28
- - **Remote Templates:** You can also learn from a remote Git repository or archive URL directly! Pass the HTTP/HTTPS URL as the `<source_path>`:
30
+ - **Update existing template:** Update an existing template with new structure/files: `pt update <template_name> <source_path> --yes`
31
+ - **Remote Templates:** Learn from a remote Git repository or archive URL directly! Pass the HTTP/HTTPS URL as the `<source_path>`:
29
32
  `pt learn https://github.com/username/my-template --name my_template --desc "Description" --yes`
30
33
  - Explain to the user that you've captured this template for future use.
31
34
  - **Automatic Variable Detection:** `pt learn` and `pt update` automatically scan text files (at root and one level deep) for `{{ variable_name }}` placeholders. You can add these to files (e.g., `README.md`, `.makerc`) and run `pt update <template> . --yes` to have them registered as template variables without manual configuration.
32
- - **JSON Template Config:** If the source directory contains a `.pt-template.json` or `template.json` file, `pt learn` will auto-detect name, description, variables, folders, copy_files, post_config, and post_copy from it — skipping the corresponding interactive prompts.
35
+ - **JSON Template Config:** If the source directory contains a `.pt-template.json` or `template.json` file, `pt learn` will auto-detect name, description, variables, folders, copy_files, post_config, and post_copy from it — skipping the corresponding interactive prompts. JSON config takes precedence over `.info.md` and shell scripts.
36
+ - **JSON Output:** Output template structure as JSON for sharing instead of saving: `pt learn <source_path> --json`
33
37
 
34
38
  4. **Template Maintenance (`pt rm`):**
35
39
  If a template is obsolete or requested for deletion, use `pt rm`.
36
- - **Command:** `pt rm <template_name> --yes`
40
+ - **Command:** `pt rm <template_name> --yes` or `pt remove <template_name> --yes`
37
41
 
38
42
  ## Template Sharing & Portability
39
43
 
@@ -41,7 +45,7 @@ Templates are designed to be fully portable. There are two approaches to sharing
41
45
 
42
46
  ### 1. Directory-based Sharing (recommended for complete templates)
43
47
 
44
- Place a `.pt-template.json` at the root of your template directory. This file can include all template metadata:
48
+ Place a `.pt-template.json` or `template.json` at the root of your template directory. This file can include all template metadata:
45
49
 
46
50
  ```json
47
51
  {
@@ -56,9 +60,9 @@ Place a `.pt-template.json` at the root of your template directory. This file ca
56
60
  }
57
61
  ```
58
62
 
59
- When someone runs `pt learn /path/to/shared-dir --yes`, all metadata is auto-detected from this file.
63
+ When someone runs `pt learn /path/to/shared-dir --yes`, all metadata is auto-detected from this file. The priority order is: `.pt-template.json` > `template.json` > `.info.md` > `post_config.sh`/`.bat`.
60
64
 
61
- **Priority order:** `.pt-template.json` > `template.json` > `.info.md` > `post_config.sh`/`.bat`
65
+ JSON config files also take precedence over shell scripts for post_config tasks.
62
66
 
63
67
  ### 2. JSON Export/Import (for config-only sharing)
64
68
 
@@ -80,6 +84,11 @@ The complete portable template workflow:
80
84
  2. Copy `.pt-template.json` into the template source directory
81
85
  3. Share the directory (zip, git repo, etc.)
82
86
  4. Recipient: `pt learn /path/to/shared-dir --yes` — auto-detects everything
87
+ 5. Or scaffold directly without registering: `pt init ./new-project --file .pt-template.json --yes`
88
+
89
+ ### JSON Output for Sharing
90
+
91
+ You can output a template as JSON for sharing without saving it: `pt learn <source_path> --json`
83
92
 
84
93
  ## Default Post-Config
85
94
 
@@ -101,7 +110,7 @@ Global variables are defined in `~/.pt/config.yaml` under `variables`. They act
101
110
  - **Purpose:** They ensure that common metadata fields (like `author`, `license`, `project_version`) are consistently offered for inclusion in every new template you create.
102
111
  - **Inheritance:** When you `learn` a new project structure, these global variables are automatically merged with any detected variables (`{{ var }}`) and offered as part of the new template's variable list.
103
112
  - **Localization:** Once a template is saved, its variables are "stamped" in. Subsequent changes to global variables in the config will **not** retroactively affect old templates, ensuring stability and portability.
104
- - **Management:** Use `pt variables --set key=value` to update your global defaults. For bulk updates, use `pt variables --set --json '...'`.
113
+ - **Management:** Use `pt variables --set key=value` to update your global defaults. For bulk updates, use `pt variables --set --json '...'`. To delete a global variable, use `pt variables --delete key`.
105
114
 
106
115
  ## Workflow Optimization
107
116
 
@@ -109,3 +118,25 @@ Global variables are defined in `~/.pt/config.yaml` under `variables`. They act
109
118
  1. Identify the closest matching template (`pt config`).
110
119
  2. Scaffold it non-interactively (`pt init ... --yes`).
111
120
  3. Make the specific manual code/configuration changes requested by the user on top of that scaffolded base.
121
+ 4. If the scaffold is close but needs updates, use `pt update <template_name> . --yes` to update the saved template with your changes.
122
+
123
+ ## Additional Notes
124
+
125
+ - JSON variables take precedence over other variable sources during `pt update`.
126
+ - Optional whitespace is allowed around template variables in the substitute function.
127
+ - Trailing `.git` is automatically stripped from repository URLs.
128
+ - Atomic saves, backups, and safe initialization logic prevent data loss.
129
+ - Platform-specific post-config scripts (`.sh`/`.bat`) are executed automatically based on the OS.
130
+
131
+ ## CLI Reference
132
+
133
+ | Command | Description |
134
+ |---------|-------------|
135
+ | `pt learn <path>` | Learn a project structure from an existing directory |
136
+ | `pt update <template> [path]` | Update an existing template with new structure/files |
137
+ | `pt init [template] [dest]` | Initialize a new project from a learned template |
138
+ | `pt config [template]` | Show current config location and list templates, or export a specific template |
139
+ | `pt variables [pairs]` | View or set global variables (comma-separated key=value) |
140
+ | `pt default-post-config` | View or set default post-config tasks |
141
+ | `pt add <name> [json]` | Import/add a template from a JSON string or file |
142
+ | `pt remove <template>` / `pt rm <template>` | Remove a learned template from the config |
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { loadConfig, getTemplateNames, CONFIG_PATH } from '../config.js';
2
+ import { loadConfig, getTemplateNames, getConfigPath } from '../config.js';
3
3
 
4
4
  export interface ConfigOptions {
5
5
  json?: boolean;
@@ -28,7 +28,7 @@ export function configCommand(templateName: string | undefined, options: ConfigO
28
28
 
29
29
  const names = getTemplateNames(config);
30
30
 
31
- console.log(chalk.cyan('Config Location:'), CONFIG_PATH);
31
+ console.log(chalk.cyan('Config Location:'), getConfigPath());
32
32
  console.log(chalk.cyan('\nLearned Templates:'));
33
33
  if (names.length === 0) {
34
34
  console.log(chalk.gray(' (none)'));
package/src/config.ts CHANGED
@@ -4,8 +4,13 @@ import path from 'path';
4
4
  import os from 'os';
5
5
  import chalk from 'chalk';
6
6
 
7
- export const HOME_DIR = path.join(os.homedir(), '.pt');
8
- export const CONFIG_PATH = path.join(HOME_DIR, 'config.yaml');
7
+ export function getHomeDir(): string {
8
+ return path.join(os.homedir(), '.pt');
9
+ }
10
+
11
+ export function getConfigPath(): string {
12
+ return path.join(getHomeDir(), 'config.yaml');
13
+ }
9
14
 
10
15
  export interface FolderNode {
11
16
  name: string;
@@ -63,14 +68,14 @@ export interface PtConfig {
63
68
  }
64
69
 
65
70
  export function ensureConfigDir() {
66
- if (!fs.existsSync(HOME_DIR)) {
67
- fs.mkdirSync(HOME_DIR, { recursive: true });
71
+ if (!fs.existsSync(getHomeDir())) {
72
+ fs.mkdirSync(getHomeDir(), { recursive: true });
68
73
  }
69
74
  }
70
75
 
71
76
  export function loadConfig(): PtConfig {
72
77
  ensureConfigDir();
73
- if (!fs.existsSync(CONFIG_PATH)) {
78
+ if (!fs.existsSync(getConfigPath())) {
74
79
  const defaultConfig: PtConfig = {
75
80
  version: '3.0',
76
81
  templates: {},
@@ -84,7 +89,7 @@ export function loadConfig(): PtConfig {
84
89
  }
85
90
 
86
91
  try {
87
- const content = fs.readFileSync(CONFIG_PATH, 'utf-8');
92
+ const content = fs.readFileSync(getConfigPath(), 'utf-8');
88
93
  if (!content.trim()) {
89
94
  throw new Error("Config file is empty");
90
95
  }
@@ -142,7 +147,7 @@ export function loadConfig(): PtConfig {
142
147
  const error = err as Error;
143
148
  console.error(chalk.red(`\nError loading config: ${error.message}`));
144
149
  // If we have a backup, maybe suggest using it
145
- const backupPath = CONFIG_PATH + '.bak';
150
+ const backupPath = getConfigPath() + '.bak';
146
151
  if (fs.existsSync(backupPath)) {
147
152
  console.error(chalk.yellow(`A backup exists at ${backupPath}. You may want to restore it.`));
148
153
  }
@@ -175,20 +180,20 @@ export function saveConfig(config: PtConfig) {
175
180
  }
176
181
 
177
182
  const content = YAML.stringify(config);
178
- const tempPath = CONFIG_PATH + '.tmp';
179
- const backupPath = CONFIG_PATH + '.bak';
183
+ const tempPath = getConfigPath() + '.tmp';
184
+ const backupPath = getConfigPath() + '.bak';
180
185
 
181
186
  try {
182
187
  // 1. Create a backup of the current valid config if it exists
183
- if (fs.existsSync(CONFIG_PATH)) {
184
- fs.copyFileSync(CONFIG_PATH, backupPath);
188
+ if (fs.existsSync(getConfigPath())) {
189
+ fs.copyFileSync(getConfigPath(), backupPath);
185
190
  }
186
191
 
187
192
  // 2. Write to a temporary file first (atomic save)
188
193
  fs.writeFileSync(tempPath, content);
189
194
 
190
195
  // 3. Rename temp file to actual config path
191
- fs.renameSync(tempPath, CONFIG_PATH);
196
+ fs.renameSync(tempPath, getConfigPath());
192
197
  } catch (err) {
193
198
  const error = err as Error;
194
199
  console.error(chalk.red(`\nFailed to save config: ${error.message}`));
@@ -0,0 +1,4 @@
1
+ // Save original HOME before modifying
2
+ const originalHome = process
3
+ .env
4
+ .HOME;
@@ -16,7 +16,7 @@ import {
16
16
  shouldExcludeFile,
17
17
  sanitizePath,
18
18
  DEFAULT_EXCLUDES,
19
- HOME_DIR,
19
+ getHomeDir,
20
20
  PtConfig,
21
21
  } from '../src/config.js';
22
22
 
@@ -31,30 +31,30 @@ after(() => {
31
31
 
32
32
  test('ensureConfigDir creates dir when it does not exist', () => {
33
33
  // Make sure the dir does NOT exist before the test
34
- if (fs.existsSync(HOME_DIR)) {
35
- fs.rmSync(HOME_DIR, { recursive: true, force: true });
34
+ if (fs.existsSync(getHomeDir())) {
35
+ fs.rmSync(getHomeDir(), { recursive: true, force: true });
36
36
  }
37
- assert.ok(!fs.existsSync(HOME_DIR), 'Precondition: HOME_DIR should not exist');
37
+ assert.ok(!fs.existsSync(getHomeDir()), 'Precondition: getHomeDir() should not exist');
38
38
 
39
39
  ensureConfigDir();
40
40
 
41
- assert.ok(fs.existsSync(HOME_DIR), 'HOME_DIR should be created');
42
- assert.ok(fs.statSync(HOME_DIR).isDirectory(), 'HOME_DIR should be a directory');
41
+ assert.ok(fs.existsSync(getHomeDir()), 'getHomeDir() should be created');
42
+ assert.ok(fs.statSync(getHomeDir()).isDirectory(), 'getHomeDir() should be a directory');
43
43
  });
44
44
 
45
45
  test('ensureConfigDir does nothing when dir already exists', () => {
46
46
  // Ensure directory exists first
47
- if (!fs.existsSync(HOME_DIR)) {
48
- fs.mkdirSync(HOME_DIR, { recursive: true });
47
+ if (!fs.existsSync(getHomeDir())) {
48
+ fs.mkdirSync(getHomeDir(), { recursive: true });
49
49
  }
50
50
  // Place a marker file inside to prove the dir is not recreated
51
- const markerPath = path.join(HOME_DIR, '.marker');
51
+ const markerPath = path.join(getHomeDir(), '.marker');
52
52
  fs.writeFileSync(markerPath, 'exists');
53
53
 
54
54
  ensureConfigDir();
55
55
 
56
- assert.ok(fs.existsSync(HOME_DIR), 'HOME_DIR should still exist');
57
- assert.ok(fs.existsSync(markerPath), 'Marker file inside HOME_DIR should still exist');
56
+ assert.ok(fs.existsSync(getHomeDir()), 'getHomeDir() should still exist');
57
+ assert.ok(fs.existsSync(markerPath), 'Marker file inside getHomeDir() should still exist');
58
58
 
59
59
  // Clean up marker
60
60
  fs.unlinkSync(markerPath);
@@ -1,13 +1,15 @@
1
- import { test, before, after } from 'node:test';
1
+ import { test, after } from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
5
 
6
6
  // Force a temporary home directory for testing before importing anything from the CLI
7
7
  const testHome = path.join(process.cwd(), '.test-home-config');
8
+
9
+ // Set HOME environment variable before importing (needed for dynamic getters)
8
10
  process.env.HOME = testHome;
9
11
 
10
- import { normalizeVariable, saveConfig, loadConfig, PtConfig, CONFIG_PATH } from '../src/config.js';
12
+ import { normalizeVariable, saveConfig, loadConfig, PtConfig, getConfigPath, getHomeDir } from '../src/config.js';
11
13
 
12
14
  test('normalizeVariable key ordering', () => {
13
15
  const variableInput = {
@@ -28,15 +30,15 @@ test('normalizeVariable key ordering', () => {
28
30
  test('saveConfig and loadConfig roundtrip', () => {
29
31
  // Save the current config to restore later
30
32
  let savedConfig: PtConfig | null = null;
31
- if (fs.existsSync(CONFIG_PATH)) {
33
+ if (fs.existsSync(getConfigPath())) {
32
34
  savedConfig = loadConfig();
33
35
  }
34
36
 
35
37
  // Clean up any existing test files
36
- if (fs.existsSync(CONFIG_PATH)) {
37
- fs.unlinkSync(CONFIG_PATH);
38
+ if (fs.existsSync(getConfigPath())) {
39
+ fs.unlinkSync(getConfigPath());
38
40
  }
39
- const backupPath = CONFIG_PATH + '.bak';
41
+ const backupPath = getConfigPath() + '.bak';
40
42
  if (fs.existsSync(backupPath)) {
41
43
  fs.unlinkSync(backupPath);
42
44
  }
@@ -64,11 +66,11 @@ test('saveConfig and loadConfig roundtrip', () => {
64
66
  // Save the config
65
67
  saveConfig(testConfig);
66
68
 
67
- // Assert CONFIG_PATH exists and has been created
68
- assert.ok(fs.existsSync(CONFIG_PATH), 'Config file should be created');
69
+ // Assert getConfigPath() exists and has been created
70
+ assert.ok(fs.existsSync(getConfigPath()), 'Config file should be created');
69
71
 
70
72
  // Verify key order in YAML string
71
- const yamlContent = fs.readFileSync(CONFIG_PATH, 'utf-8');
73
+ const yamlContent = fs.readFileSync(getConfigPath(), 'utf-8');
72
74
  assert.ok(yamlContent.includes('name: myVar'), 'Should contain myVar variable');
73
75
 
74
76
  // Load the config back
@@ -92,8 +94,8 @@ test('saveConfig and loadConfig roundtrip', () => {
92
94
  assert.ok(backupContent.includes("version: 1.0.0") || backupContent.includes("version: '1.0.0'"), 'Backup should contain previous version');
93
95
 
94
96
  // Clean up
95
- if (fs.existsSync(CONFIG_PATH)) {
96
- fs.unlinkSync(CONFIG_PATH);
97
+ if (fs.existsSync(getConfigPath())) {
98
+ fs.unlinkSync(getConfigPath());
97
99
  }
98
100
  if (fs.existsSync(backupPath)) {
99
101
  fs.unlinkSync(backupPath);
@@ -105,7 +107,7 @@ test('saveConfig and loadConfig roundtrip', () => {
105
107
  }
106
108
 
107
109
  if (fs.existsSync(testHome)) {
108
- fs.rmdirSync(testHome);
110
+ fs.rmSync(testHome, { recursive: true, force: true });
109
111
  }
110
112
  });
111
113
 
@@ -144,15 +146,15 @@ test('normalizeVariable with all fields', () => {
144
146
  test('saveConfig creates backup on overwrite', () => {
145
147
  // Save the current config to restore later
146
148
  let savedConfig: PtConfig | null = null;
147
- if (fs.existsSync(CONFIG_PATH)) {
149
+ if (fs.existsSync(getConfigPath())) {
148
150
  savedConfig = loadConfig();
149
151
  }
150
152
 
151
153
  // Clean up any existing test files
152
- if (fs.existsSync(CONFIG_PATH)) {
153
- fs.unlinkSync(CONFIG_PATH);
154
+ if (fs.existsSync(getConfigPath())) {
155
+ fs.unlinkSync(getConfigPath());
154
156
  }
155
- const backupPath = CONFIG_PATH + '.bak';
157
+ const backupPath = getConfigPath() + '.bak';
156
158
  if (fs.existsSync(backupPath)) {
157
159
  fs.unlinkSync(backupPath);
158
160
  }
@@ -168,7 +170,7 @@ test('saveConfig creates backup on overwrite', () => {
168
170
 
169
171
  // Save first config
170
172
  saveConfig(config1);
171
- assert.ok(fs.existsSync(CONFIG_PATH), 'Config file should be created');
173
+ assert.ok(fs.existsSync(getConfigPath()), 'Config file should be created');
172
174
 
173
175
  // Save second config (should create backup)
174
176
  saveConfig(config2);
@@ -179,8 +181,8 @@ test('saveConfig creates backup on overwrite', () => {
179
181
  assert.ok(backupContent.includes("version: 1.0.0") || backupContent.includes("version: '1.0.0'"), 'Backup should contain previous version');
180
182
 
181
183
  // Clean up
182
- if (fs.existsSync(CONFIG_PATH)) {
183
- fs.unlinkSync(CONFIG_PATH);
184
+ if (fs.existsSync(getConfigPath())) {
185
+ fs.unlinkSync(getConfigPath());
184
186
  }
185
187
  if (fs.existsSync(backupPath)) {
186
188
  fs.unlinkSync(backupPath);
@@ -192,22 +194,22 @@ test('saveConfig creates backup on overwrite', () => {
192
194
  }
193
195
 
194
196
  if (fs.existsSync(testHome)) {
195
- fs.rmdirSync(testHome);
197
+ fs.rmSync(testHome, { recursive: true, force: true });
196
198
  }
197
199
  });
198
200
 
199
201
  test('saveConfig atomic write behavior', () => {
200
202
  // Save the current config to restore later
201
203
  let savedConfig: PtConfig | null = null;
202
- if (fs.existsSync(CONFIG_PATH)) {
204
+ if (fs.existsSync(getConfigPath())) {
203
205
  savedConfig = loadConfig();
204
206
  }
205
207
 
206
208
  // Clean up any existing test files
207
- if (fs.existsSync(CONFIG_PATH)) {
208
- fs.unlinkSync(CONFIG_PATH);
209
+ if (fs.existsSync(getConfigPath())) {
210
+ fs.unlinkSync(getConfigPath());
209
211
  }
210
- const backupPath = CONFIG_PATH + '.bak';
212
+ const backupPath = getConfigPath() + '.bak';
211
213
  if (fs.existsSync(backupPath)) {
212
214
  fs.unlinkSync(backupPath);
213
215
  }
@@ -221,15 +223,15 @@ test('saveConfig atomic write behavior', () => {
221
223
  saveConfig(config);
222
224
 
223
225
  // Verify config exists
224
- assert.ok(fs.existsSync(CONFIG_PATH), 'Config file should be created');
226
+ assert.ok(fs.existsSync(getConfigPath()), 'Config file should be created');
225
227
 
226
228
  // Verify no temp file remains
227
- const tempPath = CONFIG_PATH + '.tmp';
229
+ const tempPath = getConfigPath() + '.tmp';
228
230
  assert.ok(!fs.existsSync(tempPath), 'Temp file should not remain');
229
231
 
230
232
  // Clean up
231
- if (fs.existsSync(CONFIG_PATH)) {
232
- fs.unlinkSync(CONFIG_PATH);
233
+ if (fs.existsSync(getConfigPath())) {
234
+ fs.unlinkSync(getConfigPath());
233
235
  }
234
236
  if (fs.existsSync(backupPath)) {
235
237
  fs.unlinkSync(backupPath);
@@ -241,22 +243,22 @@ test('saveConfig atomic write behavior', () => {
241
243
  }
242
244
 
243
245
  if (fs.existsSync(testHome)) {
244
- fs.rmdirSync(testHome);
246
+ fs.rmSync(testHome, { recursive: true, force: true });
245
247
  }
246
248
  });
247
249
 
248
250
  test('loadConfig returns default config when file missing', () => {
249
251
  // Save the current config to restore later
250
252
  let savedConfig: PtConfig | null = null;
251
- if (fs.existsSync(CONFIG_PATH)) {
253
+ if (fs.existsSync(getConfigPath())) {
252
254
  savedConfig = loadConfig();
253
255
  }
254
256
 
255
257
  // Clean up any existing test files
256
- if (fs.existsSync(CONFIG_PATH)) {
257
- fs.unlinkSync(CONFIG_PATH);
258
+ if (fs.existsSync(getConfigPath())) {
259
+ fs.unlinkSync(getConfigPath());
258
260
  }
259
- const backupPath = CONFIG_PATH + '.bak';
261
+ const backupPath = getConfigPath() + '.bak';
260
262
  if (fs.existsSync(backupPath)) {
261
263
  fs.unlinkSync(backupPath);
262
264
  }
@@ -271,7 +273,7 @@ test('loadConfig returns default config when file missing', () => {
271
273
 
272
274
  // Clean up
273
275
  if (fs.existsSync(testHome)) {
274
- fs.rmdirSync(testHome);
276
+ fs.rmSync(testHome, { recursive: true, force: true });
275
277
  }
276
278
 
277
279
  // Restore the original config if it existed
@@ -283,15 +285,15 @@ test('loadConfig returns default config when file missing', () => {
283
285
  test('saveConfig with variables normalizes key order', () => {
284
286
  // Save the current config to restore later
285
287
  let savedConfig: PtConfig | null = null;
286
- if (fs.existsSync(CONFIG_PATH)) {
288
+ if (fs.existsSync(getConfigPath())) {
287
289
  savedConfig = loadConfig();
288
290
  }
289
291
 
290
292
  // Clean up any existing test files
291
- if (fs.existsSync(CONFIG_PATH)) {
292
- fs.unlinkSync(CONFIG_PATH);
293
+ if (fs.existsSync(getConfigPath())) {
294
+ fs.unlinkSync(getConfigPath());
293
295
  }
294
- const backupPath = CONFIG_PATH + '.bak';
296
+ const backupPath = getConfigPath() + '.bak';
295
297
  if (fs.existsSync(backupPath)) {
296
298
  fs.unlinkSync(backupPath);
297
299
  }
@@ -332,8 +334,8 @@ test('saveConfig with variables normalizes key order', () => {
332
334
  assert.strictEqual(vars[1].name, 'var2', 'Second variable name should be var2');
333
335
 
334
336
  // Clean up
335
- if (fs.existsSync(CONFIG_PATH)) {
336
- fs.unlinkSync(CONFIG_PATH);
337
+ if (fs.existsSync(getConfigPath())) {
338
+ fs.unlinkSync(getConfigPath());
337
339
  }
338
340
  if (fs.existsSync(backupPath)) {
339
341
  fs.unlinkSync(backupPath);
@@ -345,28 +347,33 @@ test('saveConfig with variables normalizes key order', () => {
345
347
  }
346
348
 
347
349
  if (fs.existsSync(testHome)) {
348
- fs.rmdirSync(testHome);
350
+ fs.rmSync(testHome, { recursive: true, force: true });
349
351
  }
350
352
  });
351
353
 
352
354
  test('saveConfig with empty config file', () => {
353
355
  // Save the current config to restore later
354
356
  let savedConfig: PtConfig | null = null;
355
- if (fs.existsSync(CONFIG_PATH)) {
357
+ if (fs.existsSync(getConfigPath())) {
356
358
  savedConfig = loadConfig();
357
359
  }
358
360
 
359
361
  // Clean up any existing test files
360
- if (fs.existsSync(CONFIG_PATH)) {
361
- fs.unlinkSync(CONFIG_PATH);
362
+ if (fs.existsSync(getConfigPath())) {
363
+ fs.unlinkSync(getConfigPath());
362
364
  }
363
- const backupPath = CONFIG_PATH + '.bak';
365
+ const backupPath = getConfigPath() + '.bak';
364
366
  if (fs.existsSync(backupPath)) {
365
367
  fs.unlinkSync(backupPath);
366
368
  }
367
369
 
370
+ // Ensure the config directory exists
371
+ if (!fs.existsSync(getHomeDir())) {
372
+ fs.mkdirSync(getHomeDir(), { recursive: true });
373
+ }
374
+
368
375
  // Create empty config file
369
- fs.writeFileSync(CONFIG_PATH, '');
376
+ fs.writeFileSync(getConfigPath(), '');
370
377
 
371
378
  // loadConfig calls process.exit(1) on error, so we need to mock it
372
379
  // to prevent the test runner from dying
@@ -390,8 +397,8 @@ test('saveConfig with empty config file', () => {
390
397
  }
391
398
 
392
399
  // Clean up
393
- if (fs.existsSync(CONFIG_PATH)) {
394
- fs.unlinkSync(CONFIG_PATH);
400
+ if (fs.existsSync(getConfigPath())) {
401
+ fs.unlinkSync(getConfigPath());
395
402
  }
396
403
  if (fs.existsSync(backupPath)) {
397
404
  fs.unlinkSync(backupPath);
@@ -403,7 +410,7 @@ test('saveConfig with empty config file', () => {
403
410
  }
404
411
 
405
412
  if (fs.existsSync(testHome)) {
406
- fs.rmdirSync(testHome);
413
+ fs.rmSync(testHome, { recursive: true, force: true });
407
414
  }
408
415
  });
409
416
 
@@ -425,15 +432,15 @@ test('normalizeVariable preserves field order', () => {
425
432
  test('saveConfig handles multiple templates', () => {
426
433
  // Save the current config to restore later
427
434
  let savedConfig: PtConfig | null = null;
428
- if (fs.existsSync(CONFIG_PATH)) {
435
+ if (fs.existsSync(getConfigPath())) {
429
436
  savedConfig = loadConfig();
430
437
  }
431
438
 
432
439
  // Clean up any existing test files
433
- if (fs.existsSync(CONFIG_PATH)) {
434
- fs.unlinkSync(CONFIG_PATH);
440
+ if (fs.existsSync(getConfigPath())) {
441
+ fs.unlinkSync(getConfigPath());
435
442
  }
436
- const backupPath = CONFIG_PATH + '.bak';
443
+ const backupPath = getConfigPath() + '.bak';
437
444
  if (fs.existsSync(backupPath)) {
438
445
  fs.unlinkSync(backupPath);
439
446
  }
@@ -462,8 +469,8 @@ test('saveConfig handles multiple templates', () => {
462
469
  assert.strictEqual(loaded.templates['template2'].description, 'Template 2');
463
470
 
464
471
  // Clean up
465
- if (fs.existsSync(CONFIG_PATH)) {
466
- fs.unlinkSync(CONFIG_PATH);
472
+ if (fs.existsSync(getConfigPath())) {
473
+ fs.unlinkSync(getConfigPath());
467
474
  }
468
475
  if (fs.existsSync(backupPath)) {
469
476
  fs.unlinkSync(backupPath);
@@ -475,22 +482,22 @@ test('saveConfig handles multiple templates', () => {
475
482
  }
476
483
 
477
484
  if (fs.existsSync(testHome)) {
478
- fs.rmdirSync(testHome);
485
+ fs.rmSync(testHome, { recursive: true, force: true });
479
486
  }
480
487
  });
481
488
 
482
489
  test('saveConfig handles templates with variables', () => {
483
490
  // Save the current config to restore later
484
491
  let savedConfig: PtConfig | null = null;
485
- if (fs.existsSync(CONFIG_PATH)) {
492
+ if (fs.existsSync(getConfigPath())) {
486
493
  savedConfig = loadConfig();
487
494
  }
488
495
 
489
496
  // Clean up any existing test files
490
- if (fs.existsSync(CONFIG_PATH)) {
491
- fs.unlinkSync(CONFIG_PATH);
497
+ if (fs.existsSync(getConfigPath())) {
498
+ fs.unlinkSync(getConfigPath());
492
499
  }
493
- const backupPath = CONFIG_PATH + '.bak';
500
+ const backupPath = getConfigPath() + '.bak';
494
501
  if (fs.existsSync(backupPath)) {
495
502
  fs.unlinkSync(backupPath);
496
503
  }
@@ -528,8 +535,8 @@ test('saveConfig handles templates with variables', () => {
528
535
  assert.strictEqual(template.variables?.[0].required, true, 'Variable should be required');
529
536
 
530
537
  // Clean up
531
- if (fs.existsSync(CONFIG_PATH)) {
532
- fs.unlinkSync(CONFIG_PATH);
538
+ if (fs.existsSync(getConfigPath())) {
539
+ fs.unlinkSync(getConfigPath());
533
540
  }
534
541
  if (fs.existsSync(backupPath)) {
535
542
  fs.unlinkSync(backupPath);
@@ -541,26 +548,31 @@ test('saveConfig handles templates with variables', () => {
541
548
  }
542
549
 
543
550
  if (fs.existsSync(testHome)) {
544
- fs.rmdirSync(testHome);
551
+ fs.rmSync(testHome, { recursive: true, force: true });
545
552
  }
546
553
  });
547
554
 
548
555
  test('loadConfig handles migration from v2.0', () => {
549
556
  // Save the current config to restore later
550
557
  let savedConfig: PtConfig | null = null;
551
- if (fs.existsSync(CONFIG_PATH)) {
558
+ if (fs.existsSync(getConfigPath())) {
552
559
  savedConfig = loadConfig();
553
560
  }
554
561
 
555
562
  // Clean up any existing test files
556
- if (fs.existsSync(CONFIG_PATH)) {
557
- fs.unlinkSync(CONFIG_PATH);
563
+ if (fs.existsSync(getConfigPath())) {
564
+ fs.unlinkSync(getConfigPath());
558
565
  }
559
- const backupPath = CONFIG_PATH + '.bak';
566
+ const backupPath = getConfigPath() + '.bak';
560
567
  if (fs.existsSync(backupPath)) {
561
568
  fs.unlinkSync(backupPath);
562
569
  }
563
570
 
571
+ // Ensure the config directory exists
572
+ if (!fs.existsSync(getHomeDir())) {
573
+ fs.mkdirSync(getHomeDir(), { recursive: true });
574
+ }
575
+
564
576
  // Create a v2.0 config
565
577
  const v2Config = {
566
578
  version: '2.0',
@@ -573,7 +585,7 @@ test('loadConfig handles migration from v2.0', () => {
573
585
  }
574
586
  };
575
587
 
576
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(v2Config));
588
+ fs.writeFileSync(getConfigPath(), JSON.stringify(v2Config));
577
589
 
578
590
  // Load config - should migrate to v3.0
579
591
  const config = loadConfig();
@@ -584,8 +596,8 @@ test('loadConfig handles migration from v2.0', () => {
584
596
  assert.ok(!config.templates['test-template'].type, 'Type should be removed');
585
597
 
586
598
  // Clean up
587
- if (fs.existsSync(CONFIG_PATH)) {
588
- fs.unlinkSync(CONFIG_PATH);
599
+ if (fs.existsSync(getConfigPath())) {
600
+ fs.unlinkSync(getConfigPath());
589
601
  }
590
602
  if (fs.existsSync(backupPath)) {
591
603
  fs.unlinkSync(backupPath);
@@ -597,26 +609,31 @@ test('loadConfig handles migration from v2.0', () => {
597
609
  }
598
610
 
599
611
  if (fs.existsSync(testHome)) {
600
- fs.rmdirSync(testHome);
612
+ fs.rmSync(testHome, { recursive: true, force: true });
601
613
  }
602
614
  });
603
615
 
604
616
  test('loadConfig handles migration from v2.0 with global_post_config', () => {
605
617
  // Save the current config to restore later
606
618
  let savedConfig: PtConfig | null = null;
607
- if (fs.existsSync(CONFIG_PATH)) {
619
+ if (fs.existsSync(getConfigPath())) {
608
620
  savedConfig = loadConfig();
609
621
  }
610
622
 
611
623
  // Clean up any existing test files
612
- if (fs.existsSync(CONFIG_PATH)) {
613
- fs.unlinkSync(CONFIG_PATH);
624
+ if (fs.existsSync(getConfigPath())) {
625
+ fs.unlinkSync(getConfigPath());
614
626
  }
615
- const backupPath = CONFIG_PATH + '.bak';
627
+ const backupPath = getConfigPath() + '.bak';
616
628
  if (fs.existsSync(backupPath)) {
617
629
  fs.unlinkSync(backupPath);
618
630
  }
619
631
 
632
+ // Ensure the config directory exists
633
+ if (!fs.existsSync(getHomeDir())) {
634
+ fs.mkdirSync(getHomeDir(), { recursive: true });
635
+ }
636
+
620
637
  // Create a v2.0 config with global_post_config
621
638
  const v2Config = {
622
639
  version: '2.0',
@@ -629,7 +646,7 @@ test('loadConfig handles migration from v2.0 with global_post_config', () => {
629
646
  ]
630
647
  };
631
648
 
632
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(v2Config));
649
+ fs.writeFileSync(getConfigPath(), JSON.stringify(v2Config));
633
650
 
634
651
  // Load config - should migrate to v3.0
635
652
  const config = loadConfig();
@@ -645,8 +662,8 @@ test('loadConfig handles migration from v2.0 with global_post_config', () => {
645
662
  assert.ok(!config.global_post_config, 'global_post_config should be removed');
646
663
 
647
664
  // Clean up
648
- if (fs.existsSync(CONFIG_PATH)) {
649
- fs.unlinkSync(CONFIG_PATH);
665
+ if (fs.existsSync(getConfigPath())) {
666
+ fs.unlinkSync(getConfigPath());
650
667
  }
651
668
  if (fs.existsSync(backupPath)) {
652
669
  fs.unlinkSync(backupPath);
@@ -658,26 +675,31 @@ test('loadConfig handles migration from v2.0 with global_post_config', () => {
658
675
  }
659
676
 
660
677
  if (fs.existsSync(testHome)) {
661
- fs.rmdirSync(testHome);
678
+ fs.rmSync(testHome, { recursive: true, force: true });
662
679
  }
663
680
  });
664
681
 
665
682
  test('loadConfig handles migration from v2.0 with variables', () => {
666
683
  // Save the current config to restore later
667
684
  let savedConfig: PtConfig | null = null;
668
- if (fs.existsSync(CONFIG_PATH)) {
685
+ if (fs.existsSync(getConfigPath())) {
669
686
  savedConfig = loadConfig();
670
687
  }
671
688
 
672
689
  // Clean up any existing test files
673
- if (fs.existsSync(CONFIG_PATH)) {
674
- fs.unlinkSync(CONFIG_PATH);
690
+ if (fs.existsSync(getConfigPath())) {
691
+ fs.unlinkSync(getConfigPath());
675
692
  }
676
- const backupPath = CONFIG_PATH + '.bak';
693
+ const backupPath = getConfigPath() + '.bak';
677
694
  if (fs.existsSync(backupPath)) {
678
695
  fs.unlinkSync(backupPath);
679
696
  }
680
697
 
698
+ // Ensure the config directory exists
699
+ if (!fs.existsSync(getHomeDir())) {
700
+ fs.mkdirSync(getHomeDir(), { recursive: true });
701
+ }
702
+
681
703
  // Create a v2.0 config with variables as Record<string, string>
682
704
  const v2Config = {
683
705
  version: '2.0',
@@ -688,7 +710,7 @@ test('loadConfig handles migration from v2.0 with variables', () => {
688
710
  }
689
711
  };
690
712
 
691
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(v2Config));
713
+ fs.writeFileSync(getConfigPath(), JSON.stringify(v2Config));
692
714
 
693
715
  // Load config - should migrate to v3.0
694
716
  const config = loadConfig();
@@ -702,8 +724,8 @@ test('loadConfig handles migration from v2.0 with variables', () => {
702
724
  assert.strictEqual(config.variables[1].default, 'web', 'Second variable default should be web');
703
725
 
704
726
  // Clean up
705
- if (fs.existsSync(CONFIG_PATH)) {
706
- fs.unlinkSync(CONFIG_PATH);
727
+ if (fs.existsSync(getConfigPath())) {
728
+ fs.unlinkSync(getConfigPath());
707
729
  }
708
730
  if (fs.existsSync(backupPath)) {
709
731
  fs.unlinkSync(backupPath);
@@ -715,7 +737,7 @@ test('loadConfig handles migration from v2.0 with variables', () => {
715
737
  }
716
738
 
717
739
  if (fs.existsSync(testHome)) {
718
- fs.rmdirSync(testHome);
740
+ fs.rmSync(testHome, { recursive: true, force: true });
719
741
  }
720
742
  });
721
743
 
@@ -733,7 +755,7 @@ test('loadConfig handles existing config file', () => {
733
755
  test('saveConfig preserves existing config when not deleting', () => {
734
756
  // Save the current config to restore later
735
757
  let savedConfig: PtConfig | null = null;
736
- if (fs.existsSync(CONFIG_PATH)) {
758
+ if (fs.existsSync(getConfigPath())) {
737
759
  savedConfig = loadConfig();
738
760
  }
739
761
 
@@ -760,4 +782,12 @@ test('saveConfig preserves existing config when not deleting', () => {
760
782
  if (savedConfig) {
761
783
  saveConfig(savedConfig);
762
784
  }
785
+ });
786
+
787
+ // Cleanup: restore original os.homedir function and clean up test directory
788
+ after(() => {
789
+ // Clean up test home directory if it exists
790
+ if (fs.existsSync(testHome)) {
791
+ fs.rmSync(testHome, { recursive: true, force: true });
792
+ }
763
793
  });
@@ -8,7 +8,7 @@ const testHome = path.join(process.cwd(), '.test-home-init');
8
8
  process.env.HOME = testHome;
9
9
 
10
10
  import { init } from '../src/commands/initCommand.js';
11
- import { loadConfig, saveConfig, PtConfig, CONFIG_PATH } from '../src/config.js';
11
+ import { loadConfig, saveConfig, PtConfig, getConfigPath } from '../src/config.js';
12
12
 
13
13
  // Helper to clean up test directories
14
14
  function cleanup(...paths: string[]) {
@@ -9,7 +9,7 @@ const testHome = path.join(process.cwd(), '.test-home-learn');
9
9
  process.env.HOME = testHome;
10
10
 
11
11
  import { learn } from '../src/commands/learnCommand.js';
12
- import { loadConfig, saveConfig, PtConfig, CONFIG_PATH } from '../src/config.js';
12
+ import { loadConfig, saveConfig, PtConfig, getConfigPath } from '../src/config.js';
13
13
 
14
14
  // ---------------------------------------------------------------------------
15
15
  // Helpers
@@ -31,8 +31,8 @@ function rmrf(p: string) {
31
31
 
32
32
  /** Ensure a clean test config state (no leftover config files). */
33
33
  function cleanConfig() {
34
- if (fs.existsSync(CONFIG_PATH)) fs.unlinkSync(CONFIG_PATH);
35
- const bak = CONFIG_PATH + '.bak';
34
+ if (fs.existsSync(getConfigPath())) fs.unlinkSync(getConfigPath());
35
+ const bak = getConfigPath() + '.bak';
36
36
  if (fs.existsSync(bak)) fs.unlinkSync(bak);
37
37
  }
38
38