@visulima/vis 1.0.0-alpha.5 → 1.0.0-alpha.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +147 -81
  2. package/README.md +144 -14
  3. package/dist/ai-analysis.d.ts +3 -17
  4. package/dist/ai-cache.d.ts +1 -1
  5. package/dist/ai-types.d.ts +16 -0
  6. package/dist/bin.js +352 -221
  7. package/dist/cache-directory.d.ts +73 -0
  8. package/dist/codeowners.d.ts +30 -0
  9. package/dist/commands/action-graph.d.ts +8 -0
  10. package/dist/commands/audit.d.ts +3 -3
  11. package/dist/commands/cache.d.ts +86 -0
  12. package/dist/commands/ci.d.ts +19 -0
  13. package/dist/commands/docker.d.ts +22 -0
  14. package/dist/commands/generate.d.ts +10 -0
  15. package/dist/commands/ignore-helpers.d.ts +157 -0
  16. package/dist/commands/ignore.d.ts +17 -0
  17. package/dist/commands/info.d.ts +3 -0
  18. package/dist/commands/list.d.ts +3 -0
  19. package/dist/commands/migrate/backup.d.ts +8 -0
  20. package/dist/commands/migrate/constants.d.ts +6 -2
  21. package/dist/commands/migrate/gitleaks.d.ts +29 -0
  22. package/dist/commands/migrate/json.d.ts +4 -2
  23. package/dist/commands/migrate/kingfisher.d.ts +14 -0
  24. package/dist/commands/migrate/moon.d.ts +5 -0
  25. package/dist/commands/migrate/nano-staged.d.ts +30 -0
  26. package/dist/commands/migrate/nx.d.ts +12 -0
  27. package/dist/commands/migrate/prompt.d.ts +2 -0
  28. package/dist/commands/migrate/secretlint.d.ts +14 -0
  29. package/dist/commands/migrate/shared.d.ts +29 -0
  30. package/dist/commands/migrate/turborepo.d.ts +11 -0
  31. package/dist/commands/migrate/types.d.ts +8 -1
  32. package/dist/commands/migrate/verify.d.ts +12 -0
  33. package/dist/commands/run.d.ts +13 -0
  34. package/dist/commands/sbom.d.ts +10 -0
  35. package/dist/commands/secrets.d.ts +3 -0
  36. package/dist/commands/staged.d.ts +7 -0
  37. package/dist/commands/status.d.ts +3 -0
  38. package/dist/commands/sync.d.ts +16 -0
  39. package/dist/commands/task-why.d.ts +3 -0
  40. package/dist/config.d.ts +17 -2
  41. package/dist/config.js +1 -1
  42. package/dist/docker.d.ts +73 -0
  43. package/dist/flakiness.d.ts +40 -0
  44. package/dist/generate/discover.d.ts +29 -0
  45. package/dist/generate/index.d.ts +32 -0
  46. package/dist/generate/index.js +1 -0
  47. package/dist/generate/loader.d.ts +15 -0
  48. package/dist/generate/moon-adapter/filename-interp.d.ts +42 -0
  49. package/dist/generate/moon-adapter/filters.d.ts +22 -0
  50. package/dist/generate/moon-adapter/frontmatter.d.ts +39 -0
  51. package/dist/generate/moon-adapter/index.d.ts +19 -0
  52. package/dist/generate/moon-adapter/tera-subset.d.ts +85 -0
  53. package/dist/generate/moon-adapter/util.d.ts +14 -0
  54. package/dist/generate/prompts.d.ts +25 -0
  55. package/dist/generate/remote.d.ts +43 -0
  56. package/dist/generate/runner.d.ts +37 -0
  57. package/dist/generate/types.d.ts +152 -0
  58. package/dist/hooks.d.ts +118 -0
  59. package/dist/native-binding.d.ts +8 -1
  60. package/dist/packem_chunks/index.js +7 -0
  61. package/dist/packem_chunks/loader.js +1 -0
  62. package/dist/packem_shared/otelPlugin-CJLkguJ8.js +1 -0
  63. package/dist/plugins/otel.d.ts +63 -0
  64. package/dist/pm-runner.d.ts +24 -3
  65. package/dist/run-report.d.ts +40 -0
  66. package/dist/runtime-check.d.ts +27 -0
  67. package/dist/sbom/cyclonedx.d.ts +39 -0
  68. package/dist/sbom/installed-package.d.ts +49 -0
  69. package/dist/sbom/license.d.ts +31 -0
  70. package/dist/sbom/lockfile.d.ts +34 -0
  71. package/dist/sbom/purl.d.ts +25 -0
  72. package/dist/sbom/resolve-specifier.d.ts +24 -0
  73. package/dist/sbom/types.d.ts +196 -0
  74. package/dist/secrets/baseline.d.ts +20 -0
  75. package/dist/secrets/format.d.ts +14 -0
  76. package/dist/secrets/git.d.ts +6 -0
  77. package/dist/secrets/spinner.d.ts +9 -0
  78. package/dist/selectors.d.ts +81 -0
  79. package/dist/shell-history.d.ts +16 -0
  80. package/dist/staged/cli-parse.d.ts +18 -0
  81. package/dist/staged/config.d.ts +14 -0
  82. package/dist/staged/errors/apply-empty-commit-error.d.ts +4 -0
  83. package/dist/staged/errors/config-error.d.ts +4 -0
  84. package/dist/staged/errors/get-backup-stash-error.d.ts +4 -0
  85. package/dist/staged/errors/git-error.d.ts +6 -0
  86. package/dist/staged/errors/index.d.ts +12 -0
  87. package/dist/staged/errors/restore-original-state-error.d.ts +4 -0
  88. package/dist/staged/errors/staged-error.d.ts +8 -0
  89. package/dist/staged/errors/task-error.d.ts +6 -0
  90. package/dist/staged/git/diff.d.ts +76 -0
  91. package/dist/staged/git/exec.d.ts +43 -0
  92. package/dist/staged/git/index.d.ts +77 -0
  93. package/dist/staged/git/stash.d.ts +37 -0
  94. package/dist/staged/index.d.ts +13 -0
  95. package/dist/staged/match.d.ts +12 -0
  96. package/dist/staged/renderer/index.d.ts +9 -0
  97. package/dist/staged/renderer/ink/index.d.ts +4 -0
  98. package/dist/staged/renderer/plain.d.ts +12 -0
  99. package/dist/staged/tasks/build.d.ts +13 -0
  100. package/dist/staged/tasks/exec.d.ts +56 -0
  101. package/dist/staged/tasks/run.d.ts +26 -0
  102. package/dist/staged/types.d.ts +173 -0
  103. package/dist/target-discovery.d.ts +59 -0
  104. package/dist/target-options.d.ts +261 -0
  105. package/dist/tui/components/OutputPanel.d.ts +2 -1
  106. package/dist/tui/components/TaskListPanel.d.ts +1 -1
  107. package/dist/tui/components/TaskStore.d.ts +1 -1
  108. package/dist/tui/components/devcontainer/DevcontainerStore.d.ts +1 -1
  109. package/dist/tui/components/devcontainer/catalogs/mount-suggestions.d.ts +1 -1
  110. package/dist/tui/components/devcontainer/sections/GeneralSection.d.ts +1 -1
  111. package/dist/tui/components/devcontainer/sections/PreviewPanel.d.ts +1 -1
  112. package/dist/tui/components/devcontainer/types.d.ts +4 -4
  113. package/dist/tui/components/graph/GraphStore.d.ts +1 -1
  114. package/dist/tui/components/graph/ProjectDetailPanel.d.ts +1 -1
  115. package/dist/tui/components/optimize/OptimizeDetailPanel.d.ts +1 -1
  116. package/dist/tui/components/optimize/OptimizeStore.d.ts +1 -1
  117. package/dist/tui/components/update/PackageDetailPanel.d.ts +1 -1
  118. package/dist/tui/components/update/PackageListPanel.d.ts +2 -2
  119. package/dist/tui/components/update/UpdateStore.d.ts +1 -1
  120. package/dist/tui/components/update/VisUpdateApp.d.ts +3 -3
  121. package/dist/tui/dynamic-life-cycle.d.ts +2 -1
  122. package/dist/tui/static-life-cycle.d.ts +7 -1
  123. package/dist/watch.d.ts +65 -0
  124. package/dist/workspace.d.ts +326 -6
  125. package/index.js +727 -554
  126. package/package.json +37 -32
  127. package/schemas/project.schema.json +344 -0
  128. package/schemas/vis-config.schema.json +331 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Variable-schema → interactive prompts.
