toolcraft 0.0.23 → 0.0.24
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/README.md +2 -2
- package/dist/cli.compile-check.js +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +50 -13
- package/dist/error-report.js +32 -3
- package/dist/human-in-loop/approval-tasks.d.ts +1 -0
- package/dist/human-in-loop/approval-tasks.js +7 -5
- package/dist/human-in-loop/approvals-commands.js +51 -8
- package/dist/human-in-loop/runner.js +24 -19
- package/dist/human-in-loop/state-machine.d.ts +3 -3
- package/dist/human-in-loop/state-machine.js +13 -5
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -1
- package/dist/mcp-proxy.js +85 -19
- package/dist/mcp.compile-check.js +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +50 -8
- package/dist/renderer.js +119 -13
- package/dist/sdk.compile-check.js +1 -0
- package/dist/sdk.d.ts +1 -0
- package/dist/sdk.js +56 -11
- package/node_modules/@poe-code/agent-defs/dist/registry.d.ts +1 -1
- package/node_modules/@poe-code/agent-defs/dist/registry.js +22 -11
- package/node_modules/@poe-code/agent-defs/package.json +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript-script.js +5 -1
- package/node_modules/@poe-code/agent-human-in-loop/dist/providers/osascript.js +1 -1
- package/node_modules/@poe-code/agent-human-in-loop/package.json +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.d.ts +1 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +41 -92
- package/node_modules/@poe-code/agent-mcp-config/dist/configs.js +4 -1
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.d.ts +14 -2
- package/node_modules/@poe-code/agent-mcp-config/dist/shapes.js +11 -4
- package/node_modules/@poe-code/agent-mcp-config/package.json +1 -1
- package/node_modules/@poe-code/config-mutations/dist/execution/apply-mutation.js +200 -22
- package/node_modules/@poe-code/config-mutations/dist/execution/path-utils.js +7 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/index.js +1 -1
- package/node_modules/@poe-code/config-mutations/dist/formats/json.js +11 -7
- package/node_modules/@poe-code/config-mutations/dist/formats/object.d.ts +4 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/object.js +27 -0
- package/node_modules/@poe-code/config-mutations/dist/formats/toml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/formats/yaml.js +12 -9
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.d.ts +11 -1
- package/node_modules/@poe-code/config-mutations/dist/mutations/file-mutation.js +10 -1
- package/node_modules/@poe-code/config-mutations/dist/testing/mock-fs.js +25 -1
- package/node_modules/@poe-code/config-mutations/dist/types.d.ts +12 -2
- package/node_modules/@poe-code/config-mutations/package.json +1 -1
- package/node_modules/@poe-code/design-system/dist/acp/components.js +3 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/browser.js +6 -1
- package/node_modules/@poe-code/design-system/dist/components/color.js +9 -8
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +3 -2
- package/node_modules/@poe-code/design-system/dist/components/detail-card.d.ts +22 -0
- package/node_modules/@poe-code/design-system/dist/components/detail-card.js +69 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +88 -11
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +1 -1
- package/node_modules/@poe-code/design-system/dist/components/index.js +1 -1
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +82 -5
- package/node_modules/@poe-code/design-system/dist/components/template.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/components/template.js +198 -32
- package/node_modules/@poe-code/design-system/dist/components/text.js +29 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +2 -2
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +77 -32
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +28 -5
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +45 -28
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal-width.js +71 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/events.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/explorer/reducer.js +32 -10
- package/node_modules/@poe-code/design-system/dist/explorer/render/detail.js +3 -0
- package/node_modules/@poe-code/design-system/dist/explorer/runtime.js +57 -6
- package/node_modules/@poe-code/design-system/dist/explorer/state.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/explorer/state.js +12 -15
- package/node_modules/@poe-code/design-system/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/design-system/dist/index.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +2 -1
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +8 -5
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +1 -1
- package/node_modules/@poe-code/design-system/dist/static/menu.js +8 -2
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +10 -4
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +9 -2
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +19 -2
- package/node_modules/@poe-code/design-system/package.json +2 -1
- package/node_modules/@poe-code/process-runner/dist/docker/docker-execution-env.js +244 -110
- package/node_modules/@poe-code/process-runner/dist/docker/docker-runner.js +16 -4
- package/node_modules/@poe-code/process-runner/dist/host/host-execution-env.js +3 -2
- package/node_modules/@poe-code/process-runner/dist/host/host-runner.js +16 -1
- package/node_modules/@poe-code/process-runner/dist/index.d.ts +1 -0
- package/node_modules/@poe-code/process-runner/dist/index.js +1 -0
- package/node_modules/@poe-code/process-runner/dist/testing/mock-runner.js +30 -8
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +3 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.d.ts +57 -0
- package/node_modules/@poe-code/process-runner/dist/workspace-transfer.js +484 -0
- package/node_modules/@poe-code/process-runner/package.json +1 -1
- package/node_modules/@poe-code/task-list/README.md +0 -2
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-client.js +3 -0
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues-sync.js +89 -59
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.d.ts +9 -3
- package/node_modules/@poe-code/task-list/dist/backends/gh-issues.js +460 -99
- package/node_modules/@poe-code/task-list/dist/backends/markdown-dir.js +156 -154
- package/node_modules/@poe-code/task-list/dist/backends/utils.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/backends/utils.js +79 -0
- package/node_modules/@poe-code/task-list/dist/backends/yaml-file.js +120 -132
- package/node_modules/@poe-code/task-list/dist/index.d.ts +3 -1
- package/node_modules/@poe-code/task-list/dist/index.js +2 -0
- package/node_modules/@poe-code/task-list/dist/move.d.ts +2 -0
- package/node_modules/@poe-code/task-list/dist/move.js +215 -0
- package/node_modules/@poe-code/task-list/dist/open.js +3 -4
- package/node_modules/@poe-code/task-list/dist/state-machine.js +3 -1
- package/node_modules/@poe-code/task-list/dist/state.js +9 -0
- package/node_modules/@poe-code/task-list/dist/types.d.ts +48 -13
- package/node_modules/@poe-code/task-list/package.json +1 -2
- package/node_modules/auth-store/dist/create-secret-store.js +4 -1
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +7 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +69 -7
- package/node_modules/auth-store/dist/index.d.ts +1 -1
- package/node_modules/auth-store/dist/keychain-store.d.ts +4 -1
- package/node_modules/auth-store/dist/keychain-store.js +18 -16
- package/node_modules/auth-store/dist/provider-store.d.ts +5 -1
- package/node_modules/auth-store/dist/provider-store.js +55 -7
- package/node_modules/auth-store/dist/types.d.ts +3 -1
- package/node_modules/auth-store/package.json +2 -1
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +46 -15
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +49 -12
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +6 -1
- package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +1 -1
- package/node_modules/mcp-oauth/package.json +1 -0
- package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
- package/node_modules/tiny-mcp-client/dist/internal.d.ts +8 -4
- package/node_modules/tiny-mcp-client/dist/internal.js +237 -67
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +1 -1
- package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +4 -7
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.integration.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +46 -0
- package/node_modules/tiny-mcp-client/src/internal.ts +279 -77
- package/node_modules/tiny-mcp-client/src/mcp-client-tiny-stdio-test-server-tools.test.ts +1 -1
- package/node_modules/tiny-mcp-client/src/oauth-discovery.ts +5 -10
- package/node_modules/tiny-mcp-client/src/transports.test.ts +588 -6
- package/package.json +10 -12
- package/node_modules/@poe-code/file-lock/README.md +0 -52
- package/node_modules/@poe-code/file-lock/dist/index.d.ts +0 -1
- package/node_modules/@poe-code/file-lock/dist/index.js +0 -1
- package/node_modules/@poe-code/file-lock/dist/lock.d.ts +0 -27
- package/node_modules/@poe-code/file-lock/dist/lock.js +0 -203
- package/node_modules/@poe-code/file-lock/package.json +0 -23
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const MAX_PARTIAL_DEPTH = 100;
|
|
1
2
|
const HTML_ESCAPE = {
|
|
2
3
|
"&": "&",
|
|
3
4
|
"<": "<",
|
|
@@ -11,14 +12,34 @@ const HTML_ESCAPE = {
|
|
|
11
12
|
export function renderTemplate(template, view, options = {}) {
|
|
12
13
|
const prepared = options.yield === undefined
|
|
13
14
|
? template
|
|
14
|
-
: template.
|
|
15
|
+
: resolveTemplatePartials(template, options.partials ?? {})
|
|
16
|
+
.split("{{yield}}")
|
|
17
|
+
.join(options.yield);
|
|
15
18
|
const tokens = parseTemplate(prepared);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
validatePartialReferences(tokens, options.partials ?? {}, []);
|
|
20
|
+
if (options.validate === true) {
|
|
21
|
+
const expanded = resolveTemplatePartials(prepared, options.partials ?? {});
|
|
22
|
+
validateVariables(parseTemplate(expanded), { view });
|
|
23
|
+
}
|
|
24
|
+
const state = {
|
|
25
|
+
escape: options.escape === "none" ? String : escapeHtml,
|
|
26
|
+
partials: options.partials ?? {},
|
|
27
|
+
partialStack: [],
|
|
28
|
+
preserveMissing: options.yield !== undefined && options.escape === "none",
|
|
29
|
+
validate: options.validate === true
|
|
30
|
+
};
|
|
31
|
+
return renderTokens(tokens, { view }, prepared, state);
|
|
32
|
+
}
|
|
33
|
+
export function getTemplatePartialNames(template) {
|
|
34
|
+
const names = new Set();
|
|
35
|
+
collectPartialNames(parseTemplate(template), names);
|
|
36
|
+
return [...names];
|
|
37
|
+
}
|
|
38
|
+
export function resolveTemplatePartials(template, partials) {
|
|
39
|
+
return expandTemplatePartials(template, partials, []);
|
|
19
40
|
}
|
|
20
|
-
function renderTemplateInContext(template, context,
|
|
21
|
-
return renderTokens(parseTemplate(template), context, template,
|
|
41
|
+
function renderTemplateInContext(template, context, state) {
|
|
42
|
+
return renderTokens(parseTemplate(template), context, template, state);
|
|
22
43
|
}
|
|
23
44
|
function parseTemplate(template) {
|
|
24
45
|
const root = [];
|
|
@@ -41,9 +62,6 @@ function parseTemplate(template) {
|
|
|
41
62
|
index = standalone?.nextIndex ?? parsed.end;
|
|
42
63
|
continue;
|
|
43
64
|
}
|
|
44
|
-
if (parsed.kind === "partial") {
|
|
45
|
-
throw new Error(`Partials are not supported: "${parsed.name}"`);
|
|
46
|
-
}
|
|
47
65
|
if (parsed.kind === "delimiter") {
|
|
48
66
|
throw new Error("Custom delimiters are not supported");
|
|
49
67
|
}
|
|
@@ -61,6 +79,15 @@ function parseTemplate(template) {
|
|
|
61
79
|
index = standalone?.nextIndex ?? parsed.end;
|
|
62
80
|
continue;
|
|
63
81
|
}
|
|
82
|
+
if (parsed.kind === "partial") {
|
|
83
|
+
tokens.push({
|
|
84
|
+
type: "partial",
|
|
85
|
+
name: parsed.name,
|
|
86
|
+
indent: standalone === undefined ? "" : template.slice(standalone.lineStart, open)
|
|
87
|
+
});
|
|
88
|
+
index = standalone?.nextIndex ?? parsed.end;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
64
91
|
if (parsed.kind === "close") {
|
|
65
92
|
const frame = stack.pop();
|
|
66
93
|
if (frame === undefined) {
|
|
@@ -138,7 +165,7 @@ function getStandalone(template, tagStart, tagEnd, kind) {
|
|
|
138
165
|
}
|
|
139
166
|
return undefined;
|
|
140
167
|
}
|
|
141
|
-
function renderTokens(tokens, context, template,
|
|
168
|
+
function renderTokens(tokens, context, template, state) {
|
|
142
169
|
let output = "";
|
|
143
170
|
for (const token of tokens) {
|
|
144
171
|
switch (token.type) {
|
|
@@ -147,48 +174,76 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
|
|
|
147
174
|
continue;
|
|
148
175
|
case "name":
|
|
149
176
|
case "unescaped": {
|
|
150
|
-
const
|
|
151
|
-
if (value == null) {
|
|
152
|
-
if (
|
|
177
|
+
const result = lookup(context, token.name);
|
|
178
|
+
if (!result.hit || result.value == null) {
|
|
179
|
+
if (state.validate) {
|
|
180
|
+
throw new Error(`Template variable "${token.name}" not found.`);
|
|
181
|
+
}
|
|
182
|
+
if (state.preserveMissing) {
|
|
153
183
|
output += token.raw;
|
|
154
184
|
}
|
|
155
185
|
continue;
|
|
156
186
|
}
|
|
157
|
-
const rendered = String(value);
|
|
158
|
-
output += token.type === "name" ? escape(rendered) : rendered;
|
|
187
|
+
const rendered = String(result.value);
|
|
188
|
+
output += token.type === "name" ? state.escape(rendered) : rendered;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
case "partial": {
|
|
192
|
+
if (!Object.hasOwn(state.partials, token.name)) {
|
|
193
|
+
throw new Error(`Partial "${token.name}" not found.`);
|
|
194
|
+
}
|
|
195
|
+
if (state.partialStack.includes(token.name)) {
|
|
196
|
+
throw new Error(`Circular partial reference detected: ${[...state.partialStack, token.name].join(" -> ")}.`);
|
|
197
|
+
}
|
|
198
|
+
if (state.partialStack.length >= MAX_PARTIAL_DEPTH) {
|
|
199
|
+
throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
|
|
200
|
+
}
|
|
201
|
+
const partial = indentPartial(state.partials[token.name], token.indent);
|
|
202
|
+
output += renderTokens(parseTemplate(partial), context, partial, {
|
|
203
|
+
...state,
|
|
204
|
+
partialStack: [...state.partialStack, token.name]
|
|
205
|
+
});
|
|
159
206
|
continue;
|
|
160
207
|
}
|
|
161
208
|
case "inverted": {
|
|
162
|
-
const
|
|
209
|
+
const result = lookup(context, token.name);
|
|
210
|
+
if (!result.hit && state.validate) {
|
|
211
|
+
throw new Error(`Template variable "${token.name}" not found.`);
|
|
212
|
+
}
|
|
213
|
+
const value = result.value;
|
|
163
214
|
if (!value || (Array.isArray(value) && value.length === 0)) {
|
|
164
|
-
output += renderTokens(token.children, context, template,
|
|
215
|
+
output += renderTokens(token.children, context, template, state);
|
|
165
216
|
}
|
|
166
217
|
continue;
|
|
167
218
|
}
|
|
168
219
|
case "section": {
|
|
169
|
-
const
|
|
220
|
+
const result = lookup(context, token.name);
|
|
221
|
+
if (!result.hit && state.validate) {
|
|
222
|
+
throw new Error(`Template variable "${token.name}" not found.`);
|
|
223
|
+
}
|
|
224
|
+
const value = result.value;
|
|
170
225
|
if (!value) {
|
|
171
226
|
continue;
|
|
172
227
|
}
|
|
173
228
|
if (Array.isArray(value)) {
|
|
174
229
|
for (const item of value) {
|
|
175
|
-
output += renderTokens(token.children, pushContext(context, item), template,
|
|
230
|
+
output += renderTokens(token.children, pushContext(context, item), template, state);
|
|
176
231
|
}
|
|
177
232
|
continue;
|
|
178
233
|
}
|
|
179
234
|
if (typeof value === "function") {
|
|
180
235
|
const raw = template.slice(token.rawStart, token.rawEnd);
|
|
181
|
-
const rendered = value.call(context.view, raw, (nextTemplate) => renderTemplateInContext(nextTemplate, context,
|
|
236
|
+
const rendered = value.call(context.view, raw, (nextTemplate) => renderTemplateInContext(nextTemplate, context, state));
|
|
182
237
|
if (rendered != null) {
|
|
183
238
|
output += String(rendered);
|
|
184
239
|
}
|
|
185
240
|
continue;
|
|
186
241
|
}
|
|
187
242
|
if (typeof value === "object" || typeof value === "string" || typeof value === "number") {
|
|
188
|
-
output += renderTokens(token.children, pushContext(context, value), template,
|
|
243
|
+
output += renderTokens(token.children, pushContext(context, value), template, state);
|
|
189
244
|
continue;
|
|
190
245
|
}
|
|
191
|
-
output += renderTokens(token.children, context, template,
|
|
246
|
+
output += renderTokens(token.children, context, template, state);
|
|
192
247
|
}
|
|
193
248
|
}
|
|
194
249
|
}
|
|
@@ -196,7 +251,7 @@ function renderTokens(tokens, context, template, escape, preserveMissing) {
|
|
|
196
251
|
}
|
|
197
252
|
function lookup(context, name) {
|
|
198
253
|
if (name === ".") {
|
|
199
|
-
return callLambda(context.view, context.view);
|
|
254
|
+
return { hit: true, value: callLambda(context.view, context.view) };
|
|
200
255
|
}
|
|
201
256
|
let cursor = context;
|
|
202
257
|
while (cursor !== undefined) {
|
|
@@ -204,11 +259,124 @@ function lookup(context, name) {
|
|
|
204
259
|
? lookupDotted(cursor.view, name)
|
|
205
260
|
: lookupName(cursor.view, name);
|
|
206
261
|
if (result.hit) {
|
|
207
|
-
return callLambda(result.value, cursor.view);
|
|
262
|
+
return { hit: true, value: callLambda(result.value, cursor.view) };
|
|
208
263
|
}
|
|
209
264
|
cursor = cursor.parent;
|
|
210
265
|
}
|
|
211
|
-
return undefined;
|
|
266
|
+
return { hit: false, value: undefined };
|
|
267
|
+
}
|
|
268
|
+
function collectPartialNames(tokens, names) {
|
|
269
|
+
for (const token of tokens) {
|
|
270
|
+
if (token.type === "partial") {
|
|
271
|
+
names.add(token.name);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (token.type === "section" || token.type === "inverted") {
|
|
275
|
+
collectPartialNames(token.children, names);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function validateVariables(tokens, context) {
|
|
280
|
+
for (const token of tokens) {
|
|
281
|
+
if (token.type === "text" || token.type === "partial") {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (token.type === "name" || token.type === "unescaped") {
|
|
285
|
+
if (!lookup(context, token.name).hit) {
|
|
286
|
+
throw new Error(`Template variable "${token.name}" not found.`);
|
|
287
|
+
}
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (token.type !== "section" && token.type !== "inverted") {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const result = lookup(context, token.name);
|
|
294
|
+
if (!result.hit) {
|
|
295
|
+
throw new Error(`Template variable "${token.name}" not found.`);
|
|
296
|
+
}
|
|
297
|
+
if (Array.isArray(result.value) && result.value.length > 0) {
|
|
298
|
+
for (const item of result.value) {
|
|
299
|
+
validateVariables(token.children, pushContext(context, item));
|
|
300
|
+
}
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (typeof result.value === "object" && result.value !== null ||
|
|
304
|
+
typeof result.value === "string" ||
|
|
305
|
+
typeof result.value === "number") {
|
|
306
|
+
validateVariables(token.children, pushContext(context, result.value));
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
validateVariables(token.children, context);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function validatePartialReferences(tokens, partials, partialStack) {
|
|
313
|
+
for (const token of tokens) {
|
|
314
|
+
if (token.type === "section" || token.type === "inverted") {
|
|
315
|
+
validatePartialReferences(token.children, partials, partialStack);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (token.type !== "partial") {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (!Object.hasOwn(partials, token.name)) {
|
|
322
|
+
throw new Error(`Partial "${token.name}" not found.`);
|
|
323
|
+
}
|
|
324
|
+
if (partialStack.includes(token.name)) {
|
|
325
|
+
throw new Error(`Circular partial reference detected: ${[...partialStack, token.name].join(" -> ")}.`);
|
|
326
|
+
}
|
|
327
|
+
if (partialStack.length >= MAX_PARTIAL_DEPTH) {
|
|
328
|
+
throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
|
|
329
|
+
}
|
|
330
|
+
validatePartialReferences(parseTemplate(partials[token.name]), partials, [
|
|
331
|
+
...partialStack,
|
|
332
|
+
token.name
|
|
333
|
+
]);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function indentPartial(partial, indent) {
|
|
337
|
+
if (indent === "") {
|
|
338
|
+
return partial;
|
|
339
|
+
}
|
|
340
|
+
return partial
|
|
341
|
+
.split("\n")
|
|
342
|
+
.map((line) => (line === "" ? "" : `${indent}${line}`))
|
|
343
|
+
.join("\n");
|
|
344
|
+
}
|
|
345
|
+
function expandTemplatePartials(template, partials, partialStack) {
|
|
346
|
+
let output = "";
|
|
347
|
+
let index = 0;
|
|
348
|
+
while (index < template.length) {
|
|
349
|
+
const open = template.indexOf("{{", index);
|
|
350
|
+
if (open === -1) {
|
|
351
|
+
output += template.slice(index);
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
const parsed = parseTag(template, open);
|
|
355
|
+
if (parsed.kind !== "partial") {
|
|
356
|
+
output += template.slice(index, parsed.end);
|
|
357
|
+
index = parsed.end;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (!Object.hasOwn(partials, parsed.name)) {
|
|
361
|
+
throw new Error(`Partial "${parsed.name}" not found.`);
|
|
362
|
+
}
|
|
363
|
+
if (partialStack.includes(parsed.name)) {
|
|
364
|
+
throw new Error(`Circular partial reference detected: ${[...partialStack, parsed.name].join(" -> ")}.`);
|
|
365
|
+
}
|
|
366
|
+
if (partialStack.length >= MAX_PARTIAL_DEPTH) {
|
|
367
|
+
throw new Error(`Maximum partial depth exceeded (${MAX_PARTIAL_DEPTH}).`);
|
|
368
|
+
}
|
|
369
|
+
const standalone = getStandalone(template, open, parsed.end, parsed.kind);
|
|
370
|
+
const beforePartial = standalone === undefined
|
|
371
|
+
? template.slice(index, open)
|
|
372
|
+
: template.slice(index, standalone.lineStart);
|
|
373
|
+
const indent = standalone === undefined ? "" : template.slice(standalone.lineStart, open);
|
|
374
|
+
const partial = indentPartial(partials[parsed.name], indent);
|
|
375
|
+
output +=
|
|
376
|
+
beforePartial + expandTemplatePartials(partial, partials, [...partialStack, parsed.name]);
|
|
377
|
+
index = standalone?.nextIndex ?? parsed.end;
|
|
378
|
+
}
|
|
379
|
+
return output;
|
|
212
380
|
}
|
|
213
381
|
function lookupName(view, name) {
|
|
214
382
|
if (!isPropertyContainer(view) || !hasProperty(view, name)) {
|
|
@@ -219,15 +387,13 @@ function lookupName(view, name) {
|
|
|
219
387
|
function lookupDotted(view, name) {
|
|
220
388
|
const parts = name.split(".");
|
|
221
389
|
let value = view;
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (index === parts.length - 1) {
|
|
226
|
-
hit = hasProperty(Object(value), part);
|
|
390
|
+
for (const part of parts) {
|
|
391
|
+
if (!isPropertyContainer(value) || !hasProperty(value, part)) {
|
|
392
|
+
return { hit: false, value: undefined };
|
|
227
393
|
}
|
|
228
394
|
value = Object(value)[part];
|
|
229
395
|
}
|
|
230
|
-
return { hit, value };
|
|
396
|
+
return { hit: true, value };
|
|
231
397
|
}
|
|
232
398
|
function callLambda(value, view) {
|
|
233
399
|
return typeof value === "function" ? value.call(view) : value;
|
|
@@ -267,5 +433,5 @@ function isPropertyContainer(value) {
|
|
|
267
433
|
return (typeof value === "object" && value !== null) || typeof value === "function";
|
|
268
434
|
}
|
|
269
435
|
function hasProperty(value, key) {
|
|
270
|
-
return key
|
|
436
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
271
437
|
}
|
|
@@ -2,6 +2,30 @@ import { color } from "./color.js";
|
|
|
2
2
|
import { resolveOutputFormat } from "../internal/output-format.js";
|
|
3
3
|
import { getTheme } from "../internal/theme-detect.js";
|
|
4
4
|
import { typography } from "../tokens/typography.js";
|
|
5
|
+
function renderMarkdownInline(content) {
|
|
6
|
+
return content.replaceAll("\r\n", " ").replaceAll("\n", " ").replaceAll("\r", " ");
|
|
7
|
+
}
|
|
8
|
+
function renderMarkdownCode(content) {
|
|
9
|
+
const value = renderMarkdownInline(content);
|
|
10
|
+
let longestRun = 0;
|
|
11
|
+
let currentRun = 0;
|
|
12
|
+
for (const char of value) {
|
|
13
|
+
if (char === "`") {
|
|
14
|
+
currentRun += 1;
|
|
15
|
+
longestRun = Math.max(longestRun, currentRun);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
currentRun = 0;
|
|
19
|
+
}
|
|
20
|
+
const delimiter = "`".repeat(longestRun + 1);
|
|
21
|
+
return `${delimiter}${value}${delimiter}`;
|
|
22
|
+
}
|
|
23
|
+
function renderMarkdownLink(content) {
|
|
24
|
+
const value = renderMarkdownInline(content);
|
|
25
|
+
const label = value.replaceAll("\\", "\\\\").replaceAll("[", "\\[").replaceAll("]", "\\]");
|
|
26
|
+
const url = value.replaceAll("\\", "\\\\").replaceAll("(", "\\(").replaceAll(")", "\\)");
|
|
27
|
+
return `[${label}](${url})`;
|
|
28
|
+
}
|
|
5
29
|
export const text = {
|
|
6
30
|
intro(content) {
|
|
7
31
|
const format = resolveOutputFormat();
|
|
@@ -40,7 +64,7 @@ export const text = {
|
|
|
40
64
|
if (format === "json")
|
|
41
65
|
return content;
|
|
42
66
|
if (format === "markdown")
|
|
43
|
-
return
|
|
67
|
+
return renderMarkdownCode(content);
|
|
44
68
|
return getTheme().accent(content);
|
|
45
69
|
},
|
|
46
70
|
argument(content) {
|
|
@@ -56,7 +80,7 @@ export const text = {
|
|
|
56
80
|
if (format === "json")
|
|
57
81
|
return content;
|
|
58
82
|
if (format === "markdown")
|
|
59
|
-
return
|
|
83
|
+
return renderMarkdownCode(content);
|
|
60
84
|
return color.yellow(content);
|
|
61
85
|
},
|
|
62
86
|
example(content) {
|
|
@@ -64,7 +88,7 @@ export const text = {
|
|
|
64
88
|
if (format === "json")
|
|
65
89
|
return content;
|
|
66
90
|
if (format === "markdown")
|
|
67
|
-
return
|
|
91
|
+
return renderMarkdownCode(content);
|
|
68
92
|
return getTheme().muted(content);
|
|
69
93
|
},
|
|
70
94
|
usageCommand(content) {
|
|
@@ -72,7 +96,7 @@ export const text = {
|
|
|
72
96
|
if (format === "json")
|
|
73
97
|
return content;
|
|
74
98
|
if (format === "markdown")
|
|
75
|
-
return
|
|
99
|
+
return renderMarkdownCode(content);
|
|
76
100
|
return color.green(content);
|
|
77
101
|
},
|
|
78
102
|
link(content) {
|
|
@@ -80,7 +104,7 @@ export const text = {
|
|
|
80
104
|
if (format === "json")
|
|
81
105
|
return content;
|
|
82
106
|
if (format === "markdown")
|
|
83
|
-
return
|
|
107
|
+
return renderMarkdownLink(content);
|
|
84
108
|
return getTheme().accent(content);
|
|
85
109
|
},
|
|
86
110
|
muted(content) {
|
|
@@ -12,7 +12,7 @@ export declare function hasAnsi(text: string): boolean;
|
|
|
12
12
|
* logical lines split on "\n". Each line is a list of styled segments: contiguous
|
|
13
13
|
* printable runs of characters sharing the same style.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* used as the initial style and as the restore target for SGR reset / default color.
|
|
15
|
+
* Common cursor-affecting line controls are rendered into their visible result.
|
|
16
|
+
* `baseStyle` is used as the initial style and as the restore target for SGR reset / default color.
|
|
17
17
|
*/
|
|
18
18
|
export declare function parseAnsi(text: string, baseStyle?: CellStyle): StyledLine[];
|
|
@@ -7,38 +7,25 @@ export function hasAnsi(text) {
|
|
|
7
7
|
* logical lines split on "\n". Each line is a list of styled segments: contiguous
|
|
8
8
|
* printable runs of characters sharing the same style.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* used as the initial style and as the restore target for SGR reset / default color.
|
|
10
|
+
* Common cursor-affecting line controls are rendered into their visible result.
|
|
11
|
+
* `baseStyle` is used as the initial style and as the restore target for SGR reset / default color.
|
|
12
12
|
*/
|
|
13
13
|
export function parseAnsi(text, baseStyle) {
|
|
14
14
|
const base = normalizeStyle(baseStyle);
|
|
15
15
|
let style = { ...base };
|
|
16
|
+
let concealed = false;
|
|
16
17
|
const lines = [];
|
|
17
|
-
let
|
|
18
|
-
let
|
|
19
|
-
const flushSegment = () => {
|
|
20
|
-
if (pending.length === 0) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
const last = segments[segments.length - 1];
|
|
24
|
-
if (last && stylesEqual(last.style, style)) {
|
|
25
|
-
last.text += pending;
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
segments.push({ text: pending, style: { ...style } });
|
|
29
|
-
}
|
|
30
|
-
pending = "";
|
|
31
|
-
};
|
|
18
|
+
let cells = [];
|
|
19
|
+
let column = 0;
|
|
32
20
|
const finishLine = () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
lines.push({ segments: cellsToSegments(cells) });
|
|
22
|
+
cells = [];
|
|
23
|
+
column = 0;
|
|
36
24
|
};
|
|
37
25
|
let index = 0;
|
|
38
26
|
while (index < text.length) {
|
|
39
27
|
const ch = text[index];
|
|
40
28
|
if (ch === ESC && text[index + 1] === "[") {
|
|
41
|
-
flushSegment();
|
|
42
29
|
const paramsStart = index + 2;
|
|
43
30
|
let cursor = paramsStart;
|
|
44
31
|
while (cursor < text.length && !isCsiFinalByte(text[cursor])) {
|
|
@@ -51,13 +38,17 @@ export function parseAnsi(text, baseStyle) {
|
|
|
51
38
|
const params = text.slice(paramsStart, cursor);
|
|
52
39
|
const finalByte = text[cursor];
|
|
53
40
|
if (finalByte === "m") {
|
|
54
|
-
|
|
41
|
+
const sgr = applySgr(style, concealed, parseParams(params), base);
|
|
42
|
+
style = sgr.style;
|
|
43
|
+
concealed = sgr.concealed;
|
|
44
|
+
}
|
|
45
|
+
else if (finalByte === "K" && parseParams(params)[0] === 2) {
|
|
46
|
+
cells = [];
|
|
55
47
|
}
|
|
56
48
|
index = cursor + 1;
|
|
57
49
|
continue;
|
|
58
50
|
}
|
|
59
51
|
if (ch === ESC) {
|
|
60
|
-
flushSegment();
|
|
61
52
|
const next = text[index + 1];
|
|
62
53
|
if (next === "]" || next === "P" || next === "X" || next === "^" || next === "_") {
|
|
63
54
|
index = skipStringTerminated(text, index + 2);
|
|
@@ -67,6 +58,7 @@ export function parseAnsi(text, baseStyle) {
|
|
|
67
58
|
continue;
|
|
68
59
|
}
|
|
69
60
|
if (ch === "\r") {
|
|
61
|
+
column = 0;
|
|
70
62
|
index += 1;
|
|
71
63
|
continue;
|
|
72
64
|
}
|
|
@@ -75,17 +67,37 @@ export function parseAnsi(text, baseStyle) {
|
|
|
75
67
|
index += 1;
|
|
76
68
|
continue;
|
|
77
69
|
}
|
|
70
|
+
if (ch === "\b") {
|
|
71
|
+
column = Math.max(0, column - 1);
|
|
72
|
+
index += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
78
75
|
const code = ch.charCodeAt(0);
|
|
79
76
|
if (code < 0x20 && ch !== "\t") {
|
|
80
77
|
index += 1;
|
|
81
78
|
continue;
|
|
82
79
|
}
|
|
83
|
-
|
|
80
|
+
cells[column] = { ch: concealed ? " " : ch, style: { ...style } };
|
|
81
|
+
column += 1;
|
|
84
82
|
index += 1;
|
|
85
83
|
}
|
|
86
84
|
finishLine();
|
|
87
85
|
return lines;
|
|
88
86
|
}
|
|
87
|
+
function cellsToSegments(cells) {
|
|
88
|
+
const segments = [];
|
|
89
|
+
for (const cell of cells) {
|
|
90
|
+
const nextCell = cell ?? { ch: " ", style: {} };
|
|
91
|
+
const last = segments[segments.length - 1];
|
|
92
|
+
if (last && stylesEqual(last.style, nextCell.style)) {
|
|
93
|
+
last.text += nextCell.ch;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
segments.push({ text: nextCell.ch, style: { ...nextCell.style } });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return segments;
|
|
100
|
+
}
|
|
89
101
|
function isCsiFinalByte(ch) {
|
|
90
102
|
const code = ch.charCodeAt(0);
|
|
91
103
|
return code >= 0x40 && code <= 0x7e;
|
|
@@ -108,14 +120,21 @@ function parseParams(params) {
|
|
|
108
120
|
if (params.length === 0) {
|
|
109
121
|
return [0];
|
|
110
122
|
}
|
|
111
|
-
return params.split(";").
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
return params.split(";").flatMap((part) => {
|
|
124
|
+
const colonParams = part.split(":");
|
|
125
|
+
if (colonParams.length > 2 && colonParams[1] === "2") {
|
|
126
|
+
colonParams.splice(2, 1);
|
|
114
127
|
}
|
|
115
|
-
|
|
116
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
128
|
+
return colonParams.map(parseParam);
|
|
117
129
|
});
|
|
118
130
|
}
|
|
131
|
+
function parseParam(part) {
|
|
132
|
+
if (part.length === 0) {
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
const parsed = Number.parseInt(part, 10);
|
|
136
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
137
|
+
}
|
|
119
138
|
const BASIC_COLORS = [
|
|
120
139
|
"black",
|
|
121
140
|
"red",
|
|
@@ -136,13 +155,15 @@ const BRIGHT_COLORS = [
|
|
|
136
155
|
"cyanBright",
|
|
137
156
|
"whiteBright"
|
|
138
157
|
];
|
|
139
|
-
function applySgr(style, params, base) {
|
|
158
|
+
function applySgr(style, concealed, params, base) {
|
|
140
159
|
let next = { ...style };
|
|
160
|
+
let nextConcealed = concealed;
|
|
141
161
|
let index = 0;
|
|
142
162
|
while (index < params.length) {
|
|
143
163
|
const code = params[index];
|
|
144
164
|
if (code === 0) {
|
|
145
165
|
next = { ...base };
|
|
166
|
+
nextConcealed = false;
|
|
146
167
|
index += 1;
|
|
147
168
|
continue;
|
|
148
169
|
}
|
|
@@ -156,12 +177,32 @@ function applySgr(style, params, base) {
|
|
|
156
177
|
index += 1;
|
|
157
178
|
continue;
|
|
158
179
|
}
|
|
180
|
+
if (code === 7) {
|
|
181
|
+
next.inverse = true;
|
|
182
|
+
index += 1;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
159
185
|
if (code === 22) {
|
|
160
186
|
delete next.bold;
|
|
161
187
|
delete next.dim;
|
|
162
188
|
index += 1;
|
|
163
189
|
continue;
|
|
164
190
|
}
|
|
191
|
+
if (code === 8) {
|
|
192
|
+
nextConcealed = true;
|
|
193
|
+
index += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (code === 28) {
|
|
197
|
+
nextConcealed = false;
|
|
198
|
+
index += 1;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (code === 27) {
|
|
202
|
+
delete next.inverse;
|
|
203
|
+
index += 1;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
165
206
|
if (code >= 30 && code <= 37) {
|
|
166
207
|
next.fg = BASIC_COLORS[code - 30];
|
|
167
208
|
index += 1;
|
|
@@ -242,7 +283,7 @@ function applySgr(style, params, base) {
|
|
|
242
283
|
}
|
|
243
284
|
index += 1;
|
|
244
285
|
}
|
|
245
|
-
return next;
|
|
286
|
+
return { style: next, concealed: nextConcealed };
|
|
246
287
|
}
|
|
247
288
|
function convert256(palette) {
|
|
248
289
|
if (palette < 0 || palette > 255) {
|
|
@@ -278,7 +319,8 @@ function stylesEqual(left, right) {
|
|
|
278
319
|
return left.fg === right.fg
|
|
279
320
|
&& left.bg === right.bg
|
|
280
321
|
&& left.bold === right.bold
|
|
281
|
-
&& left.dim === right.dim
|
|
322
|
+
&& left.dim === right.dim
|
|
323
|
+
&& left.inverse === right.inverse;
|
|
282
324
|
}
|
|
283
325
|
function normalizeStyle(style) {
|
|
284
326
|
const next = {};
|
|
@@ -294,5 +336,8 @@ function normalizeStyle(style) {
|
|
|
294
336
|
if (style?.dim !== undefined) {
|
|
295
337
|
next.dim = style.dim;
|
|
296
338
|
}
|
|
339
|
+
if (style?.inverse !== undefined) {
|
|
340
|
+
next.inverse = style.inverse;
|
|
341
|
+
}
|
|
297
342
|
return next;
|
|
298
343
|
}
|