contextfy 0.0.1
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.
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +204 -0
- package/dist/chunk-7D56MHBA.js +6 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {a,e,b as b$1,f,c,g,d}from'../chunk-7D56MHBA.js';import {Command}from'commander';import*as u from'fs';import*as h from'path';import {runInit}from'@ai-coders/context';import {createServer}from'http';import {createInterface}from'readline';import {randomUUID}from'crypto';import de from'open';import {z as z$1}from'zod';import T from'inquirer';var be=process.env.WEB_URL||"https://app.contextfy.dev";async function ve(){return new Promise((t,e)=>{let s=createServer();s.listen(0,"127.0.0.1",()=>{let n=s.address();if(n&&typeof n=="object"){let c=n.port;s.close(()=>t(c));}else s.close(()=>e(new Error("Failed to get port")));}),s.on("error",e);})}function De(){let t=createInterface({input:process.stdin,output:process.stdout});return new Promise(e=>{t.question("Paste token: ",s=>{t.close(),e(s.trim());});})}function Oe(t){let e=t.indexOf(":");if(e===-1)return null;let s=t.substring(0,e),n=t.substring(e+1);return !s||!n?null:{projectId:s,apiKey:n}}function _e(t,e){let s,n,c=new Promise((r,i)=>{s=r,n=i;}),o=createServer((r,i)=>{let a=new URL(r.url||"/",`http://localhost:${t}`);if(a.pathname==="/callback"){let l=a.searchParams.get("key"),p=a.searchParams.get("projectId"),f=a.searchParams.get("state");if(f!==e){i.writeHead(400,{"Content-Type":"text/html"}),i.end(`
|
|
3
|
+
<!DOCTYPE html>
|
|
4
|
+
<html>
|
|
5
|
+
<head><title>Authentication Failed</title></head>
|
|
6
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
7
|
+
<h1>Authentication Failed</h1>
|
|
8
|
+
<p>Invalid state parameter. This may be a security issue.</p>
|
|
9
|
+
<p>Please close this window and try again.</p>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
12
|
+
`),n(new Error("State mismatch - possible CSRF attack"));return}if(!l||!p){i.writeHead(400,{"Content-Type":"text/html"}),i.end(`
|
|
13
|
+
<!DOCTYPE html>
|
|
14
|
+
<html>
|
|
15
|
+
<head><title>Authentication Failed</title></head>
|
|
16
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
17
|
+
<h1>Authentication Failed</h1>
|
|
18
|
+
<p>Missing required parameters.</p>
|
|
19
|
+
<p>Please close this window and try again.</p>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
|
22
|
+
`),n(new Error("Missing key or projectId in callback"));return}i.writeHead(200,{"Content-Type":"text/html"}),i.end(`
|
|
23
|
+
<!DOCTYPE html>
|
|
24
|
+
<html>
|
|
25
|
+
<head>
|
|
26
|
+
<title>Authentication Successful</title>
|
|
27
|
+
<style>
|
|
28
|
+
body {
|
|
29
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
30
|
+
padding: 40px;
|
|
31
|
+
text-align: center;
|
|
32
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
margin: 0;
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
}
|
|
39
|
+
.container {
|
|
40
|
+
background: white;
|
|
41
|
+
border-radius: 12px;
|
|
42
|
+
padding: 40px;
|
|
43
|
+
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
|
44
|
+
max-width: 400px;
|
|
45
|
+
}
|
|
46
|
+
.checkmark {
|
|
47
|
+
width: 64px;
|
|
48
|
+
height: 64px;
|
|
49
|
+
background: #10B981;
|
|
50
|
+
border-radius: 50%;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
margin: 0 auto 20px;
|
|
55
|
+
}
|
|
56
|
+
.checkmark svg {
|
|
57
|
+
width: 32px;
|
|
58
|
+
height: 32px;
|
|
59
|
+
stroke: white;
|
|
60
|
+
stroke-width: 3;
|
|
61
|
+
}
|
|
62
|
+
h1 { color: #1f2937; margin: 0 0 10px; }
|
|
63
|
+
p { color: #6b7280; margin: 0; }
|
|
64
|
+
</style>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<div class="container">
|
|
68
|
+
<div class="checkmark">
|
|
69
|
+
<svg viewBox="0 0 24 24" fill="none">
|
|
70
|
+
<path d="M5 13l4 4L19 7" stroke-linecap="round" stroke-linejoin="round"/>
|
|
71
|
+
</svg>
|
|
72
|
+
</div>
|
|
73
|
+
<h1>Authentication Successful!</h1>
|
|
74
|
+
<p>You can close this window and return to your terminal.</p>
|
|
75
|
+
</div>
|
|
76
|
+
</body>
|
|
77
|
+
</html>
|
|
78
|
+
`),s({key:l,projectId:p,state:f});}else i.writeHead(404,{"Content-Type":"text/plain"}),i.end("Not Found");});return o.listen(t,"127.0.0.1"),{promise:c,server:o}}async function _(t={}){let{webUrl:e=be,timeout:s=120*1e3}=t,n,c=null;try{n=await ve();}catch{return console.log(""),console.log("Could not start callback server. Using manual authentication."),je(e)}let o=randomUUID(),{promise:r,server:i}=_e(n,o);c=i;let a=new URL("/cli/auth",e);a.searchParams.set("port",n.toString()),a.searchParams.set("state",o),console.log(""),console.log("Opening browser for authentication..."),console.log(`If browser doesn't open, visit: ${a.toString()}`),console.log("");try{await de(a.toString());}catch{console.log("Could not open browser automatically.");}let l=new Promise((p,f)=>{setTimeout(()=>{f(new Error("timeout"));},s);});try{let p=await Promise.race([r,l]);return c.close(),{apiKey:p.key,projectId:p.projectId}}catch(p){c.close();let f=p instanceof Error?p.message:"unknown";return f==="timeout"?(console.log(""),console.log("Callback not received. The browser may not be able to reach localhost."),console.log("Copy the token from the browser and paste it below."),console.log("")):(console.log(""),console.log(`Authentication error: ${f}`),console.log("You can try manual entry instead."),console.log("")),ge()}}async function je(t){let e=new URL("/cli/auth",t);console.log(""),console.log("Visit this URL to authenticate:"),console.log(` ${e.toString()}`),console.log(""),console.log("After linking, copy the token and paste it below."),console.log("");try{await de(e.toString());}catch{}return ge()}async function ge(){for(;;){let t=await De();if(!t){console.log("No token provided. Please try again or press Ctrl+C to cancel.");continue}let e=Oe(t);if(!e){console.log("Invalid token format. Expected: projectId:apiKey"),console.log("Please try again.");continue}return e}}function fe(t){console.log("Next steps:"),console.log(" contextfy push Push context to cloud"),console.log(" contextfy pull Pull context from cloud"),console.log(" contextfy sync Bidirectional sync"),console.log(""),console.log("To use with AI tools, add the MCP server to your config:"),console.log(""),console.log(" Claude Desktop/Code (~/.claude/claude_desktop_config.json):"),console.log(' "contextfy": {'),console.log(' "command": "npx",'),console.log(' "args": ["@contextfy/mcp"],'),t.apiKey?console.log(` "env": { "API_KEY": "${t.apiKey}" }`):console.log(' "env": { "API_KEY": "<your-api-key>" }'),console.log(" }"),console.log(""),console.log("Your credentials are saved in .contextfy.json");}var H=new Command("init").description("Initialize local .context/ scaffolding and authenticate with Contextfy cloud").argument("[path]","Repository path",process.cwd()).option("--skip-auth","Skip browser authentication (use existing config or env)").option("-o, --output <dir>","Output directory",".context").option("--semantic","Enable semantic analysis for richer templates").option("--docs-only","Generate documentation scaffolds only").option("--agents-only","Generate agent playbooks only").option("--force","Force re-initialization even if already initialized").option("-v, --verbose","Verbose output").action(async(t,e$1)=>{let s=e$1.docsOnly?"docs":e$1.agentsOnly?"agents":"both";try{let n=a(),c=u.existsSync(h.join(t,".context"));if(n?.projectId&&c&&!e$1.force){console.log("Project already initialized!"),console.log(` Project ID: ${n.projectId}`),console.log(""),console.log("Run with --force to re-initialize."),console.log(""),fe(n);return}let o=n,r=e()!==null;if(!r&&!e$1.skipAuth){console.log("No API key found. Starting browser authentication...");try{let i=await _();o={...o,projectId:i.projectId,apiKey:i.apiKey},b$1(o,t),f(t),console.log("Authentication successful!"),console.log("");}catch(i){i instanceof Error?console.error(`Authentication failed: ${i.message}`):console.error("Authentication failed"),console.log(""),console.log("You can retry with: contextfy init"),console.log("Or skip auth with: contextfy init --skip-auth"),process.exit(1);}}else !r&&e$1.skipAuth&&(console.log("Warning: No API key found. Cloud sync will not work."),console.log("Set API_KEY environment variable or run 'contextfy init' without --skip-auth."),console.log(""));console.log("Creating .context/ scaffolding..."),await runInit(t,s,{output:e$1.output,verbose:e$1.verbose,semantic:e$1.semantic,docsOnly:e$1.docsOnly,agentsOnly:e$1.agentsOnly}),console.log(""),console.log("Project initialized!"),console.log(""),o&&fe(o);}catch(n){n instanceof Error?console.error(`Error: ${n.message}`):console.error("An unexpected error occurred"),process.exit(1);}});var w=class{apiKey;baseUrl;projectId;bearerToken;constructor(e){this.apiKey=e.apiKey,this.baseUrl=e.baseUrl||"https://api.contextfy.dev",this.projectId=e.projectId,this.bearerToken=e.bearerToken;}async request(e,s,n){let c=`${this.baseUrl}${s}`,o={"Content-Type":"application/json"};if(this.bearerToken)o.Authorization=`Bearer ${this.bearerToken}`;else if(this.apiKey)o["X-API-Key"]=this.apiKey;else throw new Error("No authentication credentials available (API key or Bearer token required)");let r=await fetch(c,{method:e,headers:o,body:n?JSON.stringify(n):void 0});if(!r.ok){let i=await r.text();throw new Error(`API request failed: ${r.status} - ${i}`)}return r.json()}async listContextFiles(){return this.request("GET",`/projects/${this.projectId}/context`)}async getContextFile(e){return this.request("GET",`/projects/${this.projectId}/context/file/${e}`)}async upsertContextFile(e,s){return this.request("PUT",`/projects/${this.projectId}/context/file/${e}`,{content:s})}async deleteContextFile(e){await this.request("DELETE",`/projects/${this.projectId}/context/file/${e}`);}async syncContext(e,s=false){return this.request("POST",`/projects/${this.projectId}/context/sync`,{files:e,deleteOrphans:s})}async getChecksums(){return this.request("GET",`/projects/${this.projectId}/context/checksums`)}async pullContext(e,s){let n=[],c=[],o=0;for(;o!==void 0;){let r=new URLSearchParams;e&&r.set("since",e),s&&r.set("fileType",s),r.set("limit","200"),r.set("offset",String(o));let i=r.toString()?`?${r.toString()}`:"",a=await this.request("GET",`/projects/${this.projectId}/context/pull${i}`);n.push(...a.files),a.deletedPaths?.length&&(c=a.deletedPaths),o=a.nextOffset;}return {files:n,deletedPaths:c}}async listWorkflows(){return this.request("GET",`/projects/${this.projectId}/workflows`)}async createWorkflow(e){return this.request("POST",`/projects/${this.projectId}/workflows`,e)}async getAllWorkflowStates(){return this.request("GET",`/projects/${this.projectId}/workflow-states`)}async getWorkflowState(e){return this.request("GET",`/projects/${this.projectId}/workflows/${e}/state`)}async createWorkflowState(e,s){return this.request("POST",`/projects/${this.projectId}/workflows/${e}/state`,s)}async updateWorkflowState(e,s){return this.request("PATCH",`/projects/${this.projectId}/workflows/${e}/state`,s)}async advancePhase(e,s){return this.request("POST",`/projects/${this.projectId}/workflows/${e}/state/advance`,s)}async approveWorkflow(e,s,n){return this.request("POST",`/projects/${this.projectId}/workflows/${e}/state/approve`,{approver:s,notes:n})}async getLinkedPlans(){return this.request("GET",`/projects/${this.projectId}/plans/linked`)}async linkPlan(e){return this.request("POST",`/projects/${this.projectId}/plans/link`,e)}async updatePlanLink(e,s){return this.request("PATCH",`/projects/${this.projectId}/plans/${e}/link`,s)}async unlinkPlan(e){await this.request("DELETE",`/projects/${this.projectId}/plans/${e}/link`);}async getPlanTracking(e){return this.request("GET",`/projects/${this.projectId}/plans/${e}/tracking`)}async updatePlanPhase(e,s,n){return this.request("PATCH",`/projects/${this.projectId}/plans/${e}/tracking/phase`,{phaseId:s,status:n})}async recordDecision(e,s){return this.request("POST",`/projects/${this.projectId}/plans/${e}/tracking/decision`,s)}async updateDecision(e,s,n){return this.request("PATCH",`/projects/${this.projectId}/plans/${e}/tracking/decision/${s}`,n)}async logAction(e){await this.request("POST",`/projects/${this.projectId}/workflow/actions`,e);}async getActions(e){let s=new URLSearchParams;e&&Object.entries(e).forEach(([c,o])=>{o!==void 0&&s.set(c,String(o));});let n=s.toString()?`?${s.toString()}`:"";return this.request("GET",`/projects/${this.projectId}/workflow/actions${n}`)}async semanticSearch(e,s){let n=new URLSearchParams;return n.set("query",e),s?.fileType&&n.set("fileType",s.fileType),s?.topK&&n.set("topK",s.topK.toString()),s?.threshold&&n.set("threshold",s.threshold.toString()),this.request("GET",`/projects/${this.projectId}/rag/search?${n.toString()}`)}async reindexProject(){return this.request("POST",`/projects/${this.projectId}/rag/reindex`)}async getRagStats(){return this.request("GET",`/projects/${this.projectId}/rag/stats`)}async getRagStatus(){return this.request("GET",`/projects/${this.projectId}/rag/status`)}async getProjectConnectedMcps(){return this.request("GET",`/projects/${this.projectId}/mcps/enabled`)}async getMcpConfigByName(e){try{return await this.request("GET",`/projects/${this.projectId}/mcps/by-name/${encodeURIComponent(e)}`)}catch{return null}}async getMcpConfigWithAuth(e){return this.request("GET",`/projects/${this.projectId}/mcps/${e}/config`)}async logToolExecution(e){await this.request("POST",`/projects/${this.projectId}/mcps/executions`,e);}};var z=new Command("push").description("Push local .context files to cloud").action(async()=>{let t=a();t||(console.error("Error: Not initialized. Run 'contextfy init' first."),process.exit(1));let e$1=e();e$1||(console.error("Error: API_KEY environment variable is required"),process.exit(1));let s=new w({apiKey:e$1,projectId:t.projectId,baseUrl:t.apiUrl}),n=c(),c$1=new g(s,n,t);console.log("Pushing local context to cloud...");try{let o=await c$1.push();o.uploaded.length>0&&(console.log(`
|
|
79
|
+
Uploaded (${o.uploaded.length}):`),o.uploaded.forEach(r=>console.log(` + ${r}`))),o.deleted.length>0&&(console.log(`
|
|
80
|
+
Deleted from cloud (${o.deleted.length}):`),o.deleted.forEach(r=>console.log(` - ${r}`))),o.errors.length>0&&(console.log(`
|
|
81
|
+
Errors (${o.errors.length}):`),o.errors.forEach(r=>console.log(` ! ${r.path}: ${r.error}`))),o.uploaded.length===0&&o.deleted.length===0&&o.errors.length===0?console.log("No changes to push."):console.log(`
|
|
82
|
+
Push complete!`);}catch(o){console.error("Push failed:",o),process.exit(1);}});var X=new Command("pull").description("Pull context files from cloud to local").action(async()=>{let t=a();t||(console.error("Error: Not initialized. Run 'contextfy init' first."),process.exit(1));let e$1=e();e$1||(console.error("Error: API_KEY environment variable is required"),process.exit(1));let s=new w({apiKey:e$1,projectId:t.projectId,baseUrl:t.apiUrl}),n=c(),c$1=new g(s,n,t);console.log("Pulling context from cloud...");try{let o=await c$1.pull();o.downloaded.length>0&&(console.log(`
|
|
83
|
+
Downloaded (${o.downloaded.length}):`),o.downloaded.forEach(r=>console.log(` + ${r}`))),o.errors.length>0&&(console.log(`
|
|
84
|
+
Errors (${o.errors.length}):`),o.errors.forEach(r=>console.log(` ! ${r.path}: ${r.error}`))),o.downloaded.length===0&&o.errors.length===0?console.log("No files to download."):console.log(`
|
|
85
|
+
Pull complete!`);}catch(o){console.error("Pull failed:",o),process.exit(1);}});var V=new Command("sync").description("Bidirectional sync between local and cloud").option("--strategy <strategy>","Sync strategy: local-wins, cloud-wins, newer-wins").action(async t=>{let e$1=a();e$1||(console.error("Error: Not initialized. Run 'contextfy init' first."),process.exit(1));let s=e();s||(console.error("Error: API_KEY environment variable is required"),process.exit(1)),t.strategy&&(e$1.syncStrategy=t.strategy);let n=new w({apiKey:s,projectId:e$1.projectId,baseUrl:e$1.apiUrl}),c$1=c(),o=new g(n,c$1,e$1);console.log(`Syncing with strategy: ${e$1.syncStrategy||"newer-wins"}...`);try{let r=await o.sync();r.uploaded.length>0&&(console.log(`
|
|
86
|
+
Uploaded (${r.uploaded.length}):`),r.uploaded.forEach(i=>console.log(` ^ ${i}`))),r.downloaded.length>0&&(console.log(`
|
|
87
|
+
Downloaded (${r.downloaded.length}):`),r.downloaded.forEach(i=>console.log(` v ${i}`))),r.conflicts.length>0&&(console.log(`
|
|
88
|
+
Conflicts (${r.conflicts.length}):`),r.conflicts.forEach(i=>console.log(` ! ${i}`)),console.log(`
|
|
89
|
+
Resolve conflicts by choosing a strategy:`),console.log(" contextfy sync --strategy local-wins"),console.log(" contextfy sync --strategy cloud-wins")),r.errors.length>0&&(console.log(`
|
|
90
|
+
Errors (${r.errors.length}):`),r.errors.forEach(i=>console.log(` ! ${i.path}: ${i.error}`))),r.uploaded.length===0&&r.downloaded.length===0&&r.conflicts.length===0&&r.errors.length===0?console.log("Everything is in sync!"):console.log(`
|
|
91
|
+
Sync complete!`);}catch(r){console.error("Sync failed:",r),process.exit(1);}});var Q=new Command("status").description("Show sync status between local and cloud").action(async()=>{let t=a();t||(console.error("Error: Not initialized. Run 'contextfy init' first."),process.exit(1));let e$1=e();e$1||(console.error("Error: API_KEY environment variable is required"),process.exit(1));let s=new w({apiKey:e$1,projectId:t.projectId,baseUrl:t.apiUrl}),n=c(),c$1=new g(s,n,t);console.log(`Checking sync status...
|
|
92
|
+
`);try{let o=await c$1.status();console.log(`Project: ${t.projectId}`),console.log(`Strategy: ${t.syncStrategy||"newer-wins"}`),console.log(""),o.synced.length>0&&(console.log(`Synced (${o.synced.length}):`),o.synced.slice(0,10).forEach(i=>console.log(` = ${i}`)),o.synced.length>10&&console.log(` ... and ${o.synced.length-10} more`),console.log("")),o.localOnly.length>0&&(console.log(`Local only - will upload (${o.localOnly.length}):`),o.localOnly.forEach(i=>console.log(` + ${i}`)),console.log("")),o.cloudOnly.length>0&&(console.log(`Cloud only - will download (${o.cloudOnly.length}):`),o.cloudOnly.forEach(i=>console.log(` - ${i}`)),console.log("")),o.modified.length>0&&(console.log(`Modified - needs sync (${o.modified.length}):`),o.modified.forEach(i=>console.log(` ~ ${i}`)),console.log(""));let r=o.synced.length+o.localOnly.length+o.cloudOnly.length+o.modified.length;console.log(`Total files: ${r}`),o.localOnly.length+o.cloudOnly.length+o.modified.length===0?console.log("Status: In sync"):(console.log("Status: Changes pending"),console.log(`
|
|
93
|
+
Run 'contextfy sync' to synchronize.`));}catch(o){console.error("Status check failed:",o),process.exit(1);}});var F=[{id:"claude",name:"Claude Code",capabilities:{supportsAgents:true,supportsSkills:true,supportsCommands:true,supportsRules:true,rulesFormat:"single-file"},paths:{directory:".claude",rulesFile:"CLAUDE.md",agentsDir:".claude/agents",skillsDir:".claude/skills",commandsDir:".claude/commands"}},{id:"cursor",name:"Cursor AI",capabilities:{supportsAgents:true,supportsSkills:false,supportsCommands:false,supportsRules:true,rulesFormat:"single-file"},paths:{directory:".cursor",rulesFile:".cursorrules",agentsDir:".cursor/agents"}},{id:"windsurf",name:"Windsurf",capabilities:{supportsAgents:true,supportsSkills:false,supportsCommands:false,supportsRules:true,rulesFormat:"single-file"},paths:{directory:".windsurf",rulesFile:".windsurfrules",agentsDir:".windsurf/agents"}},{id:"antigravity",name:"Antigravity (Gemini)",capabilities:{supportsAgents:true,supportsSkills:true,supportsCommands:false,supportsRules:true,rulesFormat:"single-file"},paths:{directory:".agent",rulesFile:"GEMINI.md",agentsDir:".agent/agents",skillsDir:".agent/skills"}},{id:"codex",name:"Codex CLI",capabilities:{supportsAgents:false,supportsSkills:true,supportsCommands:false,supportsRules:true,rulesFormat:"single-file"},paths:{directory:".codex",rulesFile:".codex/instructions.md",skillsDir:".codex/skills"}},{id:"opencode",name:"OpenCode",capabilities:{supportsAgents:true,supportsSkills:true,supportsCommands:true,supportsRules:false,rulesFormat:"single-file"},paths:{directory:".opencode",agentsDir:".opencode/agents",skillsDir:".opencode/skills",commandsDir:".opencode/commands"}},{id:"vscode",name:"VSCode",capabilities:{supportsAgents:false,supportsSkills:false,supportsCommands:false,supportsRules:true,rulesFormat:"directory"},paths:{directory:".vscode",rulesDir:".vscode/prompts"}}];F.map(t=>t.id);function ye(t){return F.find(e=>e.id===t)}z$1.object({name:z$1.string().min(1).max(100),slug:z$1.string().min(1).max(100).regex(/^[a-z0-9-]+$/),description:z$1.string().max(500).optional(),repositoryUrl:z$1.string().url().optional()});z$1.object({path:z$1.string().min(1).max(500),content:z$1.string()});z$1.object({name:z$1.string().min(1).max(200),description:z$1.string().max(1e3).optional(),scale:z$1.enum(["QUICK","STANDARD","LARGE"])});z$1.object({name:z$1.string().min(1).max(100),description:z$1.string().max(500).optional(),transportType:z$1.enum(["stdio","sse","websocket"]),connectionUrl:z$1.string().url().optional(),command:z$1.string().optional(),args:z$1.array(z$1.string()).optional(),authType:z$1.enum(["none","api_key","oauth","bearer"]).optional(),authConfig:z$1.record(z$1.unknown()).optional()});z$1.object({toolName:z$1.string().min(1),input:z$1.record(z$1.unknown()).optional()});var J=new Map(F.map(t=>[t.id,t]));function Z(t){return ye(t)}function G(){return Array.from(J.keys())}function ee(t){return t==="all"?G():[t]}function te(t){let e=[],s=[];for(let n of t)J.has(n)?e.push(n):s.push(n);return {valid:e,invalid:s}}function M(){return process.platform==="win32"?"copy":"symlink"}function B(t){try{return u.lstatSync(t).isSymbolicLink()}catch{return false}}function ne(t){try{let e=h.join(t,".git");return u.existsSync(e)?u.statSync(e).isFile():!1}catch{return false}}function Se(t){try{return u.statSync(t).mtimeMs}catch{return 0}}function Ee(t,e,s){if(!u.existsSync(e))return false;if(s==="symlink"){if(!B(e))return false;try{let n=u.readlinkSync(e),c=h.resolve(h.dirname(e),n),o=h.resolve(t);return c===o}catch{return false}}else {let n=Se(t);if(Se(e)>=n)try{let o=u.readFileSync(t,"utf-8"),r=u.readFileSync(e,"utf-8");return o===r}catch{return false}return false}}function b(t){u.existsSync(t)||u.mkdirSync(t,{recursive:true});}function We(t){B(t)?u.unlinkSync(t):u.existsSync(t)&&(u.statSync(t).isDirectory()?u.rmSync(t,{recursive:true}):u.unlinkSync(t));}function N(t,e,s){let{mode:n,force:c,dryRun:o,verbose:r}=s;if(!u.existsSync(t))return {success:false,action:"error",message:`Source file does not exist: ${t}`};if(u.existsSync(e)||B(e)){if(Ee(t,e,n))return {success:true,action:"skipped",message:"Already up-to-date"};if(!c)return {success:false,action:"skipped",message:"Target exists. Use --force to overwrite."};if(o)return {success:true,action:"updated",message:"[dry-run] Would update existing file"};We(e);}else if(o)return {success:true,action:"created",message:"[dry-run] Would create new file"};b(h.dirname(e));try{if(n==="symlink"){let a=h.relative(h.dirname(e),t);u.symlinkSync(a,e);}else u.copyFileSync(t,e);let i=u.existsSync(e)?"created":"updated";return {success:!0,action:"created",message:n==="symlink"?"Created symlink":"Copied file"}}catch(i){return {success:false,action:"error",message:i instanceof Error?i.message:String(i)}}}function K(t,e,s){let{force:n,dryRun:c}=s;if(u.existsSync(t)){if(u.readFileSync(t,"utf-8")===e)return {success:true,action:"skipped",message:"Already up-to-date"};if(!n)return {success:false,action:"skipped",message:"Target exists. Use --force to overwrite."};if(c)return {success:true,action:"updated",message:"[dry-run] Would update existing file"}}else if(c)return {success:true,action:"created",message:"[dry-run] Would create new file"};try{return b(h.dirname(t)),u.writeFileSync(t,e,"utf-8"),{success:!0,action:"created",message:"File written"}}catch(o){return {success:false,action:"error",message:o instanceof Error?o.message:String(o)}}}var xe=["README.md","project-overview.md","architecture.md","development-workflow.md","data-flow.md","testing-strategy.md","security.md","glossary.md","tooling.md"];function oe(t){let e=/^---\s*\n[\s\S]*?\n---\s*\n/;return t.replace(e,"").trim()}function Te(t,e){let s=new Date().toISOString(),n=e.map(c=>`# - ${c}`).join(`
|
|
94
|
+
`);return `# Project Rules and Guidelines
|
|
95
|
+
|
|
96
|
+
> Auto-generated by contextfy tools-sync for ${t}
|
|
97
|
+
> Generated: ${s}
|
|
98
|
+
>
|
|
99
|
+
> Sources:
|
|
100
|
+
${n}
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
`}function se(t){return u.existsSync(t)?u.readdirSync(t).filter(e=>e.endsWith(".md")).sort((e,s)=>{let n=xe.indexOf(e),c=xe.indexOf(s);return n===-1&&c===-1?e.localeCompare(s):n===-1?1:c===-1?-1:n-c}):[]}function re(t,e){let s=se(t),n=[],c=[];for(let i of s){let a=h.join(t,i);try{let l=u.readFileSync(a,"utf-8");l=oe(l),l.trim()&&(n.push(i),c.push(l));}catch{console.warn(`Warning: Could not read ${a}`);}}return {content:Te(e.name,n)+c.join(`
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
`),sources:n}}function ie(t,e){let s=se(t),n=new Map;for(let c of s){let o=h.join(t,c);try{let r=u.readFileSync(o,"utf-8");if(r=oe(r),r.trim()){let i=`# ${h.basename(c,".md").replace(/-/g," ").replace(/\b\w/g,a=>a.toUpperCase())}
|
|
109
|
+
|
|
110
|
+
> Auto-generated by contextfy tools-sync for ${e.name}
|
|
111
|
+
> Source: .context/docs/${c}
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
`;n.set(c,{content:i+r,sources:[c]});}}catch{console.warn(`Warning: Could not read ${o}`);}}return n}function ce(t){if(!u.existsSync(t))return [];let e=[],s=u.readdirSync(t,{withFileTypes:true});for(let n of s)if(n.isDirectory()){let c=h.join(t,n.name,"SKILL.md");u.existsSync(c)&&e.push({name:n.name,path:c});}return e}function ae(t){if(!u.existsSync(t))return [];let e=[],s=u.readdirSync(t,{withFileTypes:true});for(let n of s)n.isFile()&&n.name.endsWith(".md")&&e.push({name:n.name,path:h.join(t,n.name)});return e}function ke(t){if(!u.existsSync(t))return [];let e=[],s=u.readdirSync(t,{withFileTypes:true});for(let n of s)n.isFile()&&n.name.endsWith(".md")&&e.push({name:n.name,path:h.join(t,n.name)});return e}var D=class{projectRoot;contextDir;options;constructor(e,s){this.projectRoot=e,this.contextDir=h.join(e,".context"),this.options=s;}async sync(){let e=[];if(!u.existsSync(this.contextDir))throw new Error("No .context directory found. Run 'contextfy init' first.");for(let s of this.options.tools){let n=Z(s);if(!n){e.push({tool:s,created:[],updated:[],skipped:[],errors:[{path:"",error:`Unknown tool: ${s}`}]});continue}let c=await this.syncTool(n);e.push(c);}return e}async syncTool(e){let s={tool:e.id,created:[],updated:[],skipped:[],errors:[]},n=h.join(this.projectRoot,e.paths.directory);if(ne(n))return s.errors.push({path:e.paths.directory,error:"Tool directory is a git submodule. Skipping."}),s;let c={mode:this.options.mode,force:this.options.force,dryRun:this.options.dryRun,verbose:this.options.verbose};return e.capabilities.supportsAgents&&this.options.contentTypes.includes("agents")&&await this.syncAgents(e,c,s),e.capabilities.supportsSkills&&this.options.contentTypes.includes("skills")&&await this.syncSkills(e,c,s),e.capabilities.supportsCommands&&this.options.contentTypes.includes("commands")&&await this.syncCommands(e,c,s),e.capabilities.supportsRules&&this.options.contentTypes.includes("docs")&&await this.syncDocs(e,c,s),s}async syncAgents(e,s,n){if(!e.paths.agentsDir)return;let c=h.join(this.contextDir,"agents"),o=h.join(this.projectRoot,e.paths.agentsDir);if(!u.existsSync(c)){this.options.verbose&&n.skipped.push(`${e.paths.agentsDir}/ (no source agents)`);return}let r=ae(c);if(r.length===0){this.options.verbose&&n.skipped.push(`${e.paths.agentsDir}/ (empty source)`);return}s.dryRun||b(o);for(let i of r){let a=h.join(o,i.name),l=h.join(e.paths.agentsDir,i.name),p=N(i.path,a,s);p.success?p.action==="created"?n.created.push(l):p.action==="updated"?n.updated.push(l):p.action==="skipped"&&n.skipped.push(l):n.errors.push({path:l,error:p.message||"Unknown error"});}}async syncSkills(e,s,n){if(!e.paths.skillsDir)return;let c=h.join(this.contextDir,"skills"),o=h.join(this.projectRoot,e.paths.skillsDir);if(!u.existsSync(c)){this.options.verbose&&n.skipped.push(`${e.paths.skillsDir}/ (no source skills)`);return}let r=ce(c);if(r.length===0){this.options.verbose&&n.skipped.push(`${e.paths.skillsDir}/ (empty source)`);return}s.dryRun||b(o);for(let i of r){let a=`${i.name}.md`,l=h.join(o,a),p=h.join(e.paths.skillsDir,a),f=N(i.path,l,s);f.success?f.action==="created"?n.created.push(p):f.action==="updated"?n.updated.push(p):f.action==="skipped"&&n.skipped.push(p):n.errors.push({path:p,error:f.message||"Unknown error"});}}async syncCommands(e,s,n){if(!e.paths.commandsDir)return;let c=h.join(this.contextDir,"commands"),o=h.join(this.projectRoot,e.paths.commandsDir);if(!u.existsSync(c)){this.options.verbose&&n.skipped.push(`${e.paths.commandsDir}/ (no source commands)`);return}let r=ke(c);if(r.length===0){this.options.verbose&&n.skipped.push(`${e.paths.commandsDir}/ (empty source)`);return}s.dryRun||b(o);for(let i of r){let a=h.join(o,i.name),l=h.join(e.paths.commandsDir,i.name),p=N(i.path,a,s);p.success?p.action==="created"?n.created.push(l):p.action==="updated"?n.updated.push(l):p.action==="skipped"&&n.skipped.push(l):n.errors.push({path:l,error:p.message||"Unknown error"});}}async syncDocs(e,s,n){let c=h.join(this.contextDir,"docs");if(!u.existsSync(c)){this.options.verbose&&n.skipped.push(`${e.paths.rulesFile||e.paths.rulesDir} (no source docs)`);return}if(e.capabilities.rulesFormat==="single-file"&&e.paths.rulesFile){let o=re(c,e);if(o.sources.length===0){this.options.verbose&&n.skipped.push(`${e.paths.rulesFile} (no docs to compile)`);return}let r=h.join(this.projectRoot,e.paths.rulesFile),i=K(r,o.content,s);i.success?i.action==="created"?n.created.push(e.paths.rulesFile):i.action==="updated"?n.updated.push(e.paths.rulesFile):i.action==="skipped"&&n.skipped.push(e.paths.rulesFile):n.errors.push({path:e.paths.rulesFile,error:i.message||"Unknown error"});}else if(e.capabilities.rulesFormat==="directory"&&e.paths.rulesDir){let o=ie(c,e);if(o.size===0){this.options.verbose&&n.skipped.push(`${e.paths.rulesDir}/ (no docs to compile)`);return}let r=h.join(this.projectRoot,e.paths.rulesDir);s.dryRun||b(r);for(let[i,a]of o){let l=h.join(r,i),p=h.join(e.paths.rulesDir,i),f=K(l,a.content,s);f.success?f.action==="created"?n.created.push(p):f.action==="updated"?n.updated.push(p):f.action==="skipped"&&n.skipped.push(p):n.errors.push({path:p,error:f.message||"Unknown error"});}}}};var le=new Command("tools-sync").description("Sync .context/ artifacts with external AI coding tools").option("--preset <preset>","Tool preset: all, claude, cursor, windsurf, antigravity, codex, opencode, vscode","all").option("--tools <tools>","Comma-separated list of tools to sync (overrides --preset)").option("--mode <mode>","Sync mode: symlink or copy (default: symlink on Unix, copy on Windows)").option("--agents","Sync agents only").option("--skills","Sync skills only").option("--commands","Sync commands only").option("--docs","Sync docs (compile to rules) only").option("--dry-run","Preview changes without applying").option("--force","Overwrite existing files").option("--verbose","Show detailed output").action(async t=>{let e=c(),s=d(e);u.existsSync(s)||(console.error("Error: No .context directory found."),console.error("Run 'contextfy init' to initialize the project first."),process.exit(1));let n;if(t.tools){let i=t.tools.split(",").map(p=>p.trim()),{valid:a,invalid:l}=te(i);l.length>0&&(console.error(`Error: Unknown tools: ${l.join(", ")}`),console.error(`Available tools: ${G().join(", ")}`),process.exit(1)),n=a;}else {let i=t.preset;n=ee(i);}let c$1=[];t.agents&&c$1.push("agents"),t.skills&&c$1.push("skills"),t.commands&&c$1.push("commands"),t.docs&&c$1.push("docs"),c$1.length===0&&c$1.push("agents","skills","commands","docs");let o=t.mode||M();o!=="symlink"&&o!=="copy"&&(console.error("Error: Invalid mode. Use 'symlink' or 'copy'."),process.exit(1));let r={mode:o,dryRun:t.dryRun||false,force:t.force||false,verbose:t.verbose||false,contentTypes:c$1,tools:n};t.dryRun&&console.log(`[DRY RUN] Preview of changes:
|
|
116
|
+
`),console.log(`Syncing to ${n.length} tool(s): ${n.join(", ")}`),console.log(`Content types: ${c$1.join(", ")}`),console.log(`Mode: ${o}
|
|
117
|
+
`);try{let a=await new D(e,r).sync(),l=!1;for(let m of a){let d=`
|
|
118
|
+
${m.tool}:`,S="";m.created.length>0&&(l=!0,S+=`
|
|
119
|
+
Created (${m.created.length}):`,m.created.forEach(y=>S+=`
|
|
120
|
+
+ ${y}`)),m.updated.length>0&&(l=!0,S+=`
|
|
121
|
+
Updated (${m.updated.length}):`,m.updated.forEach(y=>S+=`
|
|
122
|
+
~ ${y}`)),t.verbose&&m.skipped.length>0&&(S+=`
|
|
123
|
+
Skipped (${m.skipped.length}):`,m.skipped.forEach(y=>S+=`
|
|
124
|
+
= ${y}`)),m.errors.length>0&&(S+=`
|
|
125
|
+
Errors (${m.errors.length}):`,m.errors.forEach(y=>S+=`
|
|
126
|
+
! ${y.path}: ${y.error}`)),S?console.log(d+S):t.verbose&&console.log(d+`
|
|
127
|
+
No changes`);}let p=a.reduce((m,d)=>m+d.created.length,0),f=a.reduce((m,d)=>m+d.updated.length,0),O=a.reduce((m,d)=>m+d.errors.length,0);console.log(`
|
|
128
|
+
---`),t.dryRun?console.log(`[DRY RUN] Would create ${p}, update ${f} file(s)`):console.log(!l&&O===0?"Everything is up-to-date!":`Sync complete: ${p} created, ${f} updated`),O>0&&(console.log(`${O} error(s) encountered`),process.exit(1));}catch(i){console.error("Sync failed:",i instanceof Error?i.message:i),process.exit(1);}});async function q(t){if(e())return true;console.log(`\u26A0\uFE0F This action requires authentication.
|
|
129
|
+
`);let{doAuth:e$1}=await T.prompt([{type:"confirm",name:"doAuth",message:"Would you like to authenticate now?",default:true}]);if(!e$1)return console.log(`
|
|
130
|
+
Authentication required. Returning to menu.
|
|
131
|
+
`),false;console.log(`
|
|
132
|
+
Starting browser authentication...`);try{let s=await _(),c={...a()||{},projectId:s.projectId,apiKey:s.apiKey};return b$1(c,t),f(t),console.log(`\u2713 Authentication successful!
|
|
133
|
+
`),!0}catch(s){return console.error(`
|
|
134
|
+
\u274C Authentication failed:`,s instanceof Error?s.message:s),console.log(`Returning to menu.
|
|
135
|
+
`),false}}async function Y(){console.log(`
|
|
136
|
+
\u{1F3AF} Contextfy CLI - Interactive Mode
|
|
137
|
+
`);let t=a(),e$1=c(),s=d(e$1),n=u.existsSync(s),c$1=t!==null,o=e()!==null;console.log("Status:"),console.log(` Project root: ${e$1}`),console.log(` .context dir: ${n?"\u2713 exists":"\u2717 not found"}`),console.log(` Config: ${c$1?"\u2713 configured":"\u2717 not initialized"}`),console.log(` API key: ${o?"\u2713 set":"\u2717 not set"}`),console.log("");let r=[{name:"\u{1F527} Initialize project",value:"init",disabled:c$1&&n?"Already initialized":false},{name:"\u{1F504} Sync with AI tools",value:"tools-sync",disabled:n?false:"Run init first"},{name:`\u2B06\uFE0F Push to cloud${o?"":" (requires auth)"}`,value:"push"},{name:`\u2B07\uFE0F Pull from cloud${o?"":" (requires auth)"}`,value:"pull"},{name:`\u{1F503} Bidirectional sync${o?"":" (requires auth)"}`,value:"sync"},{name:`\u{1F4CA} Show sync status${o?"":" (requires auth)"}`,value:"status"},new T.Separator,{name:"\u274C Exit",value:"exit"}],{action:i}=await T.prompt([{type:"list",name:"action",message:"What would you like to do?",choices:r}]);try{switch(i){case "init":await Ke(e$1);break;case "tools-sync":await Ie(e$1);break;case "push":await qe();break;case "pull":await Ye();break;case "sync":await Ae();break;case "status":await He();break;case "exit":console.log(`
|
|
138
|
+
Goodbye!
|
|
139
|
+
`);return}}catch(l){console.error(`
|
|
140
|
+
\u274C Error:`,l instanceof Error?l.message:l);}let{continueLoop:a$1}=await T.prompt([{type:"confirm",name:"continueLoop",message:"Would you like to do something else?",default:true}]);a$1?await Y():console.log(`
|
|
141
|
+
Goodbye!
|
|
142
|
+
`);}async function Ke(t){console.log(`
|
|
143
|
+
\u{1F527} Initialize Project
|
|
144
|
+
`);let e$1=a(),s=u.existsSync(h.join(t,".context"));if(e$1?.projectId&&s){let{forceInit:a}=await T.prompt([{type:"confirm",name:"forceInit",message:"Project already initialized. Do you want to re-initialize?",default:false}]);if(!a){console.log(`
|
|
145
|
+
Init cancelled.
|
|
146
|
+
`);return}}let{initOptions:n}=await T.prompt([{type:"checkbox",name:"initOptions",message:"Select initialization options:",choices:[{name:"Semantic analysis (richer templates)",value:"semantic",checked:true},{name:"Generate docs only",value:"docsOnly"},{name:"Generate agents only",value:"agentsOnly"}]}]),c=e()!==null,o=e$1;if(!c){let{doAuth:a}=await T.prompt([{type:"confirm",name:"doAuth",message:"No API key found. Would you like to authenticate via browser?",default:true}]);if(a){console.log(`
|
|
147
|
+
Starting browser authentication...`);try{let l=await _();o={...o,projectId:l.projectId,apiKey:l.apiKey},b$1(o,t),f(t),console.log(`\u2713 Authentication successful!
|
|
148
|
+
`);}catch(l){console.error("Authentication failed:",l instanceof Error?l.message:l),console.log(`Continuing without cloud sync...
|
|
149
|
+
`);}}else console.log(`Skipping authentication. Cloud sync will not work without API key.
|
|
150
|
+
`);}console.log("Creating .context/ scaffolding...");let r=n.includes("docsOnly")?"docs":n.includes("agentsOnly")?"agents":"both";await runInit(t,r,{output:".context",verbose:false,semantic:n.includes("semantic"),docsOnly:n.includes("docsOnly"),agentsOnly:n.includes("agentsOnly")}),console.log(`
|
|
151
|
+
\u2713 Project initialized!
|
|
152
|
+
`);let{syncTools:i}=await T.prompt([{type:"confirm",name:"syncTools",message:"Would you like to sync with AI coding tools now?",default:true}]);i&&await Ie(t);}async function Ie(t){console.log(`
|
|
153
|
+
\u{1F504} Sync with AI Coding Tools
|
|
154
|
+
`);let{selectedTools:e}=await T.prompt([{type:"checkbox",name:"selectedTools",message:"Select tools to sync:",choices:[{name:"Claude Code (.claude/, CLAUDE.md)",value:"claude",checked:true},{name:"Cursor AI (.cursor/, .cursorrules)",value:"cursor",checked:true},{name:"Windsurf (.windsurf/, .windsurfrules)",value:"windsurf",checked:true},{name:"Antigravity/Gemini (.agent/, GEMINI.md)",value:"antigravity",checked:true},{name:"Codex CLI (.codex/)",value:"codex",checked:true},{name:"VSCode (.vscode/prompts/)",value:"vscode",checked:true}],validate:d=>d.length<1?"You must select at least one tool.":true}]),{contentTypes:s}=await T.prompt([{type:"checkbox",name:"contentTypes",message:"Select content to sync:",choices:[{name:"Agents (.context/agents/ \u2192 tool agents dirs)",value:"agents",checked:true},{name:"Skills (.context/skills/ \u2192 tool skills dirs)",value:"skills",checked:true},{name:"Commands (.context/commands/ \u2192 tool commands dirs)",value:"commands",checked:true},{name:"Docs (.context/docs/ \u2192 compiled rules files)",value:"docs",checked:true}],validate:d=>d.length<1?"You must select at least one content type.":true}]),n=M(),{mode:c}=await T.prompt([{type:"list",name:"mode",message:"Select sync mode:",choices:[{name:`Symlink (recommended for Unix)${n==="symlink"?" - default":""}`,value:"symlink"},{name:`Copy (required for Windows)${n==="copy"?" - default":""}`,value:"copy"}],default:n}]),{options:o}=await T.prompt([{type:"checkbox",name:"options",message:"Additional options:",choices:[{name:"Dry run (preview changes without applying)",value:"dryRun"},{name:"Force (overwrite existing files)",value:"force",checked:true},{name:"Verbose (show detailed output)",value:"verbose"}]}]);console.log(`
|
|
155
|
+
\u{1F4CB} Summary:`),console.log(` Tools: ${e.join(", ")}`),console.log(` Content: ${s.join(", ")}`),console.log(` Mode: ${c}`),console.log(` Options: ${o.length>0?o.join(", "):"none"}`);let{confirm:r}=await T.prompt([{type:"confirm",name:"confirm",message:"Proceed with sync?",default:true}]);if(!r){console.log(`
|
|
156
|
+
Sync cancelled.
|
|
157
|
+
`);return}let i={mode:c,dryRun:o.includes("dryRun"),force:o.includes("force"),verbose:o.includes("verbose"),contentTypes:s,tools:e};i.dryRun?console.log(`
|
|
158
|
+
[DRY RUN] Preview of changes:
|
|
159
|
+
`):console.log(`
|
|
160
|
+
`);let l=await new D(t,i).sync(),p=false;for(let d of l){let S=`${d.tool}:`,y="";d.created.length>0&&(p=true,y+=`
|
|
161
|
+
Created (${d.created.length}):`,d.created.forEach(R=>y+=`
|
|
162
|
+
+ ${R}`)),d.updated.length>0&&(p=true,y+=`
|
|
163
|
+
Updated (${d.updated.length}):`,d.updated.forEach(R=>y+=`
|
|
164
|
+
~ ${R}`)),i.verbose&&d.skipped.length>0&&(y+=`
|
|
165
|
+
Skipped (${d.skipped.length}):`,d.skipped.forEach(R=>y+=`
|
|
166
|
+
= ${R}`)),d.errors.length>0&&(y+=`
|
|
167
|
+
Errors (${d.errors.length}):`,d.errors.forEach(R=>y+=`
|
|
168
|
+
! ${R.path}: ${R.error}`)),y&&console.log(S+y+`
|
|
169
|
+
`);}let f=l.reduce((d,S)=>d+S.created.length,0),O=l.reduce((d,S)=>d+S.updated.length,0),m=l.reduce((d,S)=>d+S.errors.length,0);console.log("---"),i.dryRun?console.log(`[DRY RUN] Would create ${f}, update ${O} file(s)`):console.log(!p&&m===0?"\u2713 Everything is up-to-date!":`\u2713 Sync complete: ${f} created, ${O} updated`),m>0&&console.log(`\u26A0 ${m} error(s) encountered`),console.log("");}async function qe(){console.log(`
|
|
170
|
+
\u2B06\uFE0F Push to Cloud
|
|
171
|
+
`);let t=c();if(!await q(t))return;let e$1=a();if(!e$1?.projectId){console.log(`No project configured. Please run 'Initialize project' first.
|
|
172
|
+
`);return}let s=e(),{confirm:n}=await T.prompt([{type:"confirm",name:"confirm",message:"Push local .context files to cloud?",default:true}]);if(!n){console.log(`
|
|
173
|
+
Push cancelled.
|
|
174
|
+
`);return}let c$1=new w({apiKey:s,projectId:e$1.projectId,baseUrl:e$1.apiUrl}),o=new g(c$1,t,e$1);console.log("Pushing local context to cloud...");let r=await o.push();r.uploaded.length>0&&(console.log(`
|
|
175
|
+
Uploaded (${r.uploaded.length}):`),r.uploaded.forEach(i=>console.log(` + ${i}`))),r.deleted.length>0&&(console.log(`
|
|
176
|
+
Deleted from cloud (${r.deleted.length}):`),r.deleted.forEach(i=>console.log(` - ${i}`))),r.errors.length>0&&(console.log(`
|
|
177
|
+
Errors (${r.errors.length}):`),r.errors.forEach(i=>console.log(` ! ${i.path}: ${i.error}`))),r.uploaded.length===0&&r.deleted.length===0&&r.errors.length===0?console.log(`
|
|
178
|
+
\u2713 No changes to push.`):console.log(`
|
|
179
|
+
\u2713 Push complete!`);}async function Ye(){console.log(`
|
|
180
|
+
\u2B07\uFE0F Pull from Cloud
|
|
181
|
+
`);let t=c();if(!await q(t))return;let e$1=a();if(!e$1?.projectId){console.log(`No project configured. Please run 'Initialize project' first.
|
|
182
|
+
`);return}let s=e(),{confirm:n}=await T.prompt([{type:"confirm",name:"confirm",message:"Pull context files from cloud to local?",default:true}]);if(!n){console.log(`
|
|
183
|
+
Pull cancelled.
|
|
184
|
+
`);return}let c$1=new w({apiKey:s,projectId:e$1.projectId,baseUrl:e$1.apiUrl}),o=new g(c$1,t,e$1);console.log("Pulling context from cloud...");let r=await o.pull();r.downloaded.length>0&&(console.log(`
|
|
185
|
+
Downloaded (${r.downloaded.length}):`),r.downloaded.forEach(i=>console.log(` + ${i}`))),r.errors.length>0&&(console.log(`
|
|
186
|
+
Errors (${r.errors.length}):`),r.errors.forEach(i=>console.log(` ! ${i.path}: ${i.error}`))),r.downloaded.length===0&&r.errors.length===0?console.log(`
|
|
187
|
+
\u2713 No files to download.`):console.log(`
|
|
188
|
+
\u2713 Pull complete!`);}async function Ae(){console.log(`
|
|
189
|
+
\u{1F503} Bidirectional Sync
|
|
190
|
+
`);let t=c();if(!await q(t))return;let e$1=a();if(!e$1?.projectId){console.log(`No project configured. Please run 'Initialize project' first.
|
|
191
|
+
`);return}let s=e(),{strategy:n}=await T.prompt([{type:"list",name:"strategy",message:"Select sync strategy:",choices:[{name:"Newer wins (default) - Keep the most recently modified version",value:"newer-wins"},{name:"Local wins - Always prefer local files",value:"local-wins"},{name:"Cloud wins - Always prefer cloud files",value:"cloud-wins"}],default:e$1.syncStrategy||"newer-wins"}]),{confirm:c$1}=await T.prompt([{type:"confirm",name:"confirm",message:`Sync with strategy '${n}'?`,default:true}]);if(!c$1){console.log(`
|
|
192
|
+
Sync cancelled.
|
|
193
|
+
`);return}let o={...e$1,syncStrategy:n},r=new w({apiKey:s,projectId:e$1.projectId,baseUrl:e$1.apiUrl}),i=new g(r,t,o);console.log(`
|
|
194
|
+
Syncing with strategy: ${n}...`);let a$1=await i.sync();a$1.uploaded.length>0&&(console.log(`
|
|
195
|
+
Uploaded (${a$1.uploaded.length}):`),a$1.uploaded.forEach(l=>console.log(` ^ ${l}`))),a$1.downloaded.length>0&&(console.log(`
|
|
196
|
+
Downloaded (${a$1.downloaded.length}):`),a$1.downloaded.forEach(l=>console.log(` v ${l}`))),a$1.conflicts.length>0&&(console.log(`
|
|
197
|
+
Conflicts (${a$1.conflicts.length}):`),a$1.conflicts.forEach(l=>console.log(` ! ${l}`))),a$1.errors.length>0&&(console.log(`
|
|
198
|
+
Errors (${a$1.errors.length}):`),a$1.errors.forEach(l=>console.log(` ! ${l.path}: ${l.error}`))),a$1.uploaded.length===0&&a$1.downloaded.length===0&&a$1.conflicts.length===0&&a$1.errors.length===0?console.log(`
|
|
199
|
+
\u2713 Everything is in sync!`):console.log(`
|
|
200
|
+
\u2713 Sync complete!`);}async function He(){console.log(`
|
|
201
|
+
\u{1F4CA} Sync Status
|
|
202
|
+
`);let t=c();if(!await q(t))return;let e$1=a();if(!e$1?.projectId){console.log(`No project configured. Please run 'Initialize project' first.
|
|
203
|
+
`);return}let s=e(),n=new w({apiKey:s,projectId:e$1.projectId,baseUrl:e$1.apiUrl}),c$1=new g(n,t,e$1);console.log(`Checking sync status...
|
|
204
|
+
`);let o=await c$1.status();console.log(`Project: ${e$1.projectId}`),console.log(`Strategy: ${e$1.syncStrategy||"newer-wins"}`),console.log(""),o.synced.length>0&&(console.log(`Synced (${o.synced.length}):`),o.synced.slice(0,10).forEach(i=>console.log(` = ${i}`)),o.synced.length>10&&console.log(` ... and ${o.synced.length-10} more`),console.log("")),o.localOnly.length>0&&(console.log(`Local only - will upload (${o.localOnly.length}):`),o.localOnly.forEach(i=>console.log(` + ${i}`)),console.log("")),o.cloudOnly.length>0&&(console.log(`Cloud only - will download (${o.cloudOnly.length}):`),o.cloudOnly.forEach(i=>console.log(` - ${i}`)),console.log("")),o.modified.length>0&&(console.log(`Modified - needs sync (${o.modified.length}):`),o.modified.forEach(i=>console.log(` ~ ${i}`)),console.log(""));let r=o.synced.length+o.localOnly.length+o.cloudOnly.length+o.modified.length;if(console.log(`Total files: ${r}`),o.localOnly.length+o.cloudOnly.length+o.modified.length===0)console.log("Status: \u2713 In sync");else {console.log("Status: \u26A0 Changes pending");let{doSync:i}=await T.prompt([{type:"confirm",name:"doSync",message:"Would you like to sync now?",default:true}]);i&&await Ae();}}var P=new Command;P.name("contextfy").description("CLI for syncing local .context with Contextfy cloud").version("0.0.1");P.option("-i, --interactive","Run in interactive mode").action(async t=>{t.interactive&&await Y();});P.addCommand(H);P.addCommand(z);P.addCommand(X);P.addCommand(V);P.addCommand(Q);P.addCommand(le);var Xe=process.argv.slice(2);Xe.length===0?Y().catch(console.error):P.parse(process.argv);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import*as r from'fs';import*as u from'path';import*as m from'crypto';var p=".contextfy.json";function h(c=process.cwd()){let t=c,n=u.parse(t).root;for(;t!==n;){let o=u.join(t,p);if(r.existsSync(o))return o;t=u.dirname(t);}return null}function x(c){let t=c||h();if(!t||!r.existsSync(t))return null;try{let n=r.readFileSync(t,"utf-8");return JSON.parse(n)}catch(n){return console.error(`Error reading config file: ${n}`),null}}function S(c,t=process.cwd()){let n=u.join(t,p);r.writeFileSync(n,JSON.stringify(c,null,2));}function k(c=process.cwd()){let t=h(c);return t?u.dirname(t):c}function g(c){return u.join(c,".context")}function D(){let c=x();return c?.apiKey?c.apiKey:process.env.API_KEY??null}function F(c=process.cwd()){let t=u.join(c,".gitignore"),n=p;try{let o="";if(r.existsSync(t)&&(o=r.readFileSync(t,"utf-8"),o.split(`
|
|
2
|
+
`).map(i=>i.trim()).includes(n)))return;let e=o.endsWith(`
|
|
3
|
+
`)||o===""?`${o}${n}
|
|
4
|
+
`:`${o}
|
|
5
|
+
${n}
|
|
6
|
+
`;r.writeFileSync(t,e);}catch{}}var y=class{client;contextDir;config;constructor(t,n,o){this.client=t,this.contextDir=g(n),this.config=o;}computeChecksum(t){return m.createHash("sha256").update(t).digest("hex")}shouldIgnore(t){return this.config.ignore?this.config.ignore.some(n=>n.includes("*")?new RegExp(n.replace(/\*/g,".*")).test(t):t===n||t.startsWith(n+"/")):false}async getLocalManifest(){let t=new Map;if(!r.existsSync(this.contextDir))return t;let n=o=>{let e=r.readdirSync(o,{withFileTypes:true});for(let s of e){let i=u.join(o,s.name),a=u.relative(this.contextDir,i);if(!this.shouldIgnore(a)){if(s.isDirectory())n(i);else if(s.isFile()){let d=r.readFileSync(i,"utf-8"),w=r.statSync(i),C=this.computeChecksum(d);t.set(a,{path:a,checksum:C,updatedAt:w.mtime,content:d});}}}};return n(this.contextDir),t}async getCloudManifest(){let t=await this.client.getChecksums(),n=new Map;for(let[o,e]of Object.entries(t)){let s=e;n.set(o,{checksum:s.checksum,updatedAt:new Date(s.updatedAt)});}return n}async computeDiff(){let t=await this.getLocalManifest(),n=await this.getCloudManifest(),o=this.config.syncStrategy||"newer-wins",e={toUpload:[],toDownload:[],conflicts:[],toDeleteLocal:[],toDeleteCloud:[]};for(let[s,i]of t){let a=n.get(s);if(!a)e.toUpload.push(s);else if(a.checksum!==i.checksum)if(o==="local-wins")e.toUpload.push(s);else if(o==="cloud-wins"){let d=await this.client.getContextFile(s);e.toDownload.push({path:s,content:d.content,checksum:d.checksum});}else if(i.updatedAt>a.updatedAt)e.toUpload.push(s);else if(a.updatedAt>i.updatedAt){let d=await this.client.getContextFile(s);e.toDownload.push({path:s,content:d.content,checksum:d.checksum});}else e.conflicts.push({path:s,localChecksum:i.checksum,cloudChecksum:a.checksum,localUpdatedAt:i.updatedAt.toISOString(),cloudUpdatedAt:a.updatedAt.toISOString()});}for(let[s,i]of n)if(!t.has(s)){let a=await this.client.getContextFile(s);e.toDownload.push({path:s,content:a.content,checksum:a.checksum});}return e}async push(){let t={uploaded:[],downloaded:[],deleted:[],conflicts:[],errors:[]},n=await this.getLocalManifest(),o=[];for(let[s,i]of n)i.content&&o.push({path:s,content:i.content,checksum:i.checksum});if(o.length===0)return t;let e=await this.client.syncContext(o,true);return t.uploaded=[...e.created,...e.updated],t.deleted=e.deleted,t.errors=e.errors,t}async pull(){let t={uploaded:[],downloaded:[],deleted:[],conflicts:[],errors:[]},{files:n}=await this.client.pullContext();r.existsSync(this.contextDir)||r.mkdirSync(this.contextDir,{recursive:true});for(let o of n)try{let e=u.join(this.contextDir,o.path),s=u.dirname(e);r.existsSync(s)||r.mkdirSync(s,{recursive:!0}),r.writeFileSync(e,o.content),t.downloaded.push(o.path);}catch(e){t.errors.push({path:o.path,error:e instanceof Error?e.message:String(e)});}return t}async sync(){let t={uploaded:[],downloaded:[],deleted:[],conflicts:[],errors:[]},n=await this.computeDiff();t.conflicts=n.conflicts.map(e=>e.path);let o=await this.getLocalManifest();for(let e of n.toUpload){let s=o.get(e);if(s?.content)try{await this.client.upsertContextFile(e,s.content),t.uploaded.push(e);}catch(i){t.errors.push({path:e,error:i instanceof Error?i.message:String(i)});}}for(let e of n.toDownload)try{let s=u.join(this.contextDir,e.path),i=u.dirname(s);r.existsSync(i)||r.mkdirSync(i,{recursive:!0}),r.writeFileSync(s,e.content),t.downloaded.push(e.path);}catch(s){t.errors.push({path:e.path,error:s instanceof Error?s.message:String(s)});}return t}async status(){let t=await this.getLocalManifest(),n=await this.getCloudManifest(),o={localOnly:[],cloudOnly:[],modified:[],synced:[]};for(let[e,s]of t){let i=n.get(e);i?i.checksum!==s.checksum?o.modified.push(e):o.synced.push(e):o.localOnly.push(e);}for(let e of n.keys())t.has(e)||o.cloudOnly.push(e);return o}};export{x as a,S as b,k as c,g as d,D as e,F as f,y as g};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ContextfyClient } from '@contextfy/mcp/client';
|
|
2
|
+
|
|
3
|
+
interface CliConfig {
|
|
4
|
+
projectId: string;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
syncStrategy?: "local-wins" | "cloud-wins" | "newer-wins";
|
|
8
|
+
ignore?: string[];
|
|
9
|
+
autoSync?: {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
debounceMs: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
declare function loadConfig(configPath?: string): CliConfig | null;
|
|
15
|
+
declare function saveConfig(config: CliConfig, dir?: string): void;
|
|
16
|
+
|
|
17
|
+
interface FileManifest {
|
|
18
|
+
path: string;
|
|
19
|
+
checksum: string;
|
|
20
|
+
updatedAt: Date;
|
|
21
|
+
content?: string;
|
|
22
|
+
}
|
|
23
|
+
interface SyncDiff {
|
|
24
|
+
toUpload: string[];
|
|
25
|
+
toDownload: Array<{
|
|
26
|
+
path: string;
|
|
27
|
+
content: string;
|
|
28
|
+
checksum: string;
|
|
29
|
+
}>;
|
|
30
|
+
conflicts: Array<{
|
|
31
|
+
path: string;
|
|
32
|
+
localChecksum: string;
|
|
33
|
+
cloudChecksum: string;
|
|
34
|
+
localUpdatedAt: string;
|
|
35
|
+
cloudUpdatedAt: string;
|
|
36
|
+
}>;
|
|
37
|
+
toDeleteLocal: string[];
|
|
38
|
+
toDeleteCloud: string[];
|
|
39
|
+
}
|
|
40
|
+
interface SyncResult {
|
|
41
|
+
uploaded: string[];
|
|
42
|
+
downloaded: string[];
|
|
43
|
+
deleted: string[];
|
|
44
|
+
conflicts: string[];
|
|
45
|
+
errors: Array<{
|
|
46
|
+
path: string;
|
|
47
|
+
error: string;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
declare class SyncEngine {
|
|
51
|
+
private client;
|
|
52
|
+
private contextDir;
|
|
53
|
+
private config;
|
|
54
|
+
constructor(client: ContextfyClient, projectRoot: string, config: CliConfig);
|
|
55
|
+
private computeChecksum;
|
|
56
|
+
private shouldIgnore;
|
|
57
|
+
getLocalManifest(): Promise<Map<string, FileManifest>>;
|
|
58
|
+
getCloudManifest(): Promise<Map<string, {
|
|
59
|
+
checksum: string;
|
|
60
|
+
updatedAt: Date;
|
|
61
|
+
}>>;
|
|
62
|
+
computeDiff(): Promise<SyncDiff>;
|
|
63
|
+
push(): Promise<SyncResult>;
|
|
64
|
+
pull(): Promise<SyncResult>;
|
|
65
|
+
sync(): Promise<SyncResult>;
|
|
66
|
+
status(): Promise<{
|
|
67
|
+
localOnly: string[];
|
|
68
|
+
cloudOnly: string[];
|
|
69
|
+
modified: string[];
|
|
70
|
+
synced: string[];
|
|
71
|
+
}>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { type CliConfig, SyncEngine, loadConfig, saveConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{g as SyncEngine,a as loadConfig,b as saveConfig}from'./chunk-7D56MHBA.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "contextfy",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI for syncing local .context with Contextfy cloud",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"contextfy": "./dist/bin/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/AiCoders-inc/context-maestro.git",
|
|
30
|
+
"directory": "packages/cli"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup && chmod +x dist/bin/cli.js",
|
|
34
|
+
"dev": "tsc --watch",
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"start": "node dist/bin/cli.js",
|
|
38
|
+
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@ai-coders/context": "^0.7.0",
|
|
42
|
+
"chalk": "^5.0.0",
|
|
43
|
+
"chokidar": "^3.6.0",
|
|
44
|
+
"commander": "^12.0.0",
|
|
45
|
+
"inquirer": "^9.0.0",
|
|
46
|
+
"open": "^10.0.0",
|
|
47
|
+
"zod": "^3.23.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@contextfy/env": "workspace:*",
|
|
51
|
+
"@contextfy/mcp": "workspace:*",
|
|
52
|
+
"@contextfy/shared": "workspace:*",
|
|
53
|
+
"@contextfy/typescript-config": "workspace:*",
|
|
54
|
+
"@types/inquirer": "^9.0.9",
|
|
55
|
+
"@types/node": "^20.0.0",
|
|
56
|
+
"tsup": "^8.0.0",
|
|
57
|
+
"typescript": "^5.7.0"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18"
|
|
61
|
+
},
|
|
62
|
+
"keywords": [
|
|
63
|
+
"cli",
|
|
64
|
+
"contextfy",
|
|
65
|
+
"sync",
|
|
66
|
+
"ai-context"
|
|
67
|
+
]
|
|
68
|
+
}
|