ara-cli 0.1.9.76__py3-none-any.whl → 0.1.9.78__py3-none-any.whl

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.

Potentially problematic release.


This version of ara-cli might be problematic. Click here for more details.

ara_cli/chat.py CHANGED
@@ -34,6 +34,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
34
34
  ".jpg": "image/jpeg",
35
35
  ".jpeg": "image/jpeg",
36
36
  }
37
+
38
+ DOCUMENT_TYPE_EXTENSIONS = [".docx", ".doc", ".odt", ".pdf"]
37
39
 
38
40
  def __init__(
39
41
  self,
@@ -371,9 +373,52 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
371
373
  with open(self.chat_name, 'a', encoding='utf-8') as chat_file:
372
374
  chat_file.write(write_content)
373
375
  return True
376
+
377
+ def read_docx(self, file_path):
378
+ import docx
379
+ doc = docx.Document(file_path)
380
+ return '\n'.join(para.text for para in doc.paragraphs)
381
+
382
+ def read_pdf(self, file_path):
383
+ import pymupdf4llm
384
+ return pymupdf4llm.to_markdown(file_path, write_images=False)
385
+
386
+ def read_odt(self, file_path):
387
+ import pymupdf4llm
388
+ return pymupdf4llm.to_markdown(file_path, write_images=False)
389
+
390
+ @file_exists_check
391
+ def load_document_file(self, file_path: str, prefix: str = "", suffix: str = "", block_delimiter: str = "```"):
392
+ import os
393
+
394
+ _, ext = os.path.splitext(file_path)
395
+ ext = ext.lower()
396
+
397
+ text_content = ""
398
+ match ext:
399
+ case ".docx":
400
+ text_content = self.read_docx(file_path)
401
+ case ".pdf":
402
+ text_content = self.read_pdf(file_path)
403
+ case ".odt":
404
+ text_content = self.read_odt(file_path)
405
+ # Add more cases if needed.
406
+ case _:
407
+ print("Unsupported document type.")
408
+ return False
409
+
410
+ if block_delimiter:
411
+ text_content = f"{block_delimiter}\n{text_content}\n{block_delimiter}"
412
+
413
+ write_content = f"{prefix}{text_content}{suffix}\n"
414
+
415
+ with open(self.chat_name, 'a', encoding='utf-8') as chat_file:
416
+ chat_file.write(write_content)
417
+ return True
374
418
 
375
419
  def load_file(self, file_name: str, prefix: str = "", suffix: str = "", block_delimiter: str = ""):
376
420
  binary_type_mapping = Chat.BINARY_TYPE_MAPPING
421
+ document_type_extensions = Chat.DOCUMENT_TYPE_EXTENSIONS
377
422
 
378
423
  file_type = None
379
424
  file_name_lower = file_name.lower()
@@ -382,7 +427,16 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
382
427
  file_type = mime_type
383
428
  break
384
429
 
385
- if file_type:
430
+ is_file_document = any(file_name_lower.endswith(ext) for ext in document_type_extensions)
431
+
432
+ if is_file_document:
433
+ return self.load_document_file(
434
+ file_name=file_name,
435
+ prefix=prefix,
436
+ suffix=suffix,
437
+ block_delimiter=block_delimiter
438
+ )
439
+ elif file_type:
386
440
  return self.load_binary_file(
387
441
  file_name=file_name,
388
442
  mime_type=file_type,
@@ -503,6 +557,19 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
503
557
  return False
504
558
  return True
505
559
 
560
+ @cmd2.with_category(CATEGORY_CHAT_CONTROL)
561
+ def do_LOAD_DOCUMENT(self, file_name):
562
+ """Load a document file (PDF, DOCX, DOC, ODT) and append its text content to chat file. Can be given the file name in-line. Will attempt to find the file relative to chat file first, then treat the given path as absolute"""
563
+ matching_files = self.find_matching_files_to_load(file_name)
564
+ if not matching_files:
565
+ return
566
+
567
+ for file_path in matching_files:
568
+ prefix = f"\nFile: {file_path}\n"
569
+ self.add_prompt_tag_if_needed(self.chat_name)
570
+ if not os.path.isdir(file_path) and self.load_document_file(file_path, prefix=prefix):
571
+ print(f"Loaded document file {file_path}")
572
+
506
573
  @cmd2.with_category(CATEGORY_CHAT_CONTROL)
507
574
  def do_LOAD_IMAGE(self, file_name):
508
575
  """Load an image file and append it to chat file. Can be given the file name in-line. Will attempt to find the file relative to chat file first, then treat the given path as absolute"""
ara_cli/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.9.76" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
2
+ __version__ = "0.1.9.78" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: ara_cli
3
+ Version: 0.1.9.78
4
+ Summary: Powerful, open source command-line tool for managing, structuring and automating software development artifacts in line with Business-Driven Development (BDD) and AI-assisted processes
5
+ Description-Content-Type: text/markdown
6
+ Requires-Dist: litellm
7
+ Requires-Dist: llama-index
8
+ Requires-Dist: llama-index-llms-openai
9
+ Requires-Dist: llama-index-retrievers-bm25
10
+ Requires-Dist: openai
11
+ Requires-Dist: markdown-it-py
12
+ Requires-Dist: json-repair
13
+ Requires-Dist: argparse
14
+ Requires-Dist: argcomplete
15
+ Requires-Dist: cmd2>=2.5
16
+ Requires-Dist: pydantic
17
+ Requires-Dist: pydantic_ai
18
+ Requires-Dist: python-docx
19
+ Requires-Dist: pymupdf4llm
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: requires-dist
23
+ Dynamic: summary
24
+
25
+ # ara-cli
26
+
27
+ **ara-cli** is a powerful, open source command-line tool for managing, structuring and automating software development artifacts in line with Business-Driven Development (BDD) and AI-assisted processes. With an intuitive interface and platform-independent implementation in Python, ara-cli enables teams to structure business goals, capabilities, features, user stories, and tasks, and to leverage integrated AI/chat capabilities for requirements engineering, documentation, and process automation.
28
+
29
+ ---
30
+
31
+ ## Features
32
+
33
+ - **Comprehensive Artefact Management:**
34
+ Create, edit, rename, delete, and list all core artefacts of the software development lifecycle: businessgoals, vision, capabilities, keyfeatures, features, epics, userstories, examples, and tasks.
35
+
36
+ - **Structured Traceability:**
37
+ Organize and link artefacts for full traceability from business goals to implementation tasks. Effortlessly navigate artefact hierarchies and dependencies.
38
+
39
+ - **Integrated AI and Chat:**
40
+ Interact with AI language models directly from your terminal. Use chat and prompt commands to assist with documentation, requirements refinement, and artefact management.
41
+
42
+ - **Prompt Templates:**
43
+ Fetch, use, and manage reusable prompt templates for consistent and efficient requirements and documentation workflows.
44
+
45
+ - **Artefact Status and User Management:**
46
+ Assign and query status and responsible users for artefacts to support project coordination and tracking.
47
+
48
+ - **Automated Quality Assurance:**
49
+ Scan artefact trees for inconsistencies and automatically correct issues using integrated LLM-powered autofix functionality.
50
+
51
+ - **Powerful Listing and Search:**
52
+ List artefacts and filter by type, tags, content, contributor relationships, file extensions, and more.
53
+
54
+ - **Open Source & Platform Independent:**
55
+ Implemented in Python and available on PyPI for easy installation and integration into any workflow.
56
+
57
+ ---
58
+
59
+ ## Use Cases
60
+
61
+ - **Requirements Engineering:**
62
+ Capture and structure business requirements and user stories with clear traceability.
63
+
64
+ - **Agile Development:**
65
+ Manage and automate backlog refinement, sprint planning, and task tracking.
66
+
67
+ - **AI-Enhanced Productivity:**
68
+ Use chat and prompt features to accelerate documentation, code review, and knowledge management.
69
+
70
+ - **Quality Management:**
71
+ Ensure artefact consistency and high documentation quality via automated scans and fixes.
72
+
73
+ ---
74
+
75
+ ## Quick Start
76
+
77
+ Install from PyPI:
78
+ ```bash
79
+ pip install ara-cli
80
+ ````
81
+
82
+ Create your first feature artefact:
83
+
84
+ ```bash
85
+ ara create feature login
86
+ ```
87
+
88
+ List all features:
89
+
90
+ ```bash
91
+ ara list --include-extension .feature
92
+ ```
93
+
94
+ Chat with the integrated AI:
95
+
96
+ ```bash
97
+ ara chat
98
+ ```
99
+
100
+ Scan and autofix artefacts:
101
+
102
+ ```bash
103
+ ara scan
104
+ ara autofix
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Command Overview
110
+
111
+ | Action | Description |
112
+ |--------------------|-----------------------------------------------------------------------------|
113
+ | create | Create a classified artefact with data directory |
114
+ | delete | Delete an artefact and its data directory |
115
+ | rename | Rename an artefact and its data directory |
116
+ | list, list-tags | List artefacts, show tags, filter by content, extension, hierarchy etc. |
117
+ | prompt, chat | Use AI-powered chat and prompt templates for artefact management |
118
+ | template | Print artefact templates in the terminal |
119
+ | fetch-templates | Download and manage reusable prompt templates |
120
+ | read | Output artefact contents and their full contribution chain |
121
+ | reconnect | Connect artefacts to parent artefacts |
122
+ | read-status, set-status | Query and assign status to artefacts |
123
+ | read-user, set-user | Query and assign responsible users |
124
+ | classifier-directory | Show directory of artefact classifiers |
125
+ | scan | Scan the ARA tree for incompatible or inconsistent artefacts |
126
+ | autofix | Automatically correct artefact issues with LLM assistance |
127
+
128
+ See `ara -h` for the complete list of commands and usage examples.
129
+
130
+ ---
131
+
132
+ ## Artefact Structure
133
+
134
+ ara-cli organizes your project artefacts in a clear directory structure:
135
+
136
+ ```
137
+ ./ara/
138
+ ├── businessgoals/
139
+ ├── vision/
140
+ ├── capabilities/
141
+ ├── keyfeatures/
142
+ ├── features/
143
+ ├── epics/
144
+ ├── userstories/
145
+ ├── examples/
146
+ ├── tasks/
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Example Workflows
152
+
153
+ - **Create a new feature and link it to a user story:**
154
+
155
+ ```bash
156
+ ara create feature payment contributes-to userstory checkout
157
+ ```
158
+
159
+ - **Read an artefact's content and its full parent chain:**
160
+
161
+ ```bash
162
+ ara read task implement_api --branch
163
+ ```
164
+
165
+ - **List tasks containing specific content:**
166
+
167
+ ```bash
168
+ ara list --include-extension .task --include-content "API integration"
169
+ ```
170
+
171
+ - **Automate prompt-based LLM interaction for a task:**
172
+
173
+ ```bash
174
+ ara prompt send task implement_api
175
+ ara prompt extract task implement_api
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Requirements
181
+
182
+ - Python 3.8+
183
+ - Platform-independent; tested on Linux, macOS, and Windows
184
+
185
+ ---
186
+
187
+ ## License
188
+
189
+ This project is open source and freely available under the [MIT License](vector://vector/webapp/LICENSE).
190
+
191
+ ---
192
+
193
+ ## Links
194
+
195
+ - **PyPI:** https://pypi.org/project/ara-cli/
196
+ - **Source code:** \[GitHub link or repository URL\]
197
+ - **Documentation:** \[Link if available\]
198
+
199
+ ---
200
+
201
+ ## Contributing
202
+
203
+ Contributions, issues, and feature requests are welcome! Please open an issue or submit a pull request via GitHub.
204
+
205
+ ---
206
+
207
+ **ara-cli — Structure your development. Automate with AI. Build better software.**
208
+
@@ -1,18 +1,18 @@
1
1
  ara_cli/__init__.py,sha256=0zl7IegxTid26EBGLav_fXZ4CCIV3H5TfAoFQiOHjvg,148
2
- ara_cli/__main__.py,sha256=Z6XYWRLceIoZPvfC-X9EXouSZdtFOOe84kKVWJGA4r4,1861
2
+ ara_cli/__main__.py,sha256=ppfq0FIi4x6ONRzP67784A4BPo2labh8Bd_EExuXo4U,2011
3
3
  ara_cli/ara_command_action.py,sha256=J613DUTjRxrPG8Jm-fJcIM0QlZTeULmq9Q7DKkDxJHg,22039
4
- ara_cli/ara_command_parser.py,sha256=HHuLxGeLjsd3R-JcoWJ5MUgaxXqkECHoqE95amlaVXY,18115
4
+ ara_cli/ara_command_parser.py,sha256=vyxLELnyAZFC2C3v0hH4-r9QBmJ8oIs0hCZukkMFXfc,20136
5
5
  ara_cli/ara_config.py,sha256=SgZfQVpqj5JJN4SB0n2IvAH0sKIdS3k1K1Zht2wDywA,8814
6
- ara_cli/artefact_autofix.py,sha256=XT-OGiznPCX7b6wwxFigHaVt5KzC2pFm6IT8HuwiSVs,17519
6
+ ara_cli/artefact_autofix.py,sha256=WVTiIR-jo4YKmmz4eS3qTFvl45W1YKwAk1XSuz9QX10,20015
7
7
  ara_cli/artefact_creator.py,sha256=0Ory6cB-Ahkw-BDNb8QHnTbp_OHGABdkb9bhwcEdcIc,6063
8
8
  ara_cli/artefact_deleter.py,sha256=Co4wwCH3yW8H9NrOq7_2p5571EeHr0TsfE-H8KqoOfY,1900
9
9
  ara_cli/artefact_fuzzy_search.py,sha256=iBlDqjZf-_D3VUjFf7ZwkiQbpQDcwRndIU7aG_sRTgE,2668
10
10
  ara_cli/artefact_link_updater.py,sha256=nKdxTpDKqWTOAMD8viKmUaklSFGWzJZ8S8E8xW_ADuM,3775
11
11
  ara_cli/artefact_lister.py,sha256=jhk4n4eqp7hDIq07q43QzS7-36BM3OfZ4EABxCeOGcw,4764
12
- ara_cli/artefact_reader.py,sha256=E6DMBvbOYf1OoLf-OyLaiB6K2-gd7iHmjoQZU9Rsy6g,6965
12
+ ara_cli/artefact_reader.py,sha256=Pho0_Eqm7kD9CNbVMhKb6mkNM0I3iJiCJXbXmVp1DJU,7827
13
13
  ara_cli/artefact_renamer.py,sha256=Hnz_3zD9xxnBa1FHyUE6mIktLk_9ttP2rFRvQIkmz-o,4061
14
14
  ara_cli/artefact_scan.py,sha256=msPCm-vPWOAZ_e_z5GylXxq1MtNlmJ4zvKrsdOFCWF4,4813
15
- ara_cli/chat.py,sha256=CR30wBfKaS8DVCeavEEYFMeCFp3IyRtybQnk-GrCj7U,29341
15
+ ara_cli/chat.py,sha256=Plje33XcOedSx-nmLCkuFIXSqHPIvMcy5I71xYWuYmU,31956
16
16
  ara_cli/classifier.py,sha256=zWskj7rBYdqYBGjksBm46iTgVU5IIf2PZsJr4qeiwVU,1878
17
17
  ara_cli/codefusionretriever.py,sha256=fCHgXdIBRzkVAnapX-KI2NQ44XbrrF4tEQmn5J6clUI,1980
18
18
  ara_cli/codehierachieretriever.py,sha256=Xd3EgEWWhkSf1TmTWtf8X5_YvyE_4B66nRrqarwSiTU,1182
@@ -31,9 +31,9 @@ ara_cli/run_file_lister.py,sha256=XbrrDTJXp1LFGx9Lv91SNsEHZPP-PyEMBF_P4btjbDA,23
31
31
  ara_cli/tag_extractor.py,sha256=TGdaQOVnjy25R0zDsAifB67C5oom0Fwo24s0_fr5A_I,3151
32
32
  ara_cli/template_manager.py,sha256=YwrN6AYPpl6ZrW8BVQpVXx8yTRf-oNpJUIKeg4NAggs,6606
33
33
  ara_cli/update_config_prompt.py,sha256=Oy9vNTw6UhDohyTEfSKkqE5ifEMPlmWNYkKHgUrK_pY,4607
34
- ara_cli/version.py,sha256=5e5J_7tLNCu-Uoszijp6c5omcE5kBH5OoEtO4VPWHLE,146
34
+ ara_cli/version.py,sha256=8uB-BYYtS-wDJMQLMNnX870pu9lmnUnQMT23kLhsfwE,146
35
35
  ara_cli/artefact_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- ara_cli/artefact_models/artefact_load.py,sha256=dNcwZDW2Dk0bts9YnPZ0ESmWD2NbsLIvl4Z-qQeGmTQ,401
36
+ ara_cli/artefact_models/artefact_load.py,sha256=IXzWxP-Q_j_oDGMno0m-OuXCQ7Vd5c_NctshGr4ROBw,621
37
37
  ara_cli/artefact_models/artefact_mapping.py,sha256=8aD0spBjkJ8toMAmFawc6UTUxB6-tEEViZXv2I-r88Q,1874
38
38
  ara_cli/artefact_models/artefact_model.py,sha256=qSbcrmFWAYgBqcNl9QARI1_uLQJm-TPVgP5q2AEFnjE,15983
39
39
  ara_cli/artefact_models/artefact_templates.py,sha256=8HNM-TsNvKgTpruOBs751yRDXJypTiJhc1tkWCiYG7s,9830
@@ -45,7 +45,7 @@ ara_cli/artefact_models/feature_artefact_model.py,sha256=FrR7_xydOmMySAz0QpWgrNF
45
45
  ara_cli/artefact_models/issue_artefact_model.py,sha256=v6CpKnkqiUh6Wch2kkEmyyW49c8ysdy1qz8l1Ft9uJA,2552
46
46
  ara_cli/artefact_models/keyfeature_artefact_model.py,sha256=J9oXLsCAo22AW31D5Z104y02ss0S0O4tPCcd09zYCD0,4066
47
47
  ara_cli/artefact_models/serialize_helper.py,sha256=Wks30wy-UrwJURetydKykLgJkdGRgXFHkDT24vHe5tU,595
48
- ara_cli/artefact_models/task_artefact_model.py,sha256=OrG7Z0u5QDGoS1a0PlWNxC2n47j9RtNZQo0rz1kNs84,5841
48
+ ara_cli/artefact_models/task_artefact_model.py,sha256=1BSMbz9D-RXvdpdd0RlAr9hUx84Rcuysk2YfQC8Qy14,6046
49
49
  ara_cli/artefact_models/userstory_artefact_model.py,sha256=2awH31ROtm7j4T44Bv4cylQDYLQtnfgXZMhDu_pgw-k,6435
50
50
  ara_cli/artefact_models/vision_artefact_model.py,sha256=frjaUJj-mmIlVHEhzAQztCGs-CtvNu_odSborgztfzo,5251
51
51
  ara_cli/templates/agile.artefacts,sha256=nTA8dp98HWKAD-0qhmNpVYIfkVGoJshZqMJGnphiOsE,7932
@@ -124,14 +124,14 @@ ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=by
124
124
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
125
125
  tests/test_ara_command_action.py,sha256=JTLqXM9BSMlU33OQgrk_sZnoowFJZKZAx8q-st-wa34,25821
126
126
  tests/test_ara_config.py,sha256=pvkdPLTzgLkOijil0HaN0mhLC2Rdu4Fu5RfXEyOlRfs,16672
127
- tests/test_artefact_autofix.py,sha256=K5h-IcPHZHdbLFgQHoto3K4fgX8cNW-ddHgQwCAIlFc,22145
127
+ tests/test_artefact_autofix.py,sha256=pApZ-N0dW8Ujt-cNLbgvd4bhiIIK8oXb-saLf6QlA-8,25022
128
128
  tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
129
129
  tests/test_artefact_link_updater.py,sha256=biqbEp2jCOz8giv72hu2P2hDfeJfJ9OrVGdAv5d9cK4,2191
130
130
  tests/test_artefact_lister.py,sha256=VCEOCgDgnAOeUUgIoGAbWgz60hf9UT-tdHg18LGfB34,22656
131
131
  tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
132
132
  tests/test_artefact_renamer.py,sha256=lSnKCCfoFGgKhTdDZrEaeBq1xJAak1QoqH5aSeOe9Ro,3494
133
133
  tests/test_artefact_scan.py,sha256=uNWgrt7ieZ4ogKACsPqzAsh59JF2BhTKSag31hpVrTQ,16887
134
- tests/test_chat.py,sha256=BcVCGJjdBHEIXR9l-9Q83fQXP7Hly6yHewY2qEuZ1DE,49161
134
+ tests/test_chat.py,sha256=fUGqpsyilLjwIFNlCAC69pYGEhwRuU6pplywwGJk-K8,54907
135
135
  tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
136
136
  tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
137
137
  tests/test_file_classifier.py,sha256=kLWPiePu3F5mkVuI_lK_2QlLh2kXD_Mt2K8KZZ1fAnA,10940
@@ -141,8 +141,8 @@ tests/test_list_filter.py,sha256=fJA3d_SdaOAUkE7jn68MOVS0THXGghy1fye_64Zvo1U,796
141
141
  tests/test_tag_extractor.py,sha256=nSiAYlTKZ7TLAOtcJpwK5zTWHhFYU0tI5xKnivLc1dU,2712
142
142
  tests/test_template_manager.py,sha256=q-LMHRG4rHkD6ON6YW4cpZxUx9hul6Or8wVVRC2kb-8,4099
143
143
  tests/test_update_config_prompt.py,sha256=xsqj1WTn4BsG5Q2t-sNPfu7EoMURFcS-hfb5VSXUnJc,6765
144
- ara_cli-0.1.9.76.dist-info/METADATA,sha256=DAKMzylR1gaCn0EtuJgjyL-Wx06-D9aST-DSYmEaJp4,415
145
- ara_cli-0.1.9.76.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
146
- ara_cli-0.1.9.76.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
147
- ara_cli-0.1.9.76.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
148
- ara_cli-0.1.9.76.dist-info/RECORD,,
144
+ ara_cli-0.1.9.78.dist-info/METADATA,sha256=j8cLr6oh3gVDON2FJE-pds7Psqspoxsgo008NV6btWw,6739
145
+ ara_cli-0.1.9.78.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
146
+ ara_cli-0.1.9.78.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
147
+ ara_cli-0.1.9.78.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
148
+ ara_cli-0.1.9.78.dist-info/RECORD,,
@@ -15,6 +15,7 @@ from ara_cli.artefact_autofix import (
15
15
  _has_valid_contribution,
16
16
  set_closest_contribution,
17
17
  fix_contribution,
18
+ fix_rule
18
19
  )
19
20
  from ara_cli.artefact_models.artefact_model import Artefact, ArtefactType, Contribution
20
21
 
@@ -60,6 +61,24 @@ def mock_artefact_with_contribution():
60
61
  return mock_artefact
61
62
 
62
63
 
64
+ @pytest.fixture
65
+ def mock_contribution():
66
+ m = MagicMock()
67
+ m.artefact_name = "parent_name"
68
+ m.classifier = "feature"
69
+ m.rule = "my_rule"
70
+ return m
71
+
72
+ @pytest.fixture
73
+ def mock_artefact(mock_contribution):
74
+ m = MagicMock()
75
+ m.contribution = mock_contribution
76
+ m._artefact_type.return_value.value = "requirement"
77
+ m.title = "my_title"
78
+ m.serialize.return_value = "serialized-text"
79
+ return m
80
+
81
+
63
82
  def test_read_report_file_success():
64
83
  """Tests successful reading of the report file."""
65
84
  mock_content = "# Artefact Check Report\n- `file.feature`: reason"
@@ -614,4 +633,72 @@ def test_apply_autofix_single_pass(
614
633
  assert "Single-pass mode enabled" in output
615
634
  assert "Attempt 1/1" in output
616
635
  assert "Attempt 2/1" not in output
617
- mock_check_file.assert_called_once()
636
+ mock_check_file.assert_called_once()
637
+
638
+
639
+ @patch("ara_cli.artefact_autofix._update_rule")
640
+ @patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
641
+ def test_fix_rule_with_rule(mock_populate, mock_update_rule, mock_artefact, mock_contribution, capsys):
642
+ # Contribution has a rule
643
+ artefact_class = MagicMock()
644
+ artefact_class.deserialize.return_value = mock_artefact
645
+ mock_populate.return_value = {"info": "dummy"}
646
+
647
+ result = fix_rule(
648
+ file_path="dummy.feature",
649
+ artefact_text="text",
650
+ artefact_class=artefact_class,
651
+ classified_artefact_info={},
652
+ )
653
+
654
+ # deserialize called
655
+ artefact_class.deserialize.assert_called_once_with("text")
656
+ # _update_rule called with correct args
657
+ mock_update_rule.assert_called_once_with(
658
+ artefact=mock_artefact,
659
+ name="parent_name",
660
+ classifier="feature",
661
+ classified_file_info={"info": "dummy"},
662
+ delete_if_not_found=True,
663
+ )
664
+ # Feedback message contains rule
665
+ assert "with rule" in capsys.readouterr().out
666
+ # Result is the serialized text
667
+ assert result == "serialized-text"
668
+
669
+ @patch("ara_cli.artefact_autofix._update_rule")
670
+ @patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
671
+ def test_fix_rule_without_rule(mock_populate, mock_update_rule, mock_artefact, mock_contribution, capsys):
672
+ # Contribution rule becomes None after update
673
+ mock_contribution.rule = None
674
+ artefact_class = MagicMock()
675
+ artefact_class.deserialize.return_value = mock_artefact
676
+ mock_populate.return_value = {"info": "dummy"}
677
+
678
+ result = fix_rule(
679
+ file_path="dummy.feature",
680
+ artefact_text="text",
681
+ artefact_class=artefact_class,
682
+ classified_artefact_info={},
683
+ )
684
+
685
+ # Feedback message says "without a rule"
686
+ assert "without a rule" in capsys.readouterr().out
687
+ assert result == "serialized-text"
688
+
689
+ @patch("ara_cli.artefact_autofix.populate_classified_artefact_info")
690
+ def test_fix_rule_contribution_none_raises(mock_populate):
691
+ # artefact.contribution is None: should assert
692
+ artefact = MagicMock()
693
+ artefact.contribution = None
694
+ artefact_class = MagicMock()
695
+ artefact_class.deserialize.return_value = artefact
696
+ mock_populate.return_value = {}
697
+
698
+ with pytest.raises(AssertionError):
699
+ fix_rule(
700
+ file_path="dummy.feature",
701
+ artefact_text="stuff",
702
+ artefact_class=artefact_class,
703
+ classified_artefact_info={},
704
+ )