11import * as os from 'os'
22
33import { type CustomToolDefinition } from './custom-tool'
4+ import { getFileTokenScores } from '../../packages/code-map/src/parse'
45import { getInitialSessionState } from '../../common/src/types/session-state'
56
67import type { ServerAction } from '../../common/src/actions'
78import type { AgentDefinition } from '../../common/src/templates/initial-agents-dir/types/agent-definition'
89import type { CodebuffMessage } from '../../common/src/types/message'
910import type { SessionState } from '../../common/src/types/session-state'
10- import type { CustomToolDefinitions } from '../../common/src/util/file'
11+ import type {
12+ CustomToolDefinitions ,
13+ FileTreeNode ,
14+ } from '../../common/src/util/file'
1115
1216export type RunState = {
1317 sessionState : SessionState
1418 toolResults : ServerAction < 'prompt-response' > [ 'toolResults' ]
1519}
1620
17- export function initialSessionState (
21+ export async function initialSessionState (
1822 cwd : string ,
1923 options : {
20- // TODO: Parse projectFiles into fileTree, fileTokenScores, tokenCallers
2124 projectFiles ?: Record < string , string >
2225 knowledgeFiles ?: Record < string , string >
2326 agentDefinitions ?: AgentDefinition [ ]
@@ -76,12 +79,35 @@ export function initialSessionState(
7679 ] ) ,
7780 )
7881
82+ // Generate file tree and token scores from projectFiles
83+ const filePaths = Object . keys ( projectFiles ) . sort ( )
84+
85+ // Build hierarchical file tree with directories
86+ const fileTree = buildFileTree ( filePaths )
87+ let fileTokenScores = { }
88+ let tokenCallers = { }
89+
90+ if ( filePaths . length > 0 ) {
91+ try {
92+ const tokenData = await getFileTokenScores (
93+ cwd ,
94+ filePaths ,
95+ ( filePath : string ) => projectFiles [ filePath ] || null ,
96+ )
97+ fileTokenScores = tokenData . tokenScores
98+ tokenCallers = tokenData . tokenCallers
99+ } catch ( error ) {
100+ // If token scoring fails, continue with empty scores
101+ console . warn ( 'Failed to generate parsed symbol scores:' , error )
102+ }
103+ }
104+
79105 const initialState = getInitialSessionState ( {
80106 projectRoot : cwd ,
81107 cwd,
82- fileTree : [ ] ,
83- fileTokenScores : { } ,
84- tokenCallers : { } ,
108+ fileTree,
109+ fileTokenScores,
110+ tokenCallers,
85111 knowledgeFiles,
86112 userKnowledgeFiles : { } ,
87113 agentTemplates : processedAgentTemplates ,
@@ -111,7 +137,7 @@ export function initialSessionState(
111137 return initialState
112138}
113139
114- export function generateInitialRunState ( {
140+ export async function generateInitialRunState ( {
115141 cwd,
116142 projectFiles,
117143 knowledgeFiles,
@@ -125,9 +151,9 @@ export function generateInitialRunState({
125151 agentDefinitions ?: AgentDefinition [ ]
126152 customToolDefinitions ?: CustomToolDefinition [ ]
127153 maxAgentSteps ?: number
128- } ) : RunState {
154+ } ) : Promise < RunState > {
129155 return {
130- sessionState : initialSessionState ( cwd , {
156+ sessionState : await initialSessionState ( cwd , {
131157 projectFiles,
132158 knowledgeFiles,
133159 agentDefinitions,
@@ -167,3 +193,74 @@ export function withMessageHistory({
167193
168194 return newRunState
169195}
196+
197+ /**
198+ * Builds a hierarchical file tree from a flat list of file paths
199+ */
200+ function buildFileTree ( filePaths : string [ ] ) : FileTreeNode [ ] {
201+ const tree : Record < string , FileTreeNode > = { }
202+
203+ // Build the tree structure
204+ for ( const filePath of filePaths ) {
205+ const parts = filePath . split ( '/' )
206+
207+ for ( let i = 0 ; i < parts . length ; i ++ ) {
208+ const currentPath = parts . slice ( 0 , i + 1 ) . join ( '/' )
209+ const isFile = i === parts . length - 1
210+
211+ if ( ! tree [ currentPath ] ) {
212+ tree [ currentPath ] = {
213+ name : parts [ i ] ,
214+ type : isFile ? 'file' : 'directory' ,
215+ filePath : currentPath ,
216+ children : isFile ? undefined : [ ] ,
217+ }
218+ }
219+ }
220+ }
221+
222+ // Organize into hierarchical structure
223+ const rootNodes : FileTreeNode [ ] = [ ]
224+ const processed = new Set < string > ( )
225+
226+ for ( const [ path , node ] of Object . entries ( tree ) ) {
227+ if ( processed . has ( path ) ) continue
228+
229+ const parentPath = path . substring ( 0 , path . lastIndexOf ( '/' ) )
230+ if ( parentPath && tree [ parentPath ] ) {
231+ // This node has a parent, add it to parent's children
232+ const parent = tree [ parentPath ]
233+ if (
234+ parent . children &&
235+ ! parent . children . some ( ( child ) => child . filePath === path )
236+ ) {
237+ parent . children . push ( node )
238+ }
239+ } else {
240+ // This is a root node
241+ rootNodes . push ( node )
242+ }
243+ processed . add ( path )
244+ }
245+
246+ // Sort function for nodes
247+ function sortNodes ( nodes : FileTreeNode [ ] ) : void {
248+ nodes . sort ( ( a , b ) => {
249+ // Directories first, then files
250+ if ( a . type !== b . type ) {
251+ return a . type === 'directory' ? - 1 : 1
252+ }
253+ return a . name . localeCompare ( b . name )
254+ } )
255+
256+ // Recursively sort children
257+ for ( const node of nodes ) {
258+ if ( node . children ) {
259+ sortNodes ( node . children )
260+ }
261+ }
262+ }
263+
264+ sortNodes ( rootNodes )
265+ return rootNodes
266+ }
0 commit comments