log-llm-config 1.0.31 → 1.0.32

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.
@@ -5,7 +5,7 @@ import { readJSONFile, readMCPConfig, readMarkdownFile, readInstalledExtensions
5
5
  import { getExtensionsCachePath, getExtensionsCacheInstalledSuffix, getVscdbPath } from '../paths/path_constants_helpers.js';
6
6
  import { resolvePatternToTargets, normalizePathSkipPrefixes } from '../paths/pattern_resolver.js';
7
7
  import { enrichRawFromRecipe } from './enrichment_helpers.js';
8
- import { collectDirectoryEntries } from './directory_collector.js';
8
+ import { collectDirectoryEntries, collectDirectoryMetadata } from './directory_collector.js';
9
9
  import { collectVscdbEntries } from '../readers/vscdb_config_builder.js';
10
10
  import { pushDerivedFilesFromRecipe } from './openclaw_helpers.js';
11
11
  function buildCollectionContext(patterns, projectRoot, home, homeRecurseSkipDirs = [], absolutePathPrefixes = [], mcpToolGlobSpec = null, clientPathConstants = null, pathResolutionSpecs = null) {
@@ -124,7 +124,14 @@ function collectConfigFilesFromPatterns(patterns, projectRoot, onProgress, optio
124
124
  onProgress(`scanning file_type=${t.file_type}`);
125
125
  }
126
126
  if (t.isDirectory) {
127
- configFiles.push(...collectDirectoryEntries(t));
127
+ if (metadataOnlyFileTypes.has(t.file_type) || t.collect_style === 'metadata') {
128
+ const entry = collectDirectoryMetadata(t);
129
+ if (entry)
130
+ configFiles.push(entry);
131
+ }
132
+ else {
133
+ configFiles.push(...collectDirectoryEntries(t));
134
+ }
128
135
  continue;
129
136
  }
130
137
  if (extensionsInstalledSuffix && t.path.includes(extensionsInstalledSuffix)) {
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync } from 'node:fs';
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { readJSONFile, readMarkdownFile } from '../readers/file_readers.js';
4
4
  function collectSubdirMdFiles(dirPath, fileType, mdFilename, source) {
@@ -50,4 +50,47 @@ function collectDirectoryEntries(t) {
50
50
  }
51
51
  return results;
52
52
  }
53
- export { collectDirectoryEntries };
53
+ /**
54
+ * For metadata-style DIR targets: scan files matching dir_glob, return one entry
55
+ * with the highest mtime found. Falls back to the directory's own mtime if no files match.
56
+ */
57
+ function collectDirectoryMetadata(t) {
58
+ try {
59
+ const dirStat = statSync(t.path);
60
+ let bestMtime = dirStat.mtime;
61
+ let bestPath = t.path;
62
+ const glob = t.dir_glob ?? '*';
63
+ const matchName = (name) => {
64
+ if (glob === '*')
65
+ return true;
66
+ if (glob.startsWith('*.'))
67
+ return name.endsWith(glob.slice(1));
68
+ return name === glob;
69
+ };
70
+ try {
71
+ for (const entry of readdirSync(t.path, { withFileTypes: true })) {
72
+ if (!entry.isFile() || !matchName(entry.name))
73
+ continue;
74
+ try {
75
+ const fileStat = statSync(join(t.path, entry.name));
76
+ if (fileStat.mtime > bestMtime) {
77
+ bestMtime = fileStat.mtime;
78
+ bestPath = join(t.path, entry.name);
79
+ }
80
+ }
81
+ catch { /* ignore unreadable files */ }
82
+ }
83
+ }
84
+ catch { /* ignore unreadable dir */ }
85
+ return {
86
+ file_type: t.file_type,
87
+ file_path: bestPath,
88
+ raw_content: { filename: bestPath, last_modified: bestMtime.toISOString(), source: 'file_metadata' },
89
+ collect_style: 'metadata',
90
+ };
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ export { collectDirectoryEntries, collectDirectoryMetadata };
@@ -60,17 +60,27 @@ function expandGlobPathPattern(pathPattern, fileType, home, projectRoot, content
60
60
  return targets;
61
61
  const isDir = norm.endsWith('/');
62
62
  const [before, after] = norm.split('*');
63
- const beforeNorm = before.replace(/\/+$/, '');
64
63
  const afterNorm = after.replace(/^\/+/, '');
64
+ // If `before` doesn't end with '/', the * is mid-segment (e.g. "extensions/saoudrizwan.claude-dev*/" →
65
+ // parent="extensions/", namePrefix="saoudrizwan.claude-dev"). Split on last '/' to get the real base dir.
66
+ let parentPart = before.replace(/\/+$/, '');
67
+ let namePrefix = '';
68
+ if (!before.endsWith('/')) {
69
+ const lastSlash = parentPart.lastIndexOf('/');
70
+ if (lastSlash !== -1) {
71
+ namePrefix = parentPart.slice(lastSlash + 1);
72
+ parentPart = parentPart.slice(0, lastSlash);
73
+ }
74
+ }
65
75
  let basePath;
66
- if (beforeNorm.startsWith('~/')) {
67
- basePath = join(home, beforeNorm.slice(2));
76
+ if (parentPart.startsWith('~/')) {
77
+ basePath = join(home, parentPart.slice(2));
68
78
  }
69
- else if (beforeNorm.startsWith('/') && absolutePathPrefixes.some((prefix) => beforeNorm.startsWith(prefix))) {
70
- basePath = beforeNorm;
79
+ else if (parentPart.startsWith('/') && absolutePathPrefixes.some((prefix) => parentPart.startsWith(prefix))) {
80
+ basePath = parentPart;
71
81
  }
72
82
  else {
73
- basePath = join(projectRoot, beforeNorm.startsWith('/') ? beforeNorm.slice(1) : beforeNorm);
83
+ basePath = join(projectRoot, parentPart.startsWith('/') ? parentPart.slice(1) : parentPart);
74
84
  }
75
85
  if (!existsSync(basePath))
76
86
  return targets;
@@ -78,6 +88,8 @@ function expandGlobPathPattern(pathPattern, fileType, home, projectRoot, content
78
88
  for (const entry of readdirSync(basePath, { withFileTypes: true })) {
79
89
  if (!entry.isDirectory())
80
90
  continue;
91
+ if (namePrefix && !entry.name.startsWith(namePrefix))
92
+ continue;
81
93
  const resolvedPath = join(basePath, entry.name, afterNorm);
82
94
  if (!existsSync(resolvedPath))
83
95
  continue;
@@ -1,5 +1,6 @@
1
1
  import crypto from 'node:crypto';
2
- import canonicalize from 'canonicalize';
2
+ import { createRequire } from 'node:module';
3
+ const canonicalize = createRequire(import.meta.url)('canonicalize');
3
4
  /** RFC 8785 canonical JSON — must match server's rfc8785.dumps() exactly. */
4
5
  function canonicalizePayload(payload) {
5
6
  const out = canonicalize(payload);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-llm-config",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {