@sprucelabs/spruce-skill-utils 34.0.5 → 34.0.7

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
@@ -1,24 +1,269 @@
1
- <img src="https://raw.githubusercontent.com/sprucelabsai/spruce-skill-utils/master/docs/images/hero.jpg">
2
-
3
- <h1 align="center">
4
- Spruce XP Documentation
5
- </h1>
6
- <h3 align="center">Spruce XP is an Experience Platform built to create unforgettable experiences and long-lasting relationships.
7
- </h3>
8
- <p align="center">
9
- <img align="center" width="80%" src="https://raw.githubusercontent.com/sprucelabsai/spruce-skill-utils/master/docs/images/bullets.jpg">
10
- </p>
11
- <img src="https://raw.githubusercontent.com/sprucelabsai/spruce-skill-utils/master/docs/images/spacer.png">
12
- <br />
13
- <p align="center">
14
- <img align="center" width="80%" src="https://raw.githubusercontent.com/sprucelabsai/spruce-skill-utils/master/docs/images/sprucebot-message.png">
15
- </p>
16
-
17
- <br />
18
- <p align="center">
19
- <a href="https://developer.spruce.ai/#/"><img width="250" src="https://raw.githubusercontent.com/sprucelabsai/spruce-skill-utils/master/docs/images/read-full-docs.png" /></a>
20
- </p>
21
-
22
- ### Dependencies
23
-
24
- [Arkit diagram here](docs/dependencies.md).
1
+ # Spruce Skill Utils
2
+
3
+ Utilities and services used across Spruce skills and tooling. Exports are available from `src/index.ts`.
4
+
5
+ ## Utilities
6
+
7
+ ### `buildLog` (Logging)
8
+ Structured logger with prefixes, transports, time decorations, log gating, and history tracking.
9
+
10
+ #### Quick start
11
+ ```ts
12
+ import buildLog from '@sprucelabs/spruce-skill-utils'
13
+
14
+ const log = buildLog('APP', { useColors: false })
15
+ log.info('booting')
16
+ log.warn('cache miss', { key: 'users:1' })
17
+ log.error('boom', new Error('Something failed'))
18
+ ```
19
+
20
+ #### Prefixes and hierarchy
21
+ Prefixes are chained via `buildLog`. Returned strings include the prefixes, and transports receive the prefix as a separate first argument.
22
+ ```ts
23
+ const root = buildLog('ROOT', { useColors: false })
24
+
25
+ root.info('message')
26
+
27
+ const child = root.buildLog('CHILD')
28
+ child.error('an error occurred')
29
+ ```
30
+ Outputs:
31
+ 1. `(INFO) ROOT :: message`
32
+ 2. `(ERROR) ROOT :: CHILD :: an error occurred`
33
+
34
+ #### Maximum prefix length
35
+ Use `MAXIMUM_LOG_PREFIXES_LENGTH` to limit how many prefix segments are included.
36
+ ```ts
37
+ process.env.MAXIMUM_LOG_PREFIXES_LENGTH = '2'
38
+
39
+ const log = buildLog('ONE')
40
+ .buildLog('TWO')
41
+ .buildLog('THREE')
42
+
43
+ log.info('value')
44
+ ```
45
+ Transport receives: `TWO :: THREE :: value`
46
+
47
+ Set to `0` to suppress prefixes entirely.
48
+
49
+ #### Log level gating
50
+ Control which levels are emitted using `LOG_LEVEL`.
51
+ ```ts
52
+ process.env.LOG_LEVEL = 'warn'
53
+
54
+ const log = buildLog('APP', { useColors: false })
55
+
56
+ log.info('info message')
57
+ log.warn('warn message')
58
+ log.error('error message')
59
+ ```
60
+ With `LOG_LEVEL=warn`, only `warn` and `error` are emitted.
61
+
62
+ #### Time decorations
63
+ By default, log output includes a timestamp and a time-since-last-log delta. Disable with env flags.
64
+ ```ts
65
+ process.env.SHOULD_LOG_TIME = 'false'
66
+ process.env.SHOULD_LOG_TIME_DELTAS = 'false'
67
+
68
+ const log = buildLog('TIMESTAMPS', { useColors: false })
69
+ log.info('first')
70
+ ```
71
+
72
+ #### Transports
73
+ You can route logs per level to custom transports. Transports receive prefix and args only (no `(INFO)` or timestamps).
74
+ ```ts
75
+ let infoMessage = ''
76
+ let errorMessage = ''
77
+
78
+ const log = buildLog('TEST', {
79
+ useColors: false,
80
+ transportsByLevel: {
81
+ INFO: (...parts) => { infoMessage = parts.join(' ') },
82
+ ERROR: (...parts) => { errorMessage = parts.join(' ') },
83
+ },
84
+ })
85
+
86
+ log.info('go team')
87
+ log.error('error me')
88
+ ```
89
+ Captured values:
90
+ 1. `infoMessage` becomes `TEST :: go team`
91
+ 2. `errorMessage` becomes `TEST :: error me`
92
+
93
+ You can also provide multiple transports per level:
94
+ ```ts
95
+ const log = buildLog('MULTI', {
96
+ transportsByLevel: {
97
+ INFO: [
98
+ (...parts) => console.log('one', parts.join(' ')),
99
+ (...parts) => console.log('two', parts.join(' ')),
100
+ ],
101
+ },
102
+ })
103
+ ```
104
+
105
+ #### Custom log override (rare)
106
+ If you provide `log`, it overrides the output target entirely (no console/stderr). This is typically only useful in tests.
107
+ ```ts
108
+ const log = buildLog('NOOP', { useColors: false, log: () => {} })
109
+
110
+ log.info('this returns a string, but nothing is emitted')
111
+ ```
112
+
113
+ #### Colors and TTY
114
+ Colors are enabled only when `stdout.isTTY` is true and `useColors !== false`.
115
+ ```ts
116
+ process.stdout.isTTY = false
117
+ const log = buildLog('TTY', {})
118
+ log.info('go team')
119
+ ```
120
+ Output: `(INFO) TTY :: go team`
121
+
122
+ #### Prefix-based logging control
123
+ If `isMainModule()` is false, logging is disabled unless `SPRUCE_LOGS` includes the logger’s prefix.
124
+ ```ts
125
+ process.env.SPRUCE_LOGS = 'Billing,Auth'
126
+ const log = buildLog('Auth', { useColors: false })
127
+ log.info('allowed')
128
+ ```
129
+
130
+ #### History tracking
131
+ Logging history is shared across all logger instances.
132
+ ```ts
133
+ const log = buildLog()
134
+ log.startTrackingHistory(2)
135
+
136
+ log.info('one')
137
+ log.info('two')
138
+ log.info('three')
139
+
140
+ log.getHistory()
141
+ ```
142
+ History contains: `["two", "three"]`
143
+
144
+ #### API
145
+ 1. `buildLog(prefix?: string, options?: LogOptions): Log`
146
+ 2. `Logger` class (implements `Log`)
147
+ 3. `testLog` and `stubLog`
148
+
149
+ ### `testLog`
150
+ Logger preconfigured to write to `stderr`. Useful in tests.
151
+
152
+ ### `stubLog`
153
+ Logger preconfigured to do nothing. Colors disabled.
154
+
155
+ ### `diskUtil`
156
+ Filesystem helper utilities.
157
+
158
+ Key behaviors
159
+ 1. `resolvePath` resolves relative paths to a cwd and expands `#spruce` to `.spruce`.
160
+ 2. `resolveFile` searches for bare, `.js`, and `.ts` files in order.
161
+ 3. `hasFileChanged` tracks file changes using `.change_cache` and a `.gitignore` in that cache directory.
162
+ 4. `deleteEmptyDirs` removes empty directories recursively and validates inputs.
163
+ 5. `detectProjectLanguage` infers `ts`, `js`, `go`, or `unknown`.
164
+
165
+ ### `versionUtil`
166
+ Versioned-path helper that works with `vYYYY_MM_DD` directories and `{{@latest}}` tokens.
167
+
168
+ Key behaviors
169
+ 1. `resolvePath` replaces `{{@latest}}` with the latest version directory.
170
+ 2. `resolveNewLatestPath` creates a new latest path using today’s date.
171
+ 3. `getAllVersions` returns sorted version metadata.
172
+ 4. `latestVersionAtPath` throws if no versioning exists.
173
+
174
+ ### `namesUtil`
175
+ String and naming helpers.
176
+
177
+ API
178
+ 1. `toFileNameWithoutExtension`
179
+ 2. `toCamel`
180
+ 3. `toPascal`
181
+ 4. `toConst`
182
+ 5. `toPlural`
183
+ 6. `toSingular`
184
+ 7. `toKebab`
185
+ 8. `toSnake`
186
+
187
+ ### `addonUtil`
188
+ Discovers and loads `*.addon.[t|j]s` files.
189
+
190
+ Key behaviors
191
+ 1. `import` loads all addons under a path and awaits results.
192
+ 2. `importSync` loads all addons synchronously.
193
+ 3. If a module has a default export function, it is invoked with `options`.
194
+
195
+ ### `pluginUtil`
196
+ Discovers and loads `*.plugin.[t|j]s` files.
197
+
198
+ Key behaviors
199
+ 1. `import` loads plugins asynchronously and returns their results.
200
+ 2. `importSync` loads plugins synchronously and validates inputs.
201
+ 3. Plugins must export a default function or an error is thrown.
202
+
203
+ ### `randomUtil`
204
+ Random selection helper.
205
+
206
+ API
207
+ 1. `rand<T>(possibilities: T[]): T`
208
+
209
+ ### `joinIntoSentence`
210
+ Joins words into a human-readable sentence with an `&` before the last item.
211
+
212
+ Example
213
+ 1. `['hey', 'there']` → `hey & there`
214
+ 2. `['a', 'b', 'c']` → `a, b & c`
215
+
216
+ ### `slug`
217
+ Creates a lowercase, dash-separated slug from an input string.
218
+
219
+ ### `cloneDeep`
220
+ Deprecated wrapper around `@sprucelabs/schema` `cloneDeep`. Prefer importing directly from `@sprucelabs/schema`.
221
+
222
+ ### `isEqual`
223
+ Deep equality check with special handling for `Date` and with `null`/`undefined` field removal.
224
+
225
+ ## Renderers
226
+
227
+ ### `locationRenderer`
228
+ Renders an `AddressFieldValue` into a single, trimmed line with missing fields omitted.
229
+
230
+ ## Services
231
+
232
+ ### `SettingsService`
233
+ Persists feature flags and settings to a JSON file under `.spruce`.
234
+
235
+ Key behaviors
236
+ 1. Supports `markAsInstalled` and `markAsPermanentlySkipped`.
237
+ 2. Supports nested `get`, `set`, and `unset`.
238
+ 3. Uses `.spruce/settings.json` for JS/TS projects and `.spruce/settings.json` in the repo root for Go projects.
239
+ 4. Supports custom settings filename via `setFile`.
240
+
241
+ ### `EnvService`
242
+ Reads and writes a `.env` file in the current working directory.
243
+
244
+ Key behaviors
245
+ 1. `set` writes literals with quoting and escapes newlines.
246
+ 2. `get` coerces booleans and integers.
247
+ 3. `unset` removes keys from both the file and `process.env`.
248
+
249
+ ### `PkgService`
250
+ Reads and writes `package.json`.
251
+
252
+ Key behaviors
253
+ 1. `get`, `set`, and `unset` for package fields.
254
+ 2. `doesExist` checks for `package.json`.
255
+ 3. `isInstalled` checks dependencies and devDependencies.
256
+ 4. `deleteLockFile` removes `package-lock.json` and `yarn.lock`.
257
+ 5. `buildPackageName` formats `name@version`.
258
+
259
+ ### `AuthService`
260
+ Manages local auth state for skills.
261
+
262
+ Key behaviors
263
+ 1. `Auth(cwd)` validates a `package.json` exists and returns a service instance.
264
+ 2. Persists a logged-in person to `~/.spruce/person.json`.
265
+ 3. Reads and updates current skill credentials from `.env` and `package.json`.
266
+ 4. Normalizes and validates person data against a schema.
267
+
268
+ ## Types and constants
269
+ Exports include `Skill`, `SkillAuth`, `PersonWithToken`, `EnvValue`, and constants such as `HASH_SPRUCE_DIR`, `LATEST_HANDLEBARS`, and `CORE_SCHEMA_VERSION`.
@@ -56,7 +56,7 @@ export default class PkgService {
56
56
  return (!!((_a = contents.dependencies) === null || _a === void 0 ? void 0 : _a[pkg]) ||
57
57
  !!((_b = contents.devDependencies) === null || _b === void 0 ? void 0 : _b[pkg]));
58
58
  }
