netizen-cli 0.14.1__tar.gz → 0.14.3__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 (334) hide show
  1. {netizen_cli-0.14.1/netizen_cli.egg-info → netizen_cli-0.14.3}/PKG-INFO +1 -1
  2. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/README.md +2 -2
  3. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/__init__.py +1 -1
  4. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/admin.js +48 -31
  5. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/index.html +9 -5
  6. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/worktrees.js +21 -28
  7. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/web.py +6 -6
  8. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/bindings.py +19 -11
  9. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/controls.py +14 -20
  10. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/defaults.py +2 -2
  11. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/scheduled.py +24 -6
  12. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/workspace.py +3 -11
  13. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/messages.py +12 -11
  14. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/ports.py +6 -6
  15. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/reactions.py +3 -6
  16. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/reply_presenter.py +51 -93
  17. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/topics.py +7 -7
  18. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel_app.py +175 -207
  19. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_packages.py +20 -7
  20. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/codex_runtime.py +31 -40
  21. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/database_migrations.py +2 -0
  22. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/domain.py +2 -1
  23. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/experience.py +7 -7
  24. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/main.py +2 -3
  25. netizen_cli-0.14.3/netizen_cli/migrations/v20.py +96 -0
  26. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/model_settings.py +0 -22
  27. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/mcp.py +7 -4
  28. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/models.py +1 -0
  29. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/service.py +18 -8
  30. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/store.py +25 -10
  31. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sdk_gap_adapter.py +13 -5
  32. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/presentation.py +38 -61
  33. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/service.py +20 -32
  34. {netizen_cli-0.14.1 → netizen_cli-0.14.3/netizen_cli.egg-info}/PKG-INFO +1 -1
  35. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/SOURCES.txt +4 -2
  36. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/pyproject.toml +1 -1
  37. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/admin.md +4 -2
  38. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/commands.md +1 -1
  39. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/scheduled-tasks.md +24 -3
  40. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/sessions.md +5 -7
  41. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/user-guide.md +4 -4
  42. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_bindings.py +45 -8
  43. netizen_cli-0.14.3/tests/test_card_error_feedback.py +244 -0
  44. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cards.py +2 -0
  45. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_channel_app.py +142 -125
  46. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_channel_fixtures.py +20 -0
  47. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_data.py +6 -6
  48. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_distribution.py +34 -6
  49. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_packages.py +104 -2
  50. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_runtime.py +130 -12
  51. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_completion_mentions.py +15 -17
  52. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_completion_topic_delivery.py +3 -3
  53. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cron_chat_targets.py +10 -3
  54. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_database_migrations.py +4 -4
  55. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_cards.py +1 -1
  56. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_domain_experience.py +33 -11
  57. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_lark_sdk_contract.py +19 -12
  58. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_main.py +21 -2
  59. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_migration_schema_validation.py +1 -1
  60. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_model_settings.py +4 -37
  61. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_optional_progress_card_settings_migration.py +6 -4
  62. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_partial_answer_delivery.py +9 -9
  63. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_persistent_fork_channel.py +26 -3
  64. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_feishu_completion_mention.py +4 -4
  65. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_feishu_partial_answers.py +3 -3
  66. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_feishu_turn_file_card.py +4 -2
  67. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_scheduled_tasks.py +46 -2
  68. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_settings_migration.py +3 -3
  69. netizen_cli-0.14.3/tests/test_schedule_auto_archive_migration.py +110 -0
  70. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_card_retry.py +3 -0
  71. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_cards.py +49 -1
  72. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_mcp.py +25 -0
  73. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_scope_matrix.py +2 -2
  74. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_service.py +54 -0
  75. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_migration.py +2 -2
  76. netizen_cli-0.14.3/tests/test_scheduled_auto_archive.py +270 -0
  77. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduled_channel.py +9 -8
  78. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduler_presenter.py +8 -8
  79. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedules_storage.py +30 -1
  80. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_sdk_gap_adapter.py +45 -8
  81. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_collaboration_app.py +4 -3
  82. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_defaults_migration.py +3 -3
  83. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_tools.py +40 -0
  84. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_new.py +78 -21
  85. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_service.py +11 -10
  86. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_tools_integration.py +1 -1
  87. netizen_cli-0.14.1/netizen_cli/cards/model_info.py +0 -80
  88. netizen_cli-0.14.1/tests/test_model_info_cards.py +0 -125
  89. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/MANIFEST.in +0 -0
  90. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/_build.py +0 -0
  91. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/config.example.yaml +0 -0
  92. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/__main__.py +0 -0
  93. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/account_rate_limits.py +0 -0
  94. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/__init__.py +0 -0
  95. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/auth.py +0 -0
  96. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/credentials.py +0 -0
  97. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/errors.py +0 -0
  98. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/feishu.py +0 -0
  99. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/login.py +0 -0
  100. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/port_config.py +0 -0
  101. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/presentation.py +0 -0
  102. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/queries.py +0 -0
  103. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/admin.css +0 -0
  104. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/chat-picker.css +0 -0
  105. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/chat-picker.js +0 -0
  106. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/identity.js +0 -0
  107. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/login.html +0 -0
  108. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/login.js +0 -0
  109. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/progress-card.js +0 -0
  110. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/static/session-access.js +0 -0
  111. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/transport.py +0 -0
  112. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/admin/users.py +0 -0
  113. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/builtin_skills.py +0 -0
  114. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/__init__.py +0 -0
  115. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/callbacks.py +0 -0
  116. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/chat_target.py +0 -0
  117. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/fork.py +0 -0
  118. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/pagination.py +0 -0
  119. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/questions.py +0 -0
  120. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/reply.py +0 -0
  121. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/session_ids.py +0 -0
  122. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/status.py +0 -0
  123. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cards/usage.py +0 -0
  124. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/__init__.py +0 -0
  125. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/completion_mentions.py +0 -0
  126. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/input_preparation.py +0 -0
  127. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/question_inputs.py +0 -0
  128. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/channel/session_pages.py +0 -0
  129. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/chat_avatars.py +0 -0
  130. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/chat_targets.py +0 -0
  131. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli.py +0 -0
  132. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_data.py +0 -0
  133. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_services.py +0 -0
  134. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_setup.py +0 -0
  135. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_update.py +0 -0
  136. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_update_restore.py +0 -0
  137. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/cli_update_worker.py +0 -0
  138. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/completion_mention.py +0 -0
  139. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/__init__.py +0 -0
  140. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/models.py +0 -0
  141. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/service.py +0 -0
  142. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/defaults/store.py +0 -0
  143. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/__init__.py +0 -0
  144. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/restart_worker.py +0 -0
  145. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/update_executor.py +0 -0
  146. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/deployment/update_protocol.py +0 -0
  147. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/error_messages.py +0 -0
  148. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/feishu_app_onboarding.py +0 -0
  149. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/feishu_app_permissions.py +0 -0
  150. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/feishu_links.py +0 -0
  151. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/git_status.py +0 -0
  152. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/image_inputs.py +0 -0
  153. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/instance.py +0 -0
  154. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/lark_app.py +0 -0
  155. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/__init__.py +0 -0
  156. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/blocking_io.py +0 -0
  157. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/chat_directory.py +0 -0
  158. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/chat_labels.py +0 -0
  159. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/coordination.py +0 -0
  160. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/service.py +0 -0
  161. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/management/updates.py +0 -0
  162. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/markdown_images.py +0 -0
  163. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/mcp_transport.py +0 -0
  164. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_content.py +0 -0
  165. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_history.py +0 -0
  166. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_preparation.py +0 -0
  167. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/message_projection.py +0 -0
  168. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/__init__.py +0 -0
  169. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/schema.py +0 -0
  170. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v14.py +0 -0
  171. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v15.py +0 -0
  172. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v16.py +0 -0
  173. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v17.py +0 -0
  174. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v18.py +0 -0
  175. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/migrations/v19.py +0 -0
  176. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/package_resources.py +0 -0
  177. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/pagination.py +0 -0
  178. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/partial_answers.py +0 -0
  179. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/progress_card_config.py +0 -0
  180. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/progress_card_settings.py +0 -0
  181. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/projects.py +0 -0
  182. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/prompt_projection.py +0 -0
  183. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/quoted_context.py +0 -0
  184. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/result_images.py +0 -0
  185. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/__init__.py +0 -0
  186. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/contracts.py +0 -0
  187. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/name_writes.py +0 -0
  188. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/runtime/thread_naming.py +0 -0
  189. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/__init__.py +0 -0
  190. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/schedules/scheduler.py +0 -0
  191. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/service_launcher.py +0 -0
  192. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/session_inventory.py +0 -0
  193. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/session_settings.py +0 -0
  194. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/__init__.py +0 -0
  195. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/delegation.py +0 -0
  196. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/errors.py +0 -0
  197. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/mcp.py +0 -0
  198. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/policy.py +0 -0
  199. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/schema.py +0 -0
  200. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/sessions/service.py +0 -0
  201. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/settings.py +0 -0
  202. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/skill_references.py +0 -0
  203. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/task_text.py +0 -0
  204. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/terminal_cleanup.py +0 -0
  205. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/thread_history.py +0 -0
  206. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_activity.py +0 -0
  207. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_files.py +0 -0
  208. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_patch_children.py +0 -0
  209. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/turn_plan_observer.py +0 -0
  210. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/user_questions.py +0 -0
  211. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/errors.py +0 -0
  212. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/git.py +0 -0
  213. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/models.py +0 -0
  214. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/policy.py +0 -0
  215. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/settings.py +0 -0
  216. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli/worktrees/store.py +0 -0
  217. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/dependency_links.txt +0 -0
  218. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/entry_points.txt +0 -0
  219. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/requires.txt +0 -0
  220. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/netizen_cli.egg-info/top_level.txt +0 -0
  221. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/setup.cfg +0 -0
  222. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-lark/SKILL.md +0 -0
  223. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/SKILL.md +0 -0
  224. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/concepts.md +0 -0
  225. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/faq.md +0 -0
  226. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/feedback-and-files.md +0 -0
  227. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/fork-side-goal.md +0 -0
  228. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/messages.md +0 -0
  229. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/native-capabilities.md +0 -0
  230. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/session-collaboration.md +0 -0
  231. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/skills/netizen-user-guide/references/status-and-control.md +0 -0
  232. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_account_rate_limits.py +0 -0
  233. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_admin_update_executor_probe.py +0 -0
  234. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_binding_scheduled_channel.py +0 -0
  235. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_channel_input_preparation.py +0 -0
  236. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_chat_avatars.py +0 -0
  237. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_chat_target_cards.py +0 -0
  238. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_check_sdk.py +0 -0
  239. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli.py +0 -0
  240. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_integration.py +0 -0
  241. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_packaging.py +0 -0
  242. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_services.py +0 -0
  243. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_setup.py +0 -0
  244. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_cli_update.py +0 -0
  245. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_sdk_capabilities.py +0 -0
  246. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_sdk_upgrade_0161.py +0 -0
  247. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_codex_sdk_upgrade_0163.py +0 -0
  248. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_completion_mention_cards.py +0 -0
  249. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_configuration_publication.py +0 -0
  250. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_channel.py +0 -0
  251. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_runtime.py +0 -0
  252. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_defaults_worktree_channel.py +0 -0
  253. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_deployment.py +0 -0
  254. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_documentation_links.py +0 -0
  255. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_error_messages.py +0 -0
  256. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_feishu_app_onboarding.py +0 -0
  257. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_feishu_app_permissions.py +0 -0
  258. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_file_pagination.py +0 -0
  259. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_fork_cards.py +0 -0
  260. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_git_status.py +0 -0
  261. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_goal_reply_separation.py +0 -0
  262. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_image_inputs.py +0 -0
  263. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_lark_app.py +0 -0
  264. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_markdown_images.py +0 -0
  265. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_content.py +0 -0
  266. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_history.py +0 -0
  267. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_material_inputs.py +0 -0
  268. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_message_preparation.py +0 -0
  269. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_name_writes.py +0 -0
  270. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_pagination.py +0 -0
  271. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_partial_answers.py +0 -0
  272. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_persistent_fork_bindings.py +0 -0
  273. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_persistent_fork_runtime.py +0 -0
  274. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_child_files.py +0 -0
  275. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_persistent_fork.py +0 -0
  276. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_python_sdk.py +0 -0
  277. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_sdk_completion_race.py +0 -0
  278. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_sdk_response_recovery.py +0 -0
  279. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_skill_execution.py +0 -0
  280. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_probe_thread_naming.py +0 -0
  281. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_config.py +0 -0
  282. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_entrypoints.py +0 -0
  283. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_progress_card_presentation.py +0 -0
  284. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_project_delete_channel.py +0 -0
  285. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_project_delete_runtime.py +0 -0
  286. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_project_deletion_storage.py +0 -0
  287. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_prompt_projection.py +0 -0
  288. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_question_cards.py +0 -0
  289. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_question_channel.py +0 -0
  290. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_question_target_parity.py +0 -0
  291. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_quoted_context.py +0 -0
  292. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_release_script.py +0 -0
  293. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_repository_hygiene.py +0 -0
  294. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_result_images.py +0 -0
  295. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_resume_channel.py +0 -0
  296. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_retired_deployment.py +0 -0
  297. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_runtime_resume_activation.py +0 -0
  298. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_runtime_thread_naming.py +0 -0
  299. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_run_now.py +0 -0
  300. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_cards.py +0 -0
  301. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_service.py +0 -0
  302. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_schedule_workspace_storage.py +0 -0
  303. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduled_runtime.py +0 -0
  304. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduled_worktrees.py +0 -0
  305. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_scheduler.py +0 -0
  306. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_service_launcher.py +0 -0
  307. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_card_pages.py +0 -0
  308. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_defaults.py +0 -0
  309. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_external_runtime.py +0 -0
  310. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_id_cards.py +0 -0
  311. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_policy.py +0 -0
  312. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_session_settings.py +0 -0
  313. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_settings_projects.py +0 -0
  314. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_status_cards.py +0 -0
  315. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_status_commands.py +0 -0
  316. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_task_text.py +0 -0
  317. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_terminal_cleanup.py +0 -0
  318. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_thread_history.py +0 -0
  319. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_thread_naming.py +0 -0
  320. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_activity.py +0 -0
  321. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_files.py +0 -0
  322. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_patch_children.py +0 -0
  323. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_patch_statistics.py +0 -0
  324. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_turn_plan_observer.py +0 -0
  325. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_update_executor.py +0 -0
  326. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_usage_card.py +0 -0
  327. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_usage_control.py +0 -0
  328. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_user_guide_skill.py +0 -0
  329. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_user_questions.py +0 -0
  330. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_channel_cwd.py +0 -0
  331. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_git.py +0 -0
  332. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_runtime_cwd.py +0 -0
  333. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_settings.py +0 -0
  334. {netizen_cli-0.14.1 → netizen_cli-0.14.3}/tests/test_worktree_storage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netizen-cli
