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.
- package/README.md +39 -2
- package/README.zh.md +39 -2
- package/dist/cli.js +9 -35
- package/dist/cli.js.map +1 -1
- package/dist/domain/types.d.ts +12 -1
- package/dist/services/skill-flow.d.ts +16 -13
- package/dist/services/skill-flow.js +157 -4
- package/dist/services/skill-flow.js.map +1 -1
- package/dist/services/source-service.d.ts +11 -1
- package/dist/services/source-service.js +56 -14
- package/dist/services/source-service.js.map +1 -1
- package/dist/services/workflow-service.d.ts +2 -2
- package/dist/services/workflow-service.js +17 -4
- package/dist/services/workflow-service.js.map +1 -1
- package/dist/services/workspace-bootstrap-service.d.ts +25 -0
- package/dist/services/workspace-bootstrap-service.js +148 -0
- package/dist/services/workspace-bootstrap-service.js.map +1 -0
- package/dist/state/store.js +1 -0
- package/dist/state/store.js.map +1 -1
- package/dist/tests/skill-flow.test.js +47 -16
- package/dist/tests/skill-flow.test.js.map +1 -1
- package/dist/tui/config-app.d.ts +3 -0
- package/dist/tui/config-app.js +60 -0
- package/dist/tui/config-app.js.map +1 -1
- package/dist/tui/find-app.d.ts +1 -1
- package/dist/tui/find-app.js +36 -4
- package/dist/tui/find-app.js.map +1 -1
- package/dist/utils/naming.d.ts +1 -0
- package/dist/utils/naming.js +7 -1
- package/dist/utils/naming.js.map +1 -1
- package/dist/utils/source-id.js +4 -0
- package/dist/utils/source-id.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,9 @@ Set it up once, deploy to multiple agents (Claude Code, Cursor, Windsurf, and 13
|
|
|
27
27
|
**Interactive Terminal UI**
|
|
28
28
|
Intuitive TUI: view groups → select skills → choose targets → save configuration.
|
|
29
29
|
|
|
30
|
+
**Bootstrap On Config Open**
|
|
31
|
+
`config` renders immediately, shows boot progress, adopts unmanaged skills already found in agent roots, then audits current projections before entering the main UI.
|
|
32
|
+
|
|
30
33
|
**Explicit State Tracking**
|
|
31
34
|
`manifest.json` = what you want, `lock.json` = what's actually installed. Both are readable and queryable.
|
|
32
35
|
|
|
@@ -86,11 +89,35 @@ skill-flow uninstall my-source-id
|
|
|
86
89
|
|
|
87
90
|
By default, `add` preselects all discovered skills and all detected agent targets. When `--path <repoSubpath>` is provided, the full repo is still imported, but only skills under that path are preselected.
|
|
88
91
|
|
|
92
|
+
Examples:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Local repo
|
|
96
|
+
skill-flow add ~/code/my-skills
|
|
97
|
+
|
|
98
|
+
# GitHub shorthand
|
|
99
|
+
skill-flow add garrytan/gstack
|
|
100
|
+
|
|
101
|
+
# Full Git URL
|
|
102
|
+
skill-flow add https://github.com/garrytan/gstack.git
|
|
103
|
+
skill-flow add git@github.com:garrytan/gstack.git
|
|
104
|
+
|
|
105
|
+
# GitHub tree URL
|
|
106
|
+
skill-flow add https://github.com/garrytan/gstack/tree/main/skills
|
|
107
|
+
|
|
108
|
+
# Import the repo, but only preselect skills under a subpath
|
|
109
|
+
skill-flow add garrytan/gstack --path skills
|
|
110
|
+
|
|
111
|
+
# ClawHub package
|
|
112
|
+
skill-flow add clawhub:example/skill-pack
|
|
113
|
+
skill-flow add clawhub:example/skill-pack@1.2.3
|
|
114
|
+
```
|
|
115
|
+
|
|
89
116
|
## Command Reference
|
|
90
117
|
|
|
91
118
|
| Command | Description |
|
|
92
119
|
|---|---|
|
|
93
|
-
| `add <source>` | Add a skill source (Git repo or ClawHub) |
|
|
120
|
+
| `add <source>` | Add a skill source (local path, Git repo, or ClawHub) |
|
|
94
121
|
| `find <query>` | Search installed skills, built-in Git catalogs, and ClawHub |
|
|
95
122
|
| `search <query>` | Alias of `find` |
|
|
96
123
|
| `list` | Show skills groups |
|
|
@@ -106,6 +133,7 @@ When selected skills collide by name, `skill-flow` keeps identical duplicates as
|
|
|
106
133
|
**State Management**
|
|
107
134
|
- `~/.skillflow/manifest.json` - Your configuration (what you want)
|
|
108
135
|
- `~/.skillflow/lock.json` - Actual state (what's installed)
|
|
136
|
+
- `~/.skillflow/source/local/<source-id>/` - Imported local sources and adopted unmanaged external skills
|
|
109
137
|
- `~/.skillflow/source/git/<source-id>/` - Git repo cache
|
|
110
138
|
- `~/.skillflow/source/clawhub/<source-id>/` - ClawHub cache
|
|
111
139
|
- `~/.skillflow/catalog/git/<source-id>/` - Built-in Git catalog cache
|
|
@@ -113,13 +141,22 @@ When selected skills collide by name, `skill-flow` keeps identical duplicates as
|
|
|
113
141
|
**Deployment Strategy**
|
|
114
142
|
Uses symlinks when possible, file copies when needed. Target directories are just deploy points — real state lives in lock.json.
|
|
115
143
|
|
|
144
|
+
**Config Bootstrap**
|
|
145
|
+
- detect available agent targets
|
|
146
|
+
- scan known agent `skills/` roots for unmanaged skills
|
|
147
|
+
- import unmanaged external skills into `~/.skillflow/source/local/`
|
|
148
|
+
- refresh inventory, normalize bindings, and audit projections
|
|
149
|
+
- enter the interactive config UI
|
|
150
|
+
|
|
151
|
+
Already-managed projections are not imported again. For example, if an agent root contains symlinks that already point into `~/.skillflow/source/*`, bootstrap treats them as managed state and skips them.
|
|
152
|
+
|
|
116
153
|
## Supported Agents
|
|
117
154
|
|
|
118
155
|
Claude Code · Codex · Cursor · GitHub Copilot · Gemini CLI · OpenCode · OpenClaw · Pi · Windsurf · Roo Code · Cline · Amp · Kiro
|
|
119
156
|
|
|
120
157
|
Customize target paths via environment variables (e.g., `SKILL_FLOW_TARGET_CLAUDE_CODE`).
|
|
121
158
|
|
|
122
|
-
Broader ecosystem path references, including project-level rules and instructions paths
|
|
159
|
+
Broader ecosystem path references, including project-level rules and instructions paths (docs TBD).
|
|
123
160
|
|
|
124
161
|
## Built-in Discovery Catalogs
|
|
125
162
|
|
package/README.zh.md
CHANGED
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
**交互式终端 UI**
|
|
27
27
|
直观的 TUI 界面:查看分组 → 选择技能 → 选择目标 → 保存配置。
|
|
28
28
|
|
|
29
|
+
**进入 Config 自动自举**
|
|
30
|
+
`config` 会先立即渲染,再显示启动进度;启动阶段会识别 agent 根目录里尚未纳入管理的 skill,回填到 `skill-flow`,并在进入主界面前完成状态审计。
|
|
31
|
+
|
|
29
32
|
**显式状态追踪**
|
|
30
33
|
`manifest.json` 记录你的意图,`lock.json` 记录实际安装状态。状态清晰可查。
|
|
31
34
|
|
|
@@ -85,11 +88,35 @@ skill-flow uninstall my-source-id
|
|
|
85
88
|
|
|
86
89
|
`add` 默认会预选该源的全部 skill,以及当前检测到的全部 agent 目标。传入 `--path <repoSubpath>` 时,仍然会导入整个仓库,但只会预选该路径下的 skill。
|
|
87
90
|
|
|
91
|
+
示例:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 本地仓库
|
|
95
|
+
skill-flow add ~/code/my-skills
|
|
96
|
+
|
|
97
|
+
# GitHub 简写
|
|
98
|
+
skill-flow add garrytan/gstack
|
|
99
|
+
|
|
100
|
+
# 完整 Git URL
|
|
101
|
+
skill-flow add https://github.com/garrytan/gstack.git
|
|
102
|
+
skill-flow add git@github.com:garrytan/gstack.git
|
|
103
|
+
|
|
104
|
+
# GitHub tree URL
|
|
105
|
+
skill-flow add https://github.com/garrytan/gstack/tree/main/skills
|
|
106
|
+
|
|
107
|
+
# 导入整个仓库,但只预选某个子路径下的 skill
|
|
108
|
+
skill-flow add garrytan/gstack --path skills
|
|
109
|
+
|
|
110
|
+
# ClawHub 包
|
|
111
|
+
skill-flow add clawhub:example/skill-pack
|
|
112
|
+
skill-flow add clawhub:example/skill-pack@1.2.3
|
|
113
|
+
```
|
|
114
|
+
|
|
88
115
|
## 命令参考
|
|
89
116
|
|
|
90
117
|
| 命令 | 说明 |
|
|
91
118
|
|---|---|
|
|
92
|
-
| `add <source>` |
|
|
119
|
+
| `add <source>` | 添加技能源(本地路径、Git 仓库或 ClawHub) |
|
|
93
120
|
| `find <query>` | 搜索本地已安装技能、内置 Git 仓库和 ClawHub |
|
|
94
121
|
| `search <query>` | `find` 的别名 |
|
|
95
122
|
| `list` | 显示 Skill 分组 |
|
|
@@ -105,6 +132,7 @@ skill-flow uninstall my-source-id
|
|
|
105
132
|
**状态管理**
|
|
106
133
|
- `~/.skillflow/manifest.json` - 你的配置(你想要什么)
|
|
107
134
|
- `~/.skillflow/lock.json` - 实际状态(实际装了什么)
|
|
135
|
+
- `~/.skillflow/source/local/<source-id>/` - 本地导入源,以及启动时接管的外部 skill
|
|
108
136
|
- `~/.skillflow/source/git/<source-id>/` - Git 仓库缓存
|
|
109
137
|
- `~/.skillflow/source/clawhub/<source-id>/` - ClawHub 缓存
|
|
110
138
|
- `~/.skillflow/catalog/git/<source-id>/` - 内置 Git 仓库缓存
|
|
@@ -112,13 +140,22 @@ skill-flow uninstall my-source-id
|
|
|
112
140
|
**部署策略**
|
|
113
141
|
优先使用符号链接,必要时使用文件复制。目标目录只是部署点,真正的状态在 lock.json 里。
|
|
114
142
|
|
|
143
|
+
**Config 启动时会做什么**
|
|
144
|
+
- 检测当前可用 agent 目标
|
|
145
|
+
- 扫描已知 agent `skills/` 根目录中的未受管 skill
|
|
146
|
+
- 将这些外部 skill 导入到 `~/.skillflow/source/local/`
|
|
147
|
+
- 刷新 inventory、归一化 bindings、审计当前投影状态
|
|
148
|
+
- 然后进入交互式 config 界面
|
|
149
|
+
|
|
150
|
+
如果某个 agent 根目录里的 symlink 本来就已经指向 `~/.skillflow/source/*` 下的受管内容,bootstrap 会把它视为已管理状态,不会重复回填。
|
|
151
|
+
|
|
115
152
|
## 支持的 Agent
|
|
116
153
|
|
|
117
154
|
Claude Code · Codex · Cursor · GitHub Copilot · Gemini CLI · OpenCode · OpenClaw · Pi · Windsurf · Roo Code · Cline · Amp · Kiro
|
|
118
155
|
|
|
119
156
|
可通过环境变量自定义目标路径(如 `SKILL_FLOW_TARGET_CLAUDE_CODE`)。
|
|
120
157
|
|
|
121
|
-
更广义的生态路径参考,包括 project 级 rules / instructions
|
|
158
|
+
更广义的生态路径参考,包括 project 级 rules / instructions 路径(文档整理中)。
|
|
122
159
|
|
|
123
160
|
## 默认内置发现仓库
|
|
124
161
|
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
import { SkillFlowApp } from "./services/skill-flow.js";
|
|
6
|
-
import {
|
|
6
|
+
import { ConfigBootstrapApp } from "./tui/config-app.js";
|
|
7
7
|
import { FindApp } from "./tui/find-app.js";
|
|
8
8
|
import { formatGroupRef } from "./utils/naming.js";
|
|
9
9
|
import { formatDoctorIssue, formatWorkflowList, } from "./utils/format.js";
|
|
@@ -48,13 +48,13 @@ program
|
|
|
48
48
|
.argument("<query>", "Search query")
|
|
49
49
|
.option("--json", "Print JSON output")
|
|
50
50
|
.action(async (query, options) => {
|
|
51
|
-
const result = await app.findSkills(query);
|
|
52
|
-
if (!result.ok) {
|
|
53
|
-
printErrors(result.errors);
|
|
54
|
-
process.exitCode = 1;
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
51
|
if (options.json) {
|
|
52
|
+
const result = await app.findSkills(query);
|
|
53
|
+
if (!result.ok) {
|
|
54
|
+
printErrors(result.errors);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
58
|
console.log(JSON.stringify(result.data.candidates.map((candidate) => ({
|
|
59
59
|
...candidate,
|
|
60
60
|
nextCommand: buildFindCommand(candidate),
|
|
@@ -62,37 +62,11 @@ program
|
|
|
62
62
|
printWarnings(result.warnings.map((warning) => warning.message));
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
|
-
const instance = render(_jsx(FindApp, { app: app, query: query
|
|
66
|
-
if (result.warnings.length > 0) {
|
|
67
|
-
for (const warning of result.warnings) {
|
|
68
|
-
console.warn(`warning: ${warning.message}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
65
|
+
const instance = render(_jsx(FindApp, { app: app, query: query }));
|
|
71
66
|
await instance.waitUntilExit();
|
|
72
67
|
});
|
|
73
68
|
program.command("config").action(async () => {
|
|
74
|
-
const
|
|
75
|
-
if (!result.ok) {
|
|
76
|
-
printErrors(result.errors);
|
|
77
|
-
process.exitCode = 1;
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const initialDrafts = Object.fromEntries(result.data.summaries.map((summary) => {
|
|
81
|
-
const targets = summary.bindings.targets;
|
|
82
|
-
const enabledTargets = Object.entries(targets)
|
|
83
|
-
.filter(([, value]) => value?.enabled)
|
|
84
|
-
.map(([target]) => target);
|
|
85
|
-
const selectedLeafIds = [...new Set(enabledTargets.flatMap((target) => targets[target]?.leafIds ?? []))];
|
|
86
|
-
return [
|
|
87
|
-
summary.source.id,
|
|
88
|
-
{
|
|
89
|
-
enabledTargets,
|
|
90
|
-
selectedLeafIds,
|
|
91
|
-
},
|
|
92
|
-
];
|
|
93
|
-
}));
|
|
94
|
-
const availableTargets = await app.getAvailableTargets();
|
|
95
|
-
const instance = render(_jsx(ConfigApp, { app: app, availableTargets: availableTargets, summaries: result.data.summaries, initialDrafts: initialDrafts }));
|
|
69
|
+
const instance = render(_jsx(ConfigBootstrapApp, { app: app }));
|
|
96
70
|
await instance.waitUntilExit();
|
|
97
71
|
});
|
|
98
72
|
program
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";;AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"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"}
|
package/dist/domain/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type Result<T> = {
|
|
|
17
17
|
warnings: Warning[];
|
|
18
18
|
errors: Failure[];
|
|
19
19
|
};
|
|
20
|
-
export type SourceKind = "git" | "clawhub";
|
|
20
|
+
export type SourceKind = "local" | "git" | "clawhub";
|
|
21
21
|
export type DeploymentTargetName = "claude-code" | "codex" | "cursor" | "github-copilot" | "gemini-cli" | "opencode" | "openclaw" | "pi" | "windsurf" | "roo-code" | "cline" | "amp" | "kiro";
|
|
22
22
|
export type DeploymentStrategy = "symlink" | "copy";
|
|
23
23
|
export type HealthStatus = "HEALTHY" | "ACTIVE" | "INACTIVE" | "PARTIAL" | "BLOCKED" | "INVALID" | "UPDATE AVAILABLE" | "UP TO DATE" | "DRIFT DETECTED";
|
|
@@ -28,6 +28,9 @@ export type SourceManifestRecord = {
|
|
|
28
28
|
displayName: string;
|
|
29
29
|
addedAt: string;
|
|
30
30
|
requestedPath?: string;
|
|
31
|
+
selectionMode?: "all" | "partial";
|
|
32
|
+
originLocator?: string;
|
|
33
|
+
originRequestedPath?: string;
|
|
31
34
|
};
|
|
32
35
|
export type TargetBinding = {
|
|
33
36
|
enabled: boolean;
|
|
@@ -59,6 +62,9 @@ export type SourceLockRecord = {
|
|
|
59
62
|
resolvedVersion?: string;
|
|
60
63
|
contentHash?: string;
|
|
61
64
|
versionMode?: "pinned" | "floating";
|
|
65
|
+
originBranch?: string;
|
|
66
|
+
importedFromTargets?: DeploymentTargetName[];
|
|
67
|
+
importMode?: "explicit-add" | "bootstrap-detected";
|
|
62
68
|
};
|
|
63
69
|
export type LeafRecord = {
|
|
64
70
|
id: string;
|
|
@@ -138,6 +144,11 @@ export type WorkflowSummary = {
|
|
|
138
144
|
bindings: SourceBinding;
|
|
139
145
|
activeTargetCount: number;
|
|
140
146
|
health: HealthStatus;
|
|
147
|
+
issueCounts?: {
|
|
148
|
+
warning: number;
|
|
149
|
+
error: number;
|
|
150
|
+
};
|
|
151
|
+
healthReason?: string;
|
|
141
152
|
};
|
|
142
153
|
export type SkillCandidateAction = {
|
|
143
154
|
type: "none";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DeploymentAction, DeploymentPlan, DeploymentTargetName, DoctorReport, LockFile, Manifest, Result, SkillCandidate, SourceBinding,
|
|
1
|
+
import type { DeploymentAction, DeploymentPlan, DeploymentTargetName, DoctorReport, LockFile, Manifest, Result, SkillCandidate, SourceBinding, WorkflowSummary } from "../domain/types.js";
|
|
2
2
|
import { StateStore } from "../state/store.js";
|
|
3
3
|
import { DeploymentApplier } from "./deployment-applier.js";
|
|
4
4
|
import { DeploymentPlanner } from "./deployment-planner.js";
|
|
@@ -6,7 +6,8 @@ import { DoctorService } from "./doctor-service.js";
|
|
|
6
6
|
import { InventoryService } from "./inventory-service.js";
|
|
7
7
|
import { SourceService } from "./source-service.js";
|
|
8
8
|
import { WorkflowService } from "./workflow-service.js";
|
|
9
|
-
import type { AddSourceOptions } from "./source-service.js";
|
|
9
|
+
import type { AddSourceOptions, SourceSnapshot } from "./source-service.js";
|
|
10
|
+
import { WorkspaceBootstrapService, type BootstrapEvent } from "./workspace-bootstrap-service.js";
|
|
10
11
|
export type DraftBinding = {
|
|
11
12
|
enabledTargets: DeploymentTargetName[];
|
|
12
13
|
selectedLeafIds: string[];
|
|
@@ -19,17 +20,8 @@ export declare class SkillFlowApp {
|
|
|
19
20
|
readonly applier: DeploymentApplier;
|
|
20
21
|
readonly doctorService: DoctorService;
|
|
21
22
|
readonly workflowService: WorkflowService;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
data: import("./source-service.js").SourceSnapshot;
|
|
25
|
-
warnings: Warning[];
|
|
26
|
-
errors: [];
|
|
27
|
-
} | {
|
|
28
|
-
ok: false;
|
|
29
|
-
data?: import("./source-service.js").SourceSnapshot;
|
|
30
|
-
warnings: Warning[];
|
|
31
|
-
errors: import("../domain/types.js").Failure[];
|
|
32
|
-
}>;
|
|
23
|
+
readonly workspaceBootstrapService: WorkspaceBootstrapService;
|
|
24
|
+
addSource(locator: string, options?: AddSourceOptions): Promise<Result<SourceSnapshot>>;
|
|
33
25
|
findSkills(query: string): Promise<Result<{
|
|
34
26
|
candidates: SkillCandidate[];
|
|
35
27
|
}>>;
|
|
@@ -41,6 +33,15 @@ export declare class SkillFlowApp {
|
|
|
41
33
|
lockFile: LockFile;
|
|
42
34
|
summaries: WorkflowSummary[];
|
|
43
35
|
}>>;
|
|
36
|
+
bootstrapWorkspaceState(onEvent?: (event: BootstrapEvent) => void): Promise<Result<{
|
|
37
|
+
availableTargets: DeploymentTargetName[];
|
|
38
|
+
manifest: Manifest;
|
|
39
|
+
lockFile: LockFile;
|
|
40
|
+
summaries: WorkflowSummary[];
|
|
41
|
+
initialDrafts: Record<string, DraftBinding>;
|
|
42
|
+
audit: DoctorReport;
|
|
43
|
+
importedSourceIds: string[];
|
|
44
|
+
}>>;
|
|
44
45
|
getAvailableTargets(): Promise<DeploymentTargetName[]>;
|
|
45
46
|
previewDraft(sourceId: string, draft: DraftBinding): Promise<Result<{
|
|
46
47
|
plan: DeploymentPlan;
|
|
@@ -89,4 +90,6 @@ export declare class SkillFlowApp {
|
|
|
89
90
|
private getQueryScore;
|
|
90
91
|
private getCandidateKey;
|
|
91
92
|
private formatSourceLabel;
|
|
93
|
+
private buildInitialDrafts;
|
|
94
|
+
private applySelectionModeForUpdatedSources;
|
|
92
95
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { createChannelAdapters } from "../adapters/channel-adapters.js";
|
|
3
4
|
import { StateStore } from "../state/store.js";
|
|
4
5
|
import { ensureDir, hashDirectory, pathExists, readJsonFile, removePath, writeJsonFile } from "../utils/fs.js";
|
|
@@ -14,6 +15,7 @@ import { DoctorService } from "./doctor-service.js";
|
|
|
14
15
|
import { InventoryService } from "./inventory-service.js";
|
|
15
16
|
import { SourceService } from "./source-service.js";
|
|
16
17
|
import { WorkflowService } from "./workflow-service.js";
|
|
18
|
+
import { WorkspaceBootstrapService, } from "./workspace-bootstrap-service.js";
|
|
17
19
|
export class SkillFlowApp {
|
|
18
20
|
store = new StateStore();
|
|
19
21
|
inventoryService = new InventoryService();
|
|
@@ -22,8 +24,10 @@ export class SkillFlowApp {
|
|
|
22
24
|
applier = new DeploymentApplier();
|
|
23
25
|
doctorService = new DoctorService();
|
|
24
26
|
workflowService = new WorkflowService();
|
|
27
|
+
workspaceBootstrapService = new WorkspaceBootstrapService(this.store);
|
|
25
28
|
async addSource(locator, options) {
|
|
26
|
-
const
|
|
29
|
+
const addOptions = options ?? {};
|
|
30
|
+
const result = await this.sourceService.addSource(locator, addOptions);
|
|
27
31
|
if (!result.ok) {
|
|
28
32
|
return result;
|
|
29
33
|
}
|
|
@@ -34,12 +38,29 @@ export class SkillFlowApp {
|
|
|
34
38
|
if (!source) {
|
|
35
39
|
return result;
|
|
36
40
|
}
|
|
41
|
+
source.selectionMode =
|
|
42
|
+
addOptions.selectionMode ??
|
|
43
|
+
(source.requestedPath ? "partial" : "all");
|
|
44
|
+
const enabledTargets = addOptions.enabledTargets ??
|
|
45
|
+
await this.getAvailableTargets();
|
|
37
46
|
manifest.bindings[source.id] = this.bindingFromDraft({
|
|
38
|
-
enabledTargets
|
|
47
|
+
enabledTargets,
|
|
39
48
|
selectedLeafIds: this.selectLeafIdsForRequestedPath(lockFile.leafInventory.filter((leaf) => leaf.sourceId === source.id), source.requestedPath),
|
|
40
49
|
});
|
|
41
50
|
await this.store.writeManifest(manifest);
|
|
42
|
-
|
|
51
|
+
if (addOptions.project === false) {
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
const plan = await this.planForAffectedSources(manifest, lockFile, source.id);
|
|
55
|
+
if (!plan.ok) {
|
|
56
|
+
return fail(plan.errors, [...result.warnings, ...plan.warnings]);
|
|
57
|
+
}
|
|
58
|
+
const applied = await this.applier.applyPlan(lockFile, plan.data.actions);
|
|
59
|
+
await this.store.writeLock(lockFile);
|
|
60
|
+
if (!applied.ok) {
|
|
61
|
+
return fail(applied.errors, [...result.warnings, ...plan.warnings, ...applied.warnings]);
|
|
62
|
+
}
|
|
63
|
+
return ok(result.data, [...result.warnings, ...plan.warnings, ...applied.warnings]);
|
|
43
64
|
}
|
|
44
65
|
async findSkills(query) {
|
|
45
66
|
await this.store.init();
|
|
@@ -157,6 +178,95 @@ export class SkillFlowApp {
|
|
|
157
178
|
summaries: this.workflowService.getSummaries(manifest, lockFile),
|
|
158
179
|
});
|
|
159
180
|
}
|
|
181
|
+
async bootstrapWorkspaceState(onEvent) {
|
|
182
|
+
onEvent?.({
|
|
183
|
+
phase: "detect-targets",
|
|
184
|
+
level: "info",
|
|
185
|
+
message: "Detecting available agent targets...",
|
|
186
|
+
});
|
|
187
|
+
const availableTargets = await this.getAvailableTargets();
|
|
188
|
+
await this.store.init();
|
|
189
|
+
let manifest = await this.store.readManifest();
|
|
190
|
+
let lockFile = await this.store.readLock();
|
|
191
|
+
const detected = await this.workspaceBootstrapService.detectUnmanagedExternalSkills(manifest, lockFile, onEvent);
|
|
192
|
+
const importedSourceIds = [];
|
|
193
|
+
if (detected.length > 0) {
|
|
194
|
+
onEvent?.({
|
|
195
|
+
phase: "import-unmanaged-skills",
|
|
196
|
+
level: "info",
|
|
197
|
+
message: `Importing ${detected.length} unmanaged skill${detected.length === 1 ? "" : "s"} into skill-flow...`,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
for (const item of detected) {
|
|
201
|
+
const imported = await this.addSource(item.path, {
|
|
202
|
+
enabledTargets: item.importedFromTargets,
|
|
203
|
+
selectionMode: "all",
|
|
204
|
+
project: false,
|
|
205
|
+
sourceIdOverride: item.sourceId,
|
|
206
|
+
displayNameOverride: item.displayName,
|
|
207
|
+
importedFromTargets: item.importedFromTargets,
|
|
208
|
+
importMode: "bootstrap-detected",
|
|
209
|
+
...(item.originLocator ? { originLocator: item.originLocator } : {}),
|
|
210
|
+
...(item.originRequestedPath ? { originRequestedPath: item.originRequestedPath } : {}),
|
|
211
|
+
...(item.originBranch ? { originBranch: item.originBranch } : {}),
|
|
212
|
+
});
|
|
213
|
+
if (!imported.ok) {
|
|
214
|
+
return fail(imported.errors, imported.warnings);
|
|
215
|
+
}
|
|
216
|
+
importedSourceIds.push(imported.data.manifest.id);
|
|
217
|
+
onEvent?.({
|
|
218
|
+
phase: "import-unmanaged-skills",
|
|
219
|
+
level: "success",
|
|
220
|
+
message: `Imported ${imported.data.manifest.displayName}.`,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
onEvent?.({
|
|
224
|
+
phase: "refresh-sources",
|
|
225
|
+
level: "info",
|
|
226
|
+
message: "Refreshing managed inventory...",
|
|
227
|
+
});
|
|
228
|
+
const reconciled = await this.sourceService.reconcileInventory(undefined, { force: true });
|
|
229
|
+
if (!reconciled.ok) {
|
|
230
|
+
return fail(reconciled.errors, reconciled.warnings);
|
|
231
|
+
}
|
|
232
|
+
manifest = await this.store.readManifest();
|
|
233
|
+
lockFile = await this.store.readLock();
|
|
234
|
+
onEvent?.({
|
|
235
|
+
phase: "normalize-bindings",
|
|
236
|
+
level: "info",
|
|
237
|
+
message: "Normalizing config state...",
|
|
238
|
+
});
|
|
239
|
+
await this.persistNormalizedBindings(manifest, lockFile);
|
|
240
|
+
onEvent?.({
|
|
241
|
+
phase: "audit-projections",
|
|
242
|
+
level: "info",
|
|
243
|
+
message: "Auditing current projections...",
|
|
244
|
+
});
|
|
245
|
+
const audit = await this.doctorService.run(manifest, lockFile);
|
|
246
|
+
if (!audit.ok) {
|
|
247
|
+
return fail(audit.errors, audit.warnings);
|
|
248
|
+
}
|
|
249
|
+
onEvent?.({
|
|
250
|
+
phase: "build-summaries",
|
|
251
|
+
level: "info",
|
|
252
|
+
message: "Building config summaries...",
|
|
253
|
+
});
|
|
254
|
+
const summaries = this.workflowService.getSummaries(manifest, lockFile, audit.data);
|
|
255
|
+
onEvent?.({
|
|
256
|
+
phase: "done",
|
|
257
|
+
level: "success",
|
|
258
|
+
message: "Config bootstrap complete.",
|
|
259
|
+
});
|
|
260
|
+
return ok({
|
|
261
|
+
availableTargets,
|
|
262
|
+
manifest,
|
|
263
|
+
lockFile,
|
|
264
|
+
summaries,
|
|
265
|
+
initialDrafts: this.buildInitialDrafts(summaries),
|
|
266
|
+
audit: audit.data,
|
|
267
|
+
importedSourceIds,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
160
270
|
async getAvailableTargets() {
|
|
161
271
|
const adapters = createChannelAdapters();
|
|
162
272
|
const availableTargets = [];
|
|
@@ -220,6 +330,7 @@ export class SkillFlowApp {
|
|
|
220
330
|
}
|
|
221
331
|
const manifest = await this.store.readManifest();
|
|
222
332
|
const lockFile = await this.store.readLock();
|
|
333
|
+
this.applySelectionModeForUpdatedSources(manifest, lockFile, updated.data.updated);
|
|
223
334
|
await this.persistNormalizedBindings(manifest, lockFile);
|
|
224
335
|
const activeSourceIds = manifest.sources
|
|
225
336
|
.map((source) => source.id)
|
|
@@ -339,6 +450,14 @@ export class SkillFlowApp {
|
|
|
339
450
|
}
|
|
340
451
|
prepareManifestForDraft(manifest, lockFile, sourceId, draft) {
|
|
341
452
|
manifest.bindings[sourceId] = this.bindingFromDraft(draft);
|
|
453
|
+
const source = manifest.sources.find((item) => item.id === sourceId);
|
|
454
|
+
if (source) {
|
|
455
|
+
const sourceLeafCount = lockFile.leafInventory.filter((leaf) => leaf.sourceId === sourceId).length;
|
|
456
|
+
source.selectionMode =
|
|
457
|
+
draft.selectedLeafIds.length >= sourceLeafCount && sourceLeafCount > 0
|
|
458
|
+
? "all"
|
|
459
|
+
: "partial";
|
|
460
|
+
}
|
|
342
461
|
const conflictingLeafIds = this.findExactDuplicateLeafSelections(manifest, lockFile, sourceId, draft.enabledTargets);
|
|
343
462
|
const normalizedDraft = {
|
|
344
463
|
enabledTargets: [...draft.enabledTargets],
|
|
@@ -605,11 +724,45 @@ export class SkillFlowApp {
|
|
|
605
724
|
return candidate.locator;
|
|
606
725
|
}
|
|
607
726
|
formatSourceLabel(locator, displayName) {
|
|
727
|
+
if (locator.startsWith("clawhub:")) {
|
|
728
|
+
return `${displayName}@clawhub`;
|
|
729
|
+
}
|
|
730
|
+
if (path.isAbsolute(locator)) {
|
|
731
|
+
return `${displayName}@local`;
|
|
732
|
+
}
|
|
608
733
|
const repo = parseGitHubRepo(locator);
|
|
609
734
|
if (!repo) {
|
|
610
735
|
return displayName;
|
|
611
736
|
}
|
|
612
|
-
return `${displayName}
|
|
737
|
+
return `${displayName}@${repo.owner}`;
|
|
738
|
+
}
|
|
739
|
+
buildInitialDrafts(summaries) {
|
|
740
|
+
return Object.fromEntries(summaries.map((summary) => {
|
|
741
|
+
const enabledTargets = Object.entries(summary.bindings.targets)
|
|
742
|
+
.filter(([, value]) => value?.enabled)
|
|
743
|
+
.map(([target]) => target);
|
|
744
|
+
const selectedLeafIds = [...new Set(enabledTargets.flatMap((target) => summary.bindings.targets[target]?.leafIds ?? []))];
|
|
745
|
+
return [summary.source.id, { enabledTargets, selectedLeafIds }];
|
|
746
|
+
}));
|
|
747
|
+
}
|
|
748
|
+
applySelectionModeForUpdatedSources(manifest, lockFile, updates) {
|
|
749
|
+
for (const update of updates) {
|
|
750
|
+
if (!update.changed || update.addedLeafIds.length === 0) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
const source = manifest.sources.find((item) => item.id === update.sourceId);
|
|
754
|
+
const binding = manifest.bindings[update.sourceId];
|
|
755
|
+
if (!source || !binding || source.selectionMode !== "all") {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
for (const targetBinding of Object.values(binding.targets)) {
|
|
759
|
+
if (!targetBinding?.enabled) {
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const merged = new Set([...targetBinding.leafIds, ...update.addedLeafIds]);
|
|
763
|
+
targetBinding.leafIds = [...merged].filter((leafId) => lockFile.leafInventory.some((leaf) => leaf.id === leafId && leaf.sourceId === update.sourceId));
|
|
764
|
+
}
|
|
765
|
+
}
|
|
613
766
|
}
|
|
614
767
|
}
|
|
615
768
|
//# sourceMappingURL=skill-flow.js.map
|