@mjasnikovs/pi-task 0.14.5 → 0.14.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.
@@ -13,6 +13,8 @@ export interface AutoDeps {
13
13
  resumeId?: string;
14
14
  /** Called with the inner task id once its file exists, before phases. */
15
15
  onStart?: (taskId: string) => void | Promise<void>;
16
+ /** Scope fence naming the sibling steps, forwarded into refine. */
17
+ planContext?: string;
16
18
  }) => Promise<RunSingleTaskResult>;
17
19
  /** Snapshot the working tree into one commit after a task passes. */
18
20
  commit: (cwd: string, message: string) => Promise<CommitResult>;
@@ -62,6 +64,21 @@ export declare function readableMentions(cwd: string, feature: string): Promise<
62
64
  * exactly as before.
63
65
  */
64
66
  export declare function attachSpecRefs(titles: string[], refs: string[]): string[];
67
+ /**
68
+ * Build the refine scope fence for step `currentIndex` of an N-step /task-auto
69
+ * plan. Every per-step pipeline only ever sees its own title, so without this the
70
+ * refine phase — told "the task title is only a pointer into that spec; follow the
71
+ * spec" — re-expands the whole referenced design into one task (a real run
72
+ * implemented all 24 steps under step 1). The fence lists the sibling steps by
73
+ * number and forbids touching anything they own, so refine bounds this step's
74
+ * slice. Validated on the local model: with the fence, refine's CONSTRAINTS gained
75
+ * an explicit per-step deferral list and tool calls dropped 27→11.
76
+ *
77
+ * The plan listing strips the threaded "| decisions … | spec …" tail from each
78
+ * title (keeps the human-readable head) so the model reads clean step names. The
79
+ * authoritative spec ref still rides on THIS step's own title via attachSpecRefs.
80
+ */
81
+ export declare function buildScopeFence(titles: string[], currentIndex: number): string;
65
82
  /** Plan phase: clarify → decompose → write AUTO file. Returns the new id, or null. */
66
83
  export declare function planAuto(ctx: ExtensionCommandContext, cwd: string, feature: string, deps: AutoDeps): Promise<string | null>;
67
84
  export declare function requestAutoCancel(): void;
@@ -118,6 +118,40 @@ export function attachSpecRefs(titles, refs) {
118
118
  return out;
119
119
  });
120
120
  }
121
+ /**
122
+ * Build the refine scope fence for step `currentIndex` of an N-step /task-auto
123
+ * plan. Every per-step pipeline only ever sees its own title, so without this the
124
+ * refine phase — told "the task title is only a pointer into that spec; follow the
125
+ * spec" — re-expands the whole referenced design into one task (a real run
126
+ * implemented all 24 steps under step 1). The fence lists the sibling steps by
127
+ * number and forbids touching anything they own, so refine bounds this step's
128
+ * slice. Validated on the local model: with the fence, refine's CONSTRAINTS gained
129
+ * an explicit per-step deferral list and tool calls dropped 27→11.
130
+ *
131
+ * The plan listing strips the threaded "| decisions … | spec …" tail from each
132
+ * title (keeps the human-readable head) so the model reads clean step names. The
133
+ * authoritative spec ref still rides on THIS step's own title via attachSpecRefs.
134
+ */
135
+ export function buildScopeFence(titles, currentIndex) {
136
+ const n = titles.length;
137
+ const listing = titles
138
+ .map((t, i) => {
139
+ const head = t.split(' | ')[0].trim();
140
+ const tag = i === currentIndex ? ' (THIS STEP)' : '';
141
+ return `[${i + 1}]${tag} ${head}`;
142
+ })
143
+ .join('\n');
144
+ return (`PLAN CONTEXT — this task is STEP ${currentIndex + 1} of ${n} in an already-decomposed plan. `
145
+ + `Each step below is implemented by its OWN separate run; the others are NOT your job and `
146
+ + `are done in later runs. Implement ONLY the slice named in "Task" below.\n\n`
147
+ + `The design/spec document the task references describes the WHOLE system across all ${n} `
148
+ + `steps. Read it to get exact names, types, and signatures for YOUR step and to understand `
149
+ + `how your step fits — but DO NOT design, scaffold, schema, route, page, query, component, or `
150
+ + `test anything that belongs to another step listed below. Your GOAL / CONSTRAINTS / `
151
+ + `KNOWN-UNKNOWNS must cover only THIS step's slice. Do not pull in tables, endpoints, pages, `
152
+ + `components, or flows owned by a later step.\n\n`
153
+ + `The full plan (these run separately — do NOT implement them here):\n${listing}`);
154
+ }
121
155
  /** Plan phase: clarify → decompose → write AUTO file. Returns the new id, or null. */
