janet-cli 0.3.5__py3-none-any.whl → 0.3.8__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.
janet/__init__.py CHANGED
@@ -1,3 +1,8 @@
1
1
  """Janet AI CLI - Sync tickets to local markdown files."""
2
2
 
3
- __version__ = "0.3.5"
3
+ from importlib.metadata import version, PackageNotFoundError
4
+
5
+ try:
6
+ __version__ = version("janet-cli")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0" # Fallback for local development
@@ -22,6 +22,8 @@ class MarkdownGenerator:
22
22
  ticket: Dict,
23
23
  organization_members: Optional[List[Dict]] = None,
24
24
  attachments: Optional[Dict] = None,
25
+ org_id: Optional[str] = None,
26
+ project_id: Optional[str] = None,
25
27
  ) -> str:
26
28
  """
27
29
  Generate complete markdown document from ticket data.
@@ -30,18 +32,26 @@ class MarkdownGenerator:
30
32
  ticket: Ticket dictionary
31
33
  organization_members: List of organization members (for name resolution)
32
34
  attachments: Dictionary with direct_attachments and indirect_attachments
35
+ org_id: Organization ID for generating frontend link
36
+ project_id: Project ID for generating frontend link
33
37
 
34
38
  Returns:
35
39
  Complete markdown string
36
40
  """
37
41
  sections = []
38
42
 
39
- # 1. Title
43
+ # 1. Link to Janet frontend (at the very top)
44
+ ticket_id = ticket.get("id")
45
+ if org_id and project_id and ticket_id:
46
+ link = self._generate_frontend_link(org_id, project_id, ticket_id)
47
+ sections.append(f"**View in Janet:** {link}\n")
48
+
49
+ # 2. Title
40
50
  ticket_key = ticket.get("ticket_key", "UNKNOWN")
41
51
  title = ticket.get("title", "Untitled")
42
52
  sections.append(f"# {ticket_key}: {title}\n")
43
53
 
44
- # 2. Metadata
54
+ # 3. Metadata
45
55
  sections.append(self._generate_metadata(ticket, organization_members))
46
56
 
47
57
  # 3. Description (pass attachments for inline image handling)
@@ -240,6 +250,21 @@ class MarkdownGenerator:
240
250
  lines.append("") # Empty line after child tasks
241
251
  return "\n".join(lines)
242
252
 
253
+ def _generate_frontend_link(self, org_id: str, project_id: str, ticket_id: str) -> str:
254
+ """
255
+ Generate link to view ticket in Janet frontend.
256
+
257
+ Args:
258
+ org_id: Organization ID
259
+ project_id: Project ID
260
+ ticket_id: Ticket ID
261
+
262
+ Returns:
263
+ Markdown link to Janet frontend
264
+ """
265
+ url = f"https://app.tryjanet.ai/dashboard/{org_id}/projects/{project_id}/{ticket_id}"
266
+ return url
267
+
243
268
  def _generate_footer(self, ticket_key: str) -> str:
244
269
  """Generate footer section."""
245
270
  export_date = self._format_date(datetime.utcnow().isoformat())
@@ -33,7 +33,7 @@ class ReadmeGenerator:
33
33
  sections = []
34
34
 
35
35
  # Header - addressing the AI agent directly
