@drunkcoding/agents-and-skills 0.0.44 → 0.0.46
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/.claude-plugin/marketplace.json +2 -47
- package/README.md +3 -6
- package/package.json +4 -1
- package/plugins/multica-tool/.claude-plugin/plugin.json +1 -1
- package/plugins/multica-tool/scripts/lib.mjs +53 -4
- package/plugins/multica-tool/scripts/multica-export.mjs +170 -50
- package/plugins/multica-tool/scripts/multica-import.mjs +209 -27
- package/plugins/multica-tool/skills/export/SKILL.md +91 -22
- package/plugins/multica-tool/skills/import/SKILL.md +24 -9
- package/plugins/multica-tool/skills/sync/SKILL.md +1 -1
- package/plugins/plugin-validator/.claude-plugin/plugin.json +1 -1
- package/plugins/html-effectiveness/.claude-plugin/plugin.json +0 -17
- package/plugins/html-effectiveness/README.md +0 -28
- package/plugins/html-effectiveness/agents/report-builder.md +0 -66
- package/plugins/html-effectiveness/assets/base.css +0 -51
- package/plugins/html-effectiveness/assets/base.js +0 -11
- package/plugins/html-effectiveness/assets/charts.js +0 -1
- package/plugins/html-effectiveness/assets/components.css +0 -10
- package/plugins/html-effectiveness/commands/html-report.md +0 -13
- package/plugins/html-effectiveness/docs/template-gallery.md +0 -28
- package/plugins/html-effectiveness/package.json +0 -6
- package/plugins/html-effectiveness/scripts/_tmp.js +0 -37
- package/plugins/html-effectiveness/scripts/catalog.js +0 -47
- package/plugins/html-effectiveness/scripts/extract.js +0 -104
- package/plugins/html-effectiveness/scripts/mustache.js +0 -90
- package/plugins/html-effectiveness/scripts/render.js +0 -176
- package/plugins/html-effectiveness/skills/html-effectiveness/SKILL.md +0 -94
- package/plugins/html-effectiveness/templates/01-exploration-code-approaches.html.tmpl +0 -400
- package/plugins/html-effectiveness/templates/02-exploration-visual-designs.html.tmpl +0 -456
- package/plugins/html-effectiveness/templates/03-code-review-pr.html.tmpl +0 -581
- package/plugins/html-effectiveness/templates/04-code-understanding.html.tmpl +0 -443
- package/plugins/html-effectiveness/templates/05-design-system.html.tmpl +0 -578
- package/plugins/html-effectiveness/templates/06-component-variants.html.tmpl +0 -557
- package/plugins/html-effectiveness/templates/07-prototype-animation.html.tmpl +0 -417
- package/plugins/html-effectiveness/templates/08-prototype-interaction.html.tmpl +0 -363
- package/plugins/html-effectiveness/templates/09-slide-deck.html.tmpl +0 -535
- package/plugins/html-effectiveness/templates/10-svg-illustrations.html.tmpl +0 -444
- package/plugins/html-effectiveness/templates/11-status-report.html.tmpl +0 -152
- package/plugins/html-effectiveness/templates/12-incident-report.html.tmpl +0 -547
- package/plugins/html-effectiveness/templates/13-flowchart-diagram.html.tmpl +0 -361
- package/plugins/html-effectiveness/templates/14-research-feature-explainer.html.tmpl +0 -348
- package/plugins/html-effectiveness/templates/15-research-concept-explainer.html.tmpl +0 -334
- package/plugins/html-effectiveness/templates/16-implementation-plan.html.tmpl +0 -657
- package/plugins/html-effectiveness/templates/17-pr-writeup.html.tmpl +0 -553
- package/plugins/html-effectiveness/templates/18-editor-triage-board.html.tmpl +0 -541
- package/plugins/html-effectiveness/templates/19-editor-feature-flags.html.tmpl +0 -627
- package/plugins/html-effectiveness/templates/20-editor-prompt-tuner.html.tmpl +0 -687
- package/plugins/html-effectiveness/templates/manifest.json +0 -441
- package/plugins/team-share/.claude-plugin/plugin.json +0 -18
- package/plugins/team-share/README.md +0 -46
- package/plugins/team-share/agents/team-share.md +0 -53
- package/plugins/team-share/commands/team-share.md +0 -4
- package/plugins/team-share/skills/claude-config/SKILL.md +0 -143
- package/plugins/team-share/skills/codegraph-setup/SKILL.md +0 -81
- package/plugins/team-share/skills/understand-setup/SKILL.md +0 -115
- package/plugins/tech-graph/.claude-plugin/plugin.json +0 -17
- package/plugins/tech-graph/README.md +0 -66
- package/plugins/tech-graph/agents/tech-graph.md +0 -97
- package/plugins/tech-graph/commands/tech-graph.md +0 -13
- package/plugins/tech-graph/scripts/check-deps.sh +0 -55
- package/plugins/tech-graph/skills/tech-graph/LICENSE +0 -21
- package/plugins/tech-graph/skills/tech-graph/README.md +0 -555
- package/plugins/tech-graph/skills/tech-graph/README.zh.md +0 -514
- package/plugins/tech-graph/skills/tech-graph/SKILL.md +0 -263
- package/plugins/tech-graph/skills/tech-graph/agents/openai.yaml +0 -4
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style1-flat.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style2-dark.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style3-blueprint.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style4-notion.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style5-glass.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style6-claude.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/assets/samples/sample-style7-openai.png +0 -0
- package/plugins/tech-graph/skills/tech-graph/fixtures/agent-memory-types-style4.json +0 -181
- package/plugins/tech-graph/skills/tech-graph/fixtures/api-flow-style7.json +0 -40
- package/plugins/tech-graph/skills/tech-graph/fixtures/mem0-style1.json +0 -297
- package/plugins/tech-graph/skills/tech-graph/fixtures/microservices-style3.json +0 -64
- package/plugins/tech-graph/skills/tech-graph/fixtures/multi-agent-style5.json +0 -45
- package/plugins/tech-graph/skills/tech-graph/fixtures/system-architecture-style6.json +0 -48
- package/plugins/tech-graph/skills/tech-graph/fixtures/tool-call-style2.json +0 -182
- package/plugins/tech-graph/skills/tech-graph/package.json +0 -42
- package/plugins/tech-graph/skills/tech-graph/references/diagram-types.md +0 -171
- package/plugins/tech-graph/skills/tech-graph/references/icons.md +0 -406
- package/plugins/tech-graph/skills/tech-graph/references/style-1-flat-icon.md +0 -108
- package/plugins/tech-graph/skills/tech-graph/references/style-2-dark-terminal.md +0 -107
- package/plugins/tech-graph/skills/tech-graph/references/style-3-blueprint.md +0 -113
- package/plugins/tech-graph/skills/tech-graph/references/style-4-notion-clean.md +0 -95
- package/plugins/tech-graph/skills/tech-graph/references/style-5-glassmorphism.md +0 -125
- package/plugins/tech-graph/skills/tech-graph/references/style-6-claude-official.md +0 -209
- package/plugins/tech-graph/skills/tech-graph/references/style-7-openai.md +0 -215
- package/plugins/tech-graph/skills/tech-graph/references/style-diagram-matrix.md +0 -135
- package/plugins/tech-graph/skills/tech-graph/references/svg-layout-best-practices.md +0 -100
- package/plugins/tech-graph/skills/tech-graph/references/svg-to-png.md +0 -115
- package/plugins/tech-graph/skills/tech-graph/scripts/README.md +0 -269
- package/plugins/tech-graph/skills/tech-graph/scripts/generate-diagram.sh +0 -181
- package/plugins/tech-graph/skills/tech-graph/scripts/generate-from-template.py +0 -1587
- package/plugins/tech-graph/skills/tech-graph/scripts/test-all-styles.sh +0 -143
- package/plugins/tech-graph/skills/tech-graph/scripts/validate-svg.sh +0 -306
- package/plugins/tech-graph/skills/tech-graph/templates/agent-architecture.svg +0 -28
- package/plugins/tech-graph/skills/tech-graph/templates/architecture.svg +0 -23
- package/plugins/tech-graph/skills/tech-graph/templates/comparison-matrix.svg +0 -14
- package/plugins/tech-graph/skills/tech-graph/templates/data-flow.svg +0 -28
- package/plugins/tech-graph/skills/tech-graph/templates/er-diagram.svg +0 -21
- package/plugins/tech-graph/skills/tech-graph/templates/flowchart.svg +0 -21
- package/plugins/tech-graph/skills/tech-graph/templates/sequence.svg +0 -20
- package/plugins/tech-graph/skills/tech-graph/templates/state-machine.svg +0 -20
- package/plugins/tech-graph/skills/tech-graph/templates/timeline.svg +0 -19
- package/plugins/tech-graph/skills/tech-graph/templates/use-case.svg +0 -21
|
@@ -13,19 +13,19 @@ Import a local Multica bundle (produced by the export skill) into a target works
|
|
|
13
13
|
You need the exact workspace name as registered in Multica for `--workspace` in the steps below.
|
|
14
14
|
|
|
15
15
|
- If the user named a target workspace, use it.
|
|
16
|
-
- If the user did **not** name one, default to the **basename of the import folder** — e.g. importing from `export/mx-workspace` defaults the target workspace to `mx-workspace`. State the inferred workspace name to the user before continuing.
|
|
16
|
+
- If the user did **not** name one, default to the **basename of the import folder** — e.g. importing from `export/mx-workspace` defaults the target workspace to `mx-workspace`. State the inferred workspace name to the user before continuing. This default is reliable for whole-workspace bundles (`export/<workspace-name>`); a single-resource bundle nested under `export/<workspace-name>/<slug>-<type>` has the resource slug as its basename, not the workspace — name the target workspace explicitly in that case.
|
|
17
17
|
|
|
18
18
|
No existence check is needed here: the Step 2 dry-run fails with `Unknown workspace "<name>"` if the inferred workspace is not present in the target account, at which point ask the user for the correct name.
|
|
19
19
|
|
|
20
20
|
## Step 2 — Pre-flight (dry run)
|
|
21
21
|
|
|
22
|
-
Before writing anything, run the import script with `--dry-run` to preview the bundle. Preview against the **full** set (`--include agents,squads,projects,autopilots`) regardless of what the user ultimately chooses to import — incompatibilities for a type (e.g. project caveats) are only computed when that type is included, so previewing everything up front is what lets the user see a project's or autopilot's cost before deciding whether to opt it in:
|
|
22
|
+
Before writing anything, run the import script with `--dry-run` to preview the bundle. Preview against the **full** set (`--include agents,squads,projects,autopilots,labels,properties`) regardless of what the user ultimately chooses to import — incompatibilities for a type (e.g. project caveats) are only computed when that type is included, so previewing everything up front is what lets the user see a project's or autopilot's cost before deciding whether to opt it in:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
26
26
|
--dir <folder> \
|
|
27
27
|
--workspace <workspace-name> \
|
|
28
|
-
--include agents,squads,projects,autopilots \
|
|
28
|
+
--include agents,squads,projects,autopilots,labels,properties \
|
|
29
29
|
[--runtime-map <srcId1=dstId1,srcId2=dstId2,...>] \
|
|
30
30
|
--dry-run
|
|
31
31
|
```
|
|
@@ -34,9 +34,11 @@ Present the `bundle` and `willImport` counts, and every entry in `incompatibilit
|
|
|
34
34
|
|
|
35
35
|
## Step 3 — Select which types to import
|
|
36
36
|
|
|
37
|
-
Ask the user which of `agents`, `squads`, `projects`, `autopilots` they want to import. **Default is `agents,squads`** — `projects` and `
|
|
37
|
+
Ask the user which of `agents`, `squads`, `projects`, `autopilots`, `labels`, `properties` they want to import. **Default is `agents,squads`** — `projects`, `autopilots`, `labels`, and `properties` each require explicit opt-in.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`labels` and `properties` write **workspace-wide** definitions (Multica has no project-scoped label or property), so importing them changes the target workspace's issue taxonomy for every project in it, not just the one being migrated. Say so before the user opts in.
|
|
40
|
+
|
|
41
|
+
If the pre-flight's `incompatibilities` list contains an `unmapped-runtime` or `autopilot-squad-assignee-unsupported` entry, tell the user it **aborts the import before any write** (see Step 4 below) — `unmapped-runtime` needs `--runtime-map`; a squad-assigned autopilot has no fix, since the multica CLI has no command to assign a squad to an autopilot (only `--agent`) — drop that autopilot from the bundle or recreate its assignment by hand after importing the squad. An `autopilot-assignee-missing` entry also aborts unless resolved by including `agents` or ensuring that agent already exists in the destination. Other incompatibility kinds are informational only and applied best-effort, fixed up afterward in the Multica UI: `priority-not-settable` (project priority isn't settable via the CLI, so it never round-trips), `resource-not-portable` (only `github_repo` resources are portable — other resource kinds are dropped), `lead-agent-missing` (a non-agent lead isn't re-applied to the imported project), `autopilot-priority-not-captured` (the multica CLI/API never returns an autopilot's priority, so it can't be captured or restored at all — a known platform gap, not a bug in this tool), `autopilot-project-missing` (the autopilot's target project isn't found by title in the destination — it's created/updated with no project set), `autopilot-webhook-reissued` (informational — the webhook trigger always gets a freshly issued URL), `label-description-not-settable` (the CLI has no `--description` flag on `label create`/`update`, so a label's description never round-trips — re-enter it in the UI), `property-type-conflict` (a property of that name already exists at the destination with a **different** type; type is immutable, so that property is **skipped** entirely rather than re-created, which would orphan every value already stored under that name), `property-archived` (informational — an archived source definition is imported and then archived at the destination too), `autopilot-arrives-paused` (informational — the autopilot was active at the source; every import lands paused), `mcp-server-not-creatable` (a workspace MCP server named in the bundle is absent at the destination and **cannot** be created from the bundle — `workspace mcp list` never returns a server's config, so re-add it with `multica workspace mcp add <name> --server-config ...`), and `agent-mcp-server-missing` (an agent's MCP server assignment is dropped because that server isn't in the destination library).
|
|
40
42
|
|
|
41
43
|
## Step 4 — Run the import (auto-mapping first)
|
|
42
44
|
|
|
@@ -46,7 +48,7 @@ Each exported agent record carries its source runtime's `provider` (e.g. `claude
|
|
|
46
48
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
47
49
|
--dir <folder> \
|
|
48
50
|
--workspace <workspace-name> \
|
|
49
|
-
--include <agents,squads,projects,autopilots>
|
|
51
|
+
--include <agents,squads,projects,autopilots,labels,properties>
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
If it aborts with `Unmapped runtimes: ...` (0 or 2+ runtimes share that provider in the target workspace, or the bundle predates provider capture), resolve manually:
|
|
@@ -62,7 +64,7 @@ Ask the user to pick a matching target runtime by name or ID for each unmapped `
|
|
|
62
64
|
node "${CLAUDE_PLUGIN_ROOT}/scripts/multica-import.mjs" \
|
|
63
65
|
--dir <folder> \
|
|
64
66
|
--workspace <workspace-name> \
|
|
65
|
-
--include <agents,squads,projects,autopilots> \
|
|
67
|
+
--include <agents,squads,projects,autopilots,labels,properties> \
|
|
66
68
|
--runtime-map <srcId1=dstId1,srcId2=dstId2,...>
|
|
67
69
|
```
|
|
68
70
|
|
|
@@ -70,17 +72,23 @@ If it instead aborts with `Unresolved autopilot assignees: ...`, that is not fix
|
|
|
70
72
|
|
|
71
73
|
The import also rewrites any `mention://agent/<id>` link inside squad and agent instructions (e.g. `[@dev-backend](mention://agent/<id>)`) from the source agent's id to its new id in the target workspace — the CLI does this automatically for every agent captured in the bundle; no extra flag needed. Mentions pointing to an agent outside the bundle are left untouched.
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
Workspace-wide sets are read from their own folder: `labels/labels.json`, `properties/properties.json`, and `mcp/servers.json`, located via the `labels_file` / `properties_file` / `mcp_servers_file` pointers in `manifest.json`. A pre-folder bundle that carried the arrays inline in the manifest still imports unchanged.
|
|
76
|
+
|
|
77
|
+
Agent **MCP server assignments** (`mcp_servers` on each agent record) are re-linked by **name** against the destination's workspace MCP library, then each is enabled or disabled to match the source. The library itself is **never created** — only a server's name and transport ever travel, so a missing server means its assignments are skipped and reported. Add the missing servers first (`multica workspace mcp add`), then re-run the import to attach them.
|
|
78
|
+
|
|
79
|
+
Prose fields are read back from each resource's sibling `.md` when a `*_file` key points at it — **instructions** from `<slug>.md` (agents, squads) and **description** from `<slug>.description.md` (agents, squads, projects, autopilots). Editing that Markdown is the supported way to review and enhance the prose before import. Older bundles that predate the split (prose inline in the JSON, no `*_file` key) still import unchanged.
|
|
74
80
|
|
|
75
81
|
Avatars are restored automatically, but **only when the target resource has none** — an existing agent or squad that already carries an avatar is never overwritten. New agents get their bundled image re-uploaded; new squads get their `avatar_url` (emoji or URL) set. An agent whose source avatar was an emoji can't be restored (the CLI has no emoji setter for agents) and is reported as unsupported.
|
|
76
82
|
|
|
83
|
+
Importing `labels` and `properties` matches by **name** and is idempotent: an existing label is updated to the bundle's colour, an existing property is updated in place (`property update` re-matches its options by name so option ids — and any issue values already set — survive). An archived definition is unarchived before the update and re-archived afterwards, so its archived state round-trips without ever writing to an archived record. A property whose name collides with a **different type** at the destination is skipped and reported, never re-created.
|
|
84
|
+
|
|
77
85
|
Importing `autopilots` always creates a brand-new autopilot **paused**, regardless of the source's status — activation is left as a deliberate follow-up action, never automatic. Re-importing an already-imported autopilot updates it by title (never duplicates) but leaves its current status alone. Its triggers are upserted by kind+label — a re-import never adds a duplicate trigger — and a webhook trigger always gets a freshly issued URL, never the source's.
|
|
78
86
|
|
|
79
87
|
## Step 5 — Report results
|
|
80
88
|
|
|
81
89
|
Parse the JSON output and report:
|
|
82
90
|
|
|
83
|
-
- Created and updated counts for skills, agents, squads, projects, and
|
|
91
|
+
- Created and updated counts for skills, agents, squads, projects, autopilots, labels, and properties (`created.labels`, `created.properties`, and the matching `updated.*`).
|
|
84
92
|
- Name-to-ID maps for skills and agents (`skillIdMap`, `agentIdMap`).
|
|
85
93
|
- `squadIdMap`: name-to-ID map for every squad imported.
|
|
86
94
|
- `autopilotIdMap`: name-to-ID map for every autopilot imported.
|
|
@@ -96,3 +104,10 @@ Parse the JSON output and report:
|
|
|
96
104
|
- If `autopilotSubscribersUnresolved` is non-empty, surface every `<title>:<member-name>` entry verbatim with: "NOTE: the following autopilot subscribers could not be matched by name in the target workspace — skipped: `<entry>`."
|
|
97
105
|
- If `autopilotPriorityNotCaptured` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots' priority could not be captured from the source (the multica CLI/API never returns it) — a known platform gap, not something this tool can fix: `<autopilot-title>`."
|
|
98
106
|
- If `autopilotWebhookReissued` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots had a webhook trigger created with a freshly issued URL — the previous URL does not carry over: `<autopilot-title>`."
|
|
107
|
+
- If `autopilotsActiveAtSource` is non-empty, surface every autopilot title verbatim with: "NOTE: the following autopilots were ACTIVE at the source but landed PAUSED here — activate each deliberately with `multica autopilot update <id> --status active`: `<autopilot-title>`."
|
|
108
|
+
- If `mcpServersNotCreatable` is non-empty, surface every server name verbatim with: "WARNING: the following workspace MCP servers are in the bundle but missing at the destination, and CANNOT be created from it (the CLI never returns a server's config on read) — add each with `multica workspace mcp add <name> --server-config ...`, then re-run the import to attach the agent assignments: `<server-name>`."
|
|
109
|
+
- If `mcpServersUnresolved` is non-empty, surface every `<agent-name>:<server-name>` entry verbatim with: "NOTE: the following agent MCP server assignments were skipped — no server of that name exists in the destination workspace library: `<entry>`."
|
|
110
|
+
- If `mcpServersApplyFailures` is non-empty, surface every `<agent-name>:<server-name>` entry verbatim with: "WARNING: the following agent MCP server assignments were added but the CLI rejected the enable/disable that follows — check their state in the Multica UI: `<entry>`."
|
|
111
|
+
- If `labelDescriptionUnsupported` is non-empty, surface every label name verbatim with: "NOTE: the following labels' descriptions could not be applied (the multica CLI has no `--description` flag on `label create`/`update`) — re-enter them in the Multica UI: `<label-name>`."
|
|
112
|
+
- If `propertyTypeConflicts` is non-empty, surface every entry verbatim with: "WARNING: the following custom properties were SKIPPED — a property of that name already exists at the destination with a different type, and a property's type is immutable: `<entry>`. Rename one side, or map the values by hand."
|
|
113
|
+
- If `propertiesArchived` is non-empty, surface every property name verbatim with: "NOTE: the following custom properties were imported and then archived, matching their state at the source: `<property-name>`."
|
|
@@ -18,7 +18,7 @@ sync <type> <name> from <src-ws> to <dest-ws>
|
|
|
18
18
|
|
|
19
19
|
Where `<type>` is `skill`, `agent`, `squad`, `project`, or `autopilot`; `<name>` is the resource name (for `project` and `autopilot`, its **title**); `<src-ws>` and `<dest-ws>` are workspace names registered in Multica.
|
|
20
20
|
|
|
21
|
-
Projects and autopilots are resolved by title, not ID, e.g. `multica-sync.mjs project "<title>" from <src-ws> <dest-ws>` — the project's lead agent is synced alongside it. `multica-sync.mjs autopilot "<title>" from <src-ws> <dest-ws>` syncs the autopilot
|
|
21
|
+
Projects and autopilots are resolved by title, not ID, e.g. `multica-sync.mjs project "<title>" from <src-ws> <dest-ws>` — the project's lead agent is synced alongside it. `multica-sync.mjs autopilot "<title>" from <src-ws> <dest-ws>` syncs only the autopilot configuration (title, description, triggers, etc.) — its assignee is linked by name and must already exist in the destination workspace; the assignee is not bundled or synced alongside the autopilot. A squad-assigned autopilot aborts (see the import skill — the multica CLI has no command to assign a squad to an autopilot).
|
|
22
22
|
|
|
23
23
|
## Step 2 — Run the sync (auto-mapping first)
|
|
24
24
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plugin-validator",
|
|
3
3
|
"displayName": "Plugin Validator",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.46",
|
|
5
5
|
"description": "Orchestrated validator for Claude Code plugins — validates skills, agents, commands, and hooks across every plugin under plugins/**.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Steven Hoang"
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "html-effectiveness",
|
|
3
|
-
"displayName": "HTML Effectiveness Reports",
|
|
4
|
-
"version": "0.0.44",
|
|
5
|
-
"description": "Generate self-contained interactive HTML reports from 20 upstream templates via a conversational agent.",
|
|
6
|
-
"author": {
|
|
7
|
-
"name": "Steven Hoang"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"html",
|
|
11
|
-
"report",
|
|
12
|
-
"status",
|
|
13
|
-
"incident",
|
|
14
|
-
"slide-deck",
|
|
15
|
-
"decision-matrix"
|
|
16
|
-
]
|
|
17
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# html-effectiveness
|
|
2
|
-
|
|
3
|
-
Generate self-contained interactive HTML reports (status, incident, PR writeup, decision matrix, slide deck, code review, research explainer, etc.) from 20 upstream templates.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
Invoke the `report-builder` agent. It will pick a template, gather slot data conversationally, and render to `docs/reports/YYYY-MM-DD-<slug>.html`.
|
|
8
|
-
|
|
9
|
-
Override output dir in repo `CLAUDE.md`:
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
html-effectiveness.reportDir: my/custom/reports
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Vendored templates
|
|
16
|
-
|
|
17
|
-
Templates are vendored from `ThariqS/html-effectiveness`. The pinned SHA lives in `templates/manifest.json` (`_meta.upstream_sha`). Re-fetch is **manual** — only run when you want to refresh the snapshot:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
node plugins/html-effectiveness/scripts/extract.js fetch <id>.html
|
|
21
|
-
node plugins/html-effectiveness/scripts/extract.js split <id>.html
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Files land under `.tmp/html-effectiveness/extract/` and are cleaned up automatically. Hand-merge any changes back into `templates/<id>.html.tmpl`.
|
|
25
|
-
|
|
26
|
-
## Scratch convention
|
|
27
|
-
|
|
28
|
-
All plugin scratch lives under `.tmp/html-effectiveness/<purpose>/` (gitignored). Helper API: `scripts/_tmp.js` exports `getTmpDir`, `cleanup`, and `withTmp` for try/finally + signal-handler cleanup.
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: report-builder
|
|
3
|
-
description: Conversational wizard that helps the user generate an HTML report from one of 20 templates (status, incident, PR writeup, decision matrix, slide deck, code review, research explainer, ...). Use when the user wants a polished interactive HTML output instead of markdown. Reads templates/manifest.json and CLAUDE.md html-effectiveness.reportDir for output path.
|
|
4
|
-
tools: Read, Write, Bash, Glob, Grep
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
You are `report-builder`, a conversational wizard that turns the user's intent into a polished, self-contained interactive HTML report.
|
|
8
|
-
|
|
9
|
-
## Role
|
|
10
|
-
|
|
11
|
-
Guide the user from intent → rendered HTML in a single file. Speak briefly. Ask one question at a time. Match the user's caveman setting if active.
|
|
12
|
-
|
|
13
|
-
## Startup
|
|
14
|
-
|
|
15
|
-
On every run:
|
|
16
|
-
|
|
17
|
-
1. Read `CLAUDE.md` from the repo root. Extract `html-effectiveness.reportDir` if present; fallback `docs/reports/`.
|
|
18
|
-
2. Read `plugins/html-effectiveness/templates/manifest.json` (catalog + slot schemas).
|
|
19
|
-
3. Greet briefly; confirm the user's intent.
|
|
20
|
-
|
|
21
|
-
## Conversation loop
|
|
22
|
-
|
|
23
|
-
1. **Classify** — infer the top 2-3 templates from the user's stated need by matching `use_cases` and `pattern` fields in the manifest.
|
|
24
|
-
2. **Confirm** — present the top picks with one-line summaries; let the user pick or override.
|
|
25
|
-
3. **Slot fill** — walk the required slots from `manifest[id].slots` in order. Batch obvious slots and infer from prior context to minimize questions. Cap at 6 user-facing questions; force render with best-guess defaults beyond that, with a one-line note of what was assumed.
|
|
26
|
-
4. **Preview data** — show a JSON summary of slot values; ask "looks right?" before rendering.
|
|
27
|
-
|
|
28
|
-
## Render
|
|
29
|
-
|
|
30
|
-
1. Compute slug from title (kebab-case, ASCII-only, max 60 chars).
|
|
31
|
-
2. Build path `<reportDir>/YYYY-MM-DD-<slug>.html`.
|
|
32
|
-
3. Write `<path>.data.json` first (so the user can re-render later).
|
|
33
|
-
4. Invoke render script via Bash:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
node plugins/html-effectiveness/scripts/render.js \
|
|
37
|
-
--template=<id> \
|
|
38
|
-
--data=<path>.data.json \
|
|
39
|
-
--out=<path>
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
5. On success: print the absolute path; offer `snip render --format html < <path>` to preview in the browser.
|
|
43
|
-
6. On render error: surface stderr; locate the offending slot from the `template:slot` prefix; ask the user to fix; retry.
|
|
44
|
-
|
|
45
|
-
## Output
|
|
46
|
-
|
|
47
|
-
A self-contained interactive HTML report at `<reportDir>/YYYY-MM-DD-<slug>.html` plus a `<slug>.html.data.json` sidecar for later re-rendering. The absolute path is printed to the user on success, with an offer to preview via `snip render --format html`.
|
|
48
|
-
|
|
49
|
-
## Regen path
|
|
50
|
-
|
|
51
|
-
If the user invokes the agent on an existing `<name>.data.json`:
|
|
52
|
-
|
|
53
|
-
- Skip slot fill.
|
|
54
|
-
- Edit only the slots the user wants changed.
|
|
55
|
-
- Re-render to the same path (or new path if requested).
|
|
56
|
-
|
|
57
|
-
## Guardrails
|
|
58
|
-
|
|
59
|
-
- Never write to paths outside the repo root.
|
|
60
|
-
- Create `reportDir` if missing.
|
|
61
|
-
- If the output file already exists, ask the user before overwriting; suggest `-2`, `-3`, ... suffix.
|
|
62
|
-
- Refuse to render if the manifest is missing or the requested template id doesn't exist; surface the valid id list.
|
|
63
|
-
|
|
64
|
-
## Tone
|
|
65
|
-
|
|
66
|
-
Terse, expert, no fluff. Caveman-mode-aware. Code blocks unchanged.
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/* Shared design tokens + page baseline. Vendored from ThariqS/html-effectiveness. */
|
|
2
|
-
:root {
|
|
3
|
-
--ivory: #FAF9F5;
|
|
4
|
-
--slate: #141413;
|
|
5
|
-
--clay: #D97757;
|
|
6
|
-
--oat: #E3DACC;
|
|
7
|
-
--olive: #788C5D;
|
|
8
|
-
--rust: #B04A3F;
|
|
9
|
-
--gray-100: #F0EEE6;
|
|
10
|
-
--gray-300: #D1CFC5;
|
|
11
|
-
--gray-500: #87867F;
|
|
12
|
-
--gray-700: #3D3D3A;
|
|
13
|
-
--white: #FFFFFF;
|
|
14
|
-
|
|
15
|
-
--serif: ui-serif, Georgia, serif;
|
|
16
|
-
--sans: system-ui, -apple-system, sans-serif;
|
|
17
|
-
--mono: ui-monospace, "SF Mono", Menlo, monospace;
|
|
18
|
-
|
|
19
|
-
--radius-panel: 12px;
|
|
20
|
-
--radius-row: 8px;
|
|
21
|
-
--border-thick: 1.5px solid var(--gray-300);
|
|
22
|
-
|
|
23
|
-
/* legacy aliases for components.css stubs */
|
|
24
|
-
--bg: var(--ivory);
|
|
25
|
-
--fg: var(--slate);
|
|
26
|
-
--muted: var(--gray-500);
|
|
27
|
-
--accent: var(--clay);
|
|
28
|
-
--border: 1px solid var(--gray-300);
|
|
29
|
-
--radius: var(--radius-panel);
|
|
30
|
-
--font: var(--sans);
|
|
31
|
-
--transition: 150ms ease;
|
|
32
|
-
}
|
|
33
|
-
* { box-sizing: border-box; }
|
|
34
|
-
html, body {
|
|
35
|
-
margin: 0;
|
|
36
|
-
padding: 0;
|
|
37
|
-
background: var(--ivory);
|
|
38
|
-
color: var(--slate);
|
|
39
|
-
font-family: var(--sans);
|
|
40
|
-
font-size: 15px;
|
|
41
|
-
line-height: 1.6;
|
|
42
|
-
-webkit-font-smoothing: antialiased;
|
|
43
|
-
}
|
|
44
|
-
a { color: var(--clay); text-decoration: none; }
|
|
45
|
-
a:hover { text-decoration: underline; }
|
|
46
|
-
@media print {
|
|
47
|
-
details { display: block; }
|
|
48
|
-
details > summary { display: none; }
|
|
49
|
-
[hidden] { display: revert !important; }
|
|
50
|
-
body { background: white; color: black; }
|
|
51
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Shared interactions: theme toggle, search, nav, copy-on-click. Populated from upstream during Task 7.
|
|
2
|
-
(function () {
|
|
3
|
-
document.addEventListener('click', (e) => {
|
|
4
|
-
const t = e.target.closest('[data-copy]');
|
|
5
|
-
if (!t) return;
|
|
6
|
-
const text = t.getAttribute('data-copy');
|
|
7
|
-
navigator.clipboard?.writeText(text);
|
|
8
|
-
t.setAttribute('data-copied', '1');
|
|
9
|
-
setTimeout(() => t.removeAttribute('data-copied'), 1000);
|
|
10
|
-
});
|
|
11
|
-
})();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
// Inline SVG chart helpers. Opt-in via manifest.extra_js. Populated from upstream during Task 7.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/* Shared components: cards, timeline, kanban, matrix, swatches, slides. Populated from upstream during Task 7. */
|
|
2
|
-
.report { max-width: 1200px; margin: 0 auto; padding: 2rem 1rem; }
|
|
3
|
-
.report header h1 { font-size: 1.75rem; margin: 0 0 .25rem; }
|
|
4
|
-
.report header .subtitle { color: var(--muted); margin: 0 0 1.5rem; }
|
|
5
|
-
.card { border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; transition: border-color var(--transition); }
|
|
6
|
-
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem; margin: 0 0 2rem; }
|
|
7
|
-
.stat { display: flex; flex-direction: column; gap: .25rem; }
|
|
8
|
-
.stat .value { font-size: 1.5rem; font-weight: 600; }
|
|
9
|
-
.stat .label { color: var(--muted); font-size: .875rem; }
|
|
10
|
-
.stat .delta { font-size: .75rem; }
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Conversational wizard that renders dense info as a self-contained interactive HTML report (status, incident, PR writeup, slide deck, decision matrix, ...).
|
|
3
|
-
argument-hint: [optional one-line intent or path to existing .data.json]
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Dispatch the `report-builder` subagent using the Agent tool. Pass `$ARGUMENTS` (which may be empty) as the initial intent seed in the subagent prompt.
|
|
7
|
-
|
|
8
|
-
Subagent dispatch prompt template:
|
|
9
|
-
|
|
10
|
-
> Run the html-effectiveness report wizard. Initial intent seed: `$ARGUMENTS`.
|
|
11
|
-
> If the seed looks like a path to an existing `*.data.json` sidecar, enter the regen flow: load it, ask which slots to change, re-render. Otherwise classify the intent against `plugins/html-effectiveness/templates/manifest.json`, confirm the template pick with the user, fill slots (cap 6 questions), preview the data JSON, then render via `plugins/html-effectiveness/scripts/render.js`. Respect `html-effectiveness.reportDir` from repo `CLAUDE.md` (fallback `docs/reports/`).
|
|
12
|
-
|
|
13
|
-
Do not render or write files in the main thread — the subagent owns the full wizard and the render call.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# Template Gallery
|
|
2
|
-
|
|
3
|
-
Generated from `templates/manifest.json`. Upstream: `ThariqS/html-effectiveness@a1fb5673c26f87be9b833e1876cf4fd0f30f5f2d` (fetched 2026-05-13).
|
|
4
|
-
|
|
5
|
-
| ID | Title | Pattern | Use cases | Required slots |
|
|
6
|
-
|----|-------|---------|-----------|----------------|
|
|
7
|
-
| `01-exploration-code-approaches` | Code-Approaches Exploration | `comparison_board` | "compare implementations", "approach trade-offs", "design alternatives" | `title` |
|
|
8
|
-
| `02-exploration-visual-designs` | Visual Design Exploration | `comparison_board` | "compare designs", "UI alternatives", "visual options" | `title` |
|
|
9
|
-
| `03-code-review-pr` | PR Code Review Board | `code_review_board` | "PR review", "code review", "diff review" | `title` |
|
|
10
|
-
| `04-code-understanding` | Code Walkthrough | `knowledge_explorer` | "explain code", "walk through architecture", "trace logic" | `title` |
|
|
11
|
-
| `05-design-system` | Design System Sheet | `design_system_sheet` | "design tokens", "color palette", "typography" | `title` |
|
|
12
|
-
| `06-component-variants` | Component Variant Matrix | `design_system_sheet` | "component variants", "states gallery" | `title` |
|
|
13
|
-
| `07-prototype-animation` | Animation Prototype | `interactive_report` | "animation prototype", "motion preview" | `title` |
|
|
14
|
-
| `08-prototype-interaction` | Interaction Prototype | `interactive_report` | "interaction prototype", "ux flow" | `title` |
|
|
15
|
-
| `09-slide-deck` | Slide Deck | `slide_deck` | "slide deck", "presentation", "pitch" | `title` |
|
|
16
|
-
| `10-svg-illustrations` | SVG Illustration Gallery | `knowledge_explorer` | "illustrate concept", "svg gallery" | `title` |
|
|
17
|
-
| `11-status-report` | Status Report | `interactive_report` | "weekly status", "project update", "team digest", "monthly recap", "engineering status" | `title`, `date_range` |
|
|
18
|
-
| `12-incident-report` | Incident Report | `interactive_report` | "incident report", "post-mortem", "outage writeup" | `title` |
|
|
19
|
-
| `13-flowchart-diagram` | Flowchart Diagram | `knowledge_explorer` | "flowchart", "process diagram", "decision tree" | `title` |
|
|
20
|
-
| `14-research-feature-explainer` | Feature Explainer | `knowledge_explorer` | "explain feature", "feature deep-dive" | `title` |
|
|
21
|
-
| `15-research-concept-explainer` | Concept Explainer | `knowledge_explorer` | "explain concept", "tutorial", "primer" | `title` |
|
|
22
|
-
| `16-implementation-plan` | Implementation Plan | `annotated_timeline` | "implementation plan", "phased rollout", "roadmap" | `title` |
|
|
23
|
-
| `17-pr-writeup` | PR Writeup | `interactive_report` | "PR description", "release notes", "change writeup" | `title` |
|
|
24
|
-
| `18-editor-triage-board` | Triage Board | `kanban_board` | "bug triage", "issue board", "kanban" | `title` |
|
|
25
|
-
| `19-editor-feature-flags` | Feature Flags Matrix | `decision_matrix` | "feature flags", "rollout matrix" | `title` |
|
|
26
|
-
| `20-editor-prompt-tuner` | Prompt Tuner | `interactive_report` | "prompt tuning", "LLM config" | `title` |
|
|
27
|
-
|
|
28
|
-
_20 templates._
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, rmSync } from 'node:fs';
|
|
2
|
-
import { dirname, resolve, join } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
|
|
5
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const REPO_ROOT = resolve(HERE, '..', '..', '..');
|
|
7
|
-
export const ROOT = join(REPO_ROOT, '.tmp', 'html-effectiveness');
|
|
8
|
-
|
|
9
|
-
export function getTmpDir(purpose) {
|
|
10
|
-
if (!purpose || /[\\/]/.test(purpose)) {
|
|
11
|
-
throw new Error(`purpose must be a single path segment, got: ${purpose}`);
|
|
12
|
-
}
|
|
13
|
-
const dir = join(ROOT, purpose);
|
|
14
|
-
rmSync(dir, { recursive: true, force: true });
|
|
15
|
-
mkdirSync(dir, { recursive: true });
|
|
16
|
-
return dir;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function cleanup(purpose) {
|
|
20
|
-
rmSync(join(ROOT, purpose), { recursive: true, force: true });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function withTmp(purpose, fn) {
|
|
24
|
-
const dir = getTmpDir(purpose);
|
|
25
|
-
const onSig = () => {
|
|
26
|
-
try { cleanup(purpose); } finally { process.exit(130); }
|
|
27
|
-
};
|
|
28
|
-
process.once('SIGINT', onSig);
|
|
29
|
-
process.once('SIGTERM', onSig);
|
|
30
|
-
try {
|
|
31
|
-
return fn(dir);
|
|
32
|
-
} finally {
|
|
33
|
-
cleanup(purpose);
|
|
34
|
-
process.off('SIGINT', onSig);
|
|
35
|
-
process.off('SIGTERM', onSig);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Generate docs/template-gallery.md from templates/manifest.json.
|
|
3
|
-
// Idempotent. Run after editing manifest.
|
|
4
|
-
|
|
5
|
-
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
6
|
-
import { dirname, join, resolve } from 'node:path';
|
|
7
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
8
|
-
|
|
9
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
10
|
-
const PLUGIN_ROOT = dirname(HERE);
|
|
11
|
-
|
|
12
|
-
function build() {
|
|
13
|
-
const manifest = JSON.parse(readFileSync(join(PLUGIN_ROOT, 'templates/manifest.json'), 'utf8'));
|
|
14
|
-
const meta = manifest._meta || {};
|
|
15
|
-
const rows = Object.entries(manifest)
|
|
16
|
-
.filter(([k]) => k !== '_meta')
|
|
17
|
-
.sort(([a], [b]) => a.localeCompare(b));
|
|
18
|
-
|
|
19
|
-
const lines = [];
|
|
20
|
-
lines.push('# Template Gallery');
|
|
21
|
-
lines.push('');
|
|
22
|
-
lines.push(`Generated from \`templates/manifest.json\`. Upstream: \`${meta.upstream_repo}@${meta.upstream_sha}\` (fetched ${meta.fetched_at}).`);
|
|
23
|
-
lines.push('');
|
|
24
|
-
lines.push('| ID | Title | Pattern | Use cases | Required slots |');
|
|
25
|
-
lines.push('|----|-------|---------|-----------|----------------|');
|
|
26
|
-
for (const [id, e] of rows) {
|
|
27
|
-
const reqSlots = Object.entries(e.slots || {})
|
|
28
|
-
.filter(([, s]) => (typeof s === 'object' ? s.required : false))
|
|
29
|
-
.map(([n]) => `\`${n}\``)
|
|
30
|
-
.join(', ') || '—';
|
|
31
|
-
const use = (e.use_cases || []).map((u) => `"${u}"`).join(', ');
|
|
32
|
-
lines.push(`| \`${id}\` | ${e.title} | \`${e.pattern}\` | ${use} | ${reqSlots} |`);
|
|
33
|
-
}
|
|
34
|
-
lines.push('');
|
|
35
|
-
lines.push(`_${rows.length} templates._`);
|
|
36
|
-
lines.push('');
|
|
37
|
-
return lines.join('\n');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function main() {
|
|
41
|
-
const out = join(PLUGIN_ROOT, 'docs/template-gallery.md');
|
|
42
|
-
mkdirSync(dirname(out), { recursive: true });
|
|
43
|
-
writeFileSync(out, build(), 'utf8');
|
|
44
|
-
process.stdout.write(out + '\n');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (import.meta.url === pathToFileURL(process.argv[1]).href) main();
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { writeFileSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { getTmpDir, ROOT } from './_tmp.js';
|
|
6
|
-
|
|
7
|
-
const UPSTREAM_REPO = 'ThariqS/html-effectiveness';
|
|
8
|
-
const API_BASE = 'https://api.github.com';
|
|
9
|
-
const RAW_BASE = 'https://raw.githubusercontent.com';
|
|
10
|
-
|
|
11
|
-
async function ghFetch(url, accept = 'application/vnd.github+json') {
|
|
12
|
-
const headers = { 'Accept': accept, 'User-Agent': 'html-effectiveness-vendor' };
|
|
13
|
-
if (process.env.GITHUB_TOKEN) headers['Authorization'] = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
14
|
-
const res = await fetch(url, { headers });
|
|
15
|
-
if (!res.ok) {
|
|
16
|
-
throw new Error(`HTTP ${res.status} ${res.statusText} for ${url}`);
|
|
17
|
-
}
|
|
18
|
-
return res;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function fetchUpstreamFile(filename, ref = 'main') {
|
|
22
|
-
const url = `${RAW_BASE}/${UPSTREAM_REPO}/${ref}/${filename}`;
|
|
23
|
-
const res = await ghFetch(url, 'text/plain');
|
|
24
|
-
return await res.text();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function fetchUpstreamSha(ref = 'main') {
|
|
28
|
-
const url = `${API_BASE}/repos/${UPSTREAM_REPO}/commits/${ref}`;
|
|
29
|
-
const res = await ghFetch(url);
|
|
30
|
-
const json = await res.json();
|
|
31
|
-
return json.sha;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function listUpstreamFiles(ref = 'main') {
|
|
35
|
-
const url = `${API_BASE}/repos/${UPSTREAM_REPO}/contents?ref=${ref}`;
|
|
36
|
-
const res = await ghFetch(url);
|
|
37
|
-
const json = await res.json();
|
|
38
|
-
return json.filter((e) => e.type === 'file').map((e) => e.name);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function extractParts(html) {
|
|
42
|
-
const styles = [];
|
|
43
|
-
const scripts = [];
|
|
44
|
-
let body = html;
|
|
45
|
-
body = body.replace(/<style[^>]*>([\s\S]*?)<\/style>/g, (_, css) => {
|
|
46
|
-
styles.push(css.trim());
|
|
47
|
-
return '';
|
|
48
|
-
});
|
|
49
|
-
body = body.replace(/<script[^>]*>([\s\S]*?)<\/script>/g, (_, js) => {
|
|
50
|
-
if (js.trim()) scripts.push(js.trim());
|
|
51
|
-
return '';
|
|
52
|
-
});
|
|
53
|
-
body = body
|
|
54
|
-
.replace(/<!doctype[^>]*>/i, '')
|
|
55
|
-
.replace(/<\/?html[^>]*>/gi, '')
|
|
56
|
-
.replace(/<head[\s\S]*?<\/head>/gi, '')
|
|
57
|
-
.replace(/<\/?body[^>]*>/gi, '');
|
|
58
|
-
return { styles, scripts, body: body.trim() };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function ensureWorkdir() {
|
|
62
|
-
return getTmpDir('extract');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function readFromWorkdir(name) {
|
|
66
|
-
return readFileSync(join(ROOT, 'extract', name), 'utf8');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function writeToWorkdir(dir, name, content) {
|
|
70
|
-
writeFileSync(join(dir, name), content);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function cli() {
|
|
74
|
-
const [, , cmd, arg] = process.argv;
|
|
75
|
-
if (cmd === 'fetch') {
|
|
76
|
-
if (!arg) { process.stderr.write('usage: extract.js fetch <file>\n'); process.exit(1); }
|
|
77
|
-
const dir = ensureWorkdir();
|
|
78
|
-
const html = await fetchUpstreamFile(arg);
|
|
79
|
-
writeToWorkdir(dir, arg, html);
|
|
80
|
-
process.stdout.write(`${join(dir, arg)}\n`);
|
|
81
|
-
} else if (cmd === 'sha') {
|
|
82
|
-
process.stdout.write(await fetchUpstreamSha() + '\n');
|
|
83
|
-
} else if (cmd === 'list') {
|
|
84
|
-
const files = await listUpstreamFiles();
|
|
85
|
-
process.stdout.write(files.join('\n') + '\n');
|
|
86
|
-
} else if (cmd === 'split') {
|
|
87
|
-
if (!arg) { process.stderr.write('usage: extract.js split <file>\n'); process.exit(1); }
|
|
88
|
-
const html = readFromWorkdir(arg);
|
|
89
|
-
const { styles, scripts, body } = extractParts(html);
|
|
90
|
-
const base = arg.replace(/\.html$/, '');
|
|
91
|
-
const dir = join(ROOT, 'extract');
|
|
92
|
-
writeToWorkdir(dir, `${base}.body.html`, body + '\n');
|
|
93
|
-
writeToWorkdir(dir, `${base}.styles.css`, styles.join('\n\n/* --- */\n\n') + '\n');
|
|
94
|
-
writeToWorkdir(dir, `${base}.scripts.js`, scripts.join('\n\n/* --- */\n\n') + '\n');
|
|
95
|
-
process.stdout.write(`${dir}/${base}.{body.html,styles.css,scripts.js}\n`);
|
|
96
|
-
} else {
|
|
97
|
-
process.stderr.write('usage: extract.js {fetch <file>|sha|list|split <file>}\n');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
103
|
-
cli().catch((e) => { process.stderr.write(e.stack + '\n'); process.exit(1); });
|
|
104
|
-
}
|