@contextos/core 0.3.0 → 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 +404 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7592,13 +7592,371 @@ function setGlobalLogger(logger) {
7592
7592
  }
7593
7593
 
7594
7594
  // src/generator/index.ts
7595
- import { existsSync as existsSync14, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9, readFileSync as readFileSync13 } from "fs";
7596
- import { dirname as dirname4, join as join13, resolve as resolve2, normalize as normalize3 } from "path";
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;
7597
7952
  var AIGenerator = class {
7598
7953
  config = null;
7599
7954
  gemini = null;
7600
7955
  openai = null;
7956
+ anthropic = null;
7601
7957
  rootDir = process.cwd();
7958
+ shadowFS = null;
7959
+ negativeContext = null;
7602
7960
  constructor(projectDir) {
7603
7961
  this.rootDir = projectDir || process.cwd();
7604
7962
  }
@@ -7617,17 +7975,32 @@ var AIGenerator = class {
7617
7975
  if (isOpenAIAvailable()) {
7618
7976
  this.openai = createOpenAIAdapter();
7619
7977
  }
7620
- if (!this.gemini && !this.openai) {
7621
- throw new Error("No AI API key found. Set GEMINI_API_KEY or OPENAI_API_KEY environment variable.");
7978
+ if (isAnthropicAvailable()) {
7979
+ this.anthropic = createAnthropicAdapter();
7622
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);
7623
7986
  }
7624
7987
  /**
7625
7988
  * Generate code from a prompt
7626
7989
  */
7627
7990
  async generate(prompt, options = {}) {
7628
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
+ }
7629
8001
  const context = await this.buildContext();
7630
- const fullPrompt = this.createPrompt(prompt, context);
8002
+ const negativeRules = this.negativeContext?.generatePromptInjection() || "";
8003
+ const fullPrompt = this.createPrompt(prompt, context + negativeRules);
7631
8004
  let response;
7632
8005
  let tokensUsed = 0;
7633
8006
  try {
@@ -7645,6 +8018,17 @@ var AIGenerator = class {
7645
8018
  });
7646
8019
  response = result.content;
7647
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;
7648
8032
  } else {
7649
8033
  throw new Error("No AI model available");
7650
8034
  }
@@ -7687,9 +8071,9 @@ var AIGenerator = class {
7687
8071
  const parts = [];
7688
8072
  const prdPaths = ["prd.md", "PRD.md", "docs/prd.md", "README.md"];
7689
8073
  for (const prdPath of prdPaths) {
7690
- const fullPath = join13(this.rootDir, prdPath);
7691
- if (existsSync14(fullPath)) {
7692
- const content = readFileSync13(fullPath, "utf-8");
8074
+ const fullPath = join15(this.rootDir, prdPath);
8075
+ if (existsSync16(fullPath)) {
8076
+ const content = readFileSync15(fullPath, "utf-8");
7693
8077
  parts.push(`## ${prdPath}
7694
8078
 
7695
8079
  ${content}`);
@@ -7752,8 +8136,8 @@ Generate the files now:`;
7752
8136
  const filePath = pathOrLang.replace(/\\/g, "/");
7753
8137
  const ext = filePath.split(".").pop() || "";
7754
8138
  const language = this.getLanguageFromExtension(ext);
7755
- const fullPath = join13(this.rootDir, filePath);
7756
- const isNew = !existsSync14(fullPath);
8139
+ const fullPath = join15(this.rootDir, filePath);
8140
+ const isNew = !existsSync16(fullPath);
7757
8141
  files.push({
7758
8142
  path: filePath,
7759
8143
  content: content.trim(),
@@ -7798,16 +8182,16 @@ Generate the files now:`;
7798
8182
  console.warn(`Skipping file outside project root: ${file.path}`);
7799
8183
  continue;
7800
8184
  }
7801
- const dir = dirname4(fullPath);
7802
- if (!existsSync14(dir)) {
7803
- mkdirSync9(dir, { recursive: true });
8185
+ const dir = dirname5(fullPath);
8186
+ if (!existsSync16(dir)) {
8187
+ mkdirSync10(dir, { recursive: true });
7804
8188
  }
7805
8189
  if (!file.isNew && options.backupBeforeOverwrite) {
7806
8190
  const backupPath = `${fullPath}.bak`;
7807
- const existingContent = readFileSync13(fullPath, "utf-8");
7808
- writeFileSync9(backupPath, existingContent);
8191
+ const existingContent = readFileSync15(fullPath, "utf-8");
8192
+ writeFileSync11(backupPath, existingContent);
7809
8193
  }
7810
- writeFileSync9(fullPath, file.content, "utf-8");
8194
+ writeFileSync11(fullPath, file.content, "utf-8");
7811
8195
  written.push(file);
7812
8196
  }
7813
8197
  return written;
@@ -7851,6 +8235,7 @@ export {
7851
8235
  Logger,
7852
8236
  MODEL_SPECIFIC_ADDENDUM,
7853
8237
  MetaSchema,
8238
+ NegativeContextManager,
7854
8239
  OpenAIAdapter,
7855
8240
  PluginManager,
7856
8241
  PluginRegistry,
@@ -7860,6 +8245,7 @@ export {
7860
8245
  RLMEngine,
7861
8246
  RLM_BASE_SYSTEM_PROMPT,
7862
8247
  ScopeManager,
8248
+ ShadowFileSystem,
7863
8249
  StackConfigSchema,
7864
8250
  SupportedLanguageSchema,
7865
8251
  TeamSync,
@@ -7879,6 +8265,7 @@ export {
7879
8265
  createGeminiClient,
7880
8266
  createInitialUserMessage,
7881
8267
  createLogger,
8268
+ createNegativeContextManager,
7882
8269
  createObservationMessage,
7883
8270
  createOpenAIAdapter,
7884
8271
  createPluginManager,
@@ -7888,6 +8275,7 @@ export {
7888
8275
  createSandbox,
7889
8276
  createSandboxContext,
7890
8277
  createScopeManager,
8278
+ createShadowFS,
7891
8279
  createSubAgentResultMessage,
7892
8280
  createTrainingDataCollector,
7893
8281
  createWatchdog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contextos/core",
3
- "version": "0.3.0",
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",