3
- Version: 0.14.1
3
+ Version: 0.14.3
4
4
  Summary: Use native Codex threads through a Feishu Channel.
5
5
  Requires-Python: <3.15,>=3.11
6
6
  Requires-Dist: h11==0.16.0
@@ -103,7 +103,7 @@ Agent/脚本使用 `netizen update --json`:stdout 只输出最终 JSON,进
103
103
  ### 3. 在飞书开始第一次对话
104
104
 
105
105
  1. 打开机器人单聊;如需在群里使用,先将机器人加入目标群。
106
- 2. 发送 `/settings`,创建或登记一个项目并启用。项目登记一个默认工作目录。
106
+ 2. 发送 `/projects`,创建或登记一个项目并启用。项目登记一个默认工作目录。
107
107
  3. 发送 `/new`,在卡片中选择项目。模型配置可选择“继承 Codex”;过程卡默认开启,可按需关闭。
108
108
  4. 直接发送任务,例如:**“梳理这个项目的结构,并生成一份入门说明。”**
109
109
  5. 收到回复后继续交流;如果出现“本轮文件”,点击“发送”取回需要的文件。
@@ -130,7 +130,7 @@ Git 项目可在 `/new` 中加载 worktree 选项,选择已有受管 worktree
130
130
 
131
131
  | 想做什么 | 入口 |
