Skip to content

Release 0.43.0#1607

Merged
Crunchyman-ralph merged 21 commits intomainfrom
next
Jan 27, 2026
Merged

Release 0.43.0#1607
Crunchyman-ralph merged 21 commits intomainfrom
next

Conversation

@Crunchyman-ralph
Copy link
Collaborator

@Crunchyman-ralph Crunchyman-ralph commented Jan 25, 2026

What type of PR is this?

  • 🐛 Bug fix
  • ✨ Feature
  • 🔌 Integration
  • 📝 Docs
  • 🧹 Refactor
  • Other:

Description

Related Issues

How to Test This

# Example commands or steps

Expected result:

Contributor Checklist

  • Created changeset: npm run changeset
  • Tests pass: npm test
  • Format check passes: npm run format-check (or npm run format to fix)
  • Addressed CodeRabbit comments (if any)
  • Linked related issues (if any)
  • Manually tested the changes

Changelog Entry


For Maintainers

  • PR title follows conventional commits
  • Target branch correct
  • Labels added
  • Milestone assigned (if applicable)

Note

Adds one-click Claude Desktop install and introduces durable task metadata with MCP updates, plus a streaming loop UX.

  • New MCP Bundle: manifest.json, .mcpbignore, icon.png; version script syncs manifest.json and builds .mcpb bundle
  • Loop enhancements: -v/--verbose real-time streaming, --no-output, improved error handling/validation, progress header tweaks; new LoopOutputCallbacks, includeOutput, brief
  • Task model: optional metadata on tasks/subtasks preserved across AI ops/storage; MCP tools update_task/update_subtask accept metadata (gated by TASK_MASTER_ALLOW_METADATA_UPDATES=true); validation util added
  • Storage: non-destructive updates preserve subtask metadata; expose FileOperations; bridge/API path passes useResearch and metadata
  • Docs/Tests: docs for metadata usage and MCP config; extensive metadata preservation tests; presets wording updated to "Taskmaster"
  • CI/Chore: forward-port workflow; lockfile/dev deps for MCPB

Written by Cursor Bugbot for commit 712a078. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Single-click bundle for Claude Desktop install; CLI: --verbose/-v for live streaming and --no-output to hide full responses; new modifyJSON utility; optional user-defined metadata on tasks/subtasks with MCP support.
  • Bug Fixes

    • Cross-process file locking and atomic writes to prevent data loss; improved command error handling and incompatible-option validation.
  • Documentation

    • Branding and docs updated; new metadata documentation and changelog entries.
  • Tests

    • Extensive metadata preservation and storage integration tests added.

✏️ Tip: You can customize this high-level summary in your review settings.

Crunchyman-ralph and others added 7 commits January 15, 2026 16:58
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
Fixes #1589
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: triepod-ai <199543909+triepod-ai@users.noreply.github.com>
Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
fix for #1585 (part 2: legacy utils.js pattern)
@changeset-bot
Copy link

changeset-bot bot commented Jan 25, 2026

🦋 Changeset detected

Latest commit: 712a078

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 25, 2026

📝 Walkthrough

Walkthrough

Adds user-defined Task.metadata with full plumbing (types, entity, storage merge, MCP validation and tools, bridge), introduces verbose streaming loop output with LoopOutputCallbacks and CLI flags, exports FileOperations + modifyJSON, adds MCP bundle/packaging automation, extensive tests, and a forward-port workflow.

Changes

