libretto 0.6.16 → 0.6.17
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/cli/cli.js +32 -13
- package/dist/cli/commands/browser.js +2 -2
- package/dist/cli/commands/execution.js +1 -1
- package/dist/cli/commands/search.js +69 -0
- package/dist/cli/commands/update.js +122 -0
- package/dist/cli/core/context.js +4 -0
- package/dist/cli/core/daemon/daemon.js +3 -0
- package/dist/cli/core/experiments.js +14 -1
- package/dist/cli/core/providers/index.js +5 -1
- package/dist/cli/core/providers/steel.js +56 -0
- package/dist/cli/core/session-telemetry.js +143 -7
- package/dist/cli/core/skill-version.js +1 -0
- package/dist/cli/router.js +14 -3
- package/dist/shared/html-search/search-html.d.ts +9 -0
- package/dist/shared/html-search/search-html.js +46 -0
- package/dist/shared/html-search/search-html.spec.d.ts +2 -0
- package/dist/shared/html-search/search-html.spec.js +57 -0
- package/docs/releasing.md +3 -9
- package/package.json +2 -2
- package/scripts/generate-changelog.ts +207 -12
- package/skills/libretto/SKILL.md +22 -15
- package/skills/libretto/references/code-generation-rules.md +2 -2
- package/skills/libretto/references/configuration-file-reference.md +3 -2
- package/skills/libretto-readonly/SKILL.md +1 -1
- package/src/cli/cli.ts +38 -13
- package/src/cli/commands/browser.ts +2 -3
- package/src/cli/commands/execution.ts +1 -1
- package/src/cli/commands/search.ts +74 -0
- package/src/cli/commands/update.ts +149 -0
- package/src/cli/core/context.ts +4 -0
- package/src/cli/core/daemon/daemon.ts +3 -0
- package/src/cli/core/experiments.ts +15 -1
- package/src/cli/core/providers/index.ts +5 -1
- package/src/cli/core/providers/steel.ts +75 -0
- package/src/cli/core/session-telemetry.ts +176 -13
- package/src/cli/core/skill-version.ts +1 -1
- package/src/cli/core/telemetry.ts +19 -3
- package/src/cli/router.ts +13 -2
- package/src/shared/html-search/search-html.spec.ts +65 -0
- package/src/shared/html-search/search-html.ts +75 -0
package/src/cli/router.ts
CHANGED
|
@@ -7,12 +7,14 @@ import { experimentsCommand } from "./commands/experiments.js";
|
|
|
7
7
|
import { setupCommand } from "./commands/setup.js";
|
|
8
8
|
import { statusCommand } from "./commands/status.js";
|
|
9
9
|
import { snapshotCommand } from "./commands/snapshot.js";
|
|
10
|
+
import { searchCommand } from "./commands/search.js";
|
|
11
|
+
import { updateCommand } from "./commands/update.js";
|
|
10
12
|
import { SimpleCLI } from "affordance";
|
|
11
13
|
|
|
12
14
|
export const cliRoutes = {
|
|
13
15
|
...browserCommands,
|
|
14
16
|
cloud: SimpleCLI.group({
|
|
15
|
-
description: "
|
|
17
|
+
description: "Deploy workflows and manage hosted Libretto",
|
|
16
18
|
routes: {
|
|
17
19
|
deploy: deployCommand,
|
|
18
20
|
auth: authCommands,
|
|
@@ -21,11 +23,20 @@ export const cliRoutes = {
|
|
|
21
23
|
}),
|
|
22
24
|
experiments: experimentsCommand,
|
|
23
25
|
...executionCommands,
|
|
26
|
+
search: searchCommand,
|
|
24
27
|
setup: setupCommand,
|
|
25
28
|
status: statusCommand,
|
|
26
29
|
snapshot: snapshotCommand,
|
|
30
|
+
update: updateCommand,
|
|
27
31
|
};
|
|
28
32
|
|
|
29
33
|
export function createCLIApp() {
|
|
30
|
-
return SimpleCLI.define("libretto", cliRoutes
|
|
34
|
+
return SimpleCLI.define("libretto", cliRoutes, {
|
|
35
|
+
appendHelpText: [
|
|
36
|
+
"Options:",
|
|
37
|
+
" --session <name> Required for session-scoped commands",
|
|
38
|
+
" -h, --help",
|
|
39
|
+
" -v, --version",
|
|
40
|
+
].join("\n"),
|
|
41
|
+
});
|
|
31
42
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { formatHtmlForSearch, searchFormattedHtml } from "./search-html.js";
|
|
3
|
+
|
|
4
|
+
describe("HTML search", () => {
|
|
5
|
+
test("formats condensed HTML before searching", () => {
|
|
6
|
+
const formatted = formatHtmlForSearch(
|
|
7
|
+
'<!doctype html><html><body><main><p data-testid="target">Needle</p></main></body></html>',
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
expect(formatted).toContain('<p data-testid="target">');
|
|
11
|
+
expect(formatted).toContain("Needle");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("returns merged matching regions with context", () => {
|
|
15
|
+
const formatted = [
|
|
16
|
+
"<html>",
|
|
17
|
+
"<body>",
|
|
18
|
+
"<main>",
|
|
19
|
+
"<section>",
|
|
20
|
+
"<h1>Heading</h1>",
|
|
21
|
+
'<p class="target">Needle</p>',
|
|
22
|
+
"<p>More content</p>",
|
|
23
|
+
"</section>",
|
|
24
|
+
"</main>",
|
|
25
|
+
"</body>",
|
|
26
|
+
"</html>",
|
|
27
|
+
].join("\n");
|
|
28
|
+
|
|
29
|
+
const matches = searchFormattedHtml(formatted, "Needle", 2);
|
|
30
|
+
|
|
31
|
+
expect(matches).toEqual([
|
|
32
|
+
{
|
|
33
|
+
startLine: 4,
|
|
34
|
+
endLine: 8,
|
|
35
|
+
lines: [
|
|
36
|
+
"<section>",
|
|
37
|
+
"<h1>Heading</h1>",
|
|
38
|
+
'<p class="target">Needle</p>',
|
|
39
|
+
"<p>More content</p>",
|
|
40
|
+
"</section>",
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("limits matching regions before adding context", () => {
|
|
47
|
+
const formatted = Array.from(
|
|
48
|
+
{ length: 12 },
|
|
49
|
+
(_value, index) => `<p>Needle ${index}</p>`,
|
|
50
|
+
).join("\n");
|
|
51
|
+
|
|
52
|
+
const matches = searchFormattedHtml(formatted, "Needle", 0, 8);
|
|
53
|
+
|
|
54
|
+
expect(matches).toEqual([
|
|
55
|
+
{
|
|
56
|
+
startLine: 1,
|
|
57
|
+
endLine: 8,
|
|
58
|
+
lines: Array.from(
|
|
59
|
+
{ length: 8 },
|
|
60
|
+
(_value, index) => `<p>Needle ${index}</p>`,
|
|
61
|
+
),
|
|
62
|
+
},
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { condenseDom } from "../condense-dom/condense-dom.js";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_CONTEXT_LINES = 4;
|
|
4
|
+
const DEFAULT_MATCH_LIMIT = 8;
|
|
5
|
+
|
|
6
|
+
export type SearchHtmlMatch = {
|
|
7
|
+
startLine: number;
|
|
8
|
+
endLine: number;
|
|
9
|
+
lines: string[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function formatHtmlForSearch(html: string): string {
|
|
13
|
+
const condensed = condenseDom(html).html;
|
|
14
|
+
const separated = condensed
|
|
15
|
+
.replace(/>\s+</g, ">\n<")
|
|
16
|
+
.replace(/(<[^/!][^>]*>)([^<\n][\s\S]*?)(<\/[^>]+>)/g, "$1\n$2\n$3");
|
|
17
|
+
|
|
18
|
+
const lines = separated
|
|
19
|
+
.split("\n")
|
|
20
|
+
.map((line) => line.trim())
|
|
21
|
+
.filter((line) => line.length > 0);
|
|
22
|
+
|
|
23
|
+
let indent = 0;
|
|
24
|
+
return lines
|
|
25
|
+
.map((line) => {
|
|
26
|
+
if (/^<\//.test(line)) indent = Math.max(0, indent - 1);
|
|
27
|
+
const formatted = `${" ".repeat(indent)}${line}`;
|
|
28
|
+
if (isOpeningTag(line)) indent += 1;
|
|
29
|
+
return formatted;
|
|
30
|
+
})
|
|
31
|
+
.join("\n");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function searchFormattedHtml(
|
|
35
|
+
formattedHtml: string,
|
|
36
|
+
pattern: string,
|
|
37
|
+
contextLines = DEFAULT_CONTEXT_LINES,
|
|
38
|
+
matchLimit = DEFAULT_MATCH_LIMIT,
|
|
39
|
+
): SearchHtmlMatch[] {
|
|
40
|
+
const regex = new RegExp(pattern);
|
|
41
|
+
const lines = formattedHtml.split("\n");
|
|
42
|
+
const matchingIndexes = lines
|
|
43
|
+
.map((line, index) => (regex.test(line) ? index : -1))
|
|
44
|
+
.filter((index) => index >= 0)
|
|
45
|
+
.slice(0, matchLimit);
|
|
46
|
+
|
|
47
|
+
const matches: SearchHtmlMatch[] = [];
|
|
48
|
+
for (const matchingIndex of matchingIndexes) {
|
|
49
|
+
const startLine = Math.max(0, matchingIndex - contextLines);
|
|
50
|
+
const endLine = Math.min(lines.length - 1, matchingIndex + contextLines);
|
|
51
|
+
const previous = matches.at(-1);
|
|
52
|
+
if (previous && startLine <= previous.endLine) {
|
|
53
|
+
previous.endLine = Math.max(previous.endLine, endLine + 1);
|
|
54
|
+
previous.lines = lines.slice(previous.startLine - 1, previous.endLine);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
matches.push({
|
|
58
|
+
startLine: startLine + 1,
|
|
59
|
+
endLine: endLine + 1,
|
|
60
|
+
lines: lines.slice(startLine, endLine + 1),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return matches;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isOpeningTag(line: string): boolean {
|
|
67
|
+
return (
|
|
68
|
+
/^<[^/!?][^>]*>$/.test(line) &&
|
|
69
|
+
!/\/>$/.test(line) &&
|
|
70
|
+
!/^<(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)\b/i.test(
|
|
71
|
+
line,
|
|
72
|
+
) &&
|
|
73
|
+
!/^<[^>]+>.*<\/[^>]+>$/.test(line)
|
|
74
|
+
);
|
|
75
|
+
}
|