@joshski/dust 0.1.51 → 0.1.53

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/dust.js CHANGED
@@ -3386,6 +3386,9 @@ function runBufferedProcess(spawnFn, command, commandArguments, cwd, shell, time
3386
3386
  timer = setTimeout(() => {
3387
3387
  resolved = true;
3388
3388
  proc.kill();
3389
+ proc.stdout?.destroy();
3390
+ proc.stderr?.destroy();
3391
+ proc.unref();
3389
3392
  resolve({
3390
3393
  exitCode: 1,
3391
3394
  output: chunks.join(""),
@@ -5131,4 +5134,10 @@ async function wireEntry(fsPrimitives, processPrimitives, consolePrimitives) {
5131
5134
  }
5132
5135
 
5133
5136
  // lib/cli/run.ts
5134
- await wireEntry({ existsSync, statSync: statSync2, readFile: readFile2, writeFile: writeFile2, mkdir: mkdir2, readdir: readdir2, chmod: chmod2 }, { argv: process.argv, cwd: () => process.cwd(), exit: process.exit }, { log: console.log, error: console.error });
5137
+ await wireEntry({ existsSync, statSync: statSync2, readFile: readFile2, writeFile: writeFile2, mkdir: mkdir2, readdir: readdir2, chmod: chmod2 }, {
5138
+ argv: process.argv,
5139
+ cwd: () => process.cwd(),
5140
+ exit: (code) => {
5141
+ process.exitCode = code;
5142
+ }
5143
+ }, { log: console.log, error: console.error });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Minimal debug logging framework.
3
+ *
4
+ * When the DEBUG environment variable is set, matching loggers write
5
+ * timestamped lines to `<cwd>/log/dust/<scope>.log`.
6
+ *
7
+ * The scope defaults to "debug" but can be changed via setLogScope()
8
+ * so that different commands (e.g. `loop`, `check`, `bucket`) write
9
+ * to separate log files.
10
+ *
11
+ * DEBUG is a comma-separated list of match expressions. Each expression
12
+ * can contain `*` as a wildcard (matches any sequence of characters).
13
+ *
14
+ * Examples:
15
+ * DEBUG=* → matches all loggers
16
+ * DEBUG=dust.bucket,dust.loop → exact matches
17
+ * DEBUG=dust.bucket.* → matches dust.bucket.loop, dust.bucket.ws, etc.
18
+ * DEBUG=*loop → matches dust.cli.commands.loop, dust.bucket.loop, etc.
19
+ *
20
+ * Logger names follow the convention `dust.<path>` mirroring the directory
21
+ * structure under `lib/`. For example, `lib/bucket/repository-loop.ts` uses
22
+ * the logger name `dust.bucket.repository-loop`.
23
+ *
24
+ * No external dependencies.
25
+ */
26
+ import { type WriteFn } from './sink';
27
+ export { setLogScope } from './sink';
28
+ export type LogFn = (...messages: unknown[]) => void;
29
+ /**
30
+ * Create a named logger function. The returned function writes to
31
+ * `log/dust/<scope>.log` when the logger name matches the DEBUG patterns.
32
+ *
33
+ * @param name - Logger name, e.g. `dust.bucket.loop`
34
+ * @param write - Override the default file writer (for testing)
35
+ */
36
+ export declare function createLogger(name: string, write?: WriteFn): LogFn;
37
+ /**
38
+ * Check whether a logger name would be enabled under the current DEBUG value.
39
+ */
40
+ export declare function isEnabled(name: string): boolean;
41
+ /**
42
+ * Reset internal state (for testing only).
43
+ */
44
+ export declare function _reset(): void;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Pure pattern-matching logic for debug logger names.
3
+ *
4
+ * Parses a DEBUG-style string (comma-separated, `*` wildcards)
5
+ * and tests logger names against it. No side effects.
6
+ */
7
+ /**
8
+ * Parse a DEBUG expression string into an array of RegExp matchers.
9
+ * Returns an empty array when the input is empty or undefined.
10
+ */
11
+ export declare function parsePatterns(debug: string | undefined): RegExp[];
12
+ /**
13
+ * Test whether a logger name matches any of the compiled patterns.
14
+ */
15
+ export declare function matchesAny(name: string, patterns: RegExp[]): boolean;
16
+ /**
17
+ * Format a log line with ISO timestamp and logger name.
18
+ */
19
+ export declare function formatLine(name: string, messages: unknown[]): string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * File-based log sink — the imperative shell for debug logging.
3
+ *
4
+ * Lazily creates `<cwd>/log/dust/<scope>.log` and appends lines to it.
5
+ * The scope defaults to "debug" but can be changed via setLogScope()
6
+ * so that different commands write to separate log files.
7
+ */
8
+ export type WriteFn = (line: string) => void;
9
+ /**
10
+ * Set the log scope, which determines the output filename.
11
+ * Must be called before any logger writes (i.e. at command startup).
12
+ *
13
+ * For example, `setLogScope('loop')` writes to `log/dust/loop.log`.
14
+ */
15
+ export declare function setLogScope(name: string): void;
16
+ /**
17
+ * Write a line to the debug log file.
18
+ * Silently no-ops if the file cannot be opened.
19
+ */
20
+ export declare const writeToFile: WriteFn;
21
+ /**
22
+ * Reset sink state (for testing only).
23
+ */
24
+ export declare function _resetSink(): void;
@@ -0,0 +1,86 @@
1
+ // lib/logging/match.ts
2
+ function parsePatterns(debug) {
3
+ if (!debug)
4
+ return [];
5
+ const expressions = debug.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
6
+ return expressions.map((expr) => {
7
+ const escaped = expr.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
8
+ const pattern = escaped.replace(/\*/g, ".*");
9
+ return new RegExp(`^${pattern}$`);
10
+ });
11
+ }
12
+ function matchesAny(name, patterns) {
13
+ return patterns.some((re) => re.test(name));
14
+ }
15
+ function formatLine(name, messages) {
16
+ const text = messages.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
17
+ return `${new Date().toISOString()} [${name}] ${text}
18
+ `;
19
+ }
20
+
21
+ // lib/logging/sink.ts
22
+ import { appendFileSync, mkdirSync } from "node:fs";
23
+ import { join } from "node:path";
24
+ var logPath;
25
+ var ready = false;
26
+ var scope = process.env.DEBUG_LOG_SCOPE || "debug";
27
+ function ensureLogFile() {
28
+ if (ready)
29
+ return logPath;
30
+ ready = true;
31
+ const dir = join(process.cwd(), "log", "dust");
32
+ logPath = join(dir, `${scope}.log`);
33
+ try {
34
+ mkdirSync(dir, { recursive: true });
35
+ } catch {
36
+ logPath = undefined;
37
+ }
38
+ return logPath;
39
+ }
40
+ function setLogScope(name) {
41
+ scope = name;
42
+ process.env.DEBUG_LOG_SCOPE = name;
43
+ logPath = undefined;
44
+ ready = false;
45
+ }
46
+ var writeToFile = (line) => {
47
+ const path = ensureLogFile();
48
+ if (!path)
49
+ return;
50
+ try {
51
+ appendFileSync(path, line);
52
+ } catch {}
53
+ };
54
+
55
+ // lib/logging/index.ts
56
+ var patterns = null;
57
+ var initialized = false;
58
+ function init() {
59
+ if (initialized)
60
+ return;
61
+ initialized = true;
62
+ const parsed = parsePatterns(process.env.DEBUG);
63
+ patterns = parsed.length > 0 ? parsed : null;
64
+ }
65
+ function createLogger(name, write = writeToFile) {
66
+ return (...messages) => {
67
+ init();
68
+ if (!patterns || !matchesAny(name, patterns))
69
+ return;
70
+ write(formatLine(name, messages));
71
+ };
72
+ }
73
+ function isEnabled(name) {
74
+ init();
75
+ return patterns !== null && matchesAny(name, patterns);
76
+ }
77
+ function _reset() {
78
+ initialized = false;
79
+ patterns = null;
80
+ }
81
+ export {
82
+ setLogScope,
83
+ isEnabled,
84
+ createLogger,
85
+ _reset
86
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,6 +14,10 @@
14
14
  "import": "./dist/workflow-tasks.js",
15
15
  "types": "./dist/workflow-tasks.d.ts"
16
16
  },
17
+ "./logging": {
18
+ "import": "./dist/logging.js",
19
+ "types": "./dist/logging/index.d.ts"
20
+ },
17
21
  "./istanbul/minimal-reporter": "./lib/istanbul/minimal-reporter.cjs"
18
22
  },
19
23
  "files": [
@@ -34,7 +38,7 @@
34
38
  "author": "joshski",
35
39
  "license": "MIT",
36
40
  "scripts": {
37
- "build": "bun build lib/cli/run.ts --target node --outfile dist/dust.js && printf '%s\\n%s' '#!/usr/bin/env node' \"$(cat dist/dust.js)\" > dist/dust.js && bun build lib/workflow-tasks.ts --target node --outfile dist/workflow-tasks.js && bunx tsc --project tsconfig.build.json",
41
+ "build": "bun build lib/cli/run.ts --target node --outfile dist/dust.js && printf '%s\\n%s' '#!/usr/bin/env node' \"$(cat dist/dust.js)\" > dist/dust.js && bun build lib/workflow-tasks.ts --target node --outfile dist/workflow-tasks.js && bun build lib/logging/index.ts --target node --outfile dist/logging.js && bunx tsc --project tsconfig.build.json",
38
42
  "test": "vitest run",
39
43
  "test:coverage": "vitest run --coverage",
40
44
  "eval": "bun run ./evals/run.ts"