@thinkermd/cli 0.1.5 → 0.1.7

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 +94 -78
  2. package/package.json +1 -1
package/dist/tk.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{Command as Io}from"commander";import ve from"chalk";import{join as st}from"path";import{writeFile as Wt}from"fs/promises";import k from"chalk";import Ue from"ora";import{homedir as Lt}from"os";import{join as G}from"path";import{readFile as Ut,writeFile as Bt,mkdir as $e}from"fs/promises";import{existsSync as Kt}from"fs";var y=".thinker.md",O="config.json",tt="structure.json",F="CONTEXT.md",vt="contexts",St="thinker.md.tar.gz",$t="metadata.json",It="latest",et="repos",ot="branches";var _t="thinkermd-meta-repos",Nt="1.0",ut="full",At="incremental",Ot=["node_modules",".git","dist","build",".next",".nuxt","coverage","__pycache__",".pytest_cache","venv",".venv","vendor","target",y],Ft=3,jt=1e3,Dt="[Describe the purpose",Mt=100;var L=G(Lt(),".thinkermd","config.json"),J="https://app.thinkermd.dev";function gt(){return process.env.META_REPO_ID&&process.env.JOB_TOKEN?"sandbox":"local"}function Ie(){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 I(){try{if(Kt(L)){let e=await Ut(L,"utf-8");return JSON.parse(e)}}catch{}return{}}async function rt(e){let t=G(Lt(),".thinkermd");await $e(t,{recursive:!0,mode:448}),await Bt(L,JSON.stringify(e,null,2),{mode:384})}async function S(){return gt()==="sandbox"?Ie():{mode:"local",...await I()}}var z={version:Nt,ignore:[...Ot]};async function j(e){let t=G(e,y,O);try{if(Kt(t)){let o=await Ut(t,"utf-8");return JSON.parse(o)}}catch{}return null}async function nt(e,t){let o=G(e,y,O);await Bt(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 _(e){return G(e,y)}function q(e){return G(e,y,vt)}import{execFile as _e}from"child_process";import{promisify as Ne}from"util";var Ae=Ne(_e);async function H(e,t="."){let{stdout:o}=await Ae("git",e,{cwd:t});return o.trim()}async function $(e="."){try{return await H(["branch","--show-current"],e)}catch(t){throw new Error(`Failed to get current branch: ${t}`)}}async function A(e="."){try{return await H(["rev-parse","HEAD"],e)}catch(t){throw new Error(`Failed to get current commit: ${t}`)}}async function T(e="."){try{return await H(["rev-parse","--show-toplevel"],e)}catch(t){throw new Error(`Not a git repository: ${t}`)}}async function N(e="."){try{return await H(["rev-parse","--git-dir"],e),!0}catch{return!1}}async function Jt(e=".",t){try{return await H(["log","-1","--format=%s",t||"HEAD"],e)}catch(o){throw new Error(`Failed to get commit message: ${o}`)}}async function ft(e="."){try{return await H(["remote","get-url","origin"],e)}catch{return null}}async function X(e="."){let t=await ft(e);if(!t)return null;let o=t.match(/[/:]([\w-]+)\/([\w.-]+?)(\.git)?$/);return o?`${o[1]}/${o[2]}`:null}import{join as U,relative as Oe,dirname as Fe}from"path";import{readFile as je}from"fs/promises";import{existsSync as Ht}from"fs";async function De(){let e=await import("ignore");return(e.default??e)()}var Me=[".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 Le(e){let t=await De();t.add(Me),t.add(z.ignore||[]);try{let o=U(e,".gitignore");if(Ht(o)){let r=await je(o,"utf-8");t.add(r)}}catch{}return t}async function it(e){let t=await Le(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 c of a){let l=U(t,c),d=Oe(e,l);if(!(o.ignores(d)||o.ignores(d+"/")))try{let u=await s(l);u.isDirectory()?(r.add(d),await Xt(e,l,o,r,n)):u.isFile()&&n.total++}catch{}}}async function Vt(e){let t=U(e,".thinker.md","contexts"),o=[];return Ht(U(e,".thinker.md","CONTEXT.md"))&&o.push(""),await zt(t,"",o),o}async function zt(e,t,o){let{readdir:r,stat:n}=await import("fs/promises"),i=t?U(e,t):e,s;try{s=await r(i)}catch{return}for(let a of s){let c=U(i,a),l=t?U(t,a):a;try{(await n(c)).isDirectory()?await zt(e,l,o):a==="CONTEXT.md"&&o.push(Fe(l))}catch{}}}async function h(e,t,o){let r=await S();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 qt(e,t){let o=await S();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 Yt(e,t){let r=`# ${t||"Project Root"}
2
+ import{Command as Io}from"commander";import Pe from"chalk";import{join as st}from"path";import{writeFile as Wt}from"fs/promises";import b from"chalk";import Ue from"ora";import{homedir as Lt}from"os";import{join as G}from"path";import{readFile as Ut,writeFile as Bt,mkdir as $e}from"fs/promises";import{existsSync as Kt}from"fs";var y=".thinker.md",A="config.json",tt="structure.json",O="CONTEXT.md",Pt="contexts",St="thinker.md.tar.gz",$t="metadata.json",It="latest",et="repos",ot="branches";var _t="thinkermd-meta-repos",Nt="1.0",ut="full",At="incremental",Ot=["node_modules",".git","dist","build",".next",".nuxt","coverage","__pycache__",".pytest_cache","venv",".venv","vendor","target",y],Ft=3,jt=1e3,Mt="[Describe the purpose",Dt=100;var L=G(Lt(),".thinkermd","config.json"),J="https://app.thinkermd.dev";function gt(){return process.env.META_REPO_ID&&process.env.JOB_TOKEN?"sandbox":"local"}function Ie(){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(L)){let e=await Ut(L,"utf-8");return JSON.parse(e)}}catch{}return{}}async function rt(e){let t=G(Lt(),".thinkermd");await $e(t,{recursive:!0,mode:448}),await Bt(L,JSON.stringify(e,null,2),{mode:384})}async function P(){return gt()==="sandbox"?Ie():{mode:"local",...await $()}}var z={version:Nt,ignore:[...Ot]};async function F(e){let t=G(e,y,A);try{if(Kt(t)){let o=await Ut(t,"utf-8");return JSON.parse(o)}}catch{}return null}async function nt(e,t){let o=G(e,y,A);await Bt(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 q(e){return G(e,y,Pt)}import{execFile as _e}from"child_process";import{promisify as Ne}from"util";var Ae=Ne(_e);async function H(e,t="."){let{stdout:o}=await Ae("git",e,{cwd:t});return o.trim()}async function S(e="."){try{return await H(["branch","--show-current"],e)}catch(t){throw new Error(`Failed to get current branch: ${t}`)}}async function N(e="."){try{return await H(["rev-parse","HEAD"],e)}catch(t){throw new Error(`Failed to get current commit: ${t}`)}}async function R(e="."){try{return await H(["rev-parse","--show-toplevel"],e)}catch(t){throw new Error(`Not a git repository: ${t}`)}}async function _(e="."){try{return await H(["rev-parse","--git-dir"],e),!0}catch{return!1}}async function Jt(e=".",t){try{return await H(["log","-1","--format=%s",t||"HEAD"],e)}catch(o){throw new Error(`Failed to get commit message: ${o}`)}}async function ft(e="."){try{return await H(["remote","get-url","origin"],e)}catch{return null}}async function X(e="."){let t=await ft(e);if(!t)return null;let o=t.match(/[/:]([\w-]+)\/([\w.-]+?)(\.git)?$/);return o?`${o[1]}/${o[2]}`:null}import{join as U,relative as Oe,dirname as Fe}from"path";import{readFile as je}from"fs/promises";import{existsSync as Ht}from"fs";async function Me(){let e=await import("ignore");return(e.default??e)()}var De=[".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 Le(e){let t=await Me();t.add(De),t.add(z.ignore||[]);try{let o=U(e,".gitignore");if(Ht(o)){let r=await je(o,"utf-8");t.add(r)}}catch{}return t}async function it(e){let t=await Le(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 c of a){let l=U(t,c),d=Oe(e,l);if(!(o.ignores(d)||o.ignores(d+"/")))try{let m=await s(l);m.isDirectory()?(r.add(d),await Xt(e,l,o,r,n)):m.isFile()&&n.total++}catch{}}}async function Vt(e){let t=U(e,".thinker.md","contexts"),o=[];return Ht(U(e,".thinker.md","CONTEXT.md"))&&o.push(""),await zt(t,"",o),o}async function zt(e,t,o){let{readdir:r,stat:n}=await import("fs/promises"),i=t?U(e,t):e,s;try{s=await r(i)}catch{return}for(let a of s){let c=U(i,a),l=t?U(t,a):a;try{(await n(c)).isDirectory()?await zt(e,l,o):a==="CONTEXT.md"&&o.push(Fe(l))}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 qt(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 Yt(e,t){let r=`# ${t||"Project Root"}
3
3
 
4
4
  <!--
5
5
  This context file should describe:
@@ -24,64 +24,85 @@ This context file should describe:
24
24
  ## Dependencies
25
25
 
26
26
  [Note any important dependencies or relationships]
27
- `;await Wt(e,r)}async function Be(e,t,o=!1){let{existsSync:r}=await import("fs"),n=q(e),i=0,s=0,a=st(_(e),"CONTEXT.md");o&&r(a)?s++:(await Yt(a,""),i++);for(let c of t){let l=st(n,c),d=st(l,"CONTEXT.md"),{mkdir:u}=await import("fs/promises");if(await u(l,{recursive:!0}),o&&r(d)){s++;continue}await Yt(d,c),i++}return{created:i,skipped:s}}async function Qt(e){let t=Ue(),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 T(o),n=_(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 it(r);t.succeed(`Found ${s.directoryCount} directories, ${s.fileCount} files`),await h("init",30,`Found ${s.directoryCount} directories`);let a=await $(r),c=await A(r),l=await X(r)||"unknown";t.start("Creating .thinker.md/ structure...");let{mkdir:d}=await import("fs/promises");await d(n,{recursive:!0}),await d(q(r),{recursive:!0});let u=await S(),g={...z,version:"1.0",projectId:u.metaRepoId||`local-${Date.now()}`,name:l,ignore:z.ignore||[],generatedAt:new Date().toISOString(),generatedBy:"tk-cli"};await nt(r,g),t.succeed("Created config.json"),await h("init",50,"Created config.json");let f={...s,commitSha:c,branch:a};await Wt(st(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(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 ht}from"path";import M from"chalk";import Ke from"ora";async function yt(e){let t=[],o=[],r=0,n=_(e),i=q(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 c=await j(e);c?(c.version||t.push({type:"invalid_config",path:`${y}/${O}`,message:`${O} missing required field: version`}),c.projectId||t.push({type:"invalid_config",path:`${y}/${O}`,message:`${O} missing required field: projectId`})):t.push({type:"invalid_config",path:`${y}/${O}`,message:`${O} not found or invalid`});let l=null;try{let p=ht(n,tt),C=await a(p,"utf-8");l=JSON.parse(C)}catch{t.push({type:"structure_mismatch",path:`${y}/${tt}`,message:`${tt} not found or invalid`})}let d=await it(e),u=ht(n,F);try{await s(u),r++}catch{t.push({type:"missing_context",path:F,message:`Root ${F} not found`})}for(let p of d.directories){let C=ht(i,p,F);try{if((await s(C)).isFile()){r++;let x=await a(C,"utf-8");(x.trim().length<Mt||x.includes(Dt))&&o.push({type:"empty_context",path:`${p}/${F}`,message:"Context file appears to be empty or contains only template"})}}catch{t.push({type:"missing_context",path:`${p}/${F}`,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}/${F}`,message:`Context exists for non-existent directory: ${p}`});return{valid:t.length===0,errors:t,warnings:o,validContextCount:r,directoryCount:d.directoryCount}}async function Zt(e){let t=Ke(),o=process.cwd();try{await N(o)||(console.error(M.red("Error: Not a git repository")),process.exit(1));let r=await T(o);await h("validate",0,"Validating structure..."),t.start(`Validating ${y}/ structure...`);let n=await yt(r);if(t.stop(),await h("validate",100,"Validation complete"),!e.quiet){if(console.log(),n.errors.length>0){console.log(M.red(`\u2717 ${n.errors.length} error(s):`));for(let i of n.errors)console.log(M.red(` \u2022 ${i.path}: ${i.message}`));console.log()}if(n.warnings.length>0){console.log(M.yellow(`\u26A0 ${n.warnings.length} warning(s):`));for(let i of n.warnings)console.log(M.yellow(` \u2022 ${i.path}: ${i.message}`));console.log()}n.valid?console.log(M.green(`\u2713 Validation passed: ${n.validContextCount} contexts valid`)):console.log(M.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(M.red(`Error: ${r}`)),process.exit(1)}}import{join as ie}from"path";import{writeFile as ze}from"fs/promises";import D from"chalk";import qe from"ora";import*as se from"tar";import{S3Client as Ge,PutObjectCommand as xt,GetObjectCommand as te}from"@aws-sdk/client-s3";var wt=null;function Y(){if(!wt){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");wt=new Ge({region:"auto",endpoint:e,credentials:{accessKeyId:t,secretAccessKey:o}})}return wt}function W(){return process.env.R2_BUCKET||_t}async function Q(e,t,o=Ft,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(c=>setTimeout(c,a))}}throw n}function ee(e,t,o){return`${et}/${e}/${ot}/${t}/${o}/${St}`}function Je(e,t,o){return`${et}/${e}/${ot}/${t}/${o}/${$t}`}function oe(e,t){return`${et}/${e}/${ot}/${t}/${It}`}async function He(e,t,o,r){let n=Y(),i=ee(e,t,o);return await Q(()=>n.send(new xt({Bucket:W(),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=Y(),i=Je(e,t,o);await Q(()=>n.send(new xt({Bucket:W(),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=Y(),n=oe(e,t);await Q(()=>r.send(new xt({Bucket:W(),Key:n,Body:o,ContentType:"text/plain"})),"updateLatestPointer"),console.log(`[Storage] Updated latest pointer for ${t} -> ${o}`)}async function at(e,t){let o=Y(),r=oe(e,t);try{return(await(await Q(()=>o.send(new te({Bucket:W(),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 re(e,t,o){let r=Y(),n=ee(e,t,o);try{let s=await(await Q(()=>r.send(new te({Bucket:W(),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 ne(e,t,o,r,n){let i=await He(e,t,o,r);return await Xe(e,t,o,n),await Ve(e,t,o),i}async function Ye(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 c=ie(i,a);try{(await o(c)).isDirectory()?await n(c):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function ae(e){let t=qe(),o=process.cwd();try{await N(o)||(console.error(D.red("Error: Not a git repository")),process.exit(1));let r=await T(o),n=await S(),i;if(n.mode==="sandbox"&&n.metaRepoId)i=n.metaRepoId;else{let p=await j(r);p?.projectId?i=p.projectId:(console.error(D.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 $(r);await h("pull",0,`Fetching latest context for branch '${s}'...`),console.log(`Fetching latest context for branch '${D.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 at(i,s)}catch(p){throw p instanceof Error&&p.message.includes("R2 credentials")&&(t.fail("R2 credentials not configured"),console.error(D.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 c=await re(i,s,a);c||(t.fail("Archive not found"),console.error(D.red(`No archive found for commit ${a.slice(0,7)}`)),process.exit(1)),t.succeed(`Downloaded archive: ${c.length} bytes`),await h("pull",60,"Extracting archive...");let l=ie(r,".thinker.md.tar.gz");await ze(l,c),t.start("Extracting to .thinker.md/...");let d=_(r),{rm:u}=await import("fs/promises");try{await u(d,{recursive:!0,force:!0})}catch{}await se.extract({file:l,cwd:r}),await u(l,{force:!0}),t.succeed("Extracted .thinker.md/"),await h("pull",80,"Updating config...");let g=await j(r);g&&(g.pulledFrom=a,g.pulledAt=new Date().toISOString(),await nt(r,g));let f=await Ye(d);await h("pull",100,`Pulled ${f} context files`),console.log(),console.log(D.green(`\u2713 Pulled ${f} context files`)),console.log(),console.log(`From: ${D.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 "+D.cyan("tk validate")+" to check structure"),console.log(" 3. Run "+D.cyan("tk push")+" to upload changes")}catch(r){t.fail("Pull failed"),console.error(D.red(`Error: ${r}`)),process.exit(1)}}import{join as ce}from"path";import R from"chalk";import We from"ora";import*as le from"tar";async function Qe(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 c of a){let l=ce(s,c);try{(await o(l)).isDirectory()?await i(l):(n++,c===F&&r++)}catch{}}}return await i(e),{contextFiles:r,totalFiles:n}}async function de(e){let t=We(),o=process.cwd();try{if(gt()==="sandbox")try{Gt()}catch(E){console.error(R.red(`Sandbox environment error: ${E}`)),process.exit(1)}await N(o)||(console.error(R.red("Error: Not a git repository")),process.exit(1));let r=await T(o),n=await S(),i=_(r),{stat:s,rm:a}=await import("fs/promises");try{await s(i)}catch{console.error(R.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 yt(r);if(!E.valid){t.fail("Validation failed"),console.log(),console.log(R.red("Fix the following errors before pushing:"));for(let Z of E.errors)console.log(R.red(` \u2022 ${Z.path}: ${Z.message}`));process.exit(1)}t.succeed("Validation passed"),E.warnings.length>0&&console.log(R.yellow(` ${E.warnings.length} warning(s)`))}await h("push",20,"Validation passed");let c;if(n.mode==="sandbox"&&n.metaRepoId)c=n.metaRepoId;else{let E=await j(r);E?.projectId?c=E.projectId:(console.error(R.red("Error: No project ID found. Set META_REPO_ID environment variable.")),process.exit(1))}let l=n.branch||await $(r),d=n.commitSha||await A(r),u=await Jt(r,d);await h("push",30,"Packaging .thinker.md/..."),t.start(`Packaging ${y}/...`);let g=ce(r,`${y}.tar.gz`);await le.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 Qe(i),K=await j(r),x=e.forceFull?ut:K?.pulledFrom?At:ut,Pt={branch:l,commitSha:d,commitMessage:u,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 ne(c,l,d,p,Pt);if(t.succeed("Uploaded to storage"),await h("push",90,"Finalizing..."),n.mode==="sandbox"&&n.callbackUrl){let Z={filesAnalyzed:C.totalFiles,contextFilesGenerated:C.contextFiles,tokensUsed:0,mode:x};await qt(E,Z)}await h("push",100,"Done!"),console.log(),console.log(R.green("\u2713 Successfully pushed to storage")),console.log(),console.log(`Branch: ${R.cyan(l)}`),console.log(`Commit: ${R.cyan(d.slice(0,7))}`),console.log(`Mode: ${R.cyan(x)}`),console.log(`Context files: ${R.cyan(C.contextFiles)}`),n.mode==="sandbox"&&(console.log(),console.log(R.green("\u2713 Job completed")))}catch(E){t.fail("Upload failed"),E instanceof Error&&E.message.includes("R2 credentials")?(console.error(R.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(R.red(`Error: ${E}`)),process.exit(1)}}catch(r){t.fail("Push failed"),console.error(R.red(`Error: ${r}`)),process.exit(1)}}import b from"chalk";import pe from"ora";import{createServer as me}from"http";import{randomBytes as Ze}from"crypto";import{exec as to}from"child_process";import{promisify as eo}from"util";var Ct=eo(to);async function oo(e){let t=process.platform;try{t==="darwin"?await Ct(`open "${e}"`):t==="win32"?await Ct(`start "" "${e}"`):await Ct(`xdg-open "${e}"`)}catch{}}async function ue(e=9876){return new Promise(t=>{let o=me();o.listen(e,()=>{let r=o.address(),n=typeof r=="object"&&r?r.port:e;o.close(()=>t(n))}),o.on("error",()=>{t(ue(e+1))})})}function ro(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=me((c,l)=>{let d=new URL(c.url||"",`http://localhost:${e}`);if(d.pathname==="/callback"){let u=d.searchParams.get("token"),g=d.searchParams.get("user_id"),f=d.searchParams.get("email");u&&g&&f?(l.writeHead(200,{"Content-Type":"text/html"}),l.end(`
29
- <!DOCTYPE html>
30
- <html>
31
- <head>
32
- <title>Authentication Successful</title>
33
- <style>
34
- body {
35
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
36
- display: flex;
37
- flex-direction: column;
38
- align-items: center;
39
- justify-content: center;
40
- height: 100vh;
41
- margin: 0;
42
- background: linear-gradient(to bottom, #ffffff, #f5f5f5);
43
- color: #333;
44
- }
45
- .container {
46
- text-align: center;
47
- padding: 40px;
48
- background: white;
49
- border-radius: 12px;
50
- box-shadow: 0 4px 20px rgba(0,0,0,0.1);
51
- max-width: 400px;
52
- }
53
- .icon {
54
- font-size: 48px;
55
- margin-bottom: 20px;
56
- }
57
- h1 {
58
- margin: 0 0 10px 0;
59
- font-size: 24px;
60
- }
61
- p {
62
- color: #666;
63
- margin: 0;
64
- }
65
- .close-note {
66
- margin-top: 20px;
67
- font-size: 14px;
68
- color: #999;
69
- }
70
- </style>
71
- </head>
72
- <body>
73
- <div class="container">
74
- <div class="icon">\u2713</div>
75
- <h1>Authentication Successful</h1>
76
- <p>You can now close this window and return to your terminal.</p>
77
- <p class="close-note">This window will close automatically...</p>
78
- </div>
79
- <script>setTimeout(() => window.close(), 2000);</script>
80
- </body>
81
- </html>
82
- `),a(),r({token:u,userId:g,email:f})):(l.writeHead(400,{"Content-Type":"text/plain"}),l.end("Missing required parameters"))}else l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not found")}),i.listen(e)})}async function no(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 ge(e){let t=pe(),o=e.apiUrl||J;if(e.token){t.start("Validating token...");let n=await no(o,e.token);n.valid||(t.fail("Authentication failed"),console.error(b.red(`Error: ${n.error}`)),process.exit(1));let i={apiToken:e.token,apiUrl:o,userId:n.userId};await rt(i),t.succeed("Authenticated successfully"),console.log(),console.log(`Config saved to: ${b.cyan(L)}`);return}let r=await I();if(r.apiToken){console.log(b.green("You are already logged in.")),console.log(`User ID: ${b.cyan(r.userId||"unknown")}`),console.log(),console.log("To re-authenticate, run:"),console.log(b.cyan(" tk login --token NEW_TOKEN")),console.log(),console.log("Or to login with a different account:"),console.log(b.cyan(" tk logout && tk login"));return}console.log(b.bold("ThinkerMD Login")),console.log(),t.start("Setting up authentication...");try{let n=await ue(),i=Ze(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(b.cyan(s)),console.log(),await oo(s),t.start("Waiting for authentication...");let{token:a,userId:c,email:l}=await ro(n,i);await rt({apiToken:a,apiUrl:o,userId:c}),t.succeed("Authenticated successfully"),console.log(),console.log(`Logged in as: ${b.cyan(l)}`),console.log(`Config saved to: ${b.cyan(L)}`)}catch(n){t.fail("Authentication failed"),console.error(b.red(`Error: ${n instanceof Error?n.message:n}`)),console.log(),console.log("You can also authenticate manually:"),console.log(b.cyan(" tk login --token YOUR_TOKEN")),console.log(),console.log(`Get your token at: ${b.cyan(`${o}/dashboard/settings`)}`),process.exit(1)}}async function fe(){let e=pe();e.start("Logging out..."),await rt({}),e.succeed("Logged out successfully"),console.log(),console.log(`Config cleared from: ${b.cyan(L)}`)}import{join as he}from"path";import m from"chalk";import io from"ora";async function so(e){let{readFile:t}=await import("fs/promises"),{existsSync:o}=await import("fs"),r=he(_(e),"structure.json");try{if(o(r)){let n=await t(r,"utf-8");return JSON.parse(n)}}catch{}return null}async function ao(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 c=he(i,a);try{(await o(c)).isDirectory()?await n(c):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function ye(){let e=io(),t=process.cwd();try{await N(t)||(console.error(m.red("Error: Not a git repository")),process.exit(1));let o=await T(t),r=await S(),n=_(o);console.log(m.bold("ThinkerMD Status")),console.log();let i=await X(o),s=r.branch||await $(o),a=r.commitSha||await A(o);console.log(m.dim("Repository:")),console.log(` Name: ${m.cyan(i||"unknown")}`),console.log(` Branch: ${m.cyan(s)}`),console.log(` Commit: ${m.cyan(a.slice(0,7))}`),console.log();let{stat:c}=await import("fs/promises"),l=!1;try{await c(n),l=!0}catch{}if(!l){console.log(m.dim("Local Context:")),console.log(` Status: ${m.yellow("Not initialized")}`),console.log(),console.log("Run "+m.cyan("tk init")+" to create .thinker.md/");return}console.log(m.dim("Local Context:"));let d=await j(o),u=await so(o),g=await ao(n);if(console.log(` Status: ${m.green("Initialized")}`),console.log(` Project ID: ${m.cyan(d?.projectId||"unknown")}`),console.log(` Context files: ${m.cyan(g)}`),u&&(console.log(` Directories: ${m.cyan(u.directoryCount)}`),console.log(` Files tracked: ${m.cyan(u.fileCount)}`)),d?.generatedAt){let p=new Date(d.generatedAt);console.log(` Generated: ${m.cyan(p.toLocaleString())}`)}d?.pulledFrom&&console.log(` Pulled from: ${m.cyan(d.pulledFrom.slice(0,7))}`),console.log();let f=r.metaRepoId||d?.projectId;if(f&&r.r2Endpoint){console.log(m.dim("Remote Storage:")),e.start("Checking remote...");try{let p=await at(f,s);p?(e.stop(),console.log(` Latest commit: ${m.cyan(p.slice(0,7))}`),p===a?console.log(` Status: ${m.green("Up to date")}`):d?.pulledFrom===p?console.log(` Status: ${m.yellow("Local changes not pushed")}`):(console.log(` Status: ${m.yellow("Remote has newer version")}`),console.log(),console.log("Run "+m.cyan("tk pull")+" to download latest context"))):(e.stop(),console.log(` Status: ${m.yellow("No remote context found")}`),console.log(),console.log("Run "+m.cyan("tk push")+" to upload context"))}catch{e.stop(),console.log(` Status: ${m.dim("Unable to check (no credentials)")}`)}}console.log(),console.log(m.dim("Mode:")),console.log(` ${r.mode==="sandbox"?m.cyan("Sandbox"):m.cyan("Local")}`),r.mode==="sandbox"&&console.log(` Job ID: ${m.cyan(r.jobId||"unknown")}`)}catch(o){e.fail("Failed to get status"),console.error(m.red(`Error: ${o}`)),process.exit(1)}}import w from"chalk";import co from"ora";async function we(e){let t=co(),o=process.cwd();try{await N(o)||(console.error(w.red("Error: Not a git repository")),process.exit(1));let r=await T(o);if((await S()).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();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),c=e.branch||await $(r),l=await A(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(c)}`),console.log(`Commit: ${w.cyan(l.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 u=await d.json();t.succeed(`Found meta-repository: ${u.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:u.id,commitSha:l,branch:c,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/${u.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 Eo}from"hono";import P from"chalk";import{createServer as Ro}from"http";import{homedir as Re}from"os";import{join as Te}from"path";import{readFile as mo,writeFile as uo,mkdir as go}from"fs/promises";import{existsSync as fo}from"fs";async function lo(){return(await I()).apiUrl||process.env.THINKERMD_API_URL||J}async function po(){return(await I()).apiToken||process.env.THINKERMD_API_TOKEN||null}async function ct(e,t={}){let o=await lo(),r=await po();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 l=new URLSearchParams(s);a+=`?${l.toString()}`}let c=await fetch(a,{method:n,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:i?JSON.stringify(i):void 0});if(!c.ok){let l=await c.json().catch(()=>({}));throw new Error(l.error||`API request failed: ${c.status} ${c.statusText}`)}return c.json()}async function xe(e,t,o={}){return ct("/api/mcp/search",{method:"POST",body:{repositoryId:e,query:t,branch:o.branch,limit:o.limit||10}})}async function Et(e,t,o={}){return ct("/api/mcp/read",{params:{repositoryId:e,filePath:t,...o.branch&&{branch:o.branch}}})}async function Ce(e,t={}){return ct("/api/mcp/files",{params:{repositoryId:e,...t.branch&&{branch:t.branch}}})}async function Ee(e){try{let t=await ct("/api/user/repositories",{params:{remoteUrl:e}});return t.repositories&&t.repositories.length>0?t.repositories[0]:null}catch{return null}}var Rt=Te(Re(),".thinkermd","cache","repo-mapping.json"),ho=24*60*60*1e3;async function yo(){try{if(fo(Rt)){let e=await mo(Rt,"utf-8");return JSON.parse(e)}}catch{}return{mappings:{}}}async function wo(e){let t=Te(Re(),".thinkermd","cache");await go(t,{recursive:!0}),await uo(Rt,JSON.stringify(e,null,2))}async function V(e){let t;try{t=await T(e)}catch{return console.error(`[RepoResolver] Not a git repository: ${e}`),null}let o=await ft(t);if(!o)return console.error(`[RepoResolver] No git remote found for: ${t}`),null;let r=xo(o),n=await yo(),i=n.mappings[r];if(i&&Date.now()-i.cachedAt<ho){console.log(`[RepoResolver] Cache hit for: ${r}`);let l=await $(t),d=await A(t);return{repositoryId:i.repositoryId,fullName:i.fullName,remoteUrl:r,gitRoot:t,branch:l||"main",commit:d||"HEAD"}}console.log(`[RepoResolver] Querying API for: ${r}`);let s=await Ee(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 wo(n);let a=await $(t),c=await A(t);return{repositoryId:s.id,fullName:s.fullName,remoteUrl:r,gitRoot:t,branch:a||"main",commit:c||"HEAD"}}function xo(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 lt(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 xe(n.repositoryId,o,{branch:n.branch,limit:r}),repositoryId:n.repositoryId,fullName:n.fullName}}async function dt(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 Et(r.repositoryId,o,{branch:r.branch}),repositoryId:r.repositoryId,fullName:r.fullName}}async function pt(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 Et(o.repositoryId,n,{branch:o.branch}),repositoryId:o.repositoryId,fullName:o.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}`);return{...await Ce(o.repositoryId,{branch:o.branch}),repositoryId:o.repositoryId,fullName:o.fullName}}async function ke(e){return V(e.path)}import{McpServer as Co}from"@modelcontextprotocol/sdk/server/mcp.js";import{z as B}from"zod";function be(){let e=new Co({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:B.string().describe("Search query - what you're looking for"),path:B.string().optional().describe("Path to directory (defaults to current working directory)"),limit:B.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 lt({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:B.string().describe("Path to the context file relative to repository root"),path:B.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 dt({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:B.string().describe("Path to the directory to get context for")},async({path:t})=>{try{return{content:[{type:"text",text:(await pt({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:B.string().optional().describe("Path to repository directory (defaults to current working directory)")},async({path:t})=>{try{let o=t||process.cwd(),r=await mt({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{SSEServerTransport as To}from"@modelcontextprotocol/sdk/server/sse.js";var Tt=new Map,ko=be();async function bo(e,t){console.log("[MCP] New SSE connection");let o=new To("/mcp/messages",t),r=crypto.randomUUID();Tt.set(r,o),t.setHeader("X-MCP-Session-Id",r),await ko.connect(o),t.on("close",()=>{Tt.delete(r),console.log("[MCP] SSE connection closed")})}async function Po(e,t){let o=e.headers["x-mcp-session-id"];if(!o){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Missing X-MCP-Session-Id header"}));return}let r=Tt.get(o);if(!r){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid session"}));return}try{await r.handlePostMessage(e,t)}catch(n){console.error("[MCP] Message handling error:",n),t.headersSent||(t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Failed to process message"})))}}function vo(){let e=new Eo;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 lt({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 dt({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 pt({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 mt({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 So(e,t){let o=`http://localhost:${e}/mcp`;console.log(P.cyan(`
83
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
84
- \u2551 \u2551
27
+ `;await Wt(e,r)}async function Be(e,t,o=!1){let{existsSync:r}=await import("fs"),n=q(e),i=0,s=0,a=st(I(e),"CONTEXT.md");o&&r(a)?s++:(await Yt(a,""),i++);for(let c of t){let l=st(n,c),d=st(l,"CONTEXT.md"),{mkdir:m}=await import("fs/promises");if(await m(l,{recursive:!0}),o&&r(d)){s++;continue}await Yt(d,c),i++}return{created:i,skipped:s}}async function Qt(e){let t=Ue(),o=process.cwd();try{await _(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 R(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 it(r);t.succeed(`Found ${s.directoryCount} directories, ${s.fileCount} files`),await h("init",30,`Found ${s.directoryCount} directories`);let a=await S(r),c=await N(r),l=await X(r)||"unknown";t.start("Creating .thinker.md/ structure...");let{mkdir:d}=await import("fs/promises");await d(n,{recursive:!0}),await d(q(r),{recursive:!0});let m=await P(),g={...z,version:"1.0",projectId:m.metaRepoId||`local-${Date.now()}`,name:l,ignore:z.ignore||[],generatedAt:new Date().toISOString(),generatedBy:"tk-cli"};await nt(r,g),t.succeed("Created config.json"),await h("init",50,"Created config.json");let f={...s,commitSha:c,branch:a};await Wt(st(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 ht}from"path";import D from"chalk";import Ke from"ora";async function yt(e){let t=[],o=[],r=0,n=I(e),i=q(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 c=await F(e);c?(c.version||t.push({type:"invalid_config",path:`${y}/${A}`,message:`${A} missing required field: version`}),c.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 l=null;try{let p=ht(n,tt),C=await a(p,"utf-8");l=JSON.parse(C)}catch{t.push({type:"structure_mismatch",path:`${y}/${tt}`,message:`${tt} not found or invalid`})}let d=await it(e),m=ht(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=ht(i,p,O);try{if((await s(C)).isFile()){r++;let x=await a(C,"utf-8");(x.trim().length<Dt||x.includes(Mt))&&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 Zt(e){let t=Ke(),o=process.cwd();try{await _(o)||(console.error(D.red("Error: Not a git repository")),process.exit(1));let r=await R(o);await h("validate",0,"Validating structure..."),t.start(`Validating ${y}/ structure...`);let n=await yt(r);if(t.stop(),await h("validate",100,"Validation complete"),!e.quiet){if(console.log(),n.errors.length>0){console.log(D.red(`\u2717 ${n.errors.length} error(s):`));for(let i of n.errors)console.log(D.red(` \u2022 ${i.path}: ${i.message}`));console.log()}if(n.warnings.length>0){console.log(D.yellow(`\u26A0 ${n.warnings.length} warning(s):`));for(let i of n.warnings)console.log(D.yellow(` \u2022 ${i.path}: ${i.message}`));console.log()}n.valid?console.log(D.green(`\u2713 Validation passed: ${n.validContextCount} contexts valid`)):console.log(D.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(D.red(`Error: ${r}`)),process.exit(1)}}import{join as ie}from"path";import{writeFile as ze}from"fs/promises";import j from"chalk";import qe from"ora";import*as se from"tar";import{S3Client as Ge,PutObjectCommand as xt,GetObjectCommand as te}from"@aws-sdk/client-s3";var wt=null;function Y(){if(!wt){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");wt=new Ge({region:"auto",endpoint:e,credentials:{accessKeyId:t,secretAccessKey:o}})}return wt}function W(){return process.env.R2_BUCKET||_t}async function Q(e,t,o=Ft,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(c=>setTimeout(c,a))}}throw n}function ee(e,t,o){return`${et}/${e}/${ot}/${t}/${o}/${St}`}function Je(e,t,o){return`${et}/${e}/${ot}/${t}/${o}/${$t}`}function oe(e,t){return`${et}/${e}/${ot}/${t}/${It}`}async function He(e,t,o,r){let n=Y(),i=ee(e,t,o);return await Q(()=>n.send(new xt({Bucket:W(),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=Y(),i=Je(e,t,o);await Q(()=>n.send(new xt({Bucket:W(),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=Y(),n=oe(e,t);await Q(()=>r.send(new xt({Bucket:W(),Key:n,Body:o,ContentType:"text/plain"})),"updateLatestPointer"),console.log(`[Storage] Updated latest pointer for ${t} -> ${o}`)}async function at(e,t){let o=Y(),r=oe(e,t);try{return(await(await Q(()=>o.send(new te({Bucket:W(),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 re(e,t,o){let r=Y(),n=ee(e,t,o);try{let s=await(await Q(()=>r.send(new te({Bucket:W(),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 ne(e,t,o,r,n){let i=await He(e,t,o,r);return await Xe(e,t,o,n),await Ve(e,t,o),i}async function Ye(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 c=ie(i,a);try{(await o(c)).isDirectory()?await n(c):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function ae(e){let t=qe(),o=process.cwd();try{await _(o)||(console.error(j.red("Error: Not a git repository")),process.exit(1));let r=await R(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 at(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 c=await re(i,s,a);c||(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: ${c.length} bytes`),await h("pull",60,"Extracting archive...");let l=ie(r,".thinker.md.tar.gz");await ze(l,c),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 se.extract({file:l,cwd:r}),await m(l,{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 nt(r,g));let f=await Ye(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 ce}from"path";import T from"chalk";import We from"ora";import*as le from"tar";async function Qe(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 c of a){let l=ce(s,c);try{(await o(l)).isDirectory()?await i(l):(n++,c===O&&r++)}catch{}}}return await i(e),{contextFiles:r,totalFiles:n}}async function de(e){let t=We(),o=process.cwd();try{if(gt()==="sandbox")try{Gt()}catch(E){console.error(T.red(`Sandbox environment error: ${E}`)),process.exit(1)}await _(o)||(console.error(T.red("Error: Not a git repository")),process.exit(1));let r=await R(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 yt(r);if(!E.valid){t.fail("Validation failed"),console.log(),console.log(T.red("Fix the following errors before pushing:"));for(let Z of E.errors)console.log(T.red(` \u2022 ${Z.path}: ${Z.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 c;if(n.mode==="sandbox"&&n.metaRepoId)c=n.metaRepoId;else{let E=await F(r);E?.projectId?c=E.projectId:(console.error(T.red("Error: No project ID found. Set META_REPO_ID environment variable.")),process.exit(1))}let l=n.branch||await S(r),d=n.commitSha||await N(r),m=await Jt(r,d);await h("push",30,"Packaging .thinker.md/..."),t.start(`Packaging ${y}/...`);let g=ce(r,`${y}.tar.gz`);await le.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 Qe(i),K=await F(r),x=e.forceFull?ut:K?.pulledFrom?At:ut,vt={branch:l,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 ne(c,l,d,p,vt);if(t.succeed("Uploaded to storage"),await h("push",90,"Finalizing..."),n.mode==="sandbox"&&n.callbackUrl){let Z={filesAnalyzed:C.totalFiles,contextFilesGenerated:C.contextFiles,tokensUsed:0,mode:x};await qt(E,Z)}await h("push",100,"Done!"),console.log(),console.log(T.green("\u2713 Successfully pushed to storage")),console.log(),console.log(`Branch: ${T.cyan(l)}`),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 k from"chalk";import pe from"ora";import{createServer as me}from"http";import{randomBytes as Ze}from"crypto";import{exec as to}from"child_process";import{promisify as eo}from"util";var Ct=eo(to);async function oo(e){let t=process.platform;try{t==="darwin"?await Ct(`open "${e}"`):t==="win32"?await Ct(`start "" "${e}"`):await Ct(`xdg-open "${e}"`)}catch{}}async function ue(e=9876){return new Promise(t=>{let o=me();o.listen(e,()=>{let r=o.address(),n=typeof r=="object"&&r?r.port:e;o.close(()=>t(n))}),o.on("error",()=>{t(ue(e+1))})})}function ro(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=me((c,l)=>{let d=new URL(c.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?(l.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),l.end(`<!DOCTYPE html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="UTF-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
33
+ <title>Authentication Successful - THINKER.md</title>
34
+ <style>
35
+ * { box-sizing: border-box; margin: 0; padding: 0; }
36
+ body {
37
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
38
+ display: flex;
39
+ flex-direction: column;
40
+ align-items: center;
41
+ justify-content: center;
42
+ min-height: 100vh;
43
+ background: linear-gradient(180deg, #fafafa 0%, #f0f0f0 100%);
44
+ color: #171717;
45
+ }
46
+ .container {
47
+ text-align: center;
48
+ padding: 48px 40px;
49
+ background: white;
50
+ border-radius: 16px;
51
+ box-shadow: 0 4px 24px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04);
52
+ max-width: 420px;
53
+ width: 90%;
54
+ }
55
+ .icon {
56
+ width: 64px;
57
+ height: 64px;
58
+ margin: 0 auto 24px;
59
+ background: #dcfce7;
60
+ border-radius: 50%;
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ }
65
+ .icon svg {
66
+ width: 32px;
67
+ height: 32px;
68
+ color: #16a34a;
69
+ }
70
+ .logo {
71
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
72
+ font-size: 14px;
73
+ font-weight: 600;
74
+ color: #737373;
75
+ margin-bottom: 8px;
76
+ letter-spacing: -0.5px;
77
+ }
78
+ h1 {
79
+ font-size: 24px;
80
+ font-weight: 600;
81
+ margin-bottom: 8px;
82
+ color: #171717;
83
+ }
84
+ p {
85
+ color: #525252;
86
+ font-size: 15px;
87
+ line-height: 1.5;
88
+ }
89
+ </style>
90
+ </head>
91
+ <body>
92
+ <div class="container">
93
+ <div class="logo">THINKER.md</div>
94
+ <div class="icon">
95
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor">
96
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
97
+ </svg>
98
+ </div>
99
+ <h1>Authentication Successful</h1>
100
+ <p>You can now close this window and return to your terminal.</p>
101
+ </div>
102
+ </body>
103
+ </html>`),a(),r({token:m,userId:g,email:f})):(l.writeHead(400,{"Content-Type":"text/plain"}),l.end("Missing required parameters"))}else l.writeHead(404,{"Content-Type":"text/plain"}),l.end("Not found")}),i.listen(e)})}async function no(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 ge(e){let t=pe(),o=e.apiUrl||J;if(e.token){t.start("Validating token...");let n=await no(o,e.token);n.valid||(t.fail("Authentication failed"),console.error(k.red(`Error: ${n.error}`)),process.exit(1));let i={apiToken:e.token,apiUrl:o,userId:n.userId};await rt(i),t.succeed("Authenticated successfully"),console.log(),console.log(`Config saved to: ${k.cyan(L)}`);return}let r=await $();if(r.apiToken){console.log(k.green("You are already logged in.")),console.log(`User ID: ${k.cyan(r.userId||"unknown")}`),console.log(),console.log("To re-authenticate, run:"),console.log(k.cyan(" tk login --token NEW_TOKEN")),console.log(),console.log("Or to login with a different account:"),console.log(k.cyan(" tk logout && tk login"));return}console.log(k.bold("ThinkerMD Login")),console.log(),t.start("Setting up authentication...");try{let n=await ue(),i=Ze(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(k.cyan(s)),console.log(),await oo(s),t.start("Waiting for authentication...");let{token:a,userId:c,email:l}=await ro(n,i);await rt({apiToken:a,apiUrl:o,userId:c,email:l}),t.succeed("Authenticated successfully"),console.log(),console.log(`Logged in as: ${k.cyan(l)}`),console.log(`Config saved to: ${k.cyan(L)}`)}catch(n){t.fail("Authentication failed"),console.error(k.red(`Error: ${n instanceof Error?n.message:n}`)),console.log(),console.log("You can also authenticate manually:"),console.log(k.cyan(" tk login --token YOUR_TOKEN")),console.log(),console.log(`Get your token at: ${k.cyan(`${o}/dashboard/settings`)}`),process.exit(1)}}async function fe(){let e=pe();e.start("Logging out..."),await rt({}),e.succeed("Logged out successfully"),console.log(),console.log(`Config cleared from: ${k.cyan(L)}`)}import{join as he}from"path";import u from"chalk";import io from"ora";async function so(e){let{readFile:t}=await import("fs/promises"),{existsSync:o}=await import("fs"),r=he(I(e),"structure.json");try{if(o(r)){let n=await t(r,"utf-8");return JSON.parse(n)}}catch{}return null}async function ao(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 c=he(i,a);try{(await o(c)).isDirectory()?await n(c):a==="CONTEXT.md"&&r++}catch{}}}return await n(e),r}async function ye(){let e=io(),t=process.cwd();try{await _(t)||(console.error(u.red("Error: Not a git repository")),process.exit(1));let o=await R(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 N(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:c}=await import("fs/promises"),l=!1;try{await c(n),l=!0}catch{}if(!l){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 so(o),g=await ao(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 at(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 co from"ora";async function we(e){let t=co(),o=process.cwd();try{await _(o)||(console.error(w.red("Error: Not a git repository")),process.exit(1));let r=await R(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),c=e.branch||await S(r),l=await N(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(c)}`),console.log(`Commit: ${w.cyan(l.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:l,branch:c,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 bt from"chalk";import{Hono as Eo}from"hono";import M from"chalk";import{createServer as To}from"http";import{homedir as Te}from"os";import{join as Re}from"path";import{readFile as mo,writeFile as uo,mkdir as go}from"fs/promises";import{existsSync as fo}from"fs";async function lo(){return(await $()).apiUrl||process.env.THINKERMD_API_URL||J}async function po(){return(await $()).apiToken||process.env.THINKERMD_API_TOKEN||null}async function ct(e,t={}){let o=await lo(),r=await po();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 l=new URLSearchParams(s);a+=`?${l.toString()}`}let c=await fetch(a,{method:n,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json"},body:i?JSON.stringify(i):void 0});if(!c.ok){let l=await c.json().catch(()=>({}));throw new Error(l.error||`API request failed: ${c.status} ${c.statusText}`)}return c.json()}async function xe(e,t,o={}){return ct("/api/mcp/search",{method:"POST",body:{repositoryId:e,query:t,branch:o.branch,limit:o.limit||10}})}async function Et(e,t,o={}){return ct("/api/mcp/read",{params:{repositoryId:e,filePath:t,...o.branch&&{branch:o.branch}}})}async function Ce(e,t={}){return ct("/api/mcp/files",{params:{repositoryId:e,...t.branch&&{branch:t.branch}}})}async function Ee(e){try{let t=await ct("/api/user/repositories",{params:{remoteUrl:e}});return t.repositories&&t.repositories.length>0?t.repositories[0]:null}catch{return null}}var Tt=Re(Te(),".thinkermd","cache","repo-mapping.json"),ho=24*60*60*1e3;async function yo(){try{if(fo(Tt)){let e=await mo(Tt,"utf-8");return JSON.parse(e)}}catch{}return{mappings:{}}}async function wo(e){let t=Re(Te(),".thinkermd","cache");await go(t,{recursive:!0}),await uo(Tt,JSON.stringify(e,null,2))}async function V(e){let t;try{t=await R(e)}catch{return console.error(`[RepoResolver] Not a git repository: ${e}`),null}let o=await ft(t);if(!o)return console.error(`[RepoResolver] No git remote found for: ${t}`),null;let r=xo(o),n=await yo(),i=n.mappings[r];if(i&&Date.now()-i.cachedAt<ho){console.log(`[RepoResolver] Cache hit for: ${r}`);let l=await S(t),d=await N(t);return{repositoryId:i.repositoryId,fullName:i.fullName,remoteUrl:r,gitRoot:t,branch:l||"main",commit:d||"HEAD"}}console.log(`[RepoResolver] Querying API for: ${r}`);let s=await Ee(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 wo(n);let a=await S(t),c=await N(t);return{repositoryId:s.id,fullName:s.fullName,remoteUrl:r,gitRoot:t,branch:a||"main",commit:c||"HEAD"}}function xo(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 lt(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 xe(n.repositoryId,o,{branch:n.branch,limit:r}),repositoryId:n.repositoryId,fullName:n.fullName}}async function dt(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 Et(r.repositoryId,o,{branch:r.branch}),repositoryId:r.repositoryId,fullName:r.fullName}}async function pt(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 Et(o.repositoryId,n,{branch:o.branch}),repositoryId:o.repositoryId,fullName:o.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}`);return{...await Ce(o.repositoryId,{branch:o.branch}),repositoryId:o.repositoryId,fullName:o.fullName}}async function be(e){return V(e.path)}import{McpServer as Co}from"@modelcontextprotocol/sdk/server/mcp.js";import{z as B}from"zod";function ke(){let e=new Co({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:B.string().describe("Search query - what you're looking for"),path:B.string().optional().describe("Path to directory (defaults to current working directory)"),limit:B.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 lt({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:B.string().describe("Path to the context file relative to repository root"),path:B.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 dt({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:B.string().describe("Path to the directory to get context for")},async({path:t})=>{try{return{content:[{type:"text",text:(await pt({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:B.string().optional().describe("Path to repository directory (defaults to current working directory)")},async({path:t})=>{try{let o=t||process.cwd(),r=await mt({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{SSEServerTransport as Ro}from"@modelcontextprotocol/sdk/server/sse.js";var Rt=new Map,bo=ke();async function ko(e,t){console.log("[MCP] New SSE connection");let o=new Ro("/mcp/messages",t),r=crypto.randomUUID();Rt.set(r,o),t.setHeader("X-MCP-Session-Id",r),await bo.connect(o),t.on("close",()=>{Rt.delete(r),console.log("[MCP] SSE connection closed")})}async function vo(e,t){let o=e.headers["x-mcp-session-id"];if(!o){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Missing X-MCP-Session-Id header"}));return}let r=Rt.get(o);if(!r){t.writeHead(400,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Invalid session"}));return}try{await r.handlePostMessage(e,t)}catch(n){console.error("[MCP] Message handling error:",n),t.headersSent||(t.writeHead(500,{"Content-Type":"application/json"}),t.end(JSON.stringify({error:"Failed to process message"})))}}function Po(){let e=new Eo;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 lt({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 dt({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 pt({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 mt({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 So(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(`
104
+ \u2554${"\u2550".repeat(r)}\u2557
105
+ \u2551${" ".repeat(r)}\u2551
85
106
  \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
86
107
  \u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2551
87
108
  \u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2551
@@ -89,24 +110,19 @@ This appears to be the first generation. Use 'tk init' to create initial structu
89
110
  \u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2551
90
111
  \u2551 \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u2551
91
112
  \u2551 .md \u2551
92
- \u2551 \u2551
93
- \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
94
- \u2551 \u2551
95
- \u2551 ${P.green("\u2713")} Logged in as: ${P.white(t.padEnd(42))}\u2551
96
- \u2551 ${P.green("\u2713")} Daemon running on: ${P.white(o.padEnd(38))}\u2551
97
- \u2551 \u2551
98
- \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
113
+ \u2551${" ".repeat(r)}\u2551
114
+ \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 c=` \u2713 Daemon running on: ${o}`,l=c+" ".repeat(Math.max(0,r-c.length));console.log(M.cyan("\u2551")+l+M.cyan("\u2551")),console.log(M.cyan(`\u2551${" ".repeat(r)}\u2551
116
+ \u2560${"\u2550".repeat(r)}\u2563
99
117
  \u2551 Add to your AI tool config: \u2551
100
- \u2551 \u2551
101
- \u2551 ${P.dim("{")} \u2551
102
- \u2551 ${P.dim('"mcpServers"')}: { \u2551
103
- \u2551 ${P.dim('"thinkermd"')}: { \u2551
104
- \u2551 ${P.dim('"type"')}: ${P.green('"http"')}, \u2551
105
- \u2551 ${P.dim('"url"')}: ${P.green(`"${o}"`).padEnd(40)}\u2551
106
- \u2551 } \u2551
118
+ \u2551${" ".repeat(r)}\u2551
119
+ \u2551 { \u2551
120
+ \u2551 "mcpServers": { \u2551
121
+ \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
107
123
  \u2551 } \u2551
108
- \u2551 ${P.dim("}")} \u2551
109
- \u2551 \u2551
110
- \u2551 ${P.yellow("Press Ctrl+C to stop")} \u2551
111
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
112
- `))}async function Pe(e){let{port:t}=e,o=vo(),n=(await I()).userId||"authenticated user";Ro(async(s,a)=>{let c=new URL(s.url||"/",`http://localhost:${t}`);if(c.pathname==="/mcp"&&s.method==="GET"){await bo(s,a);return}if(c.pathname==="/mcp/messages"&&s.method==="POST"){await Po(s,a);return}let l={};for(let[u,g]of Object.entries(s.headers))typeof g=="string"?l[u]=g:Array.isArray(g)&&(l[u]=g.join(", "));let d=await o.fetch(new Request(c.toString(),{method:s.method,headers:l,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 u=d.body.getReader();try{for(;;){let{done:g,value:f}=await u.read();if(g)break;a.write(f)}}finally{u.releaseLock()}}a.end()}).listen(t,()=>{So(t,n)}),await new Promise(()=>{})}var $o=3456;async function bt(e){(await I()).apiToken||(console.error(kt.red("Not authenticated. Run `tk login` first.")),process.exit(1));let o=e.port?parseInt(e.port,10):$o;(isNaN(o)||o<1||o>65535)&&(console.error(kt.red(`Invalid port: ${e.port}`)),process.exit(1));try{await Pe({port:o})}catch(r){console.error(kt.red("Failed to start daemon:"),r),process.exit(1)}}var v=new Io;v.name("tk").description("ThinkerMD CLI - Manage .thinker.md/ context files").version("0.1.0");v.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=>Qt(e));v.command("validate").description("Verify .thinker.md/ structure matches repository").option("-q, --quiet","Only output errors, no summary").action(e=>Zt(e));v.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=>ae(e));v.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=>de(e));v.command("login").description("Authenticate with ThinkerMD API").option("-t, --token <token>","API token").option("--api-url <url>","Custom API URL").action(e=>ge(e));v.command("logout").description("Remove saved authentication").action(()=>fe());v.command("status").description("Show status of .thinker.md/ context").action(()=>ye());v.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=>we(e));v.command("serve").description("Start MCP daemon for AI assistant integration").option("-p, --port <port>","Port to listen on","3456").action(e=>bt(e));v.on("command:*",()=>{console.error(ve.red(`Unknown command: ${v.args.join(" ")}`)),console.log(),v.help()});async function Se(){try{if(process.argv.length===2){await bt({port:"3456"});return}await v.parseAsync(process.argv)}catch(e){e instanceof Error&&console.error(ve.red(`Error: ${e.message}`)),process.exit(1)}}Se().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
124
+ \u2551 } \u2551
125
+ \u2551${" ".repeat(r)}\u2551
126
+ \u2551 ${M.yellow("Press Ctrl+C to stop")} \u2551
127
+ \u255A${"\u2550".repeat(r)}\u255D
128
+ `))}async function ve(e){let{port:t}=e,o=Po(),r=await $(),n=r.email||r.userId||"authenticated user";To(async(s,a)=>{let c=new URL(s.url||"/",`http://localhost:${t}`);if(c.pathname==="/mcp"&&s.method==="GET"){await ko(s,a);return}if(c.pathname==="/mcp/messages"&&s.method==="POST"){await vo(s,a);return}let l={};for(let[m,g]of Object.entries(s.headers))typeof g=="string"?l[m]=g:Array.isArray(g)&&(l[m]=g.join(", "));let d=await o.fetch(new Request(c.toString(),{method:s.method,headers:l,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,()=>{So(t,n)}),await new Promise(()=>{})}var $o=3456;async function kt(e){(await $()).apiToken||(console.error(bt.red("Not authenticated. Run `tk login` first.")),process.exit(1));let o=e.port?parseInt(e.port,10):$o;(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)}}var v=new Io;v.name("tk").description("ThinkerMD CLI - Manage .thinker.md/ context files").version("0.1.0");v.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=>Qt(e));v.command("validate").description("Verify .thinker.md/ structure matches repository").option("-q, --quiet","Only output errors, no summary").action(e=>Zt(e));v.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=>ae(e));v.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=>de(e));v.command("login").description("Authenticate with ThinkerMD API").option("-t, --token <token>","API token").option("--api-url <url>","Custom API URL").action(e=>ge(e));v.command("logout").description("Remove saved authentication").action(()=>fe());v.command("status").description("Show status of .thinker.md/ context").action(()=>ye());v.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=>we(e));v.command("serve").description("Start MCP daemon for AI assistant integration").option("-p, --port <port>","Port to listen on","3456").action(e=>kt(e));v.on("command:*",()=>{console.error(Pe.red(`Unknown command: ${v.args.join(" ")}`)),console.log(),v.help()});async function Se(){try{if(process.argv.length===2){await kt({port:"3456"});return}await v.parseAsync(process.argv)}catch(e){e instanceof Error&&console.error(Pe.red(`Error: ${e.message}`)),process.exit(1)}}Se().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.5",
3
+ "version": "0.1.7",
4
4
  "description": "ThinkerMD CLI for managing .thinker.md/ context files",
5
5
  "type": "module",
6
6
  "bin": {