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.
@@ -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'}. Call submit_review("${review.id}", "approved"/"changes_requested", "your feedback") to review.`, state.registeredName);
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 with feedback.',
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 review feedback (max 2000 chars)' } }, required: ['review_id', 'status'], additionalProperties: false },
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',
@@ -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,