Skip to content

Commit 257c33a

Browse files
committed
feat(cli): embed the difyctl skill in the binary and point agents at it
Offline install skills from the embedded copy (Bun 1.4 --asset). Adds a CI smoke check, a stderr notice on difyctl help, and a next-step line in all four installers.
1 parent 437a039 commit 257c33a

26 files changed

Lines changed: 176 additions & 399 deletions

‎.github/workflows/cli-edge.yml‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ jobs:
5757
run: CLI_VERSION="${{ steps.ver.outputs.version }}" scripts/release-write-checksums.sh
5858

5959
- name: Smoke the runner-arch binary
60-
run: ./dist/bin/difyctl-v${{ steps.ver.outputs.version }}-linux-x64 version
60+
run: |
61+
bin="./dist/bin/difyctl-v${{ steps.ver.outputs.version }}-linux-x64"
62+
"$bin" version
63+
"$bin" install skills "$RUNNER_TEMP/skill"
64+
diff -r "$RUNNER_TEMP/skill/difyctl" ../skills/difyctl
6165
6266
- name: Publish to R2
6367
env:

‎.github/workflows/cli-release.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,13 @@ jobs:
121121
- name: Generate sha256 checksum file
122122
run: scripts/release-write-checksums.sh
123123

124+
- name: Smoke the runner-arch binary
125+
run: |
126+
bin="./dist/bin/$(node scripts/release-naming.mjs asset "$version" linux-x64)"
127+
"$bin" version
128+
"$bin" install skills "$RUNNER_TEMP/skill"
129+
diff -r "$RUNNER_TEMP/skill/difyctl" ../skills/difyctl
130+
124131
- name: Attach difyctl assets to Dify release
125132
if: ${{ !inputs.dry_run }}
126133
env:

‎cli/.bun-version‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.3.11
1+
1.4.2

‎cli/ARD.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ src/
2525
net/ fetch init and proxy dispatcher, shared by the http plugin and the pre-login device flow
2626
store/ config/session/token file and keychain persistence
2727
sys/ the process edges — env, argv, exit, platform paths; io/ holds streams.ts, color.ts and view.ts (a JSON value paired with its text form, the mode picks one)
28-
skills/ reads skills from a GitHub folder or a local folder, and installs them
28+
skills/ the one embedded difyctl skill (or a local folder) and its installer
2929
```
3030

3131
---

‎cli/README.md‎

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,18 @@ difyctl has no built-in business commands. Every operation the server publishes
4545

4646
## Commands
4747

48-
| Command | Purpose |
49-
| -------------------------- | --------------------------------------------------------------- |
50-
| `login` | Log in via the OAuth device flow |
51-
| `logout` | Log out of the current session |
52-
| `version` | Print the client and (if logged in) server versions |
53-
| `use workspace <id>` | Pin the local session to a workspace |
54-
| `get config [key]` | Print the local config, or one key |
55-
| `set config <key> <value>` | Set a local config value |
56-
| `unset config <key>` | Remove a local config value, restoring its default |
57-
| `refresh cache` | Refetch the server catalog and replace the local cache |
58-
| `delete cache` | Delete the local server catalog cache |
59-
| `get skills` | List the skills in the collection |
60-
| `install skills <dir>` | Write skills from the collection into a skills root (`--skill`) |
48+
| Command | Purpose |
49+
| -------------------------- | ------------------------------------------------------ |
50+
| `login` | Log in via the OAuth device flow |
51+
| `logout` | Log out of the current session |
52+
| `version` | Print the client and (if logged in) server versions |
53+
| `use workspace <id>` | Pin the local session to a workspace |
54+
| `get config [key]` | Print the local config, or one key |
55+
| `set config <key> <value>` | Set a local config value |
56+
| `unset config <key>` | Remove a local config value, restoring its default |
57+
| `refresh cache` | Refetch the server catalog and replace the local cache |
58+
| `delete cache` | Delete the local server catalog cache |
59+
| `install skills <dir>` | Write the difyctl skill into a skills root (`--from`) |
6160

6261
`login` blocks until the browser approval arrives. An agent runs it in the background, relays the `open <url>` and `code <code>` lines from its stderr to the user, and does not cancel the job. In a sandbox it adds `--no-keyring` and sets `DIFY_CONFIG_DIR` to persistent storage on every call, so the login survives the next session.
6362

@@ -68,11 +67,9 @@ response in the error envelope instead of dropping it.
6867

6968
## Agent skills
7069

71-
difyctl installs agent skills from [`skills/`] at the repo root. It downloads them from GitHub at install time, from the commit the binary was built from; the binary holds no copy and needs no git. The basic skill, `difyctl`, teaches the discovery flow — `help` to see the map or search, `help <id>` to inspect one operation, then the operation's own command to run it — so it stays correct as the server's catalog grows. Scenario skills add guidance for one kind of task and open by reading the basic one.
70+
difyctl ships one skill, `difyctl`, built into the binary. It teaches the discovery flow — `help` to see the map or search, `help <id>` to inspect one operation, then the operation's own command to run it — so it stays correct as the server's catalog grows. The skill is a tree of linked docs under [`skills/difyctl/`].
7271

73-
- `difyctl get skills` — the collection: each skill's name and description. Writes nothing.
74-
- `difyctl install skills <dir>` — write every skill into `<dir>`, the agent's skills root: the folder that holds one subfolder per skill, for example `~/.claude/skills` for Claude Code or `~/.codex/skills` for Codex. Each skill's folder is replaced whole. `--skill <name>` (repeatable) writes only those skills; a scenario skill brings `difyctl` along.
75-
- `--from` on both reads skills from elsewhere: any GitHub folder in the standard layout (`https://github.com/<owner>/<repo>/tree/<ref>/<folder>`), or a local folder when GitHub is blocked. Set `GITHUB_TOKEN` if GitHub's rate limit (60 requests an hour per IP) gets in the way.
72+
- `difyctl install skills <dir> [--from <folder>]` — write the skill into `<dir>`, the agent's skills root: the folder that holds one subfolder per skill, for example `~/.claude/skills` for Claude Code or `~/.codex/skills` for Codex. The `difyctl` folder is replaced whole. It works offline. `--from` installs a local skill folder instead of the built-in one.
7673

