ppcos 1.0.4 → 1.0.6

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.
@@ -218,6 +218,28 @@ function migrateConfigManagedTypes(manifest) {
218
218
  }
219
219
  }
220
220
 
221
+ /**
222
+ * One-time migration: remove leaked data files from skill tmp/ directories.
223
+ * Data files were accidentally included in .claude-base.zip and distributed
224
+ * to clients. This removes them and cleans up the manifest.
225
+ */
226
+ function migrateTmpDataFiles(clientDir, manifest) {
227
+ const keepFiles = new Set(['.gitkeep', '.gitignore']);
228
+ let removed = 0;
229
+ for (const [path] of Object.entries(manifest.managedFiles)) {
230
+ if (!/\/tmp\//.test(path)) continue;
231
+ const filename = path.split('/').pop();
232
+ if (keepFiles.has(filename)) continue;
233
+ const fullPath = join(clientDir, path);
234
+ if (existsSync(fullPath)) unlinkSync(fullPath);
235
+ delete manifest.managedFiles[path];
236
+ removed++;
237
+ }
238
+ if (removed > 0) {
239
+ logger.info(` Removed ${removed} tmp data file${removed !== 1 ? 's' : ''} from tmp/`);
240
+ }
241
+ }
242
+
221
243
  /**
222
244
  * Find all client directories with .managed.json
223
245
  * @returns {string[]} Array of client names
@@ -383,6 +405,7 @@ async function updateClient(clientName, basePathOrOptions = {}, options = {}) {
383
405
  migrateSettingsHooks(clientDir);
384
406
  migrateManifestKeys(manifest);
385
407
  migrateConfigManagedTypes(manifest);
408
+ migrateTmpDataFiles(clientDir, manifest);
386
409
 
387
410
  // Check if already up to date
388
411
  if (currentVersion === packageVersion) {
@@ -2,11 +2,16 @@
2
2
  * File system helper utilities
3
3
  */
4
4
 
5
- import { readdir, stat, mkdir, copyFile, access } from 'node:fs/promises';
6
- import { readdirSync, statSync, mkdirSync, copyFileSync, existsSync } from 'node:fs';
5
+ import { readdir, stat, mkdir, copyFile, access, chmod } from 'node:fs/promises';
6
+ import { readdirSync, statSync, mkdirSync, copyFileSync, existsSync, chmodSync } from 'node:fs';
7
7
  import { join, relative, dirname } from 'node:path';
8
8
  import { constants } from 'node:fs';
9
9
 
10
+ // Directories to skip during template file enumeration.
11
+ // tmp/ dirs hold generated runtime data (analysis output, caches)
12
+ // that must never be distributed to clients.
13
+ const EXCLUDED_DIR_NAMES = new Set(['tmp']);
14
+
10
15
  /**
11
16
  * Check if a file exists (async)
12
17
  * @param {string} filePath - Path to check
@@ -36,14 +41,15 @@ export function fileExistsSync(filePath) {
36
41
  * @param {string} [baseDir] - Base directory for relative paths
37
42
  * @returns {Promise<string[]>} Array of relative file paths
38
43
  */
39
- export async function getAllFiles(dir, baseDir = dir) {
44
+ export async function getAllFiles(dir, baseDir = dir, excludeDirs = EXCLUDED_DIR_NAMES) {
40
45
  const files = [];
41
46
  const entries = await readdir(dir, { withFileTypes: true });
42
47
 
43
48
  for (const entry of entries) {
44
49
  const fullPath = join(dir, entry.name);
45
50
  if (entry.isDirectory()) {
46
- files.push(...await getAllFiles(fullPath, baseDir));
51
+ if (excludeDirs.has(entry.name)) continue;
52
+ files.push(...await getAllFiles(fullPath, baseDir, excludeDirs));
47
53
  } else {
48
54
  files.push(relative(baseDir, fullPath).replace(/\\/g, '/'));
49
55
  }
@@ -58,14 +64,15 @@ export async function getAllFiles(dir, baseDir = dir) {
58
64
  * @param {string} [baseDir] - Base directory for relative paths
59
65
  * @returns {string[]} Array of relative file paths
60
66
  */
61
- export function getAllFilesSync(dir, baseDir = dir) {
67
+ export function getAllFilesSync(dir, baseDir = dir, excludeDirs = EXCLUDED_DIR_NAMES) {
62
68
  const files = [];
63
69
  const entries = readdirSync(dir, { withFileTypes: true });
64
70
 
65
71
  for (const entry of entries) {
66
72
  const fullPath = join(dir, entry.name);
67
73
  if (entry.isDirectory()) {
68
- files.push(...getAllFilesSync(fullPath, baseDir));
74
+ if (excludeDirs.has(entry.name)) continue;
75
+ files.push(...getAllFilesSync(fullPath, baseDir, excludeDirs));
69
76
  } else {
70
77
  files.push(relative(baseDir, fullPath).replace(/\\/g, '/'));
71
78
  }
@@ -83,6 +90,8 @@ export async function copyFileWithDirs(src, dest) {
83
90
  const destDir = dirname(dest);
84
91
  await mkdir(destDir, { recursive: true });
85
92
  await copyFile(src, dest);
93
+ const srcStats = await stat(src);
94
+ await chmod(dest, srcStats.mode);
86
95
  }
87
96
 
88
97
  /**
@@ -96,6 +105,8 @@ export function copyFileWithDirsSync(src, dest) {
96
105
  mkdirSync(destDir, { recursive: true });
97
106
  }
98
107
  copyFileSync(src, dest);
108
+ const srcStats = statSync(src);
109
+ chmodSync(dest, srcStats.mode);
99
110
  }
100
111
 
101
112
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ppcos",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "CLI tool to manage Google Ads AI workflow skills and agents for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {