neohive 6.3.0 → 6.4.1
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 +110 -6
- package/cli.js +391 -64
- package/dashboard.html +107 -21
- package/dashboard.js +36 -5
- package/package.json +1 -1
- package/server.js +185 -147
- package/tools/governance.js +28 -6
- package/tools/messaging.js +2 -0
- package/tools/safety.js +3 -3
- package/tools/tasks.js +5 -5
package/tools/governance.js
CHANGED
|
@@ -115,11 +115,13 @@ module.exports = function (ctx) {
|
|
|
115
115
|
reviews.push(review);
|
|
116
116
|
writeJsonFile(REVIEWS_FILE, reviews);
|
|
117
117
|
|
|
118
|
-
broadcastSystemMessage(`[REVIEW] ${state.registeredName} requests review of "${review.file}": ${review.description || 'No description'}.
|
|
118
|
+
broadcastSystemMessage(`[REVIEW REQUEST] ${state.registeredName} requests review of "${review.file}": ${review.description || 'No description'}. To review: (1) read the file "${review.file}", (2) call submit_review("${review.id}", "approved"/"changes_requested", "<your findings — min 50 chars>"). Feedback is required and must be substantive.`, state.registeredName);
|
|
119
119
|
touchActivity();
|
|
120
120
|
return { success: true, review_id: review.id, file: review.file, next_action: 'Call listen() to wait for the review.' };
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
const REVIEW_FEEDBACK_MIN_LENGTH = 50;
|
|
124
|
+
|
|
123
125
|
function toolSubmitReview(reviewId, status, feedback) {
|
|
124
126
|
if (!state.registeredName) return { error: 'You must call register() first' };
|
|
125
127
|
|
|
@@ -131,6 +133,26 @@ module.exports = function (ctx) {
|
|
|
131
133
|
if (!review) return { error: `Review not found: ${reviewId}` };
|
|
132
134
|
if (review.requested_by === state.registeredName) return { error: 'Cannot review your own code.' };
|
|
133
135
|
|
|
136
|
+
// Enforce substantive feedback — rubber-stamping is not allowed
|
|
137
|
+
const feedbackText = (feedback || '').trim();
|
|
138
|
+
if (!feedbackText) {
|
|
139
|
+
return {
|
|
140
|
+
error: `Feedback is required. You must read "${review.file}" and describe what you found before submitting a review.`,
|
|
141
|
+
next_action: `Read the file "${review.file}" first, then call submit_review("${reviewId}", "${status}", "<your findings>").`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (feedbackText.length < REVIEW_FEEDBACK_MIN_LENGTH) {
|
|
145
|
+
return {
|
|
146
|
+
error: `Feedback too short (${feedbackText.length} chars, minimum ${REVIEW_FEEDBACK_MIN_LENGTH}). Describe specific findings — what you read, what issues you found or verified, and why you ${status === 'approved' ? 'approve' : 'request changes'}.`,
|
|
147
|
+
next_action: `Read the file "${review.file}" first, then call submit_review("${reviewId}", "${status}", "<your detailed findings>").`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Log audit entry for thin approvals (short feedback on an approval)
|
|
152
|
+
if (status === 'approved' && feedbackText.length < 150) {
|
|
153
|
+
logViolation('thin_review', state.registeredName, `Approved "${review.file}" with minimal feedback (${feedbackText.length} chars): "${feedbackText.substring(0, 100)}"`);
|
|
154
|
+
}
|
|
155
|
+
|
|
134
156
|
review.status = status;
|
|
135
157
|
review.reviewer = state.registeredName;
|
|
136
158
|
review.feedback = (feedback || '').substring(0, 2000);
|
|
@@ -384,12 +406,12 @@ module.exports = function (ctx) {
|
|
|
384
406
|
{
|
|
385
407
|
name: 'request_review',
|
|
386
408
|
description: 'Request a code review from the team. Creates a review request and notifies all agents.',
|
|
387
|
-
inputSchema: { type: 'object', properties: { file_path: { type: 'string', description: 'File to review' }, description: { type: 'string', description: 'What to focus on in the review' } }, required: ['file_path'], additionalProperties: false },
|
|
409
|
+
inputSchema: { type: 'object', properties: { file_path: { type: 'string', description: 'File to review', maxLength: 500 }, description: { type: 'string', description: 'What to focus on in the review', maxLength: 2000 } }, required: ['file_path'], additionalProperties: false },
|
|
388
410
|
},
|
|
389
411
|
{
|
|
390
412
|
name: 'submit_review',
|
|
391
|
-
description: 'Submit a code review — approve or request changes
|
|
392
|
-
inputSchema: { type: 'object', properties: { review_id: { type: 'string', description: 'Review ID' }, status: { type: 'string', enum: ['approved', 'changes_requested'], description: 'Review result' }, feedback: { type: 'string', description: 'Your
|
|
413
|
+
description: 'Submit a code review — approve or request changes. You MUST read the file under review before calling this. Feedback is required (minimum 50 chars) and must describe specific findings — what you read, what issues you found or confirmed. Rubber-stamp approvals are rejected.',
|
|
414
|
+
inputSchema: { type: 'object', properties: { review_id: { type: 'string', description: 'Review ID', maxLength: 50 }, status: { type: 'string', enum: ['approved', 'changes_requested'], description: 'Review result' }, feedback: { type: 'string', description: 'Your findings from reading the file (required, min 50 chars). Describe what you read and what you found — bugs, security issues, correctness, or confirmation that the code is clean.', maxLength: 2000, minLength: 50 } }, required: ['review_id', 'status', 'feedback'], additionalProperties: false },
|
|
393
415
|
},
|
|
394
416
|
// Rules
|
|
395
417
|
{
|
|
@@ -398,7 +420,7 @@ module.exports = function (ctx) {
|
|
|
398
420
|
inputSchema: {
|
|
399
421
|
type: 'object',
|
|
400
422
|
properties: {
|
|
401
|
-
text: { type: 'string', description: 'The rule text' },
|
|
423
|
+
text: { type: 'string', description: 'The rule text', maxLength: 2000 },
|
|
402
424
|
category: { type: 'string', description: 'Rule category: safety, workflow, code-style, communication, custom' },
|
|
403
425
|
scope: {
|
|
404
426
|
type: 'object',
|
|
@@ -433,7 +455,7 @@ module.exports = function (ctx) {
|
|
|
433
455
|
{
|
|
434
456
|
name: 'log_violation',
|
|
435
457
|
description: 'Log a workflow rule violation to the audit trail. Used automatically by review gates, or manually to flag issues.',
|
|
436
|
-
inputSchema: { type: 'object', properties: { type: { type: 'string', description: 'Violation type: review_skipped, push_without_approval, rule_violated, etc.' }, details: { type: 'string', description: 'Description of the violation' } }, required: ['type'], additionalProperties: false },
|
|
458
|
+
inputSchema: { type: 'object', properties: { type: { type: 'string', description: 'Violation type: review_skipped, push_without_approval, rule_violated, etc.', maxLength: 100 }, details: { type: 'string', description: 'Description of the violation', maxLength: 2000 } }, required: ['type'], additionalProperties: false },
|
|
437
459
|
},
|
|
438
460
|
{
|
|
439
461
|
name: 'request_push_approval',
|
package/tools/messaging.js
CHANGED
|
@@ -57,6 +57,8 @@ module.exports = function (ctx) {
|
|
|
57
57
|
const oldestAge = Math.round((Date.now() - new Date(unconsumed[0].timestamp).getTime()) / 1000);
|
|
58
58
|
result.urgency = oldestAge > 120 ? 'critical' : oldestAge > 30 ? 'urgent' : 'normal';
|
|
59
59
|
result.next_action = 'Call listen() to receive and process these messages.';
|
|
60
|
+
} else {
|
|
61
|
+
result.next_action = 'Call listen() to wait for new messages.';
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
return result;
|
package/tools/safety.js
CHANGED
|
@@ -48,15 +48,15 @@ module.exports = function (ctx) {
|
|
|
48
48
|
if (lock.agent === state.registeredName) { delete locks[fp]; count++; }
|
|
49
49
|
}
|
|
50
50
|
writeJsonFile(LOCKS_FILE, locks);
|
|
51
|
-
return { success: true, unlocked: count, message: `Unlocked ${count} file(s)
|
|
51
|
+
return { success: true, unlocked: count, message: `Unlocked ${count} file(s).`, next_action: 'Call listen() to receive messages.' };
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
if (!locks[normalized]) return { success: true, message: 'File was not locked.' };
|
|
54
|
+
if (!locks[normalized]) return { success: true, message: 'File was not locked.', next_action: 'Call listen() to receive messages.' };
|
|
55
55
|
if (locks[normalized].agent !== state.registeredName) return { error: `File is locked by ${locks[normalized].agent}, not you.` };
|
|
56
56
|
|
|
57
57
|
delete locks[normalized];
|
|
58
58
|
writeJsonFile(LOCKS_FILE, locks);
|
|
59
|
-
return { success: true, file: normalized, message: 'File unlocked.' };
|
|
59
|
+
return { success: true, file: normalized, message: 'File unlocked.', next_action: 'Call listen() to receive messages.' };
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// --- Dependencies ---
|
package/tools/tasks.js
CHANGED
|
@@ -398,9 +398,9 @@ module.exports = function (ctx) {
|
|
|
398
398
|
inputSchema: {
|
|
399
399
|
type: 'object',
|
|
400
400
|
properties: {
|
|
401
|
-
title: { type: 'string', description: 'Short task title' },
|
|
402
|
-
description: { type: 'string', description: 'Detailed task description' },
|
|
403
|
-
assignee: { type: 'string', description: 'Agent to assign to (optional, auto-assigns with 2 agents)' },
|
|
401
|
+
title: { type: 'string', description: 'Short task title', maxLength: 200 },
|
|
402
|
+
description: { type: 'string', description: 'Detailed task description', maxLength: 5000 },
|
|
403
|
+
assignee: { type: 'string', description: 'Agent to assign to (optional, auto-assigns with 2 agents)', maxLength: 50 },
|
|
404
404
|
},
|
|
405
405
|
required: ['title'],
|
|
406
406
|
additionalProperties: false,
|
|
@@ -412,9 +412,9 @@ module.exports = function (ctx) {
|
|
|
412
412
|
inputSchema: {
|
|
413
413
|
type: 'object',
|
|
414
414
|
properties: {
|
|
415
|
-
task_id: { type: 'string', description: 'Task ID to update' },
|
|
415
|
+
task_id: { type: 'string', description: 'Task ID to update', maxLength: 50 },
|
|
416
416
|
status: { type: 'string', enum: ['pending', 'in_progress', 'in_review', 'done', 'blocked', 'blocked_permanent'], description: 'New status' },
|
|
417
|
-
notes: { type: 'string', description: 'Optional progress note' },
|
|
417
|
+
notes: { type: 'string', description: 'Optional progress note', maxLength: 2000 },
|
|
418
418
|
},
|
|
419
419
|
required: ['task_id', 'status'],
|
|
420
420
|
additionalProperties: false,
|