7774
difyctl does not detect agents. You name the root, and re-running the same command refreshes the copies.
7875

@@ -131,4 +128,4 @@ Apache-2.0.
131128
[Agent skill]: #agent-skills
132129
[Dify]: https://dify.ai
133130
[`ARD.md`]: ARD.md
134-
[`skills/`]: ../skills
131+
[`skills/difyctl/`]: ../skills/difyctl

‎cli/scripts/install-cli.sh‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ err() { printf '%s\n' "install-cli: $*" >&2; }
2828
die() { err "$*"; rm -f "$FETCH_ERR_FILE"; exit 1; }
2929
need() { command -v "$1" >/dev/null 2>&1 || die "$1 is required"; }
3030
re_escape() { printf '%s' "$1" | sed 's/[][\\.^$*+?(){}|/]/\\&/g'; }
31+
print_skill_hint() {
32+
printf '%s\n' 'AI agents: run `difyctl install skills <your skills root>` to load the difyctl skill.'
33+
printf '%s\n' 'Run it again after each difyctl upgrade; the skill ships inside the binary.'
34+
}
3135

3236
detect_target() {
3337
case "$(uname -s)" in
@@ -238,6 +242,7 @@ main() {
238242
printf ' export PATH="%s:$PATH"\n' "$bin_dir"
239243
;;
240244
esac
245+
print_skill_hint
241246
}
242247

243248
if [ "${DIFYCTL_INSTALL_LIB:-0}" != "1" ]; then

‎cli/scripts/install-r2.ps1‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ function Get-ChecksumTarget([string]$text, [string]$target) {
3838
return $null
3939
}
4040

41+
function Write-SkillHint {
42+
Write-Host 'AI agents: run `difyctl install skills <your skills root>` to load the difyctl skill.'
43+
Write-Host 'Run it again after each difyctl upgrade; the skill ships inside the binary.'
44+
}
45+
4146
# Download, sha256-verify, place. Returns nothing; throws on mismatch.
4247
function Install-DifyctlBinary([string]$dlUrl, [string]$sha, [string]$version, [string]$channel, [string]$installDir) {
4348
$tmp = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetFileName($dlUrl))
@@ -55,6 +60,7 @@ function Install-DifyctlBinary([string]$dlUrl, [string]$sha, [string]$version, [
5560
Write-Host "$installDir is not on your PATH. Add it with:"
5661
Write-Host " [Environment]::SetEnvironmentVariable('PATH', `"$installDir;`$env:PATH`", 'User')"
5762
}
63+
Write-SkillHint
5864
}
5965

6066
function Install-DifyctlR2 {

‎cli/scripts/install-r2.sh‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ trap 'rm -f "$tmp_m" "${tmp_c:-}" "${tmp_b:-}"' EXIT INT TERM
2121
err() { printf '%s\n' "install-r2: $*" >&2; }
2222
die() { err "$*"; exit 1; }
2323
need() { command -v "$1" >/dev/null 2>&1 || die "$1 is required"; }
24+
print_skill_hint() {
25+
printf '%s\n' 'AI agents: run `difyctl install skills <your skills root>` to load the difyctl skill.'
26+
printf '%s\n' 'Run it again after each difyctl upgrade; the skill ships inside the binary.'
27+
}
2428

2529
detect_target() {
2630
case "$(uname -s)" in
@@ -98,6 +102,7 @@ fetch_verify_install() {
98102
*":${install_dir}:"*) ;;
99103
*) printf 'note: add %s to your PATH\n' "$install_dir" ;;
100104
esac
105+
print_skill_hint
101106
}
102107

103108
# Resolve a pinned build into download url + sha. Sets: version, dl_url, dl_sha.

‎cli/scripts/install.ps1‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ function Resolve-Release {
115115
}
116116
}
117117