59
- catch (e) {
59
+ catch (_c) {
60
60
  return false;
61
61
  }
62
62
  }
@@ -243,14 +243,14 @@ const diskUtil = {
243
243
  try {
244
244
  fileStat = fsUtil.statSync(file);
245
245
  }
246
- catch (err) {
246
+ catch (_a) {
247
247
  return true;
248
248
  }
249
249
  let cacheFileStat;
250
250
  try {
251
251
  cacheFileStat = fsUtil.statSync(cacheFile);
252
252
  }
253
- catch (err) {
253
+ catch (_b) {
254
254
  //@ts-ignore
255
255
  }
256
256
  if (!cacheFileStat || cacheFileStat.ctimeMs < fileStat.ctimeMs) {
@@ -60,7 +60,7 @@ class PkgService {
60
60
  return (!!contents.dependencies?.[pkg] ||
61
61
  !!contents.devDependencies?.[pkg]);
62
62
  }
63
- catch (e) {
63
+ catch {
64
64
  return false;
65
65
  }
66
66
  }
@@ -237,14 +237,14 @@ const diskUtil = {
237
237
  try {
238
238
  fileStat = fs_extra_1.default.statSync(file);
239
239
  }
240
- catch (err) {
240
+ catch {
241
241
  return true;
242
242
  }
243
243
  let cacheFileStat;
244
244
  try {
245
245
  cacheFileStat = fs_extra_1.default.statSync(cacheFile);
246
246
  }
247
- catch (err) {
247
+ catch {
248
248
  //@ts-ignore
249
249
  }
250
250
  if (!cacheFileStat || cacheFileStat.ctimeMs < fileStat.ctimeMs) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "34.0.5",
6
+ "version": "34.0.7",
7
7
  "skill": {
8
8
  "namespace": "skill-utils",
9
9
  "upgradeIgnoreList": [
@@ -69,29 +69,29 @@
69
69
  "test.browser": "support/test-browser.sh"
70
70
  },
71
71
  "dependencies": {
72
- "@sprucelabs/globby": "^2.0.515",
73
- "@sprucelabs/schema": "^33.1.4",
72
+ "@sprucelabs/globby": "^2.0.516",
73
+ "@sprucelabs/schema": "^33.1.5",
74
74
  "chalk": "^4.1.2",
75
- "dotenv": "^17.2.3",
75
+ "dotenv": "^17.3.1",
76
76
  "fs-extra": "^11.3.3",
77
77
  "inflection": "^3.0.2",
78
78
  "lodash": "^4.17.23"
79
79
  },
80
80
  "devDependencies": {
81
- "@sprucelabs/esm-postbuild": "^9.0.14",
82
- "@sprucelabs/jest-json-reporter": "^10.0.20",
83
- "@sprucelabs/jest-sheets-reporter": "^5.0.17",
84
- "@sprucelabs/resolve-path-aliases": "^4.0.14",
81
+ "@sprucelabs/esm-postbuild": "^9.0.15",
82
+ "@sprucelabs/jest-json-reporter": "^10.0.21",
83
+ "@sprucelabs/jest-sheets-reporter": "^5.0.18",
84
+ "@sprucelabs/resolve-path-aliases": "^4.0.15",
85
85
  "@sprucelabs/semantic-release": "^6.0.0",
86
- "@sprucelabs/test": "^11.1.2",
87
- "@sprucelabs/test-utils": "^7.2.5",
86
+ "@sprucelabs/test": "^11.1.3",
87
+ "@sprucelabs/test-utils": "^7.2.6",
88
88
  "@types/fs-extra": "^11.0.4",
89
89
  "@types/inflection": "^2.0.0",
90
90
  "@types/lodash": "^4.17.23",
91
- "@types/node": "^25.0.10",
91
+ "@types/node": "^25.2.3",
92
92
  "chokidar-cli": "^3.0.0",
93
- "eslint": "^9.39.2",
94
- "eslint-config-spruce": "^11.2.26",
93
+ "eslint": "^10.0.0",
94
+ "eslint-config-spruce": "^11.2.30",
95
95
  "jest": "^30.2.0",
96
96
  "jest-circus": "^30.2.0",
97
97
  "prettier": "^3.8.1",