Cohort / File(s) Summary
MCPB bundle & assets
manifest.json, .mcpbignore, icon.png, .changeset/add-mcpb-bundle.md
Add MCP Bundle manifest, ignore rules, icon, and changelog entry for Claude Desktop single-click installation.
Version sync & packaging script
.github/scripts/sync-manifest-version.mjs, package.json
New script syncs manifest.json version with package.json, cleans old bundles, packs new .mcpb; version script updated and @anthropic-ai/mcpb dev dep added.
Task metadata (types & entity)
packages/tm-core/src/common/types/index.ts, packages/tm-core/src/modules/tasks/entities/task.entity.ts
Add optional metadata to Task types and TaskEntity; include in serialization and public types.
Storage metadata merge & preservation
packages/tm-core/src/modules/storage/adapters/.../file-storage.ts, packages/tm-core/src/modules/tasks/entities/task.entity.spec.ts
Preserve and merge task/subtask metadata during updates; add unit tests for metadata round-trips.
Loop types, domain & service (streaming + callbacks)
packages/tm-core/src/modules/loop/types.ts, .../loop/loop-domain.ts, .../loop/services/loop.service.ts, packages/tm-core/src/modules/loop/index.ts
Add LoopOutputCallbacks, extend LoopConfig (includeOutput/verbose/brief/callbacks), implement verbose stream-json execution with real-time callbacks, richer error handling, and output aggregation.
CLI wiring & tests
apps/cli/src/commands/loop.command.ts, apps/cli/src/commands/loop.command.spec.ts
Add --no-output and -v/--verbose flags, wire createOutputCallbacks and brief detection from auth context; update tests/mocks.
MCP metadata validation & tools
apps/mcp/src/shared/utils.ts, mcp-server/src/tools/update-task.js, mcp-server/src/tools/update-subtask.js, mcp-server/src/core/direct-functions/*.js
Add validateMcpMetadata, gate metadata updates via env flag, accept optional metadata params, require prompt or metadata, and propagate parsed metadata through direct/core calls.
Bridge propagation
packages/tm-bridge/src/update-bridge.ts
Extend UpdateBridgeParams with useResearch? and metadata? and forward them to remote update calls.
Atomic JSON utilities & core export
scripts/modules/utils.js, packages/tm-core/src/index.ts, packages/.../storage/adapters/.../file-operations.js*
Export FileOperations from core and add modifyJSON wrapper using file-ops for safe atomic read-modify-write; scripts use it.
Presets & branding text
packages/tm-core/src/modules/loop/presets/*.ts, .../loop/services/loop.service.spec.ts
Normalize header text from "Task Master" → "Taskmaster" across presets and tests.
Tests: metadata coverage & integrations
packages/tm-core/tests/integration/**/metadata-*.test.ts, packages/tm-core/tests/integration/storage/*.test.ts
Add extensive unit and integration tests validating metadata preservation, merging, validation, and MCP gating.
Scripts, TUI/REPL fallback & CLI runtime
scripts/modules/commands.js, scripts/modules/utils.js, tests/setup.js
Add loadTUI stub and improved REPL fallback/help behavior, expose modifyJSON helper, and add Jest afterAll cleanup for signal listeners.
Repo automation & workflow
.github/workflows/forward-port.yml
Add forward-port workflow to create PRs from mainnext, detect conflicts, and include conflict details in PR body.
Docs & changelog
apps/docs/capabilities/task-structure.mdx, CHANGELOG.md, docs/examples/claude-code-usage.md, .changeset/*.md
Document metadata field and usage, update changelog entries, fix minor docs typos, and add changesets for new features/flags.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant LoopService
    participant Claude
    participant Callbacks

    User->>CLI: run loop --verbose
    CLI->>LoopService: run(config { verbose: true, callbacks })
    LoopService->>LoopService: validate config (reject verbose + sandbox)
    LoopService->>Callbacks: onIterationStart(iter, total)
    LoopService->>Claude: spawn child (command with --stream-json)
    Claude-->>LoopService: stream JSON lines
    LoopService->>LoopService: parse lines into events
    alt valid event
        LoopService->>Callbacks: onText/onToolUse/onStderr/onOutput
        LoopService->>LoopService: buffer/aggregate output
    else invalid event
        LoopService->>Callbacks: onError / log
    end
    LoopService->>Callbacks: onIterationEnd(iteration)
    LoopService-->>CLI: final LoopResult (output / errorMessage)
    CLI->>User: display result
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Release 0.43.0' is directly related to the changeset and accurately summarizes the main deliverable—a version release with specific features.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Crunchyman-ralph and others added 9 commits January 25, 2026 11:57
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
Fixes #1589
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: triepod-ai <199543909+triepod-ai@users.noreply.github.com>
Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
fix for #1585 (part 2: legacy utils.js pattern)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

3-52: Changelog version doesn’t match release target (0.43.0).
PR objective says this is the 0.43.0 release, but the top entry is still 0.42.0. Please add a 0.43.0 section (or confirm the release target) and place the new release notes under it so the changelog aligns with the published version.

🤖 Fix all issues with AI agents
In @.github/scripts/sync-manifest-version.mjs:
- Around line 76-85: The spawnSync call to run 'npx mcpb pack' can set
result.error (e.g., ENOENT) while result.status is null; update the error
handling after spawnSync(spawnSync, result, bundleName, rootDir) to treat any
result.error as a failure in addition to non-zero exit codes: if result.error or
result.status !== 0, log a descriptive message including result.error (and
optionally result.stdout/stderr) and call process.exit(1). Ensure you reference
the existing variables result, bundleName, and rootDir when locating the fix.

In `@tests/setup.js`:
- Around line 55-65: The afterAll cleanup currently calls
process.removeAllListeners(signal) which indiscriminately drops all handlers;
change it to preserve baseline listeners captured at suite start and only remove
listeners added by this suite: at setup (before tests run) record original
listeners for each signal via process.listeners(signal) and store them (e.g., in
a module-scope map), then in afterAll iterate signals and remove only listeners
that are not in the saved baseline (or restore by removing all and re-attaching
only the saved originals), updating the existing afterAll block (referencing the
afterAll callback, the listeners array, and the use of
process.removeAllListeners) to use the selective removal/restoration approach.
🧹 Nitpick comments (4)
packages/tm-core/src/modules/loop/services/loop.service.ts (2)

331-337: Consider documenting the 50MB buffer size rationale.

The maxBuffer of 50MB is significant. While this aligns with the comment in LoopIteration.output documentation mentioning "up to 50MB per iteration," adding a brief inline comment here would help future maintainers understand why this specific limit was chosen.

📝 Suggested documentation
 const result = spawnSync(command, args, {
   cwd: this.projectRoot,
   encoding: 'utf-8',
+  // Claude CLI output can be large; matches LoopConfig.includeOutput docs
   maxBuffer: 50 * 1024 * 1024,
   stdio: ['inherit', 'pipe', 'pipe']
 });

430-436: Redundant JSON check before logging parse error.

The condition on line 432 checks line.trim().startsWith('{') but processLine already exits early if !line.startsWith('{') on line 414. This inner check will always be true when reached.

♻️ Simplified error logging
 } catch (error) {
-  // Log malformed JSON for debugging (non-JSON lines like system output are expected)
-  if (line.trim().startsWith('{')) {
-    const parseError = `Failed to parse JSON event: ${error instanceof Error ? error.message : 'Unknown error'}. Line: ${line.substring(0, 100)}...`;
-    this.reportError(callbacks, parseError, 'warning');
-  }
+  // Log malformed JSON for debugging (we only reach here for lines starting with '{')
+  const parseError = `Failed to parse JSON event: ${error instanceof Error ? error.message : 'Unknown error'}. Line: ${line.substring(0, 100)}...`;
+  this.reportError(callbacks, parseError, 'warning');
 }
apps/cli/src/commands/loop.command.spec.ts (1)

253-330: Consider adding test coverage for new --verbose and --no-output options.

The execute integration tests don't cover the new verbose and output options. While existing tests verify the core loop functionality, tests for the new streaming/output control behavior would improve confidence.

Consider adding tests like:

  • Verify callbacks are passed to loop.run config
  • Verify verbose: true when --verbose is provided
  • Verify includeOutput: false when --no-output is provided
.github/scripts/sync-manifest-version.mjs (1)

63-71: Consider adding error handling for file deletion.

The unlinkSync call could throw if the file is locked or permissions are insufficient. While unlikely in a release context, wrapping this in a try-catch would make the script more robust.

♻️ Add error handling for file deletion
 for (const file of files) {
   if (file.endsWith('.mcpb')) {
     const filePath = join(rootDir, file);
     console.log(`Removing old bundle: ${file}`);
-    unlinkSync(filePath);
+    try {
+      unlinkSync(filePath);
+    } catch (error) {
+      console.warn(`Warning: Failed to remove ${file}: ${error.message}`);
+    }
   }
 }

Comment on lines +76 to +85
const result = spawnSync('npx', ['mcpb', 'pack', '.', bundleName], {
cwd: rootDir,
encoding: 'utf8',
stdio: 'inherit'
});

if (result.status !== 0) {
console.error('Failed to generate MCPB bundle');
process.exit(1);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle spawn error case explicitly.

If npx itself fails to execute (e.g., ENOENT), result.error will be set but result.status may be null. The current check only looks at status !== 0, which would pass when status is null.

🐛 Proposed fix
+if (result.error) {
+  console.error('Failed to execute mcpb:', result.error.message);
+  process.exit(1);
+}
+
 if (result.status !== 0) {
   console.error('Failed to generate MCPB bundle');
   process.exit(1);
 }
🤖 Prompt for AI Agents
In @.github/scripts/sync-manifest-version.mjs around lines 76 - 85, The
spawnSync call to run 'npx mcpb pack' can set result.error (e.g., ENOENT) while
result.status is null; update the error handling after spawnSync(spawnSync,
result, bundleName, rootDir) to treat any result.error as a failure in addition
to non-zero exit codes: if result.error or result.status !== 0, log a
descriptive message including result.error (and optionally result.stdout/stderr)
and call process.exit(1). Ensure you reference the existing variables result,
bundleName, and rootDir when locating the fix.

Comment on lines +55 to +65
// Clean up signal-exit listeners after all tests to prevent open handle warnings
// This is needed because packages like proper-lockfile register signal handlers
afterAll(async () => {
// Give any pending async operations time to complete
await new Promise((resolve) => setImmediate(resolve));

// Clean up any registered signal handlers from signal-exit
const listeners = ['SIGINT', 'SIGTERM', 'SIGHUP'];
for (const signal of listeners) {
process.removeAllListeners(signal);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid removing all signal listeners globally.

process.removeAllListeners(signal) can drop Jest/other cleanup handlers for subsequent suites. Prefer removing only listeners added during this suite (or restoring the original set) to avoid breaking signal handling.

🛠️ Proposed fix (remove only non-baseline listeners)
+const signalExitSignals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
+const baselineSignalListeners = new Map(
+	signalExitSignals.map((signal) => [signal, process.listeners(signal)])
+);
+
 // Clean up signal-exit listeners after all tests to prevent open handle warnings
 // This is needed because packages like proper-lockfile register signal handlers
 
 afterAll(async () => {
 	// Give any pending async operations time to complete
 	await new Promise((resolve) => setImmediate(resolve));
 
-	// Clean up any registered signal handlers from signal-exit
-	const listeners = ['SIGINT', 'SIGTERM', 'SIGHUP'];
-	for (const signal of listeners) {
-		process.removeAllListeners(signal);
-	}
+	// Clean up any registered signal handlers from signal-exit
+	for (const signal of signalExitSignals) {
+		const baseline = baselineSignalListeners.get(signal) || [];
+		for (const listener of process.listeners(signal)) {
+			if (!baseline.includes(listener)) {
+				process.removeListener(signal, listener);
+			}
+		}
+	}
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Clean up signal-exit listeners after all tests to prevent open handle warnings
// This is needed because packages like proper-lockfile register signal handlers
afterAll(async () => {
// Give any pending async operations time to complete
await new Promise((resolve) => setImmediate(resolve));
// Clean up any registered signal handlers from signal-exit
const listeners = ['SIGINT', 'SIGTERM', 'SIGHUP'];
for (const signal of listeners) {
process.removeAllListeners(signal);
}
const signalExitSignals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
const baselineSignalListeners = new Map(
signalExitSignals.map((signal) => [signal, process.listeners(signal)])
);
// Clean up signal-exit listeners after all tests to prevent open handle warnings
// This is needed because packages like proper-lockfile register signal handlers
afterAll(async () => {
// Give any pending async operations time to complete
await new Promise((resolve) => setImmediate(resolve));
// Clean up any registered signal handlers from signal-exit
for (const signal of signalExitSignals) {
const baseline = baselineSignalListeners.get(signal) || [];
for (const listener of process.listeners(signal)) {
if (!baseline.includes(listener)) {
process.removeListener(signal, listener);
}
}
}
});
🤖 Prompt for AI Agents
In `@tests/setup.js` around lines 55 - 65, The afterAll cleanup currently calls
process.removeAllListeners(signal) which indiscriminately drops all handlers;
change it to preserve baseline listeners captured at suite start and only remove
listeners added by this suite: at setup (before tests run) record original
listeners for each signal via process.listeners(signal) and store them (e.g., in
a module-scope map), then in afterAll iterate signals and remove only listeners
that are not in the saved baseline (or restore by removing all and re-attaching
only the saved originals), updating the existing afterAll block (referencing the
afterAll callback, the listeners array, and the use of
process.removeAllListeners) to use the selective removal/restoration approach.

Crunchyman-ralph and others added 2 commits January 26, 2026 15:32
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cedric Hurst <cedric@spantree.net>
Closes #1555
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/tm-core/src/common/types/index.ts (1)

258-270: Type guard isTaskStatus is missing 'completed' status.

The TaskStatus type (line 28) includes 'completed', but the isTaskStatus type guard only checks for 'review' and omits 'completed'. This inconsistency could cause valid tasks with status: 'completed' to fail the type guard check.

Proposed fix
 export function isTaskStatus(value: unknown): value is TaskStatus {
 	return (
 		typeof value === 'string' &&
 		[
 			'pending',
 			'in-progress',
 			'done',
 			'deferred',
 			'cancelled',
 			'blocked',
-			'review'
+			'review',
+			'completed'
 		].includes(value)
 	);
 }
mcp-server/src/tools/update-subtask.js (2)

27-51: Make projectRoot optional in the MCP tool schema.
Requiring it blocks clients that rely on withNormalizedProjectRoot/session defaults. Switch to .optional() so the tool works without an explicit path.

🛠️ Suggested fix
-			projectRoot: z
-				.string()
-				.describe('The directory of the project. Must be an absolute path.'),
+			projectRoot: z
+				.string()
+				.optional()
+				.describe('The directory of the project. Must be an absolute path.'),
As per coding guidelines, ...

60-66: Sanitize logged args now that metadata can include sensitive data.
Logging the full args will now include user metadata (and potentially large payloads/PII). Redact metadata (and ideally prompt) before logging.

🛡️ Suggested fix
-				log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
+				const safeArgs = {
+					...args,
+					metadata: args.metadata ? '[redacted]' : undefined
+				};
+				log.info(`Updating subtask with args: ${JSON.stringify(safeArgs)}`);
As per coding guidelines, ...
🤖 Fix all issues with AI agents
In `@scripts/modules/task-manager/update-subtask-by-id.js`:
- Around line 80-87: The prompt validation currently calls prompt.trim() without
ensuring prompt is a string and also allows forwarding non-string prompts to the
remote update; fix this by introducing a single boolean flag (e.g.,
isPromptEmpty) computed after a type check like typeof prompt === 'string' ?
prompt.trim() === '' : true, use that flag in the existing guard that throws
when both prompt is empty and metadata is falsy, and use the same flag/guard
before sending the remote update so you never forward a non-string prompt; apply
the identical change to the other prompt validation block later in the file (the
second prompt/metadata check).
🧹 Nitpick comments (2)
apps/docs/capabilities/task-structure.mdx (1)

58-60: Clarify the “AI-Safe” claim to match opt‑in metadata updates.
The wording “never overwritten by AI” is absolute, but later you document explicit MCP metadata updates. Suggest softening to “preserved by default unless explicitly updated” to avoid a contradiction.

✏️ Suggested tweak
-  AI operations preserve your metadata - it's never overwritten by AI.
+  AI operations preserve metadata by default unless you explicitly update it.
packages/tm-core/tests/integration/storage/file-storage-metadata.test.ts (1)

51-156: Consider adding a tagged-format metadata round-trip.
FileStorage supports tags; adding at least one saveTasks/loadTasks/updateTask path with a non-default tag would ensure metadata preservation across tagged task lists.

Based on learnings, ...

Comment on lines +80 to 87
// Allow metadata-only updates (no prompt required if metadata is provided)
if (
(!prompt || typeof prompt !== 'string' || prompt.trim() === '') &&
!metadata
) {
throw new Error(
'Prompt cannot be empty. Please provide context for the subtask update.'
'Prompt cannot be empty unless metadata is provided. Please provide context for the subtask update or metadata to merge.'
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard prompt type before calling .trim()
With metadata present, the current validation allows non-string prompt, but the metadata-only path calls prompt.trim() and will throw. It also forwards a non-string prompt to the remote path. Add a type guard and reuse a single isPromptEmpty flag.

Proposed fix
 		// Basic validation - ID must be present
 		if (!subtaskId || typeof subtaskId !== 'string') {
 			throw new Error('Subtask ID cannot be empty.');
 		}
+		if (typeof prompt !== 'undefined' && typeof prompt !== 'string') {
+			throw new Error('Prompt must be a string when provided.');
+		}
+		const isPromptEmpty = !prompt || prompt.trim() === '';
 		// Allow metadata-only updates (no prompt required if metadata is provided)
-		if (
-			(!prompt || typeof prompt !== 'string' || prompt.trim() === '') &&
-			!metadata
-		) {
+		if (isPromptEmpty && !metadata) {
 			throw new Error(
 				'Prompt cannot be empty unless metadata is provided. Please provide context for the subtask update or metadata to merge.'
 			);
 		}
@@
-		if (metadata && (!prompt || prompt.trim() === '')) {
+		if (metadata && isPromptEmpty) {

Also applies to: 177-180

🤖 Prompt for AI Agents
In `@scripts/modules/task-manager/update-subtask-by-id.js` around lines 80 - 87,
The prompt validation currently calls prompt.trim() without ensuring prompt is a
string and also allows forwarding non-string prompts to the remote update; fix
this by introducing a single boolean flag (e.g., isPromptEmpty) computed after a
type check like typeof prompt === 'string' ? prompt.trim() === '' : true, use
that flag in the existing guard that throws when both prompt is empty and
metadata is falsy, and use the same flag/guard before sending the remote update
so you never forward a non-string prompt; apply the identical change to the
other prompt validation block later in the file (the second prompt/metadata
check).

…1612)

The MDX parser was interpreting indented </Accordion> tags as list items,
causing deployment failures.
eyaltoledano
eyaltoledano previously approved these changes Jan 26, 2026
Co-authored-by: Ed Sumerfield <esumerfield@stackct.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

(st) =>
String(st.id) === String(updatedSubtask.id) ||
(updatedSubtask.title && st.title === updatedSubtask.title)
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subtask metadata matching uses wrong priority order

Medium Severity

The subtask metadata matching logic uses || which allows title matching to take precedence over ID matching when a title-matching subtask appears earlier in the array. The comment states the intent is to "fall back to title match if IDs don't match," but the || operator doesn't implement a fallback—it treats both conditions as equal alternatives. This can cause metadata from the wrong subtask to be preserved when an updated subtask has the same title as a different original subtask.

Fix in Cursor Fix in Web

@Crunchyman-ralph Crunchyman-ralph merged commit 99dbd08 into main Jan 27, 2026
20 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants