@joshski/dust 0.1.50 → 0.1.52

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
@@ -4131,12 +4131,16 @@ async function lintMarkdown(dependencies) {
4131
4131
  }
4132
4132
 
4133
4133
  // lib/cli/commands/check.ts
4134
+ var log5 = createLogger("dust.cli.commands.check");
4134
4135
  var DEFAULT_CHECK_TIMEOUT_MS = 13000;
4135
4136
  async function runSingleCheck(check, cwd, runner) {
4136
4137
  const timeoutMs = check.timeoutMilliseconds ?? DEFAULT_CHECK_TIMEOUT_MS;
4138
+ log5(`running check ${check.name}: ${check.command}`);
4137
4139
  const startTime = Date.now();
4138
4140
  const result = await runner.run(check.command, cwd, timeoutMs);
4139
4141
  const durationMs = Date.now() - startTime;
4142
+ const status = result.timedOut ? "timed out" : result.exitCode === 0 ? "passed" : "failed";
4143
+ log5(`check ${check.name} ${status} (${durationMs}ms)`);
4140
4144
  return {
4141
4145
  name: check.name,
4142
4146
  command: check.command,
@@ -4166,6 +4170,7 @@ async function runValidationCheck(dependencies) {
4166
4170
  stdout: (msg) => outputLines.push(msg),
4167
4171
  stderr: (msg) => outputLines.push(msg)
4168
4172
  };
4173
+ log5("running built-in check: dust lint");
4169
4174
  const startTime = Date.now();
4170
4175
  const result = await lintMarkdown({
4171
4176
  ...dependencies,
@@ -4173,6 +4178,8 @@ async function runValidationCheck(dependencies) {
4173
4178
  arguments: []
4174
4179
  });
4175
4180
  const durationMs = Date.now() - startTime;
4181
+ const lintStatus = result.exitCode === 0 ? "passed" : "failed";
4182
+ log5(`built-in check dust lint ${lintStatus} (${durationMs}ms)`);
4176
4183
  return {
4177
4184
  name: "lint",
4178
4185
  command: "dust lint",
@@ -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.50",
3
+ "version": "0.1.52",
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"