2026-03-09 18:44:19 +00:00
const http = require ( 'http' ) ;
const https = require ( 'https' ) ;
/ * *
* Phase 6 + 7 : LLM Prose Generator
* Generates human - readable prose for system documentation using Claude Sonnet .
* All structural analysis is deterministic — LLM is ONLY for prose formatting .
* /
const DEFAULT _URL = process . env . LLM _URL || 'http://192.168.86.11:8000/v1' ;
const DEFAULT _MODEL = process . env . LLM _MODEL || 'claude-sonnet-4.6' ;
const DEFAULT _API _KEY = process . env . LLM _API _KEY || 'my-super-secret-password-123' ;
/ * *
* Call an OpenAI - compatible chat completions API .
* /
function callLLM ( prompt , opts = { } ) {
const baseUrl = opts . url || DEFAULT _URL ;
const model = opts . model || DEFAULT _MODEL ;
const apiKey = opts . apiKey || DEFAULT _API _KEY ;
const maxTokens = opts . maxTokens || 1024 ;
const temperature = opts . temperature || 0.3 ;
return new Promise ( ( resolve , reject ) => {
const url = new URL ( '/v1/chat/completions' , baseUrl . replace ( /\/v1\/?$/ , '' ) ) ;
const body = JSON . stringify ( {
model ,
messages : [
{ role : 'system' , content : 'You are a senior software architect writing concise, precise technical documentation. Write in present tense. Be specific about domain logic, not syntax. No filler.' } ,
{ role : 'user' , content : prompt } ,
] ,
max _tokens : maxTokens ,
temperature ,
} ) ;
const client = url . protocol === 'https:' ? https : http ;
const req = client . request ( url , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' , 'Authorization' : ` Bearer ${ apiKey } ` } ,
} , ( res ) => {
let data = '' ;
res . on ( 'data' , c => data += c ) ;
res . on ( 'end' , ( ) => {
try {
const parsed = JSON . parse ( data ) ;
resolve ( parsed . choices ? . [ 0 ] ? . message ? . content || '' ) ;
} catch ( e ) {
reject ( new Error ( ` LLM parse error: ${ e . message } — raw: ${ data . substring ( 0 , 200 ) } ` ) ) ;
}
} ) ;
} ) ;
req . on ( 'error' , reject ) ;
req . setTimeout ( 120000 , ( ) => { req . destroy ( ) ; reject ( new Error ( 'LLM timeout (120s)' ) ) ; } ) ;
req . write ( body ) ;
req . end ( ) ;
} ) ;
}
/ * *
* Generate a prose overview for a subsystem .
* /
async function describeSubsystem ( sub , deps , llmOpts ) {
const depList = Object . entries ( deps )
. filter ( ( [ k ] ) => k . startsWith ( sub . name + '→' ) || k . endsWith ( '→' + sub . name ) )
. map ( ( [ k , v ] ) => ` ${ k } : ${ v . calls } calls, ${ v . imports } imports ` )
. slice ( 0 , 10 ) ;
const prompt = ` Write a 2-3 sentence technical overview of the " ${ sub . name } " subsystem.
Facts :
- Kind : $ { sub . kind }
- Files : $ { sub . files . length }
- Functions : $ { sub . entities . functions } , Classes : $ { sub . entities . classes } , Modules : $ { sub . entities . modules }
- Public exports : $ { sub . publicExports . slice ( 0 , 15 ) . join ( ', ' ) } $ { sub . publicExports . length > 15 ? ` (+ ${ sub . publicExports . length - 15 } more) ` : '' }
$ { depList . length > 0 ? ` - Dependencies: \n ${ depList . join ( '\n ' ) } ` : '- No cross-subsystem dependencies' }
Write ONLY the overview paragraph , no heading . ` ;
return callLLM ( prompt , llmOpts ) ;
}
/ * *
* Generate a prose narrative for a data flow trace .
* /
async function describeFlow ( flowResult , llmOpts ) {
const steps = flowResult . flow . slice ( 0 , 20 ) . map ( ( s , i ) =>
` ${ i + 1 } . [ ${ s . subsystem } ] ${ s . entity } ${ s . crossedVia ? ` (crosses via ${ s . crossedVia } ) ` : '' } `
) . join ( '\n' ) ;
const prompt = ` Write a 3-5 sentence narrative describing this data flow through the system.
Entry point : $ { flowResult . entryPoint }
Subsystem sequence : $ { flowResult . subsystemSequence . join ( ' → ' ) }
$ { flowResult . excludedNodes . length > 0 ? ` Excluded (high fan-in): ${ flowResult . excludedNodes . slice ( 0 , 5 ) . join ( ', ' ) } ` : '' }
$ { flowResult . cyclesDetected . length > 0 ? ` Cycles detected: ${ flowResult . cyclesDetected . length } ` : '' }
Steps :
$ { steps } $ { flowResult . flow . length > 20 ? ` \n ... (+ ${ flowResult . flow . length - 20 } more steps) ` : '' }
Write ONLY the narrative paragraph , no heading . Explain what happens when this entry point is triggered and how data moves across subsystem boundaries . ` ;
return callLLM ( prompt , llmOpts ) ;
}
/ * *
* Generate a prose description for a contract ( interface / type / enum ) .
* /
async function describeContract ( contract , xref , llmOpts ) {
const usedBy = xref ? . [ contract . name ] ? . usedBy || [ ] ;
let details = '' ;
2026-03-09 20:15:50 +00:00
2026-03-09 18:44:19 +00:00
if ( contract . type === 'Interface' && contract . fields ) {
details = ` Fields: ${ contract . fields . map ( f => ` ${ f . name } : ${ f . type } ` ) . join ( ', ' ) } ` ;
if ( contract . extends ) details += ` \n Extends: ${ contract . extends . join ( ', ' ) } ` ;
} else if ( contract . type === 'Enum' && contract . members ) {
details = ` Members: ${ contract . members . join ( ', ' ) } ` ;
2026-03-09 20:15:50 +00:00
} else if ( contract . type . startsWith ( 'Helm' ) ) {
// Helm contract types
if ( contract . fields ) {
details = ` Fields: ${ contract . fields . slice ( 0 , 20 ) . map ( f => ` ${ f . name } : ${ f . type } ` ) . join ( ', ' ) } ` ;
if ( contract . fields . length > 20 ) details += ` (+ ${ contract . fields . length - 20 } more) ` ;
}
2026-03-09 18:44:19 +00:00
}
2026-03-09 20:15:50 +00:00
const typeLabel = contract . type . startsWith ( 'Helm' ) ? ` Helm ${ contract . type . replace ( 'Helm' , '' ) . toLowerCase ( ) } contract ` : ` TypeScript ${ contract . type . toLowerCase ( ) } ` ;
const prompt = ` Write a 1-2 sentence description of this ${ typeLabel } .
2026-03-09 18:44:19 +00:00
Name : $ { contract . name }
Type : $ { contract . type }
Defined in : $ { contract . id }
Visibility : $ { contract . visibility }
$ { details }
$ { usedBy . length > 0 ? ` Used by subsystems: ${ usedBy . join ( ', ' ) } ` : 'Not referenced cross-subsystem' }
2026-03-09 20:15:50 +00:00
Write ONLY the description , no heading . Do not ask for more information . ` ;
2026-03-09 18:44:19 +00:00
return callLLM ( prompt , { ... llmOpts , maxTokens : 256 } ) ;
}
/ * *
* Generate a system - level architecture overview .
* /
async function describeArchitecture ( subsystems , crossCutting , stats , llmOpts ) {
const subList = subsystems . slice ( 0 , 20 ) . map ( s =>
` - ${ s . name } ( ${ s . kind } ): ${ s . entities . functions } functions, ${ s . files . length } files `
) . join ( '\n' ) ;
const prompt = ` Write a 4-6 sentence architecture overview for this software system.
Total subsystems : $ { subsystems . length }
Cross - cutting concerns : $ { crossCutting . join ( ', ' ) || 'none detected' }
Largest subsystems :
$ { subList }
Write ONLY the overview paragraph , no heading . Describe the high - level architecture , the role of cross - cutting concerns , and the overall system organization . ` ;
return callLLM ( prompt , { ... llmOpts , maxTokens : 512 } ) ;
}
module . exports = { callLLM , describeSubsystem , describeFlow , describeContract , describeArchitecture } ;