atlassian-cli 0.8.0__tar.gz → 0.10.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.
Files changed (29) hide show
  1. {atlassian_cli-0.8.0/src/atlassian_cli.egg-info → atlassian_cli-0.10.0}/PKG-INFO +1 -1
  2. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/pyproject.toml +1 -1
  3. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/confluence.py +69 -9
  4. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0/src/atlassian_cli.egg-info}/PKG-INFO +1 -1
  5. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/LICENSE +0 -0
  6. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/README.md +0 -0
  7. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/setup.cfg +0 -0
  8. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/__init__.py +0 -0
  9. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/adf.py +0 -0
  10. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/config.py +0 -0
  11. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/hints.py +0 -0
  12. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/http.py +0 -0
  13. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/jira.py +0 -0
  14. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/jira_assets.py +0 -0
  15. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/jira_issues.py +0 -0
  16. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/output.py +0 -0
  17. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli/update_check.py +0 -0
  18. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli.egg-info/SOURCES.txt +0 -0
  19. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli.egg-info/dependency_links.txt +0 -0
  20. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli.egg-info/entry_points.txt +0 -0
  21. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli.egg-info/requires.txt +0 -0
  22. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/src/atlassian_cli.egg-info/top_level.txt +0 -0
  23. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_adf.py +0 -0
  24. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_config.py +0 -0
  25. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_confluence.py +0 -0
  26. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_http.py +0 -0
  27. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_jira_assets.py +0 -0
  28. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_jira_issues.py +0 -0
  29. {atlassian_cli-0.8.0 → atlassian_cli-0.10.0}/tests/test_output.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlassian-cli
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: Fast CLI tools for Atlassian Cloud (Confluence + Jira) — optimized for AI agents
5
5
  Author-email: Alex Fishlock <alex.fishlock@catapult.cx>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "atlassian-cli"
7
- version = "0.8.0"
7
+ version = "0.10.0"
8
8
  description = "Fast CLI tools for Atlassian Cloud (Confluence + Jira) — optimized for AI agents"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -72,10 +72,15 @@ def get_space(session, base, *, key=None, space_id=None):
72
72
  return space
73
73
 
74
74
 
75
- def list_pages(session, base, space_id):
76
- """Cursor-paginated listing of all pages in a space."""
75
+ def list_pages(session, base, space_id, statuses=('current',)):
76
+ """Cursor-paginated listing of pages in a space, filtered by status.
77
+
78
+ The V2 default returns *all* statuses (current, archived, draft, deleted)
79
+ which is rarely what callers want. We default to current-only and let the
80
+ caller widen the filter if needed."""
77
81
  pages = []
78
- url = f'{base}{V2}/spaces/{space_id}/pages?limit=250&sort=id'
82
+ status_q = '&'.join(f'status={s}' for s in statuses)
83
+ url = f'{base}{V2}/spaces/{space_id}/pages?limit=250&sort=id&{status_q}'
79
84
  while url:
80
85
  resp = _retry(session.get, url)
81
86
  resp.raise_for_status()
@@ -213,12 +218,18 @@ def cmd_create(args):
213
218
 
214
219
 
215
220
  def _modify_page_metadata(session, base, page_id, *,
216
- new_title=None, new_parent_id=None, message=None):
217
- """Update a page's title and/or parentId without touching the body.
221
+ new_title=None, new_parent_id=None, new_space_id=None,
222
+ message=None):
223
+ """Update a page's title, parentId and/or spaceId without touching the body.
218
224
 
219
225
  Confluence has no dedicated 'move' or 'rename' endpoint — both go through
220
226
  PUT /pages/{id}, which requires the full body and a bumped version. Here
221
227
  we fetch the current state and round-trip it with the requested changes.
