skill-flow 1.0.3 → 1.0.4

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.
@@ -0,0 +1,148 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { TARGET_DEFINITIONS, TARGET_ORDER } from "../utils/constants.js";
5
+ import { hashDirectory, pathExists, readJsonFile } from "../utils/fs.js";
6
+ import { deriveSourceId } from "../utils/source-id.js";
7
+ export class WorkspaceBootstrapService {
8
+ store;
9
+ constructor(store) {
10
+ this.store = store;
11
+ }
12
+ async detectUnmanagedExternalSkills(manifest, lockFile, onEvent) {
13
+ const managedLocators = new Set(manifest.sources
14
+ .filter((source) => source.kind === "local")
15
+ .map((source) => path.resolve(source.locator)));
16
+ const managedCheckouts = new Set(lockFile.sources.map((source) => path.resolve(source.checkoutPath)));
17
+ const managedTargetPaths = new Set(lockFile.deployments.map((deployment) => path.resolve(deployment.targetPath)));
18
+ const agentsOrigins = await this.readAgentsOrigins();
19
+ const grouped = new Map();
20
+ onEvent?.({
21
+ phase: "scan-external-roots",
22
+ level: "info",
23
+ message: "Scanning detected agent roots for unmanaged skills...",
24
+ });
25
+ for (const target of TARGET_ORDER) {
26
+ const definition = TARGET_DEFINITIONS[target];
27
+ const overrideRoot = process.env[definition.envVar]?.trim();
28
+ const roots = [
29
+ ...new Set([
30
+ ...(overrideRoot ? [path.resolve(overrideRoot)] : []),
31
+ ...definition.writeRootCandidates,
32
+ ...definition.compatReadRootCandidates,
33
+ ]),
34
+ ];
35
+ for (const root of roots) {
36
+ if (!(await pathExists(root))) {
37
+ continue;
38
+ }
39
+ const entries = await fs.readdir(root, { withFileTypes: true });
40
+ for (const entry of entries) {
41
+ const skillDir = path.join(root, entry.name);
42
+ const isDirectoryLike = entry.isDirectory() ||
43
+ (entry.isSymbolicLink() &&
44
+ (await fs.stat(skillDir).then((stats) => stats.isDirectory()).catch(() => false)));
45
+ if (!isDirectoryLike) {
46
+ continue;
47
+ }
48
+ if (!(await pathExists(path.join(skillDir, "SKILL.md")))) {
49
+ continue;
50
+ }
51
+ const resolvedPath = await fs.realpath(skillDir).catch(() => path.resolve(skillDir));
52
+ if (this.isUnderSkillFlowStore(resolvedPath)) {
53
+ continue;
54
+ }
55
+ if (managedLocators.has(resolvedPath) ||
56
+ managedCheckouts.has(resolvedPath) ||
57
+ managedTargetPaths.has(resolvedPath)) {
58
+ continue;
59
+ }
60
+ const contentHash = await hashDirectory(resolvedPath);
61
+ const groupKey = `${entry.name}\n${contentHash}`;
62
+ const current = grouped.get(groupKey);
63
+ if (current) {
64
+ current.targets.add(target);
65
+ continue;
66
+ }
67
+ grouped.set(groupKey, {
68
+ path: resolvedPath,
69
+ displayName: entry.name,
70
+ hash: contentHash,
71
+ targets: new Set([target]),
72
+ origin: agentsOrigins.get(entry.name),
73
+ });
74
+ }
75
+ }
76
+ }
77
+ const takenSourceIds = new Set(manifest.sources.map((source) => source.id));
78
+ const results = [];
79
+ for (const item of grouped.values()) {
80
+ const baseId = deriveSourceId(item.path);
81
+ const sourceId = this.allocateSourceId(baseId, item.targets, takenSourceIds);
82
+ takenSourceIds.add(sourceId);
83
+ results.push({
84
+ path: item.path,
85
+ displayName: item.displayName,
86
+ sourceId,
87
+ importedFromTargets: TARGET_ORDER.filter((target) => item.targets.has(target)),
88
+ ...(item.origin?.originLocator ? { originLocator: item.origin.originLocator } : {}),
89
+ ...(item.origin?.originRequestedPath
90
+ ? { originRequestedPath: item.origin.originRequestedPath }
91
+ : {}),
92
+ ...(item.origin?.originBranch ? { originBranch: item.origin.originBranch } : {}),
93
+ });
94
+ }
95
+ return results.sort((left, right) => left.displayName.localeCompare(right.displayName));
96
+ }
97
+ isUnderSkillFlowStore(candidatePath) {
98
+ const relative = path.relative(this.store.rootPath, candidatePath);
99
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
100
+ }
101
+ allocateSourceId(baseId, targets, takenSourceIds) {
102
+ if (!takenSourceIds.has(baseId)) {
103
+ return baseId;
104
+ }
105
+ for (const target of TARGET_ORDER) {
106
+ if (!targets.has(target)) {
107
+ continue;
108
+ }
109
+ const suffixed = `${baseId}-${TARGET_DEFINITIONS[target].writerKey}`;
110
+ if (!takenSourceIds.has(suffixed)) {
111
+ return suffixed;
112
+ }
113
+ }
114
+ let index = 2;
115
+ while (takenSourceIds.has(`${baseId}-${index}`)) {
116
+ index += 1;
117
+ }
118
+ return `${baseId}-${index}`;
119
+ }
120
+ async readAgentsOrigins() {
121
+ const lockPath = path.join(os.homedir(), ".agents", ".skill-lock.json");
122
+ const lockFile = await readJsonFile(lockPath, {});
123
+ const results = new Map();
124
+ for (const [name, record] of Object.entries(lockFile.skills ?? {})) {
125
+ if (!record || record.sourceType !== "github") {
126
+ continue;
127
+ }
128
+ results.set(name, {
129
+ originLocator: record.source ? `https://github.com/${record.source}.git` : undefined,
130
+ originRequestedPath: record.skillPath,
131
+ originBranch: record.branch ?? record.sourceBranch ?? this.parseBranchFromSourceUrl(record.sourceUrl),
132
+ });
133
+ }
134
+ return results;
135
+ }
136
+ parseBranchFromSourceUrl(sourceUrl) {
137
+ if (!sourceUrl) {
138
+ return undefined;
139
+ }
140
+ const treeIndex = sourceUrl.indexOf("/tree/");
141
+ if (treeIndex === -1) {
142
+ return undefined;
143
+ }
144
+ const tail = sourceUrl.slice(treeIndex + "/tree/".length);
145
+ return tail.split("/")[0] || undefined;
146
+ }
147
+ }
148
+ //# sourceMappingURL=workspace-bootstrap-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-bootstrap-service.js","sourceRoot":"","sources":["../../src/services/workspace-bootstrap-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA+CvD,MAAM,OAAO,yBAAyB;IACP;IAA7B,YAA6B,KAAiB;QAAjB,UAAK,GAAL,KAAK,CAAY;IAAG,CAAC;IAElD,KAAK,CAAC,6BAA6B,CACjC,QAAkB,EAClB,QAAkB,EAClB,OAAyC;QAEzC,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,QAAQ,CAAC,OAAO;aACb,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC;aAC3C,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CACjD,CAAC;QACF,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CACpE,CAAC;QACF,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAChC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAC9E,CAAC;QACF,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,IAAI,GAAG,EASpB,CAAC;QAEJ,OAAO,EAAE,CAAC;YACR,KAAK,EAAE,qBAAqB;YAC5B,KAAK,EAAE,MAAM;YACb,OAAO,EAAE,uDAAuD;SACjE,CAAC,CAAC;QAEH,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;YAC5D,MAAM,KAAK,GAAG;gBACZ,GAAG,IAAI,GAAG,CAAC;oBACT,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrD,GAAG,UAAU,CAAC,mBAAmB;oBACjC,GAAG,UAAU,CAAC,wBAAwB;iBACvC,CAAC;aACH,CAAC;YACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;oBAC9B,SAAS;gBACX,CAAC;gBAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;oBAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC7C,MAAM,eAAe,GACnB,KAAK,CAAC,WAAW,EAAE;wBACnB,CAAC,KAAK,CAAC,cAAc,EAAE;4BACrB,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBACvF,IAAI,CAAC,eAAe,EAAE,CAAC;wBACrB,SAAS;oBACX,CAAC;oBAED,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;wBACzD,SAAS;oBACX,CAAC;oBAED,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;oBACrF,IAAI,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,EAAE,CAAC;wBAC7C,SAAS;oBACX,CAAC;oBACD,IACE,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC;wBACjC,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC;wBAClC,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EACpC,CAAC;wBACD,SAAS;oBACX,CAAC;oBAED,MAAM,WAAW,GAAG,MAAM,aAAa,CAAC,YAAY,CAAC,CAAC;oBACtD,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBACjD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACtC,IAAI,OAAO,EAAE,CAAC;wBACZ,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;wBAC5B,SAAS;oBACX,CAAC;oBAED,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE;wBACpB,IAAI,EAAE,YAAY;wBAClB,WAAW,EAAE,KAAK,CAAC,IAAI;wBACvB,IAAI,EAAE,WAAW;wBACjB,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;wBAC1B,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;qBACtC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5E,MAAM,OAAO,GAA4B,EAAE,CAAC;QAE5C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;YAC7E,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,QAAQ;gBACR,mBAAmB,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC9E,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnF,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,mBAAmB;oBAClC,CAAC,CAAC,EAAE,mBAAmB,EAAE,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE;oBAC1D,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACjF,CAAC,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IAC1F,CAAC;IAEO,qBAAqB,CAAC,aAAqB;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;QACnE,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrF,CAAC;IAEO,gBAAgB,CACtB,MAAc,EACd,OAAkC,EAClC,cAA2B;QAE3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;YACrE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC;YAChD,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;QACD,OAAO,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,kBAAkB,CAAC,CAAC;QACxE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAiB,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;QAEhD,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YACnE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAC9C,SAAS;YACX,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;gBAChB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,sBAAsB,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,SAAS;gBACpF,mBAAmB,EAAE,MAAM,CAAC,SAAS;gBACrC,YAAY,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,SAAS,CAAC;aACtG,CAAC,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,wBAAwB,CAAC,SAAkB;QACjD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;YACrB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;IACzC,CAAC;CACF"}
@@ -34,6 +34,7 @@ export class StateStore {
34
34
  return path.join(this.stateRoot, "lock.json");
35
35
  }
