@thinkermd/cli 0.1.23 → 0.1.26

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.
Files changed (2) hide show
  1. package/dist/tk.js +49 -23
  2. package/package.json +1 -1
package/dist/tk.js CHANGED
@@ -1,31 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import{Command as Mo}from"commander";import Se from"chalk";import{join as at}from"path";import{writeFile as Qt}from"fs/promises";import k from"chalk";import Be from"ora";import{homedir as Ut}from"os";import{join as G}from"path";import{readFile as Bt,writeFile as Ht,mkdir as Ie}from"fs/promises";import{existsSync as Kt}from"fs";var y=".thinker.md",A="config.json",et="structure.json",O="CONTEXT.md",St="contexts",$t="thinker.md.tar.gz",It="metadata.json",Nt="latest",ot="repos",rt="branches";var _t="thinkermd-meta-repos",At="1.0",ft="full",Ot="incremental",Ft=["node_modules",".git","dist","build",".next",".nuxt","coverage","__pycache__",".pytest_cache","venv",".venv","vendor","target",y],jt=3,Mt=1e3,Dt="[Describe the purpose",Lt=100;var U=G(Ut(),".thinkermd","config.json"),J="https://app.thinkermd.dev";function ht(){return process.env.META_REPO_ID&&process.env.JOB_TOKEN?"sandbox":"local"}function Ne(){return{mode:"sandbox",metaRepoId:process.env.META_REPO_ID,branch:process.env.BRANCH,commitSha:process.env.COMMIT_SHA,previousCommitSha:process.env.PREVIOUS_COMMIT_SHA||void 0,jobId:process.env.JOB_ID,callbackUrl:process.env.CALLBACK_URL,jobToken:process.env.JOB_TOKEN,r2Endpoint:process.env.R2_ENDPOINT,r2AccessKeyId:process.env.R2_ACCESS_KEY_ID,r2SecretAccessKey:process.env.R2_SECRET_ACCESS_KEY,r2Bucket:process.env.R2_BUCKET}}async function $(){try{if(Kt(U)){let e=await Bt(U,"utf-8");return JSON.parse(e)}}catch{}return{}}async function nt(e){let t=G(Ut(),".thinkermd");await Ie(t,{recursive:!0,mode:448}),await Ht(U,JSON.stringify(e,null,2),{mode:384})}async function P(){return ht()==="sandbox"?Ne():{mode:"local",...await $()}}var q={version:At,ignore:[...Ft]};async function F(e){let t=G(e,y,A);try{if(Kt(t)){let o=await Bt(t,"utf-8");return JSON.parse(o)}}catch{}return null}async function it(e,t){let o=G(e,y,A);await Ht(o,JSON.stringify(t,null,2))}function Gt(){let t=["META_REPO_ID","BRANCH","COMMIT_SHA","JOB_ID","CALLBACK_URL","JOB_TOKEN","R2_ENDPOINT","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY","R2_BUCKET"].filter(o=>!process.env[o]);if(t.length>0)throw new Error(`Missing required environment variables: ${t.join(", ")}`)}function I(e){return G(e,y)}function Y(e){return G(e,y,St)}import{execFile as _e}from"child_process";import{promisify as Ae}from"util";var Oe=Ae(_e);async function z(e,t="."){let{stdout:o}=await Oe("git",e,{cwd:t});return o.trim()}async function S(e="."){try{return await z(["branch","--show-current"],e)}catch(t){throw new Error(`Failed to get current branch: ${t}`)}}async function _(e="."){try{return await z(["rev-parse","HEAD"],e)}catch(t){throw new Error(`Failed to get current commit: ${t}`)}}async function b(e="."){try{return await z(["rev-parse","--show-toplevel"],e)}catch(t){throw new Error(`Not a git repository: ${t}`)}}async function N(e="."){try{return await z(["rev-parse","--git-dir"],e),!0}catch{return!1}}async function Jt(e=".",t){try{return await z(["log","-1","--format=%s",t||"HEAD"],e)}catch(o){throw new Error(`Failed to get commit message: ${o}`)}}async function yt(e="."){try{return await z(["remote","get-url","origin"],e)}catch{return null}}async function X(e="."){let t=await yt(e);if(!t)return null;let o=t.match(/[/:]([\w-]+)\/([\w.-]+?)(\.git)?$/);return o?`${o[1]}/${o[2]}`:null}import{join as B,relative as Fe,dirname as je}from"path";import{readFile as Me}from"fs/promises";import{existsSync as zt}from"fs";async function De(){let e=await import("ignore");return(e.default??e)()}var Le=[".git",".thinker.md","node_modules","__pycache__",".pytest_cache","venv",".venv",".next",".nuxt","dist","build","coverage","target",".idea",".vscode","*.log","*.lock","package-lock.json","yarn.lock","bun.lockb","pnpm-lock.yaml","Cargo.lock","poetry.lock","Gemfile.lock","*.pyc","*.pyo","*.class","*.o","*.so","*.dylib","*.dll","*.exe"];async function Ue(e){let t=await De();t.add(Le),t.add(q.ignore||[]);try{let o=B(e,".gitignore");if(zt(o)){let r=await Me(o,"utf-8");t.add(r)}}catch{}return t}async function st(e){let t=await Ue(e),o=new Set,r={total:0};await Xt(e,e,t,o,r);let n=Array.from(o).sort();return{version:"1.0",generatedAt:new Date().toISOString(),directories:n,directoryCount:n.length,fileCount:r.total}}async function Xt(e,t,o,r,n){let{readdir:i,stat:s}=await import("fs/promises"),a;try{a=await i(t)}catch{return}for(let l of a){let c=B(t,l),d=Fe(e,c);if(!(o.ignores(d)||o.ignores(d+"/")))try{let m=await s(c);m.isDirectory()?(r.add(d),await Xt(e,c,o,r,n)):m.isFile()&&n.total++}catch{}}}async function Vt(e){let t=B(e,".thinker.md","contexts"),o=[];return zt(B(e,".thinker.md","CONTEXT.md"))&&o.push(""),await qt(t,"",o),o}async function qt(e,t,o){let{readdir:r,stat:n}=await import("fs/promises"),i=t?B(e,t):e,s;try{s=await r(i)}catch{return}for(let a of s){let l=B(i,a),c=t?B(t,a):a;try{(await n(l)).isDirectory()?await qt(e,c,o):a==="CONTEXT.md"&&o.push(je(c))}catch{}}}async function h(e,t,o){let r=await P();if(r.mode!=="sandbox"||!r.callbackUrl||!r.jobToken)return;let n=r.callbackUrl.replace("/callback","/progress"),i={type:"progress",status:"processing",step:e,progress:t,message:o};try{let s=await fetch(n,{method:"POST",headers:{Authorization:`Bearer ${r.jobToken}`,"Content-Type":"application/json"},body:JSON.stringify(i)});s.ok||console.warn(`[Progress] Update failed: ${s.status} ${s.statusText}`)}catch(s){console.warn("[Progress] Update error:",s)}}async function Yt(e,t){let o=await P();if(o.mode!=="sandbox"||!o.callbackUrl||!o.jobToken)return;let r={jobId:o.jobId,status:"completed",artifactUrl:e,stats:t};try{let n=await fetch(o.callbackUrl,{method:"POST",headers:{Authorization:`Bearer ${o.jobToken}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!n.ok)throw console.error(`[Callback] Completion failed: ${n.status} ${n.statusText}`),new Error(`Callback failed: ${n.status}`);console.log("[Callback] Job completed successfully")}catch(n){throw console.error("[Callback] Completion error:",n),n}}async function Wt(e,t){let r=`# ${t||"Project Root"}
2
+ import{Command as jo}from"commander";import Se from"chalk";import{join as at}from"path";import{writeFile as Qt}from"fs/promises";import b from"chalk";import He from"ora";import{homedir as Ut}from"os";import{join as K}from"path";import{readFile as Ht,writeFile as Bt,mkdir as Ie}from"fs/promises";import{existsSync as Gt}from"fs";var y=".thinker.md",A="config.json",et="structure.json",O="CONTEXT.md",St="contexts",$t="thinker.md.tar.gz",It="metadata.json",Nt="latest",ot="repos",rt="branches";var _t="thinkermd-meta-repos",At="1.0",ft="full",Ot="incremental",Ft=["node_modules",".git","dist","build",".next",".nuxt","coverage","__pycache__",".pytest_cache","venv",".venv","vendor","target",y],Mt=3,jt=1e3,Dt="[Describe the purpose",Lt=100;var U=K(Ut(),".thinkermd","config.json"),J="https://app.thinkermd.dev";function ht(){return process.env.META_REPO_ID&&process.env.JOB_TOKEN?"sandbox":"local"}function Ne(){return{mode:"sandbox",metaRepoId:process.env.META_REPO_ID,branch:process.env.BRANCH,commitSha:process.env.COMMIT_SHA,previousCommitSha:process.env.PREVIOUS_COMMIT_SHA||void 0,jobId:process.env.JOB_ID,callbackUrl:process.env.CALLBACK_URL,jobToken:process.env.JOB_TOKEN,r2Endpoint:process.env.R2_ENDPOINT,r2AccessKeyId:process.env.R2_ACCESS_KEY_ID,r2SecretAccessKey:process.env.R2_SECRET_ACCESS_KEY,r2Bucket:process.env.R2_BUCKET}}async function $(){try{if(Gt(U)){let e=await Ht(U,"utf-8");return JSON.parse(e)}}catch{}return{}}async function nt(e){let t=K(Ut(),".thinkermd");await Ie(t,{recursive:!0,mode:448}),await Bt(U,JSON.stringify(e,null,2),{mode:384})}async function P(){return ht()==="sandbox"?Ne():{mode:"local",...await $()}}var V={version:At,ignore:[...Ft]};async function F(e){let t=K(e,y,A);try{if(Gt(t)){let o=await Ht(t,"utf-8");return JSON.parse(o)}}catch{}return null}async function it(e,t){let o=K(e,y,A);await Bt(o,JSON.stringify(t,null,2))}function Kt(){let t=["META_REPO_ID","BRANCH","COMMIT_SHA","JOB_ID","CALLBACK_URL","JOB_TOKEN","R2_ENDPOINT","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY","R2_BUCKET"].filter(o=>!process.env[o]);if(t.length>0)throw new Error(`Missing required environment variables: ${t.join(", ")}`)}function I(e){return K(e,y)}function Y(e){return K(e,y,St)}import{execFile as _e}from"child_process";import{promisify as Ae}from"util";var Oe=Ae(_e);async function X(e,t="."){let{stdout:o}=await Oe("git",e,{cwd:t});return o.trim()}async function S(e="."){try{return await X(["branch","--show-current"],e)}catch(t){throw new Error(`Failed to get current branch: ${t}`)}}async function _(e="."){try{return await X(["rev-parse","HEAD"],e)}catch(t){throw new Error(`Failed to get current commit: ${t}`)}}async function k(e="."){try{return await X(["rev-parse","--show-toplevel"],e)}catch(t){throw new Error(`Not a git repository: ${t}`)}}async function N(e="."){try{return await X(["rev-parse","--git-dir"],e),!0}catch{return!1}}async function Jt(e=".",t){try{return await X(["log","-1","--format=%s",t||"HEAD"],e)}catch(o){throw new Error(`Failed to get commit message: ${o}`)}}async function yt(e="."){try{return await X(["remote","get-url","origin"],e)}catch{return null}}async function z(e="."){let t=await yt(e);if(!t)return null;let o=t.match(/[/:]([\w-]+)\/([\w.-]+?)(\.git)?$/);return o?`${o[1]}/${o[2]}`:null}import{join as H,relative as Fe,dirname as Me}from"path";import{readFile as je}from"fs/promises";import{existsSync as Xt}from"fs";async function De(){let e=await import("ignore");return(e.default??e)()}var Le=[".git",".thinker.md","node_modules","__pycache__",".pytest_cache","venv",".venv",".next",".nuxt","dist","build","coverage","target",".idea",".vscode","*.log","*.lock","package-lock.json","yarn.lock","bun.lockb","pnpm-lock.yaml","Cargo.lock","poetry.lock","Gemfile.lock","*.pyc","*.pyo","*.class","*.o","*.so","*.dylib","*.dll","*.exe"];async function Ue(e){let t=await De();t.add(Le),t.add(V.ignore||[]);try{let o=H(e,".gitignore");if(Xt(o)){let r=await je(o,"utf-8");t.add(r)}}catch{}return t}async function st(e){let t=await Ue(e),o=new Set,r={total:0};await zt(e,e,t,o,r);let n=Array.from(o).sort();return{version:"1.0",generatedAt:new Date().toISOString(),directories:n,directoryCount:n.length,fileCount:r.total}}async function zt(e,t,o,r,n){let{readdir:i,stat:s}=await import("fs/promises"),a;try{a=await i(t)}catch{return}for(let l of a){let c=H(t,l),d=Fe(e,c);if(!(o.ignores(d)||o.ignores(d+"/")))try{let m=await s(c);m.isDirectory()?(r.add(d),await zt(e,c,o,r,n)):m.isFile()&&n.total++}catch{}}}async function qt(e){let t=H(e,".thinker.md","contexts"),o=[];return Xt(H(e,".thinker.md","CONTEXT.md"))&&o.push(""),await Vt(t,"",o),o}async function Vt(e,t,o){let{readdir:r,stat:n}=await import("fs/promises"),i=t?H(e,t):e,s;try{s=await r(i)}catch{return}for(let a of s){let l=H(i,a),c=t?H(t,a):a;try{(await n(l)).isDirectory()?await Vt(e,c,o):a==="CONTEXT.md"&&o.push(Me(c))}catch{}}}async function h(e,t,o){let r=await P();if(r.mode!=="sandbox"||!r.callbackUrl||!r.jobToken)return;let n=r.callbackUrl.replace("/callback","/progress"),i={type:"progress",status:"processing",step:e,progress:t,message:o};try{let s=await fetch(n,{method:"POST",headers:{Authorization:`Bearer ${r.jobToken}`,"Content-Type":"application/json"},body:JSON.stringify(i)});s.ok||console.warn(`[Progress] Update failed: ${s.status} ${s.statusText}`)}catch(s){console.warn("[Progress] Update error:",s)}}async function Yt(e,t){let o=await P();if(o.mode!=="sandbox"||!o.callbackUrl||!o.jobToken)return;let r={jobId:o.jobId,status:"completed",artifactUrl:e,stats:t};try{let n=await fetch(o.callbackUrl,{method:"POST",headers:{Authorization:`Bearer ${o.jobToken}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!n.ok)throw console.error(`[Callback] Completion failed: ${n.status} ${n.statusText}`),new Error(`Callback failed: ${n.status}`);console.log("[Callback] Job completed successfully")}catch(n){throw console.error("[Callback] Completion error:",n),n}}async function Wt(e,t){let r=`# ${t||"Project Root"}
3
3
 
4
- <!--
5
- This context file should describe:
6
- - Purpose of this directory
7
- - Key files and their roles
8
- - Important patterns and conventions
9
- - Dependencies and relationships
10
- -->
4
+ > [One-sentence summary: what this directory contains and its primary purpose]
11
5
 
12
- ## Overview
6
+ ## Purpose
13
7
 
14
- [Describe the purpose of this directory]
8
+ ## Key Concepts
15
9
 
16
- ## Key Files
17
-
18
- [List important files and what they do]
10
+ ## Files
19
11
 
20
12
  ## Patterns
21
13
 
22
- [Document any patterns or conventions used]
23
-
24
14
  ## Dependencies
25
15
 
26
- [Note any important dependencies or relationships]
27
- `;await Qt(e,r)}async function He(e,t,o=!1){let{existsSync:r}=await import("fs"),n=Y(e),i=0,s=0,a=at(I(e),"CONTEXT.md");o&&r(a)?s++:(await Wt(a,""),i++);for(let l of t){let c=at(n,l),d=at(c,"CONTEXT.md"),{mkdir:m}=await import("fs/promises");if(await m(c,{recursive:!0}),o&&r(d)){s++;continue}await Wt(d,l),i++}return{created:i,skipped:s}}async function Zt(e){let t=Be(),o=process.cwd();try{await N(o)||(console.error(k.red("Error: Not a git repository")),console.log("Please run this command from the root of a git repository."),process.exit(1));let r=await b(o),n=I(r),{stat:i}=await import("fs/promises");try{await i(n),!e.force&&!e.merge&&(console.error(k.yellow("Warning: .thinker.md/ already exists")),console.log("Use --force to reinitialize or --merge to add missing contexts"),process.exit(1)),e.merge?console.log(k.cyan("Merging: adding missing CONTEXT.md files...")):console.log(k.yellow("Reinitializing .thinker.md/..."))}catch{}await h("init",0,"Scanning repository structure..."),t.start("Scanning repository structure...");let s=await st(r);t.succeed(`Found ${s.directoryCount} directories, ${s.fileCount} files`),await h("init",30,`Found ${s.directoryCount} directories`);let a=await S(r),l=await _(r),c=await X(r)||"unknown";t.start("Creating .thinker.md/ structure...");let{mkdir:d}=await import("fs/promises");await d(n,{recursive:!0}),await d(Y(r),{recursive:!0});let m=await P(),g={...q,version:"1.0",projectId:m.metaRepoId||`local-${Date.now()}`,name:c,ignore:q.ignore||[],generatedAt:new Date().toISOString(),generatedBy:"tk-cli"};await it(r,g),t.succeed("Created config.json"),await h("init",50,"Created config.json");let f={...s,commitSha:l,branch:a};await Qt(at(n,"structure.json"),JSON.stringify(f,null,2)),t.succeed("Created structure.json"),await h("init",60,"Created structure.json"),t.start(e.merge?"Adding missing CONTEXT.md files...":"Creating empty CONTEXT.md files...");let{created:p,skipped:C}=await He(r,s.directories,e.merge);e.merge?(t.succeed(`Added ${p} new CONTEXT.md files (${C} existing skipped)`),await h("init",100,`Added ${p} new CONTEXT.md files`)):(t.succeed(`Created ${p} empty CONTEXT.md files`),await h("init",100,`Created ${p} CONTEXT.md files`)),console.log(),e.merge?(console.log(k.green("\u2713 Merged .thinker.md/")),console.log(),console.log(`Added: ${k.cyan(`${p}`)} new CONTEXT.md files`),console.log(`Skipped: ${k.gray(`${C}`)} existing files`)):(console.log(k.green("\u2713 Initialized .thinker.md/")),console.log(),console.log("Created:"),console.log(` ${k.cyan("config.json")} - Project configuration`),console.log(` ${k.cyan("structure.json")} - Directory structure`),console.log(` ${k.cyan(`${p} CONTEXT.md`)} files - Ready for documentation`)),console.log(),console.log("Next steps:"),console.log(" 1. Fill in the CONTEXT.md files with documentation"),console.log(" 2. Run "+k.cyan("tk validate")+" to check structure"),console.log(" 3. Run "+k.cyan("tk push")+" to upload to storage")}catch(r){t.fail("Initialization failed"),console.error(k.red(`Error: ${r}`)),process.exit(1)}}import{join as wt}from"path";import L from"chalk";import Ke from"ora";async function xt(e){let t=[],o=[],r=0,n=I(e),i=Y(e),{stat:s,readFile:a}=await import("fs/promises");try{await s(n)}catch{return t.push({type:"structure_mismatch",path:y,message:`${y}/ directory not found. Run 'tk init' first.`}),{valid:!1,errors:t,warnings:o,validContextCount:0,directoryCount:0}}let l=await F(e);l?(l.version||t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} missing required field: version`}),l.projectId||t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} missing required field: projectId`})):t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} not found or invalid`});let c=null;try{let p=wt(n,et),C=await a(p,"utf-8");c=JSON.parse(C)}catch{t.push({type:"structure_mismatch",path:`${y}/${et}`,message:`${et} not found or invalid`})}let d=await st(e),m=wt(n,O);try{await s(m),r++}catch{t.push({type:"missing_context",path:O,message:`Root ${O} not found`})}for(let p of d.directories){let C=wt(i,p,O);try{if((await s(C)).isFile()){r++;let x=await a(C,"utf-8");(x.trim().length<Lt||x.includes(Dt))&&o.push({type:"empty_context",path:`${p}/${O}`,message:"Context file appears to be empty or contains only template"})}}catch{t.push({type:"missing_context",path:`${p}/${O}`,message:`Missing context file for directory: ${p}`})}}let g=await Vt(e),f=new Set(d.directories);for(let p of g)p===""||p==="."||f.has(p)||o.push({type:"orphan_context",path:`${p}/${O}`,message:`Context exists for non-existent directory: ${p}`});return{valid:t.length===0,errors:t,warnings:o,validContextCount:r,directoryCount:d.directoryCount}}async function te(e){let t=Ke(),o=process.cwd();try{await N(o)||(console.error(L.red("Error: Not a git repository")),process.exit(1));let r=await b(o);await h("validate",0,"Validating structure..."),t.start(`Validating ${y}/ structure...`);let n=await xt(r);if(t.stop(),await h("validate",100,"Validation complete"),!e.quiet){if(console.log(),n.errors.length>0){console.log(L.red(`\u2717 ${n.errors.length} error(s):`));for(let i of n.errors)console.log(L.red(` \u2022 ${i.path}: ${i.message}`));console.log()}if(n.warnings.length>0){console.log(L.yellow(`\u26A0 ${n.warnings.length} warning(s):`));for(let i of n.warnings)console.log(L.yellow(` \u2022 ${i.path}: ${i.message}`));console.log()}n.valid?console.log(L.green(`\u2713 Validation passed: ${n.validContextCount} contexts valid`)):console.log(L.red(`\u2717 Validation failed: ${n.errors.length} error(s), ${n.warnings.length} warning(s)`))}n.valid||process.exit(1)}catch(r){t.fail("Validation failed"),console.error(L.red(`Error: ${r}`)),process.exit(1)}}import{join as se}from"path";import{writeFile as qe}from"fs/promises";import j from"chalk";import Ye from"ora";import*as ae from"tar";import{S3Client as Ge,PutObjectCommand as Et,GetObjectCommand as ee}from"@aws-sdk/client-s3";var Ct=null;function W(){if(!Ct){let e=process.env.R2_ENDPOINT,t=process.env.R2_ACCESS_KEY_ID,o=process.env.R2_SECRET_ACCESS_KEY;if(!e||!t||!o)throw new Error("R2 credentials not configured. Required: R2_ENDPOINT, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY");Ct=new Ge({region:"auto",endpoint:e,credentials:{accessKeyId:t,secretAccessKey:o}})}return Ct}function Q(){return process.env.R2_BUCKET||_t}async function Z(e,t,o=jt,r=Mt){let n;for(let i=1;i<=o;i++)try{return await e()}catch(s){if(n=s instanceof Error?s:new Error(String(s)),n.name==="NoSuchKey"||n.message.includes("credentials")||n.message.includes("AccessDenied"))throw n;if(i<o){let a=r*Math.pow(2,i-1);console.log(`[Storage] ${t} failed (attempt ${i}/${o}), retrying in ${a}ms...`),await new Promise(l=>setTimeout(l,a))}}throw n}function oe(e,t,o){return`${ot}/${e}/${rt}/${t}/${o}/${$t}`}function Je(e,t,o){return`${ot}/${e}/${rt}/${t}/${o}/${It}`}function re(e,t){return`${ot}/${e}/${rt}/${t}/${Nt}`}async function ze(e,t,o,r){let n=W(),i=oe(e,t,o);return await Z(()=>n.send(new Et({Bucket:Q(),Key:i,Body:r,ContentType:"application/gzip"})),"uploadArchive"),console.log(`[Storage] Uploaded archive to ${i}`),i}async function Xe(e,t,o,r){let n=W(),i=Je(e,t,o);await Z(()=>n.send(new Et({Bucket:Q(),Key:i,Body:JSON.stringify(r,null,2),ContentType:"application/json"})),"uploadMetadata"),console.log(`[Storage] Uploaded metadata to ${i}`)}async function Ve(e,t,o){let r=W(),n=re(e,t);await Z(()=>r.send(new Et({Bucket:Q(),Key:n,Body:o,ContentType:"text/plain"})),"updateLatestPointer"),console.log(`[Storage] Updated latest pointer for ${t} -> ${o}`)}async function ct(e,t){let o=W(),r=re(e,t);try{return(await(await Z(()=>o.send(new ee({Bucket:Q(),Key:r})),"getLatestCommitSha")).Body?.transformToString())?.trim()||null}catch(n){if(n instanceof Error&&"name"in n&&n.name==="NoSuchKey")return null;throw n}}async function ne(e,t,o){let r=W(),n=oe(e,t,o);try{let s=await(await Z(()=>r.send(new ee({Bucket:Q(),Key:n})),"downloadArchive")).Body?.transformToByteArray();return s?Buffer.from(s):null}catch(i){if(i instanceof Error&&"name"in i&&i.name==="NoSuchKey")return null;throw i}}async function ie(e,t,o,r,n){let i=await ze(e,t,o,r);return await Xe(e,t,o,n),await Ve(e,t,o),i}async function We(e){let{readdir:t,stat:o}=await import("fs/promises"),r=0;async function n(i){let s;try{s=await t(i)}catch{return}for(let a of s){let l=se(i,a);try{(await o(l)).isDirectory()?await n(l):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function ce(e){let t=Ye(),o=process.cwd();try{await N(o)||(console.error(j.red("Error: Not a git repository")),process.exit(1));let r=await b(o),n=await P(),i;if(n.mode==="sandbox"&&n.metaRepoId)i=n.metaRepoId;else{let p=await F(r);p?.projectId?i=p.projectId:(console.error(j.red("Error: No project ID found. Run 'tk init' first or set META_REPO_ID.")),process.exit(1))}let s=e.branch||n.branch||await S(r);await h("pull",0,`Fetching latest context for branch '${s}'...`),console.log(`Fetching latest context for branch '${j.cyan(s)}'...`),t.start("Looking up latest version...");let a;if(e.commit)a=e.commit,t.succeed(`Using specified commit: ${a.slice(0,7)}`);else{try{a=await ct(i,s)}catch(p){throw p instanceof Error&&p.message.includes("R2 credentials")&&(t.fail("R2 credentials not configured"),console.error(j.red(p.message)),process.exit(1)),p}a||(t.info("No previous context found for this branch"),console.log(`
28
- This appears to be the first generation. Use 'tk init' to create initial structure.`),process.exit(0)),t.succeed(`Found version: ${a.slice(0,7)}`)}await h("pull",30,`Found version: ${a.slice(0,7)}`),t.start("Downloading thinker.md.tar.gz...");let l=await ne(i,s,a);l||(t.fail("Archive not found"),console.error(j.red(`No archive found for commit ${a.slice(0,7)}`)),process.exit(1)),t.succeed(`Downloaded archive: ${l.length} bytes`),await h("pull",60,"Extracting archive...");let c=se(r,".thinker.md.tar.gz");await qe(c,l),t.start("Extracting to .thinker.md/...");let d=I(r),{rm:m}=await import("fs/promises");try{await m(d,{recursive:!0,force:!0})}catch{}await ae.extract({file:c,cwd:r}),await m(c,{force:!0}),t.succeed("Extracted .thinker.md/"),await h("pull",80,"Updating config...");let g=await F(r);g&&(g.pulledFrom=a,g.pulledAt=new Date().toISOString(),await it(r,g));let f=await We(d);await h("pull",100,`Pulled ${f} context files`),console.log(),console.log(j.green(`\u2713 Pulled ${f} context files`)),console.log(),console.log(`From: ${j.cyan(`${s}@${a.slice(0,7)}`)}`),console.log(),console.log("Next steps:"),console.log(" 1. Review and update context files as needed"),console.log(" 2. Run "+j.cyan("tk validate")+" to check structure"),console.log(" 3. Run "+j.cyan("tk push")+" to upload changes")}catch(r){t.fail("Pull failed"),console.error(j.red(`Error: ${r}`)),process.exit(1)}}import{join as le}from"path";import T from"chalk";import Qe from"ora";import*as de from"tar";async function Ze(e){let{readdir:t,stat:o}=await import("fs/promises"),r=0,n=0;async function i(s){let a;try{a=await t(s)}catch{return}for(let l of a){let c=le(s,l);try{(await o(c)).isDirectory()?await i(c):(n++,l===O&&r++)}catch{}}}return await i(e),{contextFiles:r,totalFiles:n}}async function pe(e){let t=Qe(),o=process.cwd();try{if(ht()==="sandbox")try{Gt()}catch(E){console.error(T.red(`Sandbox environment error: ${E}`)),process.exit(1)}await N(o)||(console.error(T.red("Error: Not a git repository")),process.exit(1));let r=await b(o),n=await P(),i=I(r),{stat:s,rm:a}=await import("fs/promises");try{await s(i)}catch{console.error(T.red(`Error: ${y}/ not found`)),console.log("Run 'tk init' first to create the structure."),process.exit(1)}if(await h("push",0,"Validating structure..."),!e.skipValidation){t.start(`Validating ${y}/ structure...`);let E=await xt(r);if(!E.valid){t.fail("Validation failed"),console.log(),console.log(T.red("Fix the following errors before pushing:"));for(let tt of E.errors)console.log(T.red(` \u2022 ${tt.path}: ${tt.message}`));process.exit(1)}t.succeed("Validation passed"),E.warnings.length>0&&console.log(T.yellow(` ${E.warnings.length} warning(s)`))}await h("push",20,"Validation passed");let l;if(n.mode==="sandbox"&&n.metaRepoId)l=n.metaRepoId;else{let E=await F(r);E?.projectId?l=E.projectId:(console.error(T.red("Error: No project ID found. Set META_REPO_ID environment variable.")),process.exit(1))}let c=n.branch||await S(r),d=n.commitSha||await _(r),m=await Jt(r,d);await h("push",30,"Packaging .thinker.md/..."),t.start(`Packaging ${y}/...`);let g=le(r,`${y}.tar.gz`);await de.create({file:g,cwd:r,gzip:!0},[y]);let{readFile:f}=await import("fs/promises"),p=await f(g);await a(g,{force:!0}),t.succeed(`Packaged archive: ${p.length} bytes`),await h("push",60,"Uploading to storage...");let C=await Ze(i),K=await F(r),x=e.forceFull?ft:K?.pulledFrom?Ot:ft,Pt={branch:c,commitSha:d,commitMessage:m,mode:x,parentCommitSha:K?.pulledFrom,generatedAt:new Date().toISOString(),jobId:n.jobId||`local-${Date.now()}`,tokensUsed:0,filesAnalyzed:C.totalFiles,contextFilesGenerated:C.contextFiles};t.start("Uploading to storage...");try{let E=await ie(l,c,d,p,Pt);if(t.succeed("Uploaded to storage"),await h("push",90,"Finalizing..."),n.mode==="sandbox"&&n.callbackUrl){let tt={filesAnalyzed:C.totalFiles,contextFilesGenerated:C.contextFiles,tokensUsed:0,mode:x};await Yt(E,tt)}await h("push",100,"Done!"),console.log(),console.log(T.green("\u2713 Successfully pushed to storage")),console.log(),console.log(`Branch: ${T.cyan(c)}`),console.log(`Commit: ${T.cyan(d.slice(0,7))}`),console.log(`Mode: ${T.cyan(x)}`),console.log(`Context files: ${T.cyan(C.contextFiles)}`),n.mode==="sandbox"&&(console.log(),console.log(T.green("\u2713 Job completed")))}catch(E){t.fail("Upload failed"),E instanceof Error&&E.message.includes("R2 credentials")?(console.error(T.red(E.message)),console.log(),console.log("Required environment variables:"),console.log(" R2_ENDPOINT"),console.log(" R2_ACCESS_KEY_ID"),console.log(" R2_SECRET_ACCESS_KEY"),console.log(" R2_BUCKET")):console.error(T.red(`Error: ${E}`)),process.exit(1)}}catch(r){t.fail("Push failed"),console.error(T.red(`Error: ${r}`)),process.exit(1)}}import v from"chalk";import me from"ora";import{createServer as ue}from"http";import{randomBytes as to}from"crypto";import{exec as eo}from"child_process";import{promisify as oo}from"util";var Tt=oo(eo);async function ro(e){let t=process.platform;try{t==="darwin"?await Tt(`open "${e}"`):t==="win32"?await Tt(`start "" "${e}"`):await Tt(`xdg-open "${e}"`)}catch{}}async function ge(e=9876){return new Promise(t=>{let o=ue();o.listen(e,()=>{let r=o.address(),n=typeof r=="object"&&r?r.port:e;o.close(()=>t(n))}),o.on("error",()=>{t(ge(e+1))})})}function no(e,t,o=3e5){return new Promise((r,n)=>{let i=null,s,a=()=>{s&&clearTimeout(s),i&&(i.close(),i=null)};s=setTimeout(()=>{a(),n(new Error("Authentication timed out. Please try again."))},o),i=ue((l,c)=>{let d=new URL(l.url||"",`http://localhost:${e}`);if(d.pathname==="/callback"){let m=d.searchParams.get("token"),g=d.searchParams.get("user_id"),f=d.searchParams.get("email");m&&g&&f?(c.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),c.end(`<!DOCTYPE html>
16
+ ## Common Tasks
17
+ `;await Qt(e,r)}async function Be(e,t,o=!1){let{existsSync:r}=await import("fs"),n=Y(e),i=0,s=0,a=at(I(e),"CONTEXT.md");o&&r(a)?s++:(await Wt(a,""),i++);for(let l of t){let c=at(n,l),d=at(c,"CONTEXT.md"),{mkdir:m}=await import("fs/promises");if(await m(c,{recursive:!0}),o&&r(d)){s++;continue}await Wt(d,l),i++}return{created:i,skipped:s}}async function Zt(e){let t=He(),o=process.cwd();try{await N(o)||(console.error(b.red("Error: Not a git repository")),console.log("Please run this command from the root of a git repository."),process.exit(1));let r=await k(o),n=I(r),{stat:i}=await import("fs/promises");try{await i(n),!e.force&&!e.merge&&(console.error(b.yellow("Warning: .thinker.md/ already exists")),console.log("Use --force to reinitialize or --merge to add missing contexts"),process.exit(1)),e.merge?console.log(b.cyan("Merging: adding missing CONTEXT.md files...")):console.log(b.yellow("Reinitializing .thinker.md/..."))}catch{}await h("init",0,"Scanning repository structure..."),t.start("Scanning repository structure...");let s=await st(r);t.succeed(`Found ${s.directoryCount} directories, ${s.fileCount} files`),await h("init",30,`Found ${s.directoryCount} directories`);let a=await S(r),l=await _(r),c=await z(r)||"unknown";t.start("Creating .thinker.md/ structure...");let{mkdir:d}=await import("fs/promises");await d(n,{recursive:!0}),await d(Y(r),{recursive:!0});let m=await P(),g={...V,version:"1.0",projectId:m.metaRepoId||`local-${Date.now()}`,name:c,ignore:V.ignore||[],generatedAt:new Date().toISOString(),generatedBy:"tk-cli"};await it(r,g),t.succeed("Created config.json"),await h("init",50,"Created config.json");let f={...s,commitSha:l,branch:a};await Qt(at(n,"structure.json"),JSON.stringify(f,null,2)),t.succeed("Created structure.json"),await h("init",60,"Created structure.json"),t.start(e.merge?"Adding missing CONTEXT.md files...":"Creating empty CONTEXT.md files...");let{created:p,skipped:C}=await Be(r,s.directories,e.merge);e.merge?(t.succeed(`Added ${p} new CONTEXT.md files (${C} existing skipped)`),await h("init",100,`Added ${p} new CONTEXT.md files`)):(t.succeed(`Created ${p} empty CONTEXT.md files`),await h("init",100,`Created ${p} CONTEXT.md files`)),console.log(),e.merge?(console.log(b.green("\u2713 Merged .thinker.md/")),console.log(),console.log(`Added: ${b.cyan(`${p}`)} new CONTEXT.md files`),console.log(`Skipped: ${b.gray(`${C}`)} existing files`)):(console.log(b.green("\u2713 Initialized .thinker.md/")),console.log(),console.log("Created:"),console.log(` ${b.cyan("config.json")} - Project configuration`),console.log(` ${b.cyan("structure.json")} - Directory structure`),console.log(` ${b.cyan(`${p} CONTEXT.md`)} files - Ready for documentation`)),console.log(),console.log("Next steps:"),console.log(" 1. Fill in the CONTEXT.md files with documentation"),console.log(" 2. Run "+b.cyan("tk validate")+" to check structure"),console.log(" 3. Run "+b.cyan("tk push")+" to upload to storage")}catch(r){t.fail("Initialization failed"),console.error(b.red(`Error: ${r}`)),process.exit(1)}}import{join as wt}from"path";import L from"chalk";import Ge from"ora";async function xt(e){let t=[],o=[],r=0,n=I(e),i=Y(e),{stat:s,readFile:a}=await import("fs/promises");try{await s(n)}catch{return t.push({type:"structure_mismatch",path:y,message:`${y}/ directory not found. Run 'tk init' first.`}),{valid:!1,errors:t,warnings:o,validContextCount:0,directoryCount:0}}let l=await F(e);l?(l.version||t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} missing required field: version`}),l.projectId||t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} missing required field: projectId`})):t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} not found or invalid`});let c=null;try{let p=wt(n,et),C=await a(p,"utf-8");c=JSON.parse(C)}catch{t.push({type:"structure_mismatch",path:`${y}/${et}`,message:`${et} not found or invalid`})}let d=await st(e),m=wt(n,O);try{await s(m),r++}catch{t.push({type:"missing_context",path:O,message:`Root ${O} not found`})}for(let p of d.directories){let C=wt(i,p,O);try{if((await s(C)).isFile()){r++;let x=await a(C,"utf-8");(x.trim().length<Lt||x.includes(Dt))&&o.push({type:"empty_context",path:`${p}/${O}`,message:"Context file appears to be empty or contains only template"})}}catch{t.push({type:"missing_context",path:`${p}/${O}`,message:`Missing context file for directory: ${p}`})}}let g=await qt(e),f=new Set(d.directories);for(let p of g)p===""||p==="."||f.has(p)||o.push({type:"orphan_context",path:`${p}/${O}`,message:`Context exists for non-existent directory: ${p}`});return{valid:t.length===0,errors:t,warnings:o,validContextCount:r,directoryCount:d.directoryCount}}async function te(e){let t=Ge(),o=process.cwd();try{await N(o)||(console.error(L.red("Error: Not a git repository")),process.exit(1));let r=await k(o);await h("validate",0,"Validating structure..."),t.start(`Validating ${y}/ structure...`);let n=await xt(r);if(t.stop(),await h("validate",100,"Validation complete"),!e.quiet){if(console.log(),n.errors.length>0){console.log(L.red(`\u2717 ${n.errors.length} error(s):`));for(let i of n.errors)console.log(L.red(` \u2022 ${i.path}: ${i.message}`));console.log()}if(n.warnings.length>0){console.log(L.yellow(`\u26A0 ${n.warnings.length} warning(s):`));for(let i of n.warnings)console.log(L.yellow(` \u2022 ${i.path}: ${i.message}`));console.log()}n.valid?console.log(L.green(`\u2713 Validation passed: ${n.validContextCount} contexts valid`)):console.log(L.red(`\u2717 Validation failed: ${n.errors.length} error(s), ${n.warnings.length} warning(s)`))}n.valid||process.exit(1)}catch(r){t.fail("Validation failed"),console.error(L.red(`Error: ${r}`)),process.exit(1)}}import{join as se}from"path";import{writeFile as Ve}from"fs/promises";import M from"chalk";import Ye from"ora";import*as ae from"tar";import{S3Client as Ke,PutObjectCommand as Et,GetObjectCommand as ee}from"@aws-sdk/client-s3";var Ct=null;function W(){if(!Ct){let e=process.env.R2_ENDPOINT,t=process.env.R2_ACCESS_KEY_ID,o=process.env.R2_SECRET_ACCESS_KEY;if(!e||!t||!o)throw new Error("R2 credentials not configured. Required: R2_ENDPOINT, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY");Ct=new Ke({region:"auto",endpoint:e,credentials:{accessKeyId:t,secretAccessKey:o}})}return Ct}function Q(){return process.env.R2_BUCKET||_t}async function Z(e,t,o=Mt,r=jt){let n;for(let i=1;i<=o;i++)try{return await e()}catch(s){if(n=s instanceof Error?s:new Error(String(s)),n.name==="NoSuchKey"||n.message.includes("credentials")||n.message.includes("AccessDenied"))throw n;if(i<o){let a=r*Math.pow(2,i-1);console.log(`[Storage] ${t} failed (attempt ${i}/${o}), retrying in ${a}ms...`),await new Promise(l=>setTimeout(l,a))}}throw n}function oe(e,t,o){return`${ot}/${e}/${rt}/${t}/${o}/${$t}`}function Je(e,t,o){return`${ot}/${e}/${rt}/${t}/${o}/${It}`}function re(e,t){return`${ot}/${e}/${rt}/${t}/${Nt}`}async function Xe(e,t,o,r){let n=W(),i=oe(e,t,o);return await Z(()=>n.send(new Et({Bucket:Q(),Key:i,Body:r,ContentType:"application/gzip"})),"uploadArchive"),console.log(`[Storage] Uploaded archive to ${i}`),i}async function ze(e,t,o,r){let n=W(),i=Je(e,t,o);await Z(()=>n.send(new Et({Bucket:Q(),Key:i,Body:JSON.stringify(r,null,2),ContentType:"application/json"})),"uploadMetadata"),console.log(`[Storage] Uploaded metadata to ${i}`)}async function qe(e,t,o){let r=W(),n=re(e,t);await Z(()=>r.send(new Et({Bucket:Q(),Key:n,Body:o,ContentType:"text/plain"})),"updateLatestPointer"),console.log(`[Storage] Updated latest pointer for ${t} -> ${o}`)}async function ct(e,t){let o=W(),r=re(e,t);try{return(await(await Z(()=>o.send(new ee({Bucket:Q(),Key:r})),"getLatestCommitSha")).Body?.transformToString())?.trim()||null}catch(n){if(n instanceof Error&&"name"in n&&n.name==="NoSuchKey")return null;throw n}}async function ne(e,t,o){let r=W(),n=oe(e,t,o);try{let s=await(await Z(()=>r.send(new ee({Bucket:Q(),Key:n})),"downloadArchive")).Body?.transformToByteArray();return s?Buffer.from(s):null}catch(i){if(i instanceof Error&&"name"in i&&i.name==="NoSuchKey")return null;throw i}}async function ie(e,t,o,r,n){let i=await Xe(e,t,o,r);return await ze(e,t,o,n),await qe(e,t,o),i}async function We(e){let{readdir:t,stat:o}=await import("fs/promises"),r=0;async function n(i){let s;try{s=await t(i)}catch{return}for(let a of s){let l=se(i,a);try{(await o(l)).isDirectory()?await n(l):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function ce(e){let t=Ye(),o=process.cwd();try{await N(o)||(console.error(M.red("Error: Not a git repository")),process.exit(1));let r=await k(o),n=await P(),i;if(n.mode==="sandbox"&&n.metaRepoId)i=n.metaRepoId;else{let p=await F(r);p?.projectId?i=p.projectId:(console.error(M.red("Error: No project ID found. Run 'tk init' first or set META_REPO_ID.")),process.exit(1))}let s=e.branch||n.branch||await S(r);await h("pull",0,`Fetching latest context for branch '${s}'...`),console.log(`Fetching latest context for branch '${M.cyan(s)}'...`),t.start("Looking up latest version...");let a;if(e.commit)a=e.commit,t.succeed(`Using specified commit: ${a.slice(0,7)}`);else{try{a=await ct(i,s)}catch(p){throw p instanceof Error&&p.message.includes("R2 credentials")&&(t.fail("R2 credentials not configured"),console.error(M.red(p.message)),process.exit(1)),p}a||(t.info("No previous context found for this branch"),console.log(`
18
+ This appears to be the first generation. Use 'tk init' to create initial structure.`),process.exit(0)),t.succeed(`Found version: ${a.slice(0,7)}`)}await h("pull",30,`Found version: ${a.slice(0,7)}`),t.start("Downloading thinker.md.tar.gz...");let l=await ne(i,s,a);l||(t.fail("Archive not found"),console.error(M.red(`No archive found for commit ${a.slice(0,7)}`)),process.exit(1)),t.succeed(`Downloaded archive: ${l.length} bytes`),await h("pull",60,"Extracting archive...");let c=se(r,".thinker.md.tar.gz");await Ve(c,l),t.start("Extracting to .thinker.md/...");let d=I(r),{rm:m}=await import("fs/promises");try{await m(d,{recursive:!0,force:!0})}catch{}await ae.extract({file:c,cwd:r}),await m(c,{force:!0}),t.succeed("Extracted .thinker.md/"),await h("pull",80,"Updating config...");let g=await F(r);g&&(g.pulledFrom=a,g.pulledAt=new Date().toISOString(),await it(r,g));let f=await We(d);await h("pull",100,`Pulled ${f} context files`),console.log(),console.log(M.green(`\u2713 Pulled ${f} context files`)),console.log(),console.log(`From: ${M.cyan(`${s}@${a.slice(0,7)}`)}`),console.log(),console.log("Next steps:"),console.log(" 1. Review and update context files as needed"),console.log(" 2. Run "+M.cyan("tk validate")+" to check structure"),console.log(" 3. Run "+M.cyan("tk push")+" to upload changes")}catch(r){t.fail("Pull failed"),console.error(M.red(`Error: ${r}`)),process.exit(1)}}import{join as le}from"path";import T from"chalk";import Qe from"ora";import*as de from"tar";async function Ze(e){let{readdir:t,stat:o}=await import("fs/promises"),r=0,n=0;async function i(s){let a;try{a=await t(s)}catch{return}for(let l of a){let c=le(s,l);try{(await o(c)).isDirectory()?await i(c):(n++,l===O&&r++)}catch{}}}return await i(e),{contextFiles:r,totalFiles:n}}async function pe(e){let t=Qe(),o=process.cwd();try{if(ht()==="sandbox")try{Kt()}catch(E){console.error(T.red(`Sandbox environment error: ${E}`)),process.exit(1)}await N(o)||(console.error(T.red("Error: Not a git repository")),process.exit(1));let r=await k(o),n=await P(),i=I(r),{stat:s,rm:a}=await import("fs/promises");try{await s(i)}catch{console.error(T.red(`Error: ${y}/ not found`)),console.log("Run 'tk init' first to create the structure."),process.exit(1)}if(await h("push",0,"Validating structure..."),!e.skipValidation){t.start(`Validating ${y}/ structure...`);let E=await xt(r);if(!E.valid){t.fail("Validation failed"),console.log(),console.log(T.red("Fix the following errors before pushing:"));for(let tt of E.errors)console.log(T.red(` \u2022 ${tt.path}: ${tt.message}`));process.exit(1)}t.succeed("Validation passed"),E.warnings.length>0&&console.log(T.yellow(` ${E.warnings.length} warning(s)`))}await h("push",20,"Validation passed");let l;if(n.mode==="sandbox"&&n.metaRepoId)l=n.metaRepoId;else{let E=await F(r);E?.projectId?l=E.projectId:(console.error(T.red("Error: No project ID found. Set META_REPO_ID environment variable.")),process.exit(1))}let c=n.branch||await S(r),d=n.commitSha||await _(r),m=await Jt(r,d);await h("push",30,"Packaging .thinker.md/..."),t.start(`Packaging ${y}/...`);let g=le(r,`${y}.tar.gz`);await de.create({file:g,cwd:r,gzip:!0},[y]);let{readFile:f}=await import("fs/promises"),p=await f(g);await a(g,{force:!0}),t.succeed(`Packaged archive: ${p.length} bytes`),await h("push",60,"Uploading to storage...");let C=await Ze(i),G=await F(r),x=e.forceFull?ft:G?.pulledFrom?Ot:ft,Pt={branch:c,commitSha:d,commitMessage:m,mode:x,parentCommitSha:G?.pulledFrom,generatedAt:new Date().toISOString(),jobId:n.jobId||`local-${Date.now()}`,tokensUsed:0,filesAnalyzed:C.totalFiles,contextFilesGenerated:C.contextFiles};t.start("Uploading to storage...");try{let E=await ie(l,c,d,p,Pt);if(t.succeed("Uploaded to storage"),await h("push",90,"Finalizing..."),n.mode==="sandbox"&&n.callbackUrl){let tt={filesAnalyzed:C.totalFiles,contextFilesGenerated:C.contextFiles,tokensUsed:0,mode:x};await Yt(E,tt)}await h("push",100,"Done!"),console.log(),console.log(T.green("\u2713 Successfully pushed to storage")),console.log(),console.log(`Branch: ${T.cyan(c)}`),console.log(`Commit: ${T.cyan(d.slice(0,7))}`),console.log(`Mode: ${T.cyan(x)}`),console.log(`Context files: ${T.cyan(C.contextFiles)}`),n.mode==="sandbox"&&(console.log(),console.log(T.green("\u2713 Job completed")))}catch(E){t.fail("Upload failed"),E instanceof Error&&E.message.includes("R2 credentials")?(console.error(T.red(E.message)),console.log(),console.log("Required environment variables:"),console.log(" R2_ENDPOINT"),console.log(" R2_ACCESS_KEY_ID"),console.log(" R2_SECRET_ACCESS_KEY"),console.log(" R2_BUCKET")):console.error(T.red(`Error: ${E}`)),process.exit(1)}}catch(r){t.fail("Push failed"),console.error(T.red(`Error: ${r}`)),process.exit(1)}}import v from"chalk";import me from"ora";import{createServer as ue}from"http";import{randomBytes as to}from"crypto";import{exec as eo}from"child_process";import{promisify as oo}from"util";var Tt=oo(eo);async function ro(e){let t=process.platform;try{t==="darwin"?await Tt(`open "${e}"`):t==="win32"?await Tt(`start "" "${e}"`):await Tt(`xdg-open "${e}"`)}catch{}}async function ge(e=9876){return new Promise(t=>{let o=ue();o.listen(e,()=>{let r=o.address(),n=typeof r=="object"&&r?r.port:e;o.close(()=>t(n))}),o.on("error",()=>{t(ge(e+1))})})}function no(e,t,o=3e5){return new Promise((r,n)=>{let i=null,s,a=()=>{s&&clearTimeout(s),i&&(i.close(),i=null)};s=setTimeout(()=>{a(),n(new Error("Authentication timed out. Please try again."))},o),i=ue((l,c)=>{let d=new URL(l.url||"",`http://localhost:${e}`);if(d.pathname==="/callback"){let m=d.searchParams.get("token"),g=d.searchParams.get("user_id"),f=d.searchParams.get("email");m&&g&&f?(c.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),c.end(`<!DOCTYPE html>
29
19
  <html lang="en">
30
20
  <head>
31
21
  <meta charset="UTF-8">
@@ -100,7 +90,43 @@ This appears to be the first generation. Use 'tk init' to create initial structu
100
90
  <p>You can now close this window and return to your terminal.</p>
101
91
  </div>
102
92
  </body>
103
- </html>`),a(),r({token:m,userId:g,email:f})):(c.writeHead(400,{"Content-Type":"text/plain"}),c.end("Missing required parameters"))}else c.writeHead(404,{"Content-Type":"text/plain"}),c.end("Not found")}),i.listen(e)})}async function io(e,t){try{let o=await fetch(`${e}/api/auth/validate`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});return o.ok?{valid:!0,userId:(await o.json()).userId}:o.status===401?{valid:!1,error:"Invalid or expired token"}:{valid:!1,error:`API error: ${o.status} ${o.statusText}`}}catch(o){return{valid:!1,error:`Connection failed: ${o}`}}}async function fe(e){let t=me(),o=e.apiUrl||J;if(e.token){t.start("Validating token...");let n=await io(o,e.token);n.valid||(t.fail("Authentication failed"),console.error(v.red(`Error: ${n.error}`)),process.exit(1));let i={apiToken:e.token,apiUrl:o,userId:n.userId};await nt(i),t.succeed("Authenticated successfully"),console.log(),console.log(`Config saved to: ${v.cyan(U)}`);return}let r=await $();if(r.apiToken){console.log(v.green("You are already logged in.")),console.log(`User ID: ${v.cyan(r.userId||"unknown")}`),console.log(),console.log("To re-authenticate, run:"),console.log(v.cyan(" tk login --token NEW_TOKEN")),console.log(),console.log("Or to login with a different account:"),console.log(v.cyan(" tk logout && tk login"));return}console.log(v.bold("ThinkerMD Login")),console.log(),t.start("Setting up authentication...");try{let n=await ge(),i=to(16).toString("hex"),s=`${o}/cli/auth?port=${n}&state=${i}`;t.succeed("Ready for authentication"),console.log(),console.log("Opening browser for authentication..."),console.log(),console.log("If the browser doesn't open, visit this URL manually:"),console.log(v.cyan(s)),console.log(),await ro(s),t.start("Waiting for authentication...");let{token:a,userId:l,email:c}=await no(n,i);await nt({apiToken:a,apiUrl:o,userId:l,email:c}),t.succeed("Authenticated successfully"),console.log(),console.log(`Logged in as: ${v.cyan(c)}`),console.log(`Config saved to: ${v.cyan(U)}`)}catch(n){t.fail("Authentication failed"),console.error(v.red(`Error: ${n instanceof Error?n.message:n}`)),console.log(),console.log("You can also authenticate manually:"),console.log(v.cyan(" tk login --token YOUR_TOKEN")),console.log(),console.log(`Get your token at: ${v.cyan(`${o}/dashboard/settings`)}`),process.exit(1)}}async function he(){let e=me();e.start("Logging out..."),await nt({}),e.succeed("Logged out successfully"),console.log(),console.log(`Config cleared from: ${v.cyan(U)}`)}import{join as ye}from"path";import u from"chalk";import so from"ora";async function ao(e){let{readFile:t}=await import("fs/promises"),{existsSync:o}=await import("fs"),r=ye(I(e),"structure.json");try{if(o(r)){let n=await t(r,"utf-8");return JSON.parse(n)}}catch{}return null}async function co(e){let{readdir:t,stat:o}=await import("fs/promises"),r=0;async function n(i){let s;try{s=await t(i)}catch{return}for(let a of s){let l=ye(i,a);try{(await o(l)).isDirectory()?await n(l):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function we(){let e=so(),t=process.cwd();try{await N(t)||(console.error(u.red("Error: Not a git repository")),process.exit(1));let o=await b(t),r=await P(),n=I(o);console.log(u.bold("ThinkerMD Status")),console.log();let i=await X(o),s=r.branch||await S(o),a=r.commitSha||await _(o);console.log(u.dim("Repository:")),console.log(` Name: ${u.cyan(i||"unknown")}`),console.log(` Branch: ${u.cyan(s)}`),console.log(` Commit: ${u.cyan(a.slice(0,7))}`),console.log();let{stat:l}=await import("fs/promises"),c=!1;try{await l(n),c=!0}catch{}if(!c){console.log(u.dim("Local Context:")),console.log(` Status: ${u.yellow("Not initialized")}`),console.log(),console.log("Run "+u.cyan("tk init")+" to create .thinker.md/");return}console.log(u.dim("Local Context:"));let d=await F(o),m=await ao(o),g=await co(n);if(console.log(` Status: ${u.green("Initialized")}`),console.log(` Project ID: ${u.cyan(d?.projectId||"unknown")}`),console.log(` Context files: ${u.cyan(g)}`),m&&(console.log(` Directories: ${u.cyan(m.directoryCount)}`),console.log(` Files tracked: ${u.cyan(m.fileCount)}`)),d?.generatedAt){let p=new Date(d.generatedAt);console.log(` Generated: ${u.cyan(p.toLocaleString())}`)}d?.pulledFrom&&console.log(` Pulled from: ${u.cyan(d.pulledFrom.slice(0,7))}`),console.log();let f=r.metaRepoId||d?.projectId;if(f&&r.r2Endpoint){console.log(u.dim("Remote Storage:")),e.start("Checking remote...");try{let p=await ct(f,s);p?(e.stop(),console.log(` Latest commit: ${u.cyan(p.slice(0,7))}`),p===a?console.log(` Status: ${u.green("Up to date")}`):d?.pulledFrom===p?console.log(` Status: ${u.yellow("Local changes not pushed")}`):(console.log(` Status: ${u.yellow("Remote has newer version")}`),console.log(),console.log("Run "+u.cyan("tk pull")+" to download latest context"))):(e.stop(),console.log(` Status: ${u.yellow("No remote context found")}`),console.log(),console.log("Run "+u.cyan("tk push")+" to upload context"))}catch{e.stop(),console.log(` Status: ${u.dim("Unable to check (no credentials)")}`)}}console.log(),console.log(u.dim("Mode:")),console.log(` ${r.mode==="sandbox"?u.cyan("Sandbox"):u.cyan("Local")}`),r.mode==="sandbox"&&console.log(` Job ID: ${u.cyan(r.jobId||"unknown")}`)}catch(o){e.fail("Failed to get status"),console.error(u.red(`Error: ${o}`)),process.exit(1)}}import w from"chalk";import lo from"ora";async function xe(e){let t=lo(),o=process.cwd();try{await N(o)||(console.error(w.red("Error: Not a git repository")),process.exit(1));let r=await b(o);if((await P()).mode==="sandbox"){console.error(w.yellow("Note: In sandbox mode, generation is triggered automatically.")),console.log("Use 'tk init' to create structure and 'tk push' when done.");return}let i=await $();i.apiToken||(console.error(w.red("Error: Not logged in")),console.log(),console.log("Please authenticate first:"),console.log(w.cyan(" tk login --token YOUR_TOKEN")),process.exit(1));let s=i.apiUrl||J,a=await X(r),l=e.branch||await S(r),c=await _(r);a||(console.error(w.red("Error: Unable to determine repository name")),console.log("Make sure this repository has a remote origin configured."),process.exit(1)),console.log(w.bold("Trigger Context Generation")),console.log(),console.log(`Repository: ${w.cyan(a)}`),console.log(`Branch: ${w.cyan(l)}`),console.log(`Commit: ${w.cyan(c.slice(0,7))}`),console.log(),t.start("Looking up meta-repository...");let d=await fetch(`${s}/api/meta-repositories/lookup?repo=${encodeURIComponent(a)}`,{headers:{Authorization:`Bearer ${i.apiToken}`}});d.ok||(d.status===404&&(t.fail("Meta-repository not found"),console.log(),console.log("This repository is not connected to ThinkerMD. Please add it at:"),console.log(w.cyan(`${s}/dashboard`)),process.exit(1)),t.fail("API error"),console.error(w.red(`Error: ${d.status} ${d.statusText}`)),process.exit(1));let m=await d.json();t.succeed(`Found meta-repository: ${m.id}`),t.start("Triggering generation job...");let g=await fetch(`${s}/api/jobs/generate`,{method:"POST",headers:{Authorization:`Bearer ${i.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({metaRepositoryId:m.id,commitSha:c,branch:l,priority:"normal"})});if(!g.ok){t.fail("Failed to trigger generation");let p=await g.json().catch(()=>({}));console.error(w.red(`Error: ${p.error||g.statusText}`)),process.exit(1)}let f=await g.json();if(t.succeed("Generation job queued"),console.log(),console.log(`Job ID: ${w.cyan(f.jobId)}`),console.log(`Status: ${w.cyan(f.status)}`),f.estimatedPosition&&console.log(`Queue position: ${w.cyan(f.estimatedPosition)}`),console.log(),console.log("Track progress at:"),console.log(w.cyan(`${s}/dashboard/repositories/${m.id}/jobs/${f.jobId}`)),e.wait){console.log(),t.start("Waiting for completion...");let p=!1,C="";for(;!p;){await new Promise(x=>setTimeout(x,5e3));let K=await fetch(`${s}/api/jobs/${f.jobId}`,{headers:{Authorization:`Bearer ${i.apiToken}`}});if(K.ok){let x=await K.json();x.status!==C&&(C=x.status,t.text=`Status: ${x.status} (${x.progress||0}%)`),(x.status==="completed"||x.status==="failed")&&(p=!0,x.status==="completed"?(t.succeed("Generation completed!"),console.log(),console.log("Run "+w.cyan("tk pull")+" to download the generated context")):(t.fail("Generation failed"),x.errorMessage&&console.error(w.red(`Error: ${x.errorMessage}`))))}}}}catch(r){t.fail("Failed to trigger generation"),console.error(w.red(`Error: ${r}`)),process.exit(1)}}import kt from"chalk";import{Hono as To}from"hono";import M from"chalk";import{createServer as Ro}from"http";import{homedir as Re}from"os";import{join as be}from"path";import{readFile as uo,writeFile as go,mkdir as fo}from"fs/promises";import{existsSync as ho}from"fs";async function po(){return(await $()).apiUrl||process.env.THINKERMD_API_URL||J}async function mo(){return(await $()).apiToken||process.env.THINKERMD_API_TOKEN||null}async function lt(e,t={}){let o=await po(),r=await mo();if(!r)throw new Error("Not authenticated. Run `tk login` first.");let{method:n="GET",body:i,params:s}=t,a=`${o}${e}`;if(s){let c=new URLSearchParams(s);a+=`?${c.toString()}`}let l=await fetch(a,{method:n,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:i?JSON.stringify(i):void 0});if(!l.ok){let c=await l.json().catch(()=>({}));throw new Error(c.error||`API request failed: ${l.status} ${l.statusText}`)}return l.json()}async function Ce(e,t,o={}){return lt("/api/mcp/search",{method:"POST",body:{repositoryId:e,query:t,commit:o.commit,branch:o.branch,limit:o.limit||10}})}async function Rt(e,t,o={}){return lt("/api/mcp/read",{params:{repositoryId:e,filePath:t,...o.branch&&{branch:o.branch},...o.commit&&{commit:o.commit}}})}async function Ee(e,t={}){return lt("/api/mcp/files",{params:{repositoryId:e,...t.branch&&{branch:t.branch},...t.commit&&{commit:t.commit}}})}async function Te(e){try{let t=await lt("/api/user/repositories",{params:{remoteUrl:e}});return t.repositories&&t.repositories.length>0?t.repositories[0]:null}catch{return null}}var bt=be(Re(),".thinkermd","cache","repo-mapping.json"),yo=24*60*60*1e3;async function wo(){try{if(ho(bt)){let e=await uo(bt,"utf-8");return JSON.parse(e)}}catch{}return{mappings:{}}}async function xo(e){let t=be(Re(),".thinkermd","cache");await fo(t,{recursive:!0}),await go(bt,JSON.stringify(e,null,2))}async function V(e){let t;try{t=await b(e)}catch{return console.error(`[RepoResolver] Not a git repository: ${e}`),null}let o=await yt(t);if(!o)return console.error(`[RepoResolver] No git remote found for: ${t}`),null;let r=Co(o),n=await wo(),i=n.mappings[r];if(i&&Date.now()-i.cachedAt<yo){console.log(`[RepoResolver] Cache hit for: ${r}`);let c=await S(t),d=await _(t);return{repositoryId:i.repositoryId,fullName:i.fullName,remoteUrl:r,gitRoot:t,branch:c||"main",commit:d||"HEAD"}}console.log(`[RepoResolver] Querying API for: ${r}`);let s=await Te(r);if(!s)return console.error(`[RepoResolver] Repository not found in ThinkerMD: ${r}`),null;n.mappings[r]={remoteUrl:r,repositoryId:s.id,fullName:s.fullName,cachedAt:Date.now()},await xo(n);let a=await S(t),l=await _(t);return{repositoryId:s.id,fullName:s.fullName,remoteUrl:r,gitRoot:t,branch:a||"main",commit:l||"HEAD"}}function Co(e){return e=e.replace(/\.git$/,""),e.startsWith("git@")&&(e=e.replace(/^git@/,"https://").replace(/:([^/])/,"/$1")),e.startsWith("http://")&&(e=e.replace("http://","https://")),e}async function dt(e){let{path:t,query:o,limit:r=10}=e,n=await V(t);if(!n)throw new Error(`Could not resolve repository for path: ${t}`);return{...await Ce(n.repositoryId,o,{branch:n.branch,commit:n.commit,limit:r}),repositoryId:n.repositoryId,fullName:n.fullName}}async function pt(e){let{path:t,filePath:o}=e,r=await V(t);if(!r)throw new Error(`Could not resolve repository for path: ${t}`);return{...await Rt(r.repositoryId,o,{branch:r.branch,commit:r.commit}),repositoryId:r.repositoryId,fullName:r.fullName}}async function mt(e){let{path:t}=e,o=await V(t);if(!o)throw new Error(`Could not resolve repository for path: ${t}`);let r=t;t.startsWith(o.gitRoot)&&(r=t.slice(o.gitRoot.length).replace(/^\//,""));let n=r?`.thinker.md/contexts/${r}/CONTEXT.md`:".thinker.md/contexts/CONTEXT.md";return{...await Rt(o.repositoryId,n,{branch:o.branch,commit:o.commit}),repositoryId:o.repositoryId,fullName:o.fullName}}async function ut(e){let{path:t}=e,o=await V(t);if(!o)throw new Error(`Could not resolve repository for path: ${t}`);return{...await Ee(o.repositoryId,{branch:o.branch,commit:o.commit}),repositoryId:o.repositoryId,fullName:o.fullName}}async function ke(e){return V(e.path)}import{McpServer as Eo}from"@modelcontextprotocol/sdk/server/mcp.js";import{z as H}from"zod";function gt(){let e=new Eo({name:"thinkermd",version:"0.1.0"});return e.tool("thinkermd_search","Semantic search across context files in the repository. Use this to find relevant documentation and context about the codebase.",{query:H.string().describe("Search query - what you're looking for"),path:H.string().optional().describe("Path to directory (defaults to current working directory)"),limit:H.number().optional().default(10).describe("Maximum number of results to return")},async({query:t,path:o,limit:r})=>{try{let n=o||process.cwd(),i=await dt({path:n,query:t,limit:r});return{content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(n){return{content:[{type:"text",text:`Error: ${n instanceof Error?n.message:"Search failed"}`}],isError:!0}}}),e.tool("thinkermd_read","Read a specific context file from the repository. Use this to get the full content of a CONTEXT.md file.",{filePath:H.string().describe("Path to the context file relative to repository root"),path:H.string().optional().describe("Path to repository directory (defaults to current working directory)")},async({filePath:t,path:o})=>{try{let r=o||process.cwd();return{content:[{type:"text",text:(await pt({path:r,filePath:t})).content}]}}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:"Read failed"}`}],isError:!0}}}),e.tool("thinkermd_get_context","Get the CONTEXT.md file for a specific directory. This provides contextual documentation about that part of the codebase.",{path:H.string().describe("Path to the directory to get context for")},async({path:t})=>{try{return{content:[{type:"text",text:(await mt({path:t})).content}]}}catch(o){return{content:[{type:"text",text:`Error: ${o instanceof Error?o.message:"Get context failed"}`}],isError:!0}}}),e.tool("thinkermd_list_files","List all CONTEXT.md files available in the repository. Use this to discover what documentation is available.",{path:H.string().optional().describe("Path to repository directory (defaults to current working directory)")},async({path:t})=>{try{let o=t||process.cwd(),r=await ut({path:o});return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(o){return{content:[{type:"text",text:`Error: ${o instanceof Error?o.message:"List files failed"}`}],isError:!0}}}),e}import{StreamableHTTPServerTransport as bo}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{InitializeRequestSchema as ko}from"@modelcontextprotocol/sdk/types.js";var D=new Map;function vo(e){let t=o=>ko.safeParse(o).success;return Array.isArray(e)?e.some(o=>t(o)):t(e)}async function Po(e){let t=[];for await(let o of e)t.push(o);return Buffer.concat(t).toString()}async function So(e,t){let o=e.headers["mcp-session-id"],r=await Po(e),n;try{n=JSON.parse(r)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({jsonrpc:"2.0",error:{code:-32700,message:"Parse error: Invalid JSON"},id:null}));return}if(o&&D.has(o)){await D.get(o).handleRequest(e,t,n);return}if(!o&&vo(n)){let i=new bo({sessionIdGenerator:()=>crypto.randomUUID(),enableJsonResponse:!0,onsessioninitialized:a=>{D.set(a,i),console.log(`[MCP] Session started: ${a}`)}});i.onclose=()=>{let a=i.sessionId;a&&D.has(a)&&(console.log(`[MCP] Session closed: ${a}`),D.delete(a))},i.onerror=a=>{console.error("[MCP] Transport error:",a)},await gt().connect(i),await i.handleRequest(e,t,n);return}t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null}))}async function $o(e,t){let o=e.headers["mcp-session-id"];if(!o||!D.has(o)){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid or missing session ID"}));return}console.log(`[MCP] SSE stream requested for session: ${o}`),await D.get(o).handleRequest(e,t)}async function Io(e,t){let o=e.headers["mcp-session-id"];if(!o||!D.has(o)){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid or missing session ID"}));return}console.log(`[MCP] Session termination requested: ${o}`),await D.get(o).handleRequest(e,t)}function No(e){e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, DELETE, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, mcp-session-id, Authorization"),e.setHeader("Access-Control-Expose-Headers","mcp-session-id")}async function _o(e,t){if(No(t),e.method==="OPTIONS"){t.writeHead(204),t.end();return}try{e.method==="POST"?await So(e,t):e.method==="GET"?await $o(e,t):e.method==="DELETE"?await Io(e,t):(t.writeHead(405,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Method not allowed"})))}catch(o){console.error("[MCP] Error handling request:",o),t.headersSent||(t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})))}}function Ao(){let e=new To;return e.get("/health",t=>t.json({status:"ok",version:"0.1.0"})),e.post("/mcp/search",async t=>{try{let o=await t.req.json(),{path:r,query:n,limit:i}=o;if(!r||!n)return t.json({error:"path and query are required"},400);let s=await dt({path:r,query:n,limit:i});return t.json(s)}catch(o){return console.error("[MCP] Search error:",o),t.json({error:o instanceof Error?o.message:"Search failed"},500)}}),e.get("/mcp/read",async t=>{try{let o=t.req.query("path"),r=t.req.query("filePath");if(!o||!r)return t.json({error:"path and filePath are required"},400);let n=await pt({path:o,filePath:r});return t.json(n)}catch(o){return console.error("[MCP] Read error:",o),t.json({error:o instanceof Error?o.message:"Read failed"},500)}}),e.get("/mcp/context",async t=>{try{let o=t.req.query("path");if(!o)return t.json({error:"path is required"},400);let r=await mt({path:o});return t.json(r)}catch(o){return console.error("[MCP] Get context error:",o),t.json({error:o instanceof Error?o.message:"Get context failed"},500)}}),e.get("/mcp/files",async t=>{try{let o=t.req.query("path");if(!o)return t.json({error:"path is required"},400);let r=await ut({path:o});return t.json(r)}catch(o){return console.error("[MCP] List files error:",o),t.json({error:o instanceof Error?o.message:"List files failed"},500)}}),e.get("/mcp/repo",async t=>{try{let o=t.req.query("path");if(!o)return t.json({error:"path is required"},400);let r=await ke({path:o});return r?t.json({connected:!0,...r}):t.json({connected:!1,message:"Repository not found in ThinkerMD"})}catch(o){return console.error("[MCP] Get repo info error:",o),t.json({error:o instanceof Error?o.message:"Get repo info failed"},500)}}),e}function Oo(e,t){let o=`http://localhost:${e}/mcp`,r=64,n=40,i=t.length>n?t.slice(0,n-3)+"...":t;console.log(M.cyan(`
93
+ </html>`),a(),r({token:m,userId:g,email:f})):(c.writeHead(400,{"Content-Type":"text/plain"}),c.end("Missing required parameters"))}else c.writeHead(404,{"Content-Type":"text/plain"}),c.end("Not found")}),i.listen(e)})}async function io(e,t){try{let o=await fetch(`${e}/api/auth/validate`,{method:"GET",headers:{Authorization:`Bearer ${t}`}});return o.ok?{valid:!0,userId:(await o.json()).userId}:o.status===401?{valid:!1,error:"Invalid or expired token"}:{valid:!1,error:`API error: ${o.status} ${o.statusText}`}}catch(o){return{valid:!1,error:`Connection failed: ${o}`}}}async function fe(e){let t=me(),o=e.apiUrl||J;if(e.token){t.start("Validating token...");let n=await io(o,e.token);n.valid||(t.fail("Authentication failed"),console.error(v.red(`Error: ${n.error}`)),process.exit(1));let i={apiToken:e.token,apiUrl:o,userId:n.userId};await nt(i),t.succeed("Authenticated successfully"),console.log(),console.log(`Config saved to: ${v.cyan(U)}`);return}let r=await $();if(r.apiToken){console.log(v.green("You are already logged in.")),console.log(`User ID: ${v.cyan(r.userId||"unknown")}`),console.log(),console.log("To re-authenticate, run:"),console.log(v.cyan(" tk login --token NEW_TOKEN")),console.log(),console.log("Or to login with a different account:"),console.log(v.cyan(" tk logout && tk login"));return}console.log(v.bold("ThinkerMD Login")),console.log(),t.start("Setting up authentication...");try{let n=await ge(),i=to(16).toString("hex"),s=`${o}/cli/auth?port=${n}&state=${i}`;t.succeed("Ready for authentication"),console.log(),console.log("Opening browser for authentication..."),console.log(),console.log("If the browser doesn't open, visit this URL manually:"),console.log(v.cyan(s)),console.log(),await ro(s),t.start("Waiting for authentication...");let{token:a,userId:l,email:c}=await no(n,i);await nt({apiToken:a,apiUrl:o,userId:l,email:c}),t.succeed("Authenticated successfully"),console.log(),console.log(`Logged in as: ${v.cyan(c)}`),console.log(`Config saved to: ${v.cyan(U)}`)}catch(n){t.fail("Authentication failed"),console.error(v.red(`Error: ${n instanceof Error?n.message:n}`)),console.log(),console.log("You can also authenticate manually:"),console.log(v.cyan(" tk login --token YOUR_TOKEN")),console.log(),console.log(`Get your token at: ${v.cyan(`${o}/dashboard/settings`)}`),process.exit(1)}}async function he(){let e=me();e.start("Logging out..."),await nt({}),e.succeed("Logged out successfully"),console.log(),console.log(`Config cleared from: ${v.cyan(U)}`)}import{join as ye}from"path";import u from"chalk";import so from"ora";async function ao(e){let{readFile:t}=await import("fs/promises"),{existsSync:o}=await import("fs"),r=ye(I(e),"structure.json");try{if(o(r)){let n=await t(r,"utf-8");return JSON.parse(n)}}catch{}return null}async function co(e){let{readdir:t,stat:o}=await import("fs/promises"),r=0;async function n(i){let s;try{s=await t(i)}catch{return}for(let a of s){let l=ye(i,a);try{(await o(l)).isDirectory()?await n(l):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function we(){let e=so(),t=process.cwd();try{await N(t)||(console.error(u.red("Error: Not a git repository")),process.exit(1));let o=await k(t),r=await P(),n=I(o);console.log(u.bold("ThinkerMD Status")),console.log();let i=await z(o),s=r.branch||await S(o),a=r.commitSha||await _(o);console.log(u.dim("Repository:")),console.log(` Name: ${u.cyan(i||"unknown")}`),console.log(` Branch: ${u.cyan(s)}`),console.log(` Commit: ${u.cyan(a.slice(0,7))}`),console.log();let{stat:l}=await import("fs/promises"),c=!1;try{await l(n),c=!0}catch{}if(!c){console.log(u.dim("Local Context:")),console.log(` Status: ${u.yellow("Not initialized")}`),console.log(),console.log("Run "+u.cyan("tk init")+" to create .thinker.md/");return}console.log(u.dim("Local Context:"));let d=await F(o),m=await ao(o),g=await co(n);if(console.log(` Status: ${u.green("Initialized")}`),console.log(` Project ID: ${u.cyan(d?.projectId||"unknown")}`),console.log(` Context files: ${u.cyan(g)}`),m&&(console.log(` Directories: ${u.cyan(m.directoryCount)}`),console.log(` Files tracked: ${u.cyan(m.fileCount)}`)),d?.generatedAt){let p=new Date(d.generatedAt);console.log(` Generated: ${u.cyan(p.toLocaleString())}`)}d?.pulledFrom&&console.log(` Pulled from: ${u.cyan(d.pulledFrom.slice(0,7))}`),console.log();let f=r.metaRepoId||d?.projectId;if(f&&r.r2Endpoint){console.log(u.dim("Remote Storage:")),e.start("Checking remote...");try{let p=await ct(f,s);p?(e.stop(),console.log(` Latest commit: ${u.cyan(p.slice(0,7))}`),p===a?console.log(` Status: ${u.green("Up to date")}`):d?.pulledFrom===p?console.log(` Status: ${u.yellow("Local changes not pushed")}`):(console.log(` Status: ${u.yellow("Remote has newer version")}`),console.log(),console.log("Run "+u.cyan("tk pull")+" to download latest context"))):(e.stop(),console.log(` Status: ${u.yellow("No remote context found")}`),console.log(),console.log("Run "+u.cyan("tk push")+" to upload context"))}catch{e.stop(),console.log(` Status: ${u.dim("Unable to check (no credentials)")}`)}}console.log(),console.log(u.dim("Mode:")),console.log(` ${r.mode==="sandbox"?u.cyan("Sandbox"):u.cyan("Local")}`),r.mode==="sandbox"&&console.log(` Job ID: ${u.cyan(r.jobId||"unknown")}`)}catch(o){e.fail("Failed to get status"),console.error(u.red(`Error: ${o}`)),process.exit(1)}}import w from"chalk";import lo from"ora";async function xe(e){let t=lo(),o=process.cwd();try{await N(o)||(console.error(w.red("Error: Not a git repository")),process.exit(1));let r=await k(o);if((await P()).mode==="sandbox"){console.error(w.yellow("Note: In sandbox mode, generation is triggered automatically.")),console.log("Use 'tk init' to create structure and 'tk push' when done.");return}let i=await $();i.apiToken||(console.error(w.red("Error: Not logged in")),console.log(),console.log("Please authenticate first:"),console.log(w.cyan(" tk login --token YOUR_TOKEN")),process.exit(1));let s=i.apiUrl||J,a=await z(r),l=e.branch||await S(r),c=await _(r);a||(console.error(w.red("Error: Unable to determine repository name")),console.log("Make sure this repository has a remote origin configured."),process.exit(1)),console.log(w.bold("Trigger Context Generation")),console.log(),console.log(`Repository: ${w.cyan(a)}`),console.log(`Branch: ${w.cyan(l)}`),console.log(`Commit: ${w.cyan(c.slice(0,7))}`),console.log(),t.start("Looking up meta-repository...");let d=await fetch(`${s}/api/meta-repositories/lookup?repo=${encodeURIComponent(a)}`,{headers:{Authorization:`Bearer ${i.apiToken}`}});d.ok||(d.status===404&&(t.fail("Meta-repository not found"),console.log(),console.log("This repository is not connected to ThinkerMD. Please add it at:"),console.log(w.cyan(`${s}/dashboard`)),process.exit(1)),t.fail("API error"),console.error(w.red(`Error: ${d.status} ${d.statusText}`)),process.exit(1));let m=await d.json();t.succeed(`Found meta-repository: ${m.id}`),t.start("Triggering generation job...");let g=await fetch(`${s}/api/jobs/generate`,{method:"POST",headers:{Authorization:`Bearer ${i.apiToken}`,"Content-Type":"application/json"},body:JSON.stringify({metaRepositoryId:m.id,commitSha:c,branch:l,priority:"normal"})});if(!g.ok){t.fail("Failed to trigger generation");let p=await g.json().catch(()=>({}));console.error(w.red(`Error: ${p.error||g.statusText}`)),process.exit(1)}let f=await g.json();if(t.succeed("Generation job queued"),console.log(),console.log(`Job ID: ${w.cyan(f.jobId)}`),console.log(`Status: ${w.cyan(f.status)}`),f.estimatedPosition&&console.log(`Queue position: ${w.cyan(f.estimatedPosition)}`),console.log(),console.log("Track progress at:"),console.log(w.cyan(`${s}/dashboard/repositories/${m.id}/jobs/${f.jobId}`)),e.wait){console.log(),t.start("Waiting for completion...");let p=!1,C="";for(;!p;){await new Promise(x=>setTimeout(x,5e3));let G=await fetch(`${s}/api/jobs/${f.jobId}`,{headers:{Authorization:`Bearer ${i.apiToken}`}});if(G.ok){let x=await G.json();x.status!==C&&(C=x.status,t.text=`Status: ${x.status} (${x.progress||0}%)`),(x.status==="completed"||x.status==="failed")&&(p=!0,x.status==="completed"?(t.succeed("Generation completed!"),console.log(),console.log("Run "+w.cyan("tk pull")+" to download the generated context")):(t.fail("Generation failed"),x.errorMessage&&console.error(w.red(`Error: ${x.errorMessage}`))))}}}}catch(r){t.fail("Failed to trigger generation"),console.error(w.red(`Error: ${r}`)),process.exit(1)}}import bt from"chalk";import{Hono as To}from"hono";import j from"chalk";import{createServer as Ro}from"http";import{homedir as Re}from"os";import{join as ke}from"path";import{readFile as uo,writeFile as go,mkdir as fo}from"fs/promises";import{existsSync as ho}from"fs";async function po(){return(await $()).apiUrl||process.env.THINKERMD_API_URL||J}async function mo(){return(await $()).apiToken||process.env.THINKERMD_API_TOKEN||null}async function lt(e,t={}){let o=await po(),r=await mo();if(!r)throw new Error("Not authenticated. Run `tk login` first.");let{method:n="GET",body:i,params:s}=t,a=`${o}${e}`;if(s){let c=new URLSearchParams(s);a+=`?${c.toString()}`}let l=await fetch(a,{method:n,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:i?JSON.stringify(i):void 0});if(!l.ok){let c=await l.json().catch(()=>({}));throw new Error(c.error||`API request failed: ${l.status} ${l.statusText}`)}return l.json()}async function Ce(e,t,o={}){return lt("/api/mcp/search",{method:"POST",body:{repositoryId:e,query:t,commit:o.commit,branch:o.branch,limit:o.limit||10}})}async function Rt(e,t,o={}){return lt("/api/mcp/read",{params:{repositoryId:e,filePath:t,...o.branch&&{branch:o.branch},...o.commit&&{commit:o.commit}}})}async function Ee(e,t={}){return lt("/api/mcp/files",{params:{repositoryId:e,...t.branch&&{branch:t.branch},...t.commit&&{commit:t.commit}}})}async function Te(e){try{let t=await lt("/api/user/repositories",{params:{remoteUrl:e}});return t.repositories&&t.repositories.length>0?t.repositories[0]:null}catch{return null}}var kt=ke(Re(),".thinkermd","cache","repo-mapping.json"),yo=24*60*60*1e3;async function wo(){try{if(ho(kt)){let e=await uo(kt,"utf-8");return JSON.parse(e)}}catch{}return{mappings:{}}}async function xo(e){let t=ke(Re(),".thinkermd","cache");await fo(t,{recursive:!0}),await go(kt,JSON.stringify(e,null,2))}async function q(e){let t;try{t=await k(e)}catch{return console.error(`[RepoResolver] Not a git repository: ${e}`),null}let o=await yt(t);if(!o)return console.error(`[RepoResolver] No git remote found for: ${t}`),null;let r=Co(o),n=await wo(),i=n.mappings[r];if(i&&Date.now()-i.cachedAt<yo){console.log(`[RepoResolver] Cache hit for: ${r}`);let c=await S(t),d=await _(t);return{repositoryId:i.repositoryId,fullName:i.fullName,remoteUrl:r,gitRoot:t,branch:c||"main",commit:d||"HEAD"}}console.log(`[RepoResolver] Querying API for: ${r}`);let s=await Te(r);if(!s)return console.error(`[RepoResolver] Repository not found in ThinkerMD: ${r}`),null;n.mappings[r]={remoteUrl:r,repositoryId:s.id,fullName:s.fullName,cachedAt:Date.now()},await xo(n);let a=await S(t),l=await _(t);return{repositoryId:s.id,fullName:s.fullName,remoteUrl:r,gitRoot:t,branch:a||"main",commit:l||"HEAD"}}function Co(e){return e=e.replace(/\.git$/,""),e.startsWith("git@")&&(e=e.replace(/^git@/,"https://").replace(/:([^/])/,"/$1")),e.startsWith("http://")&&(e=e.replace("http://","https://")),e}async function dt(e){let{path:t,query:o,limit:r=10}=e,n=await q(t);if(!n)throw new Error(`Could not resolve repository for path: ${t}`);return{...await Ce(n.repositoryId,o,{branch:n.branch,commit:n.commit,limit:r}),repositoryId:n.repositoryId,fullName:n.fullName}}async function pt(e){let{path:t,filePath:o}=e,r=await q(t);if(!r)throw new Error(`Could not resolve repository for path: ${t}`);return{...await Rt(r.repositoryId,o,{branch:r.branch,commit:r.commit}),repositoryId:r.repositoryId,fullName:r.fullName}}async function mt(e){let{path:t}=e,o=await q(t);if(!o)throw new Error(`Could not resolve repository for path: ${t}`);let r=t;t.startsWith(o.gitRoot)&&(r=t.slice(o.gitRoot.length).replace(/^\//,""));let n=r?`.thinker.md/contexts/${r}/CONTEXT.md`:".thinker.md/contexts/CONTEXT.md";return{...await Rt(o.repositoryId,n,{branch:o.branch,commit:o.commit}),repositoryId:o.repositoryId,fullName:o.fullName}}async function ut(e){let{path:t}=e,o=await q(t);if(!o)throw new Error(`Could not resolve repository for path: ${t}`);return{...await Ee(o.repositoryId,{branch:o.branch,commit:o.commit}),repositoryId:o.repositoryId,fullName:o.fullName}}async function be(e){return q(e.path)}import{McpServer as Eo}from"@modelcontextprotocol/sdk/server/mcp.js";import{z as B}from"zod";function gt(){let e=new Eo({name:"thinkermd",version:"0.1.0"});return e.tool("thinkermd_search",`Semantic search across CONTEXT.md files indexed from the repository.
94
+
95
+ USE THIS TOOL WHEN:
96
+ - You need to understand how something works in the codebase
97
+ - Looking for architecture decisions, patterns, or conventions
98
+ - Finding documentation about specific modules or features
99
+
100
+ RETURNS: Relevant chunks from CONTEXT.md files ranked by semantic similarity.
101
+
102
+ EXAMPLE: query="how does authentication work" \u2192 returns chunks about auth implementation`,{query:B.string().describe("Natural language search query describing what you want to find"),path:B.string().optional().describe("Local directory path to identify the repository (defaults to cwd)"),limit:B.number().optional().default(10).describe("Maximum results (default: 10)")},async({query:t,path:o,limit:r})=>{try{let n=o||process.cwd(),i=await dt({path:n,query:t,limit:r});return{content:[{type:"text",text:JSON.stringify(i,null,2)}]}}catch(n){return{content:[{type:"text",text:`Error: ${n instanceof Error?n.message:"Search failed"}`}],isError:!0}}}),e.tool("thinkermd_read",`Read a specific CONTEXT.md file from the repository.
103
+
104
+ USE THIS TOOL WHEN:
105
+ - You found a file path from thinkermd_search and want to read the full content
106
+ - You know the exact context file path you need
107
+
108
+ PATH FORMAT: Use the exact filePath as returned by thinkermd_search or thinkermd_list_files results.
109
+
110
+ RETURNS: Full content of the CONTEXT.md file.`,{filePath:B.string().describe("Full path to context file as returned by thinkermd_search or thinkermd_list_files"),path:B.string().optional().describe("Local directory path to identify the repository (defaults to cwd)")},async({filePath:t,path:o})=>{try{let r=o||process.cwd();return{content:[{type:"text",text:(await pt({path:r,filePath:t})).content}]}}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:"Read failed"}`}],isError:!0}}}),e.tool("thinkermd_get_context",`Get the CONTEXT.md documentation for a specific directory in the codebase.
111
+
112
+ USE THIS TOOL WHEN:
113
+ - You want to understand a specific directory/module
114
+ - You need the full context for a component before making changes
115
+
116
+ PATH FORMAT: Directory path relative to repository root. Do NOT include ".thinker.md/contexts/" prefix.
117
+
118
+ Example: To get context for "src/lib/" directory, pass path="src/lib"
119
+
120
+ RETURNS: Full content of the CONTEXT.md file for that directory.
121
+
122
+ PREFERRED over thinkermd_read when you know the directory but not the exact file path.`,{path:B.string().describe("Directory path relative to repo root (e.g., 'src/lib', 'components/auth'). Do NOT include '.thinker.md/contexts/'")},async({path:t})=>{try{return{content:[{type:"text",text:(await mt({path:t})).content}]}}catch(o){return{content:[{type:"text",text:`Error: ${o instanceof Error?o.message:"Get context failed"}`}],isError:!0}}}),e.tool("thinkermd_list_files",`List all indexed CONTEXT.md files in the repository.
123
+
124
+ USE THIS TOOL WHEN:
125
+ - You want to discover what documentation exists
126
+ - Planning which contexts to read before a task
127
+ - Getting an overview of the documented areas
128
+
129
+ RETURNS: Array of file paths with chunk counts. Use these paths with thinkermd_read.`,{path:B.string().optional().describe("Local directory to identify the repository (defaults to cwd)")},async({path:t})=>{try{let o=t||process.cwd(),r=await ut({path:o});return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}catch(o){return{content:[{type:"text",text:`Error: ${o instanceof Error?o.message:"List files failed"}`}],isError:!0}}}),e}import{StreamableHTTPServerTransport as ko}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{InitializeRequestSchema as bo}from"@modelcontextprotocol/sdk/types.js";var D=new Map;function vo(e){let t=o=>bo.safeParse(o).success;return Array.isArray(e)?e.some(o=>t(o)):t(e)}async function Po(e){let t=[];for await(let o of e)t.push(o);return Buffer.concat(t).toString()}async function So(e,t){let o=e.headers["mcp-session-id"],r=await Po(e),n;try{n=JSON.parse(r)}catch{t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({jsonrpc:"2.0",error:{code:-32700,message:"Parse error: Invalid JSON"},id:null}));return}if(o&&D.has(o)){await D.get(o).handleRequest(e,t,n);return}if(!o&&vo(n)){let i=new ko({sessionIdGenerator:()=>crypto.randomUUID(),enableJsonResponse:!0,onsessioninitialized:a=>{D.set(a,i),console.log(`[MCP] Session started: ${a}`)}});i.onclose=()=>{let a=i.sessionId;a&&D.has(a)&&(console.log(`[MCP] Session closed: ${a}`),D.delete(a))},i.onerror=a=>{console.error("[MCP] Transport error:",a)},await gt().connect(i),await i.handleRequest(e,t,n);return}t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null}))}async function $o(e,t){let o=e.headers["mcp-session-id"];if(!o||!D.has(o)){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid or missing session ID"}));return}console.log(`[MCP] SSE stream requested for session: ${o}`),await D.get(o).handleRequest(e,t)}async function Io(e,t){let o=e.headers["mcp-session-id"];if(!o||!D.has(o)){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid or missing session ID"}));return}console.log(`[MCP] Session termination requested: ${o}`),await D.get(o).handleRequest(e,t)}function No(e){e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, POST, DELETE, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, mcp-session-id, Authorization"),e.setHeader("Access-Control-Expose-Headers","mcp-session-id")}async function _o(e,t){if(No(t),e.method==="OPTIONS"){t.writeHead(204),t.end();return}try{e.method==="POST"?await So(e,t):e.method==="GET"?await $o(e,t):e.method==="DELETE"?await Io(e,t):(t.writeHead(405,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Method not allowed"})))}catch(o){console.error("[MCP] Error handling request:",o),t.headersSent||(t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})))}}function Ao(){let e=new To;return e.get("/health",t=>t.json({status:"ok",version:"0.1.0"})),e.post("/mcp/search",async t=>{try{let o=await t.req.json(),{path:r,query:n,limit:i}=o;if(!r||!n)return t.json({error:"path and query are required"},400);let s=await dt({path:r,query:n,limit:i});return t.json(s)}catch(o){return console.error("[MCP] Search error:",o),t.json({error:o instanceof Error?o.message:"Search failed"},500)}}),e.get("/mcp/read",async t=>{try{let o=t.req.query("path"),r=t.req.query("filePath");if(!o||!r)return t.json({error:"path and filePath are required"},400);let n=await pt({path:o,filePath:r});return t.json(n)}catch(o){return console.error("[MCP] Read error:",o),t.json({error:o instanceof Error?o.message:"Read failed"},500)}}),e.get("/mcp/context",async t=>{try{let o=t.req.query("path");if(!o)return t.json({error:"path is required"},400);let r=await mt({path:o});return t.json(r)}catch(o){return console.error("[MCP] Get context error:",o),t.json({error:o instanceof Error?o.message:"Get context failed"},500)}}),e.get("/mcp/files",async t=>{try{let o=t.req.query("path");if(!o)return t.json({error:"path is required"},400);let r=await ut({path:o});return t.json(r)}catch(o){return console.error("[MCP] List files error:",o),t.json({error:o instanceof Error?o.message:"List files failed"},500)}}),e.get("/mcp/repo",async t=>{try{let o=t.req.query("path");if(!o)return t.json({error:"path is required"},400);let r=await be({path:o});return r?t.json({connected:!0,...r}):t.json({connected:!1,message:"Repository not found in ThinkerMD"})}catch(o){return console.error("[MCP] Get repo info error:",o),t.json({error:o instanceof Error?o.message:"Get repo info failed"},500)}}),e}function Oo(e,t){let o=`http://localhost:${e}/mcp`,r=64,n=40,i=t.length>n?t.slice(0,n-3)+"...":t;console.log(j.cyan(`
104
130
  \u2554${"\u2550".repeat(r)}\u2557
105
131
  \u2551${" ".repeat(r)}\u2551
106
132
  \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
@@ -112,17 +138,17 @@ This appears to be the first generation. Use 'tk init' to create initial structu
112
138
  \u2551 .md \u2551
113
139
  \u2551${" ".repeat(r)}\u2551
114
140
  \u2560${"\u2550".repeat(r)}\u2563
115
- \u2551${" ".repeat(r)}\u2551`));let s=` \u2713 Logged in as: ${i}`,a=s+" ".repeat(Math.max(0,r-s.length));console.log(M.cyan("\u2551")+a+M.cyan("\u2551"));let l=` \u2713 Daemon running on: ${o}`,c=l+" ".repeat(Math.max(0,r-l.length));console.log(M.cyan("\u2551")+c+M.cyan("\u2551")),console.log(M.cyan(`\u2551${" ".repeat(r)}\u2551
141
+ \u2551${" ".repeat(r)}\u2551`));let s=` \u2713 Logged in as: ${i}`,a=s+" ".repeat(Math.max(0,r-s.length));console.log(j.cyan("\u2551")+a+j.cyan("\u2551"));let l=` \u2713 Daemon running on: ${o}`,c=l+" ".repeat(Math.max(0,r-l.length));console.log(j.cyan("\u2551")+c+j.cyan("\u2551")),console.log(j.cyan(`\u2551${" ".repeat(r)}\u2551
116
142
  \u2560${"\u2550".repeat(r)}\u2563
117
143
  \u2551 Add to your AI tool config: \u2551
118
144
  \u2551${" ".repeat(r)}\u2551
119
145
  \u2551 { \u2551
120
146
  \u2551 "mcpServers": { \u2551
121
147
  \u2551 "thinkermd": { \u2551
122
- \u2551 "type": "http", \u2551`));let d=` "url": "${o}"`,m=" "+d+" ".repeat(Math.max(0,r-3-d.length));console.log(M.cyan("\u2551")+m+M.cyan("\u2551")),console.log(M.cyan(`\u2551 } \u2551
148
+ \u2551 "type": "http", \u2551`));let d=` "url": "${o}"`,m=" "+d+" ".repeat(Math.max(0,r-3-d.length));console.log(j.cyan("\u2551")+m+j.cyan("\u2551")),console.log(j.cyan(`\u2551 } \u2551
123
149
  \u2551 } \u2551
124
150
  \u2551 } \u2551
125
151
  \u2551${" ".repeat(r)}\u2551
126
- \u2551 ${M.yellow("Press Ctrl+C to stop")} \u2551
152
+ \u2551 ${j.yellow("Press Ctrl+C to stop")} \u2551
127
153
  \u255A${"\u2550".repeat(r)}\u255D
128
- `))}async function ve(e){let{port:t}=e,o=Ao(),r=await $(),n=r.email||r.userId||"authenticated user";Ro(async(s,a)=>{let l=new URL(s.url||"/",`http://localhost:${t}`);if(l.pathname==="/mcp"){await _o(s,a);return}let c={};for(let[m,g]of Object.entries(s.headers))typeof g=="string"?c[m]=g:Array.isArray(g)&&(c[m]=g.join(", "));let d=await o.fetch(new Request(l.toString(),{method:s.method,headers:c,body:s.method!=="GET"&&s.method!=="HEAD"?s:void 0,duplex:"half"}));if(a.writeHead(d.status,Object.fromEntries(d.headers.entries())),d.body){let m=d.body.getReader();try{for(;;){let{done:g,value:f}=await m.read();if(g)break;a.write(f)}}finally{m.releaseLock()}}a.end()}).listen(t,()=>{Oo(t,n)}),await new Promise(()=>{})}var Fo=3456;async function vt(e){(await $()).apiToken||(console.error(kt.red("Not authenticated. Run `tk login` first.")),process.exit(1));let o=e.port?parseInt(e.port,10):Fo;(isNaN(o)||o<1||o>65535)&&(console.error(kt.red(`Invalid port: ${e.port}`)),process.exit(1));try{await ve({port:o})}catch(r){console.error(kt.red("Failed to start daemon:"),r),process.exit(1)}}import{StdioServerTransport as jo}from"@modelcontextprotocol/sdk/server/stdio.js";async function Pe(){let e=gt(),t=new jo;await e.connect(t)}var R=new Mo;R.name("tk").description("ThinkerMD CLI - Manage .thinker.md/ context files").version("0.1.0");R.command("init").description("Create .thinker.md/ structure with empty CONTEXT.md files").option("-f, --force","Reinitialize even if .thinker.md/ exists").option("-m, --merge","Only add missing CONTEXT.md files, keep existing ones").action(e=>Zt(e));R.command("validate").description("Verify .thinker.md/ structure matches repository").option("-q, --quiet","Only output errors, no summary").action(e=>te(e));R.command("pull").description("Download previous context from storage").option("-b, --branch <branch>","Specify branch to pull from").option("-c, --commit <sha>","Pull specific commit version").action(e=>ce(e));R.command("push").description("Validate and upload .thinker.md/ to storage").option("--skip-validation","Skip validation before push").option("--force-full","Force full generation mode (ignore previous context)").action(e=>pe(e));R.command("login").description("Authenticate with ThinkerMD API").option("-t, --token <token>","API token").option("--api-url <url>","Custom API URL").action(e=>fe(e));R.command("logout").description("Remove saved authentication").action(()=>he());R.command("status").description("Show status of .thinker.md/ context").action(()=>we());R.command("generate").description("Trigger context generation via API").option("-b, --branch <branch>","Specify branch to generate for").option("-w, --wait","Wait for generation to complete").action(e=>xe(e));R.command("serve").description("Start MCP daemon for AI assistant integration").option("-p, --port <port>","Port to listen on","3456").action(e=>vt(e));R.command("mcp").description("Run as stdio MCP server (for Claude Code integration)").action(()=>Pe());R.on("command:*",()=>{console.error(Se.red(`Unknown command: ${R.args.join(" ")}`)),console.log(),R.help()});async function $e(){try{if(process.argv.length===2){await vt({port:"3456"});return}await R.parseAsync(process.argv)}catch(e){e instanceof Error&&console.error(Se.red(`Error: ${e.message}`)),process.exit(1)}}$e().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
154
+ `))}async function ve(e){let{port:t}=e,o=Ao(),r=await $(),n=r.email||r.userId||"authenticated user";Ro(async(s,a)=>{let l=new URL(s.url||"/",`http://localhost:${t}`);if(l.pathname==="/mcp"){await _o(s,a);return}let c={};for(let[m,g]of Object.entries(s.headers))typeof g=="string"?c[m]=g:Array.isArray(g)&&(c[m]=g.join(", "));let d=await o.fetch(new Request(l.toString(),{method:s.method,headers:c,body:s.method!=="GET"&&s.method!=="HEAD"?s:void 0,duplex:"half"}));if(a.writeHead(d.status,Object.fromEntries(d.headers.entries())),d.body){let m=d.body.getReader();try{for(;;){let{done:g,value:f}=await m.read();if(g)break;a.write(f)}}finally{m.releaseLock()}}a.end()}).listen(t,()=>{Oo(t,n)}),await new Promise(()=>{})}var Fo=3456;async function vt(e){(await $()).apiToken||(console.error(bt.red("Not authenticated. Run `tk login` first.")),process.exit(1));let o=e.port?parseInt(e.port,10):Fo;(isNaN(o)||o<1||o>65535)&&(console.error(bt.red(`Invalid port: ${e.port}`)),process.exit(1));try{await ve({port:o})}catch(r){console.error(bt.red("Failed to start daemon:"),r),process.exit(1)}}import{StdioServerTransport as Mo}from"@modelcontextprotocol/sdk/server/stdio.js";async function Pe(){let e=gt(),t=new Mo;await e.connect(t)}var R=new jo;R.name("tk").description("ThinkerMD CLI - Manage .thinker.md/ context files").version("0.1.0");R.command("init").description("Create .thinker.md/ structure with empty CONTEXT.md files").option("-f, --force","Reinitialize even if .thinker.md/ exists").option("-m, --merge","Only add missing CONTEXT.md files, keep existing ones").action(e=>Zt(e));R.command("validate").description("Verify .thinker.md/ structure matches repository").option("-q, --quiet","Only output errors, no summary").action(e=>te(e));R.command("pull").description("Download previous context from storage").option("-b, --branch <branch>","Specify branch to pull from").option("-c, --commit <sha>","Pull specific commit version").action(e=>ce(e));R.command("push").description("Validate and upload .thinker.md/ to storage").option("--skip-validation","Skip validation before push").option("--force-full","Force full generation mode (ignore previous context)").action(e=>pe(e));R.command("login").description("Authenticate with ThinkerMD API").option("-t, --token <token>","API token").option("--api-url <url>","Custom API URL").action(e=>fe(e));R.command("logout").description("Remove saved authentication").action(()=>he());R.command("status").description("Show status of .thinker.md/ context").action(()=>we());R.command("generate").description("Trigger context generation via API").option("-b, --branch <branch>","Specify branch to generate for").option("-w, --wait","Wait for generation to complete").action(e=>xe(e));R.command("serve").description("Start MCP daemon for AI assistant integration").option("-p, --port <port>","Port to listen on","3456").action(e=>vt(e));R.command("mcp").description("Run as stdio MCP server (for Claude Code integration)").action(()=>Pe());R.on("command:*",()=>{console.error(Se.red(`Unknown command: ${R.args.join(" ")}`)),console.log(),R.help()});async function $e(){try{if(process.argv.length===2){await vt({port:"3456"});return}await R.parseAsync(process.argv)}catch(e){e instanceof Error&&console.error(Se.red(`Error: ${e.message}`)),process.exit(1)}}$e().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thinkermd/cli",
3
- "version": "0.1.23",
3
+ "version": "0.1.26",
4
4
  "description": "ThinkerMD CLI for managing .thinker.md/ context files",
5
5
  "type": "module",
6
6
  "bin": {