@topolo/cli 0.12.33 → 0.13.1

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 (123) hide show
  1. package/README.md +45 -0
  2. package/agent-packs/README.md +495 -0
  3. package/agent-packs/pyproject.toml +16 -0
  4. package/agent-packs/requirements-test.txt +1 -0
  5. package/agent-packs/src/topolo_agent_pack/__init__.py +1 -0
  6. package/agent-packs/src/topolo_agent_pack/blueprint.py +57 -0
  7. package/agent-packs/src/topolo_agent_pack/claude.py +265 -0
  8. package/agent-packs/src/topolo_agent_pack/cli.py +443 -0
  9. package/agent-packs/src/topolo_agent_pack/execution.py +154 -0
  10. package/agent-packs/src/topolo_agent_pack/fresh.py +158 -0
  11. package/agent-packs/src/topolo_agent_pack/live.py +403 -0
  12. package/agent-packs/src/topolo_agent_pack/native.py +645 -0
  13. package/agent-packs/src/topolo_agent_pack/policy.py +92 -0
  14. package/agent-packs/src/topolo_agent_pack/storage.py +271 -0
  15. package/agent-packs/src/topolo_agent_pack/topolo.py +321 -0
  16. package/agent-packs/templates/career/AGENTS.md +17 -0
  17. package/agent-packs/templates/career/README.md +14 -0
  18. package/agent-packs/templates/career/agents/intakes/career-intake/AGENTS.md +21 -0
  19. package/agent-packs/templates/career/agents/specialists/application-preparation/AGENTS.md +21 -0
  20. package/agent-packs/templates/career/agents/specialists/application-tracker/AGENTS.md +21 -0
  21. package/agent-packs/templates/career/agents/specialists/interview-coach/AGENTS.md +21 -0
  22. package/agent-packs/templates/career/agents/specialists/opportunity-research/AGENTS.md +21 -0
  23. package/agent-packs/templates/career/agents/specialists/profile-cv/AGENTS.md +21 -0
  24. package/agent-packs/templates/career/pack.json +124 -0
  25. package/agent-packs/templates/career/shared/PACK.md +22 -0
  26. package/agent-packs/templates/career/shared/TOPOLO.md +13 -0
  27. package/agent-packs/templates/career/shared/WORKFLOW.md +13 -0
  28. package/agent-packs/templates/career/skills/topolo/SKILL.md +305 -0
  29. package/agent-packs/templates/clients/AGENTS.md +17 -0
  30. package/agent-packs/templates/clients/README.md +14 -0
  31. package/agent-packs/templates/clients/agents/intakes/client-intake/AGENTS.md +21 -0
  32. package/agent-packs/templates/clients/agents/specialists/acceptance-review/AGENTS.md +21 -0
  33. package/agent-packs/templates/clients/agents/specialists/delivery-planner/AGENTS.md +21 -0
  34. package/agent-packs/templates/clients/agents/specialists/discovery/AGENTS.md +21 -0
  35. package/agent-packs/templates/clients/agents/specialists/proposal-writer/AGENTS.md +21 -0
  36. package/agent-packs/templates/clients/agents/specialists/solution-design/AGENTS.md +21 -0
  37. package/agent-packs/templates/clients/pack.json +126 -0
  38. package/agent-packs/templates/clients/shared/PACK.md +22 -0
  39. package/agent-packs/templates/clients/shared/TOPOLO.md +13 -0
  40. package/agent-packs/templates/clients/shared/WORKFLOW.md +13 -0
  41. package/agent-packs/templates/clients/skills/topolo/SKILL.md +305 -0
  42. package/agent-packs/templates/creative/AGENTS.md +17 -0
  43. package/agent-packs/templates/creative/README.md +14 -0
  44. package/agent-packs/templates/creative/agents/intakes/creative-director/AGENTS.md +21 -0
  45. package/agent-packs/templates/creative/agents/specialists/brand-copy/AGENTS.md +21 -0
  46. package/agent-packs/templates/creative/agents/specialists/media-production/AGENTS.md +21 -0
  47. package/agent-packs/templates/creative/agents/specialists/quality-review/AGENTS.md +21 -0
  48. package/agent-packs/templates/creative/agents/specialists/storytelling/AGENTS.md +21 -0
  49. package/agent-packs/templates/creative/agents/specialists/visual-design/AGENTS.md +21 -0
  50. package/agent-packs/templates/creative/pack.json +128 -0
  51. package/agent-packs/templates/creative/shared/PACK.md +22 -0
  52. package/agent-packs/templates/creative/shared/TOPOLO.md +13 -0
  53. package/agent-packs/templates/creative/shared/WORKFLOW.md +13 -0
  54. package/agent-packs/templates/creative/skills/topolo/SKILL.md +305 -0
  55. package/agent-packs/templates/founder/AGENTS.md +17 -0
  56. package/agent-packs/templates/founder/README.md +14 -0
  57. package/agent-packs/templates/founder/agents/intakes/founder-intake/AGENTS.md +21 -0
  58. package/agent-packs/templates/founder/agents/specialists/commercial-modelling/AGENTS.md +21 -0
  59. package/agent-packs/templates/founder/agents/specialists/customer-research/AGENTS.md +21 -0
  60. package/agent-packs/templates/founder/agents/specialists/decision-review/AGENTS.md +21 -0
  61. package/agent-packs/templates/founder/agents/specialists/partnerships/AGENTS.md +21 -0
  62. package/agent-packs/templates/founder/agents/specialists/strategy/AGENTS.md +21 -0
  63. package/agent-packs/templates/founder/pack.json +126 -0
  64. package/agent-packs/templates/founder/shared/PACK.md +22 -0
  65. package/agent-packs/templates/founder/shared/TOPOLO.md +13 -0
  66. package/agent-packs/templates/founder/shared/WORKFLOW.md +13 -0
  67. package/agent-packs/templates/founder/skills/topolo/SKILL.md +305 -0
  68. package/agent-packs/templates/learning/AGENTS.md +17 -0
  69. package/agent-packs/templates/learning/README.md +14 -0
  70. package/agent-packs/templates/learning/agents/intakes/learning-intake/AGENTS.md +21 -0
  71. package/agent-packs/templates/learning/agents/specialists/critical-reviewer/AGENTS.md +21 -0
  72. package/agent-packs/templates/learning/agents/specialists/curriculum-designer/AGENTS.md +21 -0
  73. package/agent-packs/templates/learning/agents/specialists/knowledge-steward/AGENTS.md +21 -0
  74. package/agent-packs/templates/learning/agents/specialists/practice-coach/AGENTS.md +21 -0
  75. package/agent-packs/templates/learning/agents/specialists/tutor/AGENTS.md +21 -0
  76. package/agent-packs/templates/learning/pack.json +124 -0
  77. package/agent-packs/templates/learning/shared/PACK.md +22 -0
  78. package/agent-packs/templates/learning/shared/TOPOLO.md +13 -0
  79. package/agent-packs/templates/learning/shared/WORKFLOW.md +13 -0
  80. package/agent-packs/templates/learning/skills/topolo/SKILL.md +305 -0
  81. package/agent-packs/templates/personal/AGENTS.md +17 -0
  82. package/agent-packs/templates/personal/README.md +14 -0
  83. package/agent-packs/templates/personal/agents/intakes/personal-intake/AGENTS.md +21 -0
  84. package/agent-packs/templates/personal/agents/specialists/correspondence/AGENTS.md +21 -0
  85. package/agent-packs/templates/personal/agents/specialists/document-assistant/AGENTS.md +21 -0
  86. package/agent-packs/templates/personal/agents/specialists/planning/AGENTS.md +21 -0
  87. package/agent-packs/templates/personal/agents/specialists/research/AGENTS.md +21 -0
  88. package/agent-packs/templates/personal/agents/specialists/travel-logistics/AGENTS.md +21 -0
  89. package/agent-packs/templates/personal/pack.json +125 -0
  90. package/agent-packs/templates/personal/shared/PACK.md +22 -0
  91. package/agent-packs/templates/personal/shared/TOPOLO.md +13 -0
  92. package/agent-packs/templates/personal/shared/WORKFLOW.md +13 -0
  93. package/agent-packs/templates/personal/skills/topolo/SKILL.md +305 -0
  94. package/agent-packs/templates/research/AGENTS.md +17 -0
  95. package/agent-packs/templates/research/README.md +14 -0
  96. package/agent-packs/templates/research/agents/intakes/research-intake/AGENTS.md +21 -0
  97. package/agent-packs/templates/research/agents/specialists/adversarial-review/AGENTS.md +21 -0
  98. package/agent-packs/templates/research/agents/specialists/data-analysis/AGENTS.md +21 -0
  99. package/agent-packs/templates/research/agents/specialists/source-research/AGENTS.md +21 -0
  100. package/agent-packs/templates/research/agents/specialists/synthesis/AGENTS.md +21 -0
  101. package/agent-packs/templates/research/agents/specialists/technical-research/AGENTS.md +21 -0
  102. package/agent-packs/templates/research/pack.json +124 -0
  103. package/agent-packs/templates/research/shared/PACK.md +22 -0
  104. package/agent-packs/templates/research/shared/TOPOLO.md +13 -0
  105. package/agent-packs/templates/research/shared/WORKFLOW.md +13 -0
  106. package/agent-packs/templates/research/skills/topolo/SKILL.md +305 -0
  107. package/agent-packs/templates/ventures/AGENTS.md +17 -0
  108. package/agent-packs/templates/ventures/README.md +14 -0
  109. package/agent-packs/templates/ventures/agents/intakes/venture-intake/AGENTS.md +21 -0
  110. package/agent-packs/templates/ventures/agents/specialists/customer-discovery/AGENTS.md +21 -0
  111. package/agent-packs/templates/ventures/agents/specialists/economics/AGENTS.md +21 -0
  112. package/agent-packs/templates/ventures/agents/specialists/experiment-design/AGENTS.md +21 -0
  113. package/agent-packs/templates/ventures/agents/specialists/market-research/AGENTS.md +21 -0
  114. package/agent-packs/templates/ventures/agents/specialists/product-concept/AGENTS.md +21 -0
  115. package/agent-packs/templates/ventures/pack.json +126 -0
  116. package/agent-packs/templates/ventures/shared/PACK.md +22 -0
  117. package/agent-packs/templates/ventures/shared/TOPOLO.md +13 -0
  118. package/agent-packs/templates/ventures/shared/WORKFLOW.md +13 -0
  119. package/agent-packs/templates/ventures/skills/topolo/SKILL.md +305 -0
  120. package/agent-packs/topolo-agent-pack +8 -0
  121. package/dist/claude-pack-backend-IDQQX3DG.js +555 -0
  122. package/dist/index.js +143 -58
  123. package/package.json +7 -4
