@codeledger/core-engine 0.1.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/LICENSE +27 -0
- package/bin/codeledger-core.cjs +21 -0
- package/bin/codeledger-core.cjs.LEGAL.txt +0 -0
- package/dist/bridge.d.ts +42 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +74 -0
- package/dist/bridge.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Intelligent Context AI, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Note: This license applies to the CLI wrapper, types, repository scanning,
|
|
26
|
+
instrumentation, harness, and report packages (the "Plugin"). The scoring
|
|
27
|
+
engine (packages/core-engine/bin/) is licensed separately under LICENSE-CORE.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* CodeLedger Core Engine — Proprietary scoring binary */
|
|
3
|
+
/* Copyright (c) 2025 Intelligent Context AI, Inc. */
|
|
4
|
+
/* Free for individual use. Commercial use requires a license. */
|
|
5
|
+
/* No network calls. No telemetry. Runs entirely local. */
|
|
6
|
+
/* Run with --license or --explain-architecture for details. */
|
|
7
|
+
"use strict";var te=require("node:fs");var G=new Set(["the","a","an","and","or","but","in","on","at","to","for","of","with","by","from","is","it","that","this","be","as","are","was","were","been","being","have","has","had","do","does","did","will","would","could","should","may","might","shall","can","need","must","not","no","if","then","else","when","up","out","so","than","too","very","just","about","into","over","after","before","between","under","above","all","each","every","both","few","more","most","some","any","add","fix","update","change","make","use","get","set","find","check","look","see","know","think","want","try","run","show","help","work","take","give","tell","call","go","come","keep","let","begin","start","end","stop","ready","live","done","good","new","old","big","small"]),ne=new Set(["test","tests","testing","build","lint","debug","deploy","review","audit","evaluate","assess","analyze","examine","application","app","project","codebase","code","repo"]),se=["ready for","go live","production ready","readiness","find out","evaluate","assess","audit","review the","status of","state of","quality of","health of","overall","end to end","full","comprehensive"].map(e=>new RegExp(`\\b${e}\\b`)),oe=["replace","everywhere","all files","every file","migrate","files that import","files that use","across all","update all","rename all"].map(e=>new RegExp(`\\b${e}\\b`)),M=50,re=[".sql",".proto",".prisma",".graphql",".gql"],ie=["openapi","swagger","schema","migrations"];function N(e){let t=e.toLowerCase(),n=se.some(r=>r.test(t))?new Set([...G,...ne]):G,s=t.split(/[^\p{L}\p{N}]+/u).filter(r=>r.length>1&&!n.has(r));return[...new Set(s)]}function H(e,t,o){let n=new Map;for(let s of e){let r=0;for(let i of t.files)(i.path.toLowerCase().includes(s)||i.content_keywords?.some(p=>p===s||p===s+"s"||s===p+"s"))&&r++;if(r===0){n.set(s,0);continue}let c=1/Math.sqrt(Math.max(1,r));s.length<=2&&(c=Math.min(c,.35)),r>o&&(c=Math.min(c,.35)),c=Math.max(.35,Math.min(1,c)),n.set(s,c)}return n}function ae(e){let t=e.toLowerCase();return oe.some(o=>o.test(t))}function ce(e,t){let o=[];for(let n of t.files){let c=(n.path.toLowerCase().split("/").pop()?.replace(/\.\w+$/,"")??"").split(/[-_]/);for(let i of e)if(c.some(f=>f===i||f===i+"s"||i===f+"s")){o.push(n.path);break}}return o}function W(e,t,o){let n=new Set,s=new Set,r=N(e),c=H(r,t,o.hot_zone_count);for(let a of t.files){let g=a.path.toLowerCase();for(let w of r){if(g.includes(w)){n.add(a.path);break}if(a.content_keywords?.some(l=>l===w||l===w+"s"||w===l+"s")){n.add(a.path);break}}}let i=(o.default_budget.max_files??1/0)<=15,p=t.churn.slice(0,o.hot_zone_count);for(let a of p)t.files.some(g=>g.path===a.path)&&(i&&!n.has(a.path)||n.add(a.path));let f=[...n];for(let a of f)ue(a,t,n,o.dependency_depth,o.dependency_cap_per_seed);if(ae(e)){let a=ce(r,t);for(let g of a){if(n.size>=M)break;let w=t.dep_graph.dependents[g]??[];for(let l of w){if(n.size>=M)break;n.add(l),s.add(l)}if(n.size<M)for(let l of w){if(n.size>=M)break;let y=t.dep_graph.dependents[l]??[];for(let u of y){if(n.size>=M)break;n.add(u),s.add(u)}}}}let d=[...n];for(let a of t.test_map)d.includes(a.source_file)&&n.add(a.test_file),d.includes(a.test_file)&&n.add(a.source_file);let h=le(t.files,o.contract_patterns);if(h.length>0&&n.size>0){let a=new Set([...n].map(g=>g.split("/").slice(0,-1).join("/")));for(let g of h){let w=g.split("/").slice(0,-1).join("/");(a.has(w)||[...a].some(y=>y.startsWith(w+"/")||w.startsWith(y+"/")))&&n.add(g)}}return{candidates:n,tokenWeights:c,fanoutFiles:s}}function le(e,t){let o=[];for(let n of e){let s=n.extension.toLowerCase(),r=n.path.toLowerCase();if(re.includes(s)){o.push(n.path);continue}if(t?.some(i=>r.includes(i.toLowerCase()))){o.push(n.path);continue}let c=r.split("/").pop()??"";ie.some(i=>c.startsWith(i))&&o.push(n.path)}return o}function ue(e,t,o,n,s){let r=[e],c=0;for(let i=0;i<n&&c<s;i++){let p=[];for(let f of r){let d=t.dep_graph.imports[f]??[];for(let a of d)!o.has(a)&&c<s&&(o.add(a),p.push(a),c++);let h=t.dep_graph.dependents[f]??[];for(let a of h)!o.has(a)&&c<s&&(o.add(a),p.push(a),c++)}r=p}}var pe=["error","exception","validation","validator","schema"];function fe(e){return(e.path.toLowerCase().split("/").pop()?.replace(/\.\w+$/,"")??"").split(/[-_]/).some(r=>pe.some(c=>r===c||r===c+"s"))?1:0}function K(e,t,o,n,s){let r=e.path.toLowerCase(),c=e.content_keywords??[],p=(r.split("/").pop()?.replace(/\.\w+$/,"")??"").split(/[-_]/),f=0,d=0,h=!1;for(let _ of t){let S=s?.get(_)??1;if(S!==0)if(d+=S,r.includes(_)){let C=p.some(B=>B===_||B===_+"s"||_===B+"s");f+=S*(C?3:1)}else c.some(C=>C===_||C===_+"s"||_===C+"s")&&(f+=S*.5,h=!0)}let a=d>0?Math.min(1,f/(d*1.5)):0,g=o.dep_graph.imports[e.path]?.length??0,w=o.dep_graph.dependents[e.path]?.length??0,l=g+w,y=Math.max(1,...o.files.map(_=>{let S=o.dep_graph.imports[_.path]?.length??0,C=o.dep_graph.dependents[_.path]?.length??0;return S+C})),u=l/y,x=o.churn.find(_=>_.path===e.path),b=x?.time_decayed_score??0,T=x?.days_since_last_touch??999,v=T<=7?1:T<=14?.8:T<=30?.5:T<=60?.2:0,R=o.test_map.filter(_=>_.source_file===e.path||_.test_file===e.path),E=0;R.length>0&&(E=.5,E+=Math.min(.5,R.length*.25));let L=Math.max(0,Math.min(1,e.lines/1e3)),D=n?.success_files.has(e.path)?1:0,m=n?.fail_files.has(e.path)?1:0,k=fe(e);return{keyword:a,centrality:u,churn:b,recent_touch:v,test_relevance:E,size_penalty:L,success_prior:D,fail_prior:m,error_infrastructure:k,_hasContentMatch:h}}function J(e){let t;return e.keyword>=.3?t=1:e.keyword>0?t=.5:e.test_relevance>0||e.centrality>.1?t=.4:t=0,t===1?e:{...e,churn:e.churn*t,recent_touch:e.recent_touch*t}}function X(e,t){return e.keyword*t.keyword+e.centrality*t.centrality+e.churn*t.churn+e.recent_touch*t.recent_touch+e.test_relevance*t.test_relevance-e.size_penalty*t.size_penalty+e.success_prior*t.success_prior-e.fail_prior*t.fail_prior+e.error_infrastructure*(t.error_infrastructure??.08)}function Z(e){let t=[];return e.keyword>0&&(t.push("keyword_match"),e._hasContentMatch&&t.push("content_match")),e.centrality>.3&&t.push("dependency_neighbor"),e.churn>.3&&t.push("high_churn"),e.recent_touch>.5&&t.push("recent_touch"),e.test_relevance>0&&t.push("test_relevant"),e.size_penalty>.5&&t.push("size_penalty"),e.success_prior>0&&t.push("success_prior"),e.error_infrastructure>0&&t.push("error_infrastructure"),t}function I(e,t,o,n,s,r,c){let i=new Map(o.files.map(f=>[f.path,f])),p=[];for(let f of e){let d=i.get(f);if(!d)continue;let h=K(d,t,o,s,r),g={...J(h),_hasContentMatch:h._hasContentMatch},w=X(g,n),l=Z(g);c?.has(f)&&(w+=.25,l.includes("dependent_neighbor")||l.push("dependent_neighbor")),p.push({path:f,score:w,features:g,reasons:l})}return p.sort((f,d)=>d.score-f.score),p}function F(e){return e*4}function de(e){return e.some(t=>t.reasons.includes("keyword_match")||t.reasons.includes("test_relevant"))}function P(e,t,o,n){return!!(t.max_files&&e.files.length>=t.max_files||t.tokens&&e.totalTokens>=t.tokens||n>0&&e.cumulativeScore/n>=o&&de(e.files))}var A=require("node:fs"),V=require("node:path"),he=10*1024*1024;function O(e,t,o,n,s){let r=(0,V.join)(e,t),c;try{let l=(0,A.statSync)(r).size;if(l>he)return{content:`// File too large (${Math.round(l/1024)}KB), skipped`,spans:null,lineCount:1};c=(0,A.readFileSync)(r,"utf-8")}catch{return{content:"",spans:null,lineCount:0}}let i=c.split(`
|
|
8
|
+
`);if(i.length<=n)return{content:c,spans:null,lineCount:i.length};let p=[],f=new Set;for(let l=0;l<i.length;l++){let y=i[l];if(me(y)){let u=Math.max(0,l-1),x=Math.min(i.length-1,l+1);ge(y)&&(x=_e(i,l,20)),p.push({start_line:u+1,end_line:x+1,reason:"type_surface"});for(let b=u;b<=x;b++)f.add(b)}}for(let l=0;l<i.length;l++){let y=i[l];if(we(y)&&!f.has(l)){let u=Math.max(0,l-1),x=Math.min(i.length-1,l+2);p.push({start_line:u+1,end_line:x+1,reason:"export_signature"});for(let b=u;b<=x;b++)f.add(b)}}for(let l=0;l<i.length;l++){let y=i[l].toLowerCase();for(let u of o)if(y.includes(u)){let x=Math.max(0,l-s),b=Math.min(i.length-1,l+s);p.push({start_line:x+1,end_line:b+1,reason:`keyword_match:${u}`});for(let T=x;T<=b;T++)f.add(T);break}}let d=[...f].sort((l,y)=>l-y),h=[],a=-2;for(let l of d)l>a+1&&h.push(`
|
|
9
|
+
// ... (lines ${a+2}\u2013${l} omitted) ...
|
|
10
|
+
`),h.push(i[l]),a=l;let g=h.join(`
|
|
11
|
+
`),w=ye(p);return{content:g,spans:w,lineCount:d.length}}function me(e){let o=e.trim().replace(/^export\s+(default\s+)?/,"").replace(/^declare\s+/,"");return o.startsWith("interface ")||o.startsWith("type ")||o.startsWith("enum ")||o.startsWith("function ")&&!o.includes("=>")||o.startsWith("abstract class ")||o.startsWith("class ")}function ge(e){let t=e.trim();return t.includes("{")&&!t.includes("}")}function _e(e,t,o){let n=0,s=Math.min(e.length-1,t+o);for(let r=t;r<=s;r++){let c=e[r];for(let i of c)i==="{"&&n++,i==="}"&&n--;if(n<=0&&r>t)return r}return s}function we(e){let t=e.trim();return t.startsWith("export ")&&(t.includes("function ")||t.includes("class ")||t.includes("interface ")||t.includes("type ")||t.includes("const ")||t.includes("enum "))}function ye(e){if(e.length===0)return[];let t=[...e].sort((n,s)=>n.start_line-s.start_line),o=[t[0]];for(let n=1;n<t.length;n++){let s=t[n],r=o[o.length-1];s.start_line<=r.end_line+1?(r.end_line=Math.max(r.end_line,s.end_line),r.reason=`${r.reason}+${s.reason}`):o.push({...s})}return o}var ee=require("node:crypto");function z(e,t,o){let n=[],s=0;if(e.length===0)return{level:"low",score:0,reasons:["Empty bundle \u2014 no files matched"]};let r=e.slice(0,5),c=r.filter(h=>h.reasons.includes("keyword_match")),i=c.length/r.length;if(i>=.6?(s+=.35,n.push(`Strong keyword signal: ${c.length}/${r.length} top files match task keywords`)):i>0?(s+=.15,n.push(`Weak keyword signal: only ${c.length}/${r.length} top files match task keywords`)):n.push("No keyword matches in top 5 files \u2014 selection driven by churn/centrality"),e.length>=2){let h=e[0].score,a=e[1].score;h>0&&a>0&&((h-a)/h>=.3?(s+=.2,n.push("Clear top-ranked file with significant score gap")):s+=.1)}let p=e.reduce((h,a)=>h+a.reasons.length,0)/e.length;p>=2?(s+=.2,n.push("Files selected for multiple independent reasons")):p>=1.5&&(s+=.1),o>=3?s+=.15:o>=2?s+=.1:n.push("Task description has few discriminating keywords \u2014 consider being more specific"),e.filter(h=>!h.reasons.every(a=>a==="test_relevant")).length>=e.length*.5&&(s+=.1);let d;return s>=.65?d="high":s>=.35?d="medium":d="low",{level:d,score:Math.round(s*100)/100,reasons:n}}var Y=require("node:fs"),Q=require("node:path");function U(e,t,o,n,s){let r=new Map;for(let d of t){let h=o.imports[d]??[];for(let a of h)t.has(a)||r.set(a,(r.get(a)??0)+1)}if(r.size===0)return[];let c=[...r.entries()].sort((d,h)=>h[1]-d[1]).slice(0,n),i=[],p=0,f=4;for(let[d]of c){let h=(0,Q.join)(e,d),a;try{a=(0,Y.readFileSync)(h,"utf-8")}catch{continue}let g=ke(a,d);if(!g)continue;let l=g.split(`
|
|
12
|
+
`).length*f;if(p+l>s)continue;let y=["interface_stub"];i.push({path:d,score:0,reasons:y,excerpt_spans:null,content:g,token_estimate:l,is_stub:!0}),p+=l}return i}function ke(e,t){let o=e.split(`
|
|
13
|
+
`),n=[],s=0;for(;s<o.length;){let r=o[s],c=r.trim();if(xe(c)){if(c.includes("{")&&!c.includes("}")){let i=Se(o,s);n.push(...i),s+=i.length;continue}n.push(r),s++;continue}if(be(c)){let i=Ce(o,s);n.push(i),s=Te(o,s);continue}s++}return n.length===0?null:`// Interface stub (auto-generated by CodeLedger)
|
|
14
|
+
${n.join(`
|
|
15
|
+
`)}`}function xe(e){let t=e.startsWith("export ")?e.slice(7).replace(/^default\s+/,""):null;return t?t.startsWith("interface ")||t.startsWith("type ")||t.startsWith("enum "):!1}function be(e){return e.startsWith("export ")?e.slice(7).replace(/^default\s+/,"").replace(/^async\s+/,"").startsWith("function "):!1}function Se(e,t){let o=[],n=0;for(let s=t;s<e.length&&s<t+50;s++){let r=e[s];o.push(r);for(let c of r)c==="{"&&n++,c==="}"&&n--;if(n<=0&&s>t)break}return o}function Ce(e,t){let o="";for(let n=t;n<e.length&&n<t+5;n++){let s=e[n],r=s.indexOf("{");if(r>=0){o+=s.slice(0,r).trimEnd()+";";break}o+=s+`
|
|
16
|
+
`}return o||e[t]}function Te(e,t){let o=0;for(let n=t;n<e.length;n++){let s=e[n];for(let r of s)r==="{"&&o++,r==="}"&&o--;if(o<=0&&n>t)return n+1}return e.length}var Ee=[{pattern:"\\bfs\\.exists\\b",message:"fs.exists is deprecated \u2014 use fs.access() or fs.stat()"},{pattern:"\\bfs\\.existsSync\\b",message:"fs.existsSync is deprecated in async contexts \u2014 prefer fs.accessSync()"},{pattern:"\\bnew Buffer\\(",message:"Buffer() constructor is deprecated \u2014 use Buffer.from() or Buffer.alloc()"},{pattern:"\\brequire\\('url'\\)\\.parse",message:"url.parse() is deprecated \u2014 use new URL() constructor"},{pattern:"\\burl\\.parse\\(",message:"url.parse() is deprecated \u2014 use new URL() constructor"},{pattern:"\\butil\\.isArray\\b",message:"util.isArray is deprecated \u2014 use Array.isArray()"},{pattern:"\\butil\\.isDate\\b",message:"util.isDate is deprecated \u2014 use instanceof Date"},{pattern:"\\butil\\.isRegExp\\b",message:"util.isRegExp is deprecated \u2014 use instanceof RegExp"},{pattern:"\\bcomponentWillMount\\b",message:"componentWillMount is deprecated \u2014 use componentDidMount or useEffect"},{pattern:"\\bcomponentWillReceiveProps\\b",message:"componentWillReceiveProps is deprecated \u2014 use getDerivedStateFromProps or useEffect"},{pattern:"\\bcomponentWillUpdate\\b",message:"componentWillUpdate is deprecated \u2014 use getSnapshotBeforeUpdate"},{pattern:"\\bReact\\.createClass\\b",message:"React.createClass is removed \u2014 use class components or function components"},{pattern:"\\bReactDOM\\.render\\(",message:"ReactDOM.render is deprecated in React 18+ \u2014 use createRoot().render()"},{pattern:"@ts-ignore",message:"Prefer @ts-expect-error over @ts-ignore for better error tracking"}];function $(e,t){let o=[...Ee,...t??[]],n=[],s=e.split(`
|
|
17
|
+
`);for(let r=0;r<s.length;r++){let c=s[r],i=c.trim();if(!(i.startsWith("//")||i.startsWith("*")||i.startsWith("/*")))for(let p of o){let f;try{f=new RegExp(p.pattern)}catch{continue}let d=f.exec(c);d&&n.push({line:r+1,matched:d[0],message:p.message})}}return n}function j(e){let{taskText:t,repoIndex:o,selectorConfig:n,budget:s=n.default_budget,sufficiencyThreshold:r=n.sufficiency_threshold,ledgerStats:c,explain:i=!1}=e,p=N(t),{candidates:f,tokenWeights:d,fanoutFiles:h}=W(t,o,n),a=I(f,p,o,n.weights,c,d,h),g=a.reduce((m,k)=>m+Math.max(0,k.score),0),l=(s.max_files??1/0)<=15?2:0,y={...s,max_files:s.max_files?s.max_files-l:void 0},u={files:[],totalTokens:0,cumulativeScore:0},x={};for(let m of a){if(P(u,y,r,g))break;let k=O(o.root,m.path,p,n.excerpt_full_file_max_lines,n.excerpt_window_lines),_=F(k.lineCount);if(s.tokens&&u.totalTokens+_>s.tokens*1.1&&u.files.length>0)break;let S=$(k.content,n.deprecation_rules),C={path:m.path,score:Math.round(m.score*1e3)/1e3,reasons:m.reasons,excerpt_spans:k.spans,content:k.content,token_estimate:_,deprecation_warnings:S.length>0?S:void 0};u.files.push(C),u.totalTokens+=_,u.cumulativeScore+=Math.max(0,m.score),i&&(x[m.path]=m.features)}let b=new Set(u.files.map(m=>m.path)),T=new Map(u.files.map(m=>[m.path,m.score])),v=[];for(let m of o.test_map)b.has(m.source_file)&&!b.has(m.test_file)&&v.push({testPath:m.test_file,sourceScore:T.get(m.source_file)??0});v.sort((m,k)=>k.sourceScore-m.sourceScore);for(let{testPath:m}of v){if(s.max_files&&u.files.length>=s.max_files)break;let k=O(o.root,m,p,n.excerpt_full_file_max_lines,n.excerpt_window_lines),_=F(k.lineCount);if(s.tokens&&u.totalTokens+_>s.tokens*1.1)continue;let S=a.find(C=>C.path===m);u.files.push({path:m,score:S?Math.round(S.score*1e3)/1e3:0,reasons:S?.reasons??["test_relevant"],excerpt_spans:k.spans,content:k.content,token_estimate:_}),u.totalTokens+=_}let R=new Set(u.files.map(m=>m.path)),E=(s.tokens??1/0)-u.totalTokens,L=Math.max(0,(s.max_files??25)-u.files.length);if(L>0&&E>0){let m=U(o.root,R,o.dep_graph,Math.min(L,5),Math.min(E,1e3));for(let k of m)u.files.push(k),u.totalTokens+=k.token_estimate}let D=z(u.files,u.cumulativeScore,p.length);return{bundle_id:`bnd_${(0,ee.randomUUID)().slice(0,12)}`,task:t,budget:s,sufficiency_threshold:r,cumulative_score:Math.round(u.cumulativeScore*1e3)/1e3,files:u.files,total_tokens:u.totalTokens,generated_at:new Date().toISOString(),confidence:D,explain:i?x:void 0}}var q="0.1.0";function ve(e){return e.includes("--version")?(process.stdout.write(`codeledger-core ${q}
|
|
18
|
+
`),!0):e.includes("--license")?(process.stdout.write([`CodeLedger Core Engine v${q}`,"","Copyright (c) 2025 Intelligent Context AI, Inc.","","This binary is licensed under the CodeLedger Core License:"," - FREE for individual developer use"," - FREE for open-source projects"," - Commercial and CI/CD usage requires a CodeLedger Pro license","","Contact: team@contextecf.com","Details: https://contextecf.com/license","","Privacy: This engine runs entirely locally. No network calls,","no telemetry, no data collection. Your code never leaves your machine.",""].join(`
|
|
19
|
+
`)),!0):e.includes("--explain-architecture")?(process.stdout.write([`CodeLedger Core Engine v${q} \u2014 Architecture`,"","The engine selects the most relevant files for a coding task using","a deterministic, multi-signal scoring algorithm:",""," 1. CANDIDATE GENERATION (five-stage pipeline)"," - Keyword matching with IDF weighting"," - Hot-zone inclusion from git churn history"," - Dependency graph expansion (imports + dependents)"," - Fan-out detection for cross-cutting changes"," - Test neighborhood pairing",""," 2. SCORING (weighted multi-signal)"," Each candidate is scored across 9 signals:"," keyword, centrality, churn, recency, test_relevance,"," size_penalty, success_prior, fail_prior, error_infrastructure",""," 3. BOUNDED SELECTION"," Files are selected in score order until the token budget"," or file count ceiling is reached. A sufficiency threshold"," stops early when cumulative score plateaus.",""," 4. POST-PROCESSING"," - Deterministic test pairing (source \u2192 test)"," - Interface stub generation for adjacent dependencies"," - Confidence assessment (high / medium / low)"," - Smart excerpt extraction with keyword windowing","","Input: Task description + repo index + config","Output: Deterministic ContextBundle (same input = same rankings and content; metadata fields like bundle_id vary)","","Privacy: Runs entirely local. No network calls. No telemetry.",""].join(`
|
|
20
|
+
`)),!0):!1}async function Me(){let e=[];for await(let t of process.stdin)e.push(Buffer.isBuffer(t)?t:Buffer.from(t));return Buffer.concat(e).toString("utf-8")}async function Re(){if(ve(process.argv.slice(2)))return;let e=await Me(),t=JSON.parse(e),o;if(t.indexPath)o=JSON.parse((0,te.readFileSync)(t.indexPath,"utf-8"));else if(t.repoIndex)o=t.repoIndex;else throw new Error("Either indexPath or repoIndex must be provided");let n=t.ledgerStats?{success_files:new Set(t.ledgerStats.success_files),fail_files:new Set(t.ledgerStats.fail_files)}:void 0,s=j({taskText:t.task,repoIndex:o,selectorConfig:t.selectorConfig,budget:t.budget,explain:t.explain,ledgerStats:n});process.stdout.write(JSON.stringify(s))}Re().catch(e=>{process.stderr.write(JSON.stringify({error:String(e)})+`
|
|
21
|
+
`),process.exit(1)});
|
|
File without changes
|
package/dist/bridge.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core engine bridge — routes bundle generation through either the compiled
|
|
3
|
+
* binary (production) or a direct import (development).
|
|
4
|
+
*
|
|
5
|
+
* In production:
|
|
6
|
+
* The selector algorithm is compiled into a single minified binary at
|
|
7
|
+
* bin/codeledger-core.cjs. The bridge invokes it as a subprocess, passing
|
|
8
|
+
* configuration via stdin JSON and receiving the ContextBundle on stdout.
|
|
9
|
+
*
|
|
10
|
+
* In development:
|
|
11
|
+
* When the binary doesn't exist (monorepo dev workflow), the bridge
|
|
12
|
+
* dynamically imports @codeledger/selector, which resolves via the
|
|
13
|
+
* pnpm workspace.
|
|
14
|
+
*
|
|
15
|
+
* This separation ensures the scoring algorithm, weighting formulas, and
|
|
16
|
+
* expansion logic are never shipped as readable source in the public plugin.
|
|
17
|
+
*/
|
|
18
|
+
import type { ContextBundle, RepoIndex, SelectorConfig, Budget } from '@codeledger/types';
|
|
19
|
+
/** Ledger-derived success/fail file sets for prior-based scoring signals. */
|
|
20
|
+
export interface LedgerStats {
|
|
21
|
+
success_files: Set<string>;
|
|
22
|
+
fail_files: Set<string>;
|
|
23
|
+
}
|
|
24
|
+
export interface EngineSelectOptions {
|
|
25
|
+
taskText: string;
|
|
26
|
+
repoIndex: RepoIndex;
|
|
27
|
+
selectorConfig: SelectorConfig;
|
|
28
|
+
budget?: Budget;
|
|
29
|
+
explain?: boolean;
|
|
30
|
+
/** Path to the repo index file on disk (avoids piping large JSON via stdin) */
|
|
31
|
+
indexPath?: string;
|
|
32
|
+
/** Ledger-derived prior data. When omitted, success_prior and fail_prior score as 0. */
|
|
33
|
+
ledgerStats?: LedgerStats;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate a deterministic context bundle.
|
|
37
|
+
*
|
|
38
|
+
* Routes through the compiled binary when available, otherwise falls back
|
|
39
|
+
* to a direct import of the selector package (development mode).
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildBundleViaEngine(opts: EngineSelectOptions): Promise<ContextBundle>;
|
|
42
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAK1F,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,CAQxB"}
|
package/dist/bridge.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core engine bridge — routes bundle generation through either the compiled
|
|
3
|
+
* binary (production) or a direct import (development).
|
|
4
|
+
*
|
|
5
|
+
* In production:
|
|
6
|
+
* The selector algorithm is compiled into a single minified binary at
|
|
7
|
+
* bin/codeledger-core.cjs. The bridge invokes it as a subprocess, passing
|
|
8
|
+
* configuration via stdin JSON and receiving the ContextBundle on stdout.
|
|
9
|
+
*
|
|
10
|
+
* In development:
|
|
11
|
+
* When the binary doesn't exist (monorepo dev workflow), the bridge
|
|
12
|
+
* dynamically imports @codeledger/selector, which resolves via the
|
|
13
|
+
* pnpm workspace.
|
|
14
|
+
*
|
|
15
|
+
* This separation ensures the scoring algorithm, weighting formulas, and
|
|
16
|
+
* expansion logic are never shipped as readable source in the public plugin.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync } from 'node:fs';
|
|
19
|
+
import { execFileSync } from 'node:child_process';
|
|
20
|
+
import { resolve, dirname } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const CORE_BINARY = resolve(__dirname, '..', 'bin', 'codeledger-core.cjs');
|
|
24
|
+
/**
|
|
25
|
+
* Generate a deterministic context bundle.
|
|
26
|
+
*
|
|
27
|
+
* Routes through the compiled binary when available, otherwise falls back
|
|
28
|
+
* to a direct import of the selector package (development mode).
|
|
29
|
+
*/
|
|
30
|
+
export async function buildBundleViaEngine(opts) {
|
|
31
|
+
// Production: use compiled binary
|
|
32
|
+
if (existsSync(CORE_BINARY) && process.env.CODELEDGER_DEV !== '1') {
|
|
33
|
+
return invokeBinary(opts);
|
|
34
|
+
}
|
|
35
|
+
// Development: direct import
|
|
36
|
+
return directBuildBundle(opts);
|
|
37
|
+
}
|
|
38
|
+
function invokeBinary(opts) {
|
|
39
|
+
const input = JSON.stringify({
|
|
40
|
+
task: opts.taskText,
|
|
41
|
+
indexPath: opts.indexPath,
|
|
42
|
+
repoIndex: opts.indexPath ? undefined : opts.repoIndex,
|
|
43
|
+
selectorConfig: opts.selectorConfig,
|
|
44
|
+
budget: opts.budget,
|
|
45
|
+
explain: opts.explain ?? false,
|
|
46
|
+
ledgerStats: opts.ledgerStats
|
|
47
|
+
? {
|
|
48
|
+
success_files: [...opts.ledgerStats.success_files],
|
|
49
|
+
fail_files: [...opts.ledgerStats.fail_files],
|
|
50
|
+
}
|
|
51
|
+
: undefined,
|
|
52
|
+
});
|
|
53
|
+
const result = execFileSync('node', [CORE_BINARY], {
|
|
54
|
+
input,
|
|
55
|
+
encoding: 'utf-8',
|
|
56
|
+
timeout: 60_000,
|
|
57
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
58
|
+
});
|
|
59
|
+
return JSON.parse(result);
|
|
60
|
+
}
|
|
61
|
+
async function directBuildBundle(opts) {
|
|
62
|
+
// Dynamic import — only resolves in the dev workspace where
|
|
63
|
+
// @codeledger/selector exists as a workspace dependency.
|
|
64
|
+
const { buildBundle } = await import('@codeledger/selector');
|
|
65
|
+
return buildBundle({
|
|
66
|
+
taskText: opts.taskText,
|
|
67
|
+
repoIndex: opts.repoIndex,
|
|
68
|
+
selectorConfig: opts.selectorConfig,
|
|
69
|
+
budget: opts.budget,
|
|
70
|
+
explain: opts.explain,
|
|
71
|
+
ledgerStats: opts.ledgerStats,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,qBAAqB,CAAC,CAAC;AAoB3E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAyB;IAEzB,kCAAkC;IAClC,IAAI,UAAU,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG,EAAE,CAAC;QAClE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,6BAA6B;IAC7B,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,YAAY,CAAC,IAAyB;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,IAAI,EAAE,IAAI,CAAC,QAAQ;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS;QACtD,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,KAAK;QAC9B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC3B,CAAC,CAAC;gBACE,aAAa,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;gBAClD,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;aAC7C;YACH,CAAC,CAAC,SAAS;KACd,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE;QACjD,KAAK;QACL,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAkB,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAyB;IACxD,4DAA4D;IAC5D,yDAAyD;IACzD,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAS1D,CAAC;IAEF,OAAO,WAAW,CAAC;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codeledger/core-engine",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Protected scoring engine for CodeLedger — compiled binary bridge",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE-CORE",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/codeledgerECF/codeledger-blackbox.git",
|
|
10
|
+
"directory": "packages/core-engine"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"bin"
|
|
18
|
+
],
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@codeledger/types": "0.1.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.4.0",
|
|
32
|
+
"@codeledger/selector": "0.1.1"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc",
|
|
36
|
+
"build:core": "node ../../scripts/build-core.mjs",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"clean": "rm -rf dist bin/codeledger-core.cjs"
|
|
39
|
+
}
|
|
40
|
+
}
|