create-zudo-doc 4.4.13 → 4.5.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/dist/scaffold.d.ts +15 -1
- package/dist/scaffold.js +41 -13
- package/package.json +1 -1
- package/templates/base/scripts/setup-doc-skill.sh +194 -5
package/dist/scaffold.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import type { UserChoices } from "./prompts.js";
|
|
2
2
|
import { getSecondaryLang } from "./utils.js";
|
|
3
3
|
export { getSecondaryLang };
|
|
4
|
+
/**
|
|
5
|
+
* TypeScript mirror of `DEFAULT_SKILL_NAME` in `scripts/setup-doc-skill.sh`
|
|
6
|
+
* (`DEFAULT_SKILL_NAME="${PROJECT_NAME}-wisdom"`, suffix-aware since #3154).
|
|
7
|
+
* The generator cannot import the shell script, so this rule is duplicated
|
|
8
|
+
* here; the two are kept from drifting apart by the committed cross-artifact
|
|
9
|
+
* parity test (#3158).
|
|
10
|
+
*
|
|
11
|
+
* If `projectName` already ends in `-wisdom` (or is exactly `wisdom`), it is
|
|
12
|
+
* used verbatim — appending `-wisdom` again would double the suffix (e.g.
|
|
13
|
+
* `zudo-test-wisdom` → `zudo-test-wisdom-wisdom`), which matches no
|
|
14
|
+
* `.gitignore` entry and leaves the generated skill directory untracked.
|
|
15
|
+
* Otherwise `-wisdom` is appended.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deriveDocSkillName(projectName: string): string;
|
|
4
18
|
/**
|
|
5
19
|
* Pinned `@takazudo/zudo-doc` version used by `generatePackageJson()`.
|
|
6
20
|
* Hoisted as a shared constant (kept even though it now has a single
|
|
@@ -18,5 +32,5 @@ export { getSecondaryLang };
|
|
|
18
32
|
*
|
|
19
33
|
* Bumped in lockstep by scripts/release-create-zudo-doc.sh.
|
|
20
34
|
*/
|
|
21
|
-
export declare const ZUDO_DOC_PIN = "^4.
|
|
35
|
+
export declare const ZUDO_DOC_PIN = "^4.5.0";
|
|
22
36
|
export declare function scaffold(choices: UserChoices): Promise<void>;
|
package/dist/scaffold.js
CHANGED
|
@@ -7,6 +7,25 @@ import { composeFeatures } from "./compose.js";
|
|
|
7
7
|
import { featureModules } from "./features/index.js";
|
|
8
8
|
import { capitalize, getSecondaryLang, hasAncestorPnpmWorkspace, pmRunCommand, } from "./utils.js";
|
|
9
9
|
export { getSecondaryLang };
|
|
10
|
+
/**
|
|
11
|
+
* TypeScript mirror of `DEFAULT_SKILL_NAME` in `scripts/setup-doc-skill.sh`
|
|
12
|
+
* (`DEFAULT_SKILL_NAME="${PROJECT_NAME}-wisdom"`, suffix-aware since #3154).
|
|
13
|
+
* The generator cannot import the shell script, so this rule is duplicated
|
|
14
|
+
* here; the two are kept from drifting apart by the committed cross-artifact
|
|
15
|
+
* parity test (#3158).
|
|
16
|
+
*
|
|
17
|
+
* If `projectName` already ends in `-wisdom` (or is exactly `wisdom`), it is
|
|
18
|
+
* used verbatim — appending `-wisdom` again would double the suffix (e.g.
|
|
19
|
+
* `zudo-test-wisdom` → `zudo-test-wisdom-wisdom`), which matches no
|
|
20
|
+
* `.gitignore` entry and leaves the generated skill directory untracked.
|
|
21
|
+
* Otherwise `-wisdom` is appended.
|
|
22
|
+
*/
|
|
23
|
+
export function deriveDocSkillName(projectName) {
|
|
24
|
+
if (projectName === "wisdom" || projectName.endsWith("-wisdom")) {
|
|
25
|
+
return projectName;
|
|
26
|
+
}
|
|
27
|
+
return `${projectName}-wisdom`;
|
|
28
|
+
}
|
|
10
29
|
/**
|
|
11
30
|
* Pinned `@takazudo/zudo-doc` version used by `generatePackageJson()`.
|
|
12
31
|
* Hoisted as a shared constant (kept even though it now has a single
|
|
@@ -24,7 +43,7 @@ export { getSecondaryLang };
|
|
|
24
43
|
*
|
|
25
44
|
* Bumped in lockstep by scripts/release-create-zudo-doc.sh.
|
|
26
45
|
*/
|
|
27
|
-
export const ZUDO_DOC_PIN = "^4.
|
|
46
|
+
export const ZUDO_DOC_PIN = "^4.5.0";
|
|
28
47
|
/**
|
|
29
48
|
* Files in `templates/base/**` that must not be copied by the unconditional
|
|
30
49
|
* base mirror. Each entry is matched against the path relative to
|
|
@@ -343,16 +362,17 @@ export async function scaffold(choices) {
|
|
|
343
362
|
// on the same feature above), so only emit its ignore entries then —
|
|
344
363
|
// otherwise they would be dead rules that could silently hide an unrelated
|
|
345
364
|
// skill a user later installs under a matching name. The skill name is
|
|
346
|
-
// deterministic (
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
365
|
+
// deterministic (deriveDocSkillName(), matching DEFAULT_SKILL_NAME in
|
|
366
|
+
// scripts/setup-doc-skill.sh), so these entries match the directory the
|
|
367
|
+
// script creates. The setup script can target either .claude or .codex, so
|
|
368
|
+
// ignore both possible generated directories. The docs-ja symlink only
|
|
369
|
+
// exists for i18n projects (the script creates it conditionally), so gate
|
|
370
|
+
// those lines on i18n.
|
|
352
371
|
if (choices.features.includes("skillSymlinker")) {
|
|
353
|
-
|
|
372
|
+
const skillName = deriveDocSkillName(choices.projectName);
|
|
373
|
+
gitignoreLines.push("# Generated doc-lookup skill", `.claude/skills/${skillName}/SKILL.md`, `.claude/skills/${skillName}/docs`, `.codex/skills/${skillName}/SKILL.md`, `.codex/skills/${skillName}/docs`);
|
|
354
374
|
if (choices.features.includes("i18n")) {
|
|
355
|
-
gitignoreLines.push(`.claude/skills/${
|
|
375
|
+
gitignoreLines.push(`.claude/skills/${skillName}/docs-ja`, `.codex/skills/${skillName}/docs-ja`);
|
|
356
376
|
}
|
|
357
377
|
gitignoreLines.push("");
|
|
358
378
|
}
|
|
@@ -550,9 +570,12 @@ function generatePackageJson(choices) {
|
|
|
550
570
|
// singleton across host-callable wiring (zfb#1652/#1653). The zfb family
|
|
551
571
|
// must stay in lockstep because the WASM browser entry depends on its
|
|
552
572
|
// resource-aware island pipeline.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
573
|
+
// 1.1.0: container-directive fixes (multi-block bodies, unclosed-opener
|
|
574
|
+
// diagnostics) plus highlighting for fences nested in MDX JSX bodies and
|
|
575
|
+
// directives. Content-rendering fixes, additive.
|
|
576
|
+
"@takazudo/zfb": "1.1.0",
|
|
577
|
+
"@takazudo/zfb-runtime": "1.1.0",
|
|
578
|
+
"@takazudo/zfb-md-wasm": "1.1.0",
|
|
556
579
|
// @takazudo/zudo-doc — published from this monorepo via
|
|
557
580
|
// .github/workflows/publish-zudo-doc.yml. The pin here is bumped in
|
|
558
581
|
// lockstep by scripts/release-create-zudo-doc.sh whenever zudo-doc's
|
|
@@ -672,7 +695,7 @@ function generatePackageJson(choices) {
|
|
|
672
695
|
// `/exclude` at module scope from the always-bundled chrome graph; #3110
|
|
673
696
|
// moved compileExclude into @takazudo/zudo-doc, so docHistory-OFF projects
|
|
674
697
|
// no longer need the package at all.
|
|
675
|
-
deps["@takazudo/zudo-doc-history-server"] = "^4.
|
|
698
|
+
deps["@takazudo/zudo-doc-history-server"] = "^4.5.0";
|
|
676
699
|
// tsx is no longer needed here: the relocated package plugin imports the
|
|
677
700
|
// runner directly (no `tsx -e` spawn) since the package ships compiled
|
|
678
701
|
// dist/ — package-first migration #2321 (#2337).
|
|
@@ -741,6 +764,11 @@ function generatePackageJson(choices) {
|
|
|
741
764
|
"tags-suggest --config src/config/tag-vocabulary.ts";
|
|
742
765
|
}
|
|
743
766
|
if (choices.features.includes("skillSymlinker")) {
|
|
767
|
+
// Deliberately omit --no-link-tracked-skills here: automatic tracked-skill
|
|
768
|
+
// linking (#3152) is the intended default for generated projects. Only the
|
|
769
|
+
// zudo-doc monorepo's own root package.json passes the opt-out, because it
|
|
770
|
+
// has 20 directories under `.claude/skills/` and a blanket loop would export
|
|
771
|
+
// 19 project-specific skills into the maintainer's global skills dir (#3157).
|
|
744
772
|
scripts["setup:doc-skill"] = "bash scripts/setup-doc-skill.sh";
|
|
745
773
|
scripts["setup:doc-skill-silent"] =
|
|
746
774
|
"bash scripts/setup-doc-skill.sh --silent";
|
package/package.json
CHANGED
|
@@ -10,6 +10,13 @@ set -euo pipefail
|
|
|
10
10
|
|
|
11
11
|
TARGET_MODE="auto"
|
|
12
12
|
|
|
13
|
+
# Tracked-skill linking (zudolab/zudo-doc#3156) is default-ON: #3152 asked for
|
|
14
|
+
# automatic linking of a site's own hand-written skills, not just the
|
|
15
|
+
# generated one. --no-link-tracked-skills opts out (e.g. this monorepo's own
|
|
16
|
+
# npm scripts, which would otherwise export ~20 project-specific skills into
|
|
17
|
+
# the user's global skills dir -- see zudolab/zudo-doc#3157).
|
|
18
|
+
LINK_TRACKED_SKILLS="true"
|
|
19
|
+
|
|
13
20
|
# Accept --silent (alias -y) for parity with the consuming-site convention:
|
|
14
21
|
# scaffolded sites expose `setup:doc-skill-silent` = `bash scripts/setup-doc-skill.sh
|
|
15
22
|
# --silent`. This script is already non-interactive (the skill name is deterministic
|
|
@@ -31,6 +38,7 @@ while [ $# -gt 0 ]; do
|
|
|
31
38
|
TARGET_MODE="${1#--target=}"
|
|
32
39
|
shift
|
|
33
40
|
;;
|
|
41
|
+
--no-link-tracked-skills) LINK_TRACKED_SKILLS="false"; shift ;;
|
|
34
42
|
--) shift; break ;;
|
|
35
43
|
-*) echo "Error: unknown flag '$1'" >&2; exit 1 ;;
|
|
36
44
|
*) break ;;
|
|
@@ -49,16 +57,29 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
49
57
|
|
|
50
58
|
# Read project name from package.json
|
|
51
59
|
PROJECT_NAME=$(node -e "console.log(require('$ROOT_DIR/package.json').name || 'my-project')")
|
|
52
|
-
|
|
60
|
+
|
|
61
|
+
# Pre-#3154 default: always `<projectName>-wisdom`, which doubles the suffix
|
|
62
|
+
# when PROJECT_NAME already ends in "-wisdom" (e.g. "zudo-test-wisdom" ->
|
|
63
|
+
# "zudo-test-wisdom-wisdom"). Kept around only to detect leftover legacy
|
|
64
|
+
# directories from that behavior -- see the migration-warning block below.
|
|
65
|
+
LEGACY_DEFAULT_SKILL_NAME="${PROJECT_NAME}-wisdom"
|
|
66
|
+
if [[ "$PROJECT_NAME" == *-wisdom ]] || [[ "$PROJECT_NAME" == "wisdom" ]]; then
|
|
67
|
+
DEFAULT_SKILL_NAME="$PROJECT_NAME"
|
|
68
|
+
else
|
|
69
|
+
DEFAULT_SKILL_NAME="$LEGACY_DEFAULT_SKILL_NAME"
|
|
70
|
+
fi
|
|
53
71
|
|
|
54
72
|
echo ""
|
|
55
73
|
echo "=== zudo-doc Skill Setup ==="
|
|
56
74
|
echo ""
|
|
57
75
|
|
|
58
|
-
# Skill name is DETERMINISTIC:
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
76
|
+
# Skill name is DETERMINISTIC and suffix-aware: verbatim when PROJECT_NAME
|
|
77
|
+
# already ends in "-wisdom" (or is exactly "wisdom"), otherwise
|
|
78
|
+
# "<projectName>-wisdom" (zudolab/zudo-doc#3154). The scaffolded .gitignore
|
|
79
|
+
# (emitted by create-zudo-doc's packages/create-zudo-doc/src/scaffold.ts,
|
|
80
|
+
# which computes the same name) hard-codes this exact rule, so the generated
|
|
81
|
+
# skill directory must match it — an interactive prompt would let the name
|
|
82
|
+
# drift from the gitignore entry and leave the skill showing as untracked
|
|
62
83
|
# (zudolab/zudo-doc#2173). An explicit override is still allowed via the first
|
|
63
84
|
# CLI arg or the SKILL_NAME env var (consumers who override must also update
|
|
64
85
|
# their .gitignore), but never via an interactive prompt.
|
|
@@ -70,6 +91,92 @@ if [[ ! "$SKILL_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
|
70
91
|
exit 1
|
|
71
92
|
fi
|
|
72
93
|
|
|
94
|
+
# Migration warning (zudolab/zudo-doc#3154, D3): the suffix-aware rule above is
|
|
95
|
+
# a BREAKING change for existing "*-wisdom" projects. Their .gitignore pins the
|
|
96
|
+
# old, doubled name, so after picking up this fix they get a NEW, unignored
|
|
97
|
+
# skill directory while the stale doubled one remains on disk -- the symptom
|
|
98
|
+
# moves rather than disappears. Detect that and tell the user what to do by
|
|
99
|
+
# hand; never delete anything automatically, it is the user's tree.
|
|
100
|
+
# Three independent symptoms are checked, and ANY one of them fires the
|
|
101
|
+
# warning: stale ignore rules (the .gitignore still names the doubled skill --
|
|
102
|
+
# on its own that already means the NEW directory is unignored, which is the
|
|
103
|
+
# whole problem, even when the legacy directory was never generated or has
|
|
104
|
+
# already been deleted), a leftover legacy directory, and a leftover global
|
|
105
|
+
# symlink. Only the steps for the symptoms actually found are printed.
|
|
106
|
+
# Only fires when SKILL_NAME still equals DEFAULT_SKILL_NAME -- an explicit
|
|
107
|
+
# $1/SKILL_NAME override means the derived name (and this guidance about it)
|
|
108
|
+
# doesn't apply to what the script is actually about to create.
|
|
109
|
+
if [ "$SKILL_NAME" = "$DEFAULT_SKILL_NAME" ] && [ "$DEFAULT_SKILL_NAME" != "$LEGACY_DEFAULT_SKILL_NAME" ]; then
|
|
110
|
+
# The ignore rules are repo-level, so this is checked once rather than per
|
|
111
|
+
# target -- both ".claude/skills/<name>/" and ".codex/skills/<name>/" entries
|
|
112
|
+
# live in the same file (create-zudo-doc emits it at the project root).
|
|
113
|
+
# -F: PROJECT_NAME comes from package.json and may contain regex characters.
|
|
114
|
+
legacy_gitignore=""
|
|
115
|
+
if [ -f "$ROOT_DIR/.gitignore" ] &&
|
|
116
|
+
grep -qF "skills/$LEGACY_DEFAULT_SKILL_NAME/" "$ROOT_DIR/.gitignore"; then
|
|
117
|
+
legacy_gitignore="$ROOT_DIR/.gitignore"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
legacy_dirs=()
|
|
121
|
+
legacy_links=()
|
|
122
|
+
for target in claude codex; do
|
|
123
|
+
legacy_dir="$ROOT_DIR/.$target/skills/$LEGACY_DEFAULT_SKILL_NAME"
|
|
124
|
+
if [ -d "$legacy_dir" ]; then
|
|
125
|
+
legacy_dirs+=("$legacy_dir")
|
|
126
|
+
fi
|
|
127
|
+
# -L only (which also catches a dangling link): this script only ever
|
|
128
|
+
# creates SYMLINKS in the global skills dir, so a real file or directory
|
|
129
|
+
# sitting at that path is user-owned and none of our business -- calling it
|
|
130
|
+
# a "stale global symlink" and suggesting `rm -f` would advise deleting
|
|
131
|
+
# someone else's file (and would simply fail for a directory).
|
|
132
|
+
legacy_global_link="$HOME/.$target/skills/$LEGACY_DEFAULT_SKILL_NAME"
|
|
133
|
+
if [ -L "$legacy_global_link" ]; then
|
|
134
|
+
legacy_links+=("$legacy_global_link")
|
|
135
|
+
fi
|
|
136
|
+
done
|
|
137
|
+
|
|
138
|
+
if [ -n "$legacy_gitignore" ] ||
|
|
139
|
+
[ "${#legacy_dirs[@]}" -gt 0 ] ||
|
|
140
|
+
[ "${#legacy_links[@]}" -gt 0 ]; then
|
|
141
|
+
echo "WARNING: stale references to the legacy skill name '$LEGACY_DEFAULT_SKILL_NAME' detected."
|
|
142
|
+
echo " The skill-name derivation rule changed; this project's skill now resolves to:"
|
|
143
|
+
echo " $DEFAULT_SKILL_NAME"
|
|
144
|
+
echo " Found:"
|
|
145
|
+
if [ -n "$legacy_gitignore" ]; then
|
|
146
|
+
echo " - stale ignore rules in $legacy_gitignore"
|
|
147
|
+
fi
|
|
148
|
+
if [ "${#legacy_dirs[@]}" -gt 0 ]; then
|
|
149
|
+
for legacy_dir in "${legacy_dirs[@]}"; do
|
|
150
|
+
echo " - legacy skill directory: $legacy_dir"
|
|
151
|
+
done
|
|
152
|
+
fi
|
|
153
|
+
if [ "${#legacy_links[@]}" -gt 0 ]; then
|
|
154
|
+
for legacy_global_link in "${legacy_links[@]}"; do
|
|
155
|
+
echo " - stale global symlink: $legacy_global_link"
|
|
156
|
+
done
|
|
157
|
+
fi
|
|
158
|
+
echo " Manual steps:"
|
|
159
|
+
step=1
|
|
160
|
+
if [ -n "$legacy_gitignore" ]; then
|
|
161
|
+
echo " $step. Update the ignore rules for '$LEGACY_DEFAULT_SKILL_NAME' to '$DEFAULT_SKILL_NAME' in $legacy_gitignore."
|
|
162
|
+
step=$((step + 1))
|
|
163
|
+
fi
|
|
164
|
+
if [ "${#legacy_dirs[@]}" -gt 0 ]; then
|
|
165
|
+
for legacy_dir in "${legacy_dirs[@]}"; do
|
|
166
|
+
echo " $step. Delete the stale directory once migrated: rm -rf \"$legacy_dir\""
|
|
167
|
+
step=$((step + 1))
|
|
168
|
+
done
|
|
169
|
+
fi
|
|
170
|
+
if [ "${#legacy_links[@]}" -gt 0 ]; then
|
|
171
|
+
for legacy_global_link in "${legacy_links[@]}"; do
|
|
172
|
+
echo " $step. Remove the stale global symlink: rm -f \"$legacy_global_link\""
|
|
173
|
+
step=$((step + 1))
|
|
174
|
+
done
|
|
175
|
+
fi
|
|
176
|
+
echo ""
|
|
177
|
+
fi
|
|
178
|
+
fi
|
|
179
|
+
|
|
73
180
|
# Resolve the main repo root (handles git worktrees correctly)
|
|
74
181
|
# Use the main worktree path so symlinks survive worktree removal
|
|
75
182
|
REPO_ROOT="$(git -C "$ROOT_DIR" worktree list | head -1 | awk '{print $1}')"
|
|
@@ -104,6 +211,46 @@ ensure_symlink() {
|
|
|
104
211
|
ln -s "$target" "$link_path"
|
|
105
212
|
}
|
|
106
213
|
|
|
214
|
+
# Helper: link a single tracked (hand-written) skill into the global skills
|
|
215
|
+
# dir WITHOUT ever deleting something this script doesn't own
|
|
216
|
+
# (zudolab/zudo-doc#3156, D4). Unlike ensure_symlink (rm -rf-based -- safe
|
|
217
|
+
# only for the generated skill, whose global name this project owns),
|
|
218
|
+
# tracked-skill names are arbitrary and could collide with a user-owned
|
|
219
|
+
# global skill or a name already claimed by another project, so an existing
|
|
220
|
+
# entry that isn't already our own correct link is left untouched, with a
|
|
221
|
+
# warning.
|
|
222
|
+
safe_link_tracked_skill() {
|
|
223
|
+
local link_path="$1"
|
|
224
|
+
local link_target="$2"
|
|
225
|
+
local skill_name="$3"
|
|
226
|
+
local target="$4"
|
|
227
|
+
|
|
228
|
+
if [ -L "$link_path" ]; then
|
|
229
|
+
local current_target
|
|
230
|
+
current_target="$(readlink "$link_path")"
|
|
231
|
+
if [ "$current_target" = "$link_target" ]; then
|
|
232
|
+
return 0 # already correct -> no-op
|
|
233
|
+
fi
|
|
234
|
+
if [ -e "$link_path" ]; then
|
|
235
|
+
echo "WARNING: [$target] skipping tracked skill '$skill_name': $link_path already links to $current_target"
|
|
236
|
+
return 0
|
|
237
|
+
fi
|
|
238
|
+
# Dangling symlink (broken target) -> safe to replace.
|
|
239
|
+
rm -f "$link_path"
|
|
240
|
+
ln -s "$link_target" "$link_path"
|
|
241
|
+
echo " [$target] Linked tracked skill '$skill_name' -> $link_target"
|
|
242
|
+
return 0
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if [ -e "$link_path" ]; then
|
|
246
|
+
echo "WARNING: [$target] skipping tracked skill '$skill_name': $link_path is a real file/directory, not a symlink"
|
|
247
|
+
return 0
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
ln -s "$link_target" "$link_path"
|
|
251
|
+
echo " [$target] Linked tracked skill '$skill_name' -> $link_target"
|
|
252
|
+
}
|
|
253
|
+
|
|
107
254
|
DOCS_JA_DIR="$ROOT_DIR/src/content/docs-ja"
|
|
108
255
|
HAS_JA=""
|
|
109
256
|
if [ -d "$DOCS_JA_DIR" ]; then
|
|
@@ -283,18 +430,60 @@ JAEOF
|
|
|
283
430
|
echo " [$target] Global symlink: $global_skills_dir/$SKILL_NAME"
|
|
284
431
|
}
|
|
285
432
|
|
|
433
|
+
# Symlink the site's OWN hand-written skills into the global skills dir
|
|
434
|
+
# (zudolab/zudo-doc#3156). Discovery walks the target's own project skills
|
|
435
|
+
# directory dynamically -- not a hard-coded list, so it stays correct as
|
|
436
|
+
# sites add skills. Sources resolve through MAIN_PROJECT_DIR, not ROOT_DIR,
|
|
437
|
+
# the same way the generated skill's docs/docs-ja symlinks do (see the
|
|
438
|
+
# REPO_ROOT/MAIN_PROJECT_DIR comment above) so the links survive worktree
|
|
439
|
+
# removal. Target-local only: each target walks only its own
|
|
440
|
+
# ".$target/skills/" -- a missing directory is a silent no-op, and there is
|
|
441
|
+
# no ".claude" -> ".codex" fallback.
|
|
442
|
+
link_tracked_skills() {
|
|
443
|
+
local target="$1"
|
|
444
|
+
local skills_root="$MAIN_PROJECT_DIR/.$target/skills"
|
|
445
|
+
local global_skills_dir="$HOME/.$target/skills"
|
|
446
|
+
|
|
447
|
+
[ -d "$skills_root" ] || return 0
|
|
448
|
+
|
|
449
|
+
mkdir -p "$global_skills_dir"
|
|
450
|
+
|
|
451
|
+
local dir name
|
|
452
|
+
for dir in "$skills_root"/*/; do
|
|
453
|
+
[ -d "$dir" ] || continue
|
|
454
|
+
name="$(basename "$dir")"
|
|
455
|
+
|
|
456
|
+
# Skip the just-generated skill (already linked above via ensure_symlink)
|
|
457
|
+
# and the legacy doubled-suffix name (zudolab/zudo-doc#3154) -- otherwise
|
|
458
|
+
# a stale leftover directory left by that rename gets misread as a
|
|
459
|
+
# hand-written skill and exported globally, compounding both bugs.
|
|
460
|
+
[ "$name" = "$SKILL_NAME" ] && continue
|
|
461
|
+
[ "$name" = "$LEGACY_DEFAULT_SKILL_NAME" ] && continue
|
|
462
|
+
|
|
463
|
+
# A candidate qualifies only if it contains SKILL.md -- iterating every
|
|
464
|
+
# directory does not prove it is a skill.
|
|
465
|
+
[ -f "${dir}SKILL.md" ] || continue
|
|
466
|
+
|
|
467
|
+
safe_link_tracked_skill "$global_skills_dir/$name" "${dir%/}" "$name" "$target"
|
|
468
|
+
done
|
|
469
|
+
}
|
|
470
|
+
|
|
286
471
|
read -r -a TARGETS <<< "$(resolve_targets)"
|
|
287
472
|
echo "Target: $TARGET_MODE -> ${TARGETS[*]}"
|
|
288
473
|
echo ""
|
|
289
474
|
|
|
290
475
|
for target in "${TARGETS[@]}"; do
|
|
291
476
|
generate_skill "$target"
|
|
477
|
+
if [ "$LINK_TRACKED_SKILLS" = "true" ]; then
|
|
478
|
+
link_tracked_skills "$target"
|
|
479
|
+
fi
|
|
292
480
|
done
|
|
293
481
|
|
|
294
482
|
echo ""
|
|
295
483
|
echo "Done! Skill '$SKILL_NAME' is ready."
|
|
296
484
|
echo ""
|
|
297
485
|
echo "Use --target claude, --target codex, or --target both to override auto-detection."
|
|
486
|
+
echo "Use --no-link-tracked-skills to skip linking this project's own hand-written skills."
|
|
298
487
|
echo "In Claude Code, use: /$SKILL_NAME <topic>"
|
|
299
488
|
echo "In Codex, mention the skill by name when asking about this documentation."
|
|
300
489
|
echo ""
|