@@ -0,0 +1,265 @@
1
+ """Versioned Claude Code Desktop session import. Never rewrites live app records.
2
+
3
+ Fresh role files and CLI transcripts are owned by this adapter. Desktop adopts
4
+ them through its native resume handoff. There is no external archive API in
5
+ that handoff; full roster replacement is therefore refused, not simulated.
6
+ """
7
+ import argparse
8
+ import hashlib
9
+ import json
10
+ import os
11
+ import pathlib
12
+ import pty
13
+ import select as io_select
14
+ import shutil
15
+ import subprocess
16
+ import sys
17
+ import time
18
+ import uuid
19
+
20
+ from .storage import Library, PackError, atomic_json, canonical, now, safe_id, select
21
+
22
+
23
+ def find_binary():
24
+ root = pathlib.Path.home() / 'Library/Application Support/Claude/claude-code'
25
+ candidates = list(root.glob('*/*/claude.app/Contents/MacOS/claude'))
26
+ if candidates:
27
+ return str(max(candidates, key=lambda p: tuple(int(x) for x in p.relative_to(root).parts[0].split('.'))))
28
+ return shutil.which('claude')
29
+
30
+
31
+ def handoff(binary, directory, session_id):
32
+ """Native --desktop requires a TTY. No prompt, model call or shell is used."""
33
+ if not binary:
34
+ raise PackError('Claude Code 2.1.285+ is required; install Claude Desktop or Claude Code.')
35
+ version = subprocess.run([binary, '--version'], capture_output=True, text=True, timeout=10)
36
+ if version.returncode or not version.stdout.startswith('2.1.289 '):
37
+ raise PackError('This native import adapter is qualified for Claude Code 2.1.289; selected binary: ' + version.stdout.strip())
38
+ master, slave = pty.openpty()
39
+ process = None
40
+ output = bytearray()
41
+ try:
42
+ process = subprocess.Popen([binary, '--desktop', '--resume', session_id],
43
+ cwd=directory, stdin=slave, stdout=slave, stderr=slave)
44
+ os.close(slave)
45
+ slave = None
46
+ deadline = time.monotonic() + 20
47
+ while process.poll() is None:
48
+ if time.monotonic() >= deadline:
49
+ raise PackError('Claude Desktop handoff timed out; run recover to reconcile imports.')
50
+ if io_select.select([master], [], [], .1)[0]:
51
+ try:
52
+ chunk = os.read(master, 65536)
53
+ except OSError:
54
+ break
55
+ output.extend(chunk)
56
+ if len(output) > 65536:
57
+ raise PackError('Claude Desktop handoff exceeded its output limit.')
58
+ process.wait(timeout=2)
59
+ if process.returncode or ('Opening session ' + session_id) not in output.decode(errors='replace'):
60
+ raise PackError('Claude refused the native Desktop handoff: ' + output.decode(errors='replace')[-2000:])
61
+ finally:
62
+ if process is not None and process.poll() is None:
63
+ process.terminate()
64
+ process.wait(timeout=5)
65
+ if slave is not None:
66
+ os.close(slave)
67
+ os.close(master)
68
+
69
+
70
+ def project_directory(config, directory):
71
+ # The native CLI's current path-key codec. A versioned compatibility boundary.
72
+ import re
73
+ key = re.sub(r'[^a-zA-Z0-9]', '-', str(directory))
74
+ if len(key) > 200:
75
+ raise PackError('Claude project path exceeds the qualified path-key boundary (200 characters).')
76
+ return config / 'projects' / key
77
+
78
+
79
+ def write_new(path, data):
80
+ path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
81
+ if path.exists() or path.is_symlink():
82
+ if path.is_symlink() or not path.is_file() or path.read_bytes() != data:
83
+ raise PackError('Refusing to overwrite existing context or transcript: ' + str(path))
84
+ return
85
+ with path.open('xb') as stream:
86
+ stream.write(data)
87
+ stream.flush()
88
+ os.fsync(stream.fileno())
89
+ path.chmod(0o600)
90
+
91
+
92
+ def materialize(lib, name, selected, config):
93
+ manifest, revision = lib.load(name)
94
+ if manifest.get('startingContext', {}).get('kind') != 'fresh':
95
+ raise PackError('Claude import requires a fresh pack with reviewed role files; old Codex history is not translated.')
96
+ records = []
97
+ root = lib.root / 'claude'
98
+ for agent in selected:
99
+ safe_id(agent['set'])
100
+ safe_id(agent['id'])
101
+ key = name + '/' + agent['set'] + '/' + agent['id']
102
+ identity = root / 'identities' / (hashlib.sha256(key.encode()).hexdigest() + '.json')
103
+ if identity.exists():
104
+ record = json.loads(identity.read_text())
105
+ if record['sourceRevision'] != revision:
106
+ raise PackError('Existing Claude agent uses a different revision; build a new named pack to preserve its context.')
107
+ if record['importState'] != 'preparing':
108
+ records.append(record)
109
+ continue
110
+ else:
111
+ session_id = str(uuid.uuid4())
112
+ workspace = root / 'workspaces' / name / agent['set'] / agent['id']
113
+ record = {'key': key, 'pack': name, 'set': agent['set'], 'agent': agent['id'],
114
+ 'title': agent['title'], 'pinnedRequested': agent.get('pinned', False),
115
+ 'sourceRevision': revision, 'sourceCodexThreadId': agent['threadId'],
116
+ 'cliSessionId': session_id, 'desktopSessionId': 'local_' + session_id,
117
+ 'workspace': str(workspace),
118
+ 'transcript': str(project_directory(config, workspace) / (session_id + '.jsonl')),
119
+ 'importState': 'preparing', 'createdAt': now(), 'bootstrapUuid': str(uuid.uuid4())}
120
+ # Save allocation before any workspace/transcript write. Retrying a
121
+ # interrupted preparation uses the same IDs and compares old bytes.
122
+ atomic_json(identity, record)
123
+ original = pathlib.Path(manifest['threads'][agent['threadId']]['cwd'])
124
+ workspace = pathlib.Path(record['workspace'])
125
+ if workspace.is_symlink():
126
+ raise PackError('Claude workspace must not be a symlink')
127
+ workspace.mkdir(parents=True, exist_ok=True, mode=0o700)
128
+ resources = [r for r in manifest['resources'] if pathlib.Path(r['original']).is_relative_to(original)]
129
+ instructions = next((r for r in resources if pathlib.Path(r['original']).relative_to(original).as_posix() == 'AGENTS.md'), None)
130
+ if instructions is None:
131
+ raise PackError('Missing reviewed role instructions for ' + key)
132
+ for resource in resources:
133
+ relative = pathlib.Path(resource['original']).relative_to(original)
134
+ if '..' in relative.parts or any(p.is_symlink() for p in (workspace / relative).parents if p != lib.root.parent):
135
+ raise PackError('Unsafe context path: ' + str(relative))
136
+ # Never copy runtime state, prior tasks, scripts or credentials into a role.
137
+ if relative.suffix != '.md' or relative.as_posix() == 'context/TEAM.md':
138
+ continue
139
+ if relative.as_posix() == 'AGENTS.md':
140
+ relative = pathlib.Path('CLAUDE.md')
141
+ write_new(workspace / relative, lib.object(resource['content']).read_bytes())
142
+ session_id = record['cliSessionId']
143
+ transcript = pathlib.Path(record['transcript'])
144
+ note = (agent['title'] + '. You are this Topolo Agent Pack role in Claude Desktop. '
145
+ 'Read CLAUDE.md and relevant context/ files before work. '
146
+ 'This is starting context only; no assignment, claim or prior conversation is inherited. '
147
+ 'Await a new human request. Coordination requires human authorization.')
148
+ rows = [{'parentUuid': None, 'isSidechain': False, 'userType': 'external',
149
+ 'cwd': str(workspace), 'sessionId': session_id, 'version': '2.1.289',
150
+ 'type': 'user', 'message': {'role': 'user', 'content': note},
151
+ 'uuid': record['bootstrapUuid'], 'timestamp': record['createdAt']},
152
+ {'type': 'custom-title', 'sessionId': session_id, 'customTitle': agent['title']}]
153
+ write_new(transcript, b''.join(canonical(row) + b'\n' for row in rows))
154
+ record['importState'] = 'prepared'
155
+ atomic_json(identity, record)
156
+ records.append(record)
157
+ team = ('# Claude pack roster\n\nThese are Claude Desktop session identities. '
158
+ 'No task, claim, messaging authorization or hosted connection is granted by this roster.\n\n' +
159
+ '\n'.join('- ' + a['key'] + ' | ' + a['title'] + ' | ' + a['desktopSessionId'] for a in records) + '\n')
160
+ for record in records:
161
+ context = pathlib.Path(record['workspace']) / 'context'
162
+ write_new(context / ('TEAM-' + hashlib.sha256(team.encode()).hexdigest()[:16] + '.md'), team.encode())
163
+ pointer = context / 'TEAM.md'
164
+ if not pointer.exists():
165
+ write_new(pointer, b'# Claude team identities\n\nRead TEAM-*.md for immutable Claude roster snapshots. Use topolo-agent-pack status --harness claude for current membership. Only human-authorized coordination is permitted.\n')
166
+ return records
167
+
168
+
169
+ def readback(records, app_data):
170
+ results = []
171
+ for record in records:
172
+ files = list((app_data / 'claude-code-sessions').glob('*/*/' + record['desktopSessionId'] + '.json'))
173
+ if len(files) != 1:
174
+ results.append({'key': record['key'], 'verified': False, 'reason': 'unique Desktop adoption record missing'})
175
+ continue
176
+ state = json.loads(files[0].read_text())
177
+ ok = (state.get('cliSessionId') == record['cliSessionId'] and
178
+ state.get('title') == record['title'] and
179
+ state.get('cwd') == record['workspace'] and not state.get('isArchived'))
180
+ results.append({'key': record['key'], 'desktopSessionId': record['desktopSessionId'],
181
+ 'verified': ok, 'completedTurns': state.get('completedTurns') or 0,
182
+ 'record': str(files[0])})
183
+ return results
184
+
185
+
186
+ def parser():
187
+ p = argparse.ArgumentParser(description='Claude Code Desktop pack sessions (native sidebar import; full switch is not yet supported).')
188
+ p.add_argument('--store', default=os.environ.get('CODEX_PACK_HOME', str(pathlib.Path.home() / 'AgentPacks')))
189
+ p.add_argument('--claude-config', default=str(pathlib.Path.home() / '.claude'))
190
+ p.add_argument('--claude-data', default=str(pathlib.Path.home() / 'Library/Application Support/Claude'))
191
+ sub = p.add_subparsers(dest='command', required=True)
192
+ for action in ['add', 'switch', 'replace']:
193
+ q = sub.add_parser(action)
194
+ q.add_argument('source')
195
+ q.add_argument('--set', '--group', dest='sets', action='append')
196
+ q.add_argument('--agent', dest='agents', action='append')
197
+ q.add_argument('--catalog', action='store_true')
198
+ q.add_argument('--no-open', action='store_true')
199
+ q.add_argument('--import-only', action='store_true', help='Explicit incomplete native session import; does not activate pack settings')
200
+ q.add_argument('--claude-bin', default=find_binary())
201
+ for action in ['status', 'doctor', 'recover', 'list']:
202
+ sub.add_parser(action)
203
+ return p
204
+
205
+
206
+ def main(argv):
207
+ args = parser().parse_args(argv)
208
+ try:
209
+ lib = Library(args.store)
210
+ app_data = pathlib.Path(args.claude_data).expanduser().resolve()
211
+ state_path = lib.root / 'claude/state.json'
212
+ with lib.lock():
213
+ state = json.loads(state_path.read_text()) if state_path.exists() else {'schemaVersion': 1, 'harness': 'claude', 'agents': []}
214
+ if args.command in {'status', 'doctor', 'recover', 'list'}:
215
+ proof = readback(state['agents'], app_data)
216
+ active_path = lib.root / 'claude/active.json'
217
+ pending_path = lib.root / 'claude/native-operation.json'
218
+ result = {**state, 'adoptionProof': proof, 'verifiedAgents': sum(p['verified'] for p in proof),
219
+ 'fullPackSwitchSupported': False, 'sidebarGroupsAndPinsVerified': False,
220
+ 'nativeRecordsRewritten': False, 'modelIdentityResponseVerified': False,
221
+ 'activationReady': False, 'modelAndPermissionSettingsVerified': False,
222
+ 'lastVerifiedActiveRoster': json.loads(active_path.read_text()) if active_path.exists() else None,
223
+ 'nativeRecoveryRequired': pending_path.exists(),
224
+ 'nativeRecoveryReplayed': False,
225
+ 'verificationBoundary': 'read-only adoption records; use the explicit native connection for current settings or recovery'}
226
+ else:
227
+ if (lib.root / 'claude/native-operation.json').exists():
228
+ raise PackError('An unresolved native Claude operation blocks imports and preparation. Use topolo agent-packs recover with the explicit native connection; no effects are replayed.')
229
+ manifest, revision = lib.load(args.source)
230
+ chosen = select(manifest, args.sets, args.agents)
231
+ keys = {args.source + '/' + a['set'] + '/' + a['id'] for a in chosen}
232
+ outgoing = {a['key'] for a in state['agents']} - keys
233
+ if args.command in {'switch', 'replace'} and outgoing:
234
+ raise PackError('Claude full-pack replacement is not qualified: native --desktop --resume cannot archive the outgoing roster. No changes were made. Use add for sidebar import.')
235
+ if not args.no_open and not args.import_only:
236
+ raise PackError('Claude pack activation requires a qualified live backend for groups, pins, model and permission settings. No changes were made. --no-open prepares context; --import-only explicitly imports sessions without completing pack activation.')
237
+ records = materialize(lib, args.source, chosen, pathlib.Path(args.claude_config).expanduser().resolve())
238
+ combined = {a['key']: a for a in state['agents']}
239
+ combined.update({a['key']: a for a in records})
240
+ state = {**state, 'agents': list(combined.values()), 'updatedAt': now()}
241
+ # Durable recovery plan before asking Desktop to adopt a session.
242
+ atomic_json(state_path, state)
243
+ if not args.no_open:
244
+ for record in records:
245
+ if not readback([record], app_data)[0]['verified']:
246
+ handoff(args.claude_bin, record['workspace'], record['cliSessionId'])
247
+ deadline = time.monotonic() + 15
248
+ while not readback([record], app_data)[0]['verified']:
249
+ if time.monotonic() > deadline:
250
+ raise PackError('Desktop adoption not verified for ' + record['key'] + '; run recover.')
251
+ time.sleep(.1)
252
+ record['importState'] = 'desktop-adopted'
253
+ atomic_json(state_path, state)
254
+ proof = readback(records, app_data)
255
+ result = {'harness': 'claude', 'state': 'prepared' if args.no_open else 'desktop-adopted',
256
+ 'selectedAgents': len(records), 'verifiedAgents': sum(p['verified'] for p in proof),
257
+ 'adoptionProof': proof, 'fullPackSwitchSupported': False,
258
+ 'previousCodexHistoryCopied': False, 'nativeRecordsRewritten': False,
259
+ 'sidebarGroupsAndPinsVerified': False, 'activationReady': False,
260
+ 'modelAndPermissionSettingsVerified': False}
261
+ print(json.dumps(result, indent=2))
262
+ return 0
263
+ except (PackError, OSError, ValueError, KeyError, subprocess.SubprocessError) as exc:
264
+ print('topolo-agent-pack (Claude): ' + str(exc), file=sys.stderr)
265
+ return 2