moflo 4.8.69 → 4.8.70

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.
@@ -233,6 +233,71 @@ Semantics (from `resolveEffectiveSandbox()` in `src/modules/spells/src/core/plat
233
233
 
234
234
  Existing projects that predate this block get it auto-appended on session start — never require `moflo init` to re-run after a version bump.
235
235
 
236
+ ## Authoring Checklist — Always Double-Check Step Permissions
237
+
238
+ Before shipping any new or edited spell step, walk through every item. Silently-missing permissions don't fail with `CAPABILITY_DENIED` — they fail confusingly several steps later.
239
+
240
+ 1. **What does the command actually do?** List every external effect — file reads/writes, git/gh calls, outbound HTTP, Claude subagents, credential use.
241
+ 2. **Which capabilities map to those effects?** `fs:read`, `fs:write`, `shell`, `net`, `credentials`, `agent`, `browser`, `memory`.
242
+ 3. **What is the minimum `permissionLevel`?**
243
+ - Pure analysis (read only) → `readonly`
244
+ - Edits project files but no shell/network → `standard`
245
+ - Runs shell commands, **or needs network inside a bwrap-sandboxed step** → `elevated`
246
+ - Spawns Claude subagents with unrestricted tools → `autonomous`
247
+ 4. **Does this step need network?** If so, either give it `permissionLevel: elevated` or declare the `net` capability explicitly. See the Troubleshooting section — bwrap strips network from any bash step that has neither, and the failure surfaces as a DNS error, not a permission error.
248
+ 5. **Does the command chain multiple statements (`;`, `&&`, `||`)?** Lead the command with `set -e`. A trailing tolerated-failure cleanup like `git stash pop ... || true` will otherwise return 0 for the whole step even when the real work (checkout, pull, push) failed.
249
+ 6. **Does this step produce state that later steps rely on?** If this step silently no-ops, will the downstream symptom be understandable? If not, tighten the failure mode (`set -e`, explicit error, `failOnError: true`).
250
+
251
+ When reviewing a spell PR, scan every bash step for a missing `permissionLevel` and ask: *does this step touch the network, or does it depend on network state from earlier?* If yes, `elevated` (or an explicit `net` grant) is required.
252
+
253
+ ## Troubleshooting
254
+
255
+ ### Symptom: bash step fails with DNS / SSH resolution errors inside a spell
256
+
257
+ Typical error messages from inside a bash step:
258
+
259
+ - `ssh: Could not resolve hostname github.com: Temporary failure in name resolution`
260
+ - `fatal: Could not read from remote repository.`
261
+ - `curl: (6) Could not resolve host ...`
262
+ - `getaddrinfo ENOTFOUND ...`
263
+ - Any other DNS/connection failure, even though the **same command works in your normal shell**.
264
+
265
+ **Tell-tale clue:** the error mentions `Temporary failure in name resolution` (a glibc-specific wording). That means the step is running inside a Linux sandbox (`bwrap` on Linux / WSL), **not** your outer shell — Git Bash or PowerShell won't produce that exact message.
266
+
267
+ **Root cause:** `src/modules/spells/src/core/bwrap-sandbox.ts` isolates the network by default:
268
+
269
+ ```ts
270
+ if (!hasNet && !needsToolHomeAccess(options.permissionLevel)) {
271
+ args.push('--unshare-net'); // ← no network, no DNS
272
+ }
273
+ ```
274
+
275
+ A bash step gets network access only when **one** of these is true:
276
+
277
+ 1. The step declares a `net` capability, **or**
278
+ 2. The step's `permissionLevel` is `elevated` or `autonomous`.
279
+
280
+ If neither applies, bwrap runs the command in a namespace with `--unshare-net`, and DNS silently fails. There is no log line announcing the network was taken away — you just see the command's own DNS error.
281
+
282
+ **Fix:** for any bash step that does `git pull`/`git push`/`git fetch`, `gh` API calls, `curl`, `npm install`, or any other outbound network:
283
+
284
+ ```yaml
285
+ - id: create-branch
286
+ type: bash
287
+ permissionLevel: elevated # ← grants network in bwrap
288
+ config:
289
+ command: "git pull origin main && ..."
290
+ ```
291
+
292
+ Or declare the `net` capability explicitly if the step doesn't need the full `elevated` profile (note: `bash-command.ts` must include `net` in its declared capabilities for the engine to accept the grant — otherwise you'll see `Capability violation: step type "bash" does not declare capability "net"`).
293
+
294
+ **Quick diagnosis checklist** when a spell's bash step can't reach the network:
295
+
296
+ 1. Does the same command work in your outer shell? If yes, it's sandbox-related, not config.
297
+ 2. Is the error wording glibc-style (`Temporary failure in name resolution`)? → bwrap is involved.
298
+ 3. Open the spell YAML — does the failing step have `permissionLevel: elevated`? If not, add it and retry.
299
+ 4. If you use `set -e` in a multi-command bash step, **do it**. Without it, a trailing `... || true` (common for stash-pop cleanups) will mask the real network failure and you'll see a confusing error several steps later (e.g. "pathspec did not match" when a branch that was never pulled/created is later checked out).
300
+
236
301
  ## See Also
237
302
 
238
303
  - `.claude/guidance/shipped/moflo-spell-engine.md` — Spell engine usage and YAML format
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moflo",
3
- "version": "4.8.69",
3
+ "version": "4.8.70",
4
4
  "description": "MoFlo — AI agent orchestration for Claude Code. Forked from ruflo/claude-flow with patches applied to source, plus feature-level orchestration.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -112,7 +112,7 @@
112
112
  "@types/js-yaml": "^4.0.9",
113
113
  "@types/node": "^20.19.37",
114
114
  "eslint": "^8.0.0",
115
- "moflo": "^4.8.68",
115
+ "moflo": "^4.8.69",
116
116
  "tsx": "^4.21.0",
117
117
  "typescript": "^5.9.3",
118
118
  "vitest": "^4.0.0"
@@ -58,6 +58,8 @@ steps:
58
58
  # on dirty tree, missing auth, or unreachable remote.
59
59
  - id: checkout-base
60
60
  type: bash
61
+ # elevated — bwrap network access for git pull (see single-branch create-branch).
62
+ permissionLevel: elevated
61
63
  preflight:
62
64
  - name: "no unmerged files"
63
65
  command: "git diff --name-only --diff-filter=U"
@@ -123,6 +125,8 @@ steps:
123
125
  # 5: Pull merged changes
124
126
  - id: pull-merged
125
127
  type: bash
128
+ # elevated — bwrap network access for git pull (see checkout-base).
129
+ permissionLevel: elevated
126
130
  config:
127
131
  # set -e: fail fast if checkout/pull fails (see checkout-base).
128
132
  command: "set -e; git stash --include-untracked -q 2>/dev/null || true; git checkout {args.base_branch}; git pull origin {args.base_branch}; git stash pop -q 2>/dev/null || true"
@@ -55,6 +55,11 @@ steps:
55
55
  # Preflights run BEFORE any step — fail fast on dirty tree or auth issues
56
56
  - id: create-branch
57
57
  type: bash
58
+ # elevated — needed for bwrap to share the host network so `git pull`
59
+ # can reach github. Without it, bwrap adds --unshare-net and the pull
60
+ # fails with a DNS / SSH resolution error even though the outer shell
61
+ # has working network.
62
+ permissionLevel: elevated
58
63
  preflight:
59
64
  - name: "no unmerged files"
60
65
  command: "git diff --name-only --diff-filter=U"
@@ -134,6 +139,8 @@ steps:
134
139
  # Preflight: verify origin remote exists so push won't fail mid-way
135
140
  - id: push-branch
136
141
  type: bash
142
+ # elevated — bwrap network access for git push (see create-branch).
143
+ permissionLevel: elevated
137
144
  preflight:
138
145
  - name: "origin remote configured"
139
146
  command: "git remote get-url origin"
@@ -2,5 +2,5 @@
2
2
  * Auto-generated by build. Do not edit manually.
3
3
  * Source of truth: root package.json → scripts/sync-version.mjs
4
4
  */
5
- export const VERSION = '4.8.69';
5
+ export const VERSION = '4.8.70';
6
6
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moflo/cli",
3
- "version": "4.8.69",
3
+ "version": "4.8.70",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",