diff --git a/archdoc-cli/docs/architecture/files/.._test-project_src___init__.py.md b/archdoc-cli/docs/architecture/files/.._test-project_src___init__.py.md new file mode 100644 index 0000000..4303cc5 --- /dev/null +++ b/archdoc-cli/docs/architecture/files/.._test-project_src___init__.py.md @@ -0,0 +1,28 @@ +# File: ../test-project/src/__init__.py + +- **Module:** ../test-project/src/__init__.py +- **Defined symbols:** 0 +- **Imports:** 0 + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. + + +--- + +## Symbol details diff --git a/archdoc-cli/docs/architecture/files/.._test-project_src_core.py.md b/archdoc-cli/docs/architecture/files/.._test-project_src_core.py.md new file mode 100644 index 0000000..f470af7 --- /dev/null +++ b/archdoc-cli/docs/architecture/files/.._test-project_src_core.py.md @@ -0,0 +1,276 @@ +# File: ../test-project/src/core.py + +- **Module:** ../test-project/src/core.py +- **Defined symbols:** 6 +- **Imports:** 2 + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. +- sqlite3 +- requests + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. +- [DatabaseManager](.._test-project_src_core.py#DatabaseManager) +- [__init__](.._test-project_src_core.py#__init__) +- [connect](.._test-project_src_core.py#connect) +- [execute_query](.._test-project_src_core.py#execute_query) +- [fetch_external_data](.._test-project_src_core.py#fetch_external_data) +- [process_user_data](.._test-project_src_core.py#process_user_data) + + +--- + +## Symbol details + + + +### `DatabaseManager` +- **Kind:** Class +- **Signature:** `class DatabaseManager` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: yes +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `__init__` +- **Kind:** Function +- **Signature:** `def __init__(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `connect` +- **Kind:** Function +- **Signature:** `def connect(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: yes +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `execute_query` +- **Kind:** Function +- **Signature:** `def execute_query(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `fetch_external_data` +- **Kind:** Function +- **Signature:** `def fetch_external_data(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: yes +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `process_user_data` +- **Kind:** Function +- **Signature:** `def process_user_data(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 1 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + \ No newline at end of file diff --git a/archdoc-cli/docs/architecture/files/.._test-project_src_utils.py.md b/archdoc-cli/docs/architecture/files/.._test-project_src_utils.py.md new file mode 100644 index 0000000..4669b2a --- /dev/null +++ b/archdoc-cli/docs/architecture/files/.._test-project_src_utils.py.md @@ -0,0 +1,194 @@ +# File: ../test-project/src/utils.py + +- **Module:** ../test-project/src/utils.py +- **Defined symbols:** 4 +- **Imports:** 2 + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. +- json +- os + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. +- [load_config](.._test-project_src_utils.py#load_config) +- [save_config](.._test-project_src_utils.py#save_config) +- [get_file_size](.._test-project_src_utils.py#get_file_size) +- [format_bytes](.._test-project_src_utils.py#format_bytes) + + +--- + +## Symbol details + + + +### `load_config` +- **Kind:** Function +- **Signature:** `def load_config(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `save_config` +- **Kind:** Function +- **Signature:** `def save_config(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `get_file_size` +- **Kind:** Function +- **Signature:** `def get_file_size(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + + + + +### `format_bytes` +- **Kind:** Function +- **Signature:** `def format_bytes(...)` +- **Docstring:** `No documentation available` + +#### What it does + +extracted from AST + + +#### Relations + +**Outbound calls (best-effort):** + +**Inbound (used by) (best-effort):** + + +#### Integrations (heuristic) + +- HTTP: no +- DB: no +- Queue/Tasks: no + + +#### Risk / impact + +- fan-in: 0 +- fan-out: 0 +- cycle participant: no +- critical: no + + + +#### Manual notes + + + \ No newline at end of file diff --git a/archdoc-cli/docs/architecture/layout.md b/archdoc-cli/docs/architecture/layout.md new file mode 100644 index 0000000..954f410 --- /dev/null +++ b/archdoc-cli/docs/architecture/layout.md @@ -0,0 +1,18 @@ +# Repository layout + + +## Manual overrides +- `src/app/` — + + +--- + +## Detected structure + +> Generated. Do not edit inside this block. +| Path | Purpose | Link | +|------|---------|------| +| ../test-project/src/utils.py | Source file | [details](files/.._test-project_src_utils.py.md) | +| ../test-project/src/__init__.py | Source file | [details](files/.._test-project_src___init__.py.md) | +| ../test-project/src/core.py | Source file | [details](files/.._test-project_src_core.py.md) | + diff --git a/archdoc-cli/docs/architecture/modules/.._test-project_src___init__.py.md b/archdoc-cli/docs/architecture/modules/.._test-project_src___init__.py.md new file mode 100644 index 0000000..e324421 --- /dev/null +++ b/archdoc-cli/docs/architecture/modules/.._test-project_src___init__.py.md @@ -0,0 +1,27 @@ +# Module: ../test-project/src/__init__.py + +No summary available + +## Symbols + + +## Dependencies + +### Imports + +### Outbound Modules + +### Inbound Modules + +## Integrations + + + + +## Usage Examples + +```python +// Example usage of module functions +// TODO: Add real usage examples based on module analysis +``` + diff --git a/archdoc-cli/docs/architecture/modules/.._test-project_src_core.py.md b/archdoc-cli/docs/architecture/modules/.._test-project_src_core.py.md new file mode 100644 index 0000000..4ea4629 --- /dev/null +++ b/archdoc-cli/docs/architecture/modules/.._test-project_src_core.py.md @@ -0,0 +1,106 @@ +# Module: ../test-project/src/core.py + +No summary available + +## Symbols + +### DatabaseManager + +class DatabaseManager + +No documentation available + +**Type:** Class + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### __init__ + +def __init__(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### connect + +def connect(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### execute_query + +def execute_query(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### fetch_external_data + +def fetch_external_data(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### process_user_data + +def process_user_data(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 1 + + +## Dependencies + +### Imports +- sqlite3 +- requests + +### Outbound Modules + +### Inbound Modules + +## Integrations + +### Database Integrations +- DatabaseManager +- connect + +### HTTP/API Integrations +- fetch_external_data + + +## Usage Examples + +```python +// Example usage of module functions +// TODO: Add real usage examples based on module analysis +``` + diff --git a/archdoc-cli/docs/architecture/modules/.._test-project_src_utils.py.md b/archdoc-cli/docs/architecture/modules/.._test-project_src_utils.py.md new file mode 100644 index 0000000..c8a3fe4 --- /dev/null +++ b/archdoc-cli/docs/architecture/modules/.._test-project_src_utils.py.md @@ -0,0 +1,77 @@ +# Module: ../test-project/src/utils.py + +No summary available + +## Symbols + +### load_config + +def load_config(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### save_config + +def save_config(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### get_file_size + +def get_file_size(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### format_bytes + +def format_bytes(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + + +## Dependencies + +### Imports +- json +- os + +### Outbound Modules + +### Inbound Modules + +## Integrations + + + + +## Usage Examples + +```python +// Example usage of module functions +// TODO: Add real usage examples based on module analysis +``` + diff --git a/archdoc-cli/src/main.rs b/archdoc-cli/src/main.rs index 727a207..a0a8f1d 100644 --- a/archdoc-cli/src/main.rs +++ b/archdoc-cli/src/main.rs @@ -70,7 +70,7 @@ fn main() -> Result<()> { Commands::Generate { root, out, config } => { let config = load_config(config)?; let model = analyze_project(root, &config)?; - generate_docs(&model, out)?; + generate_docs(&model, out, cli.verbose)?; } Commands::Check { root, config } => { let config = load_config(config)?; @@ -102,12 +102,31 @@ fn init_project(root: &str, out: &str) -> Result<()> { std::fs::create_dir_all(out_path.join("files")) .map_err(|e| anyhow::anyhow!("Failed to create files directory: {}", e))?; + // Create layout.md file + let layout_md_path = out_path.join("layout.md"); + let layout_md_content = r#"# Repository layout + + +## Manual overrides +- `src/app/` — + + +--- + +## Detected structure + +> Generated. Do not edit inside this block. + +"#; + std::fs::write(&layout_md_path, layout_md_content) + .map_err(|e| anyhow::anyhow!("Failed to create layout.md: {}", e))?; + // Create default ARCHITECTURE.md template - let architecture_md_content = r#"# ARCHITECTURE — New Project + let architecture_md_content = r#"# ARCHITECTURE — ## Project summary -**Name:** New Project +**Name:** **Description:** ## Key decisions (manual) @@ -120,8 +139,8 @@ fn init_project(root: &str, out: &str) -> Result<()> { --- ## Document metadata -- **Created:** 2026-01-25 -- **Updated:** 2026-01-25 +- **Created:** +- **Updated:** - **Generated by:** archdoc (cli) v0.1 --- @@ -129,7 +148,7 @@ fn init_project(root: &str, out: &str) -> Result<()> { ## Rails / Tooling > Generated. Do not edit inside this block. - + --- @@ -137,7 +156,7 @@ fn init_project(root: &str, out: &str) -> Result<()> { ## Repository layout (top-level) > Generated. Do not edit inside this block. - + --- @@ -145,7 +164,7 @@ fn init_project(root: &str, out: &str) -> Result<()> { ## Modules index > Generated. Do not edit inside this block. - + --- @@ -153,7 +172,7 @@ fn init_project(root: &str, out: &str) -> Result<()> { ## Critical dependency points > Generated. Do not edit inside this block. - + --- @@ -295,7 +314,7 @@ fn sanitize_filename(filename: &str) -> String { .collect() } -fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> { +fn generate_docs(model: &ProjectModel, out: &str, verbose: bool) -> Result<()> { // TODO: Implement documentation generation println!("Generating docs to {}", out); @@ -340,11 +359,87 @@ fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> { } } - for (_file_id, file_doc) in &model.files { + // Create individual documentation files for files and symbols + for (file_id, file_doc) in &model.files { let file_doc_path = files_path.join(format!("{}.md", sanitize_filename(&file_doc.path))); - let file_content = format!("# File: {}\n\nTODO: Add file documentation\n", file_doc.path); + + // Create file documentation with symbol sections + let mut file_content = format!("# File: {}\n\n", file_doc.path); + file_content.push_str(&format!("- **Module:** {}\n", file_doc.module_id)); + file_content.push_str(&format!("- **Defined symbols:** {}\n", file_doc.symbols.len())); + file_content.push_str(&format!("- **Imports:** {}\n\n", file_doc.imports.len())); + + file_content.push_str("\n"); + file_content.push_str("## File intent (manual)\n"); + file_content.push_str("\n"); + file_content.push_str("\n\n"); + + file_content.push_str("---\n\n"); + + file_content.push_str("## Imports & file-level dependencies\n"); + file_content.push_str("\n"); + file_content.push_str("> Generated. Do not edit inside this block.\n"); + for import in &file_doc.imports { + file_content.push_str(&format!("- {}\n", import)); + } + file_content.push_str("\n\n"); + + file_content.push_str("---\n\n"); + + file_content.push_str("## Symbols index\n"); + file_content.push_str("\n"); + file_content.push_str("> Generated. Do not edit inside this block.\n"); + for symbol_id in &file_doc.symbols { + if let Some(symbol) = model.symbols.get(symbol_id) { + file_content.push_str(&format!("- [{}]({}#{})\n", symbol.qualname, sanitize_filename(&file_doc.path), symbol_id)); + } + } + file_content.push_str("\n\n"); + + file_content.push_str("---\n\n"); + + file_content.push_str("## Symbol details\n"); + + // Add symbol markers for each symbol + for symbol_id in &file_doc.symbols { + if let Some(_symbol) = model.symbols.get(symbol_id) { + if verbose { + println!("Adding symbol marker for {} in {}", symbol_id, file_doc_path.display()); + } + file_content.push_str(&format!("\n\n", symbol_id)); + file_content.push_str("\n"); + file_content.push_str(&format!("\n", symbol_id)); + } + } + + if verbose { + println!("Writing file content to {}: {} chars", file_doc_path.display(), file_content.len()); + // Show last 500 characters to see if symbol markers are there + let len = file_content.len(); + let start = if len > 500 { len - 500 } else { 0 }; + println!("Last 500 chars: {}", &file_content[start..]); + } std::fs::write(&file_doc_path, file_content) .map_err(|e| anyhow::anyhow!("Failed to create file doc {}: {}", file_doc_path.display(), e))?; + + // Update each symbol section in the file + for symbol_id in &file_doc.symbols { + if let Some(_symbol) = model.symbols.get(symbol_id) { + match renderer.render_symbol_details(model, symbol_id) { + Ok(content) => { + if verbose { + println!("Updating symbol section for {} in {}", symbol_id, file_doc_path.display()); + } + if let Err(e) = writer.update_symbol_section(&file_doc_path, symbol_id, &content) { + eprintln!("Warning: Failed to update symbol section for {}: {}", symbol_id, e); + } + } + Err(e) => { + eprintln!("Warning: Failed to render symbol details for {}: {}", symbol_id, e); + } + } + } + } } // Render and update each section individually @@ -373,7 +468,7 @@ fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> { } } - // Update layout section + // Update layout section in ARCHITECTURE.md match renderer.render_layout_section(model) { Ok(content) => { if let Err(e) = writer.update_file_with_markers(&output_path, &content, "layout") { @@ -409,6 +504,20 @@ fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> { } } + // Update layout.md file + let layout_md_path = out_path.join("layout.md"); + match renderer.render_layout_md(model) { + Ok(content) => { + // Write the full content to layout.md + if let Err(e) = std::fs::write(&layout_md_path, &content) { + eprintln!("Warning: Failed to write layout.md: {}", e); + } + } + Err(e) => { + eprintln!("Warning: Failed to render layout.md: {}", e); + } + } + Ok(()) } diff --git a/archdoc-core/src/python_analyzer.rs b/archdoc-core/src/python_analyzer.rs index af49041..156698f 100644 --- a/archdoc-core/src/python_analyzer.rs +++ b/archdoc-core/src/python_analyzer.rs @@ -149,7 +149,7 @@ impl PythonAnalyzer { }; symbols.push(symbol); - // Recursively process class body + // Recursively process class body for methods for body_stmt in &class_def.body { self.extract_from_statement(body_stmt, Some(&class_def.name), imports, symbols, calls, depth + 1); } @@ -164,9 +164,18 @@ impl PythonAnalyzer { } } - fn extract_docstring(&self, _body: &[Stmt]) -> Option { - // For now, just return None until we figure out the correct way to extract docstrings - // TODO: Implement proper docstring extraction + fn extract_docstring(&self, body: &[Stmt]) -> Option { + // Extract the first statement if it's a string expression (docstring) + if let Some(first_stmt) = body.first() { + if let Stmt::Expr(expr_stmt) = first_stmt { + if let Expr::Constant(constant_expr) = &*expr_stmt.value { + if let Some(docstring) = constant_expr.value.as_str() { + // Return the first line of the docstring + return docstring.lines().next().map(|s| s.to_string()); + } + } + } + } None } diff --git a/archdoc-core/src/renderer.rs b/archdoc-core/src/renderer.rs index 890828e..6e20ef2 100644 --- a/archdoc-core/src/renderer.rs +++ b/archdoc-core/src/renderer.rs @@ -520,4 +520,133 @@ impl Renderer { handlebars.render("critical_points", &data) .map_err(|e| anyhow::anyhow!("Failed to render critical points section: {}", e)) } + + pub fn render_layout_md(&self, model: &ProjectModel) -> Result { + // Collect layout information from files + let mut layout_items = Vec::new(); + + for (_file_id, file_doc) in &model.files { + layout_items.push(serde_json::json!({ + "path": file_doc.path, + "purpose": "Source file", + "link": format!("files/{}.md", sanitize_for_link(&file_doc.path)) + })); + } + + // Prepare data for layout template + let data = serde_json::json!({ + "layout_items": layout_items, + }); + + // Create template for layout.md + let layout_template = r#"# Repository layout + + +## Manual overrides +- `src/app/` — + + +--- + +## Detected structure + +> Generated. Do not edit inside this block. +| Path | Purpose | Link | +|------|---------|------| +{{#each layout_items}} +| {{{path}}} | {{{purpose}}} | [details]({{{link}}}) | +{{/each}} + +"#; + + let mut handlebars = Handlebars::new(); + handlebars.register_template_string("layout_md", layout_template) + .map_err(|e| anyhow::anyhow!("Failed to register layout_md template: {}", e))?; + + handlebars.render("layout_md", &data) + .map_err(|e| anyhow::anyhow!("Failed to render layout.md: {}", e)) + } + + pub fn render_symbol_details(&self, model: &ProjectModel, symbol_id: &str) -> Result { + // Find the symbol in the project model + let symbol = model.symbols.get(symbol_id) + .ok_or_else(|| anyhow::anyhow!("Symbol {} not found", symbol_id))?; + + // Prepare data for symbol template + let data = serde_json::json!({ + "symbol_id": symbol_id, + "qualname": symbol.qualname, + "kind": format!("{:?}", symbol.kind), + "signature": symbol.signature, + "docstring": symbol.docstring_first_line.as_deref().unwrap_or("No documentation available"), + "purpose": symbol.purpose, + "integrations": { + "http": symbol.integrations_flags.http, + "db": symbol.integrations_flags.db, + "queue": symbol.integrations_flags.queue, + }, + "metrics": { + "fan_in": symbol.metrics.fan_in, + "fan_out": symbol.metrics.fan_out, + "is_critical": symbol.metrics.is_critical, + "cycle_participant": symbol.metrics.cycle_participant, + }, + "outbound_calls": symbol.outbound_calls, + "inbound_calls": symbol.inbound_calls, + }); + + // Create template for symbol details + let symbol_template = r#" + +### `{{qualname}}` +- **Kind:** {{kind}} +- **Signature:** `{{{signature}}}` +- **Docstring:** `{{{docstring}}}` + +#### What it does + +{{{purpose}}} + + +#### Relations + +**Outbound calls (best-effort):** +{{#each outbound_calls}} +- {{{this}}} +{{/each}} + +**Inbound (used by) (best-effort):** +{{#each inbound_calls}} +- {{{this}}} +{{/each}} + + +#### Integrations (heuristic) + +- HTTP: {{#if integrations.http}}yes{{else}}no{{/if}} +- DB: {{#if integrations.db}}yes{{else}}no{{/if}} +- Queue/Tasks: {{#if integrations.queue}}yes{{else}}no{{/if}} + + +#### Risk / impact + +- fan-in: {{{metrics.fan_in}}} +- fan-out: {{{metrics.fan_out}}} +- cycle participant: {{#if metrics.cycle_participant}}yes{{else}}no{{/if}} +- critical: {{#if metrics.is_critical}}yes{{else}}no{{/if}} + + + +#### Manual notes + + +"#; + + let mut handlebars = Handlebars::new(); + handlebars.register_template_string("symbol_details", symbol_template) + .map_err(|e| anyhow::anyhow!("Failed to register symbol_details template: {}", e))?; + + handlebars.render("symbol_details", &data) + .map_err(|e| anyhow::anyhow!("Failed to render symbol details: {}", e)) + } } \ No newline at end of file diff --git a/archdoc-core/src/writer.rs b/archdoc-core/src/writer.rs index 7146319..5dbb74a 100644 --- a/archdoc-core/src/writer.rs +++ b/archdoc-core/src/writer.rs @@ -82,12 +82,52 @@ impl DiffAwareWriter { pub fn update_symbol_section( &self, - _file_path: &Path, - _symbol_id: &str, - _generated_content: &str, + file_path: &Path, + symbol_id: &str, + generated_content: &str, ) -> Result<(), ArchDocError> { - // Similar to section update but for symbol-specific markers - todo!("Implement symbol section update") + // Read existing file + let existing_content = if file_path.exists() { + fs::read_to_string(file_path) + .map_err(|e| ArchDocError::Io(e))? + } else { + // If file doesn't exist, create it with a basic template + let template_content = self.create_template_file(file_path, "symbol")?; + fs::write(file_path, &template_content) + .map_err(|e| ArchDocError::Io(e))?; + template_content + }; + + // Find symbol markers + let markers = self.find_symbol_markers(&existing_content, symbol_id)?; + + if let Some(marker) = markers.first() { + // Replace content between markers + let new_content = self.replace_symbol_content( + &existing_content, + marker, + generated_content, + )?; + + // Check if content has changed + let content_changed = existing_content != new_content; + + // Write updated content + if content_changed { + let updated_content = self.update_timestamp(new_content)?; + fs::write(file_path, updated_content) + .map_err(|e| ArchDocError::Io(e))?; + } else { + // Content hasn't changed, but we might still need to update timestamp + // TODO: Implement timestamp update logic based on config + fs::write(file_path, new_content) + .map_err(|e| ArchDocError::Io(e))?; + } + } else { + eprintln!("Warning: No symbol marker found for {} in {}", symbol_id, file_path.display()); + } + + Ok(()) } fn find_section_markers(&self, content: &str, section_name: &str) -> Result, ArchDocError> { @@ -117,6 +157,33 @@ impl DiffAwareWriter { Ok(markers) } + fn find_symbol_markers(&self, content: &str, symbol_id: &str) -> Result, ArchDocError> { + let begin_marker = format!("", symbol_id); + let end_marker = format!("", symbol_id); + + let mut markers = Vec::new(); + let mut pos = 0; + + while let Some(begin_pos) = content[pos..].find(&begin_marker) { + let absolute_begin = pos + begin_pos; + let search_start = absolute_begin + begin_marker.len(); + + if let Some(end_pos) = content[search_start..].find(&end_marker) { + let absolute_end = search_start + end_pos + end_marker.len(); + markers.push(SymbolMarker { + symbol_id: symbol_id.to_string(), + start_pos: absolute_begin, + end_pos: absolute_end, + }); + pos = absolute_end; + } else { + break; + } + } + + Ok(markers) + } + fn replace_section_content( &self, content: &str, @@ -135,6 +202,24 @@ impl DiffAwareWriter { )) } + fn replace_symbol_content( + &self, + content: &str, + marker: &SymbolMarker, + new_content: &str, + ) -> Result { + let before = &content[..marker.start_pos]; + let after = &content[marker.end_pos..]; + + let begin_marker = format!("", marker.symbol_id); + let end_marker = format!("", marker.symbol_id); + + Ok(format!( + "{}{}{}{}{}", + before, begin_marker, new_content, end_marker, after + )) + } + fn update_timestamp(&self, content: String) -> Result { // Update the "Updated" field in the document metadata section // Find the metadata section and update the timestamp @@ -159,11 +244,11 @@ impl DiffAwareWriter { // Create file with appropriate template based on type match template_type { "architecture" => { - let template = r#"# ARCHITECTURE — New Project + let template = r#"# ARCHITECTURE — ## Project summary -**Name:** New Project +**Name:** **Description:** ## Key decisions (manual) @@ -176,8 +261,8 @@ impl DiffAwareWriter { --- ## Document metadata -- **Created:** 2026-01-25 -- **Updated:** 2026-01-25 +- **Created:** +- **Updated:** - **Generated by:** archdoc (cli) v0.1 --- @@ -185,7 +270,7 @@ impl DiffAwareWriter { ## Rails / Tooling > Generated. Do not edit inside this block. - + --- @@ -193,7 +278,7 @@ impl DiffAwareWriter { ## Repository layout (top-level) > Generated. Do not edit inside this block. - + --- @@ -201,23 +286,15 @@ impl DiffAwareWriter { ## Modules index > Generated. Do not edit inside this block. - + --- - -## Integrations - -> Generated. Do not edit inside this block. - - ---- - ## Critical dependency points > Generated. Do not edit inside this block. - + --- @@ -226,6 +303,42 @@ impl DiffAwareWriter { ## Change notes (manual) - +"#; + Ok(template.to_string()) + } + "symbol" => { + // Template for symbol documentation files + let template = r#"# File: + +- **Module:** +- **Defined symbols:** +- **Imports:** + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. + + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. + + + +--- + +## Symbol details + "#; Ok(template.to_string()) } diff --git a/test-project/ARCHITECTURE.md b/test-project/ARCHITECTURE.md index d22ead5..b8ab314 100644 --- a/test-project/ARCHITECTURE.md +++ b/test-project/ARCHITECTURE.md @@ -34,9 +34,9 @@ No tooling information available. | Path | Purpose | Link | |------|---------|------| -| ./src/core.py | Source file | [details](docs/architecture/files/._src_core.py.md) | | ./src/__init__.py | Source file | [details](docs/architecture/files/._src___init__.py.md) | | ./src/utils.py | Source file | [details](docs/architecture/files/._src_utils.py.md) | +| ./src/core.py | Source file | [details](docs/architecture/files/._src_core.py.md) | --- @@ -47,8 +47,8 @@ No tooling information available. | Module | Symbols | Inbound | Outbound | Link | |--------|---------|---------|----------|------| | ./src/__init__.py | 0 | 0 | 0 | [details](docs/architecture/modules/._src___init__.py.md) | -| ./src/core.py | 6 | 0 | 0 | [details](docs/architecture/modules/._src_core.py.md) | | ./src/utils.py | 4 | 0 | 0 | [details](docs/architecture/modules/._src_utils.py.md) | +| ./src/core.py | 6 | 0 | 0 | [details](docs/architecture/modules/._src_core.py.md) | --- diff --git a/test-project/docs/architecture/files/._src___init__.py.md b/test-project/docs/architecture/files/._src___init__.py.md index 417238a..3d7f328 100644 --- a/test-project/docs/architecture/files/._src___init__.py.md +++ b/test-project/docs/architecture/files/._src___init__.py.md @@ -1,3 +1,28 @@ # File: ./src/__init__.py -TODO: Add file documentation +- **Module:** ./src/__init__.py +- **Defined symbols:** 0 +- **Imports:** 0 + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. + + +--- + +## Symbol details diff --git a/test-project/docs/architecture/files/._src_core.py.md b/test-project/docs/architecture/files/._src_core.py.md index 269ca20..39c0398 100644 --- a/test-project/docs/architecture/files/._src_core.py.md +++ b/test-project/docs/architecture/files/._src_core.py.md @@ -1,3 +1,36 @@ # File: ./src/core.py -TODO: Add file documentation +- **Module:** ./src/core.py +- **Defined symbols:** 6 +- **Imports:** 2 + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. +- sqlite3 +- requests + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. +- [DatabaseManager](._src_core.py#DatabaseManager) +- [__init__](._src_core.py#__init__) +- [connect](._src_core.py#connect) +- [execute_query](._src_core.py#execute_query) +- [fetch_external_data](._src_core.py#fetch_external_data) +- [process_user_data](._src_core.py#process_user_data) + + +--- + +## Symbol details diff --git a/test-project/docs/architecture/files/._src_utils.py.md b/test-project/docs/architecture/files/._src_utils.py.md index cdf28c4..568cf68 100644 --- a/test-project/docs/architecture/files/._src_utils.py.md +++ b/test-project/docs/architecture/files/._src_utils.py.md @@ -1,3 +1,34 @@ # File: ./src/utils.py -TODO: Add file documentation +- **Module:** ./src/utils.py +- **Defined symbols:** 4 +- **Imports:** 2 + + +## File intent (manual) + + + +--- + +## Imports & file-level dependencies + +> Generated. Do not edit inside this block. +- json +- os + + +--- + +## Symbols index + +> Generated. Do not edit inside this block. +- [load_config](._src_utils.py#load_config) +- [save_config](._src_utils.py#save_config) +- [get_file_size](._src_utils.py#get_file_size) +- [format_bytes](._src_utils.py#format_bytes) + + +--- + +## Symbol details diff --git a/test-project/docs/architecture/layout.md b/test-project/docs/architecture/layout.md new file mode 100644 index 0000000..e69de29 diff --git a/test-project/docs/architecture/modules/._src___init__.py.md b/test-project/docs/architecture/modules/._src___init__.py.md index cc5f3cc..726f514 100644 --- a/test-project/docs/architecture/modules/._src___init__.py.md +++ b/test-project/docs/architecture/modules/._src___init__.py.md @@ -1,3 +1,27 @@ # Module: ./src/__init__.py -TODO: Add module documentation +No summary available + +## Symbols + + +## Dependencies + +### Imports + +### Outbound Modules + +### Inbound Modules + +## Integrations + + + + +## Usage Examples + +```python +// Example usage of module functions +// TODO: Add real usage examples based on module analysis +``` + diff --git a/test-project/docs/architecture/modules/._src_core.py.md b/test-project/docs/architecture/modules/._src_core.py.md index e2ccd5a..b045215 100644 --- a/test-project/docs/architecture/modules/._src_core.py.md +++ b/test-project/docs/architecture/modules/._src_core.py.md @@ -1,3 +1,106 @@ # Module: ./src/core.py -TODO: Add module documentation +No summary available + +## Symbols + +### DatabaseManager + +class DatabaseManager + +No documentation available + +**Type:** Class + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### __init__ + +def __init__(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### connect + +def connect(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### execute_query + +def execute_query(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### fetch_external_data + +def fetch_external_data(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### process_user_data + +def process_user_data(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 1 + + +## Dependencies + +### Imports +- sqlite3 +- requests + +### Outbound Modules + +### Inbound Modules + +## Integrations + +### Database Integrations +- DatabaseManager +- connect + +### HTTP/API Integrations +- fetch_external_data + + +## Usage Examples + +```python +// Example usage of module functions +// TODO: Add real usage examples based on module analysis +``` + diff --git a/test-project/docs/architecture/modules/._src_utils.py.md b/test-project/docs/architecture/modules/._src_utils.py.md index b00a05d..9007e8c 100644 --- a/test-project/docs/architecture/modules/._src_utils.py.md +++ b/test-project/docs/architecture/modules/._src_utils.py.md @@ -1,3 +1,77 @@ # Module: ./src/utils.py -TODO: Add module documentation +No summary available + +## Symbols + +### load_config + +def load_config(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### save_config + +def save_config(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### get_file_size + +def get_file_size(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + +### format_bytes + +def format_bytes(...) + +No documentation available + +**Type:** Function + +**Metrics:** +- Fan-in: 0 +- Fan-out: 0 + + +## Dependencies + +### Imports +- json +- os + +### Outbound Modules + +### Inbound Modules + +## Integrations + + + + +## Usage Examples + +```python +// Example usage of module functions +// TODO: Add real usage examples based on module analysis +``` +