jigane 0.1.0__tar.gz
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.
- jigane-0.1.0/LICENSE +21 -0
- jigane-0.1.0/PKG-INFO +12 -0
- jigane-0.1.0/jigane/README.md +111 -0
- jigane-0.1.0/jigane/__init__.py +1 -0
- jigane-0.1.0/jigane/__main__.py +5 -0
- jigane-0.1.0/jigane/commands/README.md +70 -0
- jigane-0.1.0/jigane/commands/__init__.py +103 -0
- jigane-0.1.0/jigane/commands/budget_argument.py +31 -0
- jigane-0.1.0/jigane/commands/consent.py +218 -0
- jigane-0.1.0/jigane/commands/context.py +44 -0
- jigane-0.1.0/jigane/commands/exit_codes.py +8 -0
- jigane-0.1.0/jigane/commands/failures.py +356 -0
- jigane-0.1.0/jigane/commands/generation.py +73 -0
- jigane-0.1.0/jigane/commands/group_argument.py +23 -0
- jigane-0.1.0/jigane/commands/mcp_argument.py +47 -0
- jigane-0.1.0/jigane/commands/output.py +124 -0
- jigane-0.1.0/jigane/commands/public_urls.py +43 -0
- jigane-0.1.0/jigane/commands/run_guide.py +68 -0
- jigane-0.1.0/jigane/commands/subcommands/__init__.py +1 -0
- jigane-0.1.0/jigane/commands/subcommands/archive.py +36 -0
- jigane-0.1.0/jigane/commands/subcommands/delete.py +102 -0
- jigane-0.1.0/jigane/commands/subcommands/design.py +302 -0
- jigane-0.1.0/jigane/commands/subcommands/diagrams.py +103 -0
- jigane-0.1.0/jigane/commands/subcommands/estimate.py +225 -0
- jigane-0.1.0/jigane/commands/subcommands/export_reply.py +25 -0
- jigane-0.1.0/jigane/commands/subcommands/export_report.py +23 -0
- jigane-0.1.0/jigane/commands/subcommands/flaky.py +70 -0
- jigane-0.1.0/jigane/commands/subcommands/hold.py +26 -0
- jigane-0.1.0/jigane/commands/subcommands/languages.py +411 -0
- jigane-0.1.0/jigane/commands/subcommands/list.py +74 -0
- jigane-0.1.0/jigane/commands/subcommands/new.py +125 -0
- jigane-0.1.0/jigane/commands/subcommands/object.py +27 -0
- jigane-0.1.0/jigane/commands/subcommands/pick.py +32 -0
- jigane-0.1.0/jigane/commands/subcommands/refresh.py +111 -0
- jigane-0.1.0/jigane/commands/subcommands/run.py +470 -0
- jigane-0.1.0/jigane/commands/subcommands/sequence.py +52 -0
- jigane-0.1.0/jigane/commands/subcommands/show.py +378 -0
- jigane-0.1.0/jigane/commands/subcommands/technology_list.py +48 -0
- jigane-0.1.0/jigane/commands/subcommands/templates.py +27 -0
- jigane-0.1.0/jigane/commands/subcommands/unarchive.py +18 -0
- jigane-0.1.0/jigane/commands/subcommands/unhold.py +26 -0
- jigane-0.1.0/jigane/commands/subcommands/unobject.py +26 -0
- jigane-0.1.0/jigane/commands/subcommands/unpick.py +28 -0
- jigane-0.1.0/jigane/commands/subcommands/usage.py +518 -0
- jigane-0.1.0/jigane/commands/terms_consent.py +54 -0
- jigane-0.1.0/jigane/commands/views/__init__.py +1 -0
- jigane-0.1.0/jigane/commands/views/findings.py +280 -0
- jigane-0.1.0/jigane/commands/views/review.py +249 -0
- jigane-0.1.0/jigane/design/README.md +97 -0
- jigane-0.1.0/jigane/design/__init__.py +0 -0
- jigane-0.1.0/jigane/design/division.py +327 -0
- jigane-0.1.0/jigane/design/mentions.py +298 -0
- jigane-0.1.0/jigane/design/mermaid.py +826 -0
- jigane-0.1.0/jigane/design/parse.py +249 -0
- jigane-0.1.0/jigane/design/recommend.py +995 -0
- jigane-0.1.0/jigane/design/review_documents.py +269 -0
- jigane-0.1.0/jigane/design/section_map.py +79 -0
- jigane-0.1.0/jigane/figures/README.md +21 -0
- jigane-0.1.0/jigane/figures/__init__.py +1 -0
- jigane-0.1.0/jigane/figures/models.py +482 -0
- jigane-0.1.0/jigane/findings/README.md +21 -0
- jigane-0.1.0/jigane/findings/__init__.py +1 -0
- jigane-0.1.0/jigane/findings/discard.py +777 -0
- jigane-0.1.0/jigane/findings/discarded_nits.py +89 -0
- jigane-0.1.0/jigane/findings/records.py +1943 -0
- jigane-0.1.0/jigane/flaky/README.md +72 -0
- jigane-0.1.0/jigane/flaky/__init__.py +0 -0
- jigane-0.1.0/jigane/flaky/catalog.py +260 -0
- jigane-0.1.0/jigane/flaky/catalog.toml +283 -0
- jigane-0.1.0/jigane/flaky/checks.py +1081 -0
- jigane-0.1.0/jigane/flaky/detector.py +157 -0
- jigane-0.1.0/jigane/flaky/explanation.py +33 -0
- jigane-0.1.0/jigane/flaky/finding.py +62 -0
- jigane-0.1.0/jigane/flaky/gates.py +159 -0
- jigane-0.1.0/jigane/flaky/gates_v1.py +40 -0
- jigane-0.1.0/jigane/flaky/queries/csharp.toml +122 -0
- jigane-0.1.0/jigane/flaky/queries/java.toml +160 -0
- jigane-0.1.0/jigane/flaky/queries/python.toml +114 -0
- jigane-0.1.0/jigane/flaky/queries/swift.toml +122 -0
- jigane-0.1.0/jigane/flaky/setup.py +158 -0
- jigane-0.1.0/jigane/flaky/target.py +167 -0
- jigane-0.1.0/jigane/gates/README.md +23 -0
- jigane-0.1.0/jigane/gates/__init__.py +1 -0
- jigane-0.1.0/jigane/gates/entry.py +296 -0
- jigane-0.1.0/jigane/gates/explanation.py +30 -0
- jigane-0.1.0/jigane/gates/line_scope.py +25 -0
- jigane-0.1.0/jigane/git/README.md +22 -0
- jigane-0.1.0/jigane/git/__init__.py +1 -0
- jigane-0.1.0/jigane/git/environment.py +7 -0
- jigane-0.1.0/jigane/git/process.py +255 -0
- jigane-0.1.0/jigane/git/repository.py +74 -0
- jigane-0.1.0/jigane/git/tree_files.py +139 -0
- jigane-0.1.0/jigane/groups/README.md +20 -0
- jigane-0.1.0/jigane/groups/__init__.py +1 -0
- jigane-0.1.0/jigane/groups/definitions.py +81 -0
- jigane-0.1.0/jigane/groups/policy_runs.py +136 -0
- jigane-0.1.0/jigane/judgment_map/README.md +28 -0
- jigane-0.1.0/jigane/judgment_map/__init__.py +1 -0
- jigane-0.1.0/jigane/judgment_map/allow_annotation.py +72 -0
- jigane-0.1.0/jigane/judgment_map/changes.py +120 -0
- jigane-0.1.0/jigane/judgment_map/classification.py +863 -0
- jigane-0.1.0/jigane/judgment_map/configuration.py +56 -0
- jigane-0.1.0/jigane/judgment_map/declaration.py +304 -0
- jigane-0.1.0/jigane/judgment_map/kinds.py +65 -0
- jigane-0.1.0/jigane/judgment_map/map_kind.py +240 -0
- jigane-0.1.0/jigane/judgment_map/region_table.py +198 -0
- jigane-0.1.0/jigane/languages/README.md +110 -0
- jigane-0.1.0/jigane/languages/__init__.py +0 -0
- jigane-0.1.0/jigane/languages/api/README.md +12 -0
- jigane-0.1.0/jigane/languages/api/__init__.py +41 -0
- jigane-0.1.0/jigane/languages/api/capabilities.py +143 -0
- jigane-0.1.0/jigane/languages/base.py +998 -0
- jigane-0.1.0/jigane/languages/bridges/csharp.toml +18 -0
- jigane-0.1.0/jigane/languages/bridges/java.toml +12 -0
- jigane-0.1.0/jigane/languages/bridges/python.toml +18 -0
- jigane-0.1.0/jigane/languages/bridges/swift.toml +52 -0
- jigane-0.1.0/jigane/languages/calls_core.py +276 -0
- jigane-0.1.0/jigane/languages/concurrency_core.py +226 -0
- jigane-0.1.0/jigane/languages/csharp.py +635 -0
- jigane-0.1.0/jigane/languages/csharp_concurrency.py +559 -0
- jigane-0.1.0/jigane/languages/csharp_regex.py +290 -0
- jigane-0.1.0/jigane/languages/csharp_xaml.py +61 -0
- jigane-0.1.0/jigane/languages/data_places.py +43 -0
- jigane-0.1.0/jigane/languages/fetch_main.py +67 -0
- jigane-0.1.0/jigane/languages/grammar-hashes.json +38 -0
- jigane-0.1.0/jigane/languages/grammars.py +295 -0
- jigane-0.1.0/jigane/languages/java.py +655 -0
- jigane-0.1.0/jigane/languages/java_concurrency.py +479 -0
- jigane-0.1.0/jigane/languages/java_regex.py +212 -0
- jigane-0.1.0/jigane/languages/plugin.py +53 -0
- jigane-0.1.0/jigane/languages/plugins.py +412 -0
- jigane-0.1.0/jigane/languages/python.py +723 -0
- jigane-0.1.0/jigane/languages/python_arguments.py +246 -0
- jigane-0.1.0/jigane/languages/python_concurrency.py +284 -0
- jigane-0.1.0/jigane/languages/python_file_paths.py +394 -0
- jigane-0.1.0/jigane/languages/python_regex.py +251 -0
- jigane-0.1.0/jigane/languages/python_stores.py +750 -0
- jigane-0.1.0/jigane/languages/python_walk.py +28 -0
- jigane-0.1.0/jigane/languages/queries/csharp.toml +41 -0
- jigane-0.1.0/jigane/languages/queries/java.toml +35 -0
- jigane-0.1.0/jigane/languages/queries/python.toml +39 -0
- jigane-0.1.0/jigane/languages/queries/swift.toml +53 -0
- jigane-0.1.0/jigane/languages/queries.py +42 -0
- jigane-0.1.0/jigane/languages/regex_locals.py +83 -0
- jigane-0.1.0/jigane/languages/registry.py +273 -0
- jigane-0.1.0/jigane/languages/swift.py +862 -0
- jigane-0.1.0/jigane/languages/swift_concurrency.py +565 -0
- jigane-0.1.0/jigane/languages/swift_regex.py +290 -0
- jigane-0.1.0/jigane/languages/type_names.py +107 -0
- jigane-0.1.0/jigane/llm_runs/README.md +33 -0
- jigane-0.1.0/jigane/llm_runs/__init__.py +1 -0
- jigane-0.1.0/jigane/llm_runs/claude.py +716 -0
- jigane-0.1.0/jigane/llm_runs/claude_inputs.py +623 -0
- jigane-0.1.0/jigane/llm_runs/cost.py +133 -0
- jigane-0.1.0/jigane/llm_runs/defaults.py +38 -0
- jigane-0.1.0/jigane/llm_runs/finding_model.py +339 -0
- jigane-0.1.0/jigane/llm_runs/launch_failure.py +637 -0
- jigane-0.1.0/jigane/llm_runs/output_common.py +20 -0
- jigane-0.1.0/jigane/llm_runs/prompt_stamp.py +7 -0
- jigane-0.1.0/jigane/llm_runs/usage_table.py +67 -0
- jigane-0.1.0/jigane/markdown/README.md +20 -0
- jigane-0.1.0/jigane/markdown/__init__.py +1 -0
- jigane-0.1.0/jigane/markdown/blocks.py +94 -0
- jigane-0.1.0/jigane/markdown/cells.py +55 -0
- jigane-0.1.0/jigane/policy_materials/README.md +27 -0
- jigane-0.1.0/jigane/policy_materials/__init__.py +1 -0
- jigane-0.1.0/jigane/policy_materials/changed_files.py +610 -0
- jigane-0.1.0/jigane/policy_materials/classes.py +374 -0
- jigane-0.1.0/jigane/policy_materials/current_shape.py +122 -0
- jigane-0.1.0/jigane/policy_materials/declarations.py +67 -0
- jigane-0.1.0/jigane/policy_materials/design_doc.py +418 -0
- jigane-0.1.0/jigane/policy_materials/discriminators.py +276 -0
- jigane-0.1.0/jigane/policy_materials/er.py +152 -0
- jigane-0.1.0/jigane/policy_materials/missing.py +47 -0
- jigane-0.1.0/jigane/policy_materials/moved_declarations.py +57 -0
- jigane-0.1.0/jigane/preconditions/README.md +20 -0
- jigane-0.1.0/jigane/preconditions/__init__.py +1 -0
- jigane-0.1.0/jigane/preconditions/integers.py +12 -0
- jigane-0.1.0/jigane/preconditions/required.py +5 -0
- jigane-0.1.0/jigane/publishing/README.md +21 -0
- jigane-0.1.0/jigane/publishing/__init__.py +1 -0
- jigane-0.1.0/jigane/publishing/rendering.py +330 -0
- jigane-0.1.0/jigane/pull_request_context/README.md +22 -0
- jigane-0.1.0/jigane/pull_request_context/__init__.py +1 -0
- jigane-0.1.0/jigane/pull_request_context/background.py +445 -0
- jigane-0.1.0/jigane/pull_request_context/bots.py +283 -0
- jigane-0.1.0/jigane/pull_request_context/fetching.py +154 -0
- jigane-0.1.0/jigane/pull_request_context/gh.py +148 -0
- jigane-0.1.0/jigane/py.typed +0 -0
- jigane-0.1.0/jigane/regex/README.md +31 -0
- jigane-0.1.0/jigane/regex/__init__.py +1 -0
- jigane-0.1.0/jigane/regex/gate_tags.py +135 -0
- jigane-0.1.0/jigane/regex/metrics.py +327 -0
- jigane-0.1.0/jigane/regex/source.py +29 -0
- jigane-0.1.0/jigane/regex/syntax.py +541 -0
- jigane-0.1.0/jigane/regex/tags.py +259 -0
- jigane-0.1.0/jigane/reviews/README.md +24 -0
- jigane-0.1.0/jigane/reviews/__init__.py +1 -0
- jigane-0.1.0/jigane/reviews/attachments.py +46 -0
- jigane-0.1.0/jigane/reviews/copying.py +26 -0
- jigane-0.1.0/jigane/reviews/group_state.py +31 -0
- jigane-0.1.0/jigane/reviews/lifecycle.py +1938 -0
- jigane-0.1.0/jigane/reviews/relations.py +54 -0
- jigane-0.1.0/jigane/reviews/selection.py +432 -0
- jigane-0.1.0/jigane/sequence/README.md +56 -0
- jigane-0.1.0/jigane/sequence/__init__.py +0 -0
- jigane-0.1.0/jigane/sequence/diagram.py +668 -0
- jigane-0.1.0/jigane/sequence/index.py +463 -0
- jigane-0.1.0/jigane/sequence/indexes.py +71 -0
- jigane-0.1.0/jigane/sequence/resolve.py +482 -0
- jigane-0.1.0/jigane/sequence/review_diagrams.py +364 -0
- jigane-0.1.0/jigane/sequence/roots.py +340 -0
- jigane-0.1.0/jigane/skills/README.md +82 -0
- jigane-0.1.0/jigane/skills/__init__.py +0 -0
- jigane-0.1.0/jigane/skills/adversarial.py +675 -0
- jigane-0.1.0/jigane/skills/assembly.py +65 -0
- jigane-0.1.0/jigane/skills/bundle_disclosed.py +157 -0
- jigane-0.1.0/jigane/skills/config_head.py +110 -0
- jigane-0.1.0/jigane/skills/disclosed.py +114 -0
- jigane-0.1.0/jigane/skills/estimate_result.py +297 -0
- jigane-0.1.0/jigane/skills/estimator.py +21 -0
- jigane-0.1.0/jigane/skills/group_result.py +227 -0
- jigane-0.1.0/jigane/skills/history.py +156 -0
- jigane-0.1.0/jigane/skills/input_budget.py +142 -0
- jigane-0.1.0/jigane/skills/language_support.py +80 -0
- jigane-0.1.0/jigane/skills/legacy_nits.py +15 -0
- jigane-0.1.0/jigane/skills/macro.py +759 -0
- jigane-0.1.0/jigane/skills/macro_design.py +42 -0
- jigane-0.1.0/jigane/skills/macro_shared.py +47 -0
- jigane-0.1.0/jigane/skills/mcp.py +171 -0
- jigane-0.1.0/jigane/skills/micro.py +79 -0
- jigane-0.1.0/jigane/skills/order.py +26 -0
- jigane-0.1.0/jigane/skills/output_estimate.py +204 -0
- jigane-0.1.0/jigane/skills/output_models.py +145 -0
- jigane-0.1.0/jigane/skills/pipeline.py +770 -0
- jigane-0.1.0/jigane/skills/policy_commands.py +62 -0
- jigane-0.1.0/jigane/skills/policy_concurrency.py +384 -0
- jigane-0.1.0/jigane/skills/policy_figures.py +357 -0
- jigane-0.1.0/jigane/skills/policy_file_paths.py +63 -0
- jigane-0.1.0/jigane/skills/policy_regex.py +233 -0
- jigane-0.1.0/jigane/skills/policy_repeats.py +473 -0
- jigane-0.1.0/jigane/skills/policy_stores.py +139 -0
- jigane-0.1.0/jigane/skills/policy_tables.py +89 -0
- jigane-0.1.0/jigane/skills/policy_tree.py +68 -0
- jigane-0.1.0/jigane/skills/prompts/alternative.md +10 -0
- jigane-0.1.0/jigane/skills/prompts/bundle_disclosed.md +13 -0
- jigane-0.1.0/jigane/skills/prompts/common.md +3 -0
- jigane-0.1.0/jigane/skills/prompts/defense.md +13 -0
- jigane-0.1.0/jigane/skills/prompts/judge_alternative.md +19 -0
- jigane-0.1.0/jigane/skills/prompts/judge_rebuttal.md +31 -0
- jigane-0.1.0/jigane/skills/prompts/macro_design.md +73 -0
- jigane-0.1.0/jigane/skills/prompts/macro_diagram.md +82 -0
- jigane-0.1.0/jigane/skills/prompts/macro_history_rule.md +7 -0
- jigane-0.1.0/jigane/skills/prompts/macro_standards.md +32 -0
- jigane-0.1.0/jigane/skills/prompts/micro.md +58 -0
- jigane-0.1.0/jigane/skills/prompts/rebuttal.md +18 -0
- jigane-0.1.0/jigane/skills/prompts/scope_rule.md +2 -0
- jigane-0.1.0/jigane/skills/prompts/security.md +64 -0
- jigane-0.1.0/jigane/skills/prompts/themes.md +16 -0
- jigane-0.1.0/jigane/skills/prompts/verifier_scope_rule.md +1 -0
- jigane-0.1.0/jigane/skills/received_concerns.py +114 -0
- jigane-0.1.0/jigane/skills/run_record.py +473 -0
- jigane-0.1.0/jigane/skills/scope.py +85 -0
- jigane-0.1.0/jigane/skills/security.py +87 -0
- jigane-0.1.0/jigane/skills/sharded.py +714 -0
- jigane-0.1.0/jigane/skills/shards.py +339 -0
- jigane-0.1.0/jigane/skills/shared_rules.py +55 -0
- jigane-0.1.0/jigane/skills/triage.py +225 -0
- jigane-0.1.0/jigane/storage/README.md +21 -0
- jigane-0.1.0/jigane/storage/__init__.py +1 -0
- jigane-0.1.0/jigane/storage/review_error.py +18 -0
- jigane-0.1.0/jigane/storage/store.py +366 -0
- jigane-0.1.0/jigane/storage/store_format.py +73 -0
- jigane-0.1.0/jigane/technology_list/README.md +46 -0
- jigane-0.1.0/jigane/technology_list/__init__.py +0 -0
- jigane-0.1.0/jigane/technology_list/bridges.py +300 -0
- jigane-0.1.0/jigane/technology_list/build.py +149 -0
- jigane-0.1.0/jigane/technology_list/changes.py +36 -0
- jigane-0.1.0/jigane/technology_list/manifests.py +471 -0
- jigane-0.1.0/jigane/technology_list/materials.py +86 -0
- jigane-0.1.0/jigane/technology_list/platforms.py +48 -0
- jigane-0.1.0/jigane/technology_list/repository_modules.py +77 -0
- jigane-0.1.0/jigane/technology_list/tables.py +196 -0
- jigane-0.1.0/jigane/templates/reply.md.j2 +49 -0
- jigane-0.1.0/jigane/templates/report.md.j2 +40 -0
- jigane-0.1.0/pyproject.toml +633 -0
jigane-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Koki Takeda (kokiTakashiki)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
jigane-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jigane
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GitHub の PR のレビューを助ける CLI
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Dist: tree-sitter>=0.25,<0.26
|
|
8
|
+
Requires-Dist: tree-sitter-language-pack>=1.22,<2
|
|
9
|
+
Requires-Dist: pydantic>=2.14.1,<3
|
|
10
|
+
Requires-Dist: pathspec>=1.1.1
|
|
11
|
+
Requires-Dist: jinja2>=3.1.6
|
|
12
|
+
Requires-Python: >=3.12
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# jigane
|
|
2
|
+
|
|
3
|
+
PR または作業ツリーの変更を受け取り、レビューの材料(判断の地図、背景、ボットの指摘)を決定的に集めて保存します。スキルの実行で LLM に指摘を作らせ、選んだ指摘から返信とレポートを書き出します。
|
|
4
|
+
|
|
5
|
+
保存の置き場所はデータホスト直下のレビューごとのディレクトリで、その読み書きは `storage` と `reviews` が受け持ちます。
|
|
6
|
+
|
|
7
|
+
## サブパッケージ
|
|
8
|
+
|
|
9
|
+
`jigane/` の直下は、サブパッケージと、直下に残る三つのモジュールからなります。
|
|
10
|
+
|
|
11
|
+
<!-- BEGIN GENERATED: package-table (scripts/generate_package_readme.py) -->
|
|
12
|
+
|
|
13
|
+
| サブパッケージ | 役割 |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| [`commands`](commands/README.md) | 副コマンドの入口と、表示の行の組み立てを受け持ちます。 |
|
|
16
|
+
| [`design`](design/README.md) | 設計書(Markdown)を、見出しの木、Mermaid の図、表に分けます。 |
|
|
17
|
+
| [`figures`](figures/README.md) | 方針の図(表、Mermaid、ツリー、シーケンス図)の構造の型を持ちます。 |
|
|
18
|
+
| [`findings`](findings/README.md) | 指摘の保存と選別の記録、捨てた理由を受け持ちます。 |
|
|
19
|
+
| [`flaky`](flaky/README.md) | テストのファイルから、不安定になりやすい書き方(flaky の型)を静的に探します。 |
|
|
20
|
+
| [`gates`](gates/README.md) | ゲートの違反と警告の一件の型、その説明の文、検出の対象の行の範囲を持ちます。 |
|
|
21
|
+
| [`git`](git/README.md) | 外部コマンドの起動と、git の木の読み出しを受け持ちます。 |
|
|
22
|
+
| [`groups`](groups/README.md) | 群(方針、実装、セキュリティ)の識別子、表示名、既定を持ちます。 |
|
|
23
|
+
| [`judgment_map`](judgment_map/README.md) | 判断の地図、変更の宣言、変更の種類、`.jigane.toml` の規則を受け持ちます。 |
|
|
24
|
+
| [`languages`](languages/README.md) | ソースファイルを tree-sitter で構文木にし、言語ごとの違いを `LanguageAdapter` の裏に閉じ込めます。 |
|
|
25
|
+
| [`llm_runs`](llm_runs/README.md) | LLM の起動、実行の記録、見積もりと使用量、使った額を受け持ちます。 |
|
|
26
|
+
| [`markdown`](markdown/README.md) | Markdown の柵と見出しの判別、表のセルの整形を受け持ちます。 |
|
|
27
|
+
| [`policy_materials`](policy_materials/README.md) | 方針の群に渡す資料(図と表)を組みます。 |
|
|
28
|
+
| [`preconditions`](preconditions/README.md) | 呼び出し側が保証している前提を確かめ、型を絞ります。 |
|
|
29
|
+
| [`publishing`](publishing/README.md) | 返信とレポートの描画を受け持ちます。 |
|
|
30
|
+
| [`pull_request_context`](pull_request_context/README.md) | PR の取得、変更の背景、レビューボットの指摘を受け持ちます。 |
|
|
31
|
+
| [`regex`](regex/README.md) | 変更した正規表現を、LLM を使わずに数えて札にします。 |
|
|
32
|
+
| [`reviews`](reviews/README.md) | レビューの組み立て、保存、読み込み、更新、選別、付属の文書を受け持ちます。 |
|
|
33
|
+
| [`sequence`](sequence/README.md) | レビューが変更した宣言から、変更が届く入口(根)ごとのシーケンス図を、LLM を使わずに作ります。 |
|
|
34
|
+
| [`skills`](skills/README.md) | LLM に指摘を作らせるスキル群を持ちます。 |
|
|
35
|
+
| [`storage`](storage/README.md) | データの置き場の読み書き、保存物の形の検査、読み書きの失敗を受け持ちます。 |
|
|
36
|
+
| [`technology_list`](technology_list/README.md) | レビューが変更したファイルの import から、使っている技術の表を LLM を使わずに作ります。 |
|
|
37
|
+
|
|
38
|
+
<!-- END GENERATED: package-table -->
|
|
39
|
+
|
|
40
|
+
直下に残るモジュールは次の二つです。
|
|
41
|
+
|
|
42
|
+
| モジュール | 役割 |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `__init__` | パッケージの版(`__version__`)を保持します |
|
|
45
|
+
| `__main__` | `python -m jigane` の入口です(`jigane.commands` の `main` を呼びます) |
|
|
46
|
+
|
|
47
|
+
データファイル用のディレクトリ `templates/`(返信とレポートのテンプレート)は、Python のサブパッケージではありません。
|
|
48
|
+
|
|
49
|
+
## モジュールの関係
|
|
50
|
+
|
|
51
|
+
サブパッケージは、下から core、feature、cli の三つの層に分かれます。層の名前は関係を示すための呼び名であり、パッケージの名前にもディレクトリにも入っていません。
|
|
52
|
+
|
|
53
|
+
<!-- BEGIN GENERATED: layer-diagram (scripts/generate_package_readme.py) -->
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
flowchart TB
|
|
57
|
+
subgraph core
|
|
58
|
+
storage
|
|
59
|
+
groups
|
|
60
|
+
git
|
|
61
|
+
preconditions
|
|
62
|
+
gates
|
|
63
|
+
markdown
|
|
64
|
+
findings
|
|
65
|
+
languages
|
|
66
|
+
figures
|
|
67
|
+
end
|
|
68
|
+
subgraph feature
|
|
69
|
+
design
|
|
70
|
+
flaky
|
|
71
|
+
judgment_map
|
|
72
|
+
llm_runs
|
|
73
|
+
policy_materials
|
|
74
|
+
pull_request_context
|
|
75
|
+
regex
|
|
76
|
+
reviews
|
|
77
|
+
sequence
|
|
78
|
+
skills
|
|
79
|
+
technology_list
|
|
80
|
+
publishing
|
|
81
|
+
end
|
|
82
|
+
subgraph cli
|
|
83
|
+
commands
|
|
84
|
+
end
|
|
85
|
+
cli --> feature
|
|
86
|
+
feature --> core
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
<!-- END GENERATED: layer-diagram -->
|
|
90
|
+
|
|
91
|
+
矢印は「読む」の向きです。`a --> b` は、`a` の層の中のモジュールが `b` の層の中のモジュールを import することを表します。
|
|
92
|
+
|
|
93
|
+
層の決まりは次のとおりです。上の層は、下の層も同じ層も読んでかまいません。下の層は、上の層を読みません。関数の中の import と、型の注釈だけのための import(`if TYPE_CHECKING:` の中)も、この決まりの対象です。
|
|
94
|
+
|
|
95
|
+
この決まりは `pyproject.toml` の `[tool.importlinter]` に層の契約として書かれ、`lint-imports`(`make lint` が走らせる)が import の向きを確かめます。図の層の割り当てと契約の層の割り当てが一致することは `tests/test_package_dependency_diagram.py` が確かめます。サブパッケージを足したときは、どれか一つの層に入れてください。上のサブパッケージの表と図は `scripts/generate_package_readme.py` が層の契約と各サブパッケージの README の先頭の一文から作るので、手で直さないでください。各サブパッケージの README の「他のモジュールとの関係」にある、読むサブパッケージと読まれるサブパッケージの名前の一覧も、`scripts/generate_readme_dependencies.py` が grimp で測った実際の import から作るので、手で直さないでください。
|
|
96
|
+
|
|
97
|
+
同じ層の中の依存は、宣言したものだけを許します。宣言は `pyproject.toml` の `[tool.jigane.allowed_sibling_imports]` にあり、import する側のサブパッケージの行へ、読む兄弟を足します。新しく兄弟を読むときは、その行へ相手を足して `make generate`(Windows は `just generate`)を走らせてください。`[tool.importlinter]` の forbidden の契約は、この表と層の契約から作られます。サブパッケージを足したときも、層の契約に入れて同じ生成を走らせると、上の表と図が作り直されます。
|
|
98
|
+
|
|
99
|
+
## Q&A
|
|
100
|
+
|
|
101
|
+
### なぜ保存先のファイルの書き込みと更新の排他に決まった入口があるのですか
|
|
102
|
+
|
|
103
|
+
保存先のファイルの書き込みは `storage.store.write_text_atomic` を通し、同じレビューへの更新は `storage.store.review_lock` で排他します。一時のファイルへ書いてから置き換えるので途中で落ちても壊れたファイルが残らず、排他がないと並行して実行した二つの群が、読んだ時点の状態へ自分の群だけを足して保存し、後の保存が先の群を消すためです。
|
|
104
|
+
|
|
105
|
+
### なぜツリーの中のファイルは `git.tree_files` を通して読むのですか
|
|
106
|
+
|
|
107
|
+
ツリーの中のファイルは `git.tree_files` を通して読み、ツリーの外へ出しません。ツリーは PR 由来で攻撃者が作れるので、シンボリックリンクや木の外への参照を読むとレビューする側のマシンの秘密を読み出す経路になり、その扱いを一か所に集めているためです。
|
|
108
|
+
|
|
109
|
+
### なぜ外部コマンドの起動は `git.process` を通すのですか
|
|
110
|
+
|
|
111
|
+
外部コマンドの起動は `git.process` を通します。この入口が、起こす git から `GIT_DIR` などの向き先を狂わせる環境変数を外し、失敗にコマンドと作業ディレクトリを添え、検査で差し替える唯一の場所になっているためです。
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# jigane.commands
|
|
2
|
+
|
|
3
|
+
副コマンドの入口と、表示の行の組み立てを受け持ちます。引数を解釈して副コマンドを選び、`jigane` の他のモジュールを呼んで結果を標準出力へ書き、失敗を終了コードと標準エラーに変えます。レビューの判断や保存の規則は持たず、呼び出しと表示に徹します。
|
|
4
|
+
|
|
5
|
+
## 主な入口
|
|
6
|
+
|
|
7
|
+
| 名前 | 役割 |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `commands.main` | 解析、実行、失敗の表示を行い、終了コードを返す唯一の入口です |
|
|
10
|
+
| `commands.build_parser` | 副コマンドを登録します(`subcommands/` の各モジュールの `register` を呼びます) |
|
|
11
|
+
| `subcommands/<名前>.py` | 副コマンド 1 つにつき 1 ファイルです(`register` と `run` を持ちます) |
|
|
12
|
+
| `context.Context` | 実行の関数へ渡す依存(Claude Code CLI を起動する関数)を持ちます |
|
|
13
|
+
| `output` | 標準出力へ書く部品(行、JSON、最終的な指摘)です |
|
|
14
|
+
| `views/` | 表示の行を組み立てる部品です(標準出力へは書きません) |
|
|
15
|
+
| `failures.describe` | 例外を、種類、見出し、理由、次の操作へ変換します |
|
|
16
|
+
| `exit_codes` | 終了コードの定数を持ちます |
|
|
17
|
+
| `budget_argument.add_max_budget_argument` | `--max-budget-usd-per-call` の引数を定義します。`run` と `estimate` が共有します |
|
|
18
|
+
| `group_argument` | 引数の群を入口で一度だけ `GroupId` に解き、群を取る引数を足します |
|
|
19
|
+
| `mcp_argument` | `--mcps` の引数の定義と検証をします。`run` と `estimate` が共有します |
|
|
20
|
+
| `consent` | `jigane run` が Claude Code CLI を起動する前に、利用者の同意を取ります |
|
|
21
|
+
| `terms_consent` | `jigane languages fetch` が文法を取得する前に、利用規約への同意を取ります |
|
|
22
|
+
| `public_urls` | 同意の画面が示す規約、プライバシー、SECURITY.md の URL を、入れた版に合わせて作ります |
|
|
23
|
+
| `run_guide.run_guide` | 見積もりの JSON の `run_guide`(未実行の群の案内の文)を、計画の種類と群ごとに返します |
|
|
24
|
+
| `generation.generate` | 返信とレポートの文面を作る共通の手順です |
|
|
25
|
+
|
|
26
|
+
## 副コマンド
|
|
27
|
+
|
|
28
|
+
- レビューの作成と更新:`new`、`refresh`(出所の型から更新の手への振り分け `refresh_review` を持ち、種類ごとの更新は `reviews.lifecycle` と `design.review_documents` が持ちます)、`delete`、`archive`、`unarchive`
|
|
29
|
+
- 表示:`list`、`show`、`diagrams`、`sequence`、`usage`、`estimate`
|
|
30
|
+
- 設計書の `new design-doc`:設計書をレビューします。実装の前に方針の綻びを問います。出所は設計書のパスと版、コードの版、選んだファイル、`--with` で選んだ付属の文書のパスと版です。
|
|
31
|
+
- 設計書の `design suggest`:設計書の名指しから、突き合わせるファイルと、付属の文書の候補(同じ場所の reports/ の .md)を示します。保存はしません。
|
|
32
|
+
- スキルの実行:`run`
|
|
33
|
+
- 検査と一覧:`flaky`(テストの flaky の検査だけを走らせます)、`technology-list`(変更したファイルが使う技術の表を出します)、`languages`(言語の一覧、有効化、無効化、文法の取得)
|
|
34
|
+
- 最終的な指摘の編集:`pick`、`unpick`、`hold`、`unhold`、`object`、`unobject`
|
|
35
|
+
- 出力:`export-reply`、`export-report`、`templates`
|
|
36
|
+
|
|
37
|
+
副コマンドの一覧は `subcommands/` のモジュールそのものであり、別に対応表を持ちません。
|
|
38
|
+
|
|
39
|
+
## 他のモジュールとの関係
|
|
40
|
+
|
|
41
|
+
<!-- BEGIN GENERATED: dependencies (scripts/generate_readme_dependencies.py) -->
|
|
42
|
+
|
|
43
|
+
読むサブパッケージ:`design`、`figures`、`findings`、`flaky`、`gates`、`git`、`groups`、`judgment_map`、`languages`、`llm_runs`、`publishing`、`pull_request_context`、`reviews`、`sequence`、`skills`、`storage`、`technology_list`
|
|
44
|
+
|
|
45
|
+
読まれるサブパッケージ:なし
|
|
46
|
+
|
|
47
|
+
<!-- END GENERATED: dependencies -->
|
|
48
|
+
|
|
49
|
+
- `jigane.reviews.lifecycle` でレビューを読み込み、`jigane.design.review_documents` で設計書のレビューを作成と更新し、`jigane.skills` の `run` で群を走らせます。
|
|
50
|
+
- `jigane.__main__` が `commands.main` を呼びます。
|
|
51
|
+
|
|
52
|
+
副コマンドの引数、機械が読む出力(`--json`)、失敗の JSON の鍵(`failures` の `Failure.to_dict`)、終了コード(`exit_codes`)は、外の道具との契約です。機械が読む出力を出す副コマンドは、すべて `--json` を持ちます。失敗は `--json` を付けたとき(`sequence` は `--format json` を付けたとき)だけ、標準出力に `{"error": {...}}` で出ます。`sequence` は `--json` を持たず、出力の形を `--format` で選びます。例外は、人向けの進捗だけを出す `languages fetch` です。
|
|
53
|
+
|
|
54
|
+
## Q&A
|
|
55
|
+
|
|
56
|
+
### なぜ副コマンドを足すときは `subcommands/` にモジュールを足すのですか
|
|
57
|
+
|
|
58
|
+
副コマンドを足すときは `subcommands/` にモジュールを足します。`register` を持たないモジュールがあると、パーサの組み立てで失敗します。副コマンドの一覧は `subcommands/` のモジュールそのものであり、ファイルを足せばコマンドが足される形にして、副コマンドを足すたびに巨大な分岐と対応表を直す必要をなくしたためです。
|
|
59
|
+
|
|
60
|
+
### なぜ新しい例外の型を足すときは `failures.KINDS` にも行を足すのですか
|
|
61
|
+
|
|
62
|
+
新しい例外の型を足すときは `failures.KINDS` にも行を足します。足し忘れた型は `unexpected` として表示されます。`KINDS` に当たらない型は、型ごとの見出しと次の操作を持たない `unexpected` の種類に落ちるためです。
|
|
63
|
+
|
|
64
|
+
### なぜ Namespace を受けるのは各副コマンドの run と最上位の受け口だけなのですか
|
|
65
|
+
|
|
66
|
+
Namespace を受けるのは各副コマンドの run と最上位の受け口だけで、ほかの関数には名前と型の付いた値で渡します。共有の関数が Namespace をまるごと受けると、どの属性を読むかがシグネチャに現れず、副コマンドの引数を変えても型検査が食い違いを見つけないためです。
|
|
67
|
+
|
|
68
|
+
### なぜ `views/` は行を返すだけで、標準出力へ書くのは `output` と `subcommands/` なのですか
|
|
69
|
+
|
|
70
|
+
`views/` は行を返すだけで、標準出力へ書くのは `output` と `subcommands/` です。文字と JSON のどちらで出すかを出口の一か所で決めるためと、試験が標準出力を捕まえずに行を比べられるようにするためです。
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""jigane の CLI の唯一の入口。
|
|
2
|
+
|
|
3
|
+
副コマンドは `subcommands/` に置かれたモジュールとして並ぶ。副コマンドの一覧は
|
|
4
|
+
`subcommands/` のモジュールそのものであり、別に対応表を持たない。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import importlib
|
|
9
|
+
import io
|
|
10
|
+
import json
|
|
11
|
+
import pkgutil
|
|
12
|
+
import sys
|
|
13
|
+
import traceback
|
|
14
|
+
from collections.abc import Callable, Sequence
|
|
15
|
+
from dataclasses import replace
|
|
16
|
+
|
|
17
|
+
from jigane import __version__
|
|
18
|
+
from jigane.commands import failures, subcommands
|
|
19
|
+
from jigane.commands.context import Context
|
|
20
|
+
from jigane.commands.exit_codes import EXIT_INTERRUPTED, EXIT_PROCESSING_FAILURE
|
|
21
|
+
from jigane.commands.output import install_warning_handler
|
|
22
|
+
from jigane.languages import registry
|
|
23
|
+
from jigane.llm_runs import claude
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _StrictParser(argparse.ArgumentParser):
|
|
27
|
+
"""副コマンドの解釈器も、オプションの短縮形を受けない。"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, **kwargs: object) -> None:
|
|
30
|
+
kwargs["allow_abbrev"] = False
|
|
31
|
+
super().__init__(**kwargs) # type: ignore[arg-type]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
35
|
+
"""`subcommands/` の各モジュールの `register` を呼んで、副コマンドを登録する。"""
|
|
36
|
+
parser = _StrictParser(prog="jigane")
|
|
37
|
+
parser.add_argument("--version", action="version", version=f"jigane {__version__}")
|
|
38
|
+
subparsers = parser.add_subparsers(dest="command", metavar="<command>")
|
|
39
|
+
for module_info in pkgutil.iter_modules(subcommands.__path__):
|
|
40
|
+
module = importlib.import_module(f"{subcommands.__name__}.{module_info.name}")
|
|
41
|
+
module.register(subparsers)
|
|
42
|
+
return parser
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _write_streams_as_utf8() -> None:
|
|
46
|
+
# Windows は標準出力と標準エラーの符号化を、パイプやリダイレクトでは cp1252 などの
|
|
47
|
+
# ロケールの符号化にする。日本語の出力がそのまま UnicodeEncodeError になるため、
|
|
48
|
+
# 入口で UTF-8 に揃える。argparse の --help も解釈の途中で書くので、解釈の前に行う
|
|
49
|
+
for stream in (sys.stdout, sys.stderr):
|
|
50
|
+
if isinstance(stream, io.TextIOWrapper) and stream.encoding.lower() not in {
|
|
51
|
+
"utf-8",
|
|
52
|
+
"utf8",
|
|
53
|
+
}:
|
|
54
|
+
stream.reconfigure(encoding="utf-8")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _wants_machine_output(args: argparse.Namespace) -> bool:
|
|
58
|
+
# sequence は --json を持たず、--format json で機械が読む出力を選ぶ
|
|
59
|
+
return bool(getattr(args, "json", False)) or getattr(args, "format", None) == "json"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def main(
|
|
63
|
+
argv: Sequence[str] | None = None,
|
|
64
|
+
*,
|
|
65
|
+
invoke: claude.Invoke = claude.run,
|
|
66
|
+
is_terminal: Callable[[], bool] | None = None,
|
|
67
|
+
read_answer: Callable[[str], str] | None = None,
|
|
68
|
+
) -> int:
|
|
69
|
+
_write_streams_as_utf8()
|
|
70
|
+
parser = build_parser()
|
|
71
|
+
args = parser.parse_args(argv)
|
|
72
|
+
if args.command is None:
|
|
73
|
+
parser.print_help(sys.stderr)
|
|
74
|
+
return EXIT_PROCESSING_FAILURE
|
|
75
|
+
install_warning_handler()
|
|
76
|
+
try:
|
|
77
|
+
if args.command == "languages":
|
|
78
|
+
# 使えない外の言語があっても、languages の一覧と disable で直せるようにする。
|
|
79
|
+
# ここで止めると、直す手段そのものが塞がれる
|
|
80
|
+
pass
|
|
81
|
+
else:
|
|
82
|
+
# 言語を使わない副コマンドも止める。登録は副コマンドの中のどこからでも引かれ、
|
|
83
|
+
# 使うかどうかを副コマンドごとに見分ける手段が無いため、壊れた状態を一律に先に知らせる
|
|
84
|
+
registry.require_ready()
|
|
85
|
+
context = Context(invoke=invoke)
|
|
86
|
+
if is_terminal is not None:
|
|
87
|
+
context = replace(context, is_terminal=is_terminal)
|
|
88
|
+
if read_answer is not None:
|
|
89
|
+
context = replace(context, read_answer=read_answer)
|
|
90
|
+
return int(args.handler(args, context))
|
|
91
|
+
except KeyboardInterrupt:
|
|
92
|
+
return EXIT_INTERRUPTED
|
|
93
|
+
except Exception as error: # noqa: BLE001 最上位の受け口。型の分からない失敗を報告に回す
|
|
94
|
+
failure = failures.describe(error)
|
|
95
|
+
if failure.kind == failures.UNEXPECTED:
|
|
96
|
+
# 型が表に無い失敗は、原因を辿れるよう、人が読む標準エラーに経路も残す
|
|
97
|
+
traceback.print_exc()
|
|
98
|
+
print(f"jigane: {failure.reason}", file=sys.stderr)
|
|
99
|
+
if failure.action:
|
|
100
|
+
print(f"次の操作: {failure.action}", file=sys.stderr)
|
|
101
|
+
if _wants_machine_output(args):
|
|
102
|
+
print(json.dumps({"error": failure.to_dict()}, ensure_ascii=False))
|
|
103
|
+
return EXIT_PROCESSING_FAILURE
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""`--max-budget-usd-per-call` の引数の定義。`run` と `estimate` が同じものを使う。"""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from jigane.groups.definitions import GROUPS
|
|
6
|
+
from jigane.llm_runs.defaults import resolve_run_defaults
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _budget_defaults_text() -> str:
|
|
10
|
+
budgets = {
|
|
11
|
+
group: resolve_run_defaults(group.identifier, None, None).budget.max_budget_usd
|
|
12
|
+
for group in GROUPS
|
|
13
|
+
}
|
|
14
|
+
return "、".join(f"{group.label}は {budget:g} USD" for group, budget in budgets.items())
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def add_max_budget_argument(parser: argparse.ArgumentParser) -> None:
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--max-budget-usd-per-call",
|
|
20
|
+
type=float,
|
|
21
|
+
default=None,
|
|
22
|
+
help=(
|
|
23
|
+
"Claude Code CLI の 1 回の起動あたりの上限の目安です(USD。超えることがあります)。"
|
|
24
|
+
f"省略すると群の既定({_budget_defaults_text()})を使います。"
|
|
25
|
+
"方針は起動が 1 回なので、この値が実行全体の目安になります。"
|
|
26
|
+
"実装とセキュリティはシャードと役ごとに複数回起動するため、"
|
|
27
|
+
"実行全体ではこの数倍を使うことがあります。"
|
|
28
|
+
"見込みが目安以上のときは、警告して続けます。"
|
|
29
|
+
"estimate では、この値で上限を決めた見積もりを出します"
|
|
30
|
+
),
|
|
31
|
+
)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""`jigane run` が Claude Code CLI を起動する前に取る、利用者の同意。"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from jigane.commands.public_urls import installed_version, terms_urls
|
|
8
|
+
from jigane.git.process import CONSENT_PROMPT_END
|
|
9
|
+
from jigane.groups.definitions import GROUP_BY_ID, MACRO, MICRO, SECURITY, Effort, GroupId
|
|
10
|
+
from jigane.llm_runs import claude
|
|
11
|
+
from jigane.llm_runs.defaults import resolve_run_defaults
|
|
12
|
+
from jigane.reviews.lifecycle import ReviewError
|
|
13
|
+
from jigane.skills.assembly import AssemblyInput
|
|
14
|
+
from jigane.skills.estimate_result import Estimate
|
|
15
|
+
from jigane.skills.estimator import estimate_of
|
|
16
|
+
from jigane.skills.mcp import ALL_TOOLS_NOTICE, NO_MCP, McpSelection
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConsentDeclinedError(Exception):
|
|
20
|
+
"""利用者が確認に y と答えなかった。"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ConsentUnavailableError(Exception):
|
|
24
|
+
"""標準入力が端末でないので、確認を取れなかった。"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class GroupBreakdown:
|
|
29
|
+
"""一つの群について、起動の前に分かる分の内訳。"""
|
|
30
|
+
|
|
31
|
+
group: GroupId
|
|
32
|
+
discovery_calls: int
|
|
33
|
+
per_call_usd: float
|
|
34
|
+
# 検証役を持つ群の回数は見つかった指摘の数で決まり、事前には分からない
|
|
35
|
+
has_verifier: bool
|
|
36
|
+
# 記録にある前回の実行の実績。記録が無ければ None
|
|
37
|
+
past_cost_usd: float | None
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def known_usd(self) -> float:
|
|
41
|
+
return self.discovery_calls * self.per_call_usd
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class LaunchPlan:
|
|
46
|
+
"""この実行が最初に分かる範囲で起動する回数と、1 回あたりの上限の目安。
|
|
47
|
+
|
|
48
|
+
回数と目安の出所は groups だけで、他の値はそこから導く。
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
group: GroupId
|
|
52
|
+
# 同じレビューの群の内訳。この実行の群を必ず含む
|
|
53
|
+
groups: tuple[GroupBreakdown, ...]
|
|
54
|
+
can_increase: bool
|
|
55
|
+
effort: Effort | None
|
|
56
|
+
# 出力と推論の分を含めた見込み。見込みを持つのは設計書の形の方針だけで、他は None
|
|
57
|
+
expected_total_usd: float | None = None
|
|
58
|
+
mcp: McpSelection = NO_MCP
|
|
59
|
+
|
|
60
|
+
def __post_init__(self) -> None:
|
|
61
|
+
if sum(item.group == self.group for item in self.groups) != 1:
|
|
62
|
+
raise ValueError(f"groups に {self.group} の内訳がちょうど 1 つ無い")
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def breakdown(self) -> GroupBreakdown:
|
|
66
|
+
return next(item for item in self.groups if item.group == self.group)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def minimum_calls(self) -> int:
|
|
70
|
+
return self.breakdown.discovery_calls
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def max_budget_usd_per_call(self) -> float:
|
|
74
|
+
return self.breakdown.per_call_usd
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def known_usd(self) -> float:
|
|
78
|
+
return sum(item.known_usd for item in self.groups)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _breakdown_of(group: GroupId, estimate: Estimate, per_call_usd: float) -> GroupBreakdown:
|
|
82
|
+
past = estimate.past_run
|
|
83
|
+
return GroupBreakdown(
|
|
84
|
+
group,
|
|
85
|
+
estimate.discovery_calls,
|
|
86
|
+
per_call_usd,
|
|
87
|
+
estimate.has_verifier,
|
|
88
|
+
None if past is None else past["cost_usd"],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def launch_plan(
|
|
93
|
+
assembly: AssemblyInput,
|
|
94
|
+
group: GroupId,
|
|
95
|
+
settings: claude.RunSettings,
|
|
96
|
+
mcp: McpSelection = NO_MCP,
|
|
97
|
+
) -> LaunchPlan:
|
|
98
|
+
"""見積もりと同じ関数から、確実に起動する回数を求める。"""
|
|
99
|
+
budget = settings.budget.max_budget_usd
|
|
100
|
+
estimates = {group: estimate_of(assembly, group, mcp)}
|
|
101
|
+
for each in (MACRO, MICRO, SECURITY):
|
|
102
|
+
if each in estimates:
|
|
103
|
+
continue
|
|
104
|
+
try:
|
|
105
|
+
estimates[each] = estimate_of(assembly, each)
|
|
106
|
+
except ReviewError:
|
|
107
|
+
# 他の群の材料が欠けていても、この群の実行は止めない。その群は表から外す
|
|
108
|
+
continue
|
|
109
|
+
breakdowns = tuple(
|
|
110
|
+
_breakdown_of(
|
|
111
|
+
each,
|
|
112
|
+
estimates[each],
|
|
113
|
+
budget
|
|
114
|
+
if each == group
|
|
115
|
+
else resolve_run_defaults(
|
|
116
|
+
each, estimates[each].policy_shape(), None
|
|
117
|
+
).budget.max_budget_usd,
|
|
118
|
+
)
|
|
119
|
+
for each in (MACRO, MICRO, SECURITY)
|
|
120
|
+
if each in estimates
|
|
121
|
+
)
|
|
122
|
+
# 方針は発見役の 1 回だけで検証役を持たない。他は発見役がシャードごとに 1 回で、
|
|
123
|
+
# 検証役は指摘の数で決まり、数にも分割にも上限が無い
|
|
124
|
+
# (finding_model.py の発見役の出力のモデルは findings の件数に上限を持たず、
|
|
125
|
+
# split_candidates は 10 件ずつ割る)。
|
|
126
|
+
# 検証役は最小の回数には入らない
|
|
127
|
+
return LaunchPlan(
|
|
128
|
+
group,
|
|
129
|
+
breakdowns,
|
|
130
|
+
group != MACRO,
|
|
131
|
+
settings.effort,
|
|
132
|
+
estimates[group].expected_total_usd(),
|
|
133
|
+
mcp=NO_MCP if group == MACRO else mcp,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _group_row(item: GroupBreakdown) -> str:
|
|
138
|
+
verifier = "検証役は回数が未定" if item.has_verifier else "検証役なし"
|
|
139
|
+
past = "" if item.past_cost_usd is None else f"、前回の実績 {item.past_cost_usd:.4f} USD"
|
|
140
|
+
return (
|
|
141
|
+
f" {GROUP_BY_ID[item.group].label}: 発見役 {item.discovery_calls} 回、{verifier}、"
|
|
142
|
+
f"発見役の回数 × 1 回の目安 {item.known_usd:g} USD{past}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def consent_text(
|
|
147
|
+
plan: LaunchPlan, version_lookup: Callable[[], str | None] = installed_version
|
|
148
|
+
) -> list[str]:
|
|
149
|
+
label = GROUP_BY_ID[plan.group].label
|
|
150
|
+
calls = (
|
|
151
|
+
f"少なくとも {plan.minimum_calls} 回。検証役の回数に上限はありません"
|
|
152
|
+
"(見つかった指摘の数で決まります)"
|
|
153
|
+
if plan.can_increase
|
|
154
|
+
else f"{plan.minimum_calls} 回"
|
|
155
|
+
)
|
|
156
|
+
per_call = plan.max_budget_usd_per_call
|
|
157
|
+
mcp = (
|
|
158
|
+
[f"使う MCP: {', '.join(plan.mcp.names)}", f"{ALL_TOOLS_NOTICE}。"]
|
|
159
|
+
if plan.mcp.servers
|
|
160
|
+
else ["MCP は使いません"]
|
|
161
|
+
)
|
|
162
|
+
lines = [
|
|
163
|
+
f"{label}の実行は Claude Code CLI(claude -p)を起動します。",
|
|
164
|
+
*mcp,
|
|
165
|
+
f"起動の回数: {calls}",
|
|
166
|
+
f"effort: {plan.effort or '渡さない(Claude Code CLI の既定)'}",
|
|
167
|
+
f"この群の内訳({label}):",
|
|
168
|
+
(
|
|
169
|
+
f" 発見役: {plan.minimum_calls} 回 × 1 回の目安 {per_call:g} USD"
|
|
170
|
+
f" = {plan.minimum_calls * per_call:g} USD"
|
|
171
|
+
),
|
|
172
|
+
]
|
|
173
|
+
if plan.can_increase:
|
|
174
|
+
lines.append(" 検証役: 回数は見つかった指摘の数で決まり、事前には分かりません")
|
|
175
|
+
if plan.expected_total_usd is not None:
|
|
176
|
+
lines.append(
|
|
177
|
+
f"出力と推論の分を含めた見込み: {plan.expected_total_usd:.2f} USD"
|
|
178
|
+
+ (
|
|
179
|
+
"(1 回の起動の額に達する見込みです。警告して続けます)"
|
|
180
|
+
if plan.expected_total_usd >= per_call
|
|
181
|
+
else ""
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
lines.append("同じレビューの群ごとの内訳:")
|
|
185
|
+
lines.extend(_group_row(item) for item in plan.groups)
|
|
186
|
+
lines.append(
|
|
187
|
+
f" 発見役の回数 × 1 回の目安の和(検証役の分を含まない): {plan.known_usd:g} USD"
|
|
188
|
+
)
|
|
189
|
+
lines.append(
|
|
190
|
+
"実際の額は、検証役の回数と出力の量で変わり、この額より少ないことも多いこともあります。"
|
|
191
|
+
)
|
|
192
|
+
lines.append("1 回の目安は往復の合間にしか確かめられないので、超えることがあります。")
|
|
193
|
+
security = terms_urls(version_lookup()).security
|
|
194
|
+
lines.append(f"渡す情報の一覧: {security} の「レビューの入力と Claude Code」の節")
|
|
195
|
+
return lines
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def is_yes(answer: str) -> bool:
|
|
199
|
+
"""確認への答えが y か yes(大文字小文字と前後の空白を問わない)か。"""
|
|
200
|
+
return answer.strip().lower() in ("y", "yes")
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def require_consent(
|
|
204
|
+
plan: LaunchPlan,
|
|
205
|
+
*,
|
|
206
|
+
is_terminal: Callable[[], bool],
|
|
207
|
+
read_answer: Callable[[str], str],
|
|
208
|
+
) -> None:
|
|
209
|
+
"""y か yes のときだけ返る。
|
|
210
|
+
|
|
211
|
+
それ以外の答えは ConsentDeclinedError、端末でないときは ConsentUnavailableError。
|
|
212
|
+
"""
|
|
213
|
+
if not is_terminal():
|
|
214
|
+
raise ConsentUnavailableError("同意を得られないので起動しません。端末から実行してください")
|
|
215
|
+
for line in consent_text(plan):
|
|
216
|
+
print(line, file=sys.stderr)
|
|
217
|
+
if not is_yes(read_answer(f"続けますか{CONSENT_PROMPT_END}")):
|
|
218
|
+
raise ConsentDeclinedError("同意が得られなかったので起動しませんでした")
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""副コマンドの実行の関数へ渡す、注入する依存。"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
from jigane.llm_runs import claude
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def stdio_is_terminal() -> bool:
|
|
11
|
+
return sys.stdin.isatty() and sys.stdout.isatty()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_answer(prompt: str) -> str:
|
|
15
|
+
# 標準出力は --json の本体に空ける
|
|
16
|
+
print(prompt, end="", file=sys.stderr, flush=True)
|
|
17
|
+
try:
|
|
18
|
+
return input()
|
|
19
|
+
except EOFError:
|
|
20
|
+
# 入力が閉じた。答え無しは断りと同じなので、呼ぶ側が y でない答えとして扱える空を返す。
|
|
21
|
+
# 問いの行は改行されずに残るので、次の出力が同じ行に続かないよう改行する
|
|
22
|
+
print(file=sys.stderr)
|
|
23
|
+
return ""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class Context:
|
|
28
|
+
"""入口が組み立て、解析した引数とともに実行の関数へ渡す依存。
|
|
29
|
+
|
|
30
|
+
Attributes:
|
|
31
|
+
invoke: Claude Code CLI を起動する関数。検査は偽物に差し替える。
|
|
32
|
+
is_terminal: 標準入力と標準出力がともに端末かを返す関数。同意を取れるかの判定に使う。
|
|
33
|
+
read_answer: 質問を出して利用者の答えを読む関数。
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
invoke: claude.Invoke
|
|
37
|
+
# 呼び出しのたびにモジュールの関数を引く。検査が差し替えるのはこのモジュールの関数
|
|
38
|
+
# なので、関数を直に渡すと差し替えが効かない
|
|
39
|
+
is_terminal: Callable[[], bool] = field(
|
|
40
|
+
default=lambda: stdio_is_terminal() # noqa: PLW0108 差し替えが効くよう呼び出しのたびに引く
|
|
41
|
+
)
|
|
42
|
+
read_answer: Callable[[str], str] = field(
|
|
43
|
+
default=lambda prompt: read_answer(prompt) # noqa: PLW0108 差し替えが効くよう呼び出しのたびに引く
|
|
44
|
+
)
|