Smart sync - save progress to Linear with concise updates
Sync progress to Linear with smart checklist updates and concise comments. Auto-detects issue from git branch, analyzes changes, and updates your progress with minimal input. Use after completing work to track progress without verbose updates.
/plugin marketplace add duongdev/ccpm/plugin install ccpm@duongdev-ccpm-marketplace[issue-id] [summary]Auto-detects issue from git branch and syncs progress to Linear with smart checklist updates and concise comments.
ALL Linear operations MUST use the Task tool with ccpm:linear-operations subagent.
// ✅ CORRECT - Use Task tool with subagent
Task({
subagent_type: "ccpm:linear-operations",
prompt: `operation: get_issue\nparams:\n issueId: WORK-26\ncontext:\n cache: true`
})
// ❌ WRONG - Direct MCP call
mcp__agent-mcp-gateway__execute_tool({ server: "linear", ... })
Linear is internal tracking. Execute ALL operations immediately:
NEVER ask user: "Do you want me to sync?" or "Approve?"
Focus: Scannable summary + detailed context using Linear's native collapsible syntax
Structure:
🔄 Summary (always visible)
+++ 📋 Context for Next Session
[Detailed context here]
+++
Linear's Native Syntax: The +++ Title syntax creates a true collapsible section that starts collapsed and expands on click.
# Auto-detect issue from git branch (full interactive mode)
/ccpm:sync
# Explicit issue ID
/ccpm:sync PSN-29
# With custom summary (still prompts for checklist items)
/ccpm:sync PSN-29 "Completed auth implementation"
# Auto-detect with summary
/ccpm:sync "Finished UI components"
# Quick mode: skip checklist prompt entirely (just add comment)
/ccpm:sync PSN-29 "Quick update" --quick
/ccpm:sync --quick "Just a note"
const args = process.argv.slice(2);
let issueId = args[0];
let summary = args[1];
let quickMode = false;
const ISSUE_ID_PATTERN = /^[A-Z]+-\d+$/;
// Check for --quick flag (skip checklist prompt entirely)
if (args.includes('--quick')) {
quickMode = true;
// Remove flag from args
const flagIndex = args.indexOf('--quick');
args.splice(flagIndex, 1);
}
// If first arg looks like summary (not issue ID), treat as summary
if (args[0] && !ISSUE_ID_PATTERN.test(args[0]) && !args[0].startsWith('--')) {
summary = args[0];
issueId = null;
}
// Auto-detect from git branch if no issue ID
if (!issueId) {
console.log("🔍 Auto-detecting issue from git branch...");
const branch = await Bash('git rev-parse --abbrev-ref HEAD');
const match = branch.match(/([A-Z]+-\d+)/);
if (!match) {
return error(`
❌ Could not detect issue ID from branch name
Current branch: ${branch}
Usage: /ccpm:sync [ISSUE-ID] [summary]
Examples:
/ccpm:sync PSN-29
/ccpm:sync PSN-29 "Completed feature X"
/ccpm:sync "Made progress on auth"
`);
}
issueId = match[1];
console.log(`✅ Detected issue: ${issueId}\n`);
}
if (!ISSUE_ID_PATTERN.test(issueId)) {
return error(`Invalid issue ID: ${issueId}. Expected format: PROJ-123`);
}
# Get all git information in one call
git status --porcelain && echo "---" && \
git diff --stat HEAD && echo "---" && \
git diff --cached --stat
Parse output:
const changes = {
modified: [],
added: [],
deleted: [],
insertions: 0,
deletions: 0
};
// Parse git status (M, A, D, R, ??)
lines.forEach(line => {
const [status, file] = line.trim().split(/\s+/);
if (status === 'M') changes.modified.push(file);
else if (status === 'A' || status === '??') changes.added.push(file);
else if (status === 'D') changes.deleted.push(file);
});
Use the Task tool:
Invoke ccpm:linear-operations:
operation: get_issue
params:
issueId: "{issue ID from step 1}"
context:
cache: true
command: "sync"
if (!summary && changes.modified.length + changes.added.length > 0) {
const parts = [];
if (changes.modified.length > 0) {
parts.push(`Updated ${changes.modified.length} file(s)`);
}
if (changes.added.length > 0) {
parts.push(`Added ${changes.added.length} new file(s)`);
}
if (changes.deleted.length > 0) {
parts.push(`Deleted ${changes.deleted.length} file(s)`);
}
summary = parts.join(', ') || 'Work in progress';
}
Extract unchecked items and score based on git changes:
const checklistItems = issue.description.match(/- \[ \] (.+)/g) || [];
const uncheckedItems = checklistItems.map((item, idx) => ({
index: idx,
text: item.replace('- [ ] ', ''),
score: 0
}));
uncheckedItems.forEach(item => {
const keywords = extractKeywords(item.text);
// File path matching (30 points)
changes.modified.concat(changes.added).forEach(file => {
if (keywords.some(kw => file.toLowerCase().includes(kw))) {
item.score += 30;
}
});
// File name exact match (40 points)
if (changes.modified.some(f => matchesPattern(f, item.text))) {
item.score += 40;
}
// Large changes (10-20 points)
const totalLines = changes.insertions + changes.deletions;
if (totalLines > 50) item.score += 10;
if (totalLines > 100) item.score += 20;
});
// Categorize by confidence
const highConfidence = uncheckedItems.filter(i => i.score >= 50);
const mediumConfidence = uncheckedItems.filter(i => i.score >= 30 && i.score < 50);
AskUserQuestion({
questions: [
{
question: "Which checklist items did you complete? (AI suggestions pre-selected)",
header: "Completed",
multiSelect: true,
options: uncheckedItems.map(item => ({
label: `${item.text}`,
description: item.score >= 50
? "🤖 SUGGESTED - High confidence"
: item.score >= 30
? "💡 Possible match"
: "Mark as complete"
}))
}
]
});
A) Update checklist in description (v1.0 fix - actually update it!):
If user selected checklist items to complete in Step 6:
Use the Task tool:
Invoke ccpm:linear-operations:
operation: update_checklist_items
params:
issue_id: "{issue ID}"
indices: [{completed item indices from step 6}]
mark_complete: true
add_comment: false # We'll add our own concise comment below
update_timestamp: true
context:
command: "sync"
purpose: "Update checklist based on git changes and user confirmation"
This operation will:
- \[([ x])\] (.+))- [ ] to - [x] for selected indicesTrack result:
const checklistUpdateResult = {
itemsUpdated: selectedIndices.length,
previousProgress: checklist.progress.percentage,
newProgress: result.data.checklist_summary.new_progress
};
B) Add HYBRID progress comment (concise summary + detailed context):
Use the Task tool:
Invoke ccpm:linear-operations:
operation: create_comment
params:
issueId: "{issue ID}"
body: |
🔄 **Synced** | {branch name}
{summary or auto-generated from git changes}
**Files**: {modified} modified, {added} added (+{insertions}, -{deletions})
**Checklist**: {checklistUpdateResult.itemsUpdated} completed
**Progress**: {checklistUpdateResult.previousProgress}% → {checklistUpdateResult.newProgress}%
+++ 📋 Context for Next Session
**Changed Files**:
{changes.modified.map(f => `- ${f}`).join('\n')}
{changes.added.length > 0 ? '\n**New Files**:\n' + changes.added.map(f => `- ${f}`).join('\n') : ''}
**Completed Items** (this session):
{completedItems.map(item => `- ✅ ${item.text}`).join('\n')}
**Remaining Work**:
{remainingItems.slice(0, 5).map(item => `- ⏳ ${item.text}`).join('\n')}
{remainingItems.length > 5 ? `\n_...and ${remainingItems.length - 5} more items_` : ''}
**Git Summary**:
```
{git diff --stat output or summary}
```
**Key Insights**:
- {any important notes or blockers discovered}
- {technical decisions made}
- {next logical steps}
+++
context:
command: "sync"
purpose: "Hybrid: scannable summary + detailed session context"
Use actual data from checklist update (Step 7A):
itemsUpdated: From checklistUpdateResult.itemsUpdatedpreviousProgress: From checklistUpdateResult.previousProgressnewProgress: From checklistUpdateResult.newProgressThis ensures the comment reflects the ACTUAL checklist state, not estimated values!
Example with Linear's native collapsible:
🔄 **Synced** | feature/psn-29-auth
Completed auth implementation, all tests passing
**Files**: 8 modified, 2 added (+234, -67)
**Checklist**: 2 completed
**Progress**: 40% → 60%
+++ 📋 Context for Next Session
**Changed Files**:
- src/auth/jwt.ts
- src/auth/middleware.ts
- src/auth/routes.ts
- src/tests/auth.test.ts
**New Files**:
- src/utils/validators.ts
- src/types/auth.d.ts
**Completed Items** (this session):
- ✅ Implement JWT authentication
- ✅ Add login form validation
**Remaining Work**:
- ⏳ Add password reset flow
- ⏳ Implement OAuth providers
- ⏳ Write integration tests
- ⏳ Update documentation
**Git Summary**:
8 files changed, 234 insertions(+), 67 deletions(-)
**Key Insights**:
- Used jsonwebtoken library for JWT implementation
- Chose bcrypt for password hashing (industry standard)
- Next: Password reset requires email service setup
+++
How it appears in Linear:
Comparison:
❌ OLD (verbose - 500+ words):
## 🔄 Progress Sync
**Timestamp**: 2025-11-23T10:30:00Z
**Branch**: feature/psn-29-auth
### 📝 Summary
Completed auth implementation, all tests passing
### 📊 Code Changes
**Files Changed**: 10 (+234, -67)
**Modified**:
- src/auth/jwt.ts
- src/auth/middleware.ts
- src/auth/routes.ts
- src/tests/auth.test.ts
... and 4 more
**New Files**:
- src/utils/validators.ts
- src/types/auth.d.ts
### 📋 Checklist Updated
**Completed This Session**:
- ✅ Implement JWT authentication
- ✅ Add login form
### 🎯 Current Progress
- Overall: 60% complete (6/10 items)
- This session: 2 items completed
- Remaining: 4 items
---
*Synced via /ccpm:sync*
✅ NEW (concise - 50 words):
🔄 **Synced** | feature/psn-29-auth
Completed auth implementation, all tests passing
**Files**: 8 modified, 2 added (+234, -67)
**Checklist**: 2 completed
**Progress**: 40% → 60%
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ Progress Synced to Linear!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 Issue: ${issueId} - ${issue.title}
🔗 ${issue.url}
📝 Synced:
✅ ${totalFiles} files changed
✅ ${completedItems.length} checklist items updated
💬 Concise comment added
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 Next Actions
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. ⭐ Continue work
2. 📝 Commit changes /ccpm:commit
3. ✅ Run verification /ccpm:verify
4. 🔍 View status /ccpm:utils:status ${issueId}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
If user provides summary, use streamlined flow with optional checklist update:
Flow:
// Quick mode still offers checklist selection
if (summary) {
console.log(`📝 Summary: "${summary}"\n`);
// Parse checklist for quick selection
const checklist = parseChecklist(issue.description);
if (checklist && checklist.items.some(i => !i.checked)) {
const uncheckedItems = checklist.items.filter(i => !i.checked);
// Show numbered list for quick reference
console.log('📋 Unchecked items:');
uncheckedItems.forEach((item, idx) => {
console.log(` [${item.index}] ${item.content}`);
});
// Ask which to mark complete (can skip with empty selection)
AskUserQuestion({
questions: [{
question: "Mark any items as complete? (skip to just add comment)",
header: "Checklist",
multiSelect: true,
options: uncheckedItems.slice(0, 4).map(item => ({
label: item.content.substring(0, 50) + (item.content.length > 50 ? '...' : ''),
description: `Index ${item.index}`
}))
}]
});
// If items selected, update checklist
if (selectedIndices.length > 0) {
// Call update_checklist_items (same as full mode)
}
}
// Always add comment with summary
// ...
}
Example:
/ccpm:sync PSN-29 "Completed auth implementation, all tests passing"
Output:
📝 Summary: "Completed auth implementation, all tests passing"
📋 Unchecked items:
[0] Implement JWT authentication
[2] Add password validation
[3] Write unit tests
? Mark any items as complete? (skip to just add comment)
☐ Implement JWT authentication
☐ Add password validation
☐ Write unit tests
☐ Skip - just add comment
[User selects items or skips]
✅ Quick sync complete!
💬 Comment added to Linear
📋 Checklist: 2 items completed (if selected)
📊 Progress: 40% → 60% (if updated)
Skip checklist entirely:
If no checklist exists or user wants pure quick mode, add --quick flag:
/ccpm:sync PSN-29 "Quick update" --quick
# → Skips checklist prompt entirely, just adds comment
❌ Invalid issue ID format: proj123
Expected format: PROJ-123
ℹ️ No uncommitted changes detected
You can still sync progress with a manual summary:
/ccpm:sync PSN-29 "Updated documentation"
❌ Could not detect issue ID from branch
Current branch: main
Usage: /ccpm:sync [ISSUE-ID]
Example: /ccpm:sync PSN-29
# Branch: feature/PSN-29-add-auth
/ccpm:sync
# Output:
# 🔍 Auto-detecting issue from git branch...
# ✅ Detected issue: PSN-29
#
# 📊 Detected Changes:
# Modified: 3 files (+127, -45)
#
# 🤖 AI Suggestions:
# ✅ Implement JWT authentication (High confidence)
# ✅ Add login form (High confidence)
#
# [Interactive checklist update...]
#
# ✅ Progress Synced to Linear!
# 💬 Concise comment added (50 words vs old 500+)
/ccpm:sync PSN-29 "Finished refactoring auth module"
# Output:
# ✅ Quick sync complete!
# 💬 Concise comment added to Linear
# Branch: feature/PSN-29-add-auth
/ccpm:sync "Completed UI components, tests passing"
# Output:
# ✅ Detected issue: PSN-29
# ✅ Quick sync complete!
# 💬 Concise comment: 3 lines (was 50+ lines before)
Benefits:
+++ syntax (true collapsible, not auto-collapse)Use Cases:
Technical Detail:
Linear's +++ Title syntax creates a true collapsible section that:
/ccpm:sync to save progress frequently/ccpm:commit for git commits/ccpm:verify for quality checks/ccpm:work to continuefeature/PSN-29-add-auth