36
- sections.append(f"# Project Tickets - {org_name}\n")
36
+ sections.append(f"# Instructions for AI Coding Agents - {org_name}\n")
37
37
  sections.append(
38
38
  "This directory contains project management tickets from Janet AI. "
39
39
  "Use these tickets to understand requirements, track work, and stay aligned with project goals.\n"
@@ -83,6 +83,28 @@ class ReadmeGenerator:
83
83
  sections.append("3. **Follow requirements** - Use ticket descriptions as specifications")
84
84
  sections.append("4. **Note priorities** - High/Critical tickets should be addressed first\n")
85
85
 
86
+ # Important notes for AI agents
87
+ sections.append("## ⚠️ IMPORTANT: Creating Tickets (Required Fields)\n")
88
+ sections.append("### Required Fields for Ticket Creation\n")
89
+ sections.append("**Every** ticket creation command **MUST** include:\n")
90
+ sections.append("1. **Title** - The ticket title (first positional argument)")
91
+ sections.append("2. **Project** - The project key using `--project` or `-p`")
92
+ sections.append("3. **Status** - A valid status using `--status` or `-s`\n")
93
+
94
+ sections.append("### Status Values Are Custom Per Project\n")
95
+ sections.append("Each project has custom status values (see \"Valid Statuses\" in Projects section above).\n")
96
+ sections.append("⚠️ **REQUIRED:** Always use a valid status from the project's \"Valid Statuses\" list.")
97
+ sections.append("⚠️ **If user doesn't specify a status:** Ask which status to use before running the command.")
98
+ sections.append("⚠️ **Never assume or guess status values** - they vary by project (e.g., \"To Do\" vs \"Backlog\" vs \"Open\").\n")
99
+
100
+ sections.append("### Example Workflow\n")
101
+ sections.append('**User prompt:** "Create a bug ticket in BACK for the login issue"\n')
102
+ sections.append("**Your response:** \"Which status should I use for the new ticket? Valid options for BACK are: To Do, In Progress, In Review, Done\"\n")
103
+ sections.append('**After user responds:** Run the command with the specified status:')
104
+ sections.append("```bash")
105
+ sections.append('janet ticket create "Fix login issue" -p BACK --status "To Do" --type Bug')
106
+ sections.append("```\n")
107
+
86
108
  # CLI commands for the AI - comprehensive reference
87
109
  sections.append("## CLI Commands Reference\n")
88
110
  sections.append("Use the `janet` CLI to create and update tickets.\n")
@@ -264,7 +286,7 @@ class ReadmeGenerator:
264
286
  readme_content = self.generate(org_name, projects, total_tickets, sync_time, project_statuses)
265
287
 
266
288
  # Write to root of sync directory
267
- readme_path = sync_dir / "README.md"
289
+ readme_path = sync_dir / "AI_AGENT_INSTRUCTIONS.md"
268
290
  readme_path.write_text(readme_content, encoding="utf-8")
269
291
 
270
292
  return readme_path
janet/sync/sse_watcher.py CHANGED
@@ -109,7 +109,10 @@ class SSEWatcher:
109
109
  "direct_attachments": ticket_data.get("attachments", []),
110
110
  "indirect_attachments": []
111
111
  }
112
- markdown = self.markdown_generator.generate(ticket_data, self.org_members, attachments)
112
+ org_id = self.config.selected_organization.id
113
+ markdown = self.markdown_generator.generate(
114
+ ticket_data, self.org_members, attachments, org_id, project_id
115
+ )
113
116
  self.file_manager.write_ticket(
114
117
  org_name=self.org_name,
115
118
  project_name=project_name,
janet/sync/sync_engine.py CHANGED
@@ -168,8 +168,10 @@ class SyncEngine:
168
168
  # Get pre-fetched attachments for this ticket
169
169
  ticket_attachments = attachments_map.get(ticket_id)
170
170
 
171
+ org_id = self.config.selected_organization.id
171
172
  self._sync_single_ticket_fast(
172
- merged_ticket, org_name, project_name, org_members, ticket_attachments
173
+ merged_ticket, org_name, project_name, org_members, ticket_attachments,
174
+ org_id, project_id
173
175
  )
174
176
  synced_count += 1
175
177
  except Exception as e:
@@ -188,6 +190,8 @@ class SyncEngine:
188
190
  project_name: str,
189
191
  org_members: Optional[List[Dict]] = None,
190
192
  attachments: Optional[Dict] = None,
193
+ org_id: Optional[str] = None,
194
+ project_id: Optional[str] = None,
191
195
  ) -> None:
192
196
  """
193
197
  Sync a single ticket (optimized - no individual API calls).
@@ -198,6 +202,8 @@ class SyncEngine:
198
202
  project_name: Project name
199
203
  org_members: Organization members for name resolution
200
204
  attachments: Pre-fetched attachments dict (from batch fetch)
205
+ org_id: Organization ID for generating frontend link
206
+ project_id: Project ID for generating frontend link
201
207
  """
202
208
  ticket_id = ticket.get("id")
203
209
 
@@ -224,7 +230,7 @@ class SyncEngine:
224
230
 
225
231
  # Generate markdown
226
232
  markdown = self.markdown_generator.generate(
227
- ticket, org_members, attachments
233
+ ticket, org_members, attachments, org_id, project_id
228
234
  )
229
235
 
230
236
  # Write to file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janet-cli
3
- Version: 0.3.5
3
+ Version: 0.3.8
4
4
  Summary: CLI tool to sync Janet AI tickets to local markdown files
5
5
  Author-email: Janet AI <support@janet-ai.com>
6
6
  License: MIT
@@ -194,7 +194,7 @@ Tickets are organized in a clear hierarchy:
194
194
 