36
36
  async init() {
37
+ await ensureDir(this.getSourceRoot("local"));
37
38
  await ensureDir(this.getSourceRoot("git"));
38
39
  await ensureDir(this.getSourceRoot("clawhub"));
39
40
  await ensureDir(this.catalogRoot);
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/state/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/state/store.ts"],"names":[],"mappings":"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"}
@@ -342,7 +342,7 @@ describe.sequential("skill-flow", () => {
342
342
  id: "garrytan-gstack",
343
343
  locator: "git@github.com:garrytan/gstack.git",
344
344
  displayName: "gstack",
345
- })).toBe("gstack(@garrytan)");
345
+ })).toBe("gstack@garrytan");
346
346
  });
347
347
  test("prefers groupName-skillName for projected collisions", () => {
348
348
  const projected = resolveProjectedSkillNames([
@@ -482,7 +482,7 @@ description: |
482
482
  "good/SKILL.md": skillDoc("good", "Good description."),
483
483
  });
484
484
  const app = new SkillFlowApp();
485
- const added = await app.addSource(repoPath);
485
+ const added = await app.addSource(repoPath, { project: false });
486
486
  expect(added.ok).toBe(true);
487
487
  if (!added.ok) {
488
488
  return;
@@ -509,7 +509,7 @@ description: |
509
509
  "browse/SKILL.md": skillDoc("browse", "Browser flow."),
510
510
  });
511
511
  const app = new SkillFlowApp();
512
- const added = await app.addSource(repoPath);
512
+ const added = await app.addSource(repoPath, { project: false });
513
513
  expect(added.ok).toBe(true);
514
514
  if (!added.ok) {
515
515
  return;
@@ -537,7 +537,7 @@ description: |
537
537
  "browse/SKILL.md": skillDoc("browse", "Browser flow."),
538
538
  });
