skill-flow 1.0.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.
Files changed (87) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +108 -0
  3. package/README.zh.md +108 -0
  4. package/dist/adapters/channel-adapters.d.ts +8 -0
  5. package/dist/adapters/channel-adapters.js +56 -0
  6. package/dist/adapters/channel-adapters.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +118 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/domain/types.d.ts +133 -0
  11. package/dist/domain/types.js +2 -0
  12. package/dist/domain/types.js.map +1 -0
  13. package/dist/services/deployment-applier.d.ts +6 -0
  14. package/dist/services/deployment-applier.js +54 -0
  15. package/dist/services/deployment-applier.js.map +1 -0
  16. package/dist/services/deployment-planner.d.ts +11 -0
  17. package/dist/services/deployment-planner.js +179 -0
  18. package/dist/services/deployment-planner.js.map +1 -0
  19. package/dist/services/doctor-service.d.ts +5 -0
  20. package/dist/services/doctor-service.js +129 -0
  21. package/dist/services/doctor-service.js.map +1 -0
  22. package/dist/services/inventory-service.d.ts +14 -0
  23. package/dist/services/inventory-service.js +186 -0
  24. package/dist/services/inventory-service.js.map +1 -0
  25. package/dist/services/skill-flow.d.ts +60 -0
  26. package/dist/services/skill-flow.js +260 -0
  27. package/dist/services/skill-flow.js.map +1 -0
  28. package/dist/services/source-service.d.ts +35 -0
  29. package/dist/services/source-service.js +270 -0
  30. package/dist/services/source-service.js.map +1 -0
  31. package/dist/services/workflow-service.d.ts +5 -0
  32. package/dist/services/workflow-service.js +32 -0
  33. package/dist/services/workflow-service.js.map +1 -0
  34. package/dist/state/store.d.ts +14 -0
  35. package/dist/state/store.js +59 -0
  36. package/dist/state/store.js.map +1 -0
  37. package/dist/tests/skill-flow.test.d.ts +1 -0
  38. package/dist/tests/skill-flow.test.js +926 -0
  39. package/dist/tests/skill-flow.test.js.map +1 -0
  40. package/dist/tui/config-app.d.ts +47 -0
  41. package/dist/tui/config-app.js +732 -0
  42. package/dist/tui/config-app.js.map +1 -0
  43. package/dist/tui/selection-state.d.ts +8 -0
  44. package/dist/tui/selection-state.js +32 -0
  45. package/dist/tui/selection-state.js.map +1 -0
  46. package/dist/utils/constants.d.ts +19 -0
  47. package/dist/utils/constants.js +164 -0
  48. package/dist/utils/constants.js.map +1 -0
  49. package/dist/utils/format.d.ts +6 -0
  50. package/dist/utils/format.js +45 -0
  51. package/dist/utils/format.js.map +1 -0
  52. package/dist/utils/fs.d.ts +10 -0
  53. package/dist/utils/fs.js +89 -0
  54. package/dist/utils/fs.js.map +1 -0
  55. package/dist/utils/git.d.ts +3 -0
  56. package/dist/utils/git.js +12 -0
  57. package/dist/utils/git.js.map +1 -0
  58. package/dist/utils/result.d.ts +4 -0
  59. package/dist/utils/result.js +15 -0
  60. package/dist/utils/result.js.map +1 -0
  61. package/dist/utils/source-id.d.ts +2 -0
  62. package/dist/utils/source-id.js +16 -0
  63. package/dist/utils/source-id.js.map +1 -0
  64. package/img/img-1.jpg +0 -0
  65. package/package.json +39 -0
  66. package/src/adapters/channel-adapters.ts +75 -0
  67. package/src/cli.tsx +147 -0
  68. package/src/domain/types.ts +175 -0
  69. package/src/services/deployment-applier.ts +81 -0
  70. package/src/services/deployment-planner.ts +259 -0
  71. package/src/services/doctor-service.ts +156 -0
  72. package/src/services/inventory-service.ts +251 -0
  73. package/src/services/skill-flow.ts +381 -0
  74. package/src/services/source-service.ts +427 -0
  75. package/src/services/workflow-service.ts +56 -0
  76. package/src/state/store.ts +68 -0
  77. package/src/tests/skill-flow.test.ts +1184 -0
  78. package/src/tui/config-app.tsx +1094 -0
  79. package/src/tui/selection-state.ts +45 -0
  80. package/src/utils/constants.ts +201 -0
  81. package/src/utils/format.ts +59 -0
  82. package/src/utils/fs.ts +102 -0
  83. package/src/utils/git.ts +16 -0
  84. package/src/utils/result.ts +23 -0
  85. package/src/utils/source-id.ts +19 -0
  86. package/tsconfig.json +22 -0
  87. package/vitest.config.ts +8 -0
