@erclx/aitk 0.62.1 → 0.63.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/README.md +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/core/regen-hero.sh +75 -10
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
One source for your repos' AI conventions. Install once, sync everywhere.
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
If you work across more than one repository and your AI setup has started to drift between them, this is for you. The counts above are read from the catalogs when the image is built, so they're what the repo actually ships today.
|
|
12
12
|
|
package/package.json
CHANGED
|
@@ -19,6 +19,7 @@ PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
|
19
19
|
|
|
20
20
|
TEMPLATE="$PROJECT_ROOT/assets/hero.html.tmpl"
|
|
21
21
|
OUTPUT="$PROJECT_ROOT/assets/hero.html"
|
|
22
|
+
CLI_ENTRY="$PROJECT_ROOT/src/cli.ts"
|
|
22
23
|
LISTED=10
|
|
23
24
|
|
|
24
25
|
# `bun src/cli.ts` rather than `aitk`, since a globally linked binary resolves to
|
|
@@ -32,6 +33,20 @@ if [ ! -f "$TEMPLATE" ]; then
|
|
|
32
33
|
exit 1
|
|
33
34
|
fi
|
|
34
35
|
|
|
36
|
+
# The commands have no `--json` catalog to read, so the count comes from the
|
|
37
|
+
# registration block in the CLI entry point, which is the one place a command is
|
|
38
|
+
# added. `--help` is hand-authored ASCII and would drift from what is registered.
|
|
39
|
+
if [ ! -f "$CLI_ENTRY" ]; then
|
|
40
|
+
echo "regen-hero: missing CLI entry point at $CLI_ENTRY" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
COMMAND_COUNT="$(grep -c '^import { register as ' "$CLI_ENTRY" || true)"
|
|
45
|
+
if [ "${COMMAND_COUNT:-0}" -eq 0 ]; then
|
|
46
|
+
echo "regen-hero: no command registrations found in $CLI_ENTRY, refusing to write a zeroed hero" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
35
50
|
SKILLS_JSON="$(catalog claude skills list)"
|
|
36
51
|
GOV_JSON="$(catalog gov list)"
|
|
37
52
|
STANDARDS_JSON="$(catalog standards list)"
|
|
@@ -59,12 +74,12 @@ printf '%s' "$SNIPPETS_JSON" >"$PAYLOAD_DIR/snippets.json"
|
|
|
59
74
|
printf '%s' "$TOOLING_JSON" >"$PAYLOAD_DIR/tooling.json"
|
|
60
75
|
|
|
61
76
|
export PAYLOAD_DIR
|
|
62
|
-
export TEMPLATE OUTPUT LISTED PROJECT_ROOT
|
|
77
|
+
export TEMPLATE OUTPUT LISTED PROJECT_ROOT COMMAND_COUNT
|
|
63
78
|
|
|
64
79
|
bun --eval '
|
|
65
80
|
const { readFileSync } = require("node:fs")
|
|
66
81
|
|
|
67
|
-
const { PAYLOAD_DIR, TEMPLATE, OUTPUT, LISTED, PROJECT_ROOT } = process.env
|
|
82
|
+
const { PAYLOAD_DIR, TEMPLATE, OUTPUT, LISTED, PROJECT_ROOT, COMMAND_COUNT } = process.env
|
|
68
83
|
|
|
69
84
|
const payload = (name) => readFileSync(PAYLOAD_DIR + "/" + name + ".json", "utf8")
|
|
70
85
|
|
|
@@ -79,7 +94,13 @@ const skills = JSON.parse(SKILLS_JSON).skills.map((entry) => entry.name)
|
|
|
79
94
|
const gov = JSON.parse(GOV_JSON)
|
|
80
95
|
// Rule names carry a numeric prefix that orders the load, not the identity a
|
|
81
96
|
// reader knows them by, so the frame shows the slug alone.
|
|
82
|
-
const
|
|
97
|
+
const slug = (entry) => entry.name.replace(/^\d+-/, "")
|
|
98
|
+
const rules = gov.rules.map(slug)
|
|
99
|
+
// A rule no stack names is opt-in behind `--add`, so featuring one advertises
|
|
100
|
+
// an entry no ordinary `aitk gov install` delivers. The column samples the
|
|
101
|
+
// stack-reached subset while its count and remainder describe the whole catalog.
|
|
102
|
+
const stacked = new Set(gov.stacks.flatMap((stack) => stack.rules))
|
|
103
|
+
const deliveredRules = gov.rules.filter((entry) => stacked.has(entry.name)).map(slug)
|
|
83
104
|
const standards = JSON.parse(STANDARDS_JSON).standards.map((entry) => entry.name)
|
|
84
105
|
const snippets = new Set(
|
|
85
106
|
JSON.parse(SNIPPETS_JSON).categories.flatMap((category) => category.entries),
|
|
@@ -89,20 +110,62 @@ const toolingStacks = JSON.parse(TOOLING_JSON).stacks
|
|
|
89
110
|
const escape = (value) =>
|
|
90
111
|
value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
|
91
112
|
|
|
92
|
-
// Even spacing across the sorted catalog rather than its first N
|
|
93
|
-
// names
|
|
94
|
-
//
|
|
113
|
+
// Even spacing across the sorted catalog rather than its first N, so a column
|
|
114
|
+
// whose names share a prefix does not read as a narrower catalog than the one
|
|
115
|
+
// that ships. Rules and standards sample. Skills do not, because the even step
|
|
116
|
+
// lands on the entries closest to commodity and on none of the workflow the
|
|
117
|
+
// toolkit exists to carry.
|
|
95
118
|
const sample = (names) => {
|
|
96
119
|
if (names.length <= listed) return names
|
|
97
120
|
const step = names.length / listed
|
|
98
121
|
return Array.from({ length: listed }, (_, i) => names[Math.floor(i * step)])
|
|
99
122
|
}
|
|
100
123
|
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
// The first seven are the method and exist nowhere else. The last three are
|
|
125
|
+
// recognizable on sight and are what keeps a column of chosen names from
|
|
126
|
+
// reading as an all-`claude-` catalog, which is the failure the sampler above
|
|
127
|
+
// was written against.
|
|
128
|
+
const FEATURED_SKILLS = [
|
|
129
|
+
"claude-feature",
|
|
130
|
+
"claude-groundwork",
|
|
131
|
+
"claude-intake",
|
|
132
|
+
"claude-orchestrate",
|
|
133
|
+
"claude-autoship",
|
|
134
|
+
"claude-pr-review",
|
|
135
|
+
"claude-tasks",
|
|
136
|
+
"git-ship",
|
|
137
|
+
"setup-init",
|
|
138
|
+
"systematic-debugging",
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
// A chosen name is maintained by hand where a sample never goes stale, so a
|
|
142
|
+
// rename has to fail the run rather than quietly shrink the column. This is
|
|
143
|
+
// what makes the trade acceptable.
|
|
144
|
+
//
|
|
145
|
+
// The length is asserted because the `+N more` figure below counts down from
|
|
146
|
+
// `listed` rather than from what this returns, so a list of any other length
|
|
147
|
+
// renders a remainder that is wrong by the difference.
|
|
148
|
+
const featured = (names, chosen) => {
|
|
149
|
+
if (chosen.length !== listed) {
|
|
150
|
+
console.error(`regen-hero: ${chosen.length} featured skills, expected ${listed}`)
|
|
151
|
+
process.exit(1)
|
|
152
|
+
}
|
|
153
|
+
const catalog = new Set(names)
|
|
154
|
+
const missing = chosen.filter((name) => !catalog.has(name))
|
|
155
|
+
if (missing.length > 0) {
|
|
156
|
+
console.error(`regen-hero: featured skills missing from the catalog: ${missing.join(", ")}`)
|
|
157
|
+
process.exit(1)
|
|
158
|
+
}
|
|
159
|
+
return chosen
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const markup = (names) =>
|
|
163
|
+
names
|
|
103
164
|
.map((name) => ` <div class="entry">${escape(name)}</div>`)
|
|
104
165
|
.join("\n")
|
|
105
166
|
|
|
167
|
+
const entries = (names) => markup(sample(names))
|
|
168
|
+
|
|
106
169
|
const remaining = (names) => String(Math.max(0, names.length - listed))
|
|
107
170
|
|
|
108
171
|
// An empty array is well-formed JSON, so the shell guard on an empty payload
|
|
@@ -111,6 +174,7 @@ const remaining = (names) => String(Math.max(0, names.length - listed))
|
|
|
111
174
|
for (const [label, list] of [
|
|
112
175
|
["skills", skills], ["rules", rules], ["standards", standards],
|
|
113
176
|
["snippets", [...snippets]], ["tooling stacks", toolingStacks], ["gov stacks", gov.stacks],
|
|
177
|
+
["stack-delivered rules", deliveredRules],
|
|
114
178
|
]) {
|
|
115
179
|
if (list.length === 0) {
|
|
116
180
|
console.error(`regen-hero: the ${label} catalog is empty, refusing to write a zeroed hero`)
|
|
@@ -125,8 +189,9 @@ const values = {
|
|
|
125
189
|
SNIPPET_COUNT: String(snippets.size),
|
|
126
190
|
GOV_STACK_COUNT: String(gov.stacks.length),
|
|
127
191
|
TOOLING_STACK_COUNT: String(toolingStacks.length),
|
|
128
|
-
|
|
129
|
-
|
|
192
|
+
COMMAND_COUNT: String(Number(COMMAND_COUNT)),
|
|
193
|
+
SKILL_ENTRIES: markup(featured(skills, FEATURED_SKILLS)),
|
|
194
|
+
RULE_ENTRIES: entries(deliveredRules),
|
|
130
195
|
STANDARD_ENTRIES: entries(standards),
|
|
131
196
|
SKILL_MORE: remaining(skills),
|
|
132
197
|
RULE_MORE: remaining(rules),
|