3
+ *
4
+ * Builds on the same `node:readline` helpers `vis create` uses (see
5
+ * src/commands/create/prompts.ts). The implementation is minimal — no
6
+ * fancy multi-line UI, no spinner, no terminal control codes — to
7
+ * stay consistent with the rest of the vis CLI surface.
8
+ */
9
+ import type { Options, VariableMap } from "./types.d.ts";
10
+ interface CollectOptions {
11
+ /** When true, skip prompts and use defaults / overrides only. */
12
+ defaults: boolean;
13
+ /** When false, never prompt — error on missing required values. */
14
+ interactive: boolean;
15
+ /** CLI overrides — variable name → unparsed string from `--name=value`. */
16
+ overrides: Record<string, string>;
17
+ /** Variable schema. */
18
+ variables: VariableMap;
19
+ }
20
+ /**
21
+ * Collect option values from prompts + CLI overrides + defaults, then
22
+ * validate them.
23
+ */
24
+ export declare const collectOptions: (collectOptionsArguments: CollectOptions) => Promise<Options>;
25
+ export {};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Remote template fetching for `vis generate`.
3
+ *
4
+ * Reuses giget (already a vis dep, used by `vis create`) to fetch
5
+ * `git://`, `npm://`, and `https://` archive sources into a cache
6
+ * directory before discover/load runs against them.
7
+ *
8
+ * Sources are normalized via the same patterns vis create uses; see
9
+ * `src/commands/create/templates/remote.ts` for the underlying
10
+ * downloadTemplate call.
11
+ */
12
+ /**
13
+ * True when the input looks like a remote source giget can resolve.
14
+ */
15
+ export declare const isRemoteSource: (input: string) => boolean;
16
+ interface FetchOptions {
17
+ /** Auth token forwarded to giget for private repos. */
18
+ auth?: string;
19
+ /** Prefer cached templates over re-download. */
20
+ preferOffline?: boolean;
21
+ /** Override the cache/work directory (defaults to a fresh tmp). */
22
+ targetDirectory?: string;
23
+ }
24
+ export interface FetchResult {
25
+ /**
26
+ * Release the tmp directory once the caller has finished loading
27
+ * the template into memory. No-op when the directory was supplied
28
+ * externally via `targetDirectory`.
29
+ */
30
+ cleanup: () => void;
31
+ /** Absolute directory containing the downloaded template. */
32
+ directory: string;
33
+ }
34
+ /**
35
+ * Download a remote template and return the directory it lives in.
36
+ * @example
37
+ * ```typescript
38
+ * const { directory } = await fetchRemoteTemplate("git://github.com/org/template#main");
39
+ * const template = await loadMoonTemplate(directory, "from-git");
40
+ * ```
41
+ */
42
+ export declare const fetchRemoteTemplate: (source: string, options?: FetchOptions) => Promise<FetchResult>;
43
+ export {};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Template runner — applies a `Creation` to disk.
3
+ *
4
+ * Walks the recursive `files` tree, prompts for overwrites unless
5
+ * `--force` was passed, runs scripts in phase order, and prints
6
+ * suggestions at the end. `--dry-run` prints the planned writes
7
+ * without touching the filesystem.
8
+ *
9
+ * Every write target is checked against the destination directory to
10
+ * reject `..`-escapes and absolute paths coming from rendered
11
+ * frontmatter `to:` fields or filename interpolation — template
12
+ * authors (or remote templates) must not be able to scribble
13
+ * outside the caller's chosen destination.
14
+ */
15
+ import type { Template } from "./types.d.ts";
16
+ interface RunnerOptions {
17
+ /** Caller's CWD for `working_dir`. */
18
+ cwd: string;
19
+ /** Destination directory (absolute). */
20
+ destination: string;
21
+ /** When true, print but don't write. */
22
+ dryRun?: boolean;
23
+ /** When true, overwrite existing files without prompting. */
24
+ force?: boolean;
25
+ /** Resolved option values from `collectOptions`. */
26
+ options: Record<string, unknown>;
27
+ /** When true, don't run scripts. */
28
+ skipScripts?: boolean;
29
+ /** Workspace root for `dest_rel_dir`. */
30
+ workspaceRoot: string;
31
+ }
32
+ /**
33
+ * Run a template end-to-end: invoke `produce`, write files, run scripts,
34
+ * print suggestions.
35
+ */
36
+ export declare const runTemplate: (template: Template, options: RunnerOptions) => Promise<void>;
37
+ export {};
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Type definitions for the `vis generate` template runtime.
3
+ *
4
+ * The shape intentionally echoes Bingo's `Template` so a future Bingo
5
+ * adapter (re-exporting `bingo` Templates through this runtime) is a
6
+ * one-pager. Authors do not depend on Bingo to write a vis generator.
7
+ */
8
+ export type VariableType = "array" | "boolean" | "enum" | "number" | "string";
9
+ /** Common variable fields (all types). */
10
+ interface VariableBase {
11
+ /**
12
+ * Default value when the user accepts the prompt without typing.
13
+ * For `boolean` this is `true|false`; for `enum` it must match `values`.
14
+ */
15
+ default?: boolean | number | string | string[];
16
+ /** Hide from prompts; can still be set via CLI or `--defaults`. */
17
+ internal?: boolean;
18
+ /** Sort order in prompts (lower first). Defaults to declaration order. */
19
+ order?: number;
20
+ /** Override the prompt text. Defaults to the variable name. */
21
+ prompt?: string;
22
+ /** When true, the user must provide a non-empty value. */
23
+ required?: boolean;
24
+ }
25
+ export type StringVariable = VariableBase & {
26
+ type: "string";
27
+ };
28
+ export type NumberVariable = VariableBase & {
29
+ type: "number";
30
+ };
31
+ export type BooleanVariable = VariableBase & {
32
+ default?: boolean;
33
+ type: "boolean";
34
+ };
35
+ export type ArrayVariable = VariableBase & {
36
+ type: "array";
37
+ };
38
+ export interface EnumVariable extends VariableBase {
39
+ /** Allow multiple selections (returns `string[]`). */
40
+ multiple?: boolean;
41
+ type: "enum";
42
+ /** Selectable values. */
43
+ values: string[];
44
+ }
45
+ export type Variable = ArrayVariable | BooleanVariable | EnumVariable | NumberVariable | StringVariable;
46
+ /** Map of variable name → spec, as authors declare them. */
47
+ export type VariableMap = Record<string, Variable>;
48
+ /** Resolved option values passed to `produce()`. */
49
+ export type Options = Record<string, unknown>;
50
+ /** A file in a Creation can be a string, a Buffer (binary asset), or a nested directory. */
51
+ export type CreationFile = Buffer | string;
52
+ export interface CreationDirectory {
53
+ [key: string]: CreationDirectory | CreationFile;
54
+ }
55
+ /**
56
+ * Script entry produced by `produce()`.
57
+ * - `string`: shell command, runs in the destination directory.
58
+ * - `object`: shell commands with optional phase ordering.
59
+ */
60
+ export type Script = ScriptObject | string;
61
+ export interface ScriptObject {
62
+ /** Shell command(s) to run sequentially. */
63
+ commands: string[];
64
+ /**
65
+ * Phase ordering. Phases run in ascending order; scripts within
66
+ * the same phase are dispatched concurrently. Default: 0.
67
+ */
68
+ phase?: number;
69
+ /** Suppress command output. Default: false. */
70
+ silent?: boolean;
71
+ }
72
+ /** Object returned by a template's `produce()` function. */
73
+ export interface Creation {
74
+ /** Recursive directory tree. Keys with `/` are auto-split. */
75
+ files?: CreationDirectory;
76
+ /**
77
+ * Per-file metadata keyed by the *flattened* destination path
78
+ * (e.g. `src/foo.ts`). Optional — native templates usually omit
79
+ * this; the moon adapter populates it from per-file frontmatter
80
+ * so `force: true` survives the trip to the runner without
81
+ * changing the shape of `files`.
82
+ */
83
+ filesMeta?: Record<string, FileMeta>;
84
+ /** Shell scripts to run after files are written. */
85
+ scripts?: Script[];
86
+ /** User-facing tips printed after the run. */
87
+ suggestions?: string[];
88
+ }
89
+ export interface FileMeta {
90
+ /**
91
+ * Overwrite an existing file at this path without prompting or
92
+ * consulting the global `--force` flag.
93
+ */
94
+ force?: boolean;
95
+ }
96
+ /** Context object passed to `produce()`. */
97
+ export interface TemplateContext {
98
+ /** Built-in variables: `dest_dir`, `dest_rel_dir`, `working_dir`, `workspace_root`. */
99
+ builtins: BuiltinVars;
100
+ /** Resolved option values (after prompts + CLI overrides + defaults). */
101
+ options: Options;
102
+ }
103
+ export interface BuiltinVars {
104
+ /** Absolute destination directory. */
105
+ dest_dir: string;
106
+ /** Destination relative to the workspace root. */
107
+ dest_rel_dir: string;
108
+ /** Caller's current working directory. */
109
+ working_dir: string;
110
+ /** Absolute workspace root (from `findMonorepoRootSync`, fallback to `working_dir`). */
111
+ workspace_root: string;
112
+ }
113
+ /** Top-level "About" metadata for a template. */
114
+ export interface TemplateAbout {
115
+ /** One-line description. */
116
+ description: string;
117
+ /** Short identifier shown in `vis generate --list` and prompts. */
118
+ name: string;
119
+ }
120
+ /**
121
+ * The author-facing template shape.
122
+ * Both native (`.vis/templates/&lt;name>.ts`) and moon-adapter outputs
123
+ * normalize to this.
124
+ */
125
+ export interface Template {
126
+ about: TemplateAbout;
127
+ /**
128
+ * Default destination directory (relative to workspace root unless
129
+ * absolute or starting with `./`). Honored when the user does not
130
+ * pass `--to`. Maps to moon's `template.yml` `destination`.
131
+ */
132
+ destination?: string;
133
+ /** Variable schema for prompts. */
134
+ options?: VariableMap;
135
+ /** Build the Creation given resolved options and built-in vars. */
136
+ produce: (context: TemplateContext) => Creation | Promise<Creation>;
137
+ }
138
+ /**
139
+ * Discovery record: a Template plus where it came from.
140
+ * Surfaced by `vis generate --list`.
141
+ */
142
+ export interface DiscoveredTemplate {
143
+ /** Lazy loader — invoke to materialize the Template. */
144
+ load: () => Promise<Template>;
145
+ /** Stable name used by `vis generate &lt;name>`. */
146
+ name: string;
147
+ /** Absolute path on disk (file for native, directory for moon). */
148
+ path: string;
149
+ /** Source classification — affects load + listing. */
150
+ source: "config" | "moon" | "native" | "remote";
151
+ }
152
+ export {};
@@ -0,0 +1,118 @@
1
+ import type { LifeCycleInterface, Task, TaskResult } from "@visulima/task-runner";
2
+ import type { Hookable } from "hookable";
3
+ /**
4
+ * Typed hook surface exposed to vis plugins.
5
+ *
6
+ * Plugins subscribe via `hooks.hook(name, handler)` — handlers are
7
+ * awaited sequentially in registration order. Returning a promise
8
+ * delays the next hook firing until it resolves, so plugins can
9
+ * safely perform async setup/teardown.
10
+ *
11
+ * Naming deliberately mirrors vite-task / webpack-style verbs:
12
+ * before/after for boundaries, on&lt;Event> for passive observation.
13
+ */
14
+ export interface VisHooks {
15
+ /**
16
+ * Fired after the entire task graph completes (including any
17
+ * failures). `results` maps task ID → {@link TaskResult}.
18
+ */
19
+ "run:after": (results: Map<string, TaskResult>) => Promise<void> | void;
20
+ /**
21
+ * Fired once before any task in the graph starts, after workspace
22
+ * discovery and graph construction. Throwing aborts the run.
23
+ */
24
+ "run:before": (context: {
25
+ tasks: Task[];
26
+ workspaceRoot: string;
27
+ }) => Promise<void> | void;
28
+ /**
29
+ * Fired after a task completes (success, failure, or cache hit).
30
+ * Receives the final {@link TaskResult}.
31
+ */
32
+ "task:after": (task: Task, result: TaskResult) => Promise<void> | void;
33
+ /**
34
+ * Fired before each task begins execution — after scheduling, before
35
+ * the executor runs the command. Throwing aborts that single task.
36
+ */
37
+ "task:before": (task: Task) => Promise<void> | void;
38
+ /** Fired when a task hit the local or remote cache. */
39
+ "task:cacheHit": (task: Task, result: TaskResult) => Promise<void> | void;
40
+ /**
41
+ * Fired when auto-fingerprint cache diagnostics reports a miss,
42
+ * carrying the human-readable reason string.
43
+ */
44
+ "task:cacheMiss": (task: Task, reasons: string) => Promise<void> | void;
45
+ /** Fired when a task exits non-zero. */
46
+ "task:failure": (task: Task, result: TaskResult) => Promise<void> | void;
47
+ /**
48
+ * Fired with a stderr chunk as a running task emits it. Plugins
49
+ * that ship logs live (Slack, Datadog) should prefer this over
50
+ * `task:after` so they don't wait for the full buffer.
51
+ */
52
+ "task:stderr": (task: Task, chunk: string) => Promise<void> | void;
53
+ /**
54
+ * Fired with a stdout chunk as a running task emits it. See
55
+ * `task:stderr` for semantics.
56
+ */
57
+ "task:stdout": (task: Task, chunk: string) => Promise<void> | void;
58
+ }
59
+ /**
60
+ * Public plugin contract. Implementations register handlers by
61
+ * returning a partial {@link VisHooks} map from `hooks`, or by
62
+ * mutating the Hookable instance directly via `setup(hooks)` for
63
+ * advanced cases (dynamic registration, removeHook, etc.).
64
+ *
65
+ * Plugins are loaded in the order they appear in `visConfig.plugins`.
66
+ * Handler execution order within a hook follows registration order,
67
+ * so earlier plugins see events first.
68
+ */
69
+ export interface VisPlugin {
70
+ /**
71
+ * Declarative handlers — the common shape. One entry per hook
72
+ * name; pass a function or an array of functions (all run serially
73
+ * in order).
74
+ */
75
+ hooks?: Partial<{
76
+ [K in keyof VisHooks]: VisHooks[K] | VisHooks[K][];
77
+ }>;
78
+ /** Plugin name — surfaced in debug logs. */
79
+ name: string;
80
+ /**
81
+ * Imperative setup — receives the shared Hookable instance so the
82
+ * plugin can register hooks conditionally, unregister later, or
83
+ * use advanced APIs like `hookOnce`/`beforeEach`/`afterEach`.
84
+ */
85
+ setup?: (hooks: Hookable<VisHooks>) => Promise<void> | void;
86
+ }
87
+ /**
88
+ * Optional callback invoked whenever a plugin handler throws. Lets
89
+ * callers surface buggy plugins without crashing the run — pass a
90
+ * logger or re-throw to promote plugin errors to fatals.
91
+ */
92
+ export type HookErrorHandler = (hookName: keyof VisHooks, error: unknown) => void;
93
+ /**
94
+ * Creates a fresh typed hook registry. One instance is created per
95
+ * `vis run` invocation and passed to every plugin's `setup()`.
96
+ */
97
+ export declare const createVisHooks: () => Hookable<VisHooks>;
98
+ /**
99
+ * Registers each plugin's handlers against the shared hook instance.
100
+ * Synchronous failures in `setup()` abort plugin loading and propagate.
101
+ */
102
+ export declare const registerPlugins: (hooks: Hookable<VisHooks>, plugins: VisPlugin[] | undefined) => Promise<void>;
103
+ /**
104
+ * Bridges `LifeCycleInterface` (the task-runner contract) into
105
+ * `VisHooks` events. Register this as a lifecycle alongside the
106
+ * existing UI renderers so plugin authors see the same events the
107
+ * TUI/static output do, without having to understand task-runner's
108
+ * lower-level `LifeCycleInterface` shape.
109
+ */
110
+ export declare class HookableLifeCycle implements LifeCycleInterface {
111
+ #private;
112
+ constructor(hooks: Hookable<VisHooks>, onError?: HookErrorHandler);
113
+ startTasks(tasks: Task[]): void;
114
+ endTasks(results: TaskResult[]): void;
115
+ printCacheMiss(task: Task, reasons: string): void;
116
+ onTaskStdout(task: Task, chunk: string): void;
117
+ onTaskStderr(task: Task, chunk: string): void;
118
+ }
@@ -121,12 +121,19 @@ interface NativeBindings {
121
121
  detectPackageManager: (cwd: string) => DetectedPackageManager;
122
122
  execPmCommand: (bin: string, args: string[], cwd: string) => ExecResult;
123
123
  execPmCommandInteractive: (bin: string, args: string[], cwd: string) => number;
124
+ /**
125
+ * ABI compatibility version exported from the Rust binding. The TypeScript
126
+ * loader compares this against {@link EXPECTED_NATIVE_BINDING_VERSION} and
127
+ * rejects mismatched addons so a stale `.node` file from before a
128
+ * signature change is not used silently.
129
+ */
130
+ NATIVE_BINDING_VERSION: number;
124
131
  resolveAdd: (pm: string, version: string, options: AddOptions) => ResolvedCommand;
125
132
  resolveDedupe: (pm: string, version: string, check: boolean) => ResolvedCommand;
126
133
  resolveDlx: (pm: string, version: string, options: DlxOptions) => ResolvedCommand;
127
134
  resolveExec: (pm: string, version: string, options: ExecOptions) => ResolvedCommand;
128
135
  resolveInstall: (pm: string, version: string, options: InstallOptions) => ResolvedCommand;
129
- resolveLink: (pm: string, target: string | null) => ResolvedCommand;
136
+ resolveLink: (pm: string, version: string, target: string | null) => ResolvedCommand;
130
137
  resolveOutdated: (pm: string, version: string, options: OutdatedOptions) => ResolvedCommand;
131
138
  resolvePmCommand: (pm: string, version: string, subcommand: string, extraArgs: string[]) => ResolvedCommand;
132
139
  resolveRemove: (pm: string, version: string, options: RemoveOptions) => ResolvedCommand;
@@ -0,0 +1,7 @@
1
+ var H=Object.defineProperty;var v=(e,t)=>H(e,"name",{value:t,configurable:!0});import{walkSync as J,readFileSync as k}from"@visulima/fs";import{readYamlSync as X}from"@visulima/fs/yaml";import{relative as M,join as R}from"@visulima/path";import{parse as Z}from"yaml";import{constantCase as ee,trainCase as te,snakeCase as ne,pascalCase as re,kebabCase as ie,camelCase as se}from"@visulima/string/case";var ae=Object.defineProperty,d=v((e,t)=>ae(e,"name",{value:t,configurable:!0}),"e");const h=d(e=>e==null?"":typeof e=="string"?e:String(e),"stringify"),S={camel_case:d(e=>se(h(e)),"camel_case"),kebab_case:d(e=>ie(h(e)),"kebab_case"),lower_case:d(e=>h(e).toLowerCase(),"lower_case"),pascal_case:d(e=>re(h(e)),"pascal_case"),path_join:d((e,...t)=>{const r=[h(e),...t.map(h)];return R(...r)},"path_join"),path_relative:d((e,t)=>M(h(t),h(e)),"path_relative"),snake_case:d(e=>ne(h(e)),"snake_case"),upper_case:d(e=>h(e).toUpperCase(),"upper_case"),upper_kebab_case:d(e=>te(h(e)),"upper_kebab_case"),upper_snake_case:d(e=>ee(h(e)),"upper_snake_case")};d(e=>Object.hasOwn(S,e),"isKnownFilter");const I=d((e,t,r=[])=>{const n=S[e];if(!n)throw new Error(`Unknown filter "${e}". Known filters: ${Object.keys(S).sort().join(", ")}.`);return n(t,...r)},"applyFilter");var oe=Object.defineProperty,N=v((e,t)=>oe(e,"name",{value:t,configurable:!0}),"d$1");const L=N(e=>{const t=[];let r=0,n=0,i,s=-1;for(const a of e){if(s+=1,i){a===i&&(i=void 0);continue}if(a==='"'||a==="'"){i=a;continue}a==="("?r+=1:a===")"?r-=1:a===","&&r===0&&(t.push(e.slice(n,s)),n=s+1)}return t.push(e.slice(n)),t},"splitCommaOutsideQuotes"),le=N(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1)},"stripQuotes");var ce=Object.defineProperty,$=v((e,t)=>ce(e,"name",{value:t,configurable:!0}),"i$1");const fe=/\[([^\]]+)\]/g,ue=$(e=>{const t=e.trim(),r=t.indexOf("(");if(r===-1)return{args:[],name:t};if(!t.endsWith(")"))throw new Error(`Filter call "${e}" missing closing ")"`);const n=t.slice(0,r).trim(),i=t.slice(r+1,-1).trim();return i===""?{args:[],name:n}:{args:L(i).map(s=>{const a=s.trim();return le(a)??a}),name:n}},"parseFilterCall"),pe=$(e=>{const t=e.split("|").map(n=>n.trim()),r=t[0]??"";if(!r)throw new Error(`Empty variable name in expression "${e}"`);return{filters:t.slice(1).map(ue),name:r}},"parsePipe"),me=$(e=>e==null?"":typeof e=="string"?e:String(e),"stringify"),de=$((e,t)=>{const r=e.replaceAll(fe,(n,i)=>{const{filters:s,name:a}=pe(i);if(!Object.hasOwn(t,a))throw new Error(`Variable "${a}" used in filename "${e}" but not defined`);let o=t[a];for(const l of s)o=I(l.name,o,l.args);return me(o)});return q(r)},"interpolateFilename"),q=$(e=>e.split("/").map(t=>t.endsWith(".tera")||t.endsWith(".twig")?t.slice(0,-5):t).join("/"),"stripTeraSuffix"),he=$(e=>e.endsWith(".raw")?e.slice(0,-4):e,"stripRawSuffix"),ye=$(e=>{const t=e.split("/");return(t.at(-1)??"").startsWith("_")?!0:t.slice(0,-1).includes("partials")},"isPartialPath");var ge=Object.defineProperty,be=v((e,t)=>ge(e,"name",{value:t,configurable:!0}),"i");const we=be((e,t)=>{if(!e.startsWith(`---
2
+ `)&&!e.startsWith(`---\r
3
+ `))return{body:e};const r=e.indexOf(`
4
+ ---`,3);if(r===-1)return{body:e};let n=r+1+3;e[n]==="\r"&&(n+=1),e[n]===`
5
+ `&&(n+=1);const i=e.startsWith(`---\r
6
+ `)?5:4,s=e.slice(i,r).replaceAll("\r","");let a;try{a=t(s)}catch(o){const l=o instanceof Error?o.message:String(o);throw new Error(`Failed to parse frontmatter YAML: ${l}`)}if(a==null)return{body:e.slice(n)};if(typeof a!="object"||Array.isArray(a))throw new TypeError(`Frontmatter must be a YAML mapping, got ${Array.isArray(a)?"array":typeof a}`);return{body:e.slice(n),frontmatter:a}},"splitFrontmatter");var ve=Object.defineProperty,f=v((e,t)=>ve(e,"name",{value:t,configurable:!0}),"c");const $e=/\{\{-?(.+?)-?\}\}|\{%-?(.+?)-?%\}/gs,U=f(e=>{const t=[];let r=0,n=1;const i=f(s=>{for(const a of s)a===`
7
+ `&&(n+=1)},"advanceLine");for(const s of e.matchAll($e)){const a=s.index??0;if(a>r){const c=e.slice(r,a);t.push({line:n,type:"text",value:c}),i(c)}const o=n,l=s[0];l.startsWith("{{")?t.push({line:o,type:"expr",value:(s[1]??"").trim()}):t.push({line:o,type:"stmt",value:(s[2]??"").trim()}),i(l),r=a+l.length}if(r<e.length){const s=e.slice(r);t.push({line:n,type:"text",value:s})}return t},"tokenize"),xe=new Set(["block","endblock","endfilter","endmacro","extends","filter","import","macro","set"]),G=f((e,t)=>{let r=0;const n=f((s,a)=>{throw new Error(`${t}:${s}: ${a}`)},"error"),i=f(s=>{const a=[];for(;r<e.length;){const o=e[r];if(o.type==="text"){a.push({type:"text",value:o.value}),r+=1;continue}if(o.type==="expr"){a.push({expression:o.value,line:o.line,type:"expr"}),r+=1;continue}const l=o.value.split(/\s+/)[0]??"";if(s.includes(l))return a;if(xe.has(l)&&n(o.line,`Tera feature "{% ${l} %}" is not supported. Supported: if/else/endif, for/endfor, include. Rewrite the template to avoid macros, set, extends, block, and import.`),l==="if"){r+=1;const c=o.value.slice(2).trim(),m=i(["else","endif"]);let u;r<e.length&&e[r].type==="stmt"&&e[r].value.split(/\s+/)[0]==="else"&&(r+=1,u=i(["endif"])),(r>=e.length||e[r].type!=="stmt"||e[r].value.split(/\s+/)[0]!=="endif")&&n(o.line,"Unterminated {% if %} — missing {% endif %}"),r+=1,a.push({alternate:u,condition:c,consequent:m,line:o.line,type:"if"});continue}if(l==="for"){r+=1;const c=o.value.slice(3).trim(),m=c.indexOf(" in ");m===-1&&n(o.line,"Malformed {% for %} — expected `for <name> in <collection>`");const u=c.slice(0,m).trim(),g=c.slice(m+4).trim();(!u||!g)&&n(o.line,"Malformed {% for %} — missing binding or collection");const p=i(["endfor"]);(r>=e.length||e[r].type!=="stmt"||e[r].value.split(/\s+/)[0]!=="endfor")&&n(o.line,"Unterminated {% for %} — missing {% endfor %}"),r+=1,a.push({binding:u,body:p,collection:g,line:o.line,type:"for"});continue}if(l==="include"){r+=1;const c=o.value.slice(7).trim(),m=Q(c);m===void 0&&n(o.line,"Malformed {% include %} — expected a quoted partial name"),a.push({line:o.line,name:m,type:"include"});continue}n(o.line,`Unknown tag "{% ${l} %}". Supported: if, for, include.`)}return a},"parseBlock");return i([])},"parse"),Q=f(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1)},"stripQuotes"),Ee=f(e=>e==null?"":typeof e=="boolean"||typeof e=="number"?String(e):typeof e=="string"?e:JSON.stringify(e),"stringify"),Oe=f(e=>e==null||e===!1||e===0||e===""?!1:Array.isArray(e)?e.length>0:!0,"isTruthy"),V=f((e,t,r,n,i=!0)=>{const s=e.split(".");if(i&&s.length>0&&!Object.hasOwn(t,s[0]))throw new Error(`${n}:${r}: Variable "${e}" is not defined`);let a=t;for(const o of s){if(a==null)return;if(typeof a!="object")throw new TypeError(`${n}:${r}: Cannot read "${o}" on non-object value`);a=a[o]}return a},"lookupVariable"),ke=f(e=>{const t=[];let r=0,n=0,i;for(let s=0;s<e.length;s+=1){const a=e[s];if(i){a===i&&(i=void 0);continue}if(a==='"'||a==="'"){i=a;continue}a==="("?r+=1:a===")"?r-=1:a==="|"&&r===0&&(t.push(e.slice(n,s)),n=s+1)}return t.push(e.slice(n)),t},"splitFilterPipe"),je=f(e=>{const t=e.trim(),r=t.indexOf("(");if(r===-1)return{args:[],name:t};if(!t.endsWith(")"))throw new Error(`Filter call "${e}" missing closing ")"`);const n=t.slice(0,r).trim(),i=t.slice(r+1,-1).trim();return i?{args:L(i).map(s=>s.trim()),name:n}:{args:[],name:n}},"parseFilterCall"),j=f((e,t,r,n,i=!0)=>{const s=e.trim();if(s==="true")return!0;if(s==="false")return!1;if(s==="null"||s==="none")return null;const a=Q(s);return a!==void 0?a:/^-?\d+(\.\d+)?$/.test(s)?Number(s):V(s,t,r,n,i)},"evaluatePrimary"),B=f((e,t,r,n,i=!0)=>{const s=ke(e),a=s[0].trim();let o=j(a,t,r,n,i);for(let l=1;l<s.length;l+=1){const c=s[l].trim(),{args:m,name:u}=je(c),g=m.map(p=>j(p,t,r,n,i));try{o=I(u,o,g)}catch(p){const w=p instanceof Error?p.message:String(p);throw new Error(`${n}:${r}: ${w}`)}}return o},"evaluateExpression"),D=f((e,t)=>{let r,n=0;for(let i=0;i<=e.length-t.length;i+=1){const s=e[i];if(r){s===r&&(r=void 0);continue}if(s==='"'||s==="'"){r=s;continue}if(s==="("){n+=1;continue}if(s===")"){n-=1;continue}if(n===0&&e.slice(i,i+t.length)===t)return i}return-1},"findOperatorOutsideQuotes"),A=f((e,t)=>{const r=[];let n=0,i=0;for(;i<=e.length-t.length;){const s=D(e.slice(i),t);if(s===-1)break;r.push(e.slice(n,i+s)),i+=s+t.length,n=i}return r.push(e.slice(n)),r},"splitLogical"),x=f((e,t,r,n)=>{const i=e.trim();if(i==="")return!1;if(i.startsWith("(")&&i.endsWith(")")&&Se(i)===i.length-1)return x(i.slice(1,-1),t,r,n);const s=A(i," or ");if(s.length>1)return s.some(o=>x(o,t,r,n));const a=A(i," and ");if(a.length>1)return a.every(o=>x(o,t,r,n));if(i.startsWith("not "))return!x(i.slice(4),t,r,n);for(const o of["==","!="]){const l=D(i,o);if(l!==-1){const c=j(i.slice(0,l),t,n,r,!1),m=j(i.slice(l+o.length),t,n,r,!1),u=c===m;return o==="=="?u:!u}}return Oe(B(i,t,n,r,!1))},"evaluateConditionExpression"),Se=f(e=>{if(e[0]!=="(")return-1;let t=0,r,n=-1;for(const i of e){if(n+=1,r){i===r&&(r=void 0);continue}if(i==='"'||i==="'"){r=i;continue}if(i==="(")t+=1;else if(i===")"&&(t-=1,t===0))return n}return-1},"matchingParen"),E=f((e,t)=>{let r="";for(const n of e){if(n.type==="text"){r+=n.value;continue}if(n.type==="expr"){r+=Ee(B(n.expression,t.scope,n.line,t.filename));continue}if(n.type==="if"){const i=x(n.condition,t.scope,t.filename,n.line)?n.consequent:n.alternate??[];r+=E(i,t);continue}if(n.type==="for"){const i=V(n.collection,t.scope,n.line,t.filename,!1);if(i==null)continue;if(!Array.isArray(i))throw new TypeError(`${t.filename}:${n.line}: {% for %} expected an array, got ${typeof i}`);for(const s of i){const a={...t.scope,[n.binding]:s};r+=E(n.body,{...t,scope:a})}continue}if(n.type==="include"){const i=t.partials?.get(n.name);if(!i)throw new Error(`${t.filename}:${n.line}: Partial "${n.name}" not found. Available: ${[...t.partials?.keys()??[]].join(", ")||"(none)"}`);const s=t.includeStack??new Set;if(s.has(n.name)){const a=[...s,n.name].join(" → ");throw new Error(`${t.filename}:${n.line}: Circular partial include detected: ${a}`)}s.add(n.name);try{r+=E(i,{...t,filename:`<partial:${n.name}>`,includeStack:s})}finally{s.delete(n.name)}}}return r},"renderNodes"),_e=f((e,t)=>G(U(e),t),"parseTemplate");f((e,t)=>E(e,t),"renderAst");const _=f((e,t)=>E(G(U(e),t.filename),t),"renderTemplate");var Ce=Object.defineProperty,y=v((e,t)=>Ce(e,"name",{value:t,configurable:!0}),"s");const P="template.yml",Te=".raw",We=new Set([".cjs",".css",".env",".gitattributes",".gitignore",".graphql",".html",".ini",".js",".json",".json5",".jsonc",".jsx",".lock",".md",".mdx",".mjs",".prettierrc",".raw",".rs",".scss",".sh",".sql",".svg",".tera",".toml",".ts",".tsx",".twig",".txt",".vue",".xml",".yaml",".yml"]),Ae=new Set(["Brewfile","CHANGELOG","CODEOWNERS","CONTRIBUTING","COPYING","Dockerfile","Gemfile","LICENCE","LICENSE","Makefile","NOTICE","Procfile","Rakefile","README","VERSION"]),Pe=y(e=>{const t=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\")),r=t===-1?e:e.slice(t+1),n=r.lastIndexOf(".");return n===-1||n===0?"":r.slice(n).toLowerCase()},"lastDot"),Y=y(e=>{const t=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return t===-1?e:e.slice(t+1)},"basename"),Fe=y(e=>{const t=Pe(e);if(t!=="")return We.has(t);const r=Y(e);return!!Ae.has(r)},"isLikelyText"),Me=y(e=>{const t={default:e.default,internal:e.internal,order:e.order,prompt:e.prompt,required:e.required};switch(e.type){case"array":return{...t,type:"array"};case"boolean":return{...t,default:typeof t.default=="boolean"?t.default:void 0,type:"boolean"};case"enum":return{...t,multiple:e.multiple,type:"enum",values:e.values??[]};case"number":return{...t,type:"number"};case"string":return{...t,type:"string"};default:throw new Error(`Unsupported variable type "${String(e.type)}"`)}},"yamlVariableToVariable"),Re=y(e=>e.replaceAll("\\","/").replace(/^\.\//,"").replaceAll(/\/+/g,"/"),"normalizeDestinationPath"),Ie=y((e,t,r)=>{const n=t.split("/").filter(Boolean);if(n.length===0)throw new Error("Empty destination path for value");let i=e;for(let s=0;s<n.length-1;s+=1){const a=n[s],o=i[a];if(o===void 0){const l={};i[a]=l,i=l}else if(typeof o=="object"&&!Buffer.isBuffer(o))i=o;else throw new TypeError(`Path conflict: "${n.slice(0,s+1).join("/")}" is both a file and a directory`)}i[n.at(-1)]=r},"flattenFiles"),F=y((e,t,r)=>{if(typeof e!="string")return Ne(e);const n=e.includes("{{")||e.includes("{%")?_(e,{filename:`${r}#frontmatter`,scope:t}):e;return x(n,t,`${r}#frontmatter`,1)},"evaluateGate"),Ne=y(e=>e==null||e===!1||e===0||e===""?!1:Array.isArray(e)?e.length>0:typeof e=="string"?e!=="false"&&e!=="0":!0,"truthyValue"),De=y((e,t)=>{const r=R(e,P),n=X(r);if(!n||typeof n!="object")throw new Error(`${r}: must contain a YAML mapping`);const i={};if(n.variables)for(const[l,c]of Object.entries(n.variables))i[l]=Me(c);const s=new Map,a=[];for(const l of J(e,{includeDirs:!1,includeSymlinks:!1})){if(l.path===r)continue;const c=M(e,l.path).replaceAll("\\","/");if(c===P)continue;if(ye(c)){const u=k(l.path),g=_e(u,c);for(const p of Le(c))s.has(p)&&s.get(p)!==g&&console.warn(`warn: partial name "${p}" is declared by multiple files — last one wins. Use distinct names or fully-qualified includes.`),s.set(p,g);continue}if(c.endsWith(Te)){const u=k(l.path);a.push({binary:null,isRaw:!0,rawText:u,relativePath:c});continue}if(Fe(c)){const u=k(l.path);a.push({binary:null,isRaw:!1,rawText:u,relativePath:c});continue}const m=k(l.path,{buffer:!0});a.push({binary:m,isRaw:!1,rawText:null,relativePath:c})}const o=y(l=>{const c={...l.builtins,...l.options},m={},u={};for(const p of a){const w=p.relativePath;let O,C,T=!1;if(p.binary)O=p.binary;else if(p.isRaw)O=p.rawText??"";else{const K=p.rawText??"",b=we(K,z=>Z(z));if(b.frontmatter){if(b.frontmatter.skip!==void 0&&F(b.frontmatter.skip,c,w)||b.frontmatter.if!==void 0&&!F(b.frontmatter.if,c,w))continue;typeof b.frontmatter.to=="string"&&(C=_(b.frontmatter.to,{filename:`${w}#frontmatter.to`,partials:s,scope:c})),b.frontmatter.force===!0&&(T=!0)}O=_(b.body,{filename:w,partials:s,scope:c})}const W=Re(C??de(he(w),c));Ie(m,W,O),T&&(u[W]={force:!0})}const g={files:m};return Object.keys(u).length>0&&(g.filesMeta=u),g},"produce");return{about:{description:n.description??`Moon-format template at ${e}`,name:n.title??t},destination:n.destination,options:i,produce:o}},"loadMoonTemplate"),Le=y(e=>{const t=q(e),r=new Set;r.add(t);const n=Y(t);if(r.add(n),n.startsWith("_")){r.add(n.replace(/^_+/,""));const i=t.slice(0,t.length-n.length);r.add(`${i}${n.replace(/^_+/,"")}`)}return[...r]},"partialKeys");export{De as loadMoonTemplate};
@@ -0,0 +1 @@
1
+ var n=Object.defineProperty;var o=(t,e)=>n(t,"name",{value:e,configurable:!0});import{dirname as u}from"@visulima/path";import{createJiti as l}from"jiti";var i=Object.defineProperty,a=o((t,e)=>i(t,"name",{value:e,configurable:!0}),"r");const p=a((t,e)=>{if(!e||typeof e!="object")throw new TypeError(`${t}: default export must be an object (got ${e===null?"null":typeof e}). Use createTemplate({ ... }).`);const r=e;if(typeof r.about!="object"||r.about===null)throw new TypeError(`${t}: default export missing required "about" object`);if(typeof r.produce!="function")throw new TypeError(`${t}: default export missing required "produce" function`);return e},"validateTemplateExport"),d=a(async t=>{const e=await l(u(t),{fsCache:!1,moduleCache:!1}).import(t,{default:!0,try:!0})??null;return p(t,e)},"loadNativeTemplate");export{d as loadNativeTemplate};
@@ -0,0 +1 @@
1
+ import{otelPlugin as l}from"../config.js";export{l as otelPlugin};
@@ -0,0 +1,63 @@
1
+ import type { Task } from "@visulima/task-runner";
2
+ import type { VisPlugin } from "../hooks.d.ts";
3
+ /**
4
+ * Minimal OTel-shaped span. Deliberately structural so users can pass
5
+ * an `@opentelemetry/api` Tracer, an `@opentelemetry/sdk-node` one, or
6
+ * a custom implementation without the plugin depending on any
7
+ * particular OTel package.
8
+ */
9
+ export interface OtelSpan {
10
+ end: () => void;
11
+ recordException?: (error: unknown) => void;
12
+ setAttribute?: (key: string, value: boolean | number | string) => void;
13
+ setStatus?: (status: {
14
+ code: number;
15
+ message?: string;
16
+ }) => void;
17
+ }
18
+ /**
19
+ * Minimal Tracer contract. Accepts the real
20
+ * `@opentelemetry/api`'s `Tracer.startSpan(name, options?)` shape —
21
+ * the plugin only calls the two methods it strictly needs.
22
+ */
23
+ export interface OtelTracer {
24
+ startSpan: (name: string, options?: {
25
+ attributes?: Record<string, string | number | boolean>;
26
+ }) => OtelSpan;
27
+ }
28
+ export interface OtelPluginOptions {
29
+ /**
30
+ * Rename incoming `project:target` IDs before they become OTel
31
+ * span names. Defaults to passing the id through unchanged.
32
+ */
33
+ renameSpan?: (task: Task) => string;
34
+ /** Tracer used to emit spans. Required — pass the one from `@opentelemetry/api`'s `trace.getTracer("vis")`. */
35
+ tracer: OtelTracer;
36
+ }
37
+ /**
38
+ * Reference plugin that maps vis hook lifecycle events to OTel spans.
39
+ *
40
+ * Emits:
41
+ * - one **root span** named `vis.run` spanning `run:before` → `run:after`
42
+ * - one **child span** per task spanning `task:before` → `task:after`
43
+ * with attributes `vis.task.id`, `vis.task.project`, `vis.task.target`,
44
+ * `vis.task.cache_status`, `vis.task.exit_code`
45
+ * - `task:failure` sets span status to ERROR and records the exit code
46
+ *
47
+ * Streaming stdout/stderr events are intentionally **not** emitted as
48
+ * span events — high-frequency chunks would blow up OTel backends. Use
49
+ * a log exporter if you need stream-level visibility.
50
+ * @example
51
+ * ```ts
52
+ * import { trace } from "@opentelemetry/api";
53
+ * import { defineConfig } from "@visulima/vis/config";
54
+ * import { otelPlugin } from "@visulima/vis/plugins/otel";
55
+ *
56
+ * const tracer = trace.getTracer("vis", "1.0.0");
57
+ *
58
+ * export default defineConfig({
59
+ * plugins: [otelPlugin({ tracer })],
60
+ * });
61
+ * ```
62
+ */
63
+ export declare const otelPlugin: (options: OtelPluginOptions) => VisPlugin;
@@ -2,7 +2,7 @@
2
2
  * Shared helper for executing package manager commands via native Rust bindings.
3
3
  * Falls back to JS-based detection when native bindings are unavailable.
4
4
  */
5
- import type { AddOptions, DlxOptions, ExecOptions, InstallOptions, OutdatedOptions, RemoveOptions, WhyOptions } from "./native-binding.d.ts";
5
+ import type { AddOptions, DlxOptions, ExecOptions, InstallOptions, OutdatedOptions, RemoveOptions, ResolvedCommand, WhyOptions } from "./native-binding.d.ts";
6
6
  interface PmInfo {
7
7
  name: "bun" | "npm" | "pnpm" | "yarn";
8
8
  version: string;
@@ -14,10 +14,31 @@ declare const runRemove: (pm: PmInfo, options: RemoveOptions, cwd: string, logge
14
14
  declare const runDedupe: (pm: PmInfo, check: boolean, cwd: string, logger: Console) => number;
15
15
  declare const runWhy: (pm: PmInfo, options: WhyOptions, cwd: string, logger: Console) => number;
16
16
  declare const runOutdated: (pm: PmInfo, options: OutdatedOptions, cwd: string, logger: Console) => number;
17
+ interface InfoOptions {
18
+ fields: string[];
19
+ json: boolean;
20
+ package: string;
21
+ }
22
+ /**
23
+ * Resolves a registry metadata lookup to a runnable command. Built in TS
24
+ * rather than the Rust resolver because bun needs `pm view` (two-word
25
+ * subcommand) and yarn berry needs `yarn npm info` — both shapes the existing
26
+ * `resolve_pm_command` view branch gets wrong.
27
+ *
28
+ * Pure function — exported for unit testing.
29
+ */
30
+ declare const resolveInfo: (pm: PmInfo, options: InfoOptions) => ResolvedCommand;
31
+ declare const runInfo: (pm: PmInfo, options: InfoOptions, cwd: string, logger: Console) => number;
32
+ /**
33
+ * Resolves and runs a PM `link` operation. Passes `pm.version` to the native
34
+ * resolver so it can warn about pnpm v11 restrictions (arg-less link and
35
+ * global-store name resolution were removed). `target` is `null` for arg-less
36
+ * link, or a package name / path string.
37
+ */
17
38
  declare const runLink: (pm: PmInfo, target: string | null, cwd: string, logger: Console) => number;
18
39
  declare const runUnlink: (pm: PmInfo, packages: string[], recursive: boolean, cwd: string, logger: Console) => number;
19
40
  declare const runDlx: (pm: PmInfo, options: DlxOptions, cwd: string, logger: Console) => number;
20
41
  declare const runExec: (pm: PmInfo, options: ExecOptions, cwd: string, logger: Console) => number;
21
42
  declare const runPmSubcommand: (pm: PmInfo, subcommand: string, args: string[], cwd: string, logger: Console) => number;
22
- export type { PmInfo };
23
- export { detectPm, runAdd, runDedupe, runDlx, runExec, runInstall, runLink, runOutdated, runPmSubcommand, runRemove, runUnlink, runWhy };
43
+ export type { InfoOptions, PmInfo };
44
+ export { detectPm, resolveInfo, runAdd, runDedupe, runDlx, runExec, runInfo, runInstall, runLink, runOutdated, runPmSubcommand, runRemove, runUnlink, runWhy };
@@ -0,0 +1,40 @@
1
+ import type { TaskResults } from "@visulima/task-runner";
2
+ /**
3
+ * Formats a compact one-line timing summary for display after a run.
4
+ * @param results Task execution results.
5
+ * @param durationMs Total wall-clock duration in milliseconds.
6
+ * @returns A formatted summary string, e.g. "3 succeeded · 1 cached · 0 failed · 2.4s"
7
+ */
8
+ export declare const formatTimingSummary: (results: TaskResults, durationMs: number) => string;
9
+ /**
10
+ * Shape of a persisted run summary — only the fields this module + the
11
+ * flakiness analyzer consume. Kept narrow so both consumers can share
12
+ * one loader without pulling in the full `RunSummary` type from
13
+ * task-runner.
14
+ */
15
+ export interface LoadedRunSummary {
16
+ [key: string]: unknown;
17
+ duration?: number;
18
+ startTime?: string;
19
+ tasks?: unknown[];
20
+ }
21
+ /**
22
+ * Reads every `.task-runner/runs/*.json` once and returns the parsed
23
+ * array. Callers that need to iterate historical runs (timing average,
24
+ * flakiness analysis) should call this once per command and feed the
25
+ * result into the downstream helpers rather than re-reading the
26
+ * directory multiple times.
27
+ *
28
+ * Corrupt or unreadable files are skipped silently — a single bad
29
+ * summary shouldn't take down the whole analysis.
30
+ */
31
+ export declare const loadRunSummaries: (workspaceRoot: string) => LoadedRunSummary[];
32
+ /**
33
+ * Loads durations from historical run summaries and computes the
34
+ * average. Returns the comparison string or `undefined` if no history.
35
+ *
36
+ * Pass `summaries` (from {@link loadRunSummaries}) when the caller
37
+ * has already loaded the history for another purpose (e.g. flakiness
38
+ * analysis on a failing run) to avoid re-reading the same files.
39
+ */
40
+ export declare const compareDuration: (workspaceRoot: string, currentDurationMs: number, summaries?: LoadedRunSummary[]) => string | undefined;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Runtime-version mismatch finding. vis doesn't manage runtimes directly,
3
+ * but it can warn when the currently running Node/Bun/Deno doesn't match
4
+ * whatever the repo has declared — usually a sign the user forgot to
5
+ * switch runtimes for this workspace.
6
+ */
7
+ export interface RuntimeFinding {
8
+ actual: string;
9
+ expected: string;
10
+ kind: "node" | "packageManager";
11
+ message: string;
12
+ severity: "error" | "warning";
13
+ }
14
+ /**
15
+ * Evaluates a minimal subset of semver ranges: `>=X`, `X.Y`, `X.Y.Z`,
16
+ * and compound `>=X &lt;Y`. Returns true if `actual` satisfies the range.
17
+ * Falls back to "true" for unrecognised syntax rather than false so we
18
+ * don't spam warnings on exotic ranges.
19
+ */
20
+ export declare const satisfiesRange: (actual: string, range: string) => boolean;
21
+ /**
22
+ * Checks `engines.node`, `.nvmrc`, `.node-version`, and `packageManager`
23
+ * against the running process.
24
+ * @param workspaceRoot Absolute path to the workspace root.
25
+ * @returns Findings; an empty array means everything matches.
26
+ */
27
+ export declare const checkRuntimeVersions: (workspaceRoot: string) => RuntimeFinding[];