@contextos/core 0.2.3 → 0.4.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.
Files changed (2) hide show
  1. package/dist/index.js +632 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1963,7 +1963,7 @@ var RateLimiter = class {
1963
1963
  async waitForSlot() {
1964
1964
  const result = await this.checkLimit();
1965
1965
  if (!result.allowed) {
1966
- await new Promise((resolve2) => setTimeout(resolve2, result.waitTime + 100));
1966
+ await new Promise((resolve3) => setTimeout(resolve3, result.waitTime + 100));
1967
1967
  return this.waitForSlot();
1968
1968
  }
1969
1969
  }
@@ -2091,7 +2091,7 @@ var GeminiClient = class {
2091
2091
  const waitTime = retryAfter ? parseInt(retryAfter) * 1e3 : 6e4;
2092
2092
  if (retryCount < 3) {
2093
2093
  console.warn(`Rate limited. Waiting ${waitTime}ms before retry (${retryCount + 1}/3)...`);
2094
- await new Promise((resolve2) => setTimeout(resolve2, waitTime));
2094
+ await new Promise((resolve3) => setTimeout(resolve3, waitTime));
2095
2095
  return this.request(prompt, systemPrompt, retryCount + 1);
2096
2096
  }
2097
2097
  throw new Error(`Rate limit exceeded after ${retryCount} retries. Please try again later.`);
@@ -2275,6 +2275,18 @@ Output the compressed version directly.`;
2275
2275
  tokensSaved: originalTokens - newTokens
2276
2276
  };
2277
2277
  }
2278
+ /**
2279
+ * Generate code or content from a prompt
2280
+ * Used by AIGenerator for code generation
2281
+ */
2282
+ async generate(prompt) {
2283
+ const response = await this.request(prompt);
2284
+ return {
2285
+ text: response,
2286
+ tokensUsed: Math.ceil(response.length / 4)
2287
+ // Rough estimate
2288
+ };
2289
+ }
2278
2290
  };
2279
2291
  function createGeminiClient() {
2280
2292
  const apiKey = process.env.GEMINI_API_KEY;
@@ -3066,7 +3078,7 @@ var OpenAIAdapter = class {
3066
3078
  const waitTime = retryAfter ? parseInt(retryAfter) * 1e3 : 6e4;
3067
3079
  if (retryCount < 3) {
3068
3080
  console.warn(`OpenAI rate limited. Waiting ${waitTime}ms before retry (${retryCount + 1}/3)...`);
3069
- await new Promise((resolve2) => setTimeout(resolve2, waitTime));
3081
+ await new Promise((resolve3) => setTimeout(resolve3, waitTime));
3070
3082
  return this.complete(request, retryCount + 1);
3071
3083
  }
3072
3084
  return {
@@ -3166,7 +3178,7 @@ var AnthropicAdapter = class {
3166
3178
  const waitTime = retryAfter ? parseInt(retryAfter) * 1e3 : 6e4;
3167
3179
  if (retryCount < 3) {
3168
3180
  console.warn(`Anthropic rate limited. Waiting ${waitTime}ms before retry (${retryCount + 1}/3)...`);
3169
- await new Promise((resolve2) => setTimeout(resolve2, waitTime));
3181
+ await new Promise((resolve3) => setTimeout(resolve3, waitTime));
3170
3182
  return this.complete(request, retryCount + 1);
3171
3183
  }
3172
3184
  return {
@@ -7578,7 +7590,618 @@ function createLogger(config) {
7578
7590
  function setGlobalLogger(logger) {
7579
7591
  globalLogger = logger;
7580
7592
  }
7593
+
7594
+ // src/generator/index.ts
7595
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11, readFileSync as readFileSync15 } from "fs";
7596
+ import { dirname as dirname5, join as join15, resolve as resolve2, normalize as normalize3 } from "path";
7597
+
7598
+ // src/generator/shadow-fs.ts
7599
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync13, rmSync as rmSync2, renameSync, copyFileSync } from "fs";
7600
+ import { join as join13, dirname as dirname4 } from "path";
7601
+ import { randomUUID } from "crypto";
7602
+ var ShadowFileSystem = class {
7603
+ rootDir;
7604
+ shadowRoot;
7605
+ activeTransactions = /* @__PURE__ */ new Map();
7606
+ constructor(rootDir) {
7607
+ this.rootDir = rootDir;
7608
+ this.shadowRoot = join13(rootDir, ".contextos", ".shadow");
7609
+ }
7610
+ /**
7611
+ * Begin a new transaction
7612
+ */
7613
+ beginTransaction() {
7614
+ const id = randomUUID();
7615
+ const shadowDir = join13(this.shadowRoot, id);
7616
+ mkdirSync9(shadowDir, { recursive: true });
7617
+ const transaction = {
7618
+ id,
7619
+ shadowDir,
7620
+ files: /* @__PURE__ */ new Map(),
7621
+ startedAt: /* @__PURE__ */ new Date(),
7622
+ status: "active"
7623
+ };
7624
+ this.activeTransactions.set(id, transaction);
7625
+ return transaction;
7626
+ }
7627
+ /**
7628
+ * Write file to shadow location
7629
+ */
7630
+ writeToShadow(txId, relativePath, content) {
7631
+ const tx = this.activeTransactions.get(txId);
7632
+ if (!tx) {
7633
+ return { success: false, error: "Transaction not found" };
7634
+ }
7635
+ if (tx.status !== "active") {
7636
+ return { success: false, error: `Transaction is ${tx.status}` };
7637
+ }
7638
+ try {
7639
+ const shadowPath = join13(tx.shadowDir, relativePath);
7640
+ const shadowDir = dirname4(shadowPath);
7641
+ if (!existsSync14(shadowDir)) {
7642
+ mkdirSync9(shadowDir, { recursive: true });
7643
+ }
7644
+ writeFileSync9(shadowPath, content, "utf-8");
7645
+ const realPath = join13(this.rootDir, relativePath);
7646
+ tx.files.set(realPath, shadowPath);
7647
+ return { success: true, shadowPath };
7648
+ } catch (error) {
7649
+ return {
7650
+ success: false,
7651
+ error: error instanceof Error ? error.message : String(error)
7652
+ };
7653
+ }
7654
+ }
7655
+ /**
7656
+ * Read file from shadow or real location
7657
+ */
7658
+ readFile(txId, relativePath) {
7659
+ const tx = this.activeTransactions.get(txId);
7660
+ const realPath = join13(this.rootDir, relativePath);
7661
+ if (tx && tx.files.has(realPath)) {
7662
+ const shadowPath = tx.files.get(realPath);
7663
+ if (existsSync14(shadowPath)) {
7664
+ return readFileSync13(shadowPath, "utf-8");
7665
+ }
7666
+ }
7667
+ if (existsSync14(realPath)) {
7668
+ return readFileSync13(realPath, "utf-8");
7669
+ }
7670
+ return null;
7671
+ }
7672
+ /**
7673
+ * Commit transaction - atomically move shadow files to real locations
7674
+ */
7675
+ commit(txId) {
7676
+ const tx = this.activeTransactions.get(txId);
7677
+ if (!tx) {
7678
+ return { success: false, filesCommitted: 0, error: "Transaction not found" };
7679
+ }
7680
+ if (tx.status !== "active") {
7681
+ return { success: false, filesCommitted: 0, error: `Transaction is ${tx.status}` };
7682
+ }
7683
+ let filesCommitted = 0;
7684
+ try {
7685
+ const backups = /* @__PURE__ */ new Map();
7686
+ for (const [realPath, shadowPath] of tx.files) {
7687
+ if (existsSync14(realPath)) {
7688
+ const backupPath = `${realPath}.bak.${txId.slice(0, 8)}`;
7689
+ copyFileSync(realPath, backupPath);
7690
+ backups.set(realPath, backupPath);
7691
+ }
7692
+ }
7693
+ for (const [realPath, shadowPath] of tx.files) {
7694
+ const targetDir = dirname4(realPath);
7695
+ if (!existsSync14(targetDir)) {
7696
+ mkdirSync9(targetDir, { recursive: true });
7697
+ }
7698
+ renameSync(shadowPath, realPath);
7699
+ filesCommitted++;
7700
+ }
7701
+ for (const backupPath of backups.values()) {
7702
+ if (existsSync14(backupPath)) {
7703
+ rmSync2(backupPath);
7704
+ }
7705
+ }
7706
+ this.cleanupTransaction(tx);
7707
+ tx.status = "committed";
7708
+ return { success: true, filesCommitted };
7709
+ } catch (error) {
7710
+ tx.status = "rolled_back";
7711
+ return {
7712
+ success: false,
7713
+ filesCommitted,
7714
+ error: error instanceof Error ? error.message : String(error)
7715
+ };
7716
+ }
7717
+ }
7718
+ /**
7719
+ * Rollback transaction - discard all shadow files
7720
+ */
7721
+ rollback(txId) {
7722
+ const tx = this.activeTransactions.get(txId);
7723
+ if (!tx) {
7724
+ return { success: false, error: "Transaction not found" };
7725
+ }
7726
+ try {
7727
+ this.cleanupTransaction(tx);
7728
+ tx.status = "rolled_back";
7729
+ return { success: true };
7730
+ } catch (error) {
7731
+ return {
7732
+ success: false,
7733
+ error: error instanceof Error ? error.message : String(error)
7734
+ };
7735
+ }
7736
+ }
7737
+ /**
7738
+ * Cleanup transaction resources
7739
+ */
7740
+ cleanupTransaction(tx) {
7741
+ if (existsSync14(tx.shadowDir)) {
7742
+ rmSync2(tx.shadowDir, { recursive: true, force: true });
7743
+ }
7744
+ this.activeTransactions.delete(tx.id);
7745
+ }
7746
+ /**
7747
+ * Get transaction status
7748
+ */
7749
+ getTransaction(txId) {
7750
+ return this.activeTransactions.get(txId);
7751
+ }
7752
+ /**
7753
+ * List files in transaction
7754
+ */
7755
+ listTransactionFiles(txId) {
7756
+ const tx = this.activeTransactions.get(txId);
7757
+ if (!tx) return [];
7758
+ return Array.from(tx.files.keys()).map(
7759
+ (p) => p.replace(this.rootDir + "/", "").replace(this.rootDir + "\\", "")
7760
+ );
7761
+ }
7762
+ };
7763
+ function createShadowFS(rootDir) {
7764
+ return new ShadowFileSystem(rootDir);
7765
+ }
7766
+
7767
+ // src/context/negative-context.ts
7768
+ import { existsSync as existsSync15, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
7769
+ import { join as join14 } from "path";
7770
+ var NegativeContextManager = class {
7771
+ contextIgnorePath;
7772
+ rules = [];
7773
+ constructor(rootDir) {
7774
+ this.contextIgnorePath = join14(rootDir, ".contextos", ".contextignore");
7775
+ this.load();
7776
+ }
7777
+ /**
7778
+ * Load rules from .contextignore
7779
+ */
7780
+ load() {
7781
+ if (!existsSync15(this.contextIgnorePath)) {
7782
+ this.rules = [];
7783
+ return;
7784
+ }
7785
+ try {
7786
+ const content = readFileSync14(this.contextIgnorePath, "utf-8");
7787
+ const lines = content.split("\n").filter((line) => line.trim() && !line.startsWith("#"));
7788
+ this.rules = lines.map((line) => {
7789
+ const match = line.match(/^(\w+):(.+?)\|(.+)$/);
7790
+ if (match) {
7791
+ return {
7792
+ category: match[1],
7793
+ rule: match[2].trim(),
7794
+ reason: match[3].trim(),
7795
+ learnedAt: (/* @__PURE__ */ new Date()).toISOString()
7796
+ };
7797
+ }
7798
+ return {
7799
+ rule: line.trim(),
7800
+ reason: "User defined",
7801
+ learnedAt: (/* @__PURE__ */ new Date()).toISOString(),
7802
+ category: "pattern"
7803
+ };
7804
+ });
7805
+ } catch {
7806
+ this.rules = [];
7807
+ }
7808
+ }
7809
+ /**
7810
+ * Save rules to .contextignore
7811
+ */
7812
+ save() {
7813
+ const header = `# ContextOS Negative Context Rules
7814
+ # Format: CATEGORY:RULE|REASON
7815
+ # Categories: library, pattern, file, approach
7816
+ # These rules are injected into AI prompts to prevent repeated mistakes
7817
+
7818
+ `;
7819
+ const content = this.rules.map(
7820
+ (r) => `${r.category}:${r.rule}|${r.reason}`
7821
+ ).join("\n");
7822
+ writeFileSync10(this.contextIgnorePath, header + content, "utf-8");
7823
+ }
7824
+ /**
7825
+ * Add a new negative rule
7826
+ */
7827
+ addRule(rule) {
7828
+ if (this.rules.some((r) => r.rule === rule.rule)) {
7829
+ return;
7830
+ }
7831
+ this.rules.push({
7832
+ ...rule,
7833
+ learnedAt: (/* @__PURE__ */ new Date()).toISOString()
7834
+ });
7835
+ this.save();
7836
+ }
7837
+ /**
7838
+ * Learn from an error
7839
+ */
7840
+ learnFromError(errorMessage, context) {
7841
+ const patterns = [
7842
+ // Library version issues
7843
+ {
7844
+ regex: /Module not found.*['"](.+?)['"]/i,
7845
+ category: "library",
7846
+ template: (match) => ({
7847
+ rule: `Do not use ${match[1]}`,
7848
+ reason: `Module not found: ${match[1]}`
7849
+ })
7850
+ },
7851
+ // Deprecated API
7852
+ {
7853
+ regex: /deprecated.*['"](.+?)['"]/i,
7854
+ category: "approach",
7855
+ template: (match) => ({
7856
+ rule: `Avoid deprecated API: ${match[1]}`,
7857
+ reason: "API is deprecated"
7858
+ })
7859
+ },
7860
+ // Permission denied
7861
+ {
7862
+ regex: /EACCES.*['"](.+?)['"]/i,
7863
+ category: "file",
7864
+ template: (match) => ({
7865
+ rule: `Cannot write to ${match[1]}`,
7866
+ reason: "Permission denied"
7867
+ })
7868
+ },
7869
+ // Type errors
7870
+ {
7871
+ regex: /Type '(.+?)' is not assignable/i,
7872
+ category: "pattern",
7873
+ template: (match) => ({
7874
+ rule: `Check type compatibility for ${match[1]}`,
7875
+ reason: "Type mismatch error"
7876
+ })
7877
+ }
7878
+ ];
7879
+ for (const pattern of patterns) {
7880
+ const match = errorMessage.match(pattern.regex);
7881
+ if (match) {
7882
+ const { rule, reason } = pattern.template(match);
7883
+ const newRule = {
7884
+ rule,
7885
+ reason,
7886
+ category: pattern.category,
7887
+ learnedAt: (/* @__PURE__ */ new Date()).toISOString()
7888
+ };
7889
+ this.addRule(newRule);
7890
+ return newRule;
7891
+ }
7892
+ }
7893
+ return null;
7894
+ }
7895
+ /**
7896
+ * Get all rules
7897
+ */
7898
+ getRules() {
7899
+ return [...this.rules];
7900
+ }
7901
+ /**
7902
+ * Get rules by category
7903
+ */
7904
+ getRulesByCategory(category) {
7905
+ return this.rules.filter((r) => r.category === category);
7906
+ }
7907
+ /**
7908
+ * Generate prompt injection text
7909
+ */
7910
+ generatePromptInjection() {
7911
+ if (this.rules.length === 0) {
7912
+ return "";
7913
+ }
7914
+ const lines = [
7915
+ "",
7916
+ "# IMPORTANT CONSTRAINTS (Learned from previous failures)",
7917
+ "Do NOT violate these rules:",
7918
+ ""
7919
+ ];
7920
+ for (const rule of this.rules) {
7921
+ lines.push(`- \u274C ${rule.rule} (Reason: ${rule.reason})`);
7922
+ }
7923
+ lines.push("");
7924
+ return lines.join("\n");
7925
+ }
7926
+ /**
7927
+ * Clear all rules
7928
+ */
7929
+ clear() {
7930
+ this.rules = [];
7931
+ this.save();
7932
+ }
7933
+ /**
7934
+ * Remove a specific rule
7935
+ */
7936
+ removeRule(ruleText) {
7937
+ const index = this.rules.findIndex((r) => r.rule === ruleText);
7938
+ if (index !== -1) {
7939
+ this.rules.splice(index, 1);
7940
+ this.save();
7941
+ return true;
7942
+ }
7943
+ return false;
7944
+ }
7945
+ };
7946
+ function createNegativeContextManager(rootDir) {
7947
+ return new NegativeContextManager(rootDir);
7948
+ }
7949
+
7950
+ // src/generator/index.ts
7951
+ var MAX_DEPTH = 3;
7952
+ var AIGenerator = class {
7953
+ config = null;
7954
+ gemini = null;
7955
+ openai = null;
7956
+ anthropic = null;
7957
+ rootDir = process.cwd();
7958
+ shadowFS = null;
7959
+ negativeContext = null;
7960
+ constructor(projectDir) {
7961
+ this.rootDir = projectDir || process.cwd();
7962
+ }
7963
+ /**
7964
+ * Initialize the generator
7965
+ */
7966
+ async initialize() {
7967
+ try {
7968
+ this.config = loadConfig(this.rootDir);
7969
+ this.rootDir = this.config.rootDir;
7970
+ } catch {
7971
+ }
7972
+ if (isGeminiAvailable()) {
7973
+ this.gemini = createGeminiClient();
7974
+ }
7975
+ if (isOpenAIAvailable()) {
7976
+ this.openai = createOpenAIAdapter();
7977
+ }
7978
+ if (isAnthropicAvailable()) {
7979
+ this.anthropic = createAnthropicAdapter();
7980
+ }
7981
+ if (!this.gemini && !this.openai && !this.anthropic) {
7982
+ throw new Error("No AI API key found. Set GEMINI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY environment variable.");
7983
+ }
7984
+ this.shadowFS = createShadowFS(this.rootDir);
7985
+ this.negativeContext = createNegativeContextManager(this.rootDir);
7986
+ }
7987
+ /**
7988
+ * Generate code from a prompt
7989
+ */
7990
+ async generate(prompt, options = {}) {
7991
+ const model = options.model || "auto";
7992
+ const depth = options.depth || 0;
7993
+ if (depth >= MAX_DEPTH) {
7994
+ return {
7995
+ success: false,
7996
+ files: [],
7997
+ tokensUsed: 0,
7998
+ error: `Maximum recursion depth (${MAX_DEPTH}) exceeded. Provide a heuristic summary instead.`
7999
+ };
8000
+ }
8001
+ const context = await this.buildContext();
8002
+ const negativeRules = this.negativeContext?.generatePromptInjection() || "";
8003
+ const fullPrompt = this.createPrompt(prompt, context + negativeRules);
8004
+ let response;
8005
+ let tokensUsed = 0;
8006
+ try {
8007
+ if (model === "gemini" || model === "auto" && this.gemini) {
8008
+ if (!this.gemini) throw new Error("Gemini not available");
8009
+ const result = await this.gemini.generate(fullPrompt);
8010
+ response = result.text;
8011
+ tokensUsed = result.tokensUsed;
8012
+ } else if (model === "openai" || model === "auto" && this.openai) {
8013
+ if (!this.openai) throw new Error("OpenAI not available");
8014
+ const result = await this.openai.complete({
8015
+ systemPrompt: "You are an expert software developer. Generate clean, production-ready code.",
8016
+ userMessage: fullPrompt,
8017
+ maxTokens: 8e3
8018
+ });
8019
+ response = result.content;
8020
+ tokensUsed = result.tokensUsed.total;
8021
+ } else if (model === "anthropic" || model === "auto" && this.anthropic) {
8022
+ if (!this.anthropic) throw new Error("Anthropic not available");
8023
+ const result = await this.anthropic.complete({
8024
+ systemPrompt: "You are an expert software developer. Generate clean, production-ready code.",
8025
+ userMessage: fullPrompt,
8026
+ maxTokens: 8e3,
8027
+ model: "claude-3-5-sonnet-20240620"
8028
+ // Default to 3.5 Sonnet
8029
+ });
8030
+ response = result.content;
8031
+ tokensUsed = result.tokensUsed.total;
8032
+ } else {
8033
+ throw new Error("No AI model available");
8034
+ }
8035
+ } catch (error) {
8036
+ return {
8037
+ success: false,
8038
+ files: [],
8039
+ tokensUsed: 0,
8040
+ error: error instanceof Error ? error.message : String(error)
8041
+ };
8042
+ }
8043
+ const files = this.parseResponse(response);
8044
+ const maxFiles = options.maxFiles || 20;
8045
+ if (files.length > maxFiles) {
8046
+ return {
8047
+ success: false,
8048
+ files: [],
8049
+ tokensUsed,
8050
+ error: `Too many files generated (${files.length}). Max: ${maxFiles}`
8051
+ };
8052
+ }
8053
+ if (options.dryRun) {
8054
+ return {
8055
+ success: true,
8056
+ files,
8057
+ tokensUsed
8058
+ };
8059
+ }
8060
+ const writtenFiles = await this.writeFiles(files, options);
8061
+ return {
8062
+ success: true,
8063
+ files: writtenFiles,
8064
+ tokensUsed
8065
+ };
8066
+ }
8067
+ /**
8068
+ * Build context from project files
8069
+ */
8070
+ async buildContext() {
8071
+ const parts = [];
8072
+ const prdPaths = ["prd.md", "PRD.md", "docs/prd.md", "README.md"];
8073
+ for (const prdPath of prdPaths) {
8074
+ const fullPath = join15(this.rootDir, prdPath);
8075
+ if (existsSync16(fullPath)) {
8076
+ const content = readFileSync15(fullPath, "utf-8");
8077
+ parts.push(`## ${prdPath}
8078
+
8079
+ ${content}`);
8080
+ break;
8081
+ }
8082
+ }
8083
+ if (this.config) {
8084
+ parts.push(`## Project Info
8085
+ - Name: ${this.config.context.project.name}
8086
+ - Language: ${this.config.context.project.language}
8087
+ - Description: ${this.config.context.project.description || "N/A"}
8088
+ `);
8089
+ }
8090
+ return parts.join("\n\n---\n\n");
8091
+ }
8092
+ /**
8093
+ * Create the full AI prompt
8094
+ */
8095
+ createPrompt(userPrompt, context) {
8096
+ return `# Project Context
8097
+
8098
+ ${context}
8099
+
8100
+ ---
8101
+
8102
+ # Task
8103
+
8104
+ ${userPrompt}
8105
+
8106
+ ---
8107
+
8108
+ # Instructions
8109
+
8110
+ Generate the necessary code files. Use this EXACT format for each file:
8111
+
8112
+ \`\`\`path/to/filename.ext
8113
+ // file content here
8114
+ \`\`\`
8115
+
8116
+ IMPORTANT:
8117
+ - Put the FULL FILE PATH in the code block language tag (e.g. \`\`\`src/index.ts)
8118
+ - Generate complete, working code - no placeholders
8119
+ - Include all necessary imports
8120
+ - Follow best practices for the language
8121
+ - One file per code block
8122
+
8123
+ Generate the files now:`;
8124
+ }
8125
+ /**
8126
+ * Parse AI response into file objects
8127
+ */
8128
+ parseResponse(response) {
8129
+ const files = [];
8130
+ const codeBlockRegex = /```([^\n`]+)\n([\s\S]*?)```/g;
8131
+ let match;
8132
+ while ((match = codeBlockRegex.exec(response)) !== null) {
8133
+ const pathOrLang = match[1].trim();
8134
+ const content = match[2];
8135
+ if (pathOrLang.includes("/") || pathOrLang.includes("\\") || /\.\w+$/.test(pathOrLang)) {
8136
+ const filePath = pathOrLang.replace(/\\/g, "/");
8137
+ const ext = filePath.split(".").pop() || "";
8138
+ const language = this.getLanguageFromExtension(ext);
8139
+ const fullPath = join15(this.rootDir, filePath);
8140
+ const isNew = !existsSync16(fullPath);
8141
+ files.push({
8142
+ path: filePath,
8143
+ content: content.trim(),
8144
+ isNew,
8145
+ language
8146
+ });
8147
+ }
8148
+ }
8149
+ return files;
8150
+ }
8151
+ /**
8152
+ * Get language from file extension
8153
+ */
8154
+ getLanguageFromExtension(ext) {
8155
+ const map = {
8156
+ ts: "typescript",
8157
+ tsx: "typescript",
8158
+ js: "javascript",
8159
+ jsx: "javascript",
8160
+ py: "python",
8161
+ rs: "rust",
8162
+ go: "go",
8163
+ java: "java",
8164
+ md: "markdown",
8165
+ json: "json",
8166
+ yaml: "yaml",
8167
+ yml: "yaml",
8168
+ css: "css",
8169
+ html: "html"
8170
+ };
8171
+ return map[ext] || ext;
8172
+ }
8173
+ /**
8174
+ * Write files to disk
8175
+ */
8176
+ async writeFiles(files, options) {
8177
+ const written = [];
8178
+ for (const file of files) {
8179
+ const fullPath = resolve2(this.rootDir, file.path);
8180
+ const normalizedPath = normalize3(fullPath);
8181
+ if (!normalizedPath.startsWith(normalize3(this.rootDir))) {
8182
+ console.warn(`Skipping file outside project root: ${file.path}`);
8183
+ continue;
8184
+ }
8185
+ const dir = dirname5(fullPath);
8186
+ if (!existsSync16(dir)) {
8187
+ mkdirSync10(dir, { recursive: true });
8188
+ }
8189
+ if (!file.isNew && options.backupBeforeOverwrite) {
8190
+ const backupPath = `${fullPath}.bak`;
8191
+ const existingContent = readFileSync15(fullPath, "utf-8");
8192
+ writeFileSync11(backupPath, existingContent);
8193
+ }
8194
+ writeFileSync11(fullPath, file.content, "utf-8");
8195
+ written.push(file);
8196
+ }
8197
+ return written;
8198
+ }
8199
+ };
8200
+ function createAIGenerator(projectDir) {
8201
+ return new AIGenerator(projectDir);
8202
+ }
7581
8203
  export {
8204
+ AIGenerator,
7582
8205
  ASTParser,
7583
8206
  AnalyticsCollector,
7584
8207
  AnalyticsConfigSchema,
@@ -7612,6 +8235,7 @@ export {
7612
8235
  Logger,
7613
8236
  MODEL_SPECIFIC_ADDENDUM,
7614
8237
  MetaSchema,
8238
+ NegativeContextManager,
7615
8239
  OpenAIAdapter,
7616
8240
  PluginManager,
7617
8241
  PluginRegistry,
@@ -7621,6 +8245,7 @@ export {
7621
8245
  RLMEngine,
7622
8246
  RLM_BASE_SYSTEM_PROMPT,
7623
8247
  ScopeManager,
8248
+ ShadowFileSystem,
7624
8249
  StackConfigSchema,
7625
8250
  SupportedLanguageSchema,
7626
8251
  TeamSync,
@@ -7632,6 +8257,7 @@ export {
7632
8257
  calculateCost,
7633
8258
  checkHealth,
7634
8259
  chunkCode,
8260
+ createAIGenerator,
7635
8261
  createAnthropicAdapter,
7636
8262
  createBlackboard,
7637
8263
  createContextAPI,
@@ -7639,6 +8265,7 @@ export {
7639
8265
  createGeminiClient,
7640
8266
  createInitialUserMessage,
7641
8267
  createLogger,
8268
+ createNegativeContextManager,
7642
8269
  createObservationMessage,
7643
8270
  createOpenAIAdapter,
7644
8271
  createPluginManager,
@@ -7648,6 +8275,7 @@ export {
7648
8275
  createSandbox,
7649
8276
  createSandboxContext,
7650
8277
  createScopeManager,
8278
+ createShadowFS,
7651
8279
  createSubAgentResultMessage,
7652
8280
  createTrainingDataCollector,
7653
8281
  createWatchdog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextos/core",
3
- "version": "0.2.3",
3
+ "version": "0.4.1",
4
4
  "description": "Core engine for ContextOS - context management, parsing, and ranking",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",