moflo 4.8.82 → 4.8.84
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/package.json +2 -2
- package/src/modules/cli/dist/src/epic/spells/single-branch.yaml +58 -6
- package/src/modules/cli/dist/src/services/moflo-require.js +5 -1
- package/src/modules/cli/dist/src/version.js +1 -1
- package/src/modules/cli/package.json +1 -1
- package/src/modules/spells/dist/core/interpolation.js +16 -2
- package/src/modules/spells/dist/core/runner.js +9 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.84",
|
|
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",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"@types/node": "^24.12.2",
|
|
118
118
|
"@xenova/transformers": "^2.17.0",
|
|
119
119
|
"eslint": "^8.0.0",
|
|
120
|
-
"moflo": "^4.8.
|
|
120
|
+
"moflo": "^4.8.83",
|
|
121
121
|
"tsx": "^4.21.0",
|
|
122
122
|
"typescript": "^5.9.3",
|
|
123
123
|
"vitest": "^4.0.0"
|
|
@@ -120,6 +120,18 @@ steps:
|
|
|
120
120
|
# The github `comment` action only appends a comment — it does NOT
|
|
121
121
|
# touch the task-list checkboxes. We read the body, substitute, and
|
|
122
122
|
# write it back so the epic reflects progress at a glance.
|
|
123
|
+
#
|
|
124
|
+
# Cross-platform notes:
|
|
125
|
+
# - Uses `node -e` instead of sed/awk/tr because coreutils are
|
|
126
|
+
# not guaranteed on PATH (minimal containers, some Git-Bash
|
|
127
|
+
# installs). Node is always available — moflo runs on it.
|
|
128
|
+
# - Values reach node via env vars (not argv) so the script is
|
|
129
|
+
# immune to Node's argv[1]-"[eval]" quirk across versions.
|
|
130
|
+
# - The embedded JS contains ZERO backslashes. When Node spawns
|
|
131
|
+
# `bash -c '<script>'` on Windows, Cygwin/MSYS bash strips one
|
|
132
|
+
# layer of backslash escapes from the command line, which
|
|
133
|
+
# silently broke regex escapes like `\[` and `\b`. Using
|
|
134
|
+
# indexOf + a char-code `isWord` check avoids the trap entirely.
|
|
123
135
|
- id: check-off-story
|
|
124
136
|
type: bash
|
|
125
137
|
permissionLevel: elevated
|
|
@@ -128,11 +140,42 @@ steps:
|
|
|
128
140
|
set -e
|
|
129
141
|
STORY={loop.story_number}
|
|
130
142
|
EPIC={args.epic_number}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
export STORY EPIC
|
|
144
|
+
node -e '
|
|
145
|
+
const cp = require("node:child_process");
|
|
146
|
+
const epic = process.env.EPIC;
|
|
147
|
+
const story = process.env.STORY;
|
|
148
|
+
const view = cp.spawnSync("gh", ["issue", "view", epic, "--json", "body", "-q", ".body"], { encoding: "utf8" });
|
|
149
|
+
if (view.status !== 0) {
|
|
150
|
+
process.stderr.write(view.stderr || "gh issue view failed");
|
|
151
|
+
process.exit(view.status || 1);
|
|
152
|
+
}
|
|
153
|
+
const body = view.stdout;
|
|
154
|
+
const target = "- [ ] #" + story;
|
|
155
|
+
const replacement = "- [x] #" + story;
|
|
156
|
+
function isWord(c) {
|
|
157
|
+
if (!c) return false;
|
|
158
|
+
const n = c.charCodeAt(0);
|
|
159
|
+
return (n >= 48 && n <= 57) || (n >= 65 && n <= 90) || (n >= 97 && n <= 122) || n === 95;
|
|
160
|
+
}
|
|
161
|
+
let updated = "";
|
|
162
|
+
let i = 0;
|
|
163
|
+
while (i < body.length) {
|
|
164
|
+
const idx = body.indexOf(target, i);
|
|
165
|
+
if (idx < 0) { updated += body.slice(i); break; }
|
|
166
|
+
updated += body.slice(i, idx);
|
|
167
|
+
const end = idx + target.length;
|
|
168
|
+
if (isWord(body[end])) {
|
|
169
|
+
updated += body.slice(idx, end);
|
|
170
|
+
} else {
|
|
171
|
+
updated += replacement;
|
|
172
|
+
}
|
|
173
|
+
i = end;
|
|
174
|
+
}
|
|
175
|
+
if (updated === body) process.exit(0);
|
|
176
|
+
const edit = cp.spawnSync("gh", ["issue", "edit", epic, "--body-file", "-"], { input: updated, stdio: ["pipe", "inherit", "inherit"] });
|
|
177
|
+
if (edit.status !== 0) process.exit(edit.status || 1);
|
|
178
|
+
'
|
|
136
179
|
timeout: 60000
|
|
137
180
|
failOnError: true
|
|
138
181
|
|
|
@@ -178,11 +221,20 @@ steps:
|
|
|
178
221
|
# merging the consolidated PR auto-closes each individual story issue,
|
|
179
222
|
# not just the epic. Without this, only the epic would close on merge
|
|
180
223
|
# and story issues would linger open indefinitely.
|
|
224
|
+
#
|
|
225
|
+
# Cross-platform: uses `node -e` instead of tr/awk (see check-off-story
|
|
226
|
+
# for rationale).
|
|
181
227
|
- id: build-closes-list
|
|
182
228
|
type: bash
|
|
183
229
|
config:
|
|
184
230
|
command: |
|
|
185
|
-
|
|
231
|
+
STORIES={args.stories}
|
|
232
|
+
export STORIES
|
|
233
|
+
node -e '
|
|
234
|
+
const raw = process.env.STORIES || "";
|
|
235
|
+
const stories = raw.split(",").map(s => s.trim()).filter(Boolean);
|
|
236
|
+
process.stdout.write(stories.map(s => "Closes #" + s).join(" "));
|
|
237
|
+
'
|
|
186
238
|
timeout: 30000
|
|
187
239
|
failOnError: true
|
|
188
240
|
|
|
@@ -86,13 +86,17 @@ export function mofloResolve(specifier) {
|
|
|
86
86
|
* relative to the caller's file — the same src/modules/memory/dist/index.js
|
|
87
87
|
* layout holds in both dev and consumer.
|
|
88
88
|
*
|
|
89
|
+
* Callers live at src/modules/cli/src/memory/<file>.ts, so from that dir
|
|
90
|
+
* the memory dist is 3 levels up (cli/src/memory → cli/src → cli → modules)
|
|
91
|
+
* plus `memory/dist/index.js`.
|
|
92
|
+
*
|
|
89
93
|
* @param callerUrl `import.meta.url` of the file that needs @moflo/memory
|
|
90
94
|
*/
|
|
91
95
|
export async function importMofloMemory(callerUrl) {
|
|
92
96
|
const viaRequire = await mofloImport('@moflo/memory');
|
|
93
97
|
if (viaRequire)
|
|
94
98
|
return viaRequire;
|
|
95
|
-
const memoryUrl = new URL('
|
|
99
|
+
const memoryUrl = new URL('../../../memory/dist/index.js', callerUrl);
|
|
96
100
|
return import(memoryUrl.href);
|
|
97
101
|
}
|
|
98
102
|
//# sourceMappingURL=moflo-require.js.map
|
|
@@ -4,8 +4,22 @@
|
|
|
4
4
|
* Resolves `{stepId.outputKey}` placeholders in spell step configs.
|
|
5
5
|
* Nested property access is supported: `{step1.data.nested.value}`.
|
|
6
6
|
*/
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Matches `{path}` variable references in spell step configs.
|
|
9
|
+
*
|
|
10
|
+
* Content must be identifier-shape: letter/underscore followed by
|
|
11
|
+
* letters, digits, `_`, `.`, or `-`. This tight grammar accommodates
|
|
12
|
+
* every real spell ref (`{args.x}`, `{loop.x}`, `{step-id.out}`) while
|
|
13
|
+
* letting bash steps embed literal `{...}` blocks — JS destructuring
|
|
14
|
+
* `{ foo }` (whitespace), object literals `{ a: b }` (colon), shell
|
|
15
|
+
* expansions `${VAR}` (lookbehind), and so on — without tripping the
|
|
16
|
+
* interpolator. A greedy `[^}]+` class ate JS code inside `node -e`
|
|
17
|
+
* scripts and threw "Variable not found" at runtime.
|
|
18
|
+
*
|
|
19
|
+
* The `(?<!\$)` negative lookbehind also skips `${VAR}` — bash parameter
|
|
20
|
+
* expansion inside shell steps must pass through untouched.
|
|
21
|
+
*/
|
|
22
|
+
export const VAR_REF_PATTERN = /(?<!\$)\{([A-Za-z_][A-Za-z0-9_.-]*)\}/g;
|
|
9
23
|
/**
|
|
10
24
|
* Resolve a dot-separated path against the context variables.
|
|
11
25
|
* Returns undefined if any segment is missing.
|
|
@@ -74,25 +74,27 @@ export class SpellCaster {
|
|
|
74
74
|
}
|
|
75
75
|
else {
|
|
76
76
|
const reason = acceptance.reason === 'hash-mismatch'
|
|
77
|
-
? 'Spell permissions have changed
|
|
78
|
-
: 'First run —
|
|
77
|
+
? 'Spell permissions have changed — please re-review'
|
|
78
|
+
: 'First run — please review permissions before running this spell';
|
|
79
79
|
const report = formatSpellPermissionReport(permReport);
|
|
80
80
|
console.log(`[spell] ${reason}`);
|
|
81
|
-
console.log(
|
|
81
|
+
console.log('');
|
|
82
82
|
console.log(report);
|
|
83
|
-
//
|
|
83
|
+
// Also run dry-run validation so genuine definition issues surface
|
|
84
|
+
// alongside the permission review — but keep them cleanly separated
|
|
85
|
+
// from the acceptance prompt (not having accepted is not an error).
|
|
84
86
|
const dryResult = await dryRunValidate(definition, resolvedArgs, defValidation, options, this.registry, (variables, wfId, stepIndex) => this.buildContext(variables, resolvedArgs, wfId, stepIndex, options.signal));
|
|
85
87
|
if (!dryResult.valid) {
|
|
86
|
-
console.log('\n[spell]
|
|
88
|
+
console.log('\n[spell] Issues found while validating the spell definition:');
|
|
87
89
|
for (const err of [...dryResult.definitionErrors, ...dryResult.argumentErrors]) {
|
|
88
90
|
console.log(` - ${err.message}`);
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
// Block — user must explicitly accept
|
|
92
|
-
console.log(`\n[spell] To
|
|
94
|
+
console.log(`\n[spell] To run this spell, ask Claude to accept it, or call the spell_accept tool with name "${definition.name}".`);
|
|
93
95
|
return this.failureResult(spellId, startTime, [{
|
|
94
96
|
code: 'ACCEPTANCE_REQUIRED',
|
|
95
|
-
message: `${reason}. Review the
|
|
97
|
+
message: `${reason}. Review the permissions above, then accept the spell "${definition.name}" to continue.`,
|
|
96
98
|
}], definition.name);
|
|
97
99
|
}
|
|
98
100
|
}
|