132
132
  | --- | --- |
133
- | 创建或登记项目工作目录 | `/settings` |
133
+ | 创建或登记项目工作目录 | `/projects` |
134
134
  | 新建会话,选择项目及受管 worktree | `/new` |
135
135
  | 查看、保存或删除当前聊天的会话默认配置 | `/defaults` |
136
136
  | 查看并切换已有会话 | `/sessions` |
@@ -1,3 +1,3 @@
1
1
  """Netizen: a thin Feishu Channel for native Codex Threads."""
2
2
 
3
- __version__ = "0.14.1"
3
+ __version__ = "0.14.3"
@@ -2616,13 +2616,6 @@ function renderWorkspacePolicy(input, editor, { project, loading = false, schedu
2616
2616
  input("worktree").required = workspace.mode === "existing";
2617
2617
  input("workspace-ref").value = workspace.ref || "";
2618
2618
  for (const field of ["workspace", "worktree", "workspace-ref"]) input(field).disabled = loading;
2619
- input("workspace-refs").replaceChildren();
2620
- for (const branch of options?.branches || []) {
2621
- const option = document.createElement("option");
2622
- option.value = branch.ref;
2623
- option.textContent = branch.name;
2624
- input("workspace-refs").append(option);
2625
- }
2626
2619
  const notes = [];
2627
2620
  if (loading) notes.push("正在读取 worktree 选项…");
2628
2621
  if (editor.workspaceError) notes.push(workspaceErrorMessage(editor.workspaceError));
@@ -3392,6 +3385,8 @@ function renderScheduleTarget() {
3392
3385
  scheduleInput("chat-target").hidden = binding;
3393
3386
  if (binding) { scheduleInput("chat").required = false; chatTarget("schedule").close(); }
3394
3387
  scheduleInput("session-settings").hidden = binding;
3388
+ scheduleInput("auto-archive-field").hidden = binding;
3389
+ scheduleInput("auto-archive").disabled = binding;
3395
3390
  renderScheduleWorkspace();
3396
3391
  const target = editor.targetSession;
3397
3392
  const selected = editor.plan?.target_binding_id || target?.bindingId || "";
@@ -3445,6 +3440,7 @@ function scheduleEditorValues() {
3445
3440
  .filter((input) => input.checked).map((input) => input.value);
3446
3441
  return { name: scheduleInput("name").value, instructions: scheduleInput("instructions").value,
3447
3442
  enabled: scheduleInput("enabled").checked, targetKind: scheduleTargetKind(),
3443
+ autoArchive: binding ? null : scheduleInput("auto-archive").checked,
3448
3444
  binding: binding ? scheduleInput("binding").value : "",
3449
3445
  project: binding ? "" : scheduleInput("project").value,
3450
3446
  chat: binding ? "" : scheduleInput("chat").value, rule,
@@ -3458,7 +3454,7 @@ function scheduleEditorHasChanges() {
3458
3454
  }
3459
3455
 
3460
3456
  function captureScheduleDraft() {
3461
- const fields = ["name", "instructions", "enabled", "kind", "timezone", "time", "at", "offset", "end-at", "end-offset", "every"];
3457
+ const fields = ["name", "instructions", "enabled", "auto-archive", "kind", "timezone", "time", "at", "offset", "end-at", "end-offset", "every"];
3462
3458
  return {
3463
3459
  discardBase: structuredClone(scheduleEditor.discardBase), dirty: scheduleEditorHasChanges(),
3464
3460
  fields: fields.map((id) => ({ id, value: scheduleInput(id).value, checked: scheduleInput(id).checked })),
@@ -3733,8 +3729,8 @@ function renderScheduleWorkspace() {
3733
3729
  }
3734
3730
 
3735
3731
  function updateScheduleSaveAvailability() {
3736
- scheduleInput("save").disabled = !schedulePrepared || !scheduleEditor?.action
3737
- || (!schedulePrepared.hasFuture && !scheduleEditor.plan) || !scheduleWorkspaceCanSave();
3732
+ scheduleInput("save").disabled = scheduleInput("fields").disabled || !scheduleEditor?.action
3733
+ || !scheduleWorkspaceCanSave();
3738
3734
  }
3739
3735
 
3740
3736
  async function changeScheduleProject() {
@@ -3852,9 +3848,9 @@ function readScheduleRule() {
3852
3848
  function invalidateSchedulePreview() {
3853
3849
  schedulePrepared = null;
3854
3850
  schedulePreviewSerial += 1;
3855
- scheduleInput("save").disabled = true;
3851
+ updateScheduleSaveAvailability();
3856
3852
  scheduleInput("preview-times").replaceChildren();
3857
- scheduleInput("preview-message").textContent = "保存前请预览触发时间。";
3853
+ scheduleInput("preview-message").textContent = "可预览触发时间,也可直接保存。";
3858
3854
  scheduleInput("preview-message").classList.toggle("error", false);
3859
3855
  for (const node of document.querySelectorAll("[data-schedule-rule]")) {
3860
3856
  node.hidden = !node.dataset.scheduleRule.split(" ").includes(scheduleInput("kind").value);
@@ -3879,9 +3875,12 @@ function renderSchedulePreview(selector, preview) {
3879
3875
 
3880
3876
  async function previewSchedule() {
3881
3877
  if (scheduleInput("fields").disabled || !scheduleEditor) return;
3878
+ return prepareSchedule();
3879
+ }
3880
+
3881
+ async function prepareSchedule() {
3882
3882
  const serial = ++schedulePreviewSerial;
3883
3883
  schedulePrepared = null;
3884
- scheduleInput("save").disabled = true;
3885
3884
  if (!validateScheduleFields()) return;
3886
3885
  scheduleInput("preview-message").textContent = "正在计算触发时间…";
3887
3886
  scheduleInput("preview-message").classList.toggle("error", false);
@@ -3918,6 +3917,7 @@ async function previewSchedule() {
3918
3917
  ? `预计触发时间 · ${data.schedule.timezone}`
3919
3918
  : scheduleEditor?.plan ? "没有后续触发时间。仍可保存计划修改;已认领的执行可以继续。" : "没有后续触发时间,请调整规则。";
3920
3919
  updateScheduleSaveAvailability();
3920
+ return schedulePrepared;
3921
3921
  } catch (error) {
3922
3922
  if (serial !== schedulePreviewSerial) return;
3923
3923
  if (["ambiguous_local_time", "ambiguous_end_time"].includes(error.code) && Array.isArray(error.choices)) {
@@ -3963,6 +3963,7 @@ function openScheduleEditor(plan = null, source = {}) {
3963
3963
  chatTarget("schedule", changeScheduleChat).set({ chatId: plan?.chat_id, chat: plan?.chat });
3964
3964
  scheduleInput("instructions").value = plan?.instructions || "";
3965
3965
  scheduleInput("enabled").checked = plan?.enabled ?? true;
3966
+ scheduleInput("auto-archive").checked = plan?.auto_archive ?? false;
3966
3967
  scheduleInput("timezone").value = rule.timezone;
3967
3968
  scheduleInput("kind").value = rule.kind;
3968
3969
  scheduleInput("time").value = ["daily", "weekly"].includes(rule.kind) ? rule.at : "09:00";
@@ -4001,33 +4002,45 @@ async function editSchedule(plan) {
4001
4002
 
4002
4003
  async function saveSchedule(event) {
4003
4004
  event.preventDefault();
4004
- if (scheduleInput("fields").disabled || !scheduleEditor?.action || !schedulePrepared
4005
- || schedulePrepared.fingerprint !== scheduleRuleFingerprint()) return;
4005
+ if (scheduleInput("fields").disabled || !scheduleEditor?.action) return;
4006
4006
  if (!validateScheduleFields({ saving: true })) return;
4007
4007
  const editor = scheduleEditor;
4008
- const prepared = schedulePrepared;
4009
- if (scheduleTargetKind() !== "binding") {
4010
- let validationError = null;
4011
- scheduleInput("fields").disabled = true;
4012
- scheduleInput("close").disabled = true;
4013
- try { await chatTarget("schedule").validate(); }
4014
- catch (error) {
4015
- invalidateSchedulePreview();
4016
- validationError = error;
4017
- } finally {
4018
- scheduleInput("fields").disabled = false;
4019
- scheduleInput("close").disabled = false;
4008
+ let prepared = schedulePrepared;
4009
+ let validationError = null;
4010
+ scheduleInput("fields").disabled = true;
4011
+ scheduleInput("close").disabled = true;
4012
+ updateScheduleSaveAvailability();
4013
+ try {
4014
+ // Resolve local times and validate the current draft without requiring a
4015
+ // separate preview click. The same read preserves DST and interval anchors.
4016
+ if (!prepared || prepared.fingerprint !== scheduleRuleFingerprint()) prepared = await prepareSchedule();
4017
+ if (!prepared) {
4018
+ if (scheduleInput("form-error").hidden) showScheduleFormError("配置已更新,请检查后重新保存。");
4019
+ return;
4020
4020
  }
4021
- if (scheduleEditor !== editor) return;
4022
- if (validationError) { showScheduleRequestError(validationError); return; }
4021
+ if (!prepared.hasFuture && !editor.plan) {
4022
+ showScheduleFormError("没有后续触发时间,请调整规则后保存。");
4023
+ return;
4024
+ }
4025
+ if (scheduleTargetKind() !== "binding") await chatTarget("schedule").validate();
4026
+ } catch (error) {
4027
+ invalidateSchedulePreview();
4028
+ validationError = error;
4029
+ } finally {
4030
+ scheduleInput("fields").disabled = false;
4031
+ scheduleInput("close").disabled = false;
4032
+ updateScheduleSaveAvailability();
4033
+ if (scheduleFieldErrors.size) focusScheduleError();
4023
4034
  }
4035
+ if (scheduleEditor !== editor) return;
4036
+ if (validationError) { showScheduleRequestError(validationError); return; }
4024
4037
  // A late options response may replace defaults and invalidate the preview
4025
4038
  // while group validation is in flight. Never submit that stale confirmation.
4026
4039
  if (schedulePrepared !== prepared || prepared.fingerprint !== scheduleRuleFingerprint()) {
4027
4040
  invalidateSchedulePreview();
4028
- scheduleInput("preview-message").textContent = "配置已更新,请重新预览后保存。";
4041
+ scheduleInput("preview-message").textContent = "配置已更新,请检查后重新保存。";
4029
4042
  scheduleInput("preview-message").classList.toggle("error", true);
4030
- showScheduleFormError("配置已更新,请重新预览后保存。");
4043
+ showScheduleFormError("配置已更新,请检查后重新保存。");
4031
4044
  return;
4032
4045
  }
4033
4046
  if (!scheduleWorkspaceCanSave()) {
@@ -4051,6 +4064,7 @@ async function saveSchedule(event) {
4051
4064
  } else {
4052
4065
  definition.project = scheduleInput("project").value.trim();
4053
4066
  definition.chat_id = scheduleInput("chat").value.trim();
4067
+ definition.auto_archive = scheduleInput("auto-archive").checked;
4054
4068
  if (scheduleWorkspaceChanged()) {
4055
4069
  definition.workspace = structuredClone(editor.workspace);
4056
4070
  if (editor.workspaceOptions?.projectAlias === definition.project) {
@@ -4324,6 +4338,9 @@ async function showSchedule(planId, returnFocus = document.activeElement) {
4324
4338
  : scheduleSessionSummary(data.plan.session_settings || defaultScheduleSessionSettings());
4325
4339
  scheduleInput("detail-settings").textContent += ` · ${data.plan.target_kind === "binding"
4326
4340
  ? scheduleInheritedWorkspace(data.plan) : workspacePolicySummary(data.plan.workspace, { scheduled: true })}`;
4341
+ if (data.plan.target_kind !== "binding") {
4342
+ scheduleInput("detail-settings").textContent += ` · 成功后自动归档:${data.plan.auto_archive ? "开启" : "关闭"}`;
4343
+ }
4327
4344
  scheduleInput("detail-rule").textContent = scheduleRuleLabel(data.plan.schedule);
4328
4345
  renderSchedulePreview("#schedule-detail-preview", data.preview);
4329
4346
  scheduleInput("detail-note").textContent = "";
@@ -94,8 +94,8 @@
94
94
  <div class="workspace-fields">
95
95
  <label>Project<select id="worktree-project" required><option value="">选择 Project</option></select></label>
96
96
  <label>检出方式<select id="worktree-mode"><option value="detached">Detached(不绑定命名分支)</option><option value="existing">检出已有本地分支</option><option value="new">创建并检出新分支</option></select></label>
97
- <label id="worktree-ref-label">起点提交 / 分支<input id="worktree-ref" list="worktree-refs" placeholder="HEAD"><datalist id="worktree-refs"></datalist></label>
98
- <label id="worktree-existing-label" hidden>本地分支<select id="worktree-existing"></select></label>
97
+ <label id="worktree-ref-label">起点提交 / 分支<input id="worktree-ref" maxlength="256" placeholder="HEAD、分支、标签或提交"></label>
98
+ <label id="worktree-existing-label" hidden>本地分支<input id="worktree-existing" maxlength="256" placeholder="填写本地分支名"></label>
99
99
  <label id="worktree-new-label" hidden>新分支名称<input id="worktree-new-branch" maxlength="1024"></label>
100
100
  </div>
101
101
  <p id="worktree-create-note" class="hint">只检出已提交内容,不复制未提交、未跟踪或忽略文件,不执行环境初始化。不创建会话。</p>
@@ -291,7 +291,7 @@
291
291
  <label>Project<select id="defaults-project" required><option value="">选择 Project</option></select></label>
292
292
  <label>worktree 使用方式<select id="defaults-workspace"><option value="project">项目目录(不使用 worktree)</option><option value="existing">使用已有 worktree</option><option value="new">每个新会话新建 worktree</option></select></label>
293
293
  <label id="defaults-worktree-field" hidden>受管 worktree<select id="defaults-worktree"></select></label>
294
- <label id="defaults-workspace-ref-field" hidden>起点提交 / 分支<input id="defaults-workspace-ref" list="defaults-workspace-refs" maxlength="256" placeholder="留空使用创建时 Project 的 HEAD"><datalist id="defaults-workspace-refs"></datalist></label>
294
+ <label id="defaults-workspace-ref-field" hidden>起点提交 / 分支<input id="defaults-workspace-ref" maxlength="256" placeholder="留空使用创建时 Project 的 HEAD"></label>
295
295
  </div>
296
296
  <p id="defaults-workspace-help" class="defaults-field-help" role="status"></p>
297
297
  <div class="schedule-form-grid">
@@ -417,7 +417,7 @@
417
417
  <div id="schedule-workspace-fields" class="schedule-section-fields">
418
418
  <label>worktree 使用方式<select id="schedule-workspace"><option value="project">项目目录(不使用 worktree)</option><option value="existing">使用已有 worktree</option><option value="new">每次执行新建 worktree</option></select></label>
419
419
  <label id="schedule-worktree-field" hidden>受管 worktree<select id="schedule-worktree"></select></label>
420
- <label id="schedule-workspace-ref-field" hidden>起点提交 / 分支<input id="schedule-workspace-ref" list="schedule-workspace-refs" maxlength="256" placeholder="留空使用执行时 Project 的 HEAD"><datalist id="schedule-workspace-refs"></datalist></label>
420
+ <label id="schedule-workspace-ref-field" hidden>起点提交 / 分支<input id="schedule-workspace-ref" maxlength="256" placeholder="留空使用执行时 Project 的 HEAD"></label>
421
421
  <p id="schedule-workspace-help" class="schedule-field-help" role="status"></p>
422
422
  </div>
423
423
  <div id="schedule-chat-target" class="chat-target">
@@ -461,11 +461,15 @@
461
461
  </fieldset>
462
462
  <div class="schedule-preview-panel">
463
463
  <button id="schedule-preview" type="button">预览触发时间</button>
464
- <p id="schedule-preview-message" role="status" aria-live="polite">保存前请预览触发时间。</p>
464
+ <p id="schedule-preview-message" role="status" aria-live="polite">可预览触发时间,也可直接保存。</p>
465
465
  <ol id="schedule-preview-times"></ol>
466
466
  </div>
467
467
  </div>
468
468
  </section>
469
+ <section id="schedule-auto-archive-field" class="schedule-editor-section">
470
+ <label class="schedule-checkbox"><input id="schedule-auto-archive" type="checkbox" aria-describedby="schedule-auto-archive-help">成功后自动归档</label>
471
+ <p id="schedule-auto-archive-help" class="schedule-field-help">首轮正常结束且结果送达后归档,恢复使用 /sessions archived。</p>
472
+ </section>
469
473
  <details id="schedule-session-settings" class="schedule-editor-section schedule-session-settings" open>
470
474
  <summary>会话配置<span id="schedule-session-summary">继承 Codex · 当前消息</span></summary>
471
475
  <div class="schedule-section-fields">
@@ -6,10 +6,10 @@ window.NetizenWorktrees = (() => {
6
6
  const node = (name) => document.querySelector(`#worktree-${name}`);
7
7
  let dependencies;
8
8
  let settings = null;
9
- let branches = null;
9
+ let creationOptions = null;
10
10
  let pending = false;
11
11
  let serial = 0;
12
- let branchSerial = 0;
12
+ let creationSerial = 0;
13
13
  let editor = null;
14
14
  let cursor = null;
15
15
  let nextCursor = null;
@@ -59,7 +59,7 @@ window.NetizenWorktrees = (() => {
59
59
  node("new-branch").required = mode === "new";
60
60
  node("existing").required = mode === "existing";
61
61
  node("create-submit").disabled = disabled || editor?.kind !== "create" || editor.loading || editor.checking
62
- || editor.uncertain || !branches?.actions?.create || (mode === "existing" && !node("existing").value);
62
+ || editor.uncertain || !creationOptions?.actions?.create || (mode === "existing" && !node("existing").value.trim());
63
63
  node("create-submit").textContent = pending && editor?.kind === "create" ? "正在创建…" : "创建 worktree";
64
64
  node("new").disabled = disabled;
65
65
  node("configure").disabled = disabled;
@@ -73,8 +73,8 @@ window.NetizenWorktrees = (() => {
73
73
  if (!saved && !confirmed && dirty() && !window.confirm("有未保存的修改,确定放弃吗?")) return false;
74
74
  const current = editor;
75
75
  editor = null;
76
- branchSerial += 1;
77
- branches = null;
76
+ creationSerial += 1;
77
+ creationOptions = null;
78
78
  settings = null;
79
79
  window.NetizenSelect?.close();
80
80
  node(current.kind).hidden = true;
@@ -89,14 +89,14 @@ window.NetizenWorktrees = (() => {
89
89
  const current = { kind, returnFocus, loading: true, checking: false, uncertain: false, checkAvailable: false, base: null };
90
90
  editor = current;
91
91
  settings = null;
92
- branches = null;
92
+ creationOptions = null;
93
93
  if (kind === "settings") {
94
94
  node("root").value = ""; node("fetch").checked = false;
95
95
  node("effective-root").textContent = "";
96
96
  } else {
97
97
  node("project").replaceChildren(); option(node("project"), "", "选择 Project"); node("project").value = "";
98
98
  node("mode").value = "detached"; node("ref").value = ""; node("new-branch").value = "";
99
- node("existing").replaceChildren(); node("existing").value = ""; node("refs").replaceChildren();
99
+ node("existing").value = "";
100
100
  node("create-note").textContent = ""; node("create-config").textContent = "";
101
101
  }
102
102
  current.base = values(kind);
@@ -149,32 +149,25 @@ window.NetizenWorktrees = (() => {
149
149
  return dependencies.api(path, { method: "POST", headers: { "Content-Type": "application/json" },
150
150
  body: JSON.stringify(dependencies.actionPayload(envelope, extra)) });
151
151
  }
152
- async function loadBranches() {
152
+ async function loadCreationOptions() {
153
153
  const current = editor;
154
154
  if (current?.kind !== "create" || current.loading || pending || expired()) return;
155
- const request = ++branchSerial;
155
+ const request = ++creationSerial;
156
156
  const project = node("project").value;
157
- branches = null;
158
- node("existing").replaceChildren(); node("existing").value = "";
159
- node("refs").replaceChildren();
157
+ creationOptions = null;
158
+ node("existing").value = "";
160
159
  controls();
161
160
  if (!project || expired()) return;
162
161
  try {
163
- const result = await dependencies.api(`/api/v1/worktrees/branches?${new URLSearchParams({ project })}`);
164
- if (expired() || editor !== current || request !== branchSerial || node("project").value !== project) return;
165
- branches = result;
166
- option(node("existing"), "", "选择未被占用的本地分支");
167
- for (const branch of result.items) {
168
- option(node("refs"), branch.ref, branch.name);
169
- if (!branch.remote) option(node("existing"), branch.name,
170
- `${branch.name}${branch.occupiedPaths?.length ? "(已被其他 checkout 占用)" : ""}`, Boolean(branch.occupiedPaths?.length));
171
- }
162
+ const result = await dependencies.api(`/api/v1/worktrees/create-options?${new URLSearchParams({ project })}`);
163
+ if (expired() || editor !== current || request !== creationSerial || node("project").value !== project) return;
164
+ creationOptions = result;
172
165
  node("create-note").textContent = result.isGit === false ? "该 Project 不是 Git 仓库,不能创建 worktree。"
173
166
  : "默认从此 Project 当前 HEAD 检出;不复制未提交、未跟踪或忽略文件,不执行环境初始化。不创建会话。";
174
167
  } catch (error) {
175
- if (editor === current && request === branchSerial && !expired()) node("create-note").textContent = error.message;
168
+ if (editor === current && request === creationSerial && !expired()) node("create-note").textContent = error.message;
176
169
  } finally {
177
- if (request === branchSerial) controls();
170
+ if (request === creationSerial) controls();
178
171
  }
179
172
  }
180
173
  function renderRows(items) {
@@ -273,11 +266,11 @@ window.NetizenWorktrees = (() => {
273
266
  event.preventDefault();
274
267
  const current = editor;
275
268
  if (expired() || pending || current?.kind !== "create" || current.loading || current.checking
276
- || current.uncertain || !branches?.actions?.create) return;
269
+ || current.uncertain || !creationOptions?.actions?.create) return;
277
270
  const mode = node("mode").value;
278
- const branch = mode === "existing" ? node("existing").value : mode === "new" ? node("new-branch").value.trim() : null;
271
+ const branch = mode === "existing" ? node("existing").value.trim() : mode === "new" ? node("new-branch").value.trim() : null;
279
272
  const ref = mode === "existing" ? `refs/heads/${branch}` : node("ref").value.trim() || "HEAD";
280
- const grant = branches.actions.create; branches.actions.create = null;
273
+ const grant = creationOptions.actions.create; creationOptions.actions.create = null;
281
274
  pending = true; controls(); editorMessage("正在创建 worktree…");
282
275
  let result;
283
276
  try {
@@ -339,7 +332,7 @@ window.NetizenWorktrees = (() => {
339
332
  dependencies = values;
340
333
  node("new").addEventListener("click", (event) => openEditor("create", event.currentTarget));
341
334
  node("configure").addEventListener("click", (event) => openEditor("settings", event.currentTarget));
342
- node("project").addEventListener("change", loadBranches);
335
+ node("project").addEventListener("change", loadCreationOptions);
343
336
  for (const name of ["mode", "root", "fetch", "ref", "existing", "new-branch"]) {
344
337
  node(name).addEventListener(name === "mode" ? "change" : "input", controls);
345
338
  }
@@ -361,5 +354,5 @@ window.NetizenWorktrees = (() => {
361
354
  controls();
362
355
  }
363
356
  return { init, load, dirty, busy: () => pending, discard,
364
- expire() { serial += 1; branchSerial += 1; controls(); } };
357
+ expire() { serial += 1; creationSerial += 1; controls(); } };
365
358
  })();
@@ -609,8 +609,8 @@ class AdminWebApplication:
609
609
  return await self._projects(context)
610
610
  if route == ("GET", "/api/v1/worktrees"):
611
611
  return await self._worktrees(context)
612
- if route == ("GET", "/api/v1/worktrees/branches"):
613
- return await self._worktree_branches(context)
612
+ if route == ("GET", "/api/v1/worktrees/create-options"):
613
+ return await self._worktree_creation_options(context)
614
614
  if route == ("GET", "/api/v1/worktrees/settings"):
615
615
  return self._worktree_settings(context)
616
616
  if route == ("GET", "/api/v1/progress-card/settings"):
@@ -986,7 +986,7 @@ class AdminWebApplication:
986
986
  )
987
987
  definition = payload.get("definition", {})
988
988
  if not isinstance(definition, dict) or set(definition) - {
989
- "name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id",
989
+ "name", "instructions", "project", "expected_project_revision", "chat_id", "schedule", "enabled", "session_settings", "workspace", "target_kind", "target_binding_id", "auto_archive",
990
990
  }:
991
991
  raise AdminWebError(400, "invalid_input", "定时任务定义字段无效。")
992
992
  request: dict[str, object] = {
@@ -1054,14 +1054,14 @@ class AdminWebApplication:
1054
1054
  )}} for item in result["items"]]
1055
1055
  return _json_response(200, {**result, "items": items, "requestId": context.request.request_id})
1056
1056
 
1057
- async def _worktree_branches(self, context: _RequestContext) -> Response:
1057
+ async def _worktree_creation_options(self, context: _RequestContext) -> Response:
1058
1058
  _require_query_keys(context.query, {"project"})
1059
1059
  alias = _optional_text_query(context.query, "project", maximum=64)
1060
1060
  if not alias:
1061
1061
  raise AdminWebError(400, "invalid_input", "请选择 Project。")
1062
- result = await self._worktree_service().branches(project_alias=alias)
1062
+ result = await self._worktree_service().creation_options(project_alias=alias)
1063
1063
  actions = {}
1064
- if result.get("isGit", True):
1064
+ if result["isGit"]:
1065
1065
  actions["create"] = self._grant(
1066
1066
  context, "worktrees.create", AdminActionTarget("project", alias),
1067
1067
  _empty_preconditions(project_revision=ExpectedValue.expect(result["projectRevision"])),
@@ -40,7 +40,7 @@ from .worktrees.models import WorktreeRecord
40
40
  from .worktrees.store import WorktreeStore, create_schema as create_worktree_schema
41
41
 
42
42
 
43
- SCHEMA_VERSION = 19
43
+ SCHEMA_VERSION = 20
44
44
 
45
45
  # Includes the running query and all submitted queries waiting for the reader.
46
46
  _ADMIN_QUERY_CAPACITY = 32
@@ -467,7 +467,7 @@ def _require_current_schema(connection: sqlite3.Connection) -> None:
467
467
  _require_project_metadata_schema(connection)
468
468
  require_schema(connection)
469
469
  require_defaults_schema(connection)
470
- from .migrations.v19 import validate
470
+ from .migrations.v20 import validate
471
471
  validate(connection)
472
472
 
473
473
 
@@ -563,23 +563,29 @@ class BindingStore:
563
563
  A file-backed Store owns a WAL writer plus one query-only Admin reader.
564
564
  ``:memory:`` is the explicit compatibility/test mode: Admin reads run on
565
565
  the same connection under the writer lock, never against a second empty
566
- in-memory database.
566
+ in-memory database. Service startup uses ``create=False`` so validation and
567
+ opening share the requirement for an existing, nonempty database.
567
568
  """
568
569
 
569
570
  def __init__(
570
571
  self,
571
572
  path: str | Path = ":memory:",
572
573
  *,
574
+ create: bool = True,
573
575
  id_factory: Callable[[], str] | None = None,
574
576
  wall_clock: Callable[[], float] = time.time,
575
577
  ) -> None:
576
578
  self._path = path
577
579
  self._path_string = str(path)
578
580
  self._is_memory = self._path_string == ":memory:"
579
- if not self._is_memory and Path(path).exists():
581
+ if not create or (not self._is_memory and Path(path).exists()):
580
582
  # Reject unsupported data before a writer can recover/checkpoint a
581
583
  # previous process's WAL, even while closing after an open failure.
582
- validate_channel_database(path, allow_empty=True)
584
+ validate_channel_database(path, allow_empty=create)
585
+ self._connection_target = (
586
+ self._path_string if create else Path(path).resolve().as_uri() + "?mode=rw"
587
+ )
588
+ self._connection_uri = not create
583
589
  self._id_factory = id_factory or (lambda: str(uuid.uuid4()))
584
590
  self._wall_clock = wall_clock
585
591
  self._lock = threading.RLock()
@@ -593,11 +599,12 @@ class BindingStore:
593
599
  max_workers=1,
594
600
  thread_name_prefix="netizen-binding-query",
595
601
  )
596
- if not self._is_memory:
602
+ if create and not self._is_memory:
597
603
  db_path = Path(path)
598
604
  db_path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
599
605
  self._connection = sqlite3.connect(
600
- self._path_string,
606
+ self._connection_target,
607
+ uri=self._connection_uri,
601
608
  isolation_level=None,
602
609
  check_same_thread=False,
603
610
  )
@@ -606,7 +613,7 @@ class BindingStore:
606
613
  self.defaults = DefaultsStore(self)
607
614
  self.worktrees = WorktreeStore(self)
608
615
  try:
609
- self._initialize()
616
+ self._initialize(create=create)
610
617
  if not self._is_memory:
611
618
  self._query_connection = self._query_executor.submit(
612
619
  self._open_query_connection
@@ -618,14 +625,14 @@ class BindingStore:
618
625
  if not self._is_memory:
619
626
  os.chmod(Path(path), 0o600)
620
627
 
621
- def _initialize(self) -> None:
628
+ def _initialize(self, *, create: bool) -> None:
622
629
  with self._lock:
623
630
  tables = {
624
631
  row["name"] for row in self._connection.execute(
625
632
  "SELECT name FROM sqlite_master WHERE type = 'table'"
626
633
  )
627
634
  }
628
- if tables:
635
+ if tables or not create:
629
636
  if "schema_version" not in tables:
630
637
  raise RuntimeError("Channel database is missing its schema version; existing data was not changed")
631
638
  _require_current_schema(self._connection)
@@ -862,7 +869,8 @@ class BindingStore:
862
869
 
863
870
  def _open_query_connection(self) -> sqlite3.Connection:
864
871
  connection = sqlite3.connect(
865
- self._path_string,
872
+ self._connection_target,
873
+ uri=self._connection_uri,
866
874
  isolation_level=None,
867
875
  check_same_thread=False,
868
876
  )
@@ -79,7 +79,6 @@ from .callbacks import (
79
79
  _turn_reference,
80
80
  )
81
81
  from .reply import reply_card
82
- from .model_info import with_model_details
83
82
  from .session_ids import session_identity_lines
84
83
 
85
84
 
@@ -139,7 +138,7 @@ def settings_card(
139
138
  notice: str | None = None,
140
139
  notice_is_error: bool = False,
141
140
  ) -> OutboundCard:
142
- builder = _builder("Netizen 设置", _settings_section_label(section))
141
+ builder = _builder("Netizen 项目管理", _settings_section_label(section))
143
142
  builder.raw(_settings_navigation(scope=scope, active=section))
144
143
  builder.markdown(
145
144
  "当前聊天的参与者均可管理项目;修改会影响使用这个机器人的其他聊天。"
@@ -180,7 +179,7 @@ def _render_projects_settings(
180
179
 
181
180
  builder.raw(
182
181
  _repeatable_callback_button(
183
- label="刷新当前配置",
182
+ label="刷新项目列表",
184
183
  value=_envelope(
185
184
  scope,
186
185
  CardControlName.REFRESH_SETTINGS,
@@ -546,11 +545,6 @@ def _new_workspace_elements(options: Mapping[str, Any]) -> list[dict[str, Any]]:
546
545
  f"已有 worktree · {branch} · {item['cwd']}",
547
546
  _workspace_reference(options, "id:" + item["id"]),
548
547
  ))
549
- refs = [("当前 HEAD", "HEAD")]
550
- for branch in options.get("branches", ()):
551
- occupied = " · 已占用,仅可作起点" if branch.get("occupiedPaths") else ""
552
- remote = "远端 · " if branch.get("remote") else "本地 · "
553
- refs.append((remote + branch["name"] + occupied, branch["ref"]))
554
548
  return [
555
549
  _form_label("worktree(切换项目后请重新加载选项)"),
556
550
  _static_select(
@@ -565,11 +559,9 @@ def _new_workspace_elements(options: Mapping[str, Any]) -> list[dict[str, Any]]:
565
559
  ("创建并检出新分支", "new")),
566
560
  initial_option="detached",
567
561
  ),
568
- _form_label("起点 revision;检出已有分支时请选择对应的本地分支"),
569
- _static_select(
570
- name="new_worktree_ref", placeholder="选择起点/已有分支",
571
- options=tuple(refs), initial_option="HEAD",
572
- ),
562
+ _form_label("起点 revision;检出已有分支时填写本地分支名"),
563
+ {"tag": "input", "name": "new_worktree_ref", "max_length": 256,
564
+ "placeholder": _plain_text("HEAD、分支、标签或提交"), "default_value": "HEAD"},
573
565
  {"tag": "input", "name": "new_worktree_branch", "max_length": 255,
574
566
  "label": _plain_text("新分支名(仅创建新分支时填写)"),
575
567
  "placeholder": _plain_text("例如:feature/my-task")},
@@ -1023,7 +1015,7 @@ def new_binding_card(
1023
1015
  )
1024
1016
  if not projects:
1025
1017
  builder.markdown(
1026
- "还没有已启用的项目。请先发送 `/settings` 创建、登记或启用项目,"
1018
+ "还没有已启用的项目。请先发送 `/projects` 创建、登记或启用项目,"
1027
1019
  "再发送 `/new`。发送 `/help` 查看快速开始。"
1028
1020
  )
1029
1021
  else:
@@ -1056,7 +1048,7 @@ def new_binding_card(
1056
1048
  settings_draft=settings_draft,
1057
1049
  )
1058
1050
  )
1059
- return OutboundCard(card=with_model_details(builder.to_dict(), catalog if projects else None))
1051
+ return OutboundCard(card=builder.to_dict())
1060
1052
 
1061
1053
 
1062
1054
  def config_card(
@@ -1116,7 +1108,7 @@ def config_card(
1116
1108
  catalog=catalog,
1117
1109
  )
1118
1110
  )
1119
- return OutboundCard(card=with_model_details(builder.to_dict(), catalog))
1111
+ return OutboundCard(card=builder.to_dict())
1120
1112
 
1121
1113
 
1122
1114
  def rename_binding_card(
@@ -2393,7 +2385,8 @@ def _decode_new_workspace(
2393
2385
  if match is None:
2394
2386
  raise CardActionError("worktree 选择无效,请重新加载 worktree 选项。")
2395
2387
  mode = _required_string(payload["new_worktree_mode"], "new_worktree_mode")
2396
- ref = _bounded_string(payload["new_worktree_ref"], "new_worktree_ref", max_chars=1000)
2388
+ ref_value = payload["new_worktree_ref"]
2389
+ ref = _bounded_string(ref_value, "new_worktree_ref", max_chars=256).strip() if ref_value != "" else ""
2397
2390
  branch_value = payload.get("new_worktree_branch")
2398
2391
  branch = (
2399
2392
  _bounded_string(branch_value, "new_worktree_branch", max_chars=255).strip()
@@ -2412,10 +2405,11 @@ def _decode_new_workspace(
2412
2405
  if mode == "new" and not branch:
2413
2406
  raise CardActionError("创建新分支时请填写新分支名。")
2414
2407
  if mode == "existing":
2415
- if not ref.startswith("refs/heads/") or not ref.removeprefix("refs/heads/"):
2416
- raise CardActionError("检出已有分支时,请选择未被占用的本地分支。")
2417
2408
  branch = ref.removeprefix("refs/heads/")
2418
- return None, {"mode": mode, "ref": ref, "branch": branch if mode != "detached" else None}
2409
+ if not branch or branch == "HEAD" or branch.startswith("refs/"):
2410
+ raise CardActionError("检出已有分支时,请填写本地分支名。")
2411
+ ref = "refs/heads/" + branch
2412
+ return None, {"mode": mode, "ref": ref or "HEAD", "branch": branch if mode != "detached" else None}
2419
2413
 
2420
2414
 
2421
2415
  def _decode_config_form(
@@ -139,10 +139,10 @@ def defaults_card(
139
139
  project_options.append({"text": _plain_text(f"{selected_project} · 不可用(保留原配置)"), "value": selected_value})
140
140
  builder.raw(_notice(f"Project「{selected_project}」已停用或不可用;请选择可用项目后保存,或删除本聊天的默认配置。", error=True))
141
141
  if not enabled_projects:
142
- builder.raw(_notice("没有可用 Project,请先通过 /settings 登记或启用项目。"))
142
+ builder.raw(_notice("没有可用 Project,请先通过 /projects 登记或启用项目。"))
143
143
  if not project_options:
144
144
  # Feishu cannot render an empty static select. There is no saved exact
145
- # record to delete in this branch; opening again after /settings gives
145
+ # record to delete in this branch; opening again after /projects gives
146
146
  # the ordinary single form without inventing a default Project.
147
147
  return OutboundCard(card=builder.to_dict())
148
148