195
195
  ```
196
196
  janet-tickets/
197
- ├── README.md # Context for AI agents
197
+ ├── AI_AGENT_INSTRUCTIONS.md # Instructions for AI coding agents
198
198
  └── My Organization/
199
199
  ├── Backend/
200
200
  │ ├── BACK-1.md
@@ -1,4 +1,4 @@
1
- janet/__init__.py,sha256=cODHITT3PX8MXvRmpY-mkcn_z1QP1pdN4jrAhCLFEHw,82
1
+ janet/__init__.py,sha256=3vOjIbWOQBLG674GzJSTZefIoDGGcnApbUSx4tvZ0mA,255
2
2
  janet/__main__.py,sha256=nxsNRykF1aXRRSYNcMvohre4QDfpUzyr5JMyHRHWrwI,130
3
3
  janet/cli.py,sha256=A4R2-KTlTfBk4VwvMy5N5Zk22bP0abOuGgrCEGXO7cQ,40693
4
4
  janet/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -15,20 +15,20 @@ janet/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  janet/config/manager.py,sha256=Bs5YjHIzWPR-KuAFJyQL47ntU_JN87bGhROaYP0B3r4,3216
16
16
  janet/config/models.py,sha256=lnOcSlFTGVkEjCjNMOzq9OmPZ3KP0y7cvnoEHp5iQJY,2067
17
17
  janet/markdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- janet/markdown/generator.py,sha256=Dj9n72oLvMaJmFsutH_3nFGsMrYheIQ9EIIOOVeMxhQ,11216
18
+ janet/markdown/generator.py,sha256=YLxGh1Sv1bLuXE2e6jPYIOViBqXsBoL8QRqDF3v-4Ow,12171
19
19
  janet/markdown/yjs_converter.py,sha256=9CCInQzyjbpyDciMSKaUXCNh3xPn0hAwWhl1RES0KVc,12768
20
20
  janet/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  janet/sync/file_manager.py,sha256=fwV0VYP-0ZpwTAyDVqlVUPn3MB2wK6TvFQq450Lhr0I,5839
22
- janet/sync/readme_generator.py,sha256=xsc609Vw9iKEZWgnQ8Fiimg7HG4Ngc9fI4ah_eBISdI,12315
23
- janet/sync/sse_watcher.py,sha256=L7iZfa4n77IImvywSPQf7PYxK9VVb8kjrVOS3qQ5xjQ,11157
24
- janet/sync/sync_engine.py,sha256=l4bV5KDbF86IVDAbMZRbFWwwP65TW--JqVkmJjXDnA4,9369
22
+ janet/sync/readme_generator.py,sha256=ol4CC7yXYYoDrSPYbf0VASl-38uNjwKBg5klPNEnwUI,14032
23
+ janet/sync/sse_watcher.py,sha256=m-Qk0jmAiNTAZjhxvD4VBcY3iLLPMd45j4eUS3I3brw,11277
24
+ janet/sync/sync_engine.py,sha256=u_9iv-ZdPpl-wzGID_8WjgsW0GkLIru8VPjw2yg3isU,9708
25
25
  janet/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  janet/utils/console.py,sha256=zMhX4HUXSco-or9KpqBJmYCzmfH_GWtvq-n2Sy6Dfgc,927
27
27
  janet/utils/errors.py,sha256=yxoVDF7ovMT7ooJA-8cvxJK8_3nWumGv7s1n8povi-4,783
28
28
  janet/utils/paths.py,sha256=JYp5gcSEjIbYNJaUcAwdpz6rmta9m0JzoBARC6_AWmM,1706
29
- janet_cli-0.3.5.dist-info/licenses/LICENSE,sha256=wNChlibp2El7r-zfLH8QhOg2oLPAvGwP7ETnVWyNRio,1065
30
- janet_cli-0.3.5.dist-info/METADATA,sha256=cxi2vtFo0ao6JhhWBCOGZaUBVWtVDbS0ITQDeO-Hb4E,10311
31
- janet_cli-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
- janet_cli-0.3.5.dist-info/entry_points.txt,sha256=MeYUkStK_xcqW3AylPNpQh_H5zmfojB1-d8WMhifkvw,40
33
- janet_cli-0.3.5.dist-info/top_level.txt,sha256=Ux5zWeRoPO3Tu87toTRoiwMkIQNQylV9aRO2g7KqNW4,6
34
- janet_cli-0.3.5.dist-info/RECORD,,
29
+ janet_cli-0.3.8.dist-info/licenses/LICENSE,sha256=wNChlibp2El7r-zfLH8QhOg2oLPAvGwP7ETnVWyNRio,1065
30
+ janet_cli-0.3.8.dist-info/METADATA,sha256=CnA07FnZpIePe49L-gbTqM0ParDYHsueKHZKCeksiuE,10323
31
+ janet_cli-0.3.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
32
+ janet_cli-0.3.8.dist-info/entry_points.txt,sha256=MeYUkStK_xcqW3AylPNpQh_H5zmfojB1-d8WMhifkvw,40
33
+ janet_cli-0.3.8.dist-info/top_level.txt,sha256=Ux5zWeRoPO3Tu87toTRoiwMkIQNQylV9aRO2g7KqNW4,6
34
+ janet_cli-0.3.8.dist-info/RECORD,,