539
539
  const app = new SkillFlowApp();
540
- const added = await app.addSource(repoPath);
540
+ const added = await app.addSource(repoPath, { project: false });
541
541
  expect(added.ok).toBe(true);
542
542
  if (!added.ok) {
543
543
  return;
@@ -551,14 +551,14 @@ description: |
551
551
  selectedLeafIds: [leafId],
552
552
  });
553
553
  expect(applied.ok).toBe(true);
554
- expect(path.resolve(await fs.readlink(targetPath))).toBe(path.join(stateRoot, "source", "git", sourceId, "browse"));
554
+ expect(path.resolve(await fs.readlink(targetPath))).toBe(path.join(added.data.lock.checkoutPath, "browse"));
555
555
  });
556
556
  test("keeps external different-content skill and renames our projection instead", async () => {
557
557
  const repoPath = await createRepo(sandboxRoot, {
558
558
  "browse/SKILL.md": skillDoc("browse", "Managed browser flow."),
559
559
  });
560
560
  const app = new SkillFlowApp();
561
- const added = await app.addSource(repoPath);
561
+ const added = await app.addSource(repoPath, { project: false });
562
562
  expect(added.ok).toBe(true);
563
563
  if (!added.ok) {
564
564
  return;
@@ -586,7 +586,7 @@ description: |
586
586
  "browse/SKILL.md": skillDoc("browse", "Managed browser flow."),
587
587
  });
588
588
  const app = new SkillFlowApp();
589
- const added = await app.addSource(repoPath);
589
+ const added = await app.addSource(repoPath, { project: false });
590
590
  expect(added.ok).toBe(true);
591
591
  if (!added.ok) {
592
592
  return;
@@ -620,7 +620,7 @@ description: |
620
620
  "good/SKILL.md": skillDoc("good", "Good description."),
621
621
  });
