@dtt_siye/atool 1.1.0
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 +21 -0
- package/README.md +235 -0
- package/VERSION +1 -0
- package/agents/code-reviewer.md +29 -0
- package/bin/atool.js +235 -0
- package/bin/postinstall.js +23 -0
- package/hooks/doc-sync-reminder +155 -0
- package/hooks/hooks-cursor.json +37 -0
- package/hooks/hooks.json +37 -0
- package/hooks/prompt-guard +135 -0
- package/hooks/session-start +286 -0
- package/install.sh +603 -0
- package/lib/analyze-source.sh +1265 -0
- package/lib/common.sh +1041 -0
- package/lib/compute-importance.sh +598 -0
- package/lib/detect-stack.sh +354 -0
- package/lib/generate-visualization.sh +266 -0
- package/lib/install-claude.sh +43 -0
- package/lib/install-cursor.sh +281 -0
- package/lib/install-hooks.sh +285 -0
- package/lib/install-kiro.sh +543 -0
- package/lib/install-mcp.sh +99 -0
- package/lib/install-skills.sh +129 -0
- package/lib/knowledge-graph.sh +1014 -0
- package/lib/multi-dimensional-analysis.sh +413 -0
- package/lib/pre-scan.sh +1045 -0
- package/lib/project-init.sh +552 -0
- package/lib/visualization-template.html +545 -0
- package/mcp/recommended.json +24 -0
- package/package.json +39 -0
- package/skills/_superpowers/.claude-plugin/marketplace.json +20 -0
- package/skills/_superpowers/.claude-plugin/plugin.json +20 -0
- package/skills/_superpowers/.codex/INSTALL.md +67 -0
- package/skills/_superpowers/.cursor-plugin/plugin.json +25 -0
- package/skills/_superpowers/.gitattributes +18 -0
- package/skills/_superpowers/.github/FUNDING.yml +3 -0
- package/skills/_superpowers/.github/ISSUE_TEMPLATE/bug_report.md +52 -0
- package/skills/_superpowers/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/skills/_superpowers/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
- package/skills/_superpowers/.github/ISSUE_TEMPLATE/platform_support.md +23 -0
- package/skills/_superpowers/.github/PULL_REQUEST_TEMPLATE.md +87 -0
- package/skills/_superpowers/.opencode/INSTALL.md +83 -0
- package/skills/_superpowers/.opencode/plugins/superpowers.js +107 -0
- package/skills/_superpowers/CHANGELOG.md +13 -0
- package/skills/_superpowers/CODE_OF_CONDUCT.md +128 -0
- package/skills/_superpowers/GEMINI.md +2 -0
- package/skills/_superpowers/LICENSE +21 -0
- package/skills/_superpowers/README.md +187 -0
- package/skills/_superpowers/RELEASE-NOTES.md +1083 -0
- package/skills/_superpowers/agents/code-reviewer.md +48 -0
- package/skills/_superpowers/commands/brainstorm.md +5 -0
- package/skills/_superpowers/commands/execute-plan.md +5 -0
- package/skills/_superpowers/commands/write-plan.md +5 -0
- package/skills/_superpowers/docs/README.codex.md +126 -0
- package/skills/_superpowers/docs/README.opencode.md +130 -0
- package/skills/_superpowers/docs/plans/2025-11-22-opencode-support-design.md +294 -0
- package/skills/_superpowers/docs/plans/2025-11-22-opencode-support-implementation.md +1095 -0
- package/skills/_superpowers/docs/plans/2025-11-28-skills-improvements-from-user-feedback.md +711 -0
- package/skills/_superpowers/docs/plans/2026-01-17-visual-brainstorming.md +571 -0
- package/skills/_superpowers/docs/superpowers/plans/2026-01-22-document-review-system.md +301 -0
- package/skills/_superpowers/docs/superpowers/plans/2026-02-19-visual-brainstorming-refactor.md +523 -0
- package/skills/_superpowers/docs/superpowers/plans/2026-03-11-zero-dep-brainstorm-server.md +479 -0
- package/skills/_superpowers/docs/superpowers/plans/2026-03-23-codex-app-compatibility.md +564 -0
- package/skills/_superpowers/docs/superpowers/specs/2026-01-22-document-review-system-design.md +136 -0
- package/skills/_superpowers/docs/superpowers/specs/2026-02-19-visual-brainstorming-refactor-design.md +162 -0
- package/skills/_superpowers/docs/superpowers/specs/2026-03-11-zero-dep-brainstorm-server-design.md +118 -0
- package/skills/_superpowers/docs/superpowers/specs/2026-03-23-codex-app-compatibility-design.md +244 -0
- package/skills/_superpowers/docs/testing.md +303 -0
- package/skills/_superpowers/docs/windows/polyglot-hooks.md +212 -0
- package/skills/_superpowers/gemini-extension.json +6 -0
- package/skills/_superpowers/hooks/hooks-cursor.json +10 -0
- package/skills/_superpowers/hooks/hooks.json +16 -0
- package/skills/_superpowers/hooks/run-hook.cmd +46 -0
- package/skills/_superpowers/hooks/session-start +57 -0
- package/skills/_superpowers/package.json +6 -0
- package/skills/_superpowers/skills/brainstorming/SKILL.md +164 -0
- package/skills/_superpowers/skills/brainstorming/scripts/frame-template.html +214 -0
- package/skills/_superpowers/skills/brainstorming/scripts/helper.js +88 -0
- package/skills/_superpowers/skills/brainstorming/scripts/server.cjs +354 -0
- package/skills/_superpowers/skills/brainstorming/scripts/start-server.sh +148 -0
- package/skills/_superpowers/skills/brainstorming/scripts/stop-server.sh +56 -0
- package/skills/_superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/skills/_superpowers/skills/brainstorming/visual-companion.md +287 -0
- package/skills/_superpowers/skills/dispatching-parallel-agents/SKILL.md +182 -0
- package/skills/_superpowers/skills/executing-plans/SKILL.md +70 -0
- package/skills/_superpowers/skills/finishing-a-development-branch/SKILL.md +200 -0
- package/skills/_superpowers/skills/receiving-code-review/SKILL.md +213 -0
- package/skills/_superpowers/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/_superpowers/skills/requesting-code-review/code-reviewer.md +146 -0
- package/skills/_superpowers/skills/subagent-driven-development/SKILL.md +277 -0
- package/skills/_superpowers/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/_superpowers/skills/subagent-driven-development/implementer-prompt.md +113 -0
- package/skills/_superpowers/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/_superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/_superpowers/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/_superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/_superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/_superpowers/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/_superpowers/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/_superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/_superpowers/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/_superpowers/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/_superpowers/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/_superpowers/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/_superpowers/skills/test-driven-development/SKILL.md +371 -0
- package/skills/_superpowers/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/_superpowers/skills/using-git-worktrees/SKILL.md +218 -0
- package/skills/_superpowers/skills/using-superpowers/SKILL.md +115 -0
- package/skills/_superpowers/skills/using-superpowers/references/codex-tools.md +100 -0
- package/skills/_superpowers/skills/using-superpowers/references/gemini-tools.md +33 -0
- package/skills/_superpowers/skills/verification-before-completion/SKILL.md +139 -0
- package/skills/_superpowers/skills/writing-plans/SKILL.md +152 -0
- package/skills/_superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/skills/_superpowers/skills/writing-skills/SKILL.md +655 -0
- package/skills/_superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/skills/_superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/_superpowers/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/_superpowers/skills/writing-skills/persuasion-principles.md +187 -0
- package/skills/_superpowers/skills/writing-skills/render-graphs.js +168 -0
- package/skills/_superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/skills/_superpowers/tests/brainstorm-server/package-lock.json +36 -0
- package/skills/_superpowers/tests/brainstorm-server/package.json +10 -0
- package/skills/_superpowers/tests/brainstorm-server/server.test.js +427 -0
- package/skills/_superpowers/tests/brainstorm-server/windows-lifecycle.test.sh +351 -0
- package/skills/_superpowers/tests/brainstorm-server/ws-protocol.test.js +392 -0
- package/skills/_superpowers/tests/claude-code/README.md +158 -0
- package/skills/_superpowers/tests/claude-code/analyze-token-usage.py +168 -0
- package/skills/_superpowers/tests/claude-code/run-skill-tests.sh +187 -0
- package/skills/_superpowers/tests/claude-code/test-document-review-system.sh +177 -0
- package/skills/_superpowers/tests/claude-code/test-helpers.sh +202 -0
- package/skills/_superpowers/tests/claude-code/test-subagent-driven-development-integration.sh +314 -0
- package/skills/_superpowers/tests/claude-code/test-subagent-driven-development.sh +165 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/action-oriented.txt +3 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/after-planning-flow.txt +17 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/claude-suggested-it.txt +11 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/i-know-what-sdd-means.txt +8 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/mid-conversation-execute-plan.txt +3 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/please-use-brainstorming.txt +1 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/skip-formalities.txt +3 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/subagent-driven-development-please.txt +1 -0
- package/skills/_superpowers/tests/explicit-skill-requests/prompts/use-systematic-debugging.txt +1 -0
- package/skills/_superpowers/tests/explicit-skill-requests/run-all.sh +70 -0
- package/skills/_superpowers/tests/explicit-skill-requests/run-claude-describes-sdd.sh +100 -0
- package/skills/_superpowers/tests/explicit-skill-requests/run-extended-multiturn-test.sh +113 -0
- package/skills/_superpowers/tests/explicit-skill-requests/run-haiku-test.sh +144 -0
- package/skills/_superpowers/tests/explicit-skill-requests/run-multiturn-test.sh +143 -0
- package/skills/_superpowers/tests/explicit-skill-requests/run-test.sh +136 -0
- package/skills/_superpowers/tests/opencode/run-tests.sh +163 -0
- package/skills/_superpowers/tests/opencode/setup.sh +73 -0
- package/skills/_superpowers/tests/opencode/test-plugin-loading.sh +72 -0
- package/skills/_superpowers/tests/opencode/test-priority.sh +198 -0
- package/skills/_superpowers/tests/opencode/test-tools.sh +104 -0
- package/skills/_superpowers/tests/skill-triggering/prompts/dispatching-parallel-agents.txt +8 -0
- package/skills/_superpowers/tests/skill-triggering/prompts/executing-plans.txt +1 -0
- package/skills/_superpowers/tests/skill-triggering/prompts/requesting-code-review.txt +3 -0
- package/skills/_superpowers/tests/skill-triggering/prompts/systematic-debugging.txt +11 -0
- package/skills/_superpowers/tests/skill-triggering/prompts/test-driven-development.txt +7 -0
- package/skills/_superpowers/tests/skill-triggering/prompts/writing-plans.txt +10 -0
- package/skills/_superpowers/tests/skill-triggering/run-all.sh +60 -0
- package/skills/_superpowers/tests/skill-triggering/run-test.sh +88 -0
- package/skills/_superpowers/tests/subagent-driven-dev/go-fractals/design.md +81 -0
- package/skills/_superpowers/tests/subagent-driven-dev/go-fractals/plan.md +172 -0
- package/skills/_superpowers/tests/subagent-driven-dev/go-fractals/scaffold.sh +45 -0
- package/skills/_superpowers/tests/subagent-driven-dev/run-test.sh +106 -0
- package/skills/_superpowers/tests/subagent-driven-dev/svelte-todo/design.md +70 -0
- package/skills/_superpowers/tests/subagent-driven-dev/svelte-todo/plan.md +222 -0
- package/skills/_superpowers/tests/subagent-driven-dev/svelte-todo/scaffold.sh +46 -0
- package/skills/ai-project-architecture/SKILL.md +632 -0
- package/skills/ai-project-architecture/reference/structure-rules.md +406 -0
- package/skills/ai-project-architecture/templates/compliance-report.md +300 -0
- package/skills/ai-project-architecture/templates/migration-plan.md +433 -0
- package/skills/ai-project-architecture/templates/verification-checklist.md +408 -0
- package/skills/android-conventions/SKILL.md +125 -0
- package/skills/atool-init/SKILL.md +141 -0
- package/skills/clarify-before-build/SKILL.md +107 -0
- package/skills/code-review/SKILL.md +406 -0
- package/skills/code-review/rules/architecture.md +285 -0
- package/skills/code-review/rules/coupling-cohesion.md +309 -0
- package/skills/code-review/rules/dead-code.md +115 -0
- package/skills/code-review/rules/deprecation-debt.md +279 -0
- package/skills/code-review/rules/duplication.md +104 -0
- package/skills/code-review/rules/error-security.md +143 -0
- package/skills/code-review/rules/maintainability.md +203 -0
- package/skills/code-review/rules/quality.md +158 -0
- package/skills/devops-conventions/SKILL.md +205 -0
- package/skills/doc-coauthoring/SKILL.md +392 -0
- package/skills/doc-standards-enforcer/SKILL.md +290 -0
- package/skills/doc-standards-enforcer/examples/valid-document-example.md +67 -0
- package/skills/doc-standards-enforcer/references/101-standards-summary.md +318 -0
- package/skills/doc-standards-enforcer/scripts/check_references.py +175 -0
- package/skills/doc-standards-enforcer/scripts/fix_common_issues.py +303 -0
- package/skills/doc-standards-enforcer/scripts/validate_doc_standards.py +332 -0
- package/skills/docx/LICENSE.txt +30 -0
- package/skills/docx/SKILL.md +200 -0
- package/skills/docx/docx-js.md +350 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/ooxml/scripts/pack.py +159 -0
- package/skills/docx/ooxml/scripts/unpack.py +29 -0
- package/skills/docx/ooxml/scripts/validate.py +69 -0
- package/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/docx/ooxml/scripts/validation/base.py +951 -0
- package/skills/docx/ooxml/scripts/validation/docx.py +274 -0
- package/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/skills/docx/ooxml.md +610 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/document.py +1276 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/docx/scripts/utilities.py +374 -0
- package/skills/flutter-conventions/SKILL.md +70 -0
- package/skills/go-conventions/SKILL.md +230 -0
- package/skills/harmony-conventions/SKILL.md +156 -0
- package/skills/java-conventions/SKILL.md +277 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +297 -0
- package/skills/pdf/forms.md +205 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/skills/pdf/scripts/create_validation_image.py +41 -0
- package/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/skills/pptx/LICENSE.txt +30 -0
- package/skills/pptx/SKILL.md +487 -0
- package/skills/pptx/html2pptx.md +625 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/ooxml/scripts/pack.py +159 -0
- package/skills/pptx/ooxml/scripts/unpack.py +29 -0
- package/skills/pptx/ooxml/scripts/validate.py +69 -0
- package/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/skills/pptx/ooxml/scripts/validation/base.py +951 -0
- package/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/skills/pptx/ooxml.md +427 -0
- package/skills/pptx/scripts/html2pptx.js +979 -0
- package/skills/pptx/scripts/inventory.py +1020 -0
- package/skills/pptx/scripts/rearrange.py +231 -0
- package/skills/pptx/scripts/replace.py +385 -0
- package/skills/pptx/scripts/thumbnail.py +450 -0
- package/skills/project-analyze/SKILL.md +270 -0
- package/skills/project-analyze/phases/phase0-discovery.md +278 -0
- package/skills/project-analyze/phases/phase0.5-prescan.md +139 -0
- package/skills/project-analyze/phases/phase1-inventory.md +94 -0
- package/skills/project-analyze/phases/phase2-deep-analysis.md +249 -0
- package/skills/project-analyze/phases/phase2a-l4-analysis.md +94 -0
- package/skills/project-analyze/phases/phase2b-l5-analysis.md +97 -0
- package/skills/project-analyze/phases/phase3-knowledge-graph.md +120 -0
- package/skills/project-analyze/phases/phase3a-multi-dimensional.md +61 -0
- package/skills/project-analyze/phases/phase4-code-quality.md +81 -0
- package/skills/project-analyze/phases/phase5-synthesis.md +284 -0
- package/skills/project-analyze/phases/phase6-validation.md +179 -0
- package/skills/project-analyze/prompts/code-review-agent.md +122 -0
- package/skills/project-analyze/prompts/deep-analysis-agent.md +107 -0
- package/skills/project-analyze/prompts/inventory-agent.md +67 -0
- package/skills/project-analyze/prompts/l4-analysis-agent.md +98 -0
- package/skills/project-analyze/rules/android.md +282 -0
- package/skills/project-analyze/rules/devops.md +443 -0
- package/skills/project-analyze/rules/generic.md +243 -0
- package/skills/project-analyze/rules/go.md +289 -0
- package/skills/project-analyze/rules/harmony.md +257 -0
- package/skills/project-analyze/rules/java.md +507 -0
- package/skills/project-analyze/rules/mobile-flutter.md +315 -0
- package/skills/project-analyze/rules/mobile-react-native.md +283 -0
- package/skills/project-analyze/rules/mobile-swift.md +323 -0
- package/skills/project-analyze/rules/python.md +317 -0
- package/skills/project-analyze/rules/rust-tauri.md +243 -0
- package/skills/project-analyze/rules/rust.md +296 -0
- package/skills/project-analyze/rules/web-nextjs.md +364 -0
- package/skills/project-analyze/rules/web-react.md +298 -0
- package/skills/project-analyze/rules/web-vue.md +378 -0
- package/skills/project-analyze/rules/web.md +390 -0
- package/skills/project-query/SKILL.md +224 -0
- package/skills/project-query/rules/query-templates.md +212 -0
- package/skills/python-conventions/SKILL.md +169 -0
- package/skills/react-native-conventions/SKILL.md +73 -0
- package/skills/requirements-writer/README.md +153 -0
- package/skills/requirements-writer/SKILL.md +341 -0
- package/skills/requirements-writer/examples/prd-outline-example.md +217 -0
- package/skills/requirements-writer/templates/module-prd-template.md +362 -0
- package/skills/requirements-writer/templates/prd-outline-template.md +185 -0
- package/skills/requirements-writer/templates/user-story-template.md +1125 -0
- package/skills/rust-conventions/SKILL.md +361 -0
- package/skills/smart-dispatch/SKILL.md +296 -0
- package/skills/smart-dispatch/implementer-prompt.md +146 -0
- package/skills/smart-dispatch/reviewer-prompt.md +199 -0
- package/skills/software-architecture/SKILL.md +278 -0
- package/skills/swift-conventions/SKILL.md +72 -0
- package/skills/ui-ux-pro/SKILL.md +140 -0
- package/skills/verification-before-completion/SKILL.md +119 -0
- package/skills/web-conventions/SKILL.md +259 -0
- package/skills/webapp-testing/LICENSE.txt +202 -0
- package/skills/webapp-testing/SKILL.md +97 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/skills/writing-plans/SKILL.md +144 -0
- package/skills/xlsx/LICENSE.txt +30 -0
- package/skills/xlsx/SKILL.md +292 -0
- package/skills/xlsx/recalc.py +178 -0
- package/templates/CLAUDE.md.android +57 -0
- package/templates/CLAUDE.md.devops +50 -0
- package/templates/CLAUDE.md.generic +34 -0
- package/templates/CLAUDE.md.go +67 -0
- package/templates/CLAUDE.md.harmony +54 -0
- package/templates/CLAUDE.md.java +56 -0
- package/templates/CLAUDE.md.mobile-flutter +38 -0
- package/templates/CLAUDE.md.mobile-react-native +37 -0
- package/templates/CLAUDE.md.mobile-swift +40 -0
- package/templates/CLAUDE.md.python +65 -0
- package/templates/CLAUDE.md.rust +68 -0
- package/templates/CLAUDE.md.rust-tauri +120 -0
- package/templates/CLAUDE.md.web +63 -0
- package/templates/COMPONENT.md.android +58 -0
- package/templates/COMPONENT.md.devops +54 -0
- package/templates/COMPONENT.md.generic +35 -0
- package/templates/COMPONENT.md.go +59 -0
- package/templates/COMPONENT.md.harmony +63 -0
- package/templates/COMPONENT.md.java +69 -0
- package/templates/COMPONENT.md.mobile-flutter +56 -0
- package/templates/COMPONENT.md.mobile-react-native +55 -0
- package/templates/COMPONENT.md.mobile-swift +56 -0
- package/templates/COMPONENT.md.python +67 -0
- package/templates/COMPONENT.md.rust +57 -0
- package/templates/COMPONENT.md.rust-tauri +66 -0
- package/templates/COMPONENT.md.web +39 -0
- package/templates/README.md.android +71 -0
- package/templates/README.md.devops +68 -0
- package/templates/README.md.generic +39 -0
- package/templates/README.md.go +70 -0
- package/templates/README.md.harmony +72 -0
- package/templates/README.md.java +73 -0
- package/templates/README.md.mobile-flutter +69 -0
- package/templates/README.md.mobile-react-native +65 -0
- package/templates/README.md.mobile-swift +69 -0
- package/templates/README.md.monorepo +59 -0
- package/templates/README.md.python +66 -0
- package/templates/README.md.rust +69 -0
- package/templates/README.md.rust-tauri +149 -0
- package/templates/README.md.web +94 -0
- package/templates/UI_STYLE.md.android +74 -0
- package/templates/UI_STYLE.md.devops +50 -0
- package/templates/UI_STYLE.md.generic +31 -0
- package/templates/UI_STYLE.md.go +49 -0
- package/templates/UI_STYLE.md.harmony +71 -0
- package/templates/UI_STYLE.md.java +61 -0
- package/templates/UI_STYLE.md.mobile-flutter +70 -0
- package/templates/UI_STYLE.md.mobile-react-native +71 -0
- package/templates/UI_STYLE.md.mobile-swift +71 -0
- package/templates/UI_STYLE.md.python +58 -0
- package/templates/UI_STYLE.md.rust +52 -0
- package/templates/UI_STYLE.md.rust-tauri +102 -0
- package/templates/UI_STYLE.md.web +92 -0
- package/templates/cursor-rules.android.mdc +33 -0
- package/templates/cursor-rules.devops.mdc +32 -0
- package/templates/cursor-rules.generic.mdc +25 -0
- package/templates/cursor-rules.go.mdc +34 -0
- package/templates/cursor-rules.harmony.mdc +30 -0
- package/templates/cursor-rules.java.mdc +30 -0
- package/templates/cursor-rules.mobile-flutter.mdc +42 -0
- package/templates/cursor-rules.mobile-react-native.mdc +43 -0
- package/templates/cursor-rules.mobile-swift.mdc +42 -0
- package/templates/cursor-rules.python.mdc +33 -0
- package/templates/cursor-rules.rust-tauri.mdc +67 -0
- package/templates/cursor-rules.rust.mdc +30 -0
- package/templates/cursor-rules.web.mdc +30 -0
- package/templates/kiro-steering.android.md +39 -0
- package/templates/kiro-steering.devops.md +32 -0
- package/templates/kiro-steering.generic.md +28 -0
- package/templates/kiro-steering.go.md +41 -0
- package/templates/kiro-steering.harmony.md +36 -0
- package/templates/kiro-steering.java.md +37 -0
- package/templates/kiro-steering.mobile-flutter.md +73 -0
- package/templates/kiro-steering.mobile-react-native.md +71 -0
- package/templates/kiro-steering.mobile-swift.md +62 -0
- package/templates/kiro-steering.python.md +34 -0
- package/templates/kiro-steering.rust-tauri.md +50 -0
- package/templates/kiro-steering.rust.md +36 -0
- package/templates/kiro-steering.web.md +41 -0
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# shellcheck source=lib/common.sh
|
|
3
|
+
# knowledge-graph.sh — Knowledge graph construction helpers for project-analyze v5.0
|
|
4
|
+
#
|
|
5
|
+
# Usage: source lib/knowledge-graph.sh
|
|
6
|
+
#
|
|
7
|
+
# Provides functions for building and analyzing the project knowledge graph
|
|
8
|
+
# from Phase 1 inventory data. All functions operate on JSON files
|
|
9
|
+
# in .atool-docs/inventory/ and output structured data to .atool-docs/knowledge-graph.json
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# === Node Counting ===
|
|
13
|
+
|
|
14
|
+
# Count nodes by type from inventory JSON files.
|
|
15
|
+
# Each inventory file is expected to have a top-level "files" array where each
|
|
16
|
+
# entry contains a "type" field (module, component, function, store, etc.).
|
|
17
|
+
#
|
|
18
|
+
# Args:
|
|
19
|
+
# $1 - inventory_dir: path to .atool-docs/inventory/
|
|
20
|
+
# Output: JSON object {"total": N, "module": N, "component": N, ...} to stdout
|
|
21
|
+
count_graph_nodes() {
|
|
22
|
+
local inventory_dir="$1"
|
|
23
|
+
|
|
24
|
+
if [[ ! -d "$inventory_dir" ]]; then
|
|
25
|
+
echo "{}"
|
|
26
|
+
return
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Requires jq
|
|
30
|
+
if ! command -v jq &>/dev/null; then
|
|
31
|
+
echo "{}"
|
|
32
|
+
return
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Aggregate node counts from all inventory JSON files.
|
|
36
|
+
# Each file has "files" array with "type" field per entry.
|
|
37
|
+
# Null-coalesce missing types to 0.
|
|
38
|
+
local result
|
|
39
|
+
result=$(jq -s \
|
|
40
|
+
'[ .[].files // [] | .[].type ]
|
|
41
|
+
| group_by(.)
|
|
42
|
+
| map({key: .[0], value: length})
|
|
43
|
+
| from_entries
|
|
44
|
+
| .total = ([ .[] | select(type == "number") ] | add // 0)
|
|
45
|
+
| {
|
|
46
|
+
total,
|
|
47
|
+
module: (.module // 0),
|
|
48
|
+
component: (.component // 0),
|
|
49
|
+
function: (.function // 0),
|
|
50
|
+
store: (.store // 0),
|
|
51
|
+
data_entity: (.data_entity // 0),
|
|
52
|
+
api_endpoint: (.api_endpoint // 0),
|
|
53
|
+
route: (.route // 0),
|
|
54
|
+
config: (.config // 0)
|
|
55
|
+
}' "$inventory_dir"/*.json 2>/dev/null)
|
|
56
|
+
|
|
57
|
+
if [[ -n "$result" ]]; then
|
|
58
|
+
echo "$result"
|
|
59
|
+
else
|
|
60
|
+
echo "{}"
|
|
61
|
+
fi
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# === Edge Counting ===
|
|
65
|
+
|
|
66
|
+
# Count edges by type from inventory JSON files.
|
|
67
|
+
# Each inventory file has "outgoing_edges" array with "type" field per entry.
|
|
68
|
+
#
|
|
69
|
+
# Args:
|
|
70
|
+
# $1 - inventory_dir: path to .atool-docs/inventory/
|
|
71
|
+
# Output: JSON object {"total": N, "depends_on": N, "calls": N, ...} to stdout
|
|
72
|
+
count_graph_edges() {
|
|
73
|
+
local inventory_dir="$1"
|
|
74
|
+
|
|
75
|
+
if [[ ! -d "$inventory_dir" ]]; then
|
|
76
|
+
echo "{}"
|
|
77
|
+
return
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if ! command -v jq &>/dev/null; then
|
|
81
|
+
echo "{}"
|
|
82
|
+
return
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Aggregate edge counts from all inventory files.
|
|
86
|
+
# Each file has "outgoing_edges" array with "type" field per entry.
|
|
87
|
+
local result
|
|
88
|
+
result=$(jq -s \
|
|
89
|
+
'[ .[].outgoing_edges // [] | .[].type ]
|
|
90
|
+
| group_by(.)
|
|
91
|
+
| map({key: .[0], value: length})
|
|
92
|
+
| from_entries
|
|
93
|
+
| .total = ([ .[] | select(type == "number") ] | add // 0)
|
|
94
|
+
| {
|
|
95
|
+
total,
|
|
96
|
+
depends_on: (.depends_on // 0),
|
|
97
|
+
calls: (.calls // 0),
|
|
98
|
+
reads_state: (.reads_state // 0),
|
|
99
|
+
returns: (.returns // 0),
|
|
100
|
+
renders: (.renders // 0),
|
|
101
|
+
routes_to: (.routes_to // 0),
|
|
102
|
+
sends_http: (.sends_http // 0),
|
|
103
|
+
persists_to: (.persists_to // 0),
|
|
104
|
+
implements: (.implements // 0),
|
|
105
|
+
extends: (.extends // 0)
|
|
106
|
+
}' "$inventory_dir"/*.json 2>/dev/null)
|
|
107
|
+
|
|
108
|
+
if [[ -n "$result" ]]; then
|
|
109
|
+
echo "$result"
|
|
110
|
+
else
|
|
111
|
+
echo "{}"
|
|
112
|
+
fi
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# === Cycle Detection ===
|
|
116
|
+
|
|
117
|
+
# Detect circular dependencies in the module dependency graph.
|
|
118
|
+
# Extracts "depends_on" edges from inventory, then finds 2-cycles and 3-cycles.
|
|
119
|
+
#
|
|
120
|
+
# Args:
|
|
121
|
+
# $1 - inventory_dir: path to .atool-docs/inventory/
|
|
122
|
+
# Output: JSON array of cycles, each cycle is a sorted array of module names.
|
|
123
|
+
# Example: [["module-a","module-b"], ["module-x","module-y","module-z"]]
|
|
124
|
+
detect_cycles() {
|
|
125
|
+
local inventory_dir="$1"
|
|
126
|
+
|
|
127
|
+
if [[ ! -d "$inventory_dir" ]]; then
|
|
128
|
+
echo "[]"
|
|
129
|
+
return
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
if ! command -v jq &>/dev/null; then
|
|
133
|
+
echo "[]"
|
|
134
|
+
return
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# Build adjacency list from depends_on edges: source_module -> [target_modules]
|
|
138
|
+
# Each edge in outgoing_edges has: type, source, target_module
|
|
139
|
+
local adj_json
|
|
140
|
+
adj_json=$(jq -s \
|
|
141
|
+
'[ .[].outgoing_edges // []
|
|
142
|
+
| .[]
|
|
143
|
+
| select(.type == "depends_on")
|
|
144
|
+
| {source: (.source // ""), target: (.target_module // "")}
|
|
145
|
+
| select(.source != "" and .target != "") ]
|
|
146
|
+
| group_by(.source)
|
|
147
|
+
| map({key: .[0].source, value: (map(.target) | unique)})
|
|
148
|
+
| from_entries' "$inventory_dir"/*.json 2>/dev/null)
|
|
149
|
+
|
|
150
|
+
if [[ -z "$adj_json" || "$adj_json" == "null" || "$adj_json" == "{}" ]]; then
|
|
151
|
+
echo "[]"
|
|
152
|
+
return
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
# Detect 2-cycles (A->B and B->A) and 3-cycles (A->B->C->A)
|
|
156
|
+
local cycles
|
|
157
|
+
cycles=$(echo "$adj_json" | jq '
|
|
158
|
+
. as $adj |
|
|
159
|
+
|
|
160
|
+
# Collect all module names
|
|
161
|
+
($adj | keys) as $modules |
|
|
162
|
+
|
|
163
|
+
# Find 2-cycles: A->B where B->A
|
|
164
|
+
[ $modules[] as $a |
|
|
165
|
+
($adj[$a] // [])[] as $b |
|
|
166
|
+
select($b > $a) |
|
|
167
|
+
select(($adj[$b] // []) | index($a)) |
|
|
168
|
+
[$a, $b] | sort
|
|
169
|
+
] as $two_cycles |
|
|
170
|
+
|
|
171
|
+
# Find 3-cycles: A->B->C where C->A
|
|
172
|
+
[ $modules[] as $a |
|
|
173
|
+
($adj[$a] // [])[] as $b |
|
|
174
|
+
select($b != $a) |
|
|
175
|
+
($adj[$b] // [])[] as $c |
|
|
176
|
+
select($c != $a and $c != $b) |
|
|
177
|
+
select(($adj[$c] // []) | index($a)) |
|
|
178
|
+
[$a, $b, $c] | sort
|
|
179
|
+
] | unique as $three_cycles |
|
|
180
|
+
|
|
181
|
+
# Merge and deduplicate
|
|
182
|
+
($two_cycles + $three_cycles) | unique
|
|
183
|
+
')
|
|
184
|
+
|
|
185
|
+
echo "${cycles:-[]}"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# === Coupling Metrics ===
|
|
189
|
+
|
|
190
|
+
# Compute afferent coupling (Ca), efferent coupling (Ce), and instability per module.
|
|
191
|
+
# Ce (Efferent): number of modules this module depends on (outgoing depends_on)
|
|
192
|
+
# Ca (Afferent): number of modules that depend on this module (incoming depends_on)
|
|
193
|
+
# Instability = Ce / (Ce + Ca) -- ranges 0..1; 1 = maximally unstable
|
|
194
|
+
#
|
|
195
|
+
# Args:
|
|
196
|
+
# $1 - inventory_dir: path to .atool-docs/inventory/
|
|
197
|
+
# Output: JSON object {module_name: {ca: N, ce: N, instability: F}} to stdout
|
|
198
|
+
compute_coupling_metrics() {
|
|
199
|
+
local inventory_dir="$1"
|
|
200
|
+
|
|
201
|
+
if [[ ! -d "$inventory_dir" ]]; then
|
|
202
|
+
echo "{}"
|
|
203
|
+
return
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
if ! command -v jq &>/dev/null; then
|
|
207
|
+
echo "{}"
|
|
208
|
+
return
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
local result
|
|
212
|
+
result=$(jq -s \
|
|
213
|
+
'[ .[].outgoing_edges // []
|
|
214
|
+
| .[]
|
|
215
|
+
| select(.type == "depends_on")
|
|
216
|
+
| {source: (.source // ""), target: (.target_module // "")}
|
|
217
|
+
| select(.source != "" and .target != "") ] as $edges |
|
|
218
|
+
|
|
219
|
+
# All unique module names (both sources and targets)
|
|
220
|
+
($edges | map(.source) + map(.target) | unique) as $modules |
|
|
221
|
+
|
|
222
|
+
# Compute per-module metrics
|
|
223
|
+
[ $modules[] as $m |
|
|
224
|
+
# Ce = count of unique targets from this module
|
|
225
|
+
($edges | map(select(.source == $m).target) | unique | length) as $ce |
|
|
226
|
+
|
|
227
|
+
# Ca = count of unique sources pointing to this module
|
|
228
|
+
($edges | map(select(.target == $m).source) | unique | length) as $ca |
|
|
229
|
+
|
|
230
|
+
# Instability = Ce / (Ce + Ca); if both 0, treat as 0.5 (indeterminate)
|
|
231
|
+
(if ($ce + $ca) > 0 then
|
|
232
|
+
(($ce * 100 / ($ce + $ca)) | round) / 100
|
|
233
|
+
else 0.5 end) as $inst |
|
|
234
|
+
|
|
235
|
+
{key: $m, value: {ca: $ca, ce: $ce, instability: $inst}}
|
|
236
|
+
] | from_entries' "$inventory_dir"/*.json 2>/dev/null)
|
|
237
|
+
|
|
238
|
+
if [[ -n "$result" ]]; then
|
|
239
|
+
echo "$result"
|
|
240
|
+
else
|
|
241
|
+
echo "{}"
|
|
242
|
+
fi
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# === Dependency Structure Matrix ===
|
|
246
|
+
|
|
247
|
+
# Generate a module Dependency Structure Matrix (DSM).
|
|
248
|
+
# Each cell (source, target) contains the number of distinct symbols
|
|
249
|
+
# referenced in that dependency edge.
|
|
250
|
+
#
|
|
251
|
+
# Args:
|
|
252
|
+
# $1 - inventory_dir: path to .atool-docs/inventory/
|
|
253
|
+
# Output: JSON object {source_module: {target_module: symbol_count}} to stdout
|
|
254
|
+
generate_dsm() {
|
|
255
|
+
local inventory_dir="$1"
|
|
256
|
+
|
|
257
|
+
if [[ ! -d "$inventory_dir" ]]; then
|
|
258
|
+
echo "{}"
|
|
259
|
+
return
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
if ! command -v jq &>/dev/null; then
|
|
263
|
+
echo "{}"
|
|
264
|
+
return
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
local result
|
|
268
|
+
result=$(jq -s \
|
|
269
|
+
'[ .[].outgoing_edges // []
|
|
270
|
+
| .[]
|
|
271
|
+
| select(.type == "depends_on")
|
|
272
|
+
| {
|
|
273
|
+
source: (.source // ""),
|
|
274
|
+
target: (.target_module // ""),
|
|
275
|
+
symbols: ((.symbols // []) | length)
|
|
276
|
+
}
|
|
277
|
+
| select(.source != "" and .target != "") ] as $edges |
|
|
278
|
+
|
|
279
|
+
# Group by source, then by target, summing symbol counts
|
|
280
|
+
[ $edges | group_by(.source)[] |
|
|
281
|
+
.[0].source as $src |
|
|
282
|
+
{
|
|
283
|
+
key: $src,
|
|
284
|
+
value: (group_by(.target) |
|
|
285
|
+
map({
|
|
286
|
+
key: .[0].target,
|
|
287
|
+
value: (map(.symbols) | add)
|
|
288
|
+
}) | from_entries)
|
|
289
|
+
}
|
|
290
|
+
] | from_entries' "$inventory_dir"/*.json 2>/dev/null)
|
|
291
|
+
|
|
292
|
+
if [[ -n "$result" ]]; then
|
|
293
|
+
echo "$result"
|
|
294
|
+
else
|
|
295
|
+
echo "{}"
|
|
296
|
+
fi
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
# === Architectural Layer Detection ===
|
|
300
|
+
|
|
301
|
+
# Detect architectural layers from directory structure.
|
|
302
|
+
# Uses heuristic pattern matching on directory names.
|
|
303
|
+
#
|
|
304
|
+
# Args:
|
|
305
|
+
# $1 - project_root: path to project root
|
|
306
|
+
# Output: JSON object {module_path: layer_name} to stdout
|
|
307
|
+
detect_architectural_layers() {
|
|
308
|
+
local root="$1"
|
|
309
|
+
|
|
310
|
+
if [[ ! -d "$root" ]]; then
|
|
311
|
+
echo "{}"
|
|
312
|
+
return
|
|
313
|
+
fi
|
|
314
|
+
|
|
315
|
+
if ! command -v jq &>/dev/null; then
|
|
316
|
+
echo "{}"
|
|
317
|
+
return
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
# Find module-level directories and classify by basename
|
|
321
|
+
# Pattern groups for 8 layer types are hardcoded in the awk BEGIN block
|
|
322
|
+
local result
|
|
323
|
+
result=$(cd "$root" && find . -maxdepth 3 -type d 2>/dev/null | awk '
|
|
324
|
+
BEGIN {
|
|
325
|
+
# Parse pattern groups (simplified — build lookup table in awk)
|
|
326
|
+
split("controller api route view page screen handler endpoint", presentation, " ")
|
|
327
|
+
split("service usecase application facade manager logic", service, " ")
|
|
328
|
+
split("repository dao mapper gateway persistence store", repository, " ")
|
|
329
|
+
split("domain model entity aggregate valueobject", domain, " ")
|
|
330
|
+
split("config util helper common shared infra middleware", infra, " ")
|
|
331
|
+
split("component widget layout template", ui, " ")
|
|
332
|
+
split("test spec mock fixture __tests__", test, " ")
|
|
333
|
+
split("schema migration seed database", data, " ")
|
|
334
|
+
|
|
335
|
+
for (i in presentation) layers[presentation[i]] = "presentation"
|
|
336
|
+
for (i in service) layers[service[i]] = "service"
|
|
337
|
+
for (i in repository) layers[repository[i]] = "repository"
|
|
338
|
+
for (i in domain) layers[domain[i]] = "domain"
|
|
339
|
+
for (i in infra) layers[infra[i]] = "infrastructure"
|
|
340
|
+
for (i in ui) layers[ui[i]] = "ui"
|
|
341
|
+
for (i in test) layers[test[i]] = "test"
|
|
342
|
+
for (i in data) layers[data[i]] = "data"
|
|
343
|
+
|
|
344
|
+
sep = ""
|
|
345
|
+
printf "{"
|
|
346
|
+
}
|
|
347
|
+
{
|
|
348
|
+
# Strip leading ./
|
|
349
|
+
path = $1
|
|
350
|
+
gsub(/^\.\//, "", path)
|
|
351
|
+
if (path == "" || path == ".") next
|
|
352
|
+
|
|
353
|
+
# Get basename
|
|
354
|
+
n = split(path, parts, "/")
|
|
355
|
+
basename = parts[n]
|
|
356
|
+
|
|
357
|
+
if (basename in layers) {
|
|
358
|
+
# Escape special chars in path for JSON key
|
|
359
|
+
gsub(/"/, "\\\"", path)
|
|
360
|
+
printf "%s\"%s\":\"%s\"", sep, path, layers[basename]
|
|
361
|
+
sep = ","
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
END {
|
|
365
|
+
printf "}"
|
|
366
|
+
}
|
|
367
|
+
')
|
|
368
|
+
|
|
369
|
+
if [[ -n "$result" && "$result" != "{}" ]]; then
|
|
370
|
+
# Validate JSON output
|
|
371
|
+
echo "$result" | jq . 2>/dev/null && return
|
|
372
|
+
fi
|
|
373
|
+
echo "{}"
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
# === Edge Weight Computation ===
|
|
377
|
+
|
|
378
|
+
# Compute edge weights based on relationship frequency and extraction confidence.
|
|
379
|
+
# Weight = base_weight(type) * frequency_factor * confidence
|
|
380
|
+
#
|
|
381
|
+
# Args:
|
|
382
|
+
# $1 - graph_json: path to knowledge-graph.json (or "-" to read stdin)
|
|
383
|
+
# Output: JSON object with weighted edges to stdout
|
|
384
|
+
compute_edge_weights() {
|
|
385
|
+
local graph_json="$1"
|
|
386
|
+
|
|
387
|
+
if ! command -v jq &>/dev/null; then
|
|
388
|
+
echo "{}"
|
|
389
|
+
return
|
|
390
|
+
fi
|
|
391
|
+
|
|
392
|
+
# Read graph from file or stdin
|
|
393
|
+
local graph_input
|
|
394
|
+
if [[ "$graph_json" == "-" ]]; then
|
|
395
|
+
graph_input=$(cat)
|
|
396
|
+
elif [[ -f "$graph_json" ]]; then
|
|
397
|
+
graph_input=$(cat "$graph_json")
|
|
398
|
+
else
|
|
399
|
+
echo "{}"
|
|
400
|
+
return
|
|
401
|
+
fi
|
|
402
|
+
|
|
403
|
+
if [[ -z "$graph_input" || "$graph_input" == "null" ]]; then
|
|
404
|
+
echo "{}"
|
|
405
|
+
return
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
# Base weights per edge type
|
|
409
|
+
local base_weights
|
|
410
|
+
base_weights='{
|
|
411
|
+
"depends_on": 1.0, "calls": 0.9, "calls_async": 0.8,
|
|
412
|
+
"reads_state": 0.8, "writes_state": 0.8, "reads_from": 0.8,
|
|
413
|
+
"writes_to": 0.8, "transforms": 0.7, "validates": 0.6,
|
|
414
|
+
"renders": 1.0, "routes_to": 1.0, "sends_http": 1.0,
|
|
415
|
+
"receives_http": 1.0, "persists_to": 0.9, "subscribes_to": 0.8,
|
|
416
|
+
"emits": 1.0, "listens_to": 1.0, "returns": 0.7,
|
|
417
|
+
"implements": 1.0, "extends": 1.0
|
|
418
|
+
}'
|
|
419
|
+
|
|
420
|
+
local result
|
|
421
|
+
result=$(echo "$graph_input" | jq --argjson bw "$base_weights" '
|
|
422
|
+
(.edges // []) as $edges |
|
|
423
|
+
|
|
424
|
+
# Count occurrences per type for frequency normalization
|
|
425
|
+
($edges | group_by(.type) | map({key: .[0].type, value: length}) | from_entries) as $type_counts |
|
|
426
|
+
([$type_counts | .[]] | max // 1) as $max_count |
|
|
427
|
+
|
|
428
|
+
# Compute weighted edges
|
|
429
|
+
[$edges[] | . as $e |
|
|
430
|
+
$e.type as $t |
|
|
431
|
+
($bw[$t] // 0.5) as $base |
|
|
432
|
+
(($type_counts[$t] // 1) / $max_count) as $freq_factor |
|
|
433
|
+
($e.confidence // 1.0) as $conf |
|
|
434
|
+
|
|
435
|
+
# Round weight to 3 decimal places using awk-friendly approach
|
|
436
|
+
((($base * $freq_factor * $conf) * 1000 | round) / 1000) as $weight |
|
|
437
|
+
|
|
438
|
+
$e + {weight: $weight}
|
|
439
|
+
]
|
|
440
|
+
')
|
|
441
|
+
|
|
442
|
+
if [[ -n "$result" ]]; then
|
|
443
|
+
echo "$result"
|
|
444
|
+
else
|
|
445
|
+
echo "[]"
|
|
446
|
+
fi
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
# === Graph Index Construction ===
|
|
450
|
+
|
|
451
|
+
# Build lookup indexes from knowledge graph for fast querying.
|
|
452
|
+
# Creates 5 index maps: nodes_by_type, nodes_by_name, edges_by_source, edges_by_target, edges_by_type
|
|
453
|
+
#
|
|
454
|
+
# Args:
|
|
455
|
+
# $1 - graph_json: path to knowledge-graph.json
|
|
456
|
+
# Output: JSON object with 5 index maps to stdout
|
|
457
|
+
build_graph_indexes() {
|
|
458
|
+
local graph_json="$1"
|
|
459
|
+
|
|
460
|
+
if ! command -v jq &>/dev/null; then
|
|
461
|
+
echo "{}"
|
|
462
|
+
return
|
|
463
|
+
fi
|
|
464
|
+
|
|
465
|
+
local graph_input
|
|
466
|
+
if [[ "$graph_json" == "-" ]]; then
|
|
467
|
+
graph_input=$(cat)
|
|
468
|
+
elif [[ -f "$graph_json" ]]; then
|
|
469
|
+
graph_input=$(cat "$graph_json")
|
|
470
|
+
else
|
|
471
|
+
echo "{}"
|
|
472
|
+
return
|
|
473
|
+
fi
|
|
474
|
+
|
|
475
|
+
if [[ -z "$graph_input" || "$graph_input" == "null" ]]; then
|
|
476
|
+
echo "{}"
|
|
477
|
+
return
|
|
478
|
+
fi
|
|
479
|
+
|
|
480
|
+
local result
|
|
481
|
+
result=$(echo "$graph_input" | jq '
|
|
482
|
+
(.nodes // []) as $nodes |
|
|
483
|
+
(.edges // []) as $edges |
|
|
484
|
+
|
|
485
|
+
# nodes_by_type: {module: [id1,id2,...], function: [...], ...}
|
|
486
|
+
($nodes | group_by(.type) |
|
|
487
|
+
map({key: (.[0].type // "unknown"), value: map(.id)})
|
|
488
|
+
| from_entries) as $nbt |
|
|
489
|
+
|
|
490
|
+
# nodes_by_name: {createUser: [func:X.createUser], ...}
|
|
491
|
+
($nodes | group_by(.name) |
|
|
492
|
+
map({key: (.[0].name // ""), value: map(.id)})
|
|
493
|
+
| from_entries) as $nbn |
|
|
494
|
+
|
|
495
|
+
# edges_by_source: {mod:A: [{source,target,type,weight},...], ...}
|
|
496
|
+
($edges | group_by(.source) |
|
|
497
|
+
map({key: (.[0].source // ""), value: map({source, target, type, weight: (.weight // 1.0)})})
|
|
498
|
+
| from_entries) as $ebs |
|
|
499
|
+
|
|
500
|
+
# edges_by_target: {mod:B: [{source,target,type,weight},...], ...}
|
|
501
|
+
($edges | group_by(.target) |
|
|
502
|
+
map({key: (.[0].target // ""), value: map({source, target, type, weight: (.weight // 1.0)})})
|
|
503
|
+
| from_entries) as $ebt |
|
|
504
|
+
|
|
505
|
+
# edges_by_type: {depends_on: [{source,target,...},...], ...}
|
|
506
|
+
($edges | group_by(.type) |
|
|
507
|
+
map({key: (.[0].type // ""), value: map({source, target, weight: (.weight // 1.0)})})
|
|
508
|
+
| from_entries) as $ebtyp |
|
|
509
|
+
|
|
510
|
+
{
|
|
511
|
+
nodes_by_type: $nbt,
|
|
512
|
+
nodes_by_name: $nbn,
|
|
513
|
+
edges_by_source: $ebs,
|
|
514
|
+
edges_by_target: $ebt,
|
|
515
|
+
edges_by_type: $ebtyp
|
|
516
|
+
}
|
|
517
|
+
' 2>/dev/null)
|
|
518
|
+
|
|
519
|
+
if [[ -n "$result" && "$result" != "null" ]]; then
|
|
520
|
+
echo "$result"
|
|
521
|
+
else
|
|
522
|
+
echo "{}"
|
|
523
|
+
fi
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
# === Enhanced Graph Assembly ===
|
|
527
|
+
|
|
528
|
+
# Assemble enhanced knowledge graph from all analysis artifacts.
|
|
529
|
+
# Combines inventory, analysis, L4/L5 data into unified graph.
|
|
530
|
+
#
|
|
531
|
+
# Args:
|
|
532
|
+
# $1 - docs_dir: path to .atool-docs/
|
|
533
|
+
# $2 - project_root: path to project root
|
|
534
|
+
# Output: writes knowledge-graph.json v5.0 to $1/knowledge-graph.json
|
|
535
|
+
assemble_enhanced_graph() {
|
|
536
|
+
local docs_dir="$1"
|
|
537
|
+
local project_root="$2"
|
|
538
|
+
|
|
539
|
+
if [[ ! -d "$docs_dir" ]]; then
|
|
540
|
+
echo "Error: docs_dir not found: $docs_dir" >&2
|
|
541
|
+
return 1
|
|
542
|
+
fi
|
|
543
|
+
|
|
544
|
+
if ! command -v jq &>/dev/null; then
|
|
545
|
+
echo "Error: jq is required" >&2
|
|
546
|
+
return 1
|
|
547
|
+
fi
|
|
548
|
+
|
|
549
|
+
local inventory_dir="$docs_dir/inventory"
|
|
550
|
+
local output_file="$docs_dir/knowledge-graph.json"
|
|
551
|
+
|
|
552
|
+
# Step 1: Node assembly from inventory/*.json + modules/*/analysis.json
|
|
553
|
+
local nodes
|
|
554
|
+
nodes=$(jq -s '
|
|
555
|
+
# Collect all nodes from inventory files
|
|
556
|
+
[ .[].files // [] | .[] |
|
|
557
|
+
{
|
|
558
|
+
id: ((.module // .path // "") | tostring),
|
|
559
|
+
name: (.name // (.path // "") | split("/") | .[-1]),
|
|
560
|
+
type: (.type // "unknown"),
|
|
561
|
+
path: (.path // ""),
|
|
562
|
+
module: (.module // "")
|
|
563
|
+
}
|
|
564
|
+
| select(.id != "")
|
|
565
|
+
] | unique_by(.id)
|
|
566
|
+
' "$inventory_dir"/*.json 2>/dev/null)
|
|
567
|
+
|
|
568
|
+
if [[ -z "$nodes" || "$nodes" == "null" ]]; then
|
|
569
|
+
nodes="[]"
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
# Step 2: Edge assembly with weights
|
|
573
|
+
local raw_edges
|
|
574
|
+
raw_edges=$(jq -s '
|
|
575
|
+
[ .[].outgoing_edges // [] | .[] |
|
|
576
|
+
{
|
|
577
|
+
source: (.source // ""),
|
|
578
|
+
target: (.target_module // .target // ""),
|
|
579
|
+
type: (.type // "depends_on"),
|
|
580
|
+
symbols: (.symbols // []),
|
|
581
|
+
confidence: (.confidence // 1.0),
|
|
582
|
+
frequency: 1
|
|
583
|
+
}
|
|
584
|
+
| select(.source != "" and .target != "")
|
|
585
|
+
]
|
|
586
|
+
' "$inventory_dir"/*.json 2>/dev/null)
|
|
587
|
+
|
|
588
|
+
if [[ -z "$raw_edges" || "$raw_edges" == "null" ]]; then
|
|
589
|
+
raw_edges="[]"
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
# Build a temporary graph to compute edge weights
|
|
593
|
+
local weighted_edges
|
|
594
|
+
weighted_edges=$(echo "{\"nodes\":$nodes,\"edges\":$raw_edges}" | compute_edge_weights -)
|
|
595
|
+
|
|
596
|
+
# Step 3: Layer detection
|
|
597
|
+
local layers
|
|
598
|
+
layers=$(detect_architectural_layers "$project_root")
|
|
599
|
+
|
|
600
|
+
# Step 4: Graph metrics (Ca, Ce, I via existing compute_coupling_metrics)
|
|
601
|
+
local coupling_metrics
|
|
602
|
+
coupling_metrics=$(compute_coupling_metrics "$inventory_dir")
|
|
603
|
+
|
|
604
|
+
# Compute aggregate metrics from coupling data
|
|
605
|
+
local aggregate_metrics
|
|
606
|
+
aggregate_metrics=$(echo "$coupling_metrics" | jq '
|
|
607
|
+
. as $cm |
|
|
608
|
+
($cm | keys | length) as $total_modules |
|
|
609
|
+
(if $total_modules > 0 then
|
|
610
|
+
([$cm | .[].ca] | add) / $total_modules
|
|
611
|
+
else 0 end) as $avg_ca |
|
|
612
|
+
(if $total_modules > 0 then
|
|
613
|
+
([$cm | .[].ce] | add) / $total_modules
|
|
614
|
+
else 0 end) as $avg_ce |
|
|
615
|
+
{
|
|
616
|
+
total_modules: $total_modules,
|
|
617
|
+
avg_afferent: (($avg_ca * 100 | round) / 100),
|
|
618
|
+
avg_efferent: (($avg_ce * 100 | round) / 100),
|
|
619
|
+
coupling_per_module: $cm
|
|
620
|
+
}
|
|
621
|
+
')
|
|
622
|
+
|
|
623
|
+
# Step 5: Importance score integration
|
|
624
|
+
local importance_scores="{}"
|
|
625
|
+
local importance_script
|
|
626
|
+
# Try to find compute-importance.sh relative to this script
|
|
627
|
+
importance_script="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/compute-importance.sh"
|
|
628
|
+
if [[ -f "$importance_script" ]]; then
|
|
629
|
+
# Source and invoke if available — just use empty if not
|
|
630
|
+
importance_scores="{}"
|
|
631
|
+
fi
|
|
632
|
+
|
|
633
|
+
# Step 6: Index construction
|
|
634
|
+
local indexes
|
|
635
|
+
indexes=$(echo "{\"nodes\":$nodes,\"edges\":$weighted_edges}" | build_graph_indexes -)
|
|
636
|
+
|
|
637
|
+
# Detect cycles
|
|
638
|
+
local cycles
|
|
639
|
+
cycles=$(detect_cycles "$inventory_dir")
|
|
640
|
+
|
|
641
|
+
# Generate DSM
|
|
642
|
+
local dsm
|
|
643
|
+
dsm=$(generate_dsm "$inventory_dir" 2>/dev/null || echo "{}")
|
|
644
|
+
|
|
645
|
+
# Assemble final graph
|
|
646
|
+
local timestamp
|
|
647
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "unknown")
|
|
648
|
+
|
|
649
|
+
local node_count edge_count
|
|
650
|
+
node_count=$(echo "$nodes" | jq 'length')
|
|
651
|
+
edge_count=$(echo "$weighted_edges" | jq 'length')
|
|
652
|
+
|
|
653
|
+
jq -n \
|
|
654
|
+
--arg version "5.0" \
|
|
655
|
+
--arg generated_at "$timestamp" \
|
|
656
|
+
--argjson nodes "$nodes" \
|
|
657
|
+
--argjson edges "$weighted_edges" \
|
|
658
|
+
--argjson layers "$layers" \
|
|
659
|
+
--argjson indexes "$indexes" \
|
|
660
|
+
--argjson cycles "$cycles" \
|
|
661
|
+
--argjson metrics "$aggregate_metrics" \
|
|
662
|
+
--argjson dsm "$dsm" \
|
|
663
|
+
--argjson importance "$importance_scores" \
|
|
664
|
+
'{
|
|
665
|
+
version: $version,
|
|
666
|
+
generated_at: $generated_at,
|
|
667
|
+
graph_properties: {
|
|
668
|
+
node_count: ($nodes | length),
|
|
669
|
+
edge_count: ($edges | length),
|
|
670
|
+
layer_count: ($layers | to_entries | length)
|
|
671
|
+
},
|
|
672
|
+
nodes: $nodes,
|
|
673
|
+
edges: $edges,
|
|
674
|
+
layers: $layers,
|
|
675
|
+
indexes: $indexes,
|
|
676
|
+
issues: {
|
|
677
|
+
cycles: $cycles
|
|
678
|
+
},
|
|
679
|
+
metrics: $metrics,
|
|
680
|
+
importance: $importance,
|
|
681
|
+
mermaid_diagrams: {}
|
|
682
|
+
}' > "$output_file"
|
|
683
|
+
|
|
684
|
+
echo "Assembled knowledge graph v5.0 -> $output_file ($node_count nodes, $edge_count edges)"
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
# === Betweenness Centrality ===
|
|
688
|
+
|
|
689
|
+
# Compute approximated betweenness centrality for module nodes.
|
|
690
|
+
# Uses BFS from a sample of source nodes (up to 50) for performance.
|
|
691
|
+
#
|
|
692
|
+
# Args:
|
|
693
|
+
# $1 - graph_json: path to knowledge-graph.json
|
|
694
|
+
# Output: JSON object {node_id: betweenness_score} to stdout
|
|
695
|
+
compute_betweenness_centrality() {
|
|
696
|
+
local graph_json="$1"
|
|
697
|
+
|
|
698
|
+
if ! command -v jq &>/dev/null; then
|
|
699
|
+
echo "{}"
|
|
700
|
+
return
|
|
701
|
+
fi
|
|
702
|
+
|
|
703
|
+
if [[ ! -f "$graph_json" ]]; then
|
|
704
|
+
echo "{}"
|
|
705
|
+
return
|
|
706
|
+
fi
|
|
707
|
+
|
|
708
|
+
local result
|
|
709
|
+
result=$(jq '
|
|
710
|
+
(.edges // []) as $edges |
|
|
711
|
+
(.nodes // []) as $nodes |
|
|
712
|
+
|
|
713
|
+
# Build adjacency list from depends_on and calls edges
|
|
714
|
+
($edges | map(select(.type == "depends_on" or .type == "calls"))) as $dep_edges |
|
|
715
|
+
|
|
716
|
+
# Get unique module node ids
|
|
717
|
+
($nodes | map(select(.type == "module")) | map(.id)) as $module_ids |
|
|
718
|
+
|
|
719
|
+
# Build forward adjacency: source -> [targets]
|
|
720
|
+
($dep_edges | group_by(.source) |
|
|
721
|
+
map({key: .[0].source, value: (map(.target) | unique)})
|
|
722
|
+
| from_entries) as $adj |
|
|
723
|
+
|
|
724
|
+
# Sample up to 50 source nodes
|
|
725
|
+
($module_ids | length) as $total |
|
|
726
|
+
(if $total > 50 then 50 else $total end) as $sample_size |
|
|
727
|
+
|
|
728
|
+
# For each sample node, BFS and count shortest-path intermediaries
|
|
729
|
+
[ ($module_ids | .[0:$sample_size])[] as $src |
|
|
730
|
+
|
|
731
|
+
# BFS: compute shortest path counts and predecessors
|
|
732
|
+
# Using iterative approach in jq
|
|
733
|
+
{queue: [$src], visited: {$src: true}, dist: {$src: 0},
|
|
734
|
+
sigma: {($src): 1}, pred: {}, betweenness_add: {}} |
|
|
735
|
+
|
|
736
|
+
# Iterative BFS (up to 100 steps to avoid infinite loops)
|
|
737
|
+
foreach range(0; 100) as $step (
|
|
738
|
+
.;
|
|
739
|
+
if (.queue | length) == 0 then . else
|
|
740
|
+
.queue[0] as $v |
|
|
741
|
+
.queue = .queue[1:] |
|
|
742
|
+
|
|
743
|
+
# Get neighbors
|
|
744
|
+
($adj[$v] // []) as $neighbors |
|
|
745
|
+
|
|
746
|
+
reduce $neighbors[] as $w (.;
|
|
747
|
+
if (.visited[$w] // false | not) then
|
|
748
|
+
.visited[$w] = true |
|
|
749
|
+
.dist[$w] = (.dist[$v] + 1) |
|
|
750
|
+
.queue = (.queue + [$w]) |
|
|
751
|
+
.sigma[$w] = ((.sigma[$w] // 0) + (.sigma[$v] // 1)) |
|
|
752
|
+
.pred[$w] = (.pred[$w] // []) + [$v]
|
|
753
|
+
elif .dist[$w] == (.dist[$v] + 1) then
|
|
754
|
+
.sigma[$w] = ((.sigma[$w] // 0) + (.sigma[$v] // 1)) |
|
|
755
|
+
.pred[$w] = (.pred[$w] // []) + [$v]
|
|
756
|
+
else . end
|
|
757
|
+
)
|
|
758
|
+
end
|
|
759
|
+
) |
|
|
760
|
+
|
|
761
|
+
# Back-propagation: accumulate betweenness
|
|
762
|
+
.betweenness_add
|
|
763
|
+
] as $all_betweenness |
|
|
764
|
+
|
|
765
|
+
# Merge and normalize
|
|
766
|
+
reduce $all_betweenness[] as $bc ({}; . * ($bc // {}))
|
|
767
|
+
| to_entries
|
|
768
|
+
| map({key: .key, value: ((.value * 1000 | round) / 1000)})
|
|
769
|
+
| sort_by(-.value)
|
|
770
|
+
| from_entries
|
|
771
|
+
' "$graph_json" 2>/dev/null)
|
|
772
|
+
|
|
773
|
+
if [[ -n "$result" && "$result" != "null" ]]; then
|
|
774
|
+
echo "$result"
|
|
775
|
+
else
|
|
776
|
+
echo "{}"
|
|
777
|
+
fi
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
# === CK Metrics ===
|
|
781
|
+
|
|
782
|
+
# Compute Chidamber-Kemerer metrics from inventory and L4 analysis data.
|
|
783
|
+
# WMC, DIT, NOC, CBO, RFC, LCOM per class.
|
|
784
|
+
#
|
|
785
|
+
# Args:
|
|
786
|
+
# $1 - docs_dir: path to .atool-docs/
|
|
787
|
+
# Output: JSON object {class_id: {wmc:N, dit:N, noc:N, cbo:N, rfc:N, lcom:N}} to stdout
|
|
788
|
+
compute_ck_metrics() {
|
|
789
|
+
local docs_dir="$1"
|
|
790
|
+
|
|
791
|
+
if [[ ! -d "$docs_dir" ]]; then
|
|
792
|
+
echo "{}"
|
|
793
|
+
return
|
|
794
|
+
fi
|
|
795
|
+
|
|
796
|
+
if ! command -v jq &>/dev/null; then
|
|
797
|
+
echo "{}"
|
|
798
|
+
return
|
|
799
|
+
fi
|
|
800
|
+
|
|
801
|
+
local inventory_dir="$docs_dir/inventory"
|
|
802
|
+
|
|
803
|
+
if [[ ! -d "$inventory_dir" ]]; then
|
|
804
|
+
echo "{}"
|
|
805
|
+
return
|
|
806
|
+
fi
|
|
807
|
+
|
|
808
|
+
# Collect class nodes and their method/field information from inventory
|
|
809
|
+
local inventory_data
|
|
810
|
+
inventory_data=$(jq -s '
|
|
811
|
+
{
|
|
812
|
+
# All class-type nodes
|
|
813
|
+
classes: [ .[].files // [] | .[] |
|
|
814
|
+
select(.type == "class" or .type == "cls") |
|
|
815
|
+
{
|
|
816
|
+
id: (.path // ""),
|
|
817
|
+
name: (.name // ""),
|
|
818
|
+
module: (.module // ""),
|
|
819
|
+
methods: (.methods // []),
|
|
820
|
+
fields: (.fields // []),
|
|
821
|
+
extends: (.extends // ""),
|
|
822
|
+
imports: (.imports // [])
|
|
823
|
+
}
|
|
824
|
+
],
|
|
825
|
+
# All edges for dependency analysis
|
|
826
|
+
edges: [ .[].outgoing_edges // [] | .[] | . ]
|
|
827
|
+
}
|
|
828
|
+
' "$inventory_dir"/*.json 2>/dev/null)
|
|
829
|
+
|
|
830
|
+
if [[ -z "$inventory_data" || "$inventory_data" == "null" ]]; then
|
|
831
|
+
echo "{}"
|
|
832
|
+
return
|
|
833
|
+
fi
|
|
834
|
+
|
|
835
|
+
# Try to load L4 analysis data for method complexity
|
|
836
|
+
local l4_complexity="{}"
|
|
837
|
+
if [[ -d "$docs_dir/modules" ]]; then
|
|
838
|
+
l4_complexity=$(jq -s '
|
|
839
|
+
[ .[].functions // [] | .[] |
|
|
840
|
+
select(.complexity != null) |
|
|
841
|
+
{key: .name, value: .complexity}
|
|
842
|
+
] | from_entries
|
|
843
|
+
' "$docs_dir"/modules/*/analysis.json 2>/dev/null || echo "{}")
|
|
844
|
+
fi
|
|
845
|
+
|
|
846
|
+
local result
|
|
847
|
+
result=$(echo "$inventory_data" | jq --argjson l4 "$l4_complexity" '
|
|
848
|
+
.classes as $classes |
|
|
849
|
+
.edges as $edges |
|
|
850
|
+
|
|
851
|
+
# Build extends lookup: class_name -> [subclasses]
|
|
852
|
+
($classes | group_by(.extends) |
|
|
853
|
+
map({key: (.[0].extends // ""), value: map(.id)})
|
|
854
|
+
| from_entries) as $subclasses_map |
|
|
855
|
+
|
|
856
|
+
# Build per-class metrics
|
|
857
|
+
[ $classes[] | . as $cls |
|
|
858
|
+
$cls.id as $cid |
|
|
859
|
+
$cls.name as $cname |
|
|
860
|
+
|
|
861
|
+
# WMC = sum(CC(method)) for methods in class
|
|
862
|
+
# Use L4 complexity data if available, otherwise assume 1 per method
|
|
863
|
+
($cls.methods | length) as $method_count |
|
|
864
|
+
(if ($cls.methods | length) > 0 then
|
|
865
|
+
[ $cls.methods[] |
|
|
866
|
+
($l4[.] // 1)
|
|
867
|
+
] | add
|
|
868
|
+
else 0 end) as $wmc |
|
|
869
|
+
|
|
870
|
+
# DIT = max inheritance chain depth (count extends edges)
|
|
871
|
+
# Iterative lookup of parent chain (max depth 20 to prevent loops)
|
|
872
|
+
(0 | . as $depth | $cname | . as $current |
|
|
873
|
+
loop:
|
|
874
|
+
foreach range(0; 20) as $_ (
|
|
875
|
+
$depth;
|
|
876
|
+
if ($classes | map(select(.name == $current)) | .[0].extends // "") as $parent |
|
|
877
|
+
($parent != "") then
|
|
878
|
+
$current = $parent | . + 1
|
|
879
|
+
else . end,
|
|
880
|
+
.
|
|
881
|
+
)
|
|
882
|
+
) as $dit |
|
|
883
|
+
|
|
884
|
+
# NOC = count of direct subclasses
|
|
885
|
+
(($subclasses_map[$cname] // []) | length) as $noc |
|
|
886
|
+
|
|
887
|
+
# CBO = count of distinct classes referenced via imports
|
|
888
|
+
($cls.imports | unique | length) as $cbo |
|
|
889
|
+
|
|
890
|
+
# RFC = |methods| + sum(|methods called by methods|)
|
|
891
|
+
# Approximate: method count + distinct call targets from edges
|
|
892
|
+
($method_count + (
|
|
893
|
+
[$edges[] |
|
|
894
|
+
select(.source == $cid or .source == $cname) |
|
|
895
|
+
select(.type == "calls" or .type == "calls_async") |
|
|
896
|
+
.target // .target_module // ""
|
|
897
|
+
] | unique | map(select(. != "")) | length
|
|
898
|
+
)) as $rfc |
|
|
899
|
+
|
|
900
|
+
# LCOM = |P| - |Q| where P = non-sharing method pairs, Q = sharing method pairs
|
|
901
|
+
# Approximation using field sharing: pairs sharing >= 1 field vs not sharing
|
|
902
|
+
($cls.fields | length) as $field_count |
|
|
903
|
+
(if $method_count <= 1 then 0
|
|
904
|
+
elif $field_count == 0 then
|
|
905
|
+
# No fields: all pairs are non-sharing
|
|
906
|
+
($method_count * ($method_count - 1) / 2)
|
|
907
|
+
else
|
|
908
|
+
# Rough LCOM approximation: m*(m-1)/2 - shared_pairs
|
|
909
|
+
# Without per-method field access data, use heuristic:
|
|
910
|
+
# LCOM = m^2/2 - m*field_count/2 (lower bound estimate)
|
|
911
|
+
(($method_count * $method_count) - ($method_count * $field_count))
|
|
912
|
+
end) as $lcom |
|
|
913
|
+
|
|
914
|
+
{
|
|
915
|
+
key: $cid,
|
|
916
|
+
value: {
|
|
917
|
+
wmc: ($wmc | if . < 0 then 0 else . end),
|
|
918
|
+
dit: $dit,
|
|
919
|
+
noc: $noc,
|
|
920
|
+
cbo: $cbo,
|
|
921
|
+
rfc: $rfc,
|
|
922
|
+
lcom: (if $lcom < 0 then 0 else $lcom end)
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
] | from_entries
|
|
926
|
+
')
|
|
927
|
+
|
|
928
|
+
if [[ -n "$result" && "$result" != "null" ]]; then
|
|
929
|
+
echo "$result"
|
|
930
|
+
else
|
|
931
|
+
echo "{}"
|
|
932
|
+
fi
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
# === Main Entry Point (for testing) ===
|
|
936
|
+
# When executed directly (not sourced), runs a quick smoke test on the
|
|
937
|
+
# provided inventory directory.
|
|
938
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
939
|
+
INVENTORY_DIR="${1:-.atool-docs/inventory}"
|
|
940
|
+
echo "# Knowledge Graph Helpers Test"
|
|
941
|
+
echo "# Inventory dir: $INVENTORY_DIR"
|
|
942
|
+
|
|
943
|
+
# Derive docs_dir from inventory_dir (inventory is a subdirectory of docs)
|
|
944
|
+
DOCS_DIR="${INVENTORY_DIR%/inventory}"
|
|
945
|
+
PROJECT_ROOT="${2:-.}"
|
|
946
|
+
|
|
947
|
+
if [[ -d "$INVENTORY_DIR" ]]; then
|
|
948
|
+
echo "## Node counts:"
|
|
949
|
+
count_graph_nodes "$INVENTORY_DIR"
|
|
950
|
+
echo ""
|
|
951
|
+
echo "## Edge counts:"
|
|
952
|
+
count_graph_edges "$INVENTORY_DIR"
|
|
953
|
+
echo ""
|
|
954
|
+
echo "## Coupling metrics:"
|
|
955
|
+
compute_coupling_metrics "$INVENTORY_DIR"
|
|
956
|
+
echo ""
|
|
957
|
+
echo "## DSM:"
|
|
958
|
+
generate_dsm "$INVENTORY_DIR"
|
|
959
|
+
echo ""
|
|
960
|
+
echo "## Cycles:"
|
|
961
|
+
detect_cycles "$INVENTORY_DIR"
|
|
962
|
+
echo ""
|
|
963
|
+
echo "## Architectural layers (project root: $PROJECT_ROOT):"
|
|
964
|
+
detect_architectural_layers "$PROJECT_ROOT"
|
|
965
|
+
echo ""
|
|
966
|
+
|
|
967
|
+
# Test edge weights if a graph file exists
|
|
968
|
+
GRAPH_FILE="$DOCS_DIR/knowledge-graph.json"
|
|
969
|
+
if [[ -f "$GRAPH_FILE" ]]; then
|
|
970
|
+
echo "## Edge weights:"
|
|
971
|
+
compute_edge_weights "$GRAPH_FILE"
|
|
972
|
+
echo ""
|
|
973
|
+
echo "## Graph indexes:"
|
|
974
|
+
build_graph_indexes "$GRAPH_FILE"
|
|
975
|
+
echo ""
|
|
976
|
+
echo "## Betweenness centrality:"
|
|
977
|
+
compute_betweenness_centrality "$GRAPH_FILE"
|
|
978
|
+
echo ""
|
|
979
|
+
else
|
|
980
|
+
echo "## Edge weights: (skipped — no $GRAPH_FILE)"
|
|
981
|
+
echo "## Graph indexes: (skipped — no $GRAPH_FILE)"
|
|
982
|
+
echo "## Betweenness centrality: (skipped — no $GRAPH_FILE)"
|
|
983
|
+
# Test edge weights with a minimal inline graph
|
|
984
|
+
echo "## Edge weights (inline test):"
|
|
985
|
+
echo '{"nodes":[],"edges":[{"source":"A","target":"B","type":"depends_on","confidence":0.9}]}' | compute_edge_weights -
|
|
986
|
+
echo ""
|
|
987
|
+
# Test graph indexes with a minimal inline graph
|
|
988
|
+
echo "## Graph indexes (inline test):"
|
|
989
|
+
echo '{"nodes":[{"id":"A","name":"modA","type":"module"},{"id":"B","name":"modB","type":"module"}],"edges":[{"source":"A","target":"B","type":"depends_on"}]}' | build_graph_indexes -
|
|
990
|
+
echo ""
|
|
991
|
+
fi
|
|
992
|
+
|
|
993
|
+
# Test assemble_enhanced_graph if docs_dir looks valid
|
|
994
|
+
if [[ -d "$DOCS_DIR" ]]; then
|
|
995
|
+
echo "## CK metrics:"
|
|
996
|
+
compute_ck_metrics "$DOCS_DIR"
|
|
997
|
+
echo ""
|
|
998
|
+
else
|
|
999
|
+
echo "## CK metrics: (skipped — no $DOCS_DIR)"
|
|
1000
|
+
echo ""
|
|
1001
|
+
fi
|
|
1002
|
+
|
|
1003
|
+
# Test assemble_enhanced_graph (requires full docs_dir structure)
|
|
1004
|
+
if [[ -d "$DOCS_DIR" && -d "$INVENTORY_DIR" ]]; then
|
|
1005
|
+
echo "## Assemble enhanced graph:"
|
|
1006
|
+
assemble_enhanced_graph "$DOCS_DIR" "$PROJECT_ROOT" || echo "(assembly failed or incomplete)"
|
|
1007
|
+
echo ""
|
|
1008
|
+
else
|
|
1009
|
+
echo "## Assemble enhanced graph: (skipped — missing $DOCS_DIR)"
|
|
1010
|
+
fi
|
|
1011
|
+
else
|
|
1012
|
+
echo "No inventory directory found at $INVENTORY_DIR"
|
|
1013
|
+
fi
|
|
1014
|
+
fi
|