122
156
  export async function planAuto(ctx, cwd, feature, deps) {
123
157
  // clarify — sequential & adaptive: ask one question at a time, feeding every
@@ -301,7 +335,8 @@ function defaultDeps(ctx, cwd, signal, title) {
301
335
  runTask: (c, cwd2, t, opts) => runSingleTask(c, cwd2, t, {
302
336
  waitForImplementation: true,
303
337
  resumeId: opts?.resumeId,
304
- onStart: opts?.onStart
338
+ onStart: opts?.onStart,
339
+ planContext: opts?.planContext
305
340
  }),
306
341
  commit: (cwd2, message) => getConfig().autoCommit ?
307
342
  gitCommitAll(cwd2, message, signal)
@@ -472,6 +507,12 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
472
507
  }
473
508
  const res = await deps.runTask(active, cwd, next.title, {
474
509
  resumeId,
510
+ // Fence this step against re-expanding the whole referenced spec:
511
+ // name the sibling steps so refine bounds this step's slice. Only
512
+ // matters when refine runs fresh (a resumed task past refine ignores
513
+ // it), but always supplied so a resume that restarts at refine is
514
+ // fenced too.
515
+ planContext: buildScopeFence(entries.map(e => e.title), next.index),
475
516
  onStart: resumeId ? undefined : (innerId => stampTaskInProgress(cwd, id, next.index, innerId, next.title))
476
517
  });
477
518
  active = res.ctx ?? active;
@@ -26,6 +26,7 @@ export declare class TaskRunner {
26
26
  private readonly _resumeId;
27
27
  private readonly _sendSpec;
28
28
  private readonly _onStart;
29
+ private readonly _planContext;
29
30
  private readonly _abort;
30
31
  private readonly _startedAt;
31
32
  private readonly _widgetState;
@@ -41,7 +42,7 @@ export declare class TaskRunner {
41
42
  */
42
43
  private readonly _timings;
43
44
  private _currentPhaseChildren;
44
- constructor(ctx: ExtensionCommandContext, cwd: string, rawPrompt: string, resumeId?: string, sendSpec?: (spec: string) => Promise<void>, spawnFn?: SpawnFn, onStart?: (taskId: string) => void | Promise<void>);
45
+ constructor(ctx: ExtensionCommandContext, cwd: string, rawPrompt: string, resumeId?: string, sendSpec?: (spec: string) => Promise<void>, spawnFn?: SpawnFn, onStart?: (taskId: string) => void | Promise<void>, planContext?: string);
45
46
  get taskId(): string;
46
47
  get signal(): AbortSignal;
47
48
  /** Return the current widget state, or null if not started. */
@@ -84,6 +85,13 @@ export interface RunSingleTaskOptions {
84
85
  * internal per-task runs, which must stay silent. Default false.
85
86
  */
86
87
  notifyFinish?: boolean;
88
+ /**
89
+ * Scope fence naming the sibling steps of a /task-auto plan. Forwarded into
90
+ * the refine phase so a single decomposed step bounds its slice instead of
91
+ * re-expanding the whole referenced spec doc. Set only by /task-auto's loop;
92
+ * a bare /task leaves it undefined and the refine prompt is unchanged.
93
+ */
94
+ planContext?: string;
87
95
  }
88
96
  export interface RunSingleTaskResult {
89
97
  taskId: string;
@@ -51,6 +51,7 @@ export class TaskRunner {
51
51
  _resumeId;
52
52
  _sendSpec;
53
53
  _onStart;
54
+ _planContext;
54
55
  _abort = new AbortController();
55
56
  _startedAt;
56
57
  _widgetState;
@@ -66,13 +67,14 @@ export class TaskRunner {
66
67
  */
67
68
  _timings = [];
68
69
  _currentPhaseChildren = null;
69
- constructor(ctx, cwd, rawPrompt, resumeId, sendSpec, spawnFn, onStart) {
70
+ constructor(ctx, cwd, rawPrompt, resumeId, sendSpec, spawnFn, onStart, planContext) {
70
71
  this._ctx = ctx;
71
72
  this._cwd = cwd;
72
73
  this._rawPrompt = rawPrompt;
73
74
  this._resumeId = resumeId;
74
75
  this._sendSpec = sendSpec;
75
76
  this._onStart = onStart;
77
+ this._planContext = planContext;
76
78
  this._startedAt = Date.now();
77
79
  // We'll populate id/title/phase lazily in run().
78
80
  // Placeholder — real values set in run().
@@ -109,7 +111,8 @@ export class TaskRunner {
109
111
  refined: '',
110
112
  research: '',
111
113
  qa: '',
112
- spec: ''
114
+ spec: '',
115
+ planContext: this._planContext
113
116
  };
114
117
  }
115
118
  get taskId() {
@@ -372,7 +375,7 @@ export async function runSingleTask(ctx, cwd, rawPrompt, opts = {}) {
372
375
  if (!interrupted)
373
376
  implError = implementationError(newCtx);
374
377
  }
375
- }, opts.spawnFn, opts.onStart);
378
+ }, opts.spawnFn, opts.onStart, opts.planContext);
376
379
  await runner.run();
377
380
  taskId = runner.taskId;
378
381
  }
@@ -23,6 +23,13 @@ export interface PhaseContext {
23
23
  research: string;
24
24
  qa: string;
25
25
  spec: string;
26
+ /**
27
+ * Scope fence for a task that is one step of a /task-auto plan: names the
28
+ * sibling steps so refine bounds this task's slice instead of re-expanding the
29
+ * whole spec doc. Undefined for a bare /task run (prompt unchanged). Threaded
30
+ * only into refine — its scoped output carries the boundary downstream.
31
+ */
32
+ planContext?: string;
26
33
  }
27
34
  export type OutputField = 'refined' | 'research' | 'qa' | 'spec';
28
35
  export interface PhaseConfig {
@@ -35,7 +42,7 @@ export interface PhaseConfig {
35
42
  export declare function extractToolingCommands(research: string): string[] | null;
36
43
  /** Replace the TOOLING section in a research string with a VERIFIED-TOOLING section. */
37
44
  export declare function replaceToolingWithVerified(research: string, verifiedCommands: string[]): string;
38
- export declare const phaseRefine: (deps: PhaseDeps, raw: string) => Promise<string>;
45
+ export declare const phaseRefine: (deps: PhaseDeps, raw: string, planContext?: string) => Promise<string>;
39
46
  export declare function phaseVerifyTooling(deps: PhaseDeps, research: string): Promise<string>;
40
47
  export interface PhaseResearchDeps extends ExternalContextDeps {
41
48
  getFileInventory?: (cwd: string, signal?: AbortSignal) => Promise<string>;
@@ -61,7 +61,7 @@ export function replaceToolingWithVerified(research, verifiedCommands) {
61
61
  return replaced;
62
62
  }
63
63
  // ─── Phase functions ─────────────────────────────────────────────────────────
64
- export const phaseRefine = (deps, raw) => runPhaseWithLoopGuard(deps, 'refine', 'read', hint => prependHint(hint, appendNoThink(REFINE_PROMPT(raw))));
64
+ export const phaseRefine = (deps, raw, planContext) => runPhaseWithLoopGuard(deps, 'refine', 'read', hint => prependHint(hint, appendNoThink(REFINE_PROMPT(raw, planContext))));
65
65
  export async function phaseVerifyTooling(deps, research) {
66
66
  const commands = extractToolingCommands(research);
67
67
  if (!commands || commands.length === 0) {
@@ -650,7 +650,7 @@ export const PHASES = [
650
650
  name: 'refine',
651
651
  section: 'refined prompt',
652
652
  field: 'refined',
653
- run: (d, p) => phaseRefine(d, p.rawPrompt)
653
+ run: (d, p) => phaseRefine(d, p.rawPrompt, p.planContext)
654
654
  },
655
655
  {
656
656
  name: 'research',
@@ -35,7 +35,19 @@ export declare function appendNoThink(prompt: string): string;
35
35
  * right *content* (identifying nouns) rather than trusting the model on length.
36
36
  */
37
37
  export declare const COMPRESS_LABEL_PROMPT: (title: string, maxChars: number) => string;
38
- declare const REFINE_PROMPT: (raw: string) => string;
38
+ /**
39
+ * Optional scope fence for a task that is ONE step of a /task-auto plan. Prepended
40
+ * verbatim ahead of the refine body so the model reads the step boundary as
41
+ * authoritative context before it expands the (whole-system) spec doc. Empty for a
42
+ * bare /task run, which keeps that prompt byte-for-byte unchanged. The caller
43
+ * (auto-orchestrator) builds the listing; this prompt only positions it.
44
+ *
45
+ * Without it, refine is told "the task title is only a pointer into that spec —
46
+ * follow the spec" with no signal that the other steps exist, so a one-step
47
+ * "Scaffold …" title re-expands the entire design into one task (validated: a real
48
+ * /task-auto run implemented all 24 steps under step 1).
49
+ */
50
+ declare const REFINE_PROMPT: (raw: string, planContext?: string) => string;
39
51
  declare const RESEARCH_READ_ONLY_CONSTRAINT = "IMPORTANT: You are ONLY allowed to READ. Do NOT create, modify, or delete any files. Use the read, grep, find, and ls tools to inspect the repo.";
40
52
  declare const RESEARCH_FILES_PROMPT: (refined: string) => string;
41
53
  declare const RESEARCH_APIS_PROMPT: (refined: string) => string;
@@ -49,7 +49,19 @@ Rules:
49
49
 
50
50
  TITLE:
51
51
  ${title}`;
52
- const REFINE_PROMPT = (raw) => `You receive a user's task description for an AI coding agent. Rewrite it to be unambiguous and actionable.
52
+ /**
53
+ * Optional scope fence for a task that is ONE step of a /task-auto plan. Prepended
54
+ * verbatim ahead of the refine body so the model reads the step boundary as
55
+ * authoritative context before it expands the (whole-system) spec doc. Empty for a
56
+ * bare /task run, which keeps that prompt byte-for-byte unchanged. The caller
57
+ * (auto-orchestrator) builds the listing; this prompt only positions it.
58
+ *
59
+ * Without it, refine is told "the task title is only a pointer into that spec —
60
+ * follow the spec" with no signal that the other steps exist, so a one-step
61
+ * "Scaffold …" title re-expands the entire design into one task (validated: a real
62
+ * /task-auto run implemented all 24 steps under step 1).
63
+ */
64
+ const REFINE_PROMPT = (raw, planContext) => `${planContext ? planContext + '\n\n---\n\n' : ''}You receive a user's task description for an AI coding agent. Rewrite it to be unambiguous and actionable.
53
65
 
54
66
  Output structure (four sections, exact headings, in this order):
55
67
 
@@ -116,6 +128,8 @@ NPM PACKAGES — use pi-worker-docs, NOT file reads: for any third-party npm pac
116
128
 
117
129
  PROJECT SOURCE — use pi-worker-docs with module ".", NOT file reads: for any function, class, type, or interface defined in THIS project's own .ts/.tsx source (e.g. "what does requireAuth check?", "what does CreateListingSchema look like?", "what does the listings query module export?"), call \`pi-worker-docs(".", query)\` instead of reading the file. The tool indexes all git-tracked source files and returns only the relevant chunks — far cheaper than reading whole files.
118
130
 
131
+ RUNTIME BUILTINS — verify, do NOT echo: a task (or the spec doc it references) may name a runtime/builtin import like \`bun:sql\`, \`bun:sqlite\`, \`node:fs\`, or \`Bun.password\`. A runtime exposes only a small FIXED set of \`<runtime>:<submodule>\` modules, and a spec doc can confidently name one that does not exist. Before you list ANY \`<pkg>:<sub>\` specifier, confirm it with \`pi-worker-docs\` (e.g. \`pi-worker-docs("bun:sql", "sql tagged template and SQL class — the import")\` — the tool resolves the runtime's real types) and emit the CANONICAL import the types actually prove, NOT the string copied from the task. Concretely: Bun's SQL client is \`import { sql } from "bun"\` (or \`Bun.sql\` / \`new SQL()\`) — there is NO \`bun:sql\` module. Never pass an unverified colon-specifier through to the APIS list; a phantom import laundered here becomes fabricated \`declare module\` shims in the implementation.
132
+
119
133
  APIS owns symbols and commands BY NAME ONLY. Do NOT include any file path or path fragment — no \`package.json\`, no \`./src/foo.ts\`, no \`package.json#scripts.lint\`. If the symbol is a script defined in package.json, write the invocation (\`npm run lint\`), not its location. If the symbol is a config file, it does not belong in APIS at all — it belongs in FILES.
120
134
 
121
135
  RELEVANCE — read carefully: list ONLY the symbols the agent will call, implement, modify, or directly depend on for THIS task. Do NOT enumerate the project's entire public surface or dump every exported function in a touched file. A symbol unrelated to the task does not belong here just because it sits in the same module. Keep the smallest sufficient set: include every symbol the task actually exercises and nothing more. There is no fixed limit — list as many as the task truly needs and no padding beyond that.
@@ -4,7 +4,7 @@ import * as os from 'node:os';
4
4
  import * as path from 'node:path';
5
5
  import { openCache as defaultOpenCache } from './docs-cache.js';
6
6
  import { ensureIndexed as defaultEnsureIndexed } from './docs-index.js';
7
- import { resolvePackage as defaultResolvePackage, ResolveError, detectTypesRedirect, typesPackageName, hasTypeFiles, isDtsFile } from './docs-resolve.js';
7
+ import { resolvePackage as defaultResolvePackage, ResolveError, detectTypesRedirect, typesPackageName, hasTypeFiles, isDtsFile, splitRuntimeNamespace } from './docs-resolve.js';
8
8
  import { retrieveChunks as defaultRetrieveChunks } from './docs-retrieve.js';
9
9
  import { npmVersionLookup as defaultNpmVersionLookup } from './npm-version.js';
10
10
  import { getPiInvocation } from '../shared/pi-invocation.js';
@@ -97,21 +97,27 @@ export async function docsRaw(input) {
97
97
  const openCache = input.openCache ?? defaultOpenCache;
98
98
  const spawn = input.spawn ?? defaultSpawn;
99
99
  const npmVersionLookup = input.npmVersionLookup ?? defaultNpmVersionLookup;
100
+ // A runtime builtin specifier (`bun:sql`, `node:fs`) is typed by the runtime's
101
+ // own types package, not a literal package of that colon-name — so resolve the
102
+ // runtime instead. This is what turns a `bun:sql` lookup into Bun's real SQL
103
+ // surface (`declare module "bun"` → `const sql: SQL`) rather than an
104
+ // `invalid_name` error, and it lets the docs tool disprove a phantom submodule.
105
+ const requested = splitRuntimeNamespace(input.pkg)?.runtime ?? input.pkg;
100
106
  // Fire the npm registry lookup in parallel with resolve/index/retrieve.
101
107
  // It returns null on any failure, so it never blocks the local pipeline.
102
- const npmVersionPromise = npmVersionLookup(extractParentPackage(input.pkg), {
108
+ const npmVersionPromise = npmVersionLookup(extractParentPackage(requested), {
103
109
  signal: input.signal
104
110
  }).catch(() => null);
105
111
  // Step 1: resolve package
106
112
  let pkg;
107
113
  let autoInstalled = false;
108
114
  try {
109
- pkg = resolvePackage(input.pkg, input.cwd);
115
+ pkg = resolvePackage(requested, input.cwd);
110
116
  }
111
117
  catch (firstErr) {
112
118
  if (firstErr instanceof ResolveError && firstErr.kind === 'not_installed') {
113
119
  // auto-install
114
- const parentPkg = extractParentPackage(input.pkg);
120
+ const parentPkg = extractParentPackage(requested);
115
121
  const installResult = await runAutoInstall(spawn, parentPkg, undefined);
116
122
  if (!installResult.success) {
117
123
  return {
@@ -124,7 +130,7 @@ export async function docsRaw(input) {
124
130
  }
125
131
  autoInstalled = true;
126
132
  try {
127
- pkg = resolvePackage(input.pkg, installResult.installDir);
133
+ pkg = resolvePackage(requested, installResult.installDir);
128
134
  }
129
135
  catch (retryErr) {
130
136
  if (retryErr instanceof ResolveError) {
@@ -166,7 +172,7 @@ export async function docsRaw(input) {
166
172
  // @types/<name> + triple-slash `<reference types>` chain to the package that
167
173
  // actually holds the declarations (e.g. bun -> @types/bun -> bun-types).
168
174
  // Best-effort: any failure leaves the original resolution untouched.
169
- pkg = await resolveTypeSource(pkg, input.pkg, input.cwd, spawn, resolvePackage, input.signal);
175
+ pkg = await resolveTypeSource(pkg, requested, input.cwd, spawn, resolvePackage, input.signal);
170
176
  // Step 2: open cache
171
177
  let cache = null;
172
178
  let cacheError;
@@ -11,6 +11,18 @@ export declare class ResolveError extends Error {
11
11
  readonly kind: 'not_installed' | 'invalid_name';
12
12
  constructor(kind: 'not_installed' | 'invalid_name', message: string);
13
13
  }
14
+ /**
15
+ * Split a runtime builtin specifier (`bun:sql`, `node:fs/promises`) into its
16
+ * runtime and submodule. Returns null for ordinary specifiers (including scoped
17
+ * names, which legitimately contain no colon). The resolver maps the runtime to
18
+ * its types package via the existing @types redirect chain (bun -> bun-types,
19
+ * node -> @types/node), so a `bun:sql` query lands on Bun's real SQL surface
20
+ * (`declare module "bun"` → `const sql: SQL`) instead of erroring `invalid_name`.
21
+ */
22
+ export declare function splitRuntimeNamespace(spec: string): {
23
+ runtime: string;
24
+ sub: string;
25
+ } | null;
14
26
  export declare function resolvePackage(moduleName: string, cwd: string): ResolvedPackage;
15
27
  /** Conventional DefinitelyTyped package for a runtime package that ships no
16
28
  * types of its own. `bun` -> `@types/bun`, `@scope/x` -> `@types/scope__x`.
@@ -20,6 +20,30 @@ function isValidModuleName(name) {
20
20
  return false;
21
21
  return MODULE_NAME_RE.test(name);
22
22
  }
23
+ // Runtimes whose builtin `<runtime>:<sub>` imports are typed by the runtime's own
24
+ // types package, not by a literal package named "<runtime>:<sub>". `node:fs` and
25
+ // `bun:sqlite` are real imports, but their declarations live in @types/node and
26
+ // bun-types — and a phantom like `bun:sql` (no such submodule) is only disprovable
27
+ // by resolving the runtime and finding the symbol absent. Either way the docs
28
+ // lookup target is the runtime, never the colon-name.
29
+ const RUNTIME_NAMESPACES = new Set(['bun', 'node', 'deno']);
30
+ /**
31
+ * Split a runtime builtin specifier (`bun:sql`, `node:fs/promises`) into its
32
+ * runtime and submodule. Returns null for ordinary specifiers (including scoped
33
+ * names, which legitimately contain no colon). The resolver maps the runtime to
34
+ * its types package via the existing @types redirect chain (bun -> bun-types,
35
+ * node -> @types/node), so a `bun:sql` query lands on Bun's real SQL surface
36
+ * (`declare module "bun"` → `const sql: SQL`) instead of erroring `invalid_name`.
37
+ */
38
+ export function splitRuntimeNamespace(spec) {
39
+ const m = /^([a-z]+):([a-z0-9./_-]+)$/i.exec(spec);
40
+ if (!m)
41
+ return null;
42
+ const runtime = m[1].toLowerCase();
43
+ if (!RUNTIME_NAMESPACES.has(runtime))
44
+ return null;
45
+ return { runtime, sub: m[2] };
46
+ }
23
47
  function parentPackageName(moduleName) {
24
48
  if (moduleName.startsWith('@')) {
25
49
  const parts = moduleName.split('/');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.14.5",
3
+ "version": "0.14.6",
4
4
  "description": "Deterministic spec-orchestration for local models, with a bundled real-time remote web view and web/docs/fetch/worker subagent tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",