vskill 1.0.11 → 1.0.13

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/agents.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "generatedAt": "2026-04-29T05:22:45.463Z",
3
+ "generatedAt": "2026-04-30T05:35:27.559Z",
4
4
  "agentPrefixes": [
5
5
  ".adal",
6
6
  ".agent",
@@ -130,40 +130,8 @@ export declare function parseSkillFrontmatter(content: string): Record<string, s
130
130
  * for `origin="source"` or if no registry entry matches.
131
131
  */
132
132
  export declare function deriveSourceAgent(skillDir: string, root: string, origin: "source" | "installed"): string | null;
133
- /**
134
- * 0770 Pure regex parser. Normalizes any github.com origin remote
135
- * (SSH, HTTPS, ssh://) to its canonical `https://github.com/owner/repo`
136
- * form (no `.git` suffix, no trailing path). Returns null for non-github
137
- * hosts, malformed input, empty/whitespace strings.
138
- */
139
- export declare function parseGithubRemote(remote: string | null | undefined): string | null;
140
- /**
141
- * 0770 — Walk parent directories from `startDir` looking for a `.git` entry
142
- * (directory OR file — git worktrees use a `.git` file). Bails at the
143
- * filesystem root or after `maxLevels` iterations. Returns the absolute
144
- * path of the discovered git root, or null.
145
- */
146
- export declare function walkUpForGitRoot(startDir: string, maxLevels?: number): string | null;
147
- /**
148
- * 0770 — Test-only helper to clear the module-level memoization cache so
149
- * tests can isolate detection runs across `beforeEach`.
150
- */
151
- export declare function resetAuthoredSourceLinkCache(): void;
152
- /**
153
- * 0770 — Detect source-repo provenance for a locally-authored skill (no
154
- * lockfile entry). Walks for `.git`, reads `origin` remote, normalizes via
155
- * `parseGithubRemote`, and computes `skillPath` from `git ls-files` (with a
156
- * filesystem fallback for untracked SKILL.md files). Memoized per absolute
157
- * skill dir for the eval-server process lifetime.
158
- *
159
- * All git invocations use `execFileSync` with explicit argv (no shell), a
160
- * 1500ms hard timeout, and silenced stderr. Any error converts to
161
- * `{null, null}` — `buildSkillMetadata` never throws because of git.
162
- */
163
- export declare function detectAuthoredSourceLink(skillDir: string): {
164
- repoUrl: string | null;
165
- skillPath: string | null;
166
- };
133
+ import { parseGithubRemote, walkUpForGitRoot, detectAuthoredSourceLink, resolveSourceLink, readCopiedSkillSidecar, resetAuthoredSourceLinkCache, resetCopiedSkillSidecarCache } from "./source-link.js";
134
+ export { parseGithubRemote, walkUpForGitRoot, detectAuthoredSourceLink, resolveSourceLink, readCopiedSkillSidecar, resetAuthoredSourceLinkCache, resetCopiedSkillSidecarCache, };
167
135
  /**
168
136
  * Build the T-025 metadata payload for a single skill. Reads SKILL.md from
169
137
  * disk if present; returns EMPTY_METADATA on any error so the /api/skills
@@ -233,4 +201,3 @@ export declare function detectAvailableProviders(): Promise<Array<{
233
201
  resolvedModel?: string | null;
234
202
  }>>;
235
203
  export declare function registerRoutes(router: Router, root: string, projectName?: string): void;
236
- export {};
@@ -3,7 +3,7 @@
3
3
  // ---------------------------------------------------------------------------
4
4
  import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, statSync } from "node:fs";
5
5
  import { execSync, execFileSync } from "node:child_process";
6
- import { join, resolve, dirname, basename, relative } from "node:path";
6
+ import { join, resolve, dirname } from "node:path";
7
7
  import { homedir } from "node:os";
8
8
  import { sendJson, readBody } from "./router.js";
9
9
  import { initSSE, sendSSE, sendSSEDone, withHeartbeat, startDynamicHeartbeat } from "./sse-helpers.js";
@@ -682,174 +682,12 @@ export function deriveSourceAgent(skillDir, root, origin) {
682
682
  }
683
683
  return null;
684
684
  }
685
- /**
686
- * 0770 Pure regex parser. Normalizes any github.com origin remote
687
- * (SSH, HTTPS, ssh://) to its canonical `https://github.com/owner/repo`
688
- * form (no `.git` suffix, no trailing path). Returns null for non-github
689
- * hosts, malformed input, empty/whitespace strings.
690
- */
691
- export function parseGithubRemote(remote) {
692
- const trimmed = (remote ?? "").trim();
693
- if (!trimmed)
694
- return null;
695
- // SSH: git@github.com:owner/repo[.git]
696
- let m = /^git@github\.com:([^/\s]+)\/([^/\s]+?)(?:\.git)?$/.exec(trimmed);
697
- if (m)
698
- return `https://github.com/${m[1]}/${m[2]}`;
699
- // ssh://git@github.com/owner/repo[.git]
700
- m = /^ssh:\/\/git@github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?$/.exec(trimmed);
701
- if (m)
702
- return `https://github.com/${m[1]}/${m[2]}`;
703
- // http(s)://github.com/owner/repo[.git][/...]
704
- m = /^https?:\/\/github\.com\/([^/\s]+)\/([^/\s?#]+?)(?:\.git)?(?:[/?#].*)?$/.exec(trimmed);
705
- if (m)
706
- return `https://github.com/${m[1]}/${m[2]}`;
707
- return null;
708
- }
709
- /**
710
- * 0770 — Walk parent directories from `startDir` looking for a `.git` entry
711
- * (directory OR file — git worktrees use a `.git` file). Bails at the
712
- * filesystem root or after `maxLevels` iterations. Returns the absolute
713
- * path of the discovered git root, or null.
714
- */
715
- export function walkUpForGitRoot(startDir, maxLevels = 12) {
716
- let current = resolve(startDir);
717
- for (let i = 0; i < maxLevels; i++) {
718
- if (existsSync(join(current, ".git")))
719
- return current;
720
- const parent = dirname(current);
721
- if (parent === current)
722
- return null;
723
- current = parent;
724
- }
725
- return null;
726
- }
727
- const authoredSourceLinkCache = new Map();
728
- /**
729
- * 0770 — Test-only helper to clear the module-level memoization cache so
730
- * tests can isolate detection runs across `beforeEach`.
731
- */
732
- export function resetAuthoredSourceLinkCache() {
733
- authoredSourceLinkCache.clear();
734
- }
735
- /**
736
- * 0770 — Detect source-repo provenance for a locally-authored skill (no
737
- * lockfile entry). Walks for `.git`, reads `origin` remote, normalizes via
738
- * `parseGithubRemote`, and computes `skillPath` from `git ls-files` (with a
739
- * filesystem fallback for untracked SKILL.md files). Memoized per absolute
740
- * skill dir for the eval-server process lifetime.
741
- *
742
- * All git invocations use `execFileSync` with explicit argv (no shell), a
743
- * 1500ms hard timeout, and silenced stderr. Any error converts to
744
- * `{null, null}` — `buildSkillMetadata` never throws because of git.
745
- */
746
- export function detectAuthoredSourceLink(skillDir) {
747
- const absDir = resolve(skillDir);
748
- const cached = authoredSourceLinkCache.get(absDir);
749
- if (cached)
750
- return cached;
751
- const compute = () => {
752
- const gitRoot = walkUpForGitRoot(absDir);
753
- if (!gitRoot)
754
- return { repoUrl: null, skillPath: null };
755
- let remote = "";
756
- try {
757
- remote = execFileSync("git", ["config", "--get", "remote.origin.url"], {
758
- cwd: gitRoot,
759
- timeout: 1500,
760
- stdio: ["ignore", "pipe", "ignore"],
761
- encoding: "utf-8",
762
- }).trim();
763
- }
764
- catch {
765
- return { repoUrl: null, skillPath: null };
766
- }
767
- const repoUrl = parseGithubRemote(remote);
768
- if (!repoUrl)
769
- return { repoUrl: null, skillPath: null };
770
- let skillPath = null;
771
- try {
772
- const tracked = execFileSync("git", ["ls-files", "--full-name", "SKILL.md"], {
773
- cwd: absDir,
774
- timeout: 1500,
775
- stdio: ["ignore", "pipe", "ignore"],
776
- encoding: "utf-8",
777
- }).trim();
778
- if (tracked)
779
- skillPath = tracked;
780
- }
781
- catch {
782
- // fall through to filesystem fallback
783
- }
784
- if (!skillPath) {
785
- // Filesystem fallback for untracked SKILL.md — same path the file will
786
- // have on github.com once committed and pushed.
787
- skillPath = relative(gitRoot, join(absDir, "SKILL.md")).replace(/\\/g, "/");
788
- }
789
- return { repoUrl, skillPath };
790
- };
791
- const result = compute();
792
- authoredSourceLinkCache.set(absDir, result);
793
- return result;
794
- }
795
- /**
796
- * 0737 — Resolve the source-repo provenance (repoUrl + skillPath) for a
797
- * skill by looking up its lockfile entry. Two precedences:
798
- * 1. Explicit `sourceRepoUrl` / `sourceSkillPath` (set by `vskill install`
799
- * after this change ships).
800
- * 2. Legacy `source: github:owner/repo` string (every existing install).
801
- *
802
- * Lockfile entries are keyed by plugin name; for nested-layout plugins the
803
- * skill dir basename and the lockfile key differ — fall back to the parent
804
- * directory's basename when no exact match exists.
805
- *
806
- * 0770 — When no lockfile entry resolves provenance, fall through to
807
- * `detectAuthoredSourceLink` which inspects the parent git repo's origin
808
- * remote. Lockfile-derived values still take precedence to preserve
809
- * install-time provenance when the workspace itself is a git repo.
810
- */
811
- function resolveSourceLink(skillDir, root) {
812
- const lock = readLockfile(root);
813
- if (!lock)
814
- return detectAuthoredSourceLink(skillDir);
815
- const skillName = basename(skillDir);
816
- const parentName = basename(dirname(skillDir));
817
- const entry = lock.skills[skillName] ?? lock.skills[parentName];
818
- if (!entry)
819
- return detectAuthoredSourceLink(skillDir);
820
- if (entry.sourceRepoUrl) {
821
- return {
822
- repoUrl: entry.sourceRepoUrl,
823
- skillPath: entry.sourceSkillPath ?? "SKILL.md",
824
- };
825
- }
826
- // Legacy derivation from `source: github:owner/repo`.
827
- // 0743: We DO NOT blindly default `skillPath` to "SKILL.md" here. Multi-skill
828
- // repos (vskill, marketingskills, etc.) hold the SKILL.md under a nested
829
- // path, and the legacy `source` string carries no path information.
830
- // Guessing "SKILL.md" produced confidently-wrong 404 anchors for every
831
- // install from a multi-skill repo.
832
- //
833
- // 0773 hotfix: when the matched lockfile entry KEY equals the source repo
834
- // basename (i.e. `vskill install anton-abyzov/greet-anton` keys the entry
835
- // as `greet-anton` AND the repo is `greet-anton`), the repo IS the skill —
836
- // SKILL.md sits at the repo root. Defaulting skillPath to "SKILL.md" in
837
- // that exact shape restores the working SourceFileLink anchor for
838
- // single-skill repos without re-introducing 404s for multi-skill repos.
839
- const m = /^github:([^/]+)\/([^/#]+)/.exec(entry.source ?? "");
840
- // 0770: do NOT fall through here — an installed skill with a non-github
841
- // `source` (e.g. `marketplace:...`) is still installed, not authored. Local
842
- // git detection would leak the workspace remote (umbrella, etc.).
843
- if (!m)
844
- return { repoUrl: null, skillPath: null };
845
- const repoBasename = m[2];
846
- const lockKey = lock.skills[skillName] ? skillName : parentName;
847
- const isSingleSkillRepo = repoBasename === lockKey;
848
- return {
849
- repoUrl: `https://github.com/${m[1]}/${m[2]}`,
850
- skillPath: entry.sourceSkillPath ?? (isSingleSkillRepo ? "SKILL.md" : null),
851
- };
852
- }
685
+ // 0809: parseGithubRemote, walkUpForGitRoot, detectAuthoredSourceLink,
686
+ // resolveSourceLink, and the authored-source-link cache live in
687
+ // `./source-link.ts`. Imported + re-exported here for backwards compatibility
688
+ // with existing consumers (`./skill-create-routes.ts`, the 0770 test file).
689
+ import { parseGithubRemote, walkUpForGitRoot, detectAuthoredSourceLink, resolveSourceLink, readCopiedSkillSidecar, resetAuthoredSourceLinkCache, resetCopiedSkillSidecarCache, } from "./source-link.js";
690
+ export { parseGithubRemote, walkUpForGitRoot, detectAuthoredSourceLink, resolveSourceLink, readCopiedSkillSidecar, resetAuthoredSourceLinkCache, resetCopiedSkillSidecarCache, };
853
691
  /**
854
692
  * Build the T-025 metadata payload for a single skill. Reads SKILL.md from
855
693
  * disk if present; returns EMPTY_METADATA on any error so the /api/skills