aiblueprint-cli 1.4.103 → 1.4.105
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 -7
- package/agents-config/skills/audit-skills/SKILL.md +79 -0
- package/agents-config/skills/audit-skills/agents/openai.yaml +7 -0
- package/agents-config/skills/audit-skills/scripts/audit-skills.mjs +346 -0
- package/package.json +1 -1
- package/agents-config/skills/audit/SKILL.md +0 -126
- package/agents-config/skills/audit/agents/openai.yaml +0 -10
- package/agents-config/skills/audit/assets/codex-icon.svg +0 -20
- package/agents-config/skills/commit/SKILL.md +0 -44
- package/agents-config/skills/commit/agents/openai.yaml +0 -10
- package/agents-config/skills/commit/assets/codex-icon.svg +0 -17
- package/agents-config/skills/create-pr/SKILL.md +0 -55
- package/agents-config/skills/create-pr/agents/openai.yaml +0 -10
- package/agents-config/skills/create-pr/assets/codex-icon.svg +0 -17
- package/agents-config/skills/oneshot/SKILL.md +0 -44
- package/agents-config/skills/oneshot/agents/openai.yaml +0 -10
- package/agents-config/skills/oneshot/assets/codex-icon.svg +0 -18
- package/agents-config/skills/tools/SKILL.md +0 -149
- package/agents-config/skills/use-artifacts/SKILL.md +0 -211
- package/agents-config/skills/use-artifacts/agents/openai.yaml +0 -7
- package/agents-config/skills/use-artifacts/assets/codex-icon.svg +0 -18
- package/agents-config/skills/use-artifacts/assets/local-runtime.js +0 -299
- package/agents-config/skills/use-artifacts/scripts/create_artifact.py +0 -317
- package/agents-config/skills/use-delegate/SKILL.md +0 -97
- package/agents-config/skills/use-delegate/agents/openai.yaml +0 -10
- package/agents-config/skills/use-delegate/assets/codex-icon.svg +0 -20
- package/agents-config/skills/use-delegate/references/models.md +0 -32
- package/agents-config/skills/use-goal/SKILL.md +0 -121
- package/agents-config/skills/use-goal/agents/openai.yaml +0 -7
- package/agents-config/skills/use-goal/assets/codex-icon.svg +0 -18
- package/agents-config/skills/use-goal/references/claude-code-goal.md +0 -65
- package/agents-config/skills/use-goal/references/codex-goal.md +0 -70
- package/agents-config/skills/use-goal/references/verification-harnesses.md +0 -108
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* local-runtime.js: local artifact capabilities shim. No Anthropic API,
|
|
3
|
-
* no claude.ai runtime, no network calls. Everything runs on plain
|
|
4
|
-
* browser APIs (Blob, <a download>, timers, in-memory cache).
|
|
5
|
-
*
|
|
6
|
-
* It mirrors the claude.ai artifact runtime surface (contract 0.1.14):
|
|
7
|
-
* - window.claude.downloads.save({filename, data})
|
|
8
|
-
* - window.claude.mcp.callTool / watchTool / listTools / invalidate
|
|
9
|
-
*
|
|
10
|
-
* If a real claude.ai runtime already installed a member, the shim
|
|
11
|
-
* leaves it untouched, so the same feature code is portable to a
|
|
12
|
-
* genuinely published artifact without changes.
|
|
13
|
-
*
|
|
14
|
-
* Local data sources replace viewer connectors. Register them BEFORE
|
|
15
|
-
* feature code runs:
|
|
16
|
-
*
|
|
17
|
-
* window.claudeLocal.registerTool("Analytics", "get_stats",
|
|
18
|
-
* async (input) => ({ visits: 1234 })); // live local source
|
|
19
|
-
* window.claudeLocal.registerTool("Analytics", "snapshot",
|
|
20
|
-
* { visits: 1234 }, { description: "Static snapshot 2026-07-22" });
|
|
21
|
-
*
|
|
22
|
-
* Then use the standard surface:
|
|
23
|
-
* const r = await window.claude.mcp.callTool("Analytics", "get_stats", { range: "7d" });
|
|
24
|
-
* render(r.payload);
|
|
25
|
-
*/
|
|
26
|
-
(function () {
|
|
27
|
-
"use strict";
|
|
28
|
-
if (typeof window === "undefined") return;
|
|
29
|
-
|
|
30
|
-
var root = window.claude;
|
|
31
|
-
if (!root || typeof root !== "object") {
|
|
32
|
-
root = {};
|
|
33
|
-
try { window.claude = root; } catch (e) { return; }
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* ------------------------------ downloads ------------------------------ */
|
|
37
|
-
|
|
38
|
-
var EXT_ALLOWLIST = ["gif", "png", "jpg", "jpeg", "webp", "mp4", "webm", "txt", "json", "md"];
|
|
39
|
-
var MIME_BY_EXT = {
|
|
40
|
-
gif: "image/gif", png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
|
|
41
|
-
webp: "image/webp", mp4: "video/mp4", webm: "video/webm",
|
|
42
|
-
txt: "text/plain", json: "application/json", md: "text/markdown"
|
|
43
|
-
};
|
|
44
|
-
var MAX_BYTES = 16 * 1024 * 1024;
|
|
45
|
-
var promptOpen = false;
|
|
46
|
-
|
|
47
|
-
function downloadsError(code, message) {
|
|
48
|
-
return { code: code, message: message };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function toBlob(data, mime) {
|
|
52
|
-
if (typeof data === "string" && data.length) return new Blob([data], { type: mime });
|
|
53
|
-
if (data instanceof Blob && data.size) return new Blob([data], { type: mime });
|
|
54
|
-
if (data instanceof ArrayBuffer && data.byteLength) return new Blob([data], { type: mime });
|
|
55
|
-
if (ArrayBuffer.isView(data) && data.byteLength) return new Blob([data], { type: mime });
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!root.downloads) {
|
|
60
|
-
root.downloads = {
|
|
61
|
-
save: function (request) {
|
|
62
|
-
return new Promise(function (resolve, reject) {
|
|
63
|
-
if (promptOpen) {
|
|
64
|
-
return reject(downloadsError("rate_limited", "A save prompt is already open."));
|
|
65
|
-
}
|
|
66
|
-
if (!request || typeof request !== "object") {
|
|
67
|
-
return reject(downloadsError("bad_request", "save() takes {filename, data}."));
|
|
68
|
-
}
|
|
69
|
-
var filename = request.filename;
|
|
70
|
-
if (typeof filename !== "string" || !filename || filename.length > 512) {
|
|
71
|
-
return reject(downloadsError("bad_request", "filename must be a non-empty string of at most 512 chars."));
|
|
72
|
-
}
|
|
73
|
-
var clean = filename.replace(/[\/\\:*?"<>|]+/g, "-").trim();
|
|
74
|
-
var dot = clean.lastIndexOf(".");
|
|
75
|
-
var ext = (dot > 0 ? clean.slice(dot + 1) : "").toLowerCase();
|
|
76
|
-
if (EXT_ALLOWLIST.indexOf(ext) === -1) {
|
|
77
|
-
return reject(downloadsError("rejected_extension",
|
|
78
|
-
"Extension \"." + ext + "\" is outside the allowlist: " + EXT_ALLOWLIST.join(" ")));
|
|
79
|
-
}
|
|
80
|
-
var blob = toBlob(request.data, MIME_BY_EXT[ext]);
|
|
81
|
-
if (!blob) {
|
|
82
|
-
return reject(downloadsError("bad_request",
|
|
83
|
-
"data must be a non-empty string, Blob, ArrayBuffer, or ArrayBufferView."));
|
|
84
|
-
}
|
|
85
|
-
if (blob.size > MAX_BYTES) {
|
|
86
|
-
return reject(downloadsError("too_large", "File is over 16 MiB (" + blob.size + " bytes)."));
|
|
87
|
-
}
|
|
88
|
-
promptOpen = true;
|
|
89
|
-
var accepted;
|
|
90
|
-
try {
|
|
91
|
-
accepted = window.confirm('Save "' + clean + '" (' + blob.size + " bytes)?");
|
|
92
|
-
} finally {
|
|
93
|
-
promptOpen = false;
|
|
94
|
-
}
|
|
95
|
-
if (!accepted) {
|
|
96
|
-
return reject(downloadsError("declined", "The viewer declined the save."));
|
|
97
|
-
}
|
|
98
|
-
var url = URL.createObjectURL(blob);
|
|
99
|
-
var a = document.createElement("a");
|
|
100
|
-
a.href = url;
|
|
101
|
-
a.download = clean;
|
|
102
|
-
document.body.appendChild(a);
|
|
103
|
-
a.click();
|
|
104
|
-
a.remove();
|
|
105
|
-
setTimeout(function () { URL.revokeObjectURL(url); }, 10000);
|
|
106
|
-
resolve({ status: "saved" });
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/* --------------------------------- mcp --------------------------------- */
|
|
113
|
-
|
|
114
|
-
var SEP = "\u0000";
|
|
115
|
-
var registry = {}; // "server\0tool" -> {source, isStatic, description}
|
|
116
|
-
var cache = {}; // identity -> {result, storedAt}
|
|
117
|
-
var watchers = {}; // identity -> [{handler, timer, refresh}]
|
|
118
|
-
|
|
119
|
-
function key(server, tool) { return server + SEP + tool; }
|
|
120
|
-
|
|
121
|
-
function canonical(value) {
|
|
122
|
-
if (value === undefined || value === null) return "null";
|
|
123
|
-
if (Array.isArray(value)) return "[" + value.map(canonical).join(",") + "]";
|
|
124
|
-
if (typeof value === "object") {
|
|
125
|
-
return "{" + Object.keys(value).sort().map(function (k) {
|
|
126
|
-
return JSON.stringify(k) + ":" + canonical(value[k]);
|
|
127
|
-
}).join(",") + "}";
|
|
128
|
-
}
|
|
129
|
-
return JSON.stringify(value);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function identity(server, tool, input) {
|
|
133
|
-
return key(server, tool) + SEP + canonical(input);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function mcpError(code, server, message, extra) {
|
|
137
|
-
var err = { code: code, message: message };
|
|
138
|
-
if (server !== undefined) err.server = server;
|
|
139
|
-
if (extra) Object.keys(extra).forEach(function (k) { err[k] = extra[k]; });
|
|
140
|
-
return err;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function envelope(payload) {
|
|
144
|
-
var text = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
145
|
-
var result = { content: [{ type: "text", text: text }], payload: payload };
|
|
146
|
-
if (payload !== null && typeof payload === "object") result.structuredContent = payload;
|
|
147
|
-
return result;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function cachedCopy(entry, revalidating) {
|
|
151
|
-
var copy = {};
|
|
152
|
-
Object.keys(entry.result).forEach(function (k) { copy[k] = entry.result[k]; });
|
|
153
|
-
copy.cache = { storedAt: entry.storedAt, revalidating: !!revalidating };
|
|
154
|
-
return copy;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function notifyData(id, result) {
|
|
158
|
-
(watchers[id] || []).forEach(function (w) {
|
|
159
|
-
try { w.handler({ type: "data", result: result }); } catch (e) { /* page bug */ }
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function execute(server, tool, input) {
|
|
164
|
-
var entry = registry[key(server, tool)];
|
|
165
|
-
if (!entry) {
|
|
166
|
-
return Promise.reject(mcpError("server_not_connected", server,
|
|
167
|
-
'No local source registered for "' + server + '" / "' + tool +
|
|
168
|
-
'". Call window.claudeLocal.registerTool(...) before feature code runs.'));
|
|
169
|
-
}
|
|
170
|
-
try { JSON.stringify(input === undefined ? null : input); } catch (e) {
|
|
171
|
-
return Promise.reject(mcpError("bad_request", server, "input must be plain JSON."));
|
|
172
|
-
}
|
|
173
|
-
return Promise.resolve()
|
|
174
|
-
.then(function () {
|
|
175
|
-
return entry.isStatic ? entry.source : entry.source(input === undefined ? null : input);
|
|
176
|
-
})
|
|
177
|
-
.then(function (payload) {
|
|
178
|
-
var result = envelope(payload);
|
|
179
|
-
cache[identity(server, tool, input)] = { result: result, storedAt: Date.now() };
|
|
180
|
-
return result;
|
|
181
|
-
})
|
|
182
|
-
.catch(function (e) {
|
|
183
|
-
if (e && e.code) throw e;
|
|
184
|
-
throw mcpError("tool_error", server, (e && e.message) || String(e), { result: e });
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (!root.mcp) {
|
|
189
|
-
root.mcp = {
|
|
190
|
-
callTool: function (server, tool, input, options) {
|
|
191
|
-
if (typeof server !== "string" || typeof tool !== "string") {
|
|
192
|
-
return Promise.reject(mcpError("bad_request", undefined, "server and tool must be strings."));
|
|
193
|
-
}
|
|
194
|
-
var opts = options || {};
|
|
195
|
-
var id = identity(server, tool, input);
|
|
196
|
-
var entry = cache[id];
|
|
197
|
-
var staleTime = 0;
|
|
198
|
-
var refresh = false;
|
|
199
|
-
if (opts.cache && typeof opts.cache === "object") {
|
|
200
|
-
staleTime = Math.min(opts.cache.staleTime || 0, 300000);
|
|
201
|
-
refresh = !!opts.cache.refresh;
|
|
202
|
-
}
|
|
203
|
-
if (opts.cache === false) entry = null;
|
|
204
|
-
if (refresh) entry = null;
|
|
205
|
-
if (entry && Date.now() - entry.storedAt < staleTime) {
|
|
206
|
-
return Promise.resolve(cachedCopy(entry, false));
|
|
207
|
-
}
|
|
208
|
-
return execute(server, tool, input).then(function (result) {
|
|
209
|
-
notifyData(id, result);
|
|
210
|
-
return result;
|
|
211
|
-
});
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
watchTool: function (server, tool, input, handler, options) {
|
|
215
|
-
if (typeof handler !== "function") {
|
|
216
|
-
throw new TypeError("watchTool handler must be a function.");
|
|
217
|
-
}
|
|
218
|
-
var opts = options || {};
|
|
219
|
-
var id = identity(server, tool, input);
|
|
220
|
-
var w = { handler: handler, timer: null, refresh: null };
|
|
221
|
-
(watchers[id] = watchers[id] || []).push(w);
|
|
222
|
-
|
|
223
|
-
w.refresh = function () {
|
|
224
|
-
execute(server, tool, input)
|
|
225
|
-
.then(function (result) { notifyData(id, result); })
|
|
226
|
-
.catch(function (err) {
|
|
227
|
-
try { w.handler({ type: "error", error: err }); } catch (e) { /* page bug */ }
|
|
228
|
-
});
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
Promise.resolve().then(function () {
|
|
232
|
-
var entry = cache[id];
|
|
233
|
-
if (entry) {
|
|
234
|
-
try { w.handler({ type: "data", result: cachedCopy(entry, false) }); } catch (e) { /* page bug */ }
|
|
235
|
-
} else {
|
|
236
|
-
w.refresh();
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
if (opts.refetchInterval) {
|
|
241
|
-
w.timer = setInterval(w.refresh, Math.max(5000, opts.refetchInterval));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
var done = false;
|
|
245
|
-
return function unsubscribe() {
|
|
246
|
-
if (done) return;
|
|
247
|
-
done = true;
|
|
248
|
-
if (w.timer) clearInterval(w.timer);
|
|
249
|
-
watchers[id] = (watchers[id] || []).filter(function (x) { return x !== w; });
|
|
250
|
-
};
|
|
251
|
-
},
|
|
252
|
-
|
|
253
|
-
invalidate: function (server, tool, input) {
|
|
254
|
-
Object.keys(cache).forEach(function (id) {
|
|
255
|
-
var match = true;
|
|
256
|
-
if (server !== undefined) match = id.indexOf(server + SEP) === 0;
|
|
257
|
-
if (match && tool !== undefined) match = id.indexOf(key(server, tool) + SEP) === 0;
|
|
258
|
-
if (match && input !== undefined) match = id === identity(server, tool, input);
|
|
259
|
-
if (match) {
|
|
260
|
-
delete cache[id];
|
|
261
|
-
var subs = watchers[id] || [];
|
|
262
|
-
if (subs.length) subs[0].refresh();
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
return Promise.resolve();
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
listTools: function () {
|
|
269
|
-
var byServer = {};
|
|
270
|
-
Object.keys(registry).forEach(function (k) {
|
|
271
|
-
var parts = k.split(SEP);
|
|
272
|
-
(byServer[parts[0]] = byServer[parts[0]] || []).push({
|
|
273
|
-
name: parts[1],
|
|
274
|
-
description: registry[k].description || ""
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
return Promise.resolve({
|
|
278
|
-
servers: Object.keys(byServer).map(function (s) {
|
|
279
|
-
return { server: s, authStatus: "connected", tools: byServer[s] };
|
|
280
|
-
})
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/* --------------------------- local registration ------------------------ */
|
|
287
|
-
|
|
288
|
-
window.claudeLocal = window.claudeLocal || {};
|
|
289
|
-
window.claudeLocal.registerTool = function (server, tool, source, opts) {
|
|
290
|
-
if (typeof server !== "string" || typeof tool !== "string") {
|
|
291
|
-
throw new TypeError("registerTool(server, tool, source): server and tool must be strings.");
|
|
292
|
-
}
|
|
293
|
-
registry[key(server, tool)] = {
|
|
294
|
-
source: source,
|
|
295
|
-
isStatic: typeof source !== "function",
|
|
296
|
-
description: (opts && opts.description) || ""
|
|
297
|
-
};
|
|
298
|
-
};
|
|
299
|
-
})();
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Scaffold a Claude-style local HTML artifact workspace."""
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import argparse
|
|
7
|
-
import json
|
|
8
|
-
import re
|
|
9
|
-
from datetime import datetime, timezone
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
GLOBAL_ARTIFACTS_DIR = Path.home() / ".agents" / "artifacts"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def slugify(value: str) -> str:
|
|
16
|
-
slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
|
17
|
-
return slug[:48].strip("-") or "artifact"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def unique_dir(base: Path, slug: str) -> Path:
|
|
21
|
-
candidate = base / slug
|
|
22
|
-
if not candidate.exists():
|
|
23
|
-
return candidate
|
|
24
|
-
|
|
25
|
-
stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
|
26
|
-
return base / f"{slug}-{stamp}"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def html_template(title: str, style: str, kind: str) -> str:
|
|
30
|
-
escaped_title = (
|
|
31
|
-
title.replace("&", "&")
|
|
32
|
-
.replace("<", "<")
|
|
33
|
-
.replace(">", ">")
|
|
34
|
-
.replace('"', """)
|
|
35
|
-
)
|
|
36
|
-
return f"""<!doctype html>
|
|
37
|
-
<html lang="en">
|
|
38
|
-
<head>
|
|
39
|
-
<meta charset="utf-8">
|
|
40
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
41
|
-
<title>{escaped_title}</title>
|
|
42
|
-
<style>
|
|
43
|
-
:root {{
|
|
44
|
-
color-scheme: dark;
|
|
45
|
-
--bg: #000000;
|
|
46
|
-
--panel: #111111;
|
|
47
|
-
--ink: #ffffff;
|
|
48
|
-
--muted: #888888;
|
|
49
|
-
--border: #333333;
|
|
50
|
-
--accent: #0070f3;
|
|
51
|
-
}}
|
|
52
|
-
* {{ box-sizing: border-box; }}
|
|
53
|
-
body {{
|
|
54
|
-
margin: 0;
|
|
55
|
-
min-height: 100vh;
|
|
56
|
-
background: var(--bg);
|
|
57
|
-
color: var(--ink);
|
|
58
|
-
font-family: Geist, Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
59
|
-
}}
|
|
60
|
-
main {{
|
|
61
|
-
width: min(1120px, calc(100vw - 32px));
|
|
62
|
-
margin: 0 auto;
|
|
63
|
-
padding: 48px 0;
|
|
64
|
-
}}
|
|
65
|
-
.shell {{
|
|
66
|
-
display: grid;
|
|
67
|
-
gap: 24px;
|
|
68
|
-
}}
|
|
69
|
-
.header {{
|
|
70
|
-
display: flex;
|
|
71
|
-
flex-wrap: wrap;
|
|
72
|
-
align-items: end;
|
|
73
|
-
justify-content: space-between;
|
|
74
|
-
gap: 16px;
|
|
75
|
-
border-bottom: 1px solid var(--border);
|
|
76
|
-
padding-bottom: 20px;
|
|
77
|
-
}}
|
|
78
|
-
.eyebrow {{
|
|
79
|
-
margin: 0 0 8px;
|
|
80
|
-
color: var(--accent);
|
|
81
|
-
font-size: 12px;
|
|
82
|
-
font-weight: 700;
|
|
83
|
-
letter-spacing: .08em;
|
|
84
|
-
text-transform: uppercase;
|
|
85
|
-
}}
|
|
86
|
-
h1 {{
|
|
87
|
-
margin: 0;
|
|
88
|
-
max-width: 760px;
|
|
89
|
-
font-size: clamp(40px, 7vw, 88px);
|
|
90
|
-
line-height: .95;
|
|
91
|
-
letter-spacing: -.03em;
|
|
92
|
-
}}
|
|
93
|
-
.meta {{
|
|
94
|
-
color: var(--muted);
|
|
95
|
-
font-size: 14px;
|
|
96
|
-
line-height: 1.5;
|
|
97
|
-
}}
|
|
98
|
-
.panel {{
|
|
99
|
-
min-height: 420px;
|
|
100
|
-
border: 1px solid var(--border);
|
|
101
|
-
border-radius: 8px;
|
|
102
|
-
background: var(--panel);
|
|
103
|
-
padding: 28px;
|
|
104
|
-
}}
|
|
105
|
-
.panel h2 {{
|
|
106
|
-
margin: 0 0 12px;
|
|
107
|
-
font-size: 24px;
|
|
108
|
-
letter-spacing: 0;
|
|
109
|
-
}}
|
|
110
|
-
.panel p {{
|
|
111
|
-
max-width: 680px;
|
|
112
|
-
color: var(--muted);
|
|
113
|
-
font-size: 17px;
|
|
114
|
-
line-height: 1.65;
|
|
115
|
-
}}
|
|
116
|
-
button {{
|
|
117
|
-
border: 0;
|
|
118
|
-
border-radius: 6px;
|
|
119
|
-
background: var(--ink);
|
|
120
|
-
color: var(--bg);
|
|
121
|
-
cursor: pointer;
|
|
122
|
-
font: inherit;
|
|
123
|
-
font-weight: 650;
|
|
124
|
-
padding: 10px 16px;
|
|
125
|
-
}}
|
|
126
|
-
.callout {{
|
|
127
|
-
border: 1px solid var(--border);
|
|
128
|
-
border-radius: 0;
|
|
129
|
-
background: var(--panel);
|
|
130
|
-
padding: 20px;
|
|
131
|
-
}}
|
|
132
|
-
.callout h2 {{ color: var(--ink); }}
|
|
133
|
-
.grid {{
|
|
134
|
-
display: grid;
|
|
135
|
-
gap: 16px;
|
|
136
|
-
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
137
|
-
}}
|
|
138
|
-
.croquis {{
|
|
139
|
-
min-height: 180px;
|
|
140
|
-
border: 1px solid var(--border);
|
|
141
|
-
border-radius: 0;
|
|
142
|
-
background: var(--bg);
|
|
143
|
-
padding: 14px;
|
|
144
|
-
}}
|
|
145
|
-
.bar {{
|
|
146
|
-
height: 14px;
|
|
147
|
-
border-radius: 0;
|
|
148
|
-
background: #666666;
|
|
149
|
-
margin-bottom: 10px;
|
|
150
|
-
}}
|
|
151
|
-
.box {{
|
|
152
|
-
height: 52px;
|
|
153
|
-
border: 1px solid var(--border);
|
|
154
|
-
border-radius: 0;
|
|
155
|
-
background: var(--panel);
|
|
156
|
-
margin-bottom: 10px;
|
|
157
|
-
}}
|
|
158
|
-
.note {{
|
|
159
|
-
color: var(--muted);
|
|
160
|
-
font-size: 14px;
|
|
161
|
-
line-height: 1.45;
|
|
162
|
-
}}
|
|
163
|
-
pre {{
|
|
164
|
-
overflow-x: auto;
|
|
165
|
-
border: 1px solid var(--border);
|
|
166
|
-
border-radius: 0;
|
|
167
|
-
background: var(--bg);
|
|
168
|
-
padding: 16px;
|
|
169
|
-
}}
|
|
170
|
-
code {{
|
|
171
|
-
border-radius: 0;
|
|
172
|
-
background: var(--panel);
|
|
173
|
-
padding: 2px 5px;
|
|
174
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
175
|
-
font-size: .92em;
|
|
176
|
-
}}
|
|
177
|
-
</style>
|
|
178
|
-
</head>
|
|
179
|
-
<body>
|
|
180
|
-
<main>
|
|
181
|
-
<section class="shell" aria-label="{escaped_title}">
|
|
182
|
-
<div class="header">
|
|
183
|
-
<div>
|
|
184
|
-
<p class="eyebrow">{kind} artifact</p>
|
|
185
|
-
<h1>{escaped_title}</h1>
|
|
186
|
-
</div>
|
|
187
|
-
<p class="meta">Style: {style}<br>Entrypoint: index.html</p>
|
|
188
|
-
</div>
|
|
189
|
-
<div class="callout">
|
|
190
|
-
<h2>Key finding or recommendation</h2>
|
|
191
|
-
<p>
|
|
192
|
-
Put the highest-signal conclusion near the top. Use this area for the
|
|
193
|
-
main security finding, product decision, implementation warning, or
|
|
194
|
-
core recommendation that frames the rest of the artifact.
|
|
195
|
-
</p>
|
|
196
|
-
</div>
|
|
197
|
-
<div class="panel">
|
|
198
|
-
<h2>Public reasoning surface</h2>
|
|
199
|
-
<p>
|
|
200
|
-
Turn the answer into a readable page: context, model, tradeoffs,
|
|
201
|
-
examples, edge cases, rollout steps, and verification notes. Show
|
|
202
|
-
conclusions and evidence, not private chain-of-thought.
|
|
203
|
-
</p>
|
|
204
|
-
<pre><code>// Add focused snippets when they clarify the plan.
|
|
205
|
-
function example() {{
|
|
206
|
-
return "replace this with the requested artifact content";
|
|
207
|
-
}}</code></pre>
|
|
208
|
-
</div>
|
|
209
|
-
<div class="panel">
|
|
210
|
-
<h2>Page draft</h2>
|
|
211
|
-
<p>
|
|
212
|
-
For plan artifacts, draft the actual page or content structure here:
|
|
213
|
-
title, lede, sections, key copy, calls to action, states, and the
|
|
214
|
-
narrative blocks the user should see.
|
|
215
|
-
</p>
|
|
216
|
-
</div>
|
|
217
|
-
<div class="grid">
|
|
218
|
-
<article class="panel">
|
|
219
|
-
<h2>Page croquis A</h2>
|
|
220
|
-
<div class="croquis">
|
|
221
|
-
<div class="bar" style="width: 72%;"></div>
|
|
222
|
-
<div class="box"></div>
|
|
223
|
-
<div class="box" style="width: 64%;"></div>
|
|
224
|
-
</div>
|
|
225
|
-
<p class="note">Sketch the page hierarchy quickly. This is for seeing and understanding, not final UI.</p>
|
|
226
|
-
</article>
|
|
227
|
-
<article class="panel">
|
|
228
|
-
<h2>Page croquis B</h2>
|
|
229
|
-
<div class="croquis">
|
|
230
|
-
<div class="box" style="height: 84px;"></div>
|
|
231
|
-
<div class="bar" style="width: 46%;"></div>
|
|
232
|
-
<div class="bar" style="width: 78%; opacity: .35;"></div>
|
|
233
|
-
</div>
|
|
234
|
-
<p class="note">Annotate what changes: layout, emphasis, rhythm, audience, or tradeoff.</p>
|
|
235
|
-
</article>
|
|
236
|
-
</div>
|
|
237
|
-
</section>
|
|
238
|
-
</main>
|
|
239
|
-
</body>
|
|
240
|
-
</html>
|
|
241
|
-
"""
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def highlogic_template(title: str, style: str, kind: str, created_at: str) -> str:
|
|
245
|
-
return f"""# {title}
|
|
246
|
-
|
|
247
|
-
## Intent
|
|
248
|
-
|
|
249
|
-
- Kind: {kind}
|
|
250
|
-
- Style: {style}
|
|
251
|
-
- Created: {created_at}
|
|
252
|
-
|
|
253
|
-
## User Request
|
|
254
|
-
|
|
255
|
-
TODO: Summarize the user's artifact request.
|
|
256
|
-
|
|
257
|
-
## Core Logic
|
|
258
|
-
|
|
259
|
-
TODO: Describe the public reasoning structure, sections, data model, interactions, and key design decisions. Capture conclusions, evidence, assumptions, and tradeoffs without private chain-of-thought.
|
|
260
|
-
|
|
261
|
-
## Verification
|
|
262
|
-
|
|
263
|
-
TODO: Record how the artifact was opened or tested, including browser/runtime checks when relevant.
|
|
264
|
-
|
|
265
|
-
## Iteration Notes
|
|
266
|
-
|
|
267
|
-
- Initial scaffold created.
|
|
268
|
-
"""
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
def main() -> int:
|
|
272
|
-
parser = argparse.ArgumentParser(description="Create a local HTML artifact workspace.")
|
|
273
|
-
parser.add_argument("title", help="Short artifact title or slug")
|
|
274
|
-
parser.add_argument("--style", default="custom", help="style label recorded in the manifest (requested, project:<app-name>, or subject-specific)")
|
|
275
|
-
parser.add_argument("--kind", default="thinking", help="artifact kind")
|
|
276
|
-
args = parser.parse_args()
|
|
277
|
-
|
|
278
|
-
base = GLOBAL_ARTIFACTS_DIR.resolve()
|
|
279
|
-
artifact_id = slugify(args.title)
|
|
280
|
-
artifact_dir = unique_dir(base, artifact_id)
|
|
281
|
-
created_at = datetime.now(timezone.utc).isoformat()
|
|
282
|
-
|
|
283
|
-
artifact_dir.mkdir(parents=True, exist_ok=False)
|
|
284
|
-
(artifact_dir / "versions").mkdir()
|
|
285
|
-
|
|
286
|
-
title = args.title.strip()
|
|
287
|
-
(artifact_dir / "index.html").write_text(
|
|
288
|
-
html_template(title, args.style, args.kind),
|
|
289
|
-
encoding="utf-8",
|
|
290
|
-
)
|
|
291
|
-
(artifact_dir / "HIGHLOGIC.md").write_text(
|
|
292
|
-
highlogic_template(title, args.style, args.kind, created_at),
|
|
293
|
-
encoding="utf-8",
|
|
294
|
-
)
|
|
295
|
-
manifest = {
|
|
296
|
-
"id": artifact_dir.name,
|
|
297
|
-
"title": title,
|
|
298
|
-
"kind": args.kind,
|
|
299
|
-
"style": args.style,
|
|
300
|
-
"created_at": created_at,
|
|
301
|
-
"entrypoint": "index.html",
|
|
302
|
-
"files": ["index.html", "HIGHLOGIC.md", "manifest.json"],
|
|
303
|
-
}
|
|
304
|
-
(artifact_dir / "manifest.json").write_text(
|
|
305
|
-
json.dumps(manifest, indent=2) + "\n",
|
|
306
|
-
encoding="utf-8",
|
|
307
|
-
)
|
|
308
|
-
|
|
309
|
-
index_path = artifact_dir / "index.html"
|
|
310
|
-
print(f"created={artifact_dir}")
|
|
311
|
-
print(f"index={index_path}")
|
|
312
|
-
print(f"url={index_path.as_uri()}")
|
|
313
|
-
return 0
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if __name__ == "__main__":
|
|
317
|
-
raise SystemExit(main())
|