228
+
229
+ Setting `new_space_id` moves the page to a different space (cross-space
230
+ move). If you cross spaces you also need a parent in the destination
231
+ space (Confluence doesn't allow space-root pages via this endpoint), so
232
+ callers should normally pass new_parent_id alongside new_space_id.
222
233
  """
223
234
  remote = get_page(session, base, page_id)
224
235
  current_title = remote.get('title', '')
@@ -240,18 +251,29 @@ def _modify_page_metadata(session, base, page_id, *,
240
251
  }
241
252
  if new_parent_id is not None:
242
253
  payload['parentId'] = str(new_parent_id)
254
+ if new_space_id is not None:
255
+ payload['spaceId'] = str(new_space_id)
243
256
  api_put(session, base, f'{V2}/pages/{page_id}', payload)
244
257
  return title, new_version
245
258
 
246
259
 
247
260
  def cmd_move(args):
248
261
  session, base = setup()
262
+ space_id = getattr(args, 'space_id', None)
263
+ msg = getattr(args, 'message', None)
264
+ if space_id:
265
+ msg = msg or f'Moved to space {space_id} under parent {args.parent_id}'
266
+ else:
267
+ msg = msg or f'Moved under parent {args.parent_id}'
249
268
  new_title, new_version = _modify_page_metadata(
250
269
  session, base, args.page_id,
251
270
  new_parent_id=args.parent_id,
252
- message=getattr(args, 'message', None) or f'Moved under parent {args.parent_id}',
271
+ new_space_id=space_id,
272
+ message=msg,
253
273
  )
254
- emit('OK', f'Moved {args.page_id} ({new_title}) -> parent {args.parent_id} (v{new_version})')
274
+ suffix = f' in space {space_id}' if space_id else ''
275
+ emit('OK',
276
+ f'Moved {args.page_id} ({new_title}) -> parent {args.parent_id}{suffix} (v{new_version})')
255
277
 
256
278
 
257
279
  def cmd_rename(args):
@@ -264,6 +286,30 @@ def cmd_rename(args):
264
286
  emit('OK', f'Renamed {args.page_id} -> {new_title!r} (v{new_version})')
265
287
 
266
288
 
289
+ def cmd_archive(args):
290
+ """Archive a page (and optionally its descendants) via the V2 bulk
291
+ archive endpoint."""
292
+ session, base = setup()
293
+ body = {'pages': [{'id': str(args.page_id)}]}
294
+ result = api_post(session, base, f'{V2}/pages/archive', body)
295
+ # The endpoint returns a long-running task descriptor; the page becomes
296
+ # archived asynchronously. We surface the task link so the caller can
297
+ # poll if they need confirmation.
298
+ links = result.get('links', {}) if isinstance(result, dict) else {}
299
+ status_link = links.get('status', '')
300
+ emit('OK', f'Archive submitted for {args.page_id}', data={'task': status_link})
301
+
302
+
303
+ def cmd_unarchive(args):
304
+ """Restore a previously archived page via the V2 bulk restore endpoint."""
305
+ session, base = setup()
306
+ body = {'pages': [{'id': str(args.page_id)}]}
307
+ result = api_post(session, base, f'{V2}/pages/restore', body)
308
+ links = result.get('links', {}) if isinstance(result, dict) else {}
309
+ status_link = links.get('status', '')
310
+ emit('OK', f'Restore submitted for {args.page_id}', data={'task': status_link})
311
+
312
+
267
313
  def cmd_delete(args):
268
314
  session, base = setup()
269
315
  page = get_page(session, base, args.page_id)
@@ -431,11 +477,12 @@ def cmd_index(args):
431
477
  spaces = args.space if args.space else ['POL', 'COMPLY']
432
478
  index = {}
433
479
 
480
+ statuses = ('current', 'archived') if getattr(args, 'include_archived', False) else ('current',)
434
481
  for space_key in spaces:
435
482
  space = get_space(session, base, key=space_key)
436
483
  space_id = space['id']
437
484
  print(f'Indexing {space_key}…', file=sys.stderr)
438
- pages = list_pages(session, base, space_id)
485
+ pages = list_pages(session, base, space_id, statuses=statuses)
439
486
 
440
487
  index[space_key] = []
441
488
  for page in pages:
@@ -445,6 +492,7 @@ def cmd_index(args):
445
492
  'parentId': page.get('parentId', ''),
446
493
  'version': _ver(page),
447
494
  'updatedAt': _ver_ts(page),
495
+ 'status': page.get('status', 'current'),
448
496
  })
449
497
  print(f' {space_key}: {len(pages)} pages', file=sys.stderr)
450
498
 
@@ -956,9 +1004,11 @@ def main():
956
1004
  p.add_argument('page_id', help='Confluence page ID')
957
1005
  p.set_defaults(func=cmd_delete)
958
1006
 
959
- p = sub.add_parser('move', help='Move a page to a new parent (preserves body)')
1007
+ p = sub.add_parser('move', help='Move a page to a new parent (preserves body); '
1008
+ 'optionally to a different space')
960
1009
  p.add_argument('page_id', help='Confluence page ID to move')
961
1010
  p.add_argument('parent_id', help='New parent page ID')
1011
+ p.add_argument('--space-id', help='Destination space ID (cross-space move)')
962
1012
  p.add_argument('--message', '-m', help='Version message')
963
1013
  p.set_defaults(func=cmd_move)
964
1014
 
@@ -968,6 +1018,14 @@ def main():
968
1018
  p.add_argument('--message', '-m', help='Version message')
969
1019
  p.set_defaults(func=cmd_rename)
970
1020
 
1021
+ p = sub.add_parser('archive', help='Archive a page (V2 bulk archive endpoint)')
1022
+ p.add_argument('page_id', help='Confluence page ID to archive')
1023
+ p.set_defaults(func=cmd_archive)
1024
+
1025
+ p = sub.add_parser('unarchive', help='Restore an archived page')
1026
+ p.add_argument('page_id', help='Confluence page ID to restore')
1027
+ p.set_defaults(func=cmd_unarchive)
1028
+
971
1029
  p = sub.add_parser('put', help='Upload local ADF to Confluence')
972
1030
  p.add_argument('page_id', help='Confluence page ID')
973
1031
  p.add_argument('--dir', default='pages', help='Pages directory (default: pages)')
@@ -995,6 +1053,8 @@ def main():
995
1053
  p = sub.add_parser('index', help='Rebuild page-index.json from API')
996
1054
  p.add_argument('--space', action='append', help='Space key(s) to index (default: POL COMPLY)')
997
1055
  p.add_argument('--output', default='page-index.json', help='Output file (default: page-index.json)')
1056
+ p.add_argument('--include-archived', action='store_true',
1057
+ help='Also include archived pages (default is current only)')
998
1058
  p.set_defaults(func=cmd_index)
999
1059
 
1000
1060
  p = sub.add_parser('comments', help='List comments on a page')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atlassian-cli
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: Fast CLI tools for Atlassian Cloud (Confluence + Jira) — optimized for AI agents
5
5
  Author-email: Alex Fishlock <alex.fishlock@catapult.cx>
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes