abelworkflow 1.0.0-rc.1 → 1.1.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.
- package/README.md +7 -8
- package/extensions/pi-gpt-responses-compat/index.ts +1 -1
- package/lib/cli/args.mjs +2 -2
- package/lib/cli/main.mjs +39 -41
- package/lib/cli/prompts.mjs +16 -29
- package/lib/installer/assets.mjs +13 -15
- package/lib/installer/install.mjs +18 -32
- package/lib/installer/state.mjs +0 -19
- package/lib/providers/claude.mjs +23 -214
- package/lib/providers/codex.mjs +14 -22
- package/lib/providers/pi.mjs +273 -58
- package/lib/providers/skills.mjs +39 -88
- package/lib/templates/workflow/AGENTS.md +10 -10
- package/lib/templates/workflow/commands/abel-design.md +175 -0
- package/lib/templates/workflow/commands/abel-implement.md +64 -46
- package/lib/templates/workflow/commands/abel-init.md +2 -4
- package/package.json +1 -7
- package/skills/grok-search/.env.example +3 -3
- package/skills/grok-search/SKILL.md +1 -1
- package/skills/grok-search/defaults.json +1 -1
- package/skills/grok-search/scripts/groksearch_cli.py +42 -15
- package/skills/prompt-enhancer/SKILL.md +2 -27
- package/lib/installer/render.mjs +0 -46
- package/lib/templates/workflow/commands/abel-plan.md +0 -82
- package/lib/templates/workflow/commands/abel-research.md +0 -126
- package/skills/prompt-enhancer/.env.example +0 -12
- package/skills/prompt-enhancer/ADVANCED.md +0 -103
- package/skills/prompt-enhancer/requirements.txt +0 -1
- package/skills/prompt-enhancer/scripts/_dotenv.py +0 -32
- package/skills/prompt-enhancer/scripts/enhance.py +0 -144
- package/skills/prompt-enhancer/scripts/prompt_enhancer_entry.py +0 -238
package/README.md
CHANGED
|
@@ -13,19 +13,18 @@ AbelWorkflow 为 Codex、Claude Code 和 Pi 部署统一的 Skills、工作流
|
|
|
13
13
|
| 命令 | 用途 |
|
|
14
14
|
| --- | --- |
|
|
15
15
|
| `/abel-init` | 初始化 OpenSpec 环境并验证工具链 |
|
|
16
|
-
| `/abel-
|
|
17
|
-
| `/abel-plan` | 将已确认变更细化为可执行计划 |
|
|
16
|
+
| `/abel-design` | 通过双闸门澄清生成可实施、可验证规格 |
|
|
18
17
|
| `/abel-implement` | 按强制 TDD 实施已批准变更 |
|
|
19
18
|
| `/abel-diagnose` | 进行根因分析、回归测试和批量修复 |
|
|
20
19
|
|
|
21
20
|
工作流:
|
|
22
21
|
|
|
23
22
|
```text
|
|
24
|
-
/abel-init → /abel-
|
|
25
|
-
|
|
23
|
+
/abel-init → /abel-design → /abel-implement(TDD)
|
|
24
|
+
↘ /abel-diagnose (bug fix)
|
|
26
25
|
```
|
|
27
26
|
|
|
28
|
-
OpenSpec 命令保持独立:`/opsx:
|
|
27
|
+
OpenSpec 命令保持独立:`/opsx:propose`、`/opsx:explore`、`/opsx:apply`、`/opsx:update`、`/opsx:sync`、`/opsx:archive`、`openspec view`、`openspec status`。
|
|
29
28
|
|
|
30
29
|
## 安装与更新
|
|
31
30
|
|
|
@@ -62,10 +61,10 @@ node .\bin\abelworkflow.mjs install --agents-dir "$HOME\.agents"
|
|
|
62
61
|
|
|
63
62
|
## 配置行为
|
|
64
63
|
|
|
65
|
-
- Claude
|
|
64
|
+
- Claude 配置默认写入 `bypassPermissions` 全权限模式(YOLO),第三方 API 仅使用 API Key 认证,不添加任何 MCP 权限。已有 permissions、deny、hooks 和 timeout 保持不变。
|
|
66
65
|
- Codex 只更新 AbelWorkflow 管理的认证字段,保留未知字段、其他 Provider 和用户 token。
|
|
67
|
-
- Pi 0.80.0
|
|
68
|
-
- Grok 默认模型统一为 `grok-4.20-
|
|
66
|
+
- Pi 0.80.0 及以上版本按无会话启动时实际选中的当前有效 Provider 配置 API,并将其 API Key 保存在 `~/.pi/agent/auth.json`;`models.json` 只保留模型定义。旧 models-only key 会按先写 auth、后删除旧 key 的顺序迁移。
|
|
67
|
+
- Grok 默认模型统一为 `grok-4.20-non-reasoning`。
|
|
69
68
|
- Context7 使用显式 CommonJS 入口 `context7-api.cjs`。
|
|
70
69
|
- dev-browser 发布运行时使用 Node ESM 编译产物,入口为 `node dist/scripts/start.js`,不依赖 Bun 或运行时 `npx tsx`。
|
|
71
70
|
- skill 密钥写入 `~/.agents/skills/<skill>/.env`;这些文件属于私密配置。
|
|
@@ -21,7 +21,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
21
21
|
pi.on("before_provider_request", (event, ctx) => {
|
|
22
22
|
const payload = event.payload as any;
|
|
23
23
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return;
|
|
24
|
-
if (ctx.model?.
|
|
24
|
+
if (ctx.model?.api !== "openai-responses") return;
|
|
25
25
|
if (!("input" in payload)) return;
|
|
26
26
|
|
|
27
27
|
const input = Array.isArray(payload.input) ? payload.input : [];
|
package/lib/cli/args.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function parseArgs(argv, { defaultAgentsDir, resolvePath }) {
|
|
1
|
+
function parseArgs(argv, { defaultAgentsDir, resolvePath, env = process.env }) {
|
|
2
2
|
const options = {
|
|
3
3
|
agentsDir: defaultAgentsDir,
|
|
4
4
|
force: false,
|
|
@@ -60,7 +60,7 @@ function parseArgs(argv, { defaultAgentsDir, resolvePath }) {
|
|
|
60
60
|
if (options.command !== "install" && (options.force || options.relinkOnly || options.agentsDir !== defaultAgentsDir)) {
|
|
61
61
|
throw new Error("`--force`、`--link-only`、`--agents-dir` 仅能与 `install` 命令一起使用");
|
|
62
62
|
}
|
|
63
|
-
if (!options.nonInteractive &&
|
|
63
|
+
if (!options.nonInteractive && env.CI) options.nonInteractive = true;
|
|
64
64
|
return options;
|
|
65
65
|
}
|
|
66
66
|
|
package/lib/cli/main.mjs
CHANGED
|
@@ -6,28 +6,23 @@ import { assertInteractiveMenuSupported, parseArgs } from "./args.mjs";
|
|
|
6
6
|
import * as promptApi from "./prompts.mjs";
|
|
7
7
|
import {
|
|
8
8
|
ensurePiResourcesLinked,
|
|
9
|
-
|
|
9
|
+
ensureSkillPresent,
|
|
10
10
|
installManagedWorkflow,
|
|
11
11
|
packageVersionFor
|
|
12
12
|
} from "../installer/install.mjs";
|
|
13
13
|
import {
|
|
14
14
|
finalizeProviderInstallMetadata,
|
|
15
|
-
getPreviousClaudePermissionProfile,
|
|
16
15
|
getPreviousManagedCodexAgentFiles,
|
|
17
16
|
getPreviousManagedCodexAuthKeys,
|
|
18
17
|
readInstallMetadata,
|
|
19
18
|
writeInstallMetadata
|
|
20
19
|
} from "../installer/state.mjs";
|
|
21
|
-
import {
|
|
22
|
-
configureClaudeApi,
|
|
23
|
-
getPreviousManagedClaudePermissions
|
|
24
|
-
} from "../providers/claude.mjs";
|
|
20
|
+
import { configureClaudeApi } from "../providers/claude.mjs";
|
|
25
21
|
import { configureCodexApi } from "../providers/codex.mjs";
|
|
26
22
|
import { configurePiApi } from "../providers/pi.mjs";
|
|
27
23
|
import {
|
|
28
24
|
configureContext7Env,
|
|
29
|
-
configureGrokSearchEnv
|
|
30
|
-
configurePromptEnhancerEnv
|
|
25
|
+
configureGrokSearchEnv
|
|
31
26
|
} from "../providers/skills.mjs";
|
|
32
27
|
import { commandExists, installCliTool } from "../tools/cli-installer.mjs";
|
|
33
28
|
import { createPaths, defaultPaths, pathToLabel } from "../paths.mjs";
|
|
@@ -36,11 +31,8 @@ const {
|
|
|
36
31
|
buildCliToolMenuDescriptors,
|
|
37
32
|
CancelledError,
|
|
38
33
|
confirmOrCancel,
|
|
39
|
-
getClaudePermissionProfilePromptOptions,
|
|
40
|
-
getAugmentContextEnginePromptOptions,
|
|
41
34
|
interactiveMenuDefaultValue,
|
|
42
|
-
interactiveMenuDescriptors
|
|
43
|
-
selectOrCancel
|
|
35
|
+
interactiveMenuDescriptors
|
|
44
36
|
} = promptApi;
|
|
45
37
|
|
|
46
38
|
const installReportSummaryFields = [
|
|
@@ -66,6 +58,19 @@ function presentInstallReport(report, reportOutput) {
|
|
|
66
58
|
.map(([label, field]) => `${label} ${pathsByField[field].length}`)
|
|
67
59
|
.join(",")}`);
|
|
68
60
|
|
|
61
|
+
for (const [message, field] of [
|
|
62
|
+
["已更新", "updated"],
|
|
63
|
+
["已跳过", "unchanged"]
|
|
64
|
+
]) {
|
|
65
|
+
const paths = pathsByField[field];
|
|
66
|
+
if (paths.length > 0) {
|
|
67
|
+
reportOutput.message([
|
|
68
|
+
`${message} ${paths.length} 个${field === "unchanged" ? "未变化" : ""}文件:`,
|
|
69
|
+
...paths.map((path) => `- ${path}`)
|
|
70
|
+
].join("\n"));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
const conflictPaths = pathsByField.conflicts;
|
|
70
75
|
const conflictSet = new Set(conflictPaths);
|
|
71
76
|
const preservedPaths = pathsByField.preserved.filter((path) => !conflictSet.has(path));
|
|
@@ -90,6 +95,18 @@ async function runManagedInstall(options) {
|
|
|
90
95
|
return report;
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
async function ensureSkillPresentWithReport(paths, skillName) {
|
|
99
|
+
const report = await ensureSkillPresent(paths, skillName);
|
|
100
|
+
presentInstallReport(report, p.log);
|
|
101
|
+
return report;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function ensurePiResourcesLinkedWithReport(paths) {
|
|
105
|
+
const report = await ensurePiResourcesLinked(paths);
|
|
106
|
+
presentInstallReport(report, p.log);
|
|
107
|
+
return report;
|
|
108
|
+
}
|
|
109
|
+
|
|
93
110
|
function printHelp() {
|
|
94
111
|
console.log(`${c.bold("AbelWorkflow")} ${c.cyan("installer")}
|
|
95
112
|
|
|
@@ -129,33 +146,18 @@ async function configureCodexForPaths(paths) {
|
|
|
129
146
|
async function configureClaudeForPaths(paths) {
|
|
130
147
|
const metadata = await readInstallMetadata(paths);
|
|
131
148
|
const packageVersion = await packageVersionFor(paths, metadata);
|
|
132
|
-
|
|
133
|
-
getPreviousClaudePermissionProfile(metadata)
|
|
134
|
-
));
|
|
135
|
-
const result = await configureClaudeApi(paths, promptApi, {
|
|
136
|
-
permissionProfile,
|
|
137
|
-
previousManagedPermissions: getPreviousManagedClaudePermissions(metadata)
|
|
138
|
-
});
|
|
149
|
+
await configureClaudeApi(paths, promptApi);
|
|
139
150
|
await writeInstallMetadata(paths, finalizeProviderInstallMetadata({
|
|
140
151
|
previousMetadata: metadata,
|
|
141
|
-
packageVersion
|
|
142
|
-
overrides: {
|
|
143
|
-
claudePermissionProfile: result.permissionProfile,
|
|
144
|
-
managedClaudePermissions: result.managedPermissions
|
|
145
|
-
}
|
|
152
|
+
packageVersion
|
|
146
153
|
}));
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
async function runFullInit(options) {
|
|
150
|
-
const augmentContextEngine = options.nonInteractive
|
|
151
|
-
? false
|
|
152
|
-
: await confirmOrCancel(getAugmentContextEnginePromptOptions());
|
|
153
|
-
|
|
154
157
|
const installReport = await runManagedInstall({
|
|
155
158
|
paths: options.paths,
|
|
156
159
|
force: options.force,
|
|
157
|
-
relinkOnly: false
|
|
158
|
-
featureOverrides: { augmentContextEngine }
|
|
160
|
+
relinkOnly: false
|
|
159
161
|
});
|
|
160
162
|
|
|
161
163
|
if (await confirmOrCancel({ message: "是否安装或更新 Claude Code CLI?", initialValue: false })) {
|
|
@@ -173,17 +175,14 @@ async function runFullInit(options) {
|
|
|
173
175
|
if (await confirmOrCancel({ message: "是否安装或更新 Pi CLI?", initialValue: false })) {
|
|
174
176
|
await installCliTool("pi", promptApi);
|
|
175
177
|
}
|
|
176
|
-
if (await confirmOrCancel({ message: "是否配置 Pi
|
|
178
|
+
if (await confirmOrCancel({ message: "是否配置 Pi 当前有效 Provider API?", initialValue: commandExists("pi") })) {
|
|
177
179
|
await configurePiApi(options.paths, ensurePiResourcesLinked, promptApi);
|
|
178
180
|
}
|
|
179
181
|
if (await confirmOrCancel({ message: "是否填写 grok-search 环境变量?", initialValue: false })) {
|
|
180
|
-
await configureGrokSearchEnv(options.paths,
|
|
182
|
+
await configureGrokSearchEnv(options.paths, (paths) => ensureSkillPresent(paths, "grok-search"), promptApi);
|
|
181
183
|
}
|
|
182
184
|
if (await confirmOrCancel({ message: "是否填写 context7-auto-research 环境变量?", initialValue: false })) {
|
|
183
|
-
await configureContext7Env(options.paths,
|
|
184
|
-
}
|
|
185
|
-
if (await confirmOrCancel({ message: "是否填写 prompt-enhancer 环境变量?", initialValue: false })) {
|
|
186
|
-
await configurePromptEnhancerEnv(options.paths, ensureWorkflowPresent, promptApi);
|
|
185
|
+
await configureContext7Env(options.paths, (paths) => ensureSkillPresent(paths, "context7-auto-research"), promptApi);
|
|
187
186
|
}
|
|
188
187
|
|
|
189
188
|
const conflictCount = uniqueReportPaths(installReport.conflicts).length;
|
|
@@ -211,7 +210,7 @@ async function runInteractiveMenu(options) {
|
|
|
211
210
|
title: "Pi",
|
|
212
211
|
actions: {
|
|
213
212
|
"pi-install": async () => installCliTool("pi", promptApi),
|
|
214
|
-
"pi-api": async () => configurePiApi(options.paths,
|
|
213
|
+
"pi-api": async () => configurePiApi(options.paths, ensurePiResourcesLinkedWithReport, promptApi)
|
|
215
214
|
}
|
|
216
215
|
},
|
|
217
216
|
"codex-cli": {
|
|
@@ -236,7 +235,7 @@ async function runInteractiveMenu(options) {
|
|
|
236
235
|
const choice = await p.select({
|
|
237
236
|
message: `请选择 ${title} 操作`,
|
|
238
237
|
options: buildCliToolMenuDescriptors(tool).map(buildOption),
|
|
239
|
-
initialValue: `${tool}-
|
|
238
|
+
initialValue: `${tool}-api`
|
|
240
239
|
});
|
|
241
240
|
|
|
242
241
|
if (p.isCancel(choice) || choice === "back") {
|
|
@@ -258,9 +257,8 @@ async function runInteractiveMenu(options) {
|
|
|
258
257
|
force: options.force,
|
|
259
258
|
relinkOnly: options.relinkOnly
|
|
260
259
|
}),
|
|
261
|
-
"grok-search": async () => configureGrokSearchEnv(options.paths,
|
|
262
|
-
context7: async () => configureContext7Env(options.paths,
|
|
263
|
-
"prompt-enhancer": async () => configurePromptEnhancerEnv(options.paths, ensureWorkflowPresent, promptApi),
|
|
260
|
+
"grok-search": async () => configureGrokSearchEnv(options.paths, (paths) => ensureSkillPresentWithReport(paths, "grok-search"), promptApi),
|
|
261
|
+
context7: async () => configureContext7Env(options.paths, (paths) => ensureSkillPresentWithReport(paths, "context7-auto-research"), promptApi),
|
|
264
262
|
"pi-cli": async () => runCliToolMenu(cliToolMenus["pi-cli"]),
|
|
265
263
|
"codex-cli": async () => runCliToolMenu(cliToolMenus["codex-cli"]),
|
|
266
264
|
"claude-cli": async () => runCliToolMenu(cliToolMenus["claude-cli"])
|
package/lib/cli/prompts.mjs
CHANGED
|
@@ -5,35 +5,29 @@ const interactiveMenuDescriptors = [
|
|
|
5
5
|
{ value: "install", label: "仅同步工作流", group: "main" },
|
|
6
6
|
{ value: "grok-search", label: "配置 grok-search", hint: "技能", group: "skill" },
|
|
7
7
|
{ value: "context7", label: "配置 context7-auto-research", hint: "技能", group: "skill" },
|
|
8
|
-
{ value: "
|
|
9
|
-
{ value: "
|
|
10
|
-
{ value: "
|
|
11
|
-
{ value: "claude-cli", label: "安装/配置 Claude Code", hint: "CLI", group: "cli" },
|
|
8
|
+
{ value: "pi-cli", label: "配置/安装 Pi", hint: "CLI", group: "cli" },
|
|
9
|
+
{ value: "codex-cli", label: "配置/安装 Codex", hint: "CLI", group: "cli" },
|
|
10
|
+
{ value: "claude-cli", label: "配置/安装 Claude Code", hint: "CLI", group: "cli" },
|
|
12
11
|
{ value: "exit", label: "退出", group: "exit" }
|
|
13
12
|
];
|
|
14
13
|
const interactiveMenuDefaultValue = "full-init";
|
|
15
14
|
const cliToolMenuDescriptorMap = {
|
|
16
15
|
pi: [
|
|
17
|
-
{ value: "pi-install", label: "安装/更新 Pi" },
|
|
18
16
|
{ value: "pi-api", label: "配置 Pi API" },
|
|
17
|
+
{ value: "pi-install", label: "安装/更新 Pi" },
|
|
19
18
|
{ value: "back", label: "返回上一级" }
|
|
20
19
|
],
|
|
21
20
|
codex: [
|
|
22
|
-
{ value: "codex-install", label: "安装/更新 Codex" },
|
|
23
21
|
{ value: "codex-api", label: "配置 Codex API" },
|
|
22
|
+
{ value: "codex-install", label: "安装/更新 Codex" },
|
|
24
23
|
{ value: "back", label: "返回上一级" }
|
|
25
24
|
],
|
|
26
25
|
claude: [
|
|
27
|
-
{ value: "claude-install", label: "安装/更新 Claude Code" },
|
|
28
26
|
{ value: "claude-api", label: "配置 Claude Code API" },
|
|
27
|
+
{ value: "claude-install", label: "安装/更新 Claude Code" },
|
|
29
28
|
{ value: "back", label: "返回上一级" }
|
|
30
29
|
]
|
|
31
30
|
};
|
|
32
|
-
const augmentContextEngineFeaturePrompt = {
|
|
33
|
-
message: "是否启用 augment-context-engine MCP 代码检索支持?不确定建议选否,可减少 MCP 安装和配置麻烦。",
|
|
34
|
-
initialValue: false
|
|
35
|
-
};
|
|
36
|
-
|
|
37
31
|
class CancelledError extends Error {
|
|
38
32
|
constructor(message = "用户取消") {
|
|
39
33
|
super(message);
|
|
@@ -72,6 +66,15 @@ function resolvePasswordValue(userInput, existingValue) {
|
|
|
72
66
|
return existingValue || undefined;
|
|
73
67
|
}
|
|
74
68
|
|
|
69
|
+
function passwordPromptOptions(message, existingValue, validate) {
|
|
70
|
+
const configured = typeof existingValue === "string" && existingValue.trim() !== "";
|
|
71
|
+
return {
|
|
72
|
+
message: `${message}${configured ? "(已配置,直接回车保留;输入 - 清除)" : "(输入 - 清除)"}`,
|
|
73
|
+
mask: "*",
|
|
74
|
+
...(validate ? { validate } : {})
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
75
78
|
function assertNotCancelled(value) {
|
|
76
79
|
if (p.isCancel(value)) throw new CancelledError();
|
|
77
80
|
}
|
|
@@ -88,30 +91,14 @@ async function selectOrCancel(options) {
|
|
|
88
91
|
return value;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
function getAugmentContextEnginePromptOptions() {
|
|
92
|
-
return { ...augmentContextEngineFeaturePrompt };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function getClaudePermissionProfilePromptOptions(initialValue = "standard") {
|
|
96
|
-
return {
|
|
97
|
-
message: "请选择 Claude Code 权限配置档",
|
|
98
|
-
options: [
|
|
99
|
-
{ value: "standard", label: "standard(默认,不预授权 Bash/Write/Edit)" },
|
|
100
|
-
{ value: "trusted", label: "trusted(显式授予完整工具权限)" }
|
|
101
|
-
],
|
|
102
|
-
initialValue: initialValue === "trusted" ? "trusted" : "standard"
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
94
|
export {
|
|
107
95
|
assertNotCancelled,
|
|
108
96
|
buildCliToolMenuDescriptors,
|
|
109
97
|
CancelledError,
|
|
110
98
|
confirmOrCancel,
|
|
111
|
-
getClaudePermissionProfilePromptOptions,
|
|
112
|
-
getAugmentContextEnginePromptOptions,
|
|
113
99
|
interactiveMenuDefaultValue,
|
|
114
100
|
interactiveMenuDescriptors,
|
|
101
|
+
passwordPromptOptions,
|
|
115
102
|
required,
|
|
116
103
|
requiredUnlessExisting,
|
|
117
104
|
resolvePasswordValue,
|
package/lib/installer/assets.mjs
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
readJsonFileSafe,
|
|
9
9
|
writeText
|
|
10
10
|
} from "../config/store.mjs";
|
|
11
|
-
import { renderWorkflowTemplate } from "./render.mjs";
|
|
12
11
|
import { createInstallReport, readInstallMetadata } from "./state.mjs";
|
|
13
12
|
|
|
14
13
|
const ignoredSkillPathPatterns = [
|
|
@@ -81,7 +80,7 @@ function shouldCopySkillPath(skillsRoot, sourcePath) {
|
|
|
81
80
|
return !ignoredSkillPathPatterns.some((pattern) => pattern.test(relativePath));
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
async function collectFiles(root, outputPrefix, { filter, mapTargetPath
|
|
83
|
+
async function collectFiles(root, outputPrefix, { filter, mapTargetPath } = {}) {
|
|
85
84
|
const assets = new Map();
|
|
86
85
|
async function visit(directory) {
|
|
87
86
|
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
@@ -97,17 +96,14 @@ async function collectFiles(root, outputPrefix, { filter, mapTargetPath, render
|
|
|
97
96
|
const sourceRelativePath = normalizeRelativePath(relative(root, sourcePath));
|
|
98
97
|
if (sourceRelativePath.endsWith(".skill-lock.json")) continue;
|
|
99
98
|
const sourceContent = await fs.readFile(sourcePath);
|
|
100
|
-
const renderedContent = render
|
|
101
|
-
? Buffer.from(render(sourceContent.toString("utf8"), sourceRelativePath))
|
|
102
|
-
: sourceContent;
|
|
103
99
|
const sourceStat = await fs.stat(sourcePath);
|
|
104
100
|
const targetRelativePath = normalizeRelativePath(join(
|
|
105
101
|
outputPrefix,
|
|
106
102
|
mapTargetPath ? mapTargetPath(sourceRelativePath) : sourceRelativePath
|
|
107
103
|
));
|
|
108
104
|
assets.set(targetRelativePath, {
|
|
109
|
-
content:
|
|
110
|
-
hash: hashBytes(
|
|
105
|
+
content: sourceContent,
|
|
106
|
+
hash: hashBytes(sourceContent),
|
|
111
107
|
sourceHash: hashBytes(sourceContent),
|
|
112
108
|
mode: sourceStat.mode & 0o777,
|
|
113
109
|
sourcePath
|
|
@@ -126,12 +122,11 @@ async function addAssetTree(manifest, root, outputPrefix, options = {}) {
|
|
|
126
122
|
for (const [key, value] of await collectFiles(root, outputPrefix, options)) manifest.set(key, value);
|
|
127
123
|
}
|
|
128
124
|
|
|
129
|
-
async function collectManagedAssets(paths
|
|
125
|
+
async function collectManagedAssets(paths) {
|
|
130
126
|
const manifest = new Map();
|
|
131
127
|
await addAssetTree(manifest, paths.workflowTemplateRoot, "", {
|
|
132
128
|
required: true,
|
|
133
|
-
mapTargetPath: mapGitignoreTemplate
|
|
134
|
-
render: (content) => renderWorkflowTemplate(content, featureState)
|
|
129
|
+
mapTargetPath: mapGitignoreTemplate
|
|
135
130
|
});
|
|
136
131
|
|
|
137
132
|
const readmePath = join(paths.packageRoot, "README.md");
|
|
@@ -258,16 +253,17 @@ async function readPackageVersion(paths) {
|
|
|
258
253
|
|
|
259
254
|
async function syncManagedFiles({
|
|
260
255
|
paths,
|
|
261
|
-
|
|
262
|
-
|
|
256
|
+
force = false,
|
|
257
|
+
pathPrefix = null
|
|
263
258
|
}) {
|
|
264
259
|
const previousMetadata = await readInstallMetadata(paths);
|
|
265
260
|
const previousManagedFiles = await readManagedFiles(paths, previousMetadata);
|
|
266
|
-
const manifest = await collectManagedAssets(paths
|
|
261
|
+
const manifest = await collectManagedAssets(paths);
|
|
267
262
|
const report = createInstallReport();
|
|
268
|
-
const managedFiles = {};
|
|
263
|
+
const managedFiles = pathPrefix ? { ...previousManagedFiles } : {};
|
|
269
264
|
|
|
270
265
|
for (const [relativePath, asset] of manifest) {
|
|
266
|
+
if (pathPrefix && !relativePath.startsWith(pathPrefix)) continue;
|
|
271
267
|
const managedHash = await applyManifestEntry(
|
|
272
268
|
paths,
|
|
273
269
|
relativePath,
|
|
@@ -277,18 +273,20 @@ async function syncManagedFiles({
|
|
|
277
273
|
report
|
|
278
274
|
);
|
|
279
275
|
if (managedHash) managedFiles[relativePath] = managedHash;
|
|
276
|
+
else delete managedFiles[relativePath];
|
|
280
277
|
}
|
|
281
278
|
for (const [relativePath, previousHash] of Object.entries(previousManagedFiles)) {
|
|
279
|
+
if (pathPrefix && !relativePath.startsWith(pathPrefix)) continue;
|
|
282
280
|
if (!manifest.has(relativePath)) {
|
|
283
281
|
const managedHash = await removeStaleManagedFile(paths, relativePath, previousHash, force, report);
|
|
284
282
|
if (managedHash) managedFiles[relativePath] = managedHash;
|
|
283
|
+
else delete managedFiles[relativePath];
|
|
285
284
|
}
|
|
286
285
|
}
|
|
287
286
|
|
|
288
287
|
return {
|
|
289
288
|
previousMetadata,
|
|
290
289
|
packageVersion: await readPackageVersion(paths),
|
|
291
|
-
features: featureState,
|
|
292
290
|
managedFiles,
|
|
293
291
|
report
|
|
294
292
|
};
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { pathExists, readJsonFileSafe } from "../config/store.mjs";
|
|
3
3
|
import { createPaths, defaultPaths } from "../paths.mjs";
|
|
4
|
-
import {
|
|
5
|
-
ensureClaudeSettingsForFeature,
|
|
6
|
-
getPreviousManagedClaudePermissions
|
|
7
|
-
} from "../providers/claude.mjs";
|
|
8
4
|
import { readManagedFiles, syncManagedFiles } from "./assets.mjs";
|
|
9
5
|
import { linkClaude, linkCodex, linkPi } from "./links.mjs";
|
|
10
|
-
import { resolveAugmentContextEngineFeature } from "./render.mjs";
|
|
11
6
|
import {
|
|
12
7
|
buildInstallMetadata,
|
|
13
8
|
createInstallReport,
|
|
@@ -41,18 +36,6 @@ function resolveInstallPaths(options = {}) {
|
|
|
41
36
|
});
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
function resolveFeatureState(options, previousMetadata) {
|
|
45
|
-
const overrides = {
|
|
46
|
-
...(options.featureOverrides ?? {})
|
|
47
|
-
};
|
|
48
|
-
return {
|
|
49
|
-
...(previousMetadata.features && typeof previousMetadata.features === "object"
|
|
50
|
-
? previousMetadata.features
|
|
51
|
-
: {}),
|
|
52
|
-
augmentContextEngine: resolveAugmentContextEngineFeature(overrides, previousMetadata)
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
39
|
function reportFromLinkResults(results) {
|
|
57
40
|
const report = createInstallReport();
|
|
58
41
|
for (const result of results) {
|
|
@@ -86,7 +69,6 @@ async function packageVersionFor(paths, previousMetadata) {
|
|
|
86
69
|
async function installWorkflow(options) {
|
|
87
70
|
const paths = validatedPaths(options.paths);
|
|
88
71
|
const previousMetadata = await readInstallMetadata(paths);
|
|
89
|
-
const featureState = resolveFeatureState(options, previousMetadata);
|
|
90
72
|
let assetResult;
|
|
91
73
|
|
|
92
74
|
if (options.relinkOnly) {
|
|
@@ -96,14 +78,12 @@ async function installWorkflow(options) {
|
|
|
96
78
|
assetResult = {
|
|
97
79
|
previousMetadata,
|
|
98
80
|
packageVersion: await packageVersionFor(paths, previousMetadata),
|
|
99
|
-
features: featureState,
|
|
100
81
|
managedFiles: await readManagedFiles(paths, previousMetadata),
|
|
101
82
|
report: createInstallReport()
|
|
102
83
|
};
|
|
103
84
|
} else {
|
|
104
85
|
assetResult = await syncManagedFiles({
|
|
105
86
|
paths,
|
|
106
|
-
featureState,
|
|
107
87
|
force: options.force ?? false
|
|
108
88
|
});
|
|
109
89
|
}
|
|
@@ -114,16 +94,12 @@ async function installWorkflow(options) {
|
|
|
114
94
|
const codexResults = await linkCodex(paths, previousLinkedTargets, linkOptions);
|
|
115
95
|
const piResults = await linkPi(paths, previousLinkedTargets, linkOptions);
|
|
116
96
|
const linkResults = [...claudeResults, ...codexResults, ...piResults];
|
|
117
|
-
const ensureClaudeSettings = options.ensureClaudeSettings ?? ensureClaudeSettingsForFeature;
|
|
118
|
-
const managedClaudePermissions = await ensureClaudeSettings(paths, featureState.augmentContextEngine, previousMetadata);
|
|
119
97
|
const linkedTargets = mergeLinkedTargets(previousLinkedTargets, linkResults);
|
|
120
98
|
|
|
121
99
|
const metadata = buildInstallMetadata({
|
|
122
100
|
previousMetadata,
|
|
123
101
|
packageVersion: assetResult.packageVersion,
|
|
124
|
-
features: featureState,
|
|
125
102
|
managedFiles: assetResult.managedFiles,
|
|
126
|
-
managedClaudePermissions,
|
|
127
103
|
linkedTargets
|
|
128
104
|
});
|
|
129
105
|
await writeInstallMetadata(paths, metadata);
|
|
@@ -134,33 +110,43 @@ async function installManagedWorkflow(options = {}) {
|
|
|
134
110
|
return installWorkflow({ ...options, paths: resolveInstallPaths(options) });
|
|
135
111
|
}
|
|
136
112
|
|
|
137
|
-
async function
|
|
113
|
+
async function ensureSkillPresent(inputPaths, skillName) {
|
|
138
114
|
const paths = validatedPaths(inputPaths);
|
|
139
|
-
|
|
115
|
+
const previousMetadata = await readInstallMetadata(paths);
|
|
116
|
+
const assetResult = await syncManagedFiles({
|
|
117
|
+
paths,
|
|
118
|
+
force: false,
|
|
119
|
+
pathPrefix: `skills/${skillName}/`
|
|
120
|
+
});
|
|
121
|
+
const metadata = buildInstallMetadata({
|
|
122
|
+
previousMetadata,
|
|
123
|
+
packageVersion: assetResult.packageVersion,
|
|
124
|
+
managedFiles: assetResult.managedFiles,
|
|
125
|
+
linkedTargets: previousMetadata.linkedTargets ?? {}
|
|
126
|
+
});
|
|
127
|
+
await writeInstallMetadata(paths, metadata);
|
|
128
|
+
return assetResult.report;
|
|
140
129
|
}
|
|
141
130
|
|
|
142
131
|
async function ensurePiResourcesLinked(inputPaths) {
|
|
143
132
|
const paths = validatedPaths(inputPaths);
|
|
144
133
|
const previousMetadata = await readInstallMetadata(paths);
|
|
145
|
-
const
|
|
146
|
-
const assetResult = await syncManagedFiles({ paths, featureState, force: false });
|
|
134
|
+
const assetResult = await syncManagedFiles({ paths, force: false });
|
|
147
135
|
const previousLinkedTargets = previousMetadata.linkedTargets ?? {};
|
|
148
136
|
const piResults = await linkPi(paths, previousLinkedTargets, { force: false });
|
|
149
137
|
const metadata = buildInstallMetadata({
|
|
150
138
|
previousMetadata,
|
|
151
139
|
packageVersion: assetResult.packageVersion,
|
|
152
|
-
features: featureState,
|
|
153
140
|
managedFiles: assetResult.managedFiles,
|
|
154
|
-
managedClaudePermissions: getPreviousManagedClaudePermissions(previousMetadata),
|
|
155
141
|
linkedTargets: mergeLinkedTargets(previousLinkedTargets, piResults)
|
|
156
142
|
});
|
|
157
143
|
await writeInstallMetadata(paths, metadata);
|
|
158
|
-
return piResults;
|
|
144
|
+
return mergeInstallReports(assetResult.report, reportFromLinkResults(piResults));
|
|
159
145
|
}
|
|
160
146
|
|
|
161
147
|
export {
|
|
162
148
|
ensurePiResourcesLinked,
|
|
163
|
-
|
|
149
|
+
ensureSkillPresent,
|
|
164
150
|
installManagedWorkflow,
|
|
165
151
|
installWorkflow,
|
|
166
152
|
packageVersionFor
|
package/lib/installer/state.mjs
CHANGED
|
@@ -43,12 +43,6 @@ function getPreviousManagedCodexAgentFiles(previousMetadata = {}) {
|
|
|
43
43
|
return normalizeManagedCodexAgentFiles(previousMetadata.managedCodexAgentFiles);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function getPreviousClaudePermissionProfile(previousMetadata = {}) {
|
|
47
|
-
return isMetadataV2(previousMetadata) && previousMetadata.claudePermissionProfile === "trusted"
|
|
48
|
-
? "trusted"
|
|
49
|
-
: "standard";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
46
|
async function readInstallMetadata(paths) {
|
|
53
47
|
const metadata = await readJsonFileSafe(metadataPathFor(paths), {});
|
|
54
48
|
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
@@ -64,23 +58,17 @@ async function writeInstallMetadata(paths, metadata) {
|
|
|
64
58
|
function buildInstallMetadata({
|
|
65
59
|
previousMetadata = {},
|
|
66
60
|
packageVersion,
|
|
67
|
-
features = {},
|
|
68
61
|
managedFiles = {},
|
|
69
|
-
managedClaudePermissions = [],
|
|
70
62
|
managedCodexAuthKeys = getPreviousManagedCodexAuthKeys(previousMetadata),
|
|
71
63
|
managedCodexAgentFiles = getPreviousManagedCodexAgentFiles(previousMetadata),
|
|
72
|
-
claudePermissionProfile = getPreviousClaudePermissionProfile(previousMetadata),
|
|
73
64
|
linkedTargets = {}
|
|
74
65
|
}) {
|
|
75
66
|
const metadata = {
|
|
76
67
|
schemaVersion: 2,
|
|
77
68
|
packageVersion,
|
|
78
|
-
features: sortObject(features),
|
|
79
69
|
managedFiles: sortObject(managedFiles),
|
|
80
|
-
managedClaudePermissions: [...new Set(managedClaudePermissions)].sort(),
|
|
81
70
|
managedCodexAuthKeys: [...new Set(managedCodexAuthKeys)].sort(),
|
|
82
71
|
managedCodexAgentFiles: normalizeManagedCodexAgentFiles(managedCodexAgentFiles),
|
|
83
|
-
claudePermissionProfile,
|
|
84
72
|
linkedTargets: sortObject(linkedTargets)
|
|
85
73
|
};
|
|
86
74
|
if (typeof previousMetadata.installedAt === "string") {
|
|
@@ -102,18 +90,12 @@ function finalizeProviderInstallMetadata({
|
|
|
102
90
|
&& previousMetadata.packageVersion
|
|
103
91
|
? previousMetadata.packageVersion
|
|
104
92
|
: packageVersion,
|
|
105
|
-
features: objectField(previousMetadata.features),
|
|
106
93
|
managedFiles: isV2 ? objectField(previousMetadata.managedFiles) : {},
|
|
107
|
-
managedClaudePermissions: stringArray(
|
|
108
|
-
overrides.managedClaudePermissions ?? previousMetadata.managedClaudePermissions
|
|
109
|
-
),
|
|
110
94
|
managedCodexAuthKeys: stringArray(
|
|
111
95
|
overrides.managedCodexAuthKeys ?? getPreviousManagedCodexAuthKeys(previousMetadata)
|
|
112
96
|
),
|
|
113
97
|
managedCodexAgentFiles: overrides.managedCodexAgentFiles
|
|
114
98
|
?? getPreviousManagedCodexAgentFiles(previousMetadata),
|
|
115
|
-
claudePermissionProfile: overrides.claudePermissionProfile
|
|
116
|
-
?? getPreviousClaudePermissionProfile(previousMetadata),
|
|
117
99
|
linkedTargets: objectField(previousMetadata.linkedTargets)
|
|
118
100
|
});
|
|
119
101
|
}
|
|
@@ -170,7 +152,6 @@ export {
|
|
|
170
152
|
buildInstallMetadata,
|
|
171
153
|
createInstallReport,
|
|
172
154
|
finalizeProviderInstallMetadata,
|
|
173
|
-
getPreviousClaudePermissionProfile,
|
|
174
155
|
getPreviousManagedCodexAgentFiles,
|
|
175
156
|
getPreviousManagedCodexAuthKeys,
|
|
176
157
|
linkedTargetsFromResults,
|