622
622
  const app = new SkillFlowApp();
623
- const added = await app.addSource(repoPath);
623
+ const added = await app.addSource(repoPath, { project: false });
624
624
  expect(added.ok).toBe(true);
625
625
  if (!added.ok) {
626
626
  return;
@@ -632,7 +632,7 @@ description: |
632
632
  selectedLeafIds: [leafId],
633
633
  });
634
634
  expect(applied.ok).toBe(true);
635
- await fs.rm(path.join(stateRoot, "source", "git", sourceId, "good"), {
635
+ await fs.rm(path.join(added.data.lock.checkoutPath, "good"), {
636
636
  recursive: true,
637
637
  force: true,
638
638
  });
@@ -747,7 +747,7 @@ description: |
747
747
  "browse/SKILL.md": skillDoc("browse", "Browser flow."),
748
748
  });
749
749
  const app = new SkillFlowApp();
750
- const added = await app.addSource(repoPath);
750
+ const added = await app.addSource(`file://${repoPath}`);
751
751
  expect(added.ok).toBe(true);
752
752
  if (!added.ok) {
753
753
  return;
@@ -863,7 +863,7 @@ description: |
863
863
  "good/SKILL.md": skillDoc("good", "Good description."),
864
864
  });
865
865
  const app = new SkillFlowApp();
866
- const added = await app.addSource(repoPath);
866
+ const added = await app.addSource(`file://${repoPath}`);
867
867
  expect(added.ok).toBe(true);