@@ -0,0 +1,186 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { hashDirectory, slugify } from "../utils/fs.js";
4
+ export class InventoryService {
5
+ static IGNORED_DIRECTORIES = new Set([
6
+ ".git",
7
+ "node_modules",
8
+ ]);
9
+ async scanSource(sourceId, checkoutPath) {
10
+ const skillFiles = await this.findSkillFiles(checkoutPath);
11
+ const candidates = [];
12
+ const invalidLeafs = [];
13
+ for (const skillFilePath of skillFiles) {
14
+ const leafRoot = path.dirname(skillFilePath);
15
+ const relativePath = path.relative(checkoutPath, leafRoot) || ".";
16
+ const raw = await fs.readFile(skillFilePath, "utf8");
17
+ const linkName = path.basename(leafRoot) || sourceId;
18
+ const parsed = this.parseSkillFile(raw, linkName);
19
+ if (!parsed.valid) {
20
+ invalidLeafs.push({
21
+ path: relativePath,
22
+ reason: parsed.reason,
23
+ });
24
+ continue;
25
+ }
26
+ const safeName = slugify(parsed.name) || linkName || sourceId;
27
+ candidates.push({
28
+ id: `${sourceId}:${relativePath}`,
29
+ sourceId,
30
+ name: safeName,
31
+ linkName,
32
+ title: parsed.title,
33
+ description: parsed.description,
34
+ relativePath,
35
+ absolutePath: leafRoot,
36
+ skillFilePath,
37
+ contentHash: await hashDirectory(leafRoot),
38
+ metadataWarnings: parsed.metadataWarnings,
39
+ valid: true,
40
+ dedupeKey: `${parsed.name}\n${parsed.description}`,
41
+ });
42
+ }
43
+ const leafs = this.dedupeCandidates(candidates, invalidLeafs);
44
+ leafs.sort((left, right) => left.relativePath.localeCompare(right.relativePath));
45
+ invalidLeafs.sort((left, right) => left.path.localeCompare(right.path));
46
+ return { leafs, invalidLeafs };
47
+ }
48
+ async findSkillFiles(rootPath) {
49
+ const discovered = [];
50
+ async function walk(currentPath) {
51
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
52
+ const files = entries
53
+ .filter((entry) => entry.isFile())
54
+ .sort((left, right) => left.name.localeCompare(right.name));
55
+ const visibleDirectories = entries
56
+ .filter((entry) => entry.isDirectory() &&
57
+ !InventoryService.IGNORED_DIRECTORIES.has(entry.name) &&
58
+ !entry.name.startsWith("."))
59
+ .sort((left, right) => left.name.localeCompare(right.name));
60
+ const hiddenDirectories = entries
61
+ .filter((entry) => entry.isDirectory() &&
62
+ !InventoryService.IGNORED_DIRECTORIES.has(entry.name) &&
63
+ entry.name.startsWith("."))
64
+ .sort((left, right) => left.name.localeCompare(right.name));
65
+ for (const entry of files) {
66
+ if (entry.name === "SKILL.md") {
67
+ discovered.push(path.join(currentPath, entry.name));
68
+ }
69
+ }
70
+ for (const entry of [...visibleDirectories, ...hiddenDirectories]) {
71
+ await walk(path.join(currentPath, entry.name));
72
+ }
73
+ }
74
+ await walk(rootPath);
75
+ return discovered;
76
+ }
77
+ parseSkillFile(raw, parentDirName) {
78
+ const lines = raw.split(/\r?\n/);
79
+ const frontmatter = this.parseFrontmatter(lines);
80
+ if (!frontmatter) {
81
+ return { valid: false, reason: "SKILL.md must start with YAML frontmatter" };
82
+ }
83
+ if (!Object.hasOwn(frontmatter.data, "name")) {
84
+ return {
85
+ valid: false,
86
+ reason: "SKILL.md frontmatter must include required field 'name'",
87
+ };
88
+ }
89
+ if (!Object.hasOwn(frontmatter.data, "description")) {
90
+ return {
91
+ valid: false,
92
+ reason: "SKILL.md frontmatter must include required field 'description'",
93
+ };
94
+ }
95
+ const bodyLines = lines.slice(frontmatter.bodyStartLine);
96
+ const firstHeading = bodyLines.find((line) => line.trim().startsWith("# "));
97
+ const rawName = (frontmatter.data.name ?? "").trim();
98
+ const rawDescription = frontmatter.data.description ?? "";
99
+ const metadataWarnings = [];
100
+ if (rawName.length < 1 ||
101
+ rawName.length > 64 ||
102
+ !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(rawName)) {
103
+ metadataWarnings.push("name should be 1-64 chars, lowercase letters/numbers/hyphens only, with no leading/trailing hyphen or consecutive '--'");
104
+ }
105
+ if (rawName !== parentDirName) {
106
+ metadataWarnings.push(`name should match parent directory name '${parentDirName}'`);
107
+ }
108
+ if (rawDescription.trim().length === 0) {
109
+ metadataWarnings.push("description should be non-empty");
110
+ }
111
+ if (rawDescription.length > 1024) {
112
+ metadataWarnings.push("description should be at most 1024 characters");
113
+ }
114
+ const title = firstHeading?.trim().slice(2).trim() || rawName || "Untitled skill";
115
+ return {
116
+ valid: true,
117
+ name: rawName,
118
+ title,
119
+ description: rawDescription.trim(),
120
+ metadataWarnings,
121
+ };
122
+ }
123
+ dedupeCandidates(candidates, invalidLeafs) {
124
+ const keptByKey = new Map();
125
+ for (const candidate of candidates) {
126
+ if (keptByKey.has(candidate.dedupeKey)) {
127
+ const kept = keptByKey.get(candidate.dedupeKey);
128
+ invalidLeafs.push({
129
+ path: candidate.relativePath,
130
+ reason: `Duplicate skill content skipped because ${kept.relativePath} was discovered first`,
131
+ });
132
+ continue;
133
+ }
134
+ const { dedupeKey: _dedupeKey, ...leaf } = candidate;
135
+ keptByKey.set(candidate.dedupeKey, leaf);
136
+ }
137
+ return [...keptByKey.values()];
138
+ }
139
+ parseFrontmatter(lines) {
140
+ if (lines[0]?.trim() !== "---") {
141
+ return undefined;
142
+ }
143
+ const data = {};
144
+ let index = 1;
145
+ while (index < lines.length) {
146
+ const line = lines[index] ?? "";
147
+ if (line.trim() === "---") {
148
+ return { data, bodyStartLine: index + 1 };
149
+ }
150
+ const pair = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
151
+ if (!pair) {
152
+ index += 1;
153
+ continue;
154
+ }
155
+ const key = pair[1];
156
+ const rest = pair[2];
157
+ if (!key || rest === undefined) {
158
+ index += 1;
159
+ continue;
160
+ }
161
+ if (rest === "|" || rest === ">") {
162
+ const blockLines = [];
163
+ index += 1;
164
+ while (index < lines.length) {
165
+ const blockLine = lines[index] ?? "";
166
+ if (blockLine.length === 0) {
167
+ blockLines.push("");
168
+ index += 1;
169
+ continue;
170
+ }
171
+ if (!blockLine.startsWith(" ")) {
172
+ break;
173
+ }
174
+ blockLines.push(blockLine.slice(2));
175
+ index += 1;
176
+ }
177
+ data[key] = blockLines.join("\n").trim();
178
+ continue;
179
+ }
180
+ data[key] = rest.trim();
181
+ index += 1;
182
+ }
183
+ return undefined;
184
+ }
185
+ }
186
+ //# sourceMappingURL=inventory-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inventory-service.js","sourceRoot":"","sources":["../../src/services/inventory-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { DeploymentAction, DeploymentPlan, DeploymentTargetName, DoctorReport, LockFile, Manifest, Result, SourceBinding, WorkflowSummary } from "../domain/types.js";
2
+ import { StateStore } from "../state/store.js";
3
+ import { DeploymentApplier } from "./deployment-applier.js";
4
+ import { DeploymentPlanner } from "./deployment-planner.js";
5
+ import { DoctorService } from "./doctor-service.js";
6
+ import { InventoryService } from "./inventory-service.js";
7
+ import { SourceService } from "./source-service.js";
8
+ import { WorkflowService } from "./workflow-service.js";
9
+ export type DraftBinding = {
10
+ enabledTargets: DeploymentTargetName[];
11
+ selectedLeafIds: string[];
12
+ };
13
+ export declare class SkillFlowApp {
14
+ readonly store: StateStore;
15
+ readonly inventoryService: InventoryService;
16
+ readonly sourceService: SourceService;
17
+ readonly planner: DeploymentPlanner;
18
+ readonly applier: DeploymentApplier;
19
+ readonly doctorService: DoctorService;
20
+ readonly workflowService: WorkflowService;
21
+ addSource(locator: string): Promise<Result<import("./source-service.js").SourceSnapshot>>;
22
+ listWorkflows(): Promise<Result<{
23
+ summaries: WorkflowSummary[];
24
+ }>>;
25
+ getConfigData(): Promise<Result<{
26
+ manifest: Manifest;
27
+ lockFile: LockFile;
28
+ summaries: WorkflowSummary[];
29
+ }>>;
30
+ getAvailableTargets(): Promise<DeploymentTargetName[]>;
31
+ previewDraft(sourceId: string, draft: DraftBinding): Promise<Result<{
32
+ plan: DeploymentPlan;
33
+ manifest: Manifest;
34
+ lockFile: LockFile;
35
+ }>>;
36
+ applyDraft(sourceId: string, draft: DraftBinding): Promise<Result<{
37
+ actions: DeploymentAction[];
38
+ draft: DraftBinding;
39
+ }>>;
40
+ updateSources(sourceIds?: string[]): Promise<Result<{
41
+ updated: Array<{
42
+ sourceId: string;
43
+ changed: boolean;
44
+ addedLeafIds: string[];
45
+ removedLeafIds: string[];
46
+ invalidatedLeafIds: string[];
47
+ }>;
48
+ }>>;
49
+ doctor(): Promise<Result<DoctorReport>>;
50
+ uninstall(sourceIds: string[]): Promise<Result<{
51
+ removed: string[];
52
+ warnings: string[];
53
+ }>>;
54
+ bindingFromDraft(draft: DraftBinding): SourceBinding;
55
+ private prepareManifestForDraft;
56
+ private findExactDuplicateLeafSelections;
57
+ private getExactDuplicateKey;
58
+ private planForAffectedSources;
59
+ private hasActiveTargets;
60
+ }
@@ -0,0 +1,260 @@
1
+ import fs from "node:fs/promises";
2
+ import { createChannelAdapters } from "../adapters/channel-adapters.js";
3
+ import { StateStore } from "../state/store.js";
4
+ import { hashDirectory, pathExists, removePath } from "../utils/fs.js";
5
+ import { fail, ok } from "../utils/result.js";
6
+ import { DeploymentApplier } from "./deployment-applier.js";
7
+ import { DeploymentPlanner } from "./deployment-planner.js";
8
+ import { DoctorService } from "./doctor-service.js";
9
+ import { InventoryService } from "./inventory-service.js";
10
+ import { SourceService } from "./source-service.js";
11
+ import { WorkflowService } from "./workflow-service.js";
12
+ export class SkillFlowApp {
13
+ store = new StateStore();
14
+ inventoryService = new InventoryService();
15
+ sourceService = new SourceService(this.store, this.inventoryService);
16
+ planner = new DeploymentPlanner(createChannelAdapters());
17
+ applier = new DeploymentApplier();
18
+ doctorService = new DoctorService();
19
+ workflowService = new WorkflowService();
20
+ async addSource(locator) {
21
+ return this.sourceService.addSource(locator);
22
+ }
23
+ async listWorkflows() {
24
+ const reconciled = await this.sourceService.reconcileInventory(undefined, {
25
+ force: true,
26
+ });
27
+ if (!reconciled.ok) {
28
+ return fail(reconciled.errors, reconciled.warnings);
29
+ }
30
+ await this.store.init();
31
+ const manifest = await this.store.readManifest();
32
+ const lockFile = await this.store.readLock();
33
+ return ok({
34
+ summaries: this.workflowService.getSummaries(manifest, lockFile),
35
+ });
36
+ }
37
+ async getConfigData() {
38
+ const reconciled = await this.sourceService.reconcileInventory(undefined, {
39
+ force: true,
40
+ });
41
+ if (!reconciled.ok) {
42
+ return fail(reconciled.errors, reconciled.warnings);
43
+ }
44
+ await this.store.init();
45
+ const manifest = await this.store.readManifest();
46
+ const lockFile = await this.store.readLock();
47
+ return ok({
48
+ manifest,
49
+ lockFile,
50
+ summaries: this.workflowService.getSummaries(manifest, lockFile),
51
+ });
52
+ }
53
+ async getAvailableTargets() {
54
+ const adapters = createChannelAdapters();
55
+ const availableTargets = [];
56
+ for (const adapter of adapters) {
57
+ const detection = await adapter.detect();
58
+ if (detection.available) {
59
+ availableTargets.push(adapter.target);
60
+ }
61
+ }
62
+ return availableTargets;
63
+ }
64
+ async previewDraft(sourceId, draft) {
65
+ // config TUI state flow:
66
+ // draft -> previewDraft() -> plan only
67
+ // draft -> applyDraft() -> plan + filesystem + manifest/lock writes
68
+ await this.store.init();
69
+ const manifest = await this.store.readManifest();
70
+ const lockFile = await this.store.readLock();
71
+ const prepared = this.prepareManifestForDraft(manifest, lockFile, sourceId, draft);
72
+ const plan = await this.planForAffectedSources(prepared.manifest, lockFile, sourceId);
73
+ if (!plan.ok) {
74
+ return fail(plan.errors, [...prepared.warnings, ...plan.warnings]);
75
+ }
76
+ return ok({ plan: plan.data, manifest: prepared.manifest, lockFile }, [...prepared.warnings, ...plan.warnings]);
77
+ }
78
+ async applyDraft(sourceId, draft) {
79
+ const reconciled = await this.sourceService.reconcileInventory([sourceId], {
80
+ force: true,
81
+ });
82
+ if (!reconciled.ok) {
83
+ return fail(reconciled.errors, reconciled.warnings);
84
+ }
85
+ await this.store.init();
86
+ const manifest = await this.store.readManifest();
87
+ const lockFile = await this.store.readLock();
88
+ const prepared = this.prepareManifestForDraft(manifest, lockFile, sourceId, draft);
89
+ const plan = await this.planForAffectedSources(prepared.manifest, lockFile, sourceId);
90
+ if (!plan.ok) {
91
+ return fail(plan.errors, [...prepared.warnings, ...plan.warnings]);
92
+ }
93
+ const applyResult = await this.applier.applyPlan(lockFile, plan.data.actions);
94
+ await this.store.writeManifest(prepared.manifest);
95
+ await this.store.writeLock(lockFile);
96
+ if (!applyResult.ok) {
97
+ return fail(applyResult.errors, [...prepared.warnings, ...plan.warnings, ...applyResult.warnings]);
98
+ }
99
+ return ok({ actions: plan.data.actions, draft: prepared.draft }, [...prepared.warnings, ...plan.warnings, ...applyResult.warnings]);
100
+ }
101
+ async updateSources(sourceIds) {
102
+ const reconciled = await this.sourceService.reconcileInventory(sourceIds, {
103
+ force: true,
104
+ });
105
+ if (!reconciled.ok) {
106
+ return fail(reconciled.errors, reconciled.warnings);
107
+ }
108
+ const updated = await this.sourceService.updateSources(sourceIds);
109
+ if (!updated.ok) {
110
+ return updated;
111
+ }
112
+ const manifest = await this.store.readManifest();
113
+ const lockFile = await this.store.readLock();
114
+ const activeSourceIds = manifest.sources
115
+ .map((source) => source.id)
116
+ .filter((id) => this.hasActiveTargets(manifest, id));
117
+ for (const sourceId of activeSourceIds) {
118
+ const plan = await this.planner.planForSource(sourceId, manifest, lockFile);
119
+ if (!plan.ok) {
120
+ return fail(plan.errors, plan.warnings);
121
+ }
122
+ await this.applier.applyPlan(lockFile, plan.data.actions);
123
+ }
124
+ await this.store.writeLock(lockFile);
125
+ return updated;
126
+ }
127
+ async doctor() {
128
+ const reconciled = await this.sourceService.reconcileInventory();
129
+ if (!reconciled.ok) {
130
+ return fail(reconciled.errors, reconciled.warnings);
131
+ }
132
+ await this.store.init();
133
+ const manifest = await this.store.readManifest();
134
+ const lockFile = await this.store.readLock();
135
+ return this.doctorService.run(manifest, lockFile);
136
+ }
137
+ async uninstall(sourceIds) {
138
+ await this.store.init();
139
+ const lockFile = await this.store.readLock();
140
+ const warnings = [];
141
+ for (const sourceId of sourceIds) {
142
+ const deployments = lockFile.deployments.filter((deployment) => deployment.sourceId === sourceId);
143
+ for (const deployment of deployments) {
144
+ if (!(await pathExists(deployment.targetPath))) {
145
+ continue;
146
+ }
147
+ if (deployment.strategy === "symlink") {
148
+ const stats = await fs.lstat(deployment.targetPath);
149
+ if (stats.isSymbolicLink()) {
150
+ await removePath(deployment.targetPath);
151
+ }
152
+ else {
153
+ warnings.push(`Skipped ${deployment.targetPath} because it no longer looks like a managed symlink.`);
154
+ }
155
+ continue;
156
+ }
157
+ const currentHash = await hashDirectory(deployment.targetPath);
158
+ if (currentHash === deployment.contentHash) {
159
+ await removePath(deployment.targetPath);
160
+ }
161
+ else {
162
+ warnings.push(`Skipped ${deployment.targetPath} because the copied skill has drifted from saved state.`);
163
+ }
164
+ }
165
+ }
166
+ const removed = await this.sourceService.removeSource(sourceIds);
167
+ if (!removed.ok) {
168
+ return fail(removed.errors, removed.warnings);
169
+ }
170
+ return ok({ removed: removed.data.removed, warnings });
171
+ }
172
+ bindingFromDraft(draft) {
173
+ const targets = {};
174
+ for (const target of draft.enabledTargets) {
175
+ targets[target] = {
176
+ enabled: true,
177
+ leafIds: [...draft.selectedLeafIds],
178
+ };
179
+ }
180
+ return { targets };
181
+ }
182
+ prepareManifestForDraft(manifest, lockFile, sourceId, draft) {
183
+ manifest.bindings[sourceId] = this.bindingFromDraft(draft);
184
+ const conflictingLeafIds = this.findExactDuplicateLeafSelections(manifest, lockFile, sourceId, draft.enabledTargets);
185
+ const normalizedDraft = {
186
+ enabledTargets: [...draft.enabledTargets],
187
+ selectedLeafIds: draft.selectedLeafIds.filter((leafId) => !conflictingLeafIds.has(leafId)),
188
+ };
189
+ manifest.bindings[sourceId] = this.bindingFromDraft(normalizedDraft);
190
+ const warnings = [...conflictingLeafIds].map((leafId) => ({
191
+ code: "DUPLICATE_LEAF_SELECTION_SKIPPED",
192
+ message: `${leafId} skipped because an identical skill is already selected in another workflow group.`,
193
+ }));
194
+ return {
195
+ manifest,
196
+ draft: normalizedDraft,
197
+ warnings,
198
+ };
199
+ }
200
+ findExactDuplicateLeafSelections(manifest, lockFile, currentSourceId, enabledTargets) {
201
+ const conflictingKeys = new Set();
202
+ for (const source of manifest.sources) {
203
+ if (source.id === currentSourceId) {
204
+ continue;
205
+ }
206
+ const binding = manifest.bindings[source.id];
207
+ if (!binding) {
208
+ continue;
209
+ }
210
+ for (const target of enabledTargets) {
211
+ const targetBinding = binding.targets[target];
212
+ if (!targetBinding?.enabled) {
213
+ continue;
214
+ }
215
+ for (const leafId of targetBinding.leafIds) {
216
+ const leaf = lockFile.leafInventory.find((item) => item.id === leafId);
217
+ if (!leaf) {
218
+ continue;
219
+ }
220
+ conflictingKeys.add(this.getExactDuplicateKey(leaf));
221
+ }
222
+ }
223
+ }
224
+ const currentLeafs = lockFile.leafInventory.filter((leaf) => leaf.sourceId === currentSourceId);
225
+ return new Set(currentLeafs
226
+ .filter((leaf) => conflictingKeys.has(this.getExactDuplicateKey(leaf)))
227
+ .map((leaf) => leaf.id));
228
+ }
229
+ getExactDuplicateKey(leaf) {
230
+ return `${leaf.linkName}\n${leaf.name}\n${leaf.description}`;
231
+ }
232
+ async planForAffectedSources(manifest, lockFile, primarySourceId) {
233
+ const sourceIds = manifest.sources
234
+ .map((source) => source.id)
235
+ .filter((sourceId) => sourceId === primarySourceId || this.hasActiveTargets(manifest, sourceId));
236
+ const actions = [];
237
+ const warnings = [];
238
+ for (const sourceId of sourceIds) {
239
+ const plan = await this.planner.planForSource(sourceId, manifest, lockFile);
240
+ if (!plan.ok) {
241
+ return fail(plan.errors, [...warnings, ...plan.warnings]);
242
+ }
243
+ actions.push(...plan.data.actions);
244
+ warnings.push(...plan.warnings);
245
+ }
246
+ return ok({
247
+ actions,
248
+ warnings,
249
+ blocked: actions.filter((action) => action.kind === "blocked"),
250
+ }, warnings);
251
+ }
252
+ hasActiveTargets(manifest, sourceId) {
253
+ const binding = manifest.bindings[sourceId];
254
+ if (!binding) {
255
+ return false;
256
+ }
257
+ return Object.values(binding.targets).some((target) => target?.enabled);
258
+ }
259
+ }
260
+ //# sourceMappingURL=skill-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-flow.js","sourceRoot":"","sources":["../../src/services/skill-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { Result, SourceLockRecord, SourceManifestRecord } from "../domain/types.js";
2
+ import { StateStore } from "../state/store.js";
3
+ import { InventoryService } from "./inventory-service.js";
4
+ export type SourceSnapshot = {
5
+ manifest: SourceManifestRecord;
6
+ lock: SourceLockRecord;
7
+ leafCount: number;
8
+ invalidLeafCount: number;
9
+ };
10
+ export declare class SourceService {
11
+ private readonly store;
12
+ private readonly inventoryService;
13
+ constructor(store: StateStore, inventoryService: InventoryService);
14
+ addSource(locator: string): Promise<Result<SourceSnapshot>>;
15
+ updateSources(sourceIds?: string[]): Promise<Result<{
16
+ updated: Array<{
17
+ sourceId: string;
18
+ changed: boolean;
19
+ addedLeafIds: string[];
20
+ removedLeafIds: string[];
21
+ invalidatedLeafIds: string[];
22
+ }>;
23
+ }>>;
24
+ removeSource(sourceIds: string[]): Promise<Result<{
25
+ removed: string[];
26
+ }>>;
27
+ reconcileInventory(sourceIds?: string[], options?: {
28
+ force?: boolean;
29
+ }): Promise<Result<{
30
+ updatedSourceIds: string[];
31
+ }>>;
32
+ private needsInventoryReconcile;
33
+ private buildSnapshot;
34
+ private normalizeLocator;
35
+ }