fix(worktree): address CodeRabbit PR review feedback

- Document --branch override in npx-cli help text
- Guard ContextBuilder against empty projects[] override; fall back to cwd-derived primary
- Ensure merged_into_project indexes are created even if ALTER ran in a prior partial migration
- Reject adopt --branch/--cwd flags with missing or flag-like values
- Use defined --color-border-primary token for merged badge border

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-16 20:03:27 -07:00
parent d24f3a7019
commit f6fda8fff4
8 changed files with 27 additions and 17 deletions
+1 -1
View File
@@ -758,4 +758,4 @@ ${c.gray}${"\u2500".repeat(60)}${c.reset}
${c.dim}No previous sessions found for this project yet.${c.reset}
`}function lt(r,e,t,s){let n=[];return s?n.push(...tt(r)):n.push(...He(r)),s?n.push(...st()):n.push(...Xe()),s?n.push(...rt()):n.push(...Ge()),s?n.push(...nt()):n.push(...Be()),q(t)&&(s?n.push(...ot(e,t)):n.push(...We(e,t))),n}var _e=L(require("path"),1);function z(r){if(!r)return[];try{let e=JSON.parse(r);return Array.isArray(e)?e:[]}catch(e){return _.debug("PARSER","Failed to parse JSON array, using empty fallback",{preview:r?.substring(0,50)},e),[]}}function pe(r){return new Date(r).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function le(r){return new Date(r).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function gt(r){return new Date(r).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function Et(r,e){return _e.default.isAbsolute(r)?_e.default.relative(e,r):r}function Tt(r,e,t){let s=z(r);if(s.length>0)return Et(s[0],e);if(t){let n=z(t);if(n.length>0)return Et(n[0],e)}return"General"}function Yt(r){let e=new Map;for(let s of r){let n=s.type==="observation"?s.data.created_at:s.data.displayTime,o=gt(n);e.has(o)||e.set(o,[]),e.get(o).push(s)}let t=Array.from(e.entries()).sort((s,n)=>{let o=new Date(s[0]).getTime(),i=new Date(n[0]).getTime();return o-i});return new Map(t)}function ft(r,e){return e.fullObservationField==="narrative"?r.narrative:r.facts?z(r.facts).join(`
`):null}function qt(r,e,t,s){let n=[];n.push(...Ye(r));let o="";for(let i of e)if(i.type==="summary"){let a=i.data,d=pe(a.displayTime);n.push(...Je(a,d))}else{let a=i.data,d=le(a.created_at),u=d!==o?d:"";if(o=d,t.has(a.id)){let E=ft(a,s);n.push(...Ke(a,u,E,s))}else n.push(Ve(a,u,s))}return n}function Vt(r,e,t,s,n){let o=[];o.push(...it(r));let i=null,a="";for(let d of e)if(d.type==="summary"){i=null,a="";let m=d.data,u=pe(m.displayTime);o.push(...mt(m,u))}else{let m=d.data,u=Tt(m.files_modified,n,m.files_read),l=le(m.created_at),E=l!==a;a=l;let T=t.has(m.id);if(u!==i&&(o.push(...at(u)),i=u),T){let O=ft(m,s);o.push(...ct(m,l,E,O,s))}else o.push(dt(m,l,E,s))}return o.push(""),o}function Kt(r,e,t,s,n,o){return o?Vt(r,e,t,s,n):qt(r,e,t,s)}function St(r,e,t,s,n){let o=[],i=Yt(r);for(let[a,d]of i)o.push(...Kt(a,d,e,t,s,n));return o}function bt(r,e,t){return!(!r.showLastSummary||!e||!!!(e.investigated||e.learned||e.completed||e.next_steps)||t&&e.created_at_epoch<=t.created_at_epoch)}function ht(r,e){let t=[];return e?(t.push(...F("Investigated",r.investigated,c.blue)),t.push(...F("Learned",r.learned,c.yellow)),t.push(...F("Completed",r.completed,c.green)),t.push(...F("Next Steps",r.next_steps,c.magenta))):(t.push(...$("Investigated",r.investigated)),t.push(...$("Learned",r.learned)),t.push(...$("Completed",r.completed)),t.push(...$("Next Steps",r.next_steps))),t}function Ot(r,e){return e?ut(r):ze(r)}function At(r,e,t){return!q(e)||r.totalDiscoveryTokens<=0||r.savings<=0?[]:t?_t(r.totalDiscoveryTokens,r.totalReadTokens):Qe(r.totalDiscoveryTokens,r.totalReadTokens)}var Jt=Rt.default.join((0,Nt.homedir)(),".claude","plugins","marketplaces","thedotmack","plugin",".install-version");function zt(){try{return new B}catch(r){if(r.code==="ERR_DLOPEN_FAILED"){try{(0,Ct.unlinkSync)(Jt)}catch(e){_.debug("SYSTEM","Marker file cleanup failed (may not exist)",{},e)}return _.error("SYSTEM","Native module rebuild needed - restart Claude Code to auto-fix"),null}throw r}}function Qt(r,e){return e?pt(r):Ze(r)}function Zt(r,e,t,s,n,o,i){let a=[],d=ae(e);a.push(...lt(r,d,s,i));let m=t.slice(0,s.sessionCount),u=we(m,t),l=ue(e,u),E=Pe(e,s.fullObservationCount);a.push(...St(l,E,s,n,i));let T=t[0],O=e[0];bt(s,T,O)&&a.push(...ht(T,i));let S=me(e,s,o,n);return a.push(...Ot(S,i)),a.push(...At(d,s,i)),a.join(`
`).trimEnd()}async function Ee(r,e=!1){let t=ne(),s=r?.cwd??process.cwd(),n=te(s),o=r?.platform_source,i=r?.projects??n.allProjects,a=i[i.length-1];r?.full&&(t.totalObservationCount=999999,t.sessionCount=999999);let d=zt();if(!d)return"";try{let m=i.length>1?$e(d,i,t,o):de(d,a,t,o),u=i.length>1?Fe(d,i,t,o):ce(d,a,t,o);return m.length===0&&u.length===0?Qt(a,e):Zt(a,m,u,t,s,r?.session_id,e)}finally{d.close()}}0&&(module.exports={generateContext});
`).trimEnd()}async function Ee(r,e=!1){let t=ne(),s=r?.cwd??process.cwd(),n=te(s),o=r?.platform_source,i=r?.projects?.length?r.projects:n.allProjects,a=i[i.length-1]??n.primary;r?.full&&(t.totalObservationCount=999999,t.sessionCount=999999);let d=zt();if(!d)return"";try{let m=i.length>1?$e(d,i,t,o):de(d,a,t,o),u=i.length>1?Fe(d,i,t,o):ce(d,a,t,o);return m.length===0&&u.length===0?Qt(a,e):Zt(a,m,u,t,s,r?.session_id,e)}finally{d.close()}}0&&(module.exports={generateContext});
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1139,7 +1139,7 @@
letter-spacing: 0.02em;
color: var(--color-text-muted);
background: var(--color-type-badge-bg);
border: 1px solid var(--color-border);
border: 1px solid var(--color-border-primary);
opacity: 0.85;
}
+1 -1
View File
@@ -52,7 +52,7 @@ ${pc.bold('Runtime Commands')} (requires Bun, delegates to installed plugin):
${pc.cyan('npx claude-mem restart')} Restart worker service
${pc.cyan('npx claude-mem status')} Show worker status
${pc.cyan('npx claude-mem search <query>')} Search observations
${pc.cyan('npx claude-mem adopt [--dry-run]')} Stamp merged worktrees into parent project
${pc.cyan('npx claude-mem adopt [--dry-run] [--branch <name>]')} Stamp merged worktrees into parent project
${pc.cyan('npx claude-mem transcript watch')} Start transcript watcher
${pc.bold('IDE Identifiers')}:
+2 -2
View File
@@ -136,8 +136,8 @@ export async function generateContext(
// `project` (used for header + single-project query) is always the last entry
// of `projects` so the empty-state header and the query target stay in sync
// when a caller passes `projects` without a matching cwd (e.g. worker route).
const projects = input?.projects ?? context.allProjects;
const project = projects[projects.length - 1];
const projects = input?.projects?.length ? input.projects : context.allProjects;
const project = projects[projects.length - 1] ?? context.primary;
// Full mode: fetch all observations but keep normal rendering (level 1 summaries)
if (input?.full) {
+6 -6
View File
@@ -937,19 +937,19 @@ export class MigrationRunner {
.all() as TableColumnInfo[];
if (!obsCols.some(c => c.name === 'merged_into_project')) {
this.db.run('ALTER TABLE observations ADD COLUMN merged_into_project TEXT');
this.db.run(
'CREATE INDEX IF NOT EXISTS idx_observations_merged_into ON observations(merged_into_project)'
);
}
this.db.run(
'CREATE INDEX IF NOT EXISTS idx_observations_merged_into ON observations(merged_into_project)'
);
const sumCols = this.db
.query('PRAGMA table_info(session_summaries)')
.all() as TableColumnInfo[];
if (!sumCols.some(c => c.name === 'merged_into_project')) {
this.db.run('ALTER TABLE session_summaries ADD COLUMN merged_into_project TEXT');
this.db.run(
'CREATE INDEX IF NOT EXISTS idx_summaries_merged_into ON session_summaries(merged_into_project)'
);
}
this.db.run(
'CREATE INDEX IF NOT EXISTS idx_summaries_merged_into ON session_summaries(merged_into_project)'
);
}
}
+12 -2
View File
@@ -1211,11 +1211,21 @@ async function main() {
case 'adopt': {
const dryRun = process.argv.includes('--dry-run');
const branchIndex = process.argv.indexOf('--branch');
const onlyBranch = branchIndex !== -1 ? process.argv[branchIndex + 1] : undefined;
const branchValue = branchIndex !== -1 ? process.argv[branchIndex + 1] : undefined;
if (branchIndex !== -1 && (!branchValue || branchValue.startsWith('--'))) {
console.error('Usage: adopt [--dry-run] [--branch <branch>] [--cwd <path>]');
process.exit(1);
}
const onlyBranch = branchValue;
// Honor an explicit --cwd override so the NPX CLI can pass through the
// user's working directory (the spawn sets cwd to the marketplace dir).
const cwdIndex = process.argv.indexOf('--cwd');
const repoPath = cwdIndex !== -1 ? process.argv[cwdIndex + 1] : process.cwd();
const cwdValue = cwdIndex !== -1 ? process.argv[cwdIndex + 1] : undefined;
if (cwdIndex !== -1 && (!cwdValue || cwdValue.startsWith('--'))) {
console.error('Usage: adopt [--dry-run] [--branch <branch>] [--cwd <path>]');
process.exit(1);
}
const repoPath = cwdValue ?? process.cwd();
const result = await adoptMergedWorktrees({ repoPath, dryRun, onlyBranch });
+1 -1
View File
@@ -1139,7 +1139,7 @@
letter-spacing: 0.02em;
color: var(--color-text-muted);
background: var(--color-type-badge-bg);
border: 1px solid var(--color-border);
border: 1px solid var(--color-border-primary);
opacity: 0.85;
}