868
868
  if (!added.ok) {
869
869
  return;
@@ -886,14 +886,16 @@ description: |
886
886
  const repoPath = await createRepo(sandboxRoot, {
887
887
  "good/SKILL.md": skillDoc("good", "Good description."),
888
888
  });
889
+ const remotePath = await createBareRemote(repoPath, sandboxRoot);
889
890
  const app = new SkillFlowApp();
890
- const added = await app.addSource(repoPath);
891
+ const added = await app.addSource(`file://${remotePath}`);
891
892
  expect(added.ok).toBe(true);
892
893
  await writeRepoFiles(repoPath, {
893
894
  "extra/SKILL.md": skillDoc("extra", "Extra description."),
894
895
  });
895
896
  git(repoPath, ["add", "."]);
896
897
  git(repoPath, ["commit", "-m", "add extra"]);
898
+ git(repoPath, ["push", "origin", "HEAD"]);
897
899
  const updated = await app.updateSources([added.ok ? added.data.manifest.id : ""]);
898
900
  expect(updated.ok).toBe(true);
899
901
  if (!updated.ok) {
@@ -905,8 +907,9 @@ description: |
905
907
  const repoPath = await createRepo(sandboxRoot, {
906
908
  "good/SKILL.md": skillDoc("good", "Good description."),
907
909
  });
910
+ const remotePath = await createBareRemote(repoPath, sandboxRoot);
908
911
  const app = new SkillFlowApp();
909
- const added = await app.addSource(repoPath);
912
+ const added = await app.addSource(`file://${remotePath}`);
910
913
  expect(added.ok).toBe(true);
911
914
  if (!added.ok) {
912
915
  return;
@@ -920,6 +923,7 @@ description: |
920
923
  await fs.rm(path.join(repoPath, "good"), { recursive: true, force: true });
921
924
  git(repoPath, ["add", "."]);
922
925
  git(repoPath, ["commit", "-m", "remove good"]);
926
+ git(repoPath, ["push", "origin", "HEAD"]);
923
927
  const updated = await app.updateSources([sourceId]);
924
928
  expect(updated.ok).toBe(true);
925
929
  if (!updated.ok) {
@@ -932,8 +936,9 @@ description: |
932
936
  const repoPath = await createRepo(sandboxRoot, {
933
937
  "good/SKILL.md": skillDoc("good", "Good description."),
934
938
  });
939
+ const remotePath = await createBareRemote(repoPath, sandboxRoot);
935
940
  const app = new SkillFlowApp();
936
- const added = await app.addSource(repoPath);
941
+ const added = await app.addSource(`file://${remotePath}`);
937
942
  expect(added.ok).toBe(true);
938
943
  if (!added.ok) {
939
944
  return;
@@ -943,6 +948,7 @@ description: |
943
948
  });
944
949
  git(repoPath, ["add", "."]);
945
950
  git(repoPath, ["commit", "-m", "invalidate"]);
951
+ git(repoPath, ["push", "origin", "HEAD"]);
946
952
  const updated = await app.updateSources([added.data.manifest.id]);
947
953
  expect(updated.ok).toBe(true);
948
954
  if (!updated.ok) {
@@ -979,8 +985,13 @@ description: |
979
985
  enabledTargets: ["openclaw"],
980
986
  selectedLeafIds: [leafId],
981
987
  });
982
- await writeRepoFiles(process.env.SKILL_FLOW_TARGET_OPENCLAW, {
983
- ["good/SKILL.md"]: "# Good\nMutated copy.",
988
+ const lock = await app.store.readLock();
989
+ const deployment = lock.deployments.find((item) => item.sourceId === sourceId && item.leafId === leafId && item.target === "openclaw");
990
+ if (!deployment) {
991
+ throw new Error("expected deployment for openclaw");
992
+ }
993
+ await writeRepoFiles(path.dirname(deployment.targetPath), {
994
+ [`${path.basename(deployment.targetPath)}/SKILL.md`]: "# Good\nMutated copy.",
984
995
  });
985
996
  const doctor = await app.doctor();
986
997
  expect(doctor.ok).toBe(true);
@@ -989,6 +1000,19 @@ description: |
989
1000
  }
990
1001
  expect(doctor.data.issues.some((issue) => issue.code === "DRIFT_COPY")).toBe(true);
991
1002
  });
1003
+ test("bootstrap detects symlinked skills inside agent roots", async () => {
1004
+ const app = new SkillFlowApp();
1005
+ const externalRoot = path.join(sandboxRoot, "external-skill");
1006
+ await writeRepoFiles(externalRoot, {
1007
+ "SKILL.md": skillDoc("linked-skill", "Symlinked external skill."),
1008
+ });
1009
+ await fs.symlink(externalRoot, path.join(process.env.SKILL_FLOW_TARGET_CODEX, "linked-skill"), "junction");
1010
+ await app.store.init();
1011
+ const manifest = await app.store.readManifest();
1012
+ const lock = await app.store.readLock();
1013
+ const detected = await app.workspaceBootstrapService.detectUnmanagedExternalSkills(manifest, lock);
1014
+ expect(detected.some((item) => item.displayName === "linked-skill")).toBe(true);
1015
+ });
992
1016
  test("keeps metadata warnings on valid skills", async () => {
993
1017
  const repoPath = await createRepo(sandboxRoot, {
994
1018
  "folder-name/SKILL.md": skillDoc("bad--name", "x".repeat(1025)),
@@ -1389,6 +1413,13 @@ async function createRepo(root, files) {
1389
1413
  git(repoPath, ["commit", "-m", "initial"]);
1390
1414
  return repoPath;
1391
1415
  }
1416
+ async function createBareRemote(repoPath, root) {
1417
+ const remotePath = await fs.mkdtemp(path.join(root, "remote-"));
1418
+ git(remotePath, ["init", "--bare"]);
1419
+ git(repoPath, ["remote", "add", "origin", remotePath]);
1420
+ git(repoPath, ["push", "-u", "origin", "HEAD"]);
1421
+ return remotePath;
1422
+ }
1392
1423
  async function seedBuiltinCatalog(app) {
1393
1424
  for (const builtin of builtinGitSources.getBuiltinGitSources()) {
1394
1425
  await fs.mkdir(app.store.getCatalogCheckoutPath(deriveSourceId(builtin.locator)), {