@jenga-ai/agent 3.6.0 → 4.1.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 +32 -1
- package/lib/generate-skill-allow-list.js +42 -5
- package/lib/skill-allow-list.json +1 -1
- package/package.json +1 -2
- package/project/app/api/lib/resolve-project-root.js +1 -1
- package/project/app/api/scripts/capture-snapshot.js +12 -7
- package/project/app/package.json +4 -0
- package/project/app/ui/dist/assets/index-BADc5mmH.css +1 -0
- package/project/app/ui/dist/assets/index-DX2pfTAW.js +104 -0
- package/project/app/ui/dist/index.html +2 -2
- package/scripts/acquire-concurrency-slot.sh +34 -4
- package/scripts/apply-j-prefix.sh +1 -1
- package/scripts/delete-bare-skill-dirs.sh +1 -2
- package/scripts/idea_manager.sh +16 -1
- package/scripts/release-concurrency-slot.sh +34 -4
- package/scripts/render-ranked-list.sh +270 -0
- package/scripts/repoint-dead-bare-path-prose.py +81 -0
- package/scripts/rewrite-stale-skill-preambles.py +188 -0
- package/scripts/strip-polyfill-frontmatter.py +166 -0
- package/scripts/todo_manager.sh +16 -1
- package/scripts/validate-typed-object.sh +750 -0
- package/scripts/verify-postinstall-reconcile.sh +33 -7
- package/skills/index/scripts/board_index.py +29 -1
- package/skills/j-brainstorm/SKILL.md +3 -4
- package/skills/j-btw/SKILL.md +3 -4
- package/skills/j-clearify/SKILL.md +3 -4
- package/skills/j-close-story/SKILL.md +11 -12
- package/skills/j-close-story/scripts/check-story-closeable.sh +11 -4
- package/skills/j-commit/SKILL.md +3 -4
- package/skills/j-continue/SKILL.md +5 -6
- package/skills/j-deep-dive/SKILL.md +3 -4
- package/skills/j-distribute/SKILL.md +3 -4
- package/skills/j-do/SKILL.md +13 -15
- package/skills/j-doc/SKILL.md +3 -4
- package/skills/j-doc/scripts/resolve_last_update.py +27 -1
- package/skills/j-doc-sync/SKILL.md +3 -4
- package/skills/j-dooo/SKILL.md +6 -15
- package/skills/j-error/SKILL.md +3 -4
- package/skills/j-evaluate/SKILL.md +3 -4
- package/skills/j-examplify/SKILL.md +3 -4
- package/skills/j-help/SKILL.md +3 -4
- package/skills/j-idea/SKILL.md +3 -4
- package/skills/j-improve/SKILL.md +3 -4
- package/skills/j-init/SKILL.md +20 -11
- package/skills/j-init/scripts/apply-scaffold-visibility.sh +8 -6
- package/skills/j-jbp/SKILL.md +3 -4
- package/skills/j-lgtm/SKILL.md +3 -4
- package/skills/j-pi-plan/SKILL.md +3 -4
- package/skills/j-proceed/SKILL.md +3 -4
- package/skills/j-publish/SKILL.md +3 -4
- package/skills/j-publish/scripts/run_gates.sh +1 -1
- package/skills/j-reconcile/SKILL.md +38 -5
- package/skills/j-reconcile/assets/report_format.md +11 -0
- package/skills/j-reconcile/scripts/detect-unlinked-code.sh +2 -2
- package/skills/j-reconcile-origin/SKILL.md +3 -4
- package/skills/j-redo/SKILL.md +3 -4
- package/skills/j-skillify/SKILL.md +3 -4
- package/skills/j-spinoff/SKILL.md +3 -4
- package/skills/j-status/SKILL.md +4 -5
- package/skills/j-todo/SKILL.md +42 -5
- package/skills/j-todo/scripts/add_trivial_task.sh +12 -1
- package/skills/j-todo/scripts/argument-is-not-ranked-list.sh +92 -0
- package/skills/j-todo/scripts/argument-is-ranked-list.sh +78 -0
- package/skills/j-uncharted/SKILL.md +251 -12
- package/skills/j-uncharted/scripts/detect-dependencies.sh +79 -21
- package/skills/j-uncharted/scripts/diff-since-baseline.sh +600 -0
- package/skills/j-uncharted/scripts/find-scan-baseline.sh +545 -0
- package/skills/j-uncharted/scripts/run-engine.sh +36 -2
- package/skills/j-uncharted/scripts/write-scan-record.sh +361 -0
- package/skills/j-wtf/SKILL.md +3 -4
- package/skills/jenga/SKILL.md +106 -10
- package/skills/jenga/playbooks/schema.json +4 -4
- package/skills/jenga/scripts/enrich-nl-prompt.sh +225 -0
- package/skills/jenga/scripts/load-nl-catalog.js +6 -3
- package/skills/jenga/scripts/load-playbooks.sh +289 -4
- package/skills/jenga/scripts/match-playbook.sh +6 -5
- package/skills/jenga/scripts/run-playbook-step.sh +270 -3
- package/templates/permission-levels/level-1-locked.json +1 -1
- package/templates/permission-levels/level-2-guarded.json +1 -1
- package/templates/permission-levels/level-3-standard.json +1 -1
- package/templates/permission-levels/level-4-elevated.json +1 -1
- package/templates/permission-levels/level-5-unrestricted.json +1 -1
- package/templates/playbook-types.json +6 -0
- package/project/app/ui/dist/assets/index-BVR_7Owg.css +0 -1
- package/project/app/ui/dist/assets/index-CtU2xLQm.js +0 -104
- package/scripts/audit-twin-divergence.sh +0 -693
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
E50_S22_T02 — Rewrite the 37 stale generator-era body preambles to the canonical
|
|
4
|
+
hand-edited framing.
|
|
5
|
+
|
|
6
|
+
37 of the skills/j-*/SKILL.md files open their body (directly under the H1) with a
|
|
7
|
+
two-paragraph preamble describing the retired twin-generation era: a bare
|
|
8
|
+
skills/<name>/ "source" this file was supposedly generated from, and a warning to
|
|
9
|
+
re-run scripts/generate-j-alias.sh instead of hand-editing. Both the bare
|
|
10
|
+
directories (E50_S15) and the generator itself (E50_S14) are gone.
|
|
11
|
+
|
|
12
|
+
This script:
|
|
13
|
+
1. Replaces that two-paragraph block, per file, with the canonical/hand-edited
|
|
14
|
+
framing already used by skills/j-do/SKILL.md and skills/j-dev-done/SKILL.md,
|
|
15
|
+
name-substituted.
|
|
16
|
+
2. Corrects skills/j-do/SKILL.md's and skills/j-dev-done/SKILL.md's own existing
|
|
17
|
+
warning text, which predates E50_S15 landing and still claims a bare copy is
|
|
18
|
+
"awaiting deletion" and warns against running a generator that no longer
|
|
19
|
+
exists at all.
|
|
20
|
+
3. Rewrites j-dev-done's "Known divergence" paragraph, which asserted (future
|
|
21
|
+
tense) that the bare /dev-done form "still exhibits" old behavior "until
|
|
22
|
+
E50_S15 deletes it" — E50_S15 already landed, so this is restated as a past-
|
|
23
|
+
tense historical note.
|
|
24
|
+
|
|
25
|
+
Idempotent and safe to re-run (already-corrected files simply won't match and are
|
|
26
|
+
skipped). Fails loudly (non-zero exit) if a file containing the trigger phrase
|
|
27
|
+
"literal-directory-name duplicate" doesn't fit the fully-verified-uniform pattern,
|
|
28
|
+
rather than silently leaving it unrewritten.
|
|
29
|
+
|
|
30
|
+
Usage: scripts/rewrite-stale-skill-preambles.py [--dry-run]
|
|
31
|
+
Run from the repository root.
|
|
32
|
+
"""
|
|
33
|
+
import glob
|
|
34
|
+
import re
|
|
35
|
+
import sys
|
|
36
|
+
|
|
37
|
+
TRIGGER = "literal-directory-name duplicate"
|
|
38
|
+
|
|
39
|
+
STALE_PREAMBLE_RE = re.compile(
|
|
40
|
+
r"This skill is a literal-directory-name duplicate of `skills/(?P<name>[a-z0-9-]+)/`\. "
|
|
41
|
+
r"It exists so that `/j-(?P=name)` \(and `j\.j-(?P=name)`\) give a guaranteed-unshadowed "
|
|
42
|
+
r"way to reach the same flow as `/(?P=name)`, even if a host tool's own built-in command "
|
|
43
|
+
r"of the same name would otherwise shadow or override the bare `/(?P=name)` alias "
|
|
44
|
+
r"\(Claude Code's native skill resolution is a literal-string, directory-name-based match "
|
|
45
|
+
r"— see `docs/skill-authoring\.md`'s \"Invocation Convention\"\)\.\n\n"
|
|
46
|
+
r"This file is generated/synced by `scripts/generate-j-alias\.sh (?P=name)` from "
|
|
47
|
+
r"`skills/(?P=name)/SKILL\.md` — do not hand-edit it; re-run the generator instead to "
|
|
48
|
+
r"pick up source changes\."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def canonical_replacement(name):
|
|
53
|
+
return (
|
|
54
|
+
f"`skills/j-{name}/` is the **canonical, hand-edited** directory for this skill, per "
|
|
55
|
+
"CLAUDE.md's \"The Canonical Naming Contract\" (the `E50` reopening of 2026-09-09, "
|
|
56
|
+
f"which promoted `skills/j-{name}/` from generated twin to sole canonical form). The "
|
|
57
|
+
"`j-` prefix is there for collision safety — a real directory under a distinct name, so "
|
|
58
|
+
"a host tool shipping its own same-named built-in command cannot shadow it (Claude "
|
|
59
|
+
"Code's native skill resolution is a literal-string, directory-name-based match; see "
|
|
60
|
+
"`docs/skill-authoring.md`'s \"Invocation Convention\").\n\n"
|
|
61
|
+
"> ⚠️ **`scripts/generate-j-alias.sh` was retired by `E50_S14` and no longer exists — "
|
|
62
|
+
"there is nothing to run.** This file was previously generated from a bare "
|
|
63
|
+
f"`skills/{name}/SKILL.md` source; `E50_S15` deleted that directory. This file is now "
|
|
64
|
+
"the sole canonical, hand-edited source for this skill — edit it directly."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# --- j-do and j-dev-done: targeted corrections to their already-correct-model text ---
|
|
69
|
+
|
|
70
|
+
J_DO_OLD = (
|
|
71
|
+
"> ⚠️ **Do not run `scripts/generate-j-alias.sh do` against this directory.** This file was\n"
|
|
72
|
+
"> previously generated from `skills/do/SKILL.md`, and carried a banner saying so. That relationship\n"
|
|
73
|
+
"> is inverted under the contract above: edits land here first, and `skills/do/` is the copy awaiting\n"
|
|
74
|
+
"> deletion by `E50_S15`. Regenerating would overwrite this file from the stale bare directory.\n"
|
|
75
|
+
"> CLAUDE.md states the same prohibition in general terms; this is the concrete instance of it."
|
|
76
|
+
)
|
|
77
|
+
J_DO_NEW = (
|
|
78
|
+
"> ⚠️ **`scripts/generate-j-alias.sh` was retired by `E50_S14` and no longer exists — there is\n"
|
|
79
|
+
"> nothing to run.** This file was previously generated from a bare `skills/do/SKILL.md` source;\n"
|
|
80
|
+
"> `E50_S15` deleted that directory. This file is now the sole canonical, hand-edited source for\n"
|
|
81
|
+
"> this skill — edit it directly."
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
J_DEV_DONE_OLD_WARNING = (
|
|
85
|
+
"> ⚠️ **Do not run `scripts/generate-j-alias.sh dev-done` against this directory.** This file was\n"
|
|
86
|
+
"> previously generated from `skills/dev-done/SKILL.md`, and carried a banner saying so. That\n"
|
|
87
|
+
"> relationship is inverted under the contract above: edits land here first, and `skills/dev-done/` is\n"
|
|
88
|
+
"> the copy awaiting deletion by `E50_S15`. Regenerating would overwrite this file from the stale bare\n"
|
|
89
|
+
"> directory and silently drop the post-sync commit step below. CLAUDE.md states the same prohibition\n"
|
|
90
|
+
"> in general terms; this is the concrete instance of it."
|
|
91
|
+
)
|
|
92
|
+
J_DEV_DONE_NEW_WARNING = (
|
|
93
|
+
"> ⚠️ **`scripts/generate-j-alias.sh` was retired by `E50_S14` and no longer exists — there is\n"
|
|
94
|
+
"> nothing to run.** This file was previously generated from a bare `skills/dev-done/SKILL.md`\n"
|
|
95
|
+
"> source; `E50_S15` deleted that directory. This file is now the sole canonical, hand-edited\n"
|
|
96
|
+
"> source for this skill — edit it directly."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
J_DEV_DONE_OLD_DIVERGENCE = (
|
|
100
|
+
"> **Known divergence:** `skills/dev-done/` does **not** carry the post-sync commit step. Until\n"
|
|
101
|
+
"> `E50_S15` deletes it, the bare `/dev-done` form still exhibits the original\n"
|
|
102
|
+
"> leaves-the-tree-dirty behavior. Invoke `/j-dev-done` to get the fix."
|
|
103
|
+
)
|
|
104
|
+
J_DEV_DONE_NEW_DIVERGENCE = (
|
|
105
|
+
"> **Historical note:** before `E50_S15` deleted the bare `skills/dev-done/` directory, that\n"
|
|
106
|
+
"> copy did not carry the post-sync commit step below — this canonical file was already ahead\n"
|
|
107
|
+
"> of it. That divergence is now moot; there is no bare form left to diverge from."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def process_generic_file(path, dry_run=False):
|
|
112
|
+
with open(path, encoding="utf-8") as f:
|
|
113
|
+
content = f.read()
|
|
114
|
+
if TRIGGER not in content:
|
|
115
|
+
return None
|
|
116
|
+
match = STALE_PREAMBLE_RE.search(content)
|
|
117
|
+
if not match:
|
|
118
|
+
return "unmatched"
|
|
119
|
+
name = match.group("name")
|
|
120
|
+
new_content = content[: match.start()] + canonical_replacement(name) + content[match.end() :]
|
|
121
|
+
if not dry_run:
|
|
122
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
123
|
+
f.write(new_content)
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def process_special_file(path, old_new_pairs, dry_run=False):
|
|
128
|
+
with open(path, encoding="utf-8") as f:
|
|
129
|
+
content = f.read()
|
|
130
|
+
changed = False
|
|
131
|
+
for old, new in old_new_pairs:
|
|
132
|
+
if old not in content:
|
|
133
|
+
print(f"ERROR: expected text not found verbatim in {path}:", file=sys.stderr)
|
|
134
|
+
print(repr(old), file=sys.stderr)
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
content = content.replace(old, new, 1)
|
|
137
|
+
changed = True
|
|
138
|
+
if changed and not dry_run:
|
|
139
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
140
|
+
f.write(content)
|
|
141
|
+
return changed
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def main():
|
|
145
|
+
dry_run = "--dry-run" in sys.argv
|
|
146
|
+
files = sorted(glob.glob("skills/j-*/SKILL.md"))
|
|
147
|
+
changed_files = []
|
|
148
|
+
unmatched = []
|
|
149
|
+
|
|
150
|
+
for path in files:
|
|
151
|
+
if path in ("skills/j-do/SKILL.md", "skills/j-dev-done/SKILL.md"):
|
|
152
|
+
continue
|
|
153
|
+
result = process_generic_file(path, dry_run=dry_run)
|
|
154
|
+
if result == "unmatched":
|
|
155
|
+
unmatched.append(path)
|
|
156
|
+
elif result:
|
|
157
|
+
changed_files.append(path)
|
|
158
|
+
|
|
159
|
+
if process_special_file("skills/j-do/SKILL.md", [(J_DO_OLD, J_DO_NEW)], dry_run=dry_run):
|
|
160
|
+
changed_files.append("skills/j-do/SKILL.md")
|
|
161
|
+
|
|
162
|
+
if process_special_file(
|
|
163
|
+
"skills/j-dev-done/SKILL.md",
|
|
164
|
+
[
|
|
165
|
+
(J_DEV_DONE_OLD_WARNING, J_DEV_DONE_NEW_WARNING),
|
|
166
|
+
(J_DEV_DONE_OLD_DIVERGENCE, J_DEV_DONE_NEW_DIVERGENCE),
|
|
167
|
+
],
|
|
168
|
+
dry_run=dry_run,
|
|
169
|
+
):
|
|
170
|
+
changed_files.append("skills/j-dev-done/SKILL.md")
|
|
171
|
+
|
|
172
|
+
print(f"Changed {len(changed_files)} file(s):")
|
|
173
|
+
for p in sorted(changed_files):
|
|
174
|
+
print(f" {p}")
|
|
175
|
+
|
|
176
|
+
if unmatched:
|
|
177
|
+
print(
|
|
178
|
+
f"\nERROR: {len(unmatched)} file(s) contain the trigger phrase but did not match the "
|
|
179
|
+
"expected preamble pattern — investigate manually:",
|
|
180
|
+
file=sys.stderr,
|
|
181
|
+
)
|
|
182
|
+
for p in unmatched:
|
|
183
|
+
print(f" {p}", file=sys.stderr)
|
|
184
|
+
sys.exit(1)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
if __name__ == "__main__":
|
|
188
|
+
main()
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
E50_S22_T01 — Strip retired-era polyfill boilerplate from j-* skill frontmatter.
|
|
4
|
+
|
|
5
|
+
For every skills/j-*/SKILL.md file (excluding the 6 natively-authored skills, which
|
|
6
|
+
never carried this boilerplate), this script:
|
|
7
|
+
|
|
8
|
+
1. Strips the leading prefix "Polyfill alias of the <name> skill under a
|
|
9
|
+
collision-safe directory name. Identical behavior to /<name> — " from the
|
|
10
|
+
frontmatter `description:` field, preserving everything after it verbatim.
|
|
11
|
+
2. Strips the trailing generic clause "Use when the bare /<name> form is shadowed
|
|
12
|
+
by another tool's own built-in command of the same name." if present verbatim.
|
|
13
|
+
(skills/j-init/SKILL.md carries a different, substantive custom clause about a
|
|
14
|
+
real GitHub Copilot collision — that one is intentionally left untouched.)
|
|
15
|
+
3. Special-cases skills/j-idea/SKILL.md: also strips the leftover E50_S21
|
|
16
|
+
"DEPRECATED - use the prefixed 'j-idea' skill instead. " clause.
|
|
17
|
+
4. Removes any " - polyfill" line from the frontmatter `keywords:` block.
|
|
18
|
+
|
|
19
|
+
Idempotent and safe to re-run. Reports every file it changes and fails loudly
|
|
20
|
+
(non-zero exit) if a file matching the target set doesn't fit the expected pattern,
|
|
21
|
+
rather than silently skipping it.
|
|
22
|
+
|
|
23
|
+
Usage: scripts/strip-polyfill-frontmatter.py [--dry-run]
|
|
24
|
+
Run from the repository root.
|
|
25
|
+
"""
|
|
26
|
+
import glob
|
|
27
|
+
import json
|
|
28
|
+
import re
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
import yaml
|
|
33
|
+
except ImportError:
|
|
34
|
+
yaml = None
|
|
35
|
+
|
|
36
|
+
NATIVE_SKILLS = {
|
|
37
|
+
"j-cloud-connect",
|
|
38
|
+
"j-dashboard",
|
|
39
|
+
"j-dashboard-share",
|
|
40
|
+
"j-gitignore",
|
|
41
|
+
"j-playbook",
|
|
42
|
+
"j-playbook-new",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
PREFIX_RE = re.compile(
|
|
46
|
+
r"^description: Polyfill alias of the .+? skill under a collision-safe directory name\. "
|
|
47
|
+
r"Identical behavior to /\S+ — "
|
|
48
|
+
)
|
|
49
|
+
GENERIC_SUFFIX_RE = re.compile(
|
|
50
|
+
r" Use when the bare /\S+ form is shadowed by another tool's own built-in command of the same name\.$"
|
|
51
|
+
)
|
|
52
|
+
IDEA_DEPRECATED_RE = re.compile(
|
|
53
|
+
r"^description: DEPRECATED - use the prefixed 'j-idea' skill instead\. "
|
|
54
|
+
)
|
|
55
|
+
# Matches the preceding newline too, so removal doesn't leave a blank line behind
|
|
56
|
+
# (whether "polyfill" sits mid-list or is the last entry right before the closing "---").
|
|
57
|
+
POLYFILL_KEYWORD_RE = re.compile(r"\n[ \t]*-[ \t]*polyfill[ \t]*(?=\n)")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def requote_if_unsafe(line):
|
|
61
|
+
"""
|
|
62
|
+
Stripping the polyfill prefix can leave a plain-scalar description value that
|
|
63
|
+
starts with a YAML-special character (e.g. j-deep-dive's substantive text is
|
|
64
|
+
the literal ">.", which YAML reads as an invalid block-scalar indicator once it
|
|
65
|
+
is no longer preceded by other text). If the resulting `description: <value>`
|
|
66
|
+
line doesn't parse as valid YAML, re-render it as a double-quoted (JSON-style,
|
|
67
|
+
which is also valid YAML) scalar instead, preserving the value verbatim.
|
|
68
|
+
"""
|
|
69
|
+
if yaml is None or not line.startswith("description: "):
|
|
70
|
+
return line
|
|
71
|
+
value = line[len("description: ") :]
|
|
72
|
+
try:
|
|
73
|
+
yaml.safe_load(line)
|
|
74
|
+
return line
|
|
75
|
+
except yaml.YAMLError:
|
|
76
|
+
return "description: " + json.dumps(value)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def process_file(path, dry_run=False):
|
|
80
|
+
with open(path, encoding="utf-8") as f:
|
|
81
|
+
content = f.read()
|
|
82
|
+
|
|
83
|
+
if not content.startswith("---\n"):
|
|
84
|
+
return None # no frontmatter, nothing to do
|
|
85
|
+
end = content.find("\n---", 4)
|
|
86
|
+
if end == -1:
|
|
87
|
+
return None
|
|
88
|
+
frontmatter = content[: end + 4]
|
|
89
|
+
rest = content[end + 4 :]
|
|
90
|
+
|
|
91
|
+
lines = frontmatter.split("\n")
|
|
92
|
+
changed = False
|
|
93
|
+
desc_found = False
|
|
94
|
+
|
|
95
|
+
for i, line in enumerate(lines):
|
|
96
|
+
if line.startswith("description:"):
|
|
97
|
+
desc_found = True
|
|
98
|
+
original = line
|
|
99
|
+
new_line = PREFIX_RE.sub("description: ", line)
|
|
100
|
+
if new_line == line:
|
|
101
|
+
# Not the polyfill-prefixed shape — leave untouched (native skills).
|
|
102
|
+
continue
|
|
103
|
+
new_line = GENERIC_SUFFIX_RE.sub("", new_line)
|
|
104
|
+
if "/j-idea/" in path or path.endswith("j-idea/SKILL.md"):
|
|
105
|
+
new_line = IDEA_DEPRECATED_RE.sub("description: ", new_line)
|
|
106
|
+
new_line = requote_if_unsafe(new_line)
|
|
107
|
+
if new_line != original:
|
|
108
|
+
lines[i] = new_line
|
|
109
|
+
changed = True
|
|
110
|
+
|
|
111
|
+
new_frontmatter = "\n".join(lines)
|
|
112
|
+
|
|
113
|
+
# Strip " - polyfill" keyword lines (including their preceding newline, so no
|
|
114
|
+
# blank line is left behind regardless of position in the list).
|
|
115
|
+
if POLYFILL_KEYWORD_RE.search(new_frontmatter):
|
|
116
|
+
new_frontmatter = POLYFILL_KEYWORD_RE.sub("", new_frontmatter)
|
|
117
|
+
changed = True
|
|
118
|
+
|
|
119
|
+
if not changed:
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
new_content = new_frontmatter + rest
|
|
123
|
+
if not dry_run:
|
|
124
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
125
|
+
f.write(new_content)
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main():
|
|
130
|
+
dry_run = "--dry-run" in sys.argv
|
|
131
|
+
files = sorted(glob.glob("skills/j-*/SKILL.md"))
|
|
132
|
+
changed_files = []
|
|
133
|
+
unmatched_polyfill_prefix = []
|
|
134
|
+
|
|
135
|
+
for path in files:
|
|
136
|
+
skill_dir = path.split("/")[1]
|
|
137
|
+
if skill_dir in NATIVE_SKILLS:
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
with open(path, encoding="utf-8") as f:
|
|
141
|
+
head = f.read(4000)
|
|
142
|
+
has_prefix = "Polyfill alias of the" in head
|
|
143
|
+
|
|
144
|
+
result = process_file(path, dry_run=dry_run)
|
|
145
|
+
if has_prefix and result is False:
|
|
146
|
+
unmatched_polyfill_prefix.append(path)
|
|
147
|
+
elif result:
|
|
148
|
+
changed_files.append(path)
|
|
149
|
+
|
|
150
|
+
print(f"Changed {len(changed_files)} file(s):")
|
|
151
|
+
for p in changed_files:
|
|
152
|
+
print(f" {p}")
|
|
153
|
+
|
|
154
|
+
if unmatched_polyfill_prefix:
|
|
155
|
+
print(
|
|
156
|
+
f"\nERROR: {len(unmatched_polyfill_prefix)} file(s) contain 'Polyfill alias of the' "
|
|
157
|
+
"but did not match the expected description pattern — investigate manually:",
|
|
158
|
+
file=sys.stderr,
|
|
159
|
+
)
|
|
160
|
+
for p in unmatched_polyfill_prefix:
|
|
161
|
+
print(f" {p}", file=sys.stderr)
|
|
162
|
+
sys.exit(1)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
if __name__ == "__main__":
|
|
166
|
+
main()
|
package/scripts/todo_manager.sh
CHANGED
|
@@ -3,7 +3,22 @@
|
|
|
3
3
|
# Run from the repository root.
|
|
4
4
|
|
|
5
5
|
TODO_FILE="project/todo.md"
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
# PACKAGE_ROOT locates the package-shipped todo template (skills/j-todo/assets/).
|
|
8
|
+
# This script always lives at scripts/, one level below the package root, in
|
|
9
|
+
# both layouts:
|
|
10
|
+
# - this monorepo checkout
|
|
11
|
+
# - a consumer install, inside node_modules/@jenga-ai/agent/
|
|
12
|
+
# BASH_SOURCE-derived resolution is exact in both cases (same reasoning as
|
|
13
|
+
# scripts/jenga-permission-level-switch.sh's PACKAGE_ROOT derivation), with a
|
|
14
|
+
# defensive node_modules-relative fallback matching the PKG_ROOT convention in
|
|
15
|
+
# skills/init/scripts/init.sh, in case BASH_SOURCE resolution is ever
|
|
16
|
+
# unavailable (e.g. the script is sourced rather than executed).
|
|
17
|
+
PACKAGE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
18
|
+
TEMPLATE="$PACKAGE_ROOT/skills/j-todo/assets/todo_template.md"
|
|
19
|
+
if [ ! -f "$TEMPLATE" ]; then
|
|
20
|
+
TEMPLATE="node_modules/@jenga-ai/agent/skills/j-todo/assets/todo_template.md"
|
|
21
|
+
fi
|
|
7
22
|
|
|
8
23
|
usage() {
|
|
9
24
|
cat >&2 <<EOF
|