artshelf 0.5.0 → 0.7.0
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/CHANGELOG.md +24 -0
- package/README.md +58 -27
- package/SPEC.md +28 -0
- package/docs/agent-clean.html +126 -0
- package/docs/agent-create.html +98 -0
- package/docs/agent-monitor.html +150 -0
- package/docs/agent-review.html +120 -0
- package/docs/agent-usage.html +104 -313
- package/docs/agent-usage.md +36 -379
- package/docs/examples/artshelf-review-report.json +116 -0
- package/docs/index.html +160 -152
- package/docs/install.html +187 -109
- package/docs/quickstart.html +105 -106
- package/docs/reference.html +214 -164
- package/docs/schemas/artshelf-review-report.schema.json +315 -0
- package/docs/site.css +675 -490
- package/docs/site.js +397 -0
- package/examples/artshelf-review-report.json +116 -0
- package/package.json +3 -1
- package/schemas/artshelf-review-report.schema.json +315 -0
- package/skills/artshelf/SKILL.md +140 -256
- package/skills/artshelf/examples/artshelf-review-report.json +116 -0
- package/skills/artshelf/schemas/artshelf-review-report.schema.json +315 -0
- package/skills/artshelf/scripts/render-review-report.mjs +160 -0
- package/docs/theme.js +0 -42
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://calvinnwq.github.io/artshelf/schemas/artshelf-review-report.schema.json",
|
|
4
|
+
"title": "ArtshelfReviewReport",
|
|
5
|
+
"description": "Machine-readable decision packet for rendering an Artshelf review report.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"schemaVersion",
|
|
10
|
+
"scope",
|
|
11
|
+
"decisionSummary",
|
|
12
|
+
"decisionGroups",
|
|
13
|
+
"summary",
|
|
14
|
+
"recommendation",
|
|
15
|
+
"items",
|
|
16
|
+
"alternatives",
|
|
17
|
+
"safety",
|
|
18
|
+
"verification"
|
|
19
|
+
],
|
|
20
|
+
"properties": {
|
|
21
|
+
"schemaVersion": {
|
|
22
|
+
"type": "integer",
|
|
23
|
+
"const": 1
|
|
24
|
+
},
|
|
25
|
+
"scope": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"required": ["registryPath", "ledgerCount", "health", "registryHealth"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"registryPath": { "type": "string" },
|
|
31
|
+
"ledgerCount": { "type": "integer", "minimum": 0 },
|
|
32
|
+
"health": { "type": "string", "enum": ["ok", "attention"] },
|
|
33
|
+
"registryHealth": { "type": "string", "enum": ["ok", "attention"] },
|
|
34
|
+
"affectedLedgers": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"items": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"additionalProperties": false,
|
|
39
|
+
"required": ["ledgerPath"],
|
|
40
|
+
"properties": {
|
|
41
|
+
"name": { "type": "string" },
|
|
42
|
+
"ledgerPath": { "type": "string" },
|
|
43
|
+
"validationStatus": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"enum": ["ok", "missing", "invalid"]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"plans": {
|
|
53
|
+
"type": "array",
|
|
54
|
+
"items": { "$ref": "#/$defs/plan" },
|
|
55
|
+
"default": []
|
|
56
|
+
},
|
|
57
|
+
"decisionSummary": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"required": ["readyForApproval", "needsReviewFirst", "blocked"],
|
|
61
|
+
"properties": {
|
|
62
|
+
"readyForApproval": { "type": "integer", "minimum": 0 },
|
|
63
|
+
"needsReviewFirst": { "type": "integer", "minimum": 0 },
|
|
64
|
+
"blocked": { "type": "integer", "minimum": 0 }
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"decisionGroups": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"required": ["readyForApproval", "needsReviewFirst", "blocked"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"readyForApproval": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": { "$ref": "#/$defs/approvalDecision" }
|
|
75
|
+
},
|
|
76
|
+
"needsReviewFirst": {
|
|
77
|
+
"type": "array",
|
|
78
|
+
"items": { "$ref": "#/$defs/nonApprovalDecision" }
|
|
79
|
+
},
|
|
80
|
+
"blocked": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": { "$ref": "#/$defs/nonApprovalDecision" }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"summary": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"additionalProperties": false,
|
|
89
|
+
"required": [
|
|
90
|
+
"executable",
|
|
91
|
+
"skipped",
|
|
92
|
+
"refused",
|
|
93
|
+
"manualReview",
|
|
94
|
+
"missingPath",
|
|
95
|
+
"trashed"
|
|
96
|
+
],
|
|
97
|
+
"properties": {
|
|
98
|
+
"executable": { "type": "integer", "minimum": 0 },
|
|
99
|
+
"skipped": { "type": "integer", "minimum": 0 },
|
|
100
|
+
"refused": { "type": "integer", "minimum": 0 },
|
|
101
|
+
"manualReview": { "type": "integer", "minimum": 0 },
|
|
102
|
+
"missingPath": { "type": "integer", "minimum": 0 },
|
|
103
|
+
"trashed": { "type": "integer", "minimum": 0 }
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"recommendation": { "type": "string", "minLength": 1 },
|
|
107
|
+
"items": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"items": { "$ref": "#/$defs/item" }
|
|
110
|
+
},
|
|
111
|
+
"alternatives": {
|
|
112
|
+
"type": "array",
|
|
113
|
+
"minItems": 1,
|
|
114
|
+
"items": { "type": "string", "minLength": 1 }
|
|
115
|
+
},
|
|
116
|
+
"safety": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"additionalProperties": false,
|
|
119
|
+
"required": [
|
|
120
|
+
"dryRunOnly",
|
|
121
|
+
"executeAllRefused",
|
|
122
|
+
"noExecuteRan",
|
|
123
|
+
"noResolveRan",
|
|
124
|
+
"noDeleteRan"
|
|
125
|
+
],
|
|
126
|
+
"properties": {
|
|
127
|
+
"dryRunOnly": { "type": "boolean" },
|
|
128
|
+
"executeAllRefused": { "type": "boolean" },
|
|
129
|
+
"noExecuteRan": { "type": "boolean" },
|
|
130
|
+
"noResolveRan": { "type": "boolean" },
|
|
131
|
+
"noDeleteRan": { "type": "boolean" }
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"verification": {
|
|
135
|
+
"type": "object",
|
|
136
|
+
"additionalProperties": false,
|
|
137
|
+
"required": ["command", "successCondition"],
|
|
138
|
+
"properties": {
|
|
139
|
+
"command": { "type": "string", "minLength": 1 },
|
|
140
|
+
"successCondition": { "type": "string", "minLength": 1 }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"$defs": {
|
|
145
|
+
"plan": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"additionalProperties": false,
|
|
148
|
+
"required": ["type", "ledgerPath", "planId", "approvalTarget"],
|
|
149
|
+
"properties": {
|
|
150
|
+
"type": { "type": "string", "enum": ["cleanup", "trash-purge"] },
|
|
151
|
+
"ledgerPath": { "type": "string" },
|
|
152
|
+
"planId": { "type": "string" },
|
|
153
|
+
"planPath": { "type": ["string", "null"] },
|
|
154
|
+
"approvalTarget": { "type": "string" }
|
|
155
|
+
},
|
|
156
|
+
"allOf": [
|
|
157
|
+
{
|
|
158
|
+
"if": {
|
|
159
|
+
"properties": { "type": { "const": "cleanup" } },
|
|
160
|
+
"required": ["type"]
|
|
161
|
+
},
|
|
162
|
+
"then": {
|
|
163
|
+
"properties": {
|
|
164
|
+
"approvalTarget": {
|
|
165
|
+
"pattern": "^approve artshelf cleanup ledger .+ plan .+$"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"if": {
|
|
172
|
+
"properties": { "type": { "const": "trash-purge" } },
|
|
173
|
+
"required": ["type"]
|
|
174
|
+
},
|
|
175
|
+
"then": {
|
|
176
|
+
"properties": {
|
|
177
|
+
"approvalTarget": {
|
|
178
|
+
"pattern": "^approve artshelf trash purge ledger .+ plan .+$"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
"item": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"additionalProperties": false,
|
|
188
|
+
"required": [
|
|
189
|
+
"classification",
|
|
190
|
+
"proposedAction",
|
|
191
|
+
"dueStatus",
|
|
192
|
+
"reason",
|
|
193
|
+
"note"
|
|
194
|
+
],
|
|
195
|
+
"properties": {
|
|
196
|
+
"id": { "type": "string" },
|
|
197
|
+
"path": { "type": "string" },
|
|
198
|
+
"classification": {
|
|
199
|
+
"type": "string",
|
|
200
|
+
"enum": [
|
|
201
|
+
"trash-safe",
|
|
202
|
+
"needs-human-review",
|
|
203
|
+
"resolve-candidate",
|
|
204
|
+
"registry-problem"
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
"proposedAction": { "type": "string", "minLength": 1 },
|
|
208
|
+
"dueStatus": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"enum": ["kept", "due", "manual-review", "missing-path", "trashed"]
|
|
211
|
+
},
|
|
212
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
213
|
+
"note": { "type": "string", "minLength": 1 }
|
|
214
|
+
},
|
|
215
|
+
"anyOf": [
|
|
216
|
+
{ "required": ["id"] },
|
|
217
|
+
{ "required": ["path"] }
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
"decision": {
|
|
221
|
+
"type": "object",
|
|
222
|
+
"additionalProperties": false,
|
|
223
|
+
"required": ["label", "actionType", "reason", "nextStep"],
|
|
224
|
+
"properties": {
|
|
225
|
+
"label": { "type": "string", "minLength": 1 },
|
|
226
|
+
"itemIds": {
|
|
227
|
+
"type": "array",
|
|
228
|
+
"items": { "type": "string", "minLength": 1 },
|
|
229
|
+
"default": []
|
|
230
|
+
},
|
|
231
|
+
"actionType": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"enum": [
|
|
234
|
+
"cleanup",
|
|
235
|
+
"trash-purge",
|
|
236
|
+
"resolve-missing",
|
|
237
|
+
"inspect",
|
|
238
|
+
"fix-registry",
|
|
239
|
+
"keep-or-snooze",
|
|
240
|
+
"change-retention"
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
"approvalTarget": { "type": ["string", "null"] },
|
|
244
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
245
|
+
"nextStep": { "type": "string", "minLength": 1 }
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"approvalDecision": {
|
|
249
|
+
"allOf": [
|
|
250
|
+
{ "$ref": "#/$defs/decision" },
|
|
251
|
+
{
|
|
252
|
+
"required": ["approvalTarget"],
|
|
253
|
+
"properties": {
|
|
254
|
+
"actionType": {
|
|
255
|
+
"enum": ["cleanup", "trash-purge", "resolve-missing"]
|
|
256
|
+
},
|
|
257
|
+
"approvalTarget": { "type": "string", "minLength": 1 }
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"if": {
|
|
262
|
+
"properties": { "actionType": { "const": "cleanup" } },
|
|
263
|
+
"required": ["actionType"]
|
|
264
|
+
},
|
|
265
|
+
"then": {
|
|
266
|
+
"properties": {
|
|
267
|
+
"approvalTarget": {
|
|
268
|
+
"pattern": "^approve artshelf cleanup ledger .+ plan .+$"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"if": {
|
|
275
|
+
"properties": { "actionType": { "const": "trash-purge" } },
|
|
276
|
+
"required": ["actionType"]
|
|
277
|
+
},
|
|
278
|
+
"then": {
|
|
279
|
+
"properties": {
|
|
280
|
+
"approvalTarget": {
|
|
281
|
+
"pattern": "^approve artshelf trash purge ledger .+ plan .+$"
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"if": {
|
|
288
|
+
"properties": { "actionType": { "const": "resolve-missing" } },
|
|
289
|
+
"required": ["actionType"]
|
|
290
|
+
},
|
|
291
|
+
"then": {
|
|
292
|
+
"properties": {
|
|
293
|
+
"approvalTarget": {
|
|
294
|
+
"pattern": "^approve artshelf resolve missing ledger .+ ids .+$"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
"nonApprovalDecision": {
|
|
302
|
+
"allOf": [
|
|
303
|
+
{ "$ref": "#/$defs/decision" },
|
|
304
|
+
{
|
|
305
|
+
"properties": {
|
|
306
|
+
"actionType": {
|
|
307
|
+
"enum": ["inspect", "fix-registry", "keep-or-snooze", "change-retention"]
|
|
308
|
+
},
|
|
309
|
+
"approvalTarget": { "type": "null" }
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
const APPROVAL_ACTIONS = new Set(["cleanup", "trash-purge", "resolve-missing"]);
|
|
7
|
+
const NON_APPROVAL_ACTIONS = new Set(["inspect", "fix-registry", "keep-or-snooze", "change-retention"]);
|
|
8
|
+
const APPROVAL_TARGET_PATTERNS = {
|
|
9
|
+
cleanup: /^approve artshelf cleanup ledger .+ plan .+$/,
|
|
10
|
+
"trash-purge": /^approve artshelf trash purge ledger .+ plan .+$/,
|
|
11
|
+
"resolve-missing": /^approve artshelf resolve missing ledger .+ ids .+$/
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function readInput() {
|
|
15
|
+
const path = process.argv[2];
|
|
16
|
+
if (path && path !== "-") {
|
|
17
|
+
return readFileSync(path, "utf8");
|
|
18
|
+
}
|
|
19
|
+
return readFileSync(0, "utf8");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function lineForEmpty(value) {
|
|
23
|
+
return value.length === 0 ? "<none>" : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function formatApprovalDecision(decision, index) {
|
|
27
|
+
const lines = [
|
|
28
|
+
`${index + 1}. ${decision.label}`,
|
|
29
|
+
` Why: ${decision.reason}`,
|
|
30
|
+
` Action: ${decision.nextStep}`,
|
|
31
|
+
` ${decision.approvalTarget}`
|
|
32
|
+
];
|
|
33
|
+
return lines.join("\n");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function formatNonApprovalDecision(decision, index) {
|
|
37
|
+
return [
|
|
38
|
+
`${index + 1}. ${decision.label}`,
|
|
39
|
+
` Why: ${decision.reason}`,
|
|
40
|
+
` Suggested next step: ${decision.nextStep}`
|
|
41
|
+
].join("\n");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatGroup(title, decisions, formatter) {
|
|
45
|
+
const empty = lineForEmpty(decisions);
|
|
46
|
+
return [
|
|
47
|
+
title,
|
|
48
|
+
empty ?? decisions.map((decision, index) => formatter(decision, index)).join("\n\n")
|
|
49
|
+
].join("\n");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function requireBoolean(report, path) {
|
|
53
|
+
const value = path.reduce((current, key) => current?.[key], report);
|
|
54
|
+
if (typeof value !== "boolean") {
|
|
55
|
+
throw new Error(`missing boolean ${path.join(".")}`);
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function requireArray(report, path) {
|
|
61
|
+
const value = path.reduce((current, key) => current?.[key], report);
|
|
62
|
+
if (!Array.isArray(value)) {
|
|
63
|
+
throw new Error(`missing array ${path.join(".")}`);
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function requireString(value, path) {
|
|
69
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
70
|
+
throw new Error(`missing string ${path}`);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function requireActionType(value, allowed, path) {
|
|
76
|
+
const actionType = requireString(value, path);
|
|
77
|
+
if (!allowed.has(actionType)) {
|
|
78
|
+
throw new Error(`unsupported actionType ${path}`);
|
|
79
|
+
}
|
|
80
|
+
return actionType;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validateDecision(decision, path, allowedActions) {
|
|
84
|
+
if (!decision || typeof decision !== "object" || Array.isArray(decision)) {
|
|
85
|
+
throw new Error(`missing object ${path}`);
|
|
86
|
+
}
|
|
87
|
+
requireString(decision.label, `${path}.label`);
|
|
88
|
+
requireActionType(decision.actionType, allowedActions, `${path}.actionType`);
|
|
89
|
+
requireString(decision.reason, `${path}.reason`);
|
|
90
|
+
requireString(decision.nextStep, `${path}.nextStep`);
|
|
91
|
+
return decision;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function validateApprovalDecision(decision, index) {
|
|
95
|
+
const path = `decisionGroups.readyForApproval.${index}`;
|
|
96
|
+
validateDecision(decision, path, APPROVAL_ACTIONS);
|
|
97
|
+
const approvalTarget = requireString(decision.approvalTarget, `${path}.approvalTarget`);
|
|
98
|
+
if (!APPROVAL_TARGET_PATTERNS[decision.actionType].test(approvalTarget)) {
|
|
99
|
+
throw new Error(`invalid approvalTarget ${path}.approvalTarget`);
|
|
100
|
+
}
|
|
101
|
+
return decision;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function validateNonApprovalDecision(group, decision, index) {
|
|
105
|
+
return validateDecision(decision, `decisionGroups.${group}.${index}`, NON_APPROVAL_ACTIONS);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function renderReviewReport(report) {
|
|
109
|
+
if (report?.schemaVersion !== 1) {
|
|
110
|
+
throw new Error("unsupported ArtshelfReviewReport schemaVersion");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const scope = report.scope ?? {};
|
|
114
|
+
const ready = requireArray(report, ["decisionGroups", "readyForApproval"]).map(validateApprovalDecision);
|
|
115
|
+
const needsReview = requireArray(report, ["decisionGroups", "needsReviewFirst"])
|
|
116
|
+
.map((decision, index) => validateNonApprovalDecision("needsReviewFirst", decision, index));
|
|
117
|
+
const blocked = requireArray(report, ["decisionGroups", "blocked"])
|
|
118
|
+
.map((decision, index) => validateNonApprovalDecision("blocked", decision, index));
|
|
119
|
+
const recommendation = requireString(report.recommendation, "recommendation");
|
|
120
|
+
|
|
121
|
+
const dryRunOnly = requireBoolean(report, ["safety", "dryRunOnly"]);
|
|
122
|
+
const executeAllRefused = requireBoolean(report, ["safety", "executeAllRefused"]);
|
|
123
|
+
const noExecuteRan = requireBoolean(report, ["safety", "noExecuteRan"]);
|
|
124
|
+
const noResolveRan = requireBoolean(report, ["safety", "noResolveRan"]);
|
|
125
|
+
const noDeleteRan = requireBoolean(report, ["safety", "noDeleteRan"]);
|
|
126
|
+
const safetyLine = dryRunOnly && executeAllRefused && noExecuteRan && noResolveRan && noDeleteRan
|
|
127
|
+
? "Dry-run only. No execute, resolve, or delete ran."
|
|
128
|
+
: "Attention: safety flags show a mutation may have run.";
|
|
129
|
+
|
|
130
|
+
return [
|
|
131
|
+
"Artshelf daily review",
|
|
132
|
+
`Status: ${scope.health ?? "attention"}; registry ${scope.registryHealth ?? "attention"}`,
|
|
133
|
+
"",
|
|
134
|
+
`Ready for approval: ${ready.length}`,
|
|
135
|
+
`Needs review first: ${needsReview.length}`,
|
|
136
|
+
`Blocked: ${blocked.length}`,
|
|
137
|
+
"",
|
|
138
|
+
"Recommended action",
|
|
139
|
+
recommendation,
|
|
140
|
+
"",
|
|
141
|
+
formatGroup("Ready for approval", ready, formatApprovalDecision),
|
|
142
|
+
"",
|
|
143
|
+
formatGroup("Needs review first", needsReview, formatNonApprovalDecision),
|
|
144
|
+
"",
|
|
145
|
+
formatGroup("Blocked", blocked, formatNonApprovalDecision),
|
|
146
|
+
"",
|
|
147
|
+
"Safety",
|
|
148
|
+
safetyLine
|
|
149
|
+
].join("\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
153
|
+
try {
|
|
154
|
+
const report = JSON.parse(readInput());
|
|
155
|
+
process.stdout.write(`${renderReviewReport(report)}\n`);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
package/docs/theme.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
(function () {
|
|
2
|
-
const storageKey = "artshelf-docs-theme";
|
|
3
|
-
const root = document.documentElement;
|
|
4
|
-
const systemDark = window.matchMedia("(prefers-color-scheme: dark)");
|
|
5
|
-
const toggleIcon = `
|
|
6
|
-
<svg class="theme-icon-moon" viewBox="0 0 20 20" aria-hidden="true"><path d="M14.6 12.1A6.5 6.5 0 0 1 7.4 2.7a6.5 6.5 0 1 0 7.2 9.4z" fill="currentColor"></path></svg>
|
|
7
|
-
<svg class="theme-icon-sun" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="3.4" fill="currentColor"></circle><g stroke="currentColor" stroke-width="1.6" stroke-linecap="round"><line x1="10" y1="2" x2="10" y2="4"></line><line x1="10" y1="16" x2="10" y2="18"></line><line x1="2" y1="10" x2="4" y2="10"></line><line x1="16" y1="10" x2="18" y2="10"></line><line x1="4.2" y1="4.2" x2="5.6" y2="5.6"></line><line x1="14.4" y1="14.4" x2="15.8" y2="15.8"></line><line x1="4.2" y1="15.8" x2="5.6" y2="14.4"></line><line x1="14.4" y1="5.6" x2="15.8" y2="4.2"></line></g></svg>
|
|
8
|
-
`;
|
|
9
|
-
|
|
10
|
-
function preferredTheme() {
|
|
11
|
-
const saved = window.localStorage.getItem(storageKey);
|
|
12
|
-
if (saved === "light" || saved === "dark") return saved;
|
|
13
|
-
return systemDark.matches ? "dark" : "light";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function applyTheme(theme) {
|
|
17
|
-
root.dataset.theme = theme;
|
|
18
|
-
root.style.colorScheme = theme;
|
|
19
|
-
document.querySelectorAll("[data-theme-toggle]").forEach((button) => {
|
|
20
|
-
button.setAttribute("aria-pressed", theme === "dark" ? "true" : "false");
|
|
21
|
-
button.setAttribute("title", theme === "dark" ? "Switch to light mode" : "Switch to dark mode");
|
|
22
|
-
if (button.innerHTML !== toggleIcon) button.innerHTML = toggleIcon;
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
applyTheme(preferredTheme());
|
|
27
|
-
|
|
28
|
-
window.addEventListener("DOMContentLoaded", () => {
|
|
29
|
-
applyTheme(preferredTheme());
|
|
30
|
-
document.querySelectorAll("[data-theme-toggle]").forEach((button) => {
|
|
31
|
-
button.addEventListener("click", () => {
|
|
32
|
-
const nextTheme = root.dataset.theme === "dark" ? "light" : "dark";
|
|
33
|
-
window.localStorage.setItem(storageKey, nextTheme);
|
|
34
|
-
applyTheme(nextTheme);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
systemDark.addEventListener("change", () => {
|
|
40
|
-
if (!window.localStorage.getItem(storageKey)) applyTheme(preferredTheme());
|
|
41
|
-
});
|
|
42
|
-
})();
|