@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.
- package/dist/task/auto-orchestrator.d.ts +17 -0
- package/dist/task/auto-orchestrator.js +42 -1
- package/dist/task/orchestrator.d.ts +9 -1
- package/dist/task/orchestrator.js +6 -3
- package/dist/task/phases.d.ts +8 -1
- package/dist/task/phases.js +2 -2
- package/dist/task/prompts.d.ts +13 -1
- package/dist/task/prompts.js +15 -1
- package/dist/workers/docs-core.js +12 -6
- package/dist/workers/docs-resolve.d.ts +12 -0
- package/dist/workers/docs-resolve.js +24 -0
- package/package.json +1 -1
|
@@ -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
|
}
|
package/dist/task/phases.d.ts
CHANGED
|
@@ -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>;
|
package/dist/task/phases.js
CHANGED
|
@@ -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',
|
package/dist/task/prompts.d.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/task/prompts.js
CHANGED
|
@@ -49,7 +49,19 @@ Rules:
|
|
|
49
49
|
|
|
50
50
|
TITLE:
|
|
51
51
|
${title}`;
|
|
52
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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.
|
|
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",
|