drafted 1.19.49 → 1.19.51
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/install-mcp.sh +71 -25
- package/mcp/server.mjs +4 -1
- package/package.json +1 -1
package/install-mcp.sh
CHANGED
|
@@ -550,47 +550,93 @@ else
|
|
|
550
550
|
PLUGIN_SRC=""
|
|
551
551
|
fi
|
|
552
552
|
|
|
553
|
+
# Skills and commands are LINKED, never copied: a copy is a point-in-time snapshot that only
|
|
554
|
+
# refreshes when someone re-runs this installer, so `npm i -g drafted@latest` would leave the
|
|
555
|
+
# installed skill silently behind the package it came from. A link tracks the upgrade.
|
|
556
|
+
#
|
|
557
|
+
# link_plugin_entry is idempotent and non-destructive: a correct link is left alone, a wrong or
|
|
558
|
+
# dangling one is repointed, and a real file/directory (which the user may have written by hand)
|
|
559
|
+
# is moved aside rather than deleted. Backups go OUTSIDE the skills/commands dirs — a
|
|
560
|
+
# `drafted.backup/` sitting next to `drafted/` would register as a second, stale skill.
|
|
561
|
+
DRAFTED_BACKUP_DIR="$HOME/.claude/.drafted-backups"
|
|
562
|
+
link_plugin_entry() {
|
|
563
|
+
local source="$1" target="$2" source_real backup
|
|
564
|
+
source_real="$(cd "$(dirname "$source")" && pwd)/$(basename "$source")"
|
|
565
|
+
if [ -L "$target" ]; then
|
|
566
|
+
if [ "$(readlink "$target")" = "$source_real" ]; then return 0; fi
|
|
567
|
+
rm -f "$target"
|
|
568
|
+
elif [ -e "$target" ]; then
|
|
569
|
+
# Byte-identical to the source: this is a prior copy-install of ours, not user content, and
|
|
570
|
+
# the link reproduces it losslessly. Anything else is backed up.
|
|
571
|
+
if diff -rq "$source_real" "$target" >/dev/null 2>&1; then
|
|
572
|
+
rm -rf "$target"
|
|
573
|
+
else
|
|
574
|
+
mkdir -p "$DRAFTED_BACKUP_DIR"
|
|
575
|
+
backup="$DRAFTED_BACKUP_DIR/$(basename "$target").$(date +%Y%m%d%H%M%S)"
|
|
576
|
+
mv "$target" "$backup"
|
|
577
|
+
echo -e " ${YELLOW}Existing $(basename "$target") moved aside → $backup${RESET}"
|
|
578
|
+
fi
|
|
579
|
+
fi
|
|
580
|
+
ln -s "$source_real" "$target"
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
# Remove links WE made whose source is gone — an entry dropped from the package, or the package
|
|
584
|
+
# uninstalled entirely. A dangling skill dir is worse than a stale one: it looks installed and
|
|
585
|
+
# loads nothing. Matched on the link's own target path so other installers' links are untouched.
|
|
586
|
+
sweep_plugin_links() {
|
|
587
|
+
local dir="$1" marker="$2" link
|
|
588
|
+
[ -d "$dir" ] || return 0
|
|
589
|
+
while IFS= read -r link; do
|
|
590
|
+
case "$(readlink "$link")" in
|
|
591
|
+
*"$marker"*)
|
|
592
|
+
if [ ! -e "$link" ]; then
|
|
593
|
+
rm -f "$link"
|
|
594
|
+
echo -e " ${YELLOW}Removed stale link: $(basename "$link")${RESET}"
|
|
595
|
+
fi
|
|
596
|
+
;;
|
|
597
|
+
esac
|
|
598
|
+
done < <(find "$dir" -maxdepth 1 -type l 2>/dev/null)
|
|
599
|
+
}
|
|
600
|
+
|
|
553
601
|
# Skills — Claude Code auto-loads these from ~/.claude/skills/<name>/SKILL.md
|
|
602
|
+
SKILLS_DIR="$HOME/.claude/skills"
|
|
603
|
+
sweep_plugin_links "$SKILLS_DIR" "/plugin/skills/"
|
|
554
604
|
if [ -n "$PLUGIN_SRC" ] && [ -d "$PLUGIN_SRC/skills" ]; then
|
|
555
|
-
SKILLS_DIR="$HOME/.claude/skills"
|
|
556
605
|
mkdir -p "$SKILLS_DIR"
|
|
557
606
|
SKILL_COUNT=0
|
|
558
607
|
for d in "$PLUGIN_SRC/skills/"*/; do
|
|
559
608
|
[ -d "$d" ] || continue
|
|
560
609
|
DIRNAME="$(basename "$d")"
|
|
561
|
-
|
|
562
|
-
cp -r "$d" "$SKILLS_DIR/$DIRNAME"
|
|
610
|
+
link_plugin_entry "${d%/}" "$SKILLS_DIR/$DIRNAME"
|
|
563
611
|
SKILL_COUNT=$((SKILL_COUNT + 1))
|
|
564
612
|
done
|
|
565
|
-
ok "
|
|
613
|
+
ok "Linked $SKILL_COUNT skill(s) into $SKILLS_DIR (source: $PLUGIN_SRC/skills)"
|
|
566
614
|
else
|
|
567
615
|
echo -e " ${YELLOW}Skill source not found in package — skipping skills.${RESET}"
|
|
568
616
|
fi
|
|
569
617
|
|
|
570
618
|
# Commands — namespaced under drafted/ so they register as /drafted:<name>,
|
|
571
|
-
# matching the marketplace install and the commands' own cross-references.
|
|
619
|
+
# matching the marketplace install and the commands' own cross-references. Linked per FILE into a
|
|
620
|
+
# real directory (not a linked directory) so discovery is unchanged; the containing dir is no
|
|
621
|
+
# longer rm -rf'd, so anything else a user put there survives.
|
|
622
|
+
CLAUDE_CMD_DIR="$HOME/.claude/commands/drafted"
|
|
623
|
+
CODEX_CMD_DIR="$HOME/.codex/prompts/drafted"
|
|
624
|
+
sweep_plugin_links "$CLAUDE_CMD_DIR" "/plugin/commands/"
|
|
625
|
+
sweep_plugin_links "$CODEX_CMD_DIR" "/plugin/commands/"
|
|
572
626
|
if [ -n "$PLUGIN_SRC" ] && [ -d "$PLUGIN_SRC/commands" ]; then
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
cp "$PLUGIN_SRC/commands/"*.md "$CLAUDE_CMD_DIR/" 2>/dev/null || true
|
|
584
|
-
ok "Installed $CMD_COUNT command(s) to $CLAUDE_CMD_DIR"
|
|
585
|
-
fi
|
|
627
|
+
link_commands_into() {
|
|
628
|
+
local dest="$1" count=0 f
|
|
629
|
+
mkdir -p "$dest"
|
|
630
|
+
for f in "$PLUGIN_SRC/commands/"*.md; do
|
|
631
|
+
[ -f "$f" ] || continue
|
|
632
|
+
link_plugin_entry "$f" "$dest/$(basename "$f")"
|
|
633
|
+
count=$((count + 1))
|
|
634
|
+
done
|
|
635
|
+
ok "Linked $count command(s) into $dest"
|
|
636
|
+
}
|
|
586
637
|
|
|
587
|
-
if [ "$
|
|
588
|
-
|
|
589
|
-
rm -rf "$CODEX_CMD_DIR"
|
|
590
|
-
mkdir -p "$CODEX_CMD_DIR"
|
|
591
|
-
cp "$PLUGIN_SRC/commands/"*.md "$CODEX_CMD_DIR/" 2>/dev/null || true
|
|
592
|
-
ok "Installed $CMD_COUNT command(s) to $CODEX_CMD_DIR"
|
|
593
|
-
fi
|
|
638
|
+
if [ "$CLIENT_CLAUDE_CODE" = true ]; then link_commands_into "$CLAUDE_CMD_DIR"; fi
|
|
639
|
+
if [ "$CLIENT_CODEX" = true ]; then link_commands_into "$CODEX_CMD_DIR"; fi
|
|
594
640
|
else
|
|
595
641
|
echo -e " ${YELLOW}Command source not found in package — skipping commands.${RESET}"
|
|
596
642
|
fi
|
package/mcp/server.mjs
CHANGED
|
@@ -4235,7 +4235,10 @@ tool('fs', 'Navigate Drafted like a local filesystem. A FOLDER is the single con
|
|
|
4235
4235
|
if (!dest || !dest.meta || !dest.meta.id || dest.rest.length < 2) {
|
|
4236
4236
|
return err(new Error('readonly link requires to=/projects/<project>/<layer>/<lane>/<file> or a frame UUID'));
|
|
4237
4237
|
}
|
|
4238
|
-
|
|
4238
|
+
// Scope via header, NOT the query string — api() also appends
|
|
4239
|
+
// projectId for the bound session and two projectId params made
|
|
4240
|
+
// the route resolve both as an array (500).
|
|
4241
|
+
const src = await api('GET', `/api/fs/${dest.rest.join('/')}`, undefined, { ...orgHeader, 'X-Drafted-Project': dest.meta.id });
|
|
4239
4242
|
if (!src?.id) return err(new Error(`Source frame not found: ${t}`));
|
|
4240
4243
|
sourceId = src.id;
|
|
4241
4244
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.51",
|
|
4
4
|
"description": "Drafted — visual thinking surface for humans and AI agents. Renders HTML, markdown, images, and code as frames on a zoomable canvas, with MCP tools for AI agents and real-time sync for humans.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|