@claude-flow/cli 3.56.0 → 3.56.2
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/.claude/helpers/helpers.manifest.json +2 -2
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/config.js +1 -1
- package/dist/src/mcp-tools/config-tools.js +118 -13
- package/dist/src/mcp-tools/task-tools.d.ts +5 -0
- package/dist/src/mcp-tools/task-tools.js +51 -7
- package/dist/src/memory/memory-initializer.js +5 -1
- package/dist/src/services/config-file-manager.d.ts +2 -2
- package/dist/src/services/config-file-manager.js +22 -3
- package/dist/src/services/policy-ledger-anchor.d.ts +8 -0
- package/dist/src/services/policy-ledger-anchor.js +18 -3
- package/dist/src/services/policy-runtime.js +2 -2
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/package.json +1 -1
- package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +1 -1
- package/plugins/ruflo-metaharness/CHANGELOG.md +3 -0
- package/plugins/ruflo-metaharness/scripts/smoke.sh +4 -4
- package/plugins/ruflo-metaharness/skills/harness-bench/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-evolve/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-genome/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-mcp-scan/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-mint/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-score/SKILL.md +1 -1
- package/plugins/ruflo-metaharness/skills/harness-threat-model/SKILL.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.56.
|
|
3
|
+
"version": "3.56.2",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "c8d218526a3c34034c2a69de23f507f0eb0f70d59c16426a8edc849e6ac22529",
|
|
6
6
|
"hook-handler.cjs": "34d2aacfb59a73ff94b1467bb57ab9d9c4af89c0e470aadf3bd0d74a503a658d",
|
|
@@ -9,6 +9,6 @@
|
|
|
9
9
|
"router.cjs": "16648b3c0d73b6681ac2d8d13def6a68286c57f5a11004de5b19a0fa088c833d"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"signature": "
|
|
12
|
+
"signature": "03RFXZ6Pjgky6pMS4AtU7Ba4ohrtwT67+aH8+COTVfhl5eZ0R1LsSXrGfMlROX+5U+FRlrbGoEaiNmNOhHRPDg==",
|
|
13
13
|
"algorithm": "ed25519"
|
|
14
14
|
}
|
package/catalog-manifest.json
CHANGED
|
@@ -309,7 +309,7 @@ const resetCommand = {
|
|
|
309
309
|
],
|
|
310
310
|
action: async (ctx) => {
|
|
311
311
|
try {
|
|
312
|
-
const configPath = configManager.reset(ctx.cwd);
|
|
312
|
+
const configPath = configManager.reset(ctx.cwd, ctx.flags.section);
|
|
313
313
|
output.writeln(`Configuration reset to defaults: ${configPath}`);
|
|
314
314
|
return { success: true };
|
|
315
315
|
}
|
|
@@ -35,12 +35,110 @@ function ensureConfigDir() {
|
|
|
35
35
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
// Plain CLI files and legacy MCP envelopes share the same path. Keep the
|
|
39
|
+
// original plain document so MCP writes remain readable by the CLI.
|
|
40
|
+
const plainDocuments = new WeakMap();
|
|
41
|
+
const STORE_METADATA = new Set(['scopes', 'version', 'updatedAt']);
|
|
42
|
+
function isRecord(value) {
|
|
43
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
function flattenValues(document) {
|
|
46
|
+
const values = {};
|
|
47
|
+
function visit(value, key) {
|
|
48
|
+
if (isRecord(value) && Object.keys(value).length > 0) {
|
|
49
|
+
for (const [child, entry] of Object.entries(value))
|
|
50
|
+
visit(entry, `${key}.${child}`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
Object.defineProperty(values, key, { value, enumerable: true, configurable: true, writable: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const [key, value] of Object.entries(document)) {
|
|
57
|
+
if (!STORE_METADATA.has(key))
|
|
58
|
+
visit(value, key);
|
|
59
|
+
}
|
|
60
|
+
// CLI reads literal dotted keys before traversing nested objects.
|
|
61
|
+
for (const [key, value] of Object.entries(document)) {
|
|
62
|
+
if (key.includes('.'))
|
|
63
|
+
Object.defineProperty(values, key, { value, enumerable: true, configurable: true, writable: true });
|
|
64
|
+
}
|
|
65
|
+
return values;
|
|
66
|
+
}
|
|
67
|
+
function readDefaultValue(store, key) {
|
|
68
|
+
const document = plainDocuments.get(store);
|
|
69
|
+
if (!document)
|
|
70
|
+
return store.values[key];
|
|
71
|
+
return Object.hasOwn(document, key) ? document[key] : getNestedValue(document, key);
|
|
72
|
+
}
|
|
73
|
+
function writeDefaultValue(store, key, value) {
|
|
74
|
+
const document = plainDocuments.get(store);
|
|
75
|
+
if (!document) {
|
|
76
|
+
store.values[key] = value;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Keep existing literal dotted keys; otherwise use the CLI's nested shape.
|
|
80
|
+
if (Object.hasOwn(document, key)) {
|
|
81
|
+
// Reuse the same segment validation as nested writes.
|
|
82
|
+
setNestedValue({}, key, value);
|
|
83
|
+
document[key] = value;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
setNestedValue(document, key, value);
|
|
87
|
+
}
|
|
88
|
+
store.values = flattenValues(document);
|
|
89
|
+
}
|
|
90
|
+
function deleteDefaultValue(store, key) {
|
|
91
|
+
const document = plainDocuments.get(store);
|
|
92
|
+
if (!document)
|
|
93
|
+
return Object.hasOwn(store.values, key) && delete store.values[key];
|
|
94
|
+
let target = document;
|
|
95
|
+
let leaf = key;
|
|
96
|
+
if (!Object.hasOwn(document, key)) {
|
|
97
|
+
const parts = key.split('.');
|
|
98
|
+
leaf = parts.pop();
|
|
99
|
+
for (const part of parts) {
|
|
100
|
+
if (!Object.hasOwn(target, part) || !isRecord(target[part]))
|
|
101
|
+
return false;
|
|
102
|
+
target = target[part];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const deleted = Object.hasOwn(target, leaf) && delete target[leaf];
|
|
106
|
+
store.values = flattenValues(document);
|
|
107
|
+
return deleted;
|
|
108
|
+
}
|
|
109
|
+
function replaceDefaultValues(store, values) {
|
|
110
|
+
const document = plainDocuments.get(store);
|
|
111
|
+
if (!document) {
|
|
112
|
+
store.values = values;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
for (const key of Object.keys(document)) {
|
|
116
|
+
if (!STORE_METADATA.has(key))
|
|
117
|
+
delete document[key];
|
|
118
|
+
}
|
|
119
|
+
for (const [key, value] of Object.entries(values))
|
|
120
|
+
writeDefaultValue(store, key, value);
|
|
121
|
+
}
|
|
38
122
|
function loadConfigStore() {
|
|
39
123
|
try {
|
|
40
124
|
const path = getConfigPath();
|
|
41
125
|
if (existsSync(path)) {
|
|
42
126
|
const data = readFileSync(path, 'utf-8');
|
|
43
|
-
|
|
127
|
+
const document = JSON.parse(data);
|
|
128
|
+
const isEnvelope = isRecord(document) && isRecord(document.values)
|
|
129
|
+
&& isRecord(document.scopes) && typeof document.version === 'string'
|
|
130
|
+
&& typeof document.updatedAt === 'string';
|
|
131
|
+
if (isRecord(document) && !isEnvelope) {
|
|
132
|
+
const store = {
|
|
133
|
+
values: flattenValues(document),
|
|
134
|
+
scopes: isRecord(document.scopes) ? document.scopes : {},
|
|
135
|
+
version: typeof document.version === 'string' ? document.version : '3.0.0',
|
|
136
|
+
updatedAt: typeof document.updatedAt === 'string' ? document.updatedAt : '',
|
|
137
|
+
};
|
|
138
|
+
plainDocuments.set(store, document);
|
|
139
|
+
return store;
|
|
140
|
+
}
|
|
141
|
+
return document;
|
|
44
142
|
}
|
|
45
143
|
}
|
|
46
144
|
catch {
|
|
@@ -56,13 +154,20 @@ function loadConfigStore() {
|
|
|
56
154
|
function saveConfigStore(store) {
|
|
57
155
|
ensureConfigDir();
|
|
58
156
|
store.updatedAt = new Date().toISOString();
|
|
59
|
-
|
|
157
|
+
const document = plainDocuments.get(store);
|
|
158
|
+
if (document) {
|
|
159
|
+
document.updatedAt = store.updatedAt;
|
|
160
|
+
if (Object.keys(store.scopes).length > 0 || Object.hasOwn(document, 'scopes')) {
|
|
161
|
+
document.scopes = store.scopes;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
writeFileSync(getConfigPath(), JSON.stringify(document ?? store, null, 2), 'utf-8');
|
|
60
165
|
}
|
|
61
166
|
function getNestedValue(obj, key) {
|
|
62
167
|
const parts = key.split('.');
|
|
63
168
|
let current = obj;
|
|
64
169
|
for (const part of parts) {
|
|
65
|
-
if (current && typeof current === 'object' && part
|
|
170
|
+
if (current && typeof current === 'object' && Object.hasOwn(current, part)) {
|
|
66
171
|
current = current[part];
|
|
67
172
|
}
|
|
68
173
|
else {
|
|
@@ -95,7 +200,7 @@ function setNestedValue(obj, key, value) {
|
|
|
95
200
|
let current = obj;
|
|
96
201
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
97
202
|
const part = parts[i];
|
|
98
|
-
if (!(part
|
|
203
|
+
if (!Object.hasOwn(current, part) || current[part] === null || typeof current[part] !== 'object') {
|
|
99
204
|
current[part] = {};
|
|
100
205
|
}
|
|
101
206
|
current = current[part];
|
|
@@ -134,7 +239,7 @@ export const configTools = [
|
|
|
134
239
|
value = store.scopes[scope][key];
|
|
135
240
|
}
|
|
136
241
|
if (value === undefined) {
|
|
137
|
-
value = store
|
|
242
|
+
value = readDefaultValue(store, key);
|
|
138
243
|
}
|
|
139
244
|
if (value === undefined) {
|
|
140
245
|
value = DEFAULT_CONFIG[key];
|
|
@@ -144,7 +249,7 @@ export const configTools = [
|
|
|
144
249
|
value,
|
|
145
250
|
scope,
|
|
146
251
|
exists: value !== undefined,
|
|
147
|
-
source: value !== undefined ? (store
|
|
252
|
+
source: value !== undefined ? (readDefaultValue(store, key) !== undefined ? 'stored' : 'default') : 'none',
|
|
148
253
|
};
|
|
149
254
|
},
|
|
150
255
|
},
|
|
@@ -175,9 +280,9 @@ export const configTools = [
|
|
|
175
280
|
const key = input.key;
|
|
176
281
|
const value = input.value;
|
|
177
282
|
const scope = input.scope || 'default';
|
|
178
|
-
const previousValue = store
|
|
283
|
+
const previousValue = readDefaultValue(store, key);
|
|
179
284
|
if (scope === 'default') {
|
|
180
|
-
store
|
|
285
|
+
writeDefaultValue(store, key, value);
|
|
181
286
|
}
|
|
182
287
|
else {
|
|
183
288
|
if (!store.scopes[scope]) {
|
|
@@ -289,8 +394,7 @@ export const configTools = [
|
|
|
289
394
|
if (key) {
|
|
290
395
|
// Reset specific key
|
|
291
396
|
if (scope === 'default') {
|
|
292
|
-
if (key
|
|
293
|
-
delete store.values[key];
|
|
397
|
+
if (deleteDefaultValue(store, key)) {
|
|
294
398
|
resetKeys.push(key);
|
|
295
399
|
}
|
|
296
400
|
}
|
|
@@ -303,7 +407,7 @@ export const configTools = [
|
|
|
303
407
|
// Reset all keys in scope
|
|
304
408
|
if (scope === 'default') {
|
|
305
409
|
resetKeys = Object.keys(store.values);
|
|
306
|
-
store
|
|
410
|
+
replaceDefaultValues(store, { ...DEFAULT_CONFIG });
|
|
307
411
|
}
|
|
308
412
|
else if (store.scopes[scope]) {
|
|
309
413
|
resetKeys = Object.keys(store.scopes[scope]);
|
|
@@ -385,10 +489,11 @@ export const configTools = [
|
|
|
385
489
|
const importedKeys = Object.keys(config);
|
|
386
490
|
if (scope === 'default') {
|
|
387
491
|
if (merge) {
|
|
388
|
-
Object.
|
|
492
|
+
for (const [key, value] of Object.entries(config))
|
|
493
|
+
writeDefaultValue(store, key, value);
|
|
389
494
|
}
|
|
390
495
|
else {
|
|
391
|
-
store
|
|
496
|
+
replaceDefaultValues(store, { ...DEFAULT_CONFIG, ...config });
|
|
392
497
|
}
|
|
393
498
|
}
|
|
394
499
|
else {
|
|
@@ -4,5 +4,10 @@
|
|
|
4
4
|
* Tool definitions for task management with file persistence.
|
|
5
5
|
*/
|
|
6
6
|
import { type MCPTool } from './types.js';
|
|
7
|
+
export declare const TASK_STATUSES: readonly ["pending", "in_progress", "completed", "failed", "cancelled"];
|
|
8
|
+
type TaskStatus = (typeof TASK_STATUSES)[number];
|
|
9
|
+
/** The canonical status for a written one (trimmed, any case, known aliases), or null when it is none of the five. */
|
|
10
|
+
export declare function statusOf(value: unknown): TaskStatus | null;
|
|
7
11
|
export declare const taskTools: MCPTool[];
|
|
12
|
+
export {};
|
|
8
13
|
//# sourceMappingURL=task-tools.d.ts.map
|
|
@@ -12,6 +12,21 @@ import { validateIdentifier, validateText } from './validate-input.js';
|
|
|
12
12
|
const STORAGE_DIR = '.claude-flow';
|
|
13
13
|
const TASK_DIR = 'tasks';
|
|
14
14
|
const TASK_FILE = 'store.json';
|
|
15
|
+
export const TASK_STATUSES = ['pending', 'in_progress', 'completed', 'failed', 'cancelled'];
|
|
16
|
+
// Spellings agents write for the same five states (seen in the wild: "complete").
|
|
17
|
+
const STATUS_ALIASES = {
|
|
18
|
+
complete: 'completed', done: 'completed', canceled: 'cancelled',
|
|
19
|
+
running: 'in_progress', 'in-progress': 'in_progress', inprogress: 'in_progress',
|
|
20
|
+
};
|
|
21
|
+
/** The canonical status for a written one (trimmed, any case, known aliases), or null when it is none of the five. */
|
|
22
|
+
export function statusOf(value) {
|
|
23
|
+
if (typeof value !== 'string')
|
|
24
|
+
return null;
|
|
25
|
+
const key = value.trim().toLowerCase();
|
|
26
|
+
if (TASK_STATUSES.includes(key))
|
|
27
|
+
return key;
|
|
28
|
+
return Object.hasOwn(STATUS_ALIASES, key) ? STATUS_ALIASES[key] : null;
|
|
29
|
+
}
|
|
15
30
|
function getTaskDir() {
|
|
16
31
|
return join(getProjectCwd(), STORAGE_DIR, TASK_DIR);
|
|
17
32
|
}
|
|
@@ -290,7 +305,9 @@ export const taskTools = [
|
|
|
290
305
|
type: 'object',
|
|
291
306
|
properties: {
|
|
292
307
|
taskId: { type: 'string', description: 'Task ID' },
|
|
293
|
-
|
|
308
|
+
// No schema enum: the HTTP/WebSocket registry validates enums before the handler, which would refuse the aliases there
|
|
309
|
+
// while stdio accepted them. The handler is the one place that normalises and refuses, for every transport.
|
|
310
|
+
status: { type: 'string', description: 'New status: pending, in_progress, completed, failed or cancelled' },
|
|
294
311
|
progress: { type: 'number', description: 'Progress percentage (0-100)' },
|
|
295
312
|
assignTo: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to assign' },
|
|
296
313
|
result: { type: 'object', description: 'Result data (e.g. the failure of a failed task)' },
|
|
@@ -302,26 +319,52 @@ export const taskTools = [
|
|
|
302
319
|
const vId = validateIdentifier(input.taskId, 'taskId');
|
|
303
320
|
if (!vId.valid)
|
|
304
321
|
return { success: false, error: vId.error };
|
|
322
|
+
// A status outside the five is stored as written otherwise, and every reader that buckets
|
|
323
|
+
// by status (task_list filters, the console kanban) then miscounts it: "complete" read as pending.
|
|
324
|
+
const status = input.status === undefined ? undefined : statusOf(input.status);
|
|
325
|
+
if (status === null) {
|
|
326
|
+
return { success: false, error: `status must be one of ${TASK_STATUSES.join(', ')}` };
|
|
327
|
+
}
|
|
305
328
|
const store = loadTaskStore();
|
|
306
329
|
const taskId = input.taskId;
|
|
307
330
|
const task = store.tasks[taskId];
|
|
308
331
|
if (task) {
|
|
309
|
-
|
|
310
|
-
|
|
332
|
+
// Worker effects run AFTER the task store is saved, as task_complete does: a failed save must not leave
|
|
333
|
+
// workers freed and counted against a task that still reads unfinished.
|
|
334
|
+
let afterSave;
|
|
335
|
+
if (status !== undefined) {
|
|
336
|
+
const newStatus = status;
|
|
337
|
+
const wasCompleted = task.status === 'completed';
|
|
311
338
|
task.status = newStatus;
|
|
312
339
|
if (newStatus === 'in_progress' && !task.startedAt) {
|
|
313
340
|
task.startedAt = new Date().toISOString();
|
|
314
341
|
}
|
|
315
|
-
// A
|
|
316
|
-
//
|
|
317
|
-
|
|
342
|
+
// A finished task frees its workers, as task_complete and task_cancel do; otherwise a worker
|
|
343
|
+
// whose task was finished through task_update stays `busy` forever, and a later task_complete
|
|
344
|
+
// returns early because the status already reads completed.
|
|
345
|
+
if (newStatus === 'failed' || newStatus === 'cancelled') {
|
|
346
|
+
task.completedAt = new Date().toISOString();
|
|
347
|
+
afterSave = () => releaseAgents(task.assignedTo, taskId);
|
|
348
|
+
}
|
|
349
|
+
if (newStatus === 'completed' && !wasCompleted) {
|
|
318
350
|
task.completedAt = new Date().toISOString();
|
|
319
|
-
|
|
351
|
+
// Credited only to a worker still holding this task, so complete -> pending -> complete
|
|
352
|
+
// (no reassignment in between) never counts the same work twice.
|
|
353
|
+
afterSave = () => updateAgents(task.assignedTo, (agent) => {
|
|
354
|
+
if (agent.currentTask === taskId) {
|
|
355
|
+
agent.status = 'idle';
|
|
356
|
+
agent.currentTask = null;
|
|
357
|
+
agent.taskCount = (agent.taskCount || 0) + 1;
|
|
358
|
+
}
|
|
359
|
+
});
|
|
320
360
|
}
|
|
321
361
|
}
|
|
322
362
|
if (typeof input.progress === 'number') {
|
|
323
363
|
task.progress = Math.min(100, Math.max(0, input.progress));
|
|
324
364
|
}
|
|
365
|
+
// Completing is 100% whatever progress the same call carried.
|
|
366
|
+
if (task.status === 'completed')
|
|
367
|
+
task.progress = 100;
|
|
325
368
|
if (input.assignTo) {
|
|
326
369
|
task.assignedTo = input.assignTo;
|
|
327
370
|
}
|
|
@@ -329,6 +372,7 @@ export const taskTools = [
|
|
|
329
372
|
task.result = input.result;
|
|
330
373
|
}
|
|
331
374
|
saveTaskStore(store);
|
|
375
|
+
afterSave?.();
|
|
332
376
|
return {
|
|
333
377
|
success: true,
|
|
334
378
|
taskId: task.taskId,
|
|
@@ -1235,7 +1235,11 @@ export async function ensureSchemaColumns(dbPath, options = {}) {
|
|
|
1235
1235
|
if (columnsAdded.includes('status') || existingColumns.has('status')) {
|
|
1236
1236
|
try {
|
|
1237
1237
|
db.run(`UPDATE memory_entries SET status = 'active' WHERE status IS NULL`);
|
|
1238
|
-
|
|
1238
|
+
// #3918: only a backfill that changed rows dirties the image. Setting
|
|
1239
|
+
// this unconditionally rewrote every up-to-date store on each open (and,
|
|
1240
|
+
// with encryption at rest, with a fresh nonce, so the bytes changed too).
|
|
1241
|
+
if (db.getRowsModified() > 0)
|
|
1242
|
+
modified = true;
|
|
1239
1243
|
}
|
|
1240
1244
|
catch {
|
|
1241
1245
|
/* table is read-only or doesn't exist — skip */
|
|
@@ -18,8 +18,8 @@ export declare class ConfigFileManager {
|
|
|
18
18
|
set(cwd: string, key: string, value: unknown): void;
|
|
19
19
|
/** Create a new config file with defaults */
|
|
20
20
|
create(cwd: string, overrides?: Record<string, unknown>, force?: boolean): string;
|
|
21
|
-
/** Reset
|
|
22
|
-
reset(cwd: string): string;
|
|
21
|
+
/** Reset all configuration or only the selected section to defaults. */
|
|
22
|
+
reset(cwd: string, section?: string): string;
|
|
23
23
|
/** Export config to a specific path */
|
|
24
24
|
exportTo(cwd: string, exportPath: string): void;
|
|
25
25
|
/** Import config from a specific path */
|
|
@@ -148,11 +148,30 @@ export class ConfigFileManager {
|
|
|
148
148
|
this.configPath = targetPath;
|
|
149
149
|
return targetPath;
|
|
150
150
|
}
|
|
151
|
-
/** Reset
|
|
152
|
-
reset(cwd) {
|
|
151
|
+
/** Reset all configuration or only the selected section to defaults. */
|
|
152
|
+
reset(cwd, section = 'all') {
|
|
153
|
+
if (!['agents', 'swarm', 'memory', 'mcp', 'providers', 'all'].includes(section)) {
|
|
154
|
+
throw new Error(`Unknown configuration section: ${section}`);
|
|
155
|
+
}
|
|
153
156
|
this.selectProject(cwd);
|
|
154
157
|
const targetPath = this.findConfig(cwd) ?? path.resolve(cwd, CONFIG_FILENAMES[0]);
|
|
155
|
-
const config =
|
|
158
|
+
const config = section === 'all'
|
|
159
|
+
? structuredClone(DEFAULT_CONFIG)
|
|
160
|
+
: structuredClone(this.load(cwd) ?? {});
|
|
161
|
+
if (section === 'providers') {
|
|
162
|
+
// Provider defaults are supplied by the provider command, so remove
|
|
163
|
+
// only the persisted override, as a whole reset already does.
|
|
164
|
+
delete config.providers;
|
|
165
|
+
}
|
|
166
|
+
else if (section !== 'all') {
|
|
167
|
+
config[section] = structuredClone(DEFAULT_CONFIG[section]);
|
|
168
|
+
}
|
|
169
|
+
if (section !== 'all') {
|
|
170
|
+
for (const key of Object.keys(config)) {
|
|
171
|
+
if (key.startsWith(`${section}.`))
|
|
172
|
+
delete config[key];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
156
175
|
this.writeAtomic(targetPath, config);
|
|
157
176
|
this.config = config;
|
|
158
177
|
this.configPath = targetPath;
|
|
@@ -22,6 +22,14 @@ export type AnchorAssessment = {
|
|
|
22
22
|
};
|
|
23
23
|
export type AnchorWriter = (file: string, value: unknown) => Promise<void>;
|
|
24
24
|
export declare const anchorLogPath: (projectRoot: string) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Directory holding per-project trust material (anchor mirror, HMAC key).
|
|
27
|
+
* Resolved from the environment at call time (#3919): $XDG_CONFIG_HOME when it
|
|
28
|
+
* is absolute, else $HOME/.config (os.homedir() honours $HOME), so a scratch
|
|
29
|
+
* HOME keeps tests and sandboxed runs out of the developer's real home. An XDG
|
|
30
|
+
* user who already has trust state under the legacy ~/.config keeps using it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function policyTrustRoot(): string;
|
|
25
33
|
/**
|
|
26
34
|
* How the stored anchors relate to `state`; read-only. `rebuild` is the
|
|
27
35
|
* explicit-establish path: an unreadable log or mirror is treated as absent, but
|
|
@@ -13,14 +13,29 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from 'node:crypto';
|
|
15
15
|
import { existsSync, mkdirSync, readFileSync, realpathSync } from 'node:fs';
|
|
16
|
-
import { dirname, join, resolve } from 'node:path';
|
|
17
|
-
import {
|
|
16
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
18
|
const LOG_FILE = 'ledger-anchors.json';
|
|
19
19
|
const MIRROR_FILE = 'ledger-anchor-head.json';
|
|
20
20
|
export const anchorLogPath = (projectRoot) => join(resolve(projectRoot), '.claude-flow', 'policy', LOG_FILE);
|
|
21
|
+
/**
|
|
22
|
+
* Directory holding per-project trust material (anchor mirror, HMAC key).
|
|
23
|
+
* Resolved from the environment at call time (#3919): $XDG_CONFIG_HOME when it
|
|
24
|
+
* is absolute, else $HOME/.config (os.homedir() honours $HOME), so a scratch
|
|
25
|
+
* HOME keeps tests and sandboxed runs out of the developer's real home. An XDG
|
|
26
|
+
* user who already has trust state under the legacy ~/.config keeps using it.
|
|
27
|
+
*/
|
|
28
|
+
export function policyTrustRoot() {
|
|
29
|
+
const legacy = join(homedir(), '.config', 'ruflo', 'policy-trust');
|
|
30
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
31
|
+
if (!xdg || !isAbsolute(xdg))
|
|
32
|
+
return legacy;
|
|
33
|
+
const preferred = join(xdg, 'ruflo', 'policy-trust');
|
|
34
|
+
return !existsSync(preferred) && existsSync(legacy) ? legacy : preferred;
|
|
35
|
+
}
|
|
21
36
|
function mirrorPath(projectRoot) {
|
|
22
37
|
const id = createHash('sha256').update(realpathSync(projectRoot)).digest('hex');
|
|
23
|
-
return join(
|
|
38
|
+
return join(policyTrustRoot(), id, MIRROR_FILE);
|
|
24
39
|
}
|
|
25
40
|
function entryHash(entry) {
|
|
26
41
|
return createHash('sha256').update(JSON.stringify([
|
|
@@ -5,7 +5,7 @@ import { closeSync, constants as fsConstants, existsSync, fstatSync, lstatSync,
|
|
|
5
5
|
import { dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { hostname, userInfo } from 'node:os';
|
|
7
7
|
import { syncPolicyProjection } from '../mods/policy-projection.js';
|
|
8
|
-
import { assessAnchors, recordAnchor } from './policy-ledger-anchor.js';
|
|
8
|
+
import { assessAnchors, policyTrustRoot, recordAnchor } from './policy-ledger-anchor.js';
|
|
9
9
|
const POLICY_DIR = join('.claude-flow', 'policy');
|
|
10
10
|
const POLICY_FILE = 'state.json';
|
|
11
11
|
const LOCK_FILE = 'state.lock';
|
|
@@ -194,7 +194,7 @@ async function writeJsonAtomic(file, value) {
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
function trustPaths(projectRoot) {
|
|
197
|
-
const trustRoot =
|
|
197
|
+
const trustRoot = policyTrustRoot();
|
|
198
198
|
const projectId = createHash('sha256').update(realpathSync(projectRoot)).digest('hex');
|
|
199
199
|
const dir = join(trustRoot, projectId);
|
|
200
200
|
return { key: join(dir, 'anchor.key'), anchor: join(dir, 'state.anchor.json') };
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.56.
|
|
3
|
+
"version": "3.56.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruflo-metaharness",
|
|
3
3
|
"description": "MetaHarness integration for ruflo — surfaces score/genome/mint/mcp-scan/threat-model via skills; pairs with @metaharness/router (ADR-148/149) for cost-optimal model routing; honors ADR-150's architectural constraint that MetaHarness remains an optional augmentation, never a required runtime dependency. As a mod (ADR-445): confirms flywheel promotion, keeps secrets out of inputs, /metaharness-mod, and a status file the console shows",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ruvnet",
|
|
7
7
|
"url": "https://github.com/ruvnet"
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Newest first. One `## <version> — <date>` heading per version, then `feat:`, `fix:`, `breaking:` and `chore:` bullets (ADR-478). Built from git history; this is the whole history.
|
|
4
4
|
|
|
5
|
+
## 0.2.5 — 2026-10-09
|
|
6
|
+
- fix: seven harness-* skill descriptions no longer carry angle-bracket placeholders (claude.ai strips them as XML tags)
|
|
7
|
+
|
|
5
8
|
## 0.2.4 — 2026-10-07
|
|
6
9
|
- fix: run the installed ruflo CLI before `npx @claude-flow/cli@latest` in cost-tracker, adr and metaharness
|
|
7
10
|
- chore: 3.55.0 versions, leaf pins, lockfiles, plugin bumps, changelog
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Structural smoke test for ruflo-metaharness v0.2.
|
|
2
|
+
# Structural smoke test for ruflo-metaharness v0.2.5 (ADR-150 Phase 1).
|
|
3
3
|
set -u
|
|
4
4
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
5
|
PASS=0
|
|
@@ -32,10 +32,10 @@ else
|
|
|
32
32
|
bad "extraction-regex-rot: EXPECTED_TOOLS=$EXPECTED_TOOLS EXPECTED_SUBS=$EXPECTED_SUBS"
|
|
33
33
|
fi
|
|
34
34
|
|
|
35
|
-
step "1. plugin.json declares 0.2.
|
|
35
|
+
step "1. plugin.json declares 0.2.5 with adr-150 keywords"
|
|
36
36
|
v=$(grep -E '"version"' "$ROOT/.claude-plugin/plugin.json" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
|
37
|
-
if [[ "$v" != "0.2.
|
|
38
|
-
bad "expected 0.2.
|
|
37
|
+
if [[ "$v" != "0.2.5" ]]; then
|
|
38
|
+
bad "expected 0.2.5, got '$v'"
|
|
39
39
|
else
|
|
40
40
|
miss=""
|
|
41
41
|
for k in ruflo metaharness harness scorecard genome mcp-scan threat-model router adr-150 adr-148 adr-149 optional-dependency graceful-degradation subprocess phase-1-mvp; do
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-bench
|
|
3
|
-
description: Manage `@metaharness/darwin` bench suites — `bench create
|
|
3
|
+
description: Manage `@metaharness/darwin` bench suites — `bench create {repo}` scaffolds a JSON suite from a repo's test corpus; `bench verify {suite.json}` checks suite well-formedness. Bench suites are the fixed evaluation corpora that `harness-evolve --bench {suite.json}` scores variants against, decoupling evolution from the repo's natural tests. Degrades gracefully when @metaharness/darwin is absent.
|
|
4
4
|
argument-hint: "--op create --repo <path> [--out <path>] | --op verify --suite <path>"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-evolve
|
|
3
|
-
description: Run `@metaharness/darwin evolve
|
|
3
|
+
description: Run `@metaharness/darwin evolve {repo}` to mutate a harness's seven policy surfaces (planner/contextBuilder/reviewer/retryPolicy/toolPolicy/memoryPolicy/scorePolicy), sandbox-score each variant, and promote only measured wins. The model is frozen; the harness evolves. Closes the loop ADR-150 opens (score+genome describe; evolve changes). Degrades gracefully when @metaharness/darwin is absent (ADR-150 + ADR-153 architectural constraints).
|
|
4
4
|
argument-hint: "--repo <path> [--generations 3] [--children 3] [--concurrency 2] [--sandbox real|mock|agent] [--selection pareto|quality-diversity|...] [--mutator deterministic|ruvllm] [--diagnose] [--confirm]"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-genome
|
|
3
|
-
description: 7-section repo readiness report from `metaharness genome
|
|
3
|
+
description: 7-section repo readiness report from `metaharness genome {path}`. Returns repo_type / agent_topology / risk_score / mcp_surface / test_confidence / publish_readiness. Pure-read; degrades gracefully (ADR-150).
|
|
4
4
|
argument-hint: "[--path .] [--alert-on-risk-above 0.5] [--format table|json]"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-mcp-scan
|
|
3
|
-
description: Static security scan of a harness's declared MCP surface via `harness mcp-scan
|
|
3
|
+
description: Static security scan of a harness's declared MCP surface via `harness mcp-scan {path}`. Reads `.mcp/servers.json` + `.harness/claims.json`. Pure-read, no dispatch. Exits 1 on findings at or above `--fail-on` severity.
|
|
4
4
|
argument-hint: "[--path .] [--fail-on low|medium|high] [--format table|json]"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-mint
|
|
3
|
-
description: Scaffold a custom AI agent harness via `metaharness new
|
|
3
|
+
description: Scaffold a custom AI agent harness via `metaharness new {name} --template {id} --host {id}`. Defaults to DRY-RUN (no writes) unless --confirm is passed. Refuses to write to the calling repo root or anywhere inside it. Honors ADR-150 architectural constraint + ruflo's "destructive-action confirmation" pattern.
|
|
4
4
|
argument-hint: "--name <id> --template <vertical:coding|minimal|…> [--host claude-code|codex|…] [--target /abs/path] [--confirm] [--format table|json]"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-score
|
|
3
|
-
description: 5-dimension harness readiness scorecard from `metaharness score
|
|
3
|
+
description: 5-dimension harness readiness scorecard from `metaharness score {path}`. Returns harnessFit / compileConfidence / taskCoverage / toolSafety / memoryUsefulness + estCostPerRunUsd + scaffoldReady. Pure-read; subprocess invocation; degrades gracefully when MetaHarness is absent (ADR-150 architectural constraint).
|
|
4
4
|
argument-hint: "[--path .] [--alert-on-fit-below 70] [--format table|json]"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: harness-threat-model
|
|
3
|
-
description: Enterprise-review-grade threat model from `harness threat-model
|
|
3
|
+
description: Enterprise-review-grade threat model from `harness threat-model {path}`. Categorizes MCP-surface threats; emits `worst: 'clean'|'low'|'medium'|'high'` + per-threat findings. Pure-read.
|
|
4
4
|
argument-hint: "[--path .] [--fail-on clean|low|medium|high] [--format table|json]"
|
|
5
5
|
allowed-tools: Bash
|
|
6
6
|
---
|