118+
function Write-SkillHint {
119+
Write-Host 'AI agents: run `difyctl install skills <your skills root>` to load the difyctl skill.'
120+
Write-Host 'Run it again after each difyctl upgrade; the skill ships inside the binary.'
121+
}
122+
118123
function Invoke-Main {
119124
$release = Resolve-Release
120125
$difyTag = $release.tag_name
@@ -158,6 +163,7 @@ function Invoke-Main {
158163
else {
159164
Write-Host 'verify: run "difyctl version"'
160165
}
166+
Write-SkillHint
161167
}
162168
finally {
163169
Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue

‎cli/scripts/release-build.sh‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ DIFYCTL_BUILD_DATE="${DIFYCTL_BUILD_DATE:-$(date -u +%Y-%m-%dT%H:%M:%SZ)}"
4141

4242
[[ "$CLI_VERSION" != "undefined" ]] || die "CLI_VERSION could not be derived from package.json"
4343

44+
repo_root="$(cd "${cli_root}/.." && pwd)"
45+
skill_dir="${repo_root}/skills/difyctl"
46+
[[ -f "${skill_dir}/SKILL.md" ]] || die "skill not found: ${skill_dir}"
47+
4448
[[ -f "$entry" ]] || die "entry not found: $entry"
4549

4650
rm -rf "$out_dir"
@@ -60,12 +64,13 @@ defines=(
6064
while IFS=$'\t' read -r bun_target asset_target _exe; do
6165
out="${out_dir}/$(naming asset "$CLI_VERSION" "$asset_target")"
6266
log::info "compiling ${asset_target} -> $(basename "$out")..."
63-
bun build "$entry" \
67+
(cd "$repo_root" && bun build "$entry" \
68+
--asset "$skill_dir" \
6469
--target="$bun_target" \
6570
--compile \
6671
--minify \
6772
"${defines[@]}" \
68-
--outfile="$out" >/dev/null
73+
--outfile="$out") >/dev/null
6974
done < <(naming targets)
7075

7176
log::info "built $(find "$out_dir" -type f | wc -l | tr -d ' ') binaries:"

0